diff --git a/.checkignore b/.checkignore new file mode 100644 index 000000000..28ba17f00 --- /dev/null +++ b/.checkignore @@ -0,0 +1,2 @@ +kotti/testing.py +kotti/tests/* diff --git a/.github/workflows/mysql.yml b/.github/workflows/mysql.yml new file mode 100644 index 000000000..8c4cfc84f --- /dev/null +++ b/.github/workflows/mysql.yml @@ -0,0 +1,65 @@ +name: MySQL + +on: + push: + branches: [ master, testing, stable ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.6, 3.7, 3.8, 3.9, "3.10"] + + # Service containers to run with `runner-job` + services: + # Label used to access the service container + mysql: + image: mariadb:latest + env: + MYSQL_USER: kotti + MYSQL_PASSWORD: kotti + MYSQL_DATABASE: kotti + MYSQL_ROOT_PASSWORD: kotti + options: >- + --health-cmd="mysqladmin ping" + --health-interval=5s + --health-timeout=2s + --health-retries=3 + ports: + # Maps tcp port 3306 on service container to the host + - 3306:3306 + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e ".[testing]" + pip install mysqlclient + # - name: Lint with flake8 + # run: | + # # stop the build if there are Python syntax errors or undefined names + # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Verify MySQL connection + env: + PORT: ${{ job.services.mysql.ports[3306] }} + run: | + while ! mysqladmin ping -h"127.0.0.1" -P"$PORT" --silent; do + sleep 1 + done + - name: Test with pytest + run: | + pytest + env: + KOTTI_TEST_DB_STRING: mysql://kotti:kotti@127.0.0.1:3306/kotti + diff --git a/.github/workflows/postgres.yml b/.github/workflows/postgres.yml new file mode 100644 index 000000000..c5c2bd111 --- /dev/null +++ b/.github/workflows/postgres.yml @@ -0,0 +1,57 @@ +name: PostgreSQL + +on: + push: + branches: [ master, testing, stable ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.6, 3.7, 3.8, 3.9, "3.10"] + + # Service containers to run with `runner-job` + services: + # Label used to access the service container + postgres: + # Docker Hub image + image: postgres + # Provide the password for postgres + env: + POSTGRES_PASSWORD: postgres + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + # Maps tcp port 5432 on service container to the host + - 5432:5432 + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e ".[testing]" + pip install psycopg2-binary + # - name: Lint with flake8 + # run: | + # # stop the build if there are Python syntax errors or undefined names + # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest + env: + KOTTI_TEST_DB_STRING: postgresql://postgres:postgres@localhost:5432/postgres + diff --git a/.github/workflows/sqlite.yml b/.github/workflows/sqlite.yml new file mode 100644 index 000000000..28a98ecbf --- /dev/null +++ b/.github/workflows/sqlite.yml @@ -0,0 +1,38 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +name: SQLite + +on: + push: + branches: [ master, testing, stable ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.6, 3.7, 3.8, 3.9, "3.10"] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e ".[testing]" + pip install psycopg2-binary + # - name: Lint with flake8 + # run: | + # # stop the build if there are Python syntax errors or undefined names + # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest + diff --git a/.gitignore b/.gitignore index e28e50f5d..2aa502150 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ /.installed.cfg /.pip.log /.tox +.pytest_cache /Kotti.db* /Kotti.egg-info/ /bin/ @@ -24,3 +25,15 @@ junit*.xml *.sublime-* bower_components node_modules +__pycache__ + +filestore +share +.eggs +.python-version +.long-description.html +pyvenv.cfg +.pytest_cache/v/pep8/mtimes + +*.DS_Store +.venv diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 000000000..cdde86116 --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,25 @@ +build: + environment: + python: 3.6.3 + dependencies: + before: + - pip install -U pip wheel setuptools + - pip install psycopg2 mysqlclient python-coveralls codecov + - pip install -e .[testing] + tests: + override: + - command: pytest + coverage: + file: '.coverage' + config_file: '.coveragerc' + format: 'py-cc' + +checks: + python: + code_rating: true + duplicate_code: true + +filter: + excluded_paths: + - kotti/tests/ + - kotti/alembic/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3baa3b755..000000000 --- a/.travis.yml +++ /dev/null @@ -1,20 +0,0 @@ -language: python -python: - - "2.6" - - "2.7" -env: - - KOTTI_TEST_DB_STRING=postgresql://postgres@localhost:5432/kotti_testing - - KOTTI_TEST_DB_STRING=mysql+oursql://root@localhost:3306/kotti_testing - - KOTTI_TEST_DB_STRING=sqlite:// -install: - - pip install "pip==1.3.1" # fix issue with fanstatic==1.0a - - pip install -e . -r requirements.txt - - python setup.py dev - - pip install psycopg2 oursql -before_script: - - psql -c 'create database kotti_testing;' -U postgres - - mysql -e 'create database kotti_testing;' -script: py.test --runslow --tb=native --cov=kotti --cov-report=term-missing -notifications: - irc: "irc.freenode.org#kotti" - email: false diff --git a/AUTHORS.txt b/AUTHORS.txt index 10d2577f5..7dcbfb109 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -1,7 +1,7 @@ Authors ======= -Kotti was created by Daniel Nouri. +Kotti was created by Daniel Nouri and is currently maintained by Andreas Kaiser. Major contributing authors include: diff --git a/CHANGES.txt b/CHANGES.txt index 7cd85d875..56e247efb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,8 +1,523 @@ Change History ============== -0.10 - Unreleased ------------------ +2.0.10 - unreleased +------------------- + +- No changes yet. + +2.0.9 - 2022-05-05 +------------------ + +- Replace ``py-bcrypt`` (unmaintained, last release in 2013) with ``bcrypt``. +- Pin bleach to ``<5`` (incompatible API changes). + +2.0.8 - 2022-05-05 +------------------ + +- Brownbag release (hidden). + +2.0.7 - 2021-09-02 +------------------ + +- Add compatibility for (and require) SQLAlchemy 1.4. +- Fix ``url_normalizer`` tests (not picked up before). +- Optimize traversal for applications with large ``Node`` subclass hierarchies. +- Add proper caching for ``get_root``. + +2.0.6 - 2021-05-12 +------------------ + +- Fix performance issue: ``Content._tags`` is configured with ``lazy="select"`` now. + This doesn't cause any change in API usage, however it speeds up selects on ``Node`` and descendants by 1-2 orders of magnitude in some cases at the price of a slight performance degradation in scenarios with few tables and heavy tag usage. +- Update dependencies to their most recent compatible versions. + +2.0.5 - 2021-03-16 +------------------ + +- Fix sanitizer error when values are None (ie nothing to sanitize at all). +- Pin Pyramid to < 2 (for now). +- Pin SQLAlchemy to < 1.4 to prevent no longer working private import in sqlalchemy-utils. + +2.0.4 - 2020-11-20 +------------------ + +- Replace deprecated ``bleach_whitelist`` with its successor ``bleach-allowlist``. +- Upgrade ``iso8601``. + +2.0.3 - 2020-11-18 +------------------ + +- Upgrade to ``pytest>=6``. +- Replace ``pytest-pep8`` with ``pytest-flake8``; fix or silence flake8 warnings/errors + in existing code (mostly tests and migrations). + +2.0.2 - 2020-06-22 +------------------ + +- Upgrade ``requirements.txt``. [fixes #576] + +- Remove CircleCI + +- Fix broken tags widget (#562). + +- Run ``pyupgrade --py36-plus`` on all Python files to use some more modern language + features where appropriate (for example f-strings). + +- Declare Python 3.7 and 3.8 compatibility. + +- Remove Python 3.5 compatibility. + +- Remove ``rfc6266-parser`` dependency. + +2.0.1 - 2019-01-09 +------------------ + +- Reformat source code with black. + +- Remove testing import from package code. + +2.0.0 - 2019-01-07 +------------------ + +- Update documentation (kotti-cookiecutter is the new scaffolding template). + See https://github.com/Kotti/kotti-cookiecutter + +- Require pytest >= 4.1.0, use ``request.node.get_closest_marker`` instead of + deprecated ``request.keywords``. + +2.0.0b2 - 2018-04-04 +-------------------- + +- Fix CSRF issue in ``@@share`` view (#551). + +2.0.0b1 - 2018-03-19 +-------------------- + +- Remove links to the demo site, which has been down for too long. + +2.0.0a1 - 2018-02-22 +-------------------- + +**This is a alpha release. Blindly upgrading your production environments will make the universe collapse!** + +Backward Incompatibilities +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- **Python 3 compatibility** + + Kotti now runs on Python >= 3.5. + + Python 2.x support was dropped entirely and won't come back until somebody implements it or is willing to pay for it. + +- Everything marked as deprecated in Kotti 1.x was removed. + +- Scaffolding support via ``pcreate`` was dropped as it's deprecated by Pyramid now. + An alternate solution (cookiecutter template) might become available again in the future. + +New Features +~~~~~~~~~~~~ + +- **Python 3 compatibility** + +- Extensive testing and code analysis on Circle CI, Travis CI and Scrutinizer. + +Fixes +~~~~~ + +- Avoid deprecation warning ``pyramid.security.has_permission`` + +1.3.2 - 2018-04-04 +------------------ + +**This release fixes a CSRF (Cross Site Request Forgery) security vulnerablity which was reported in #551. You should upgrade your installations ASAP.** + +1.3.1 - 2018-01-17 +------------------ + +- When rendering slot views, use ``request.blank()`` to create the request. + This is the proper behaviour, in tune with customizing + ``kotti.request_factory``. Also added ``blank()`` method to + ``kotti.testing.DummyRequest``. + +- When authenticated, show workflow state in the edit bar. Before it was + shown only if the 'edit' permission was available. + +- Optimize the File edit form: don't load initial file data to session data + and don't rewrite the file data after saving the form if that data has not + been changed through the edit form. + +- Bugfix: when showing addable content in the menu, check if the factory has + a defined add_view. This avoids a hard crash with, for example, a content + type derived from Content that has no add_view defined. + +- Added ``nav-bar`` slot to ``edit/master.pt``, ``edit-bar`` and ``nav-bar`` + slots to ``view/master.pt`` + +- Bugfix: Simplify 404 page, no longer crash when authenticated + +- Change: simplify `kotti.util.LinkBase.selected()`: use request.view_name + instead of deriving the view name from request.url. Also, consider the View + editor bar entry as selected even when the url doesn't end with a slash '/' + +- Feature: add Czech translation. + +- remove pytest-warnings from test dependencies (already integrated in + modern pytest versions) + +1.3.0 - 2016-10-10 +------------------ + +Breaking Changes +~~~~~~~~~~~~~~~~ + +- Upgrade to ``repoze.workflow==1.0b``. **If your application has a custom + ``workflow.zcml``, it needs a little modification: ``state`` and + ``transition`` titles are no longer ``key`` nodes, but attributes on the + respective ``state`` or ``transition`` nodes. See Kotti's ``workflow.zcml`` + for an example.** + +Features and Fixes +~~~~~~~~~~~~~~~~~~ + +- Add a fallback in ``contents.pt`` when ``creation_date`` or + ``modification_date`` is ``None``. + +- Transform workflow state title to TranslationStrings without eval and + deprecate it. + +- Replace some Python 2 only code with equivalents that also support Python 3. + +- Use generic SQLAlchemy type Text as base type for JsonType. This allows + SQLAlchemy to map Text type to the most suitable type available on given + database system. Previously used TEXT type is not available in Oracle + database. In case of existing installation of Kotti with database system, + for which SQLAlchemy maps generic Text type to type different than TEXT it's + necessary to either convert existing columns "nodes._acl" and + "nodes.annotations" to that type or configure SQLAlchemy to map generic Text + type to existing type of these two columns. For example of how to do this + please see http://stackoverflow.com/a/36506666/95735. For all database + systems for which SQLAlchemy provides dialects except Oracle (Firebird, + Microsoft SQL Server, MySQL, Postgres, SQLite, Sybase) there's no need to + do anything. + +- We use PEP 440 normalized form for the project's version thus current + "1.3.0-alpha.5-dev" became "1.3.0a5.dev0". + +- Upgrade tests to ``zope.testbrowser>=5.0.0``. This removes the ``mechanize`` + and ``wsgi_intercept`` dependencies and thus the last blocker for Python 3 + compatibility. + +- Move ``pytest`` config from ``setup.cfg`` to new ``pytest.ini``. This + prevents a deprecation warning with ``pytest>=3.0``. + +- Rename ``kotti.testing.TestingRootFactory`` to + ``kotti.testing.RootFactory`` to prevent another deprecation warning with + ``pytest>=3.0``. + +1.3.0-alpha.4 - 2015-01-15 +-------------------------- + +**This is a alpha release. Blindly upgrading your production environments will +make the universe collapse!** + + +- Add a ``kotti.depot_replace_wsgi_file_wrapper`` option to replace the WSGI + file wrapper with ``pyramid.response.FileIter`` for problematic environments. + +1.3.0-alpha.3 - 2016-01-11 +-------------------------- + +**This is a alpha release. Blindly upgrading your production environments will +make the universe collapse!** + + +- Bugfix: don't try to get api.root via the lineage if not in a location aware + context (for example 404 view). Return the site root instead. + +1.3.0-alpha.2 - 2016-01-05 +-------------------------- + +**This is a alpha release. Blindly upgrading your production environments will +make the universe collapse!** + +- Add a custom traverser, which gets all nodes in a single DB query. For deeply + nested trees this results in drastic performance improvements. See + https://kotti.readthedocs.io/en/master/api/kotti.traversal.html for details. + +- Bugfix: copy and paste of file nodes wouldn't create a new depot file, but + instead lead to multiple references to a single file which would cause + undesired results when one of them was deleted later. + +- Bugfix: local 'role:owner' was not set when a new node was created by copy + and paste. + +- Bugfix: kotti.events._update_children_paths could fail under unclear + conditions (at least under Python 2.6 with SQLite). + +- Get rid of more browser doctests (converted to webtest). + +1.3.0-alpha.1 - 2015-12-22 +-------------------------- + +**This is a alpha release. Blindly upgrading your production environments will +make the universe collapse!** + +- Completely revised ``Depot`` integration. See + https://kotti.readthedocs.io/en/latest/developing/advanced/blobs.html + for details. + +- Make ``kotti.resources.SaveDataMixin`` more versatile in that it now supports + a ``data_filters`` attribute (or even a completely overridden ``data`` + attribute) on subclasses. For an example for what this is useful, see the + new ``kotti_image`` package's readme and the Depot documentation + (https://depot.readthedocs.io/en/latest/database.html#custom-behaviour-in-attachments). + + **These changes require a database migration.** + + A migration script is included, which can be executed by running + ``kotti-migrate upgrade_all``. However, this script will fail if + you subclassed from ``kotti.resources.Image`` in your application. It also + doesn't cover custom classes inherited from ``kotti.resources.File`` (other + than Kotti's ``Image``). Migration of those can be performed easily, by + copying the code from the included migration step to your package's migration + environment and adjust it to your needs. + +- Move all image related code to the new ``kotti_image`` add on package. + All classes and functions are imported into their former place, so that code + that imports from there will still be working. + +- Fix broken upload type selector. + +- Create RFC6266 compliant content disposition headers for non-ASCII filenames. + +- Add request.uploaded_file_response method. + +1.2.4 - 2015-11-26 +------------------ + +- Fix broken packaging of 1.2.3. Sorry for the inconvenience! + +1.2.3 - 2015-11-26 +------------------ + +- Add Kotti logo and icon to static assets. +- Use Kotti logo as favicon. +- Move favicon definition to separate template to make it easily overridable. +- Fix permission check in ``kotti.views.util.nodes_tree``. + +1.2.2 - 2015-10-28 +------------------ + +- Add simple, default ``not found view``. +- In ``workflow-dropdown`` replace hard-coded permission check with individual + permission checks for each existing transition. +- Upgrade requirements. + +1.2.1 - 2015-10-07 +------------------ + +- Outfactor the code that runs after successful authentication into a + configurable ``kotti.login_success_callback`` function. +- Outfactor the code that runs after a valid password reset request into a + configurable ``kotti.password_reset_callback`` function. +- Support principal search on non string attributes. +- Support principal searches matching *all* arguments + (i.e. using the ``and`` operator, ``or`` is still the default). +- Support optional --rev with kotti-migrate upgrade. + +1.2.0 - 2015-09-27 +------------------ + +- **Greatly** reduce the number of queries that are sent to the DB: + - Add caching for the root node. + - Use eager / joined loading for local_groups. + - Don't query principals for roles +- Add "missing" foreign key indices (with corresponding migration step). +- Add a ``kotti.modification_date_excludes`` configuration option. + It takes a list of attributes in dotted name notation that should not trigger + an update of ``modification_date`` on change. Defaults to + ``kotti.resources.Node.position``. +- Don't try to set a caching header from the NewRequest handler when Pyramid's + tweens didn't follow the usual chain of calls. This fixes compatibility with + ``bowerstatic``. +- Don't assume ``renderer_name`` exists in a rendering event (ex. + BeforeRender). The official docstring of ``pyramid.interfaces.IRenderer`` is + a bit ambigous in regards to what the ``system`` parameter should include + when a renderer gets called. This fixes compatibility with + ``pyramid_layout``. +- Add a ``kotti.modification_date_excludes`` configuration option. + It takes a list of attributes in dotted name notation that should not trigger + an update of ``modification_date`` on change. Defaults to + ``kotti.resources.Node.position``. + +1.1.5 - 2015-09-04 +------------------ + +- Fix migration error on MySQL. +- Only wrap methods that do exist on the wrapped type (in + ``kotti.sqla.MutationList`` / ``kotti.sqla.MutationDict``). This + fixes an error that occurs when MutationLists are exposed to the UI + via ``colander.SequenceSchema``. +- Upgrade requirements to latest versions (``filedepot``, ``waitress``). + +1.1.4 - 2015-06-27 +------------------ + +- Add compatibility with SQLAlchemy 1.0. Also require SQLAlchemy 1.0.6 now. +- Ignore HTTPForbidden exceptions during slot rendering + +1.1.3 - 2015-06-17 +------------------ + +- Fix a bug in kotti-migrate that prevented initial migration steps from being + run successfully. +- Require kotti_tinymce 0.5.3. + +1.1.2 - 2015-06-12 +------------------ + +- Enlarge column sizes for ``name``, ``path`` and ``title`` (see #427). + Upgrading from any version older than 1.1.2 requires you to run a migration + script on your database. To run the migration, call:: + + $ bin/kotti-migrate upgrade + +- Add length validator for ``title`` (fix partially #404). See #428 + +- Remove 40 chars max length constraint for the html segment ``name`` + (``Kotti.util.title_to_name``). See #428 + +- Update italian translation + +- Update documentation + +- Add an ``add_permission`` attribute to ``kotti.resources.TypeInfo`` with a + default value of ``add``. See #436 + +- Add a "cancel" button to the delete node view. + +1.1.1 2015-05-11 +---------------- + +- Update scaffold's README file. See #417. +- Fix broken multifile upload. See #425. + +1.1.0 2015-04-16 +---------------- + +- Separate the default actions to a ``kotti.resources.default_actions`` + variable, to allows easier customization of default actions of all + content types. This is a ``LinkParent``, you can append new + ``kotti.util.Link`` objects to its ``children``. + +- Add ``target`` option to ``kotti.util.Link``. See #405. + +- Add sanitizers. See ``docs/development/advanced/sanitizers`` and + ``kotti.sanitizers`` for details. This fixes #296. + +- Added new document on how to customize the edit interface. + See ``docs/development/advanced/add-to-edit-interface``. + +- Make it easier to customize default actions by separating them to a new + ``kotti.resources.default_actions`` variable. Before, to customize them, + you'd have to change ``Content.type_info.edit_links[3].children``, now you + can mutate ``default_actions`` directly. See + ``docs/development/advanced/add-to-edit-interface`` for details. + +- Upgrade ``WebOb``, ``html2text``, ``pyramid`` and ``xlwt`` to their latest + stable versions. + +1.1.0-alpha.1 - 2015-03-19 +-------------------------- + +- Allow moving ``File`` and ``Image`` blob data from the database to + configurable storages. To achieve this we use `filedepot`_, a third-party + library with several plugin storages already built in. See + docs/developing/advanced/blobs.rst for details on what this brings. Upgrading + from any version older then 1.1.0 requires you to run a migration script on + your database. To run the migration, call:: + + $ bin/kotti-migrate upgrade + + Please note that, before running the migration, you should take the time to + read the documentation and configure your desired storage scheme. + +.. _filedepot: https://pypi.python.org/pypi/filedepot/ + +- Allow storing blob data in the database using ``DBStoredFile`` and + ``DBFileStorage``, a database centered storage plugin for ``filedepot``. This + storage is the default storage for blob data, unless configured otherwise. + +- Added a script to migrate blob data between depot storages. See + docs/developing/advanced/blobs.rst for details on how to use it. + +- Simplify serving blob data by using ``kotti.views.file.UploadedFileResponse``, + which also streams data. Please note that the default ``DBStoredFile`` still + needs to load its entire data in memory, to benefit from this feature you + should configure another default depot storage. + +- Added three new test fixtures: ``mock_filedepot``, to be used in simple unit + tests with no dependency on a database session, ``filedepot``, which + integrates with the ``dbsession`` fixture and ``no_filedepot``, a fixture + that can be used in developing tests for new file depot plugins - by + preserving the depot configuration before and after running the test. NOTE: + in order to test edit views with uploaded data in the request, you need to + mixin the ``filedepot`` fixture. + +- Initialize pyramid.paster.logging for custom commands defined via + ``kotti.util.command``, to allow log message output for kotti sessions + started via custom commands. + +- Remove unused ``kotti.js``. + +- Remove deprecated ``kotti.views.slots.local_navigation`` and + ``kotti.views.slots.includeme_local_navigation``. Use + ``kotti.views.navigation.local_navigation`` and + ``kotti.views.navigation.includeme_local_navigation`` instead. + +- Upgrade ``plone.scale`` and ``SQLAlchemy`` to their latest stable versions. + +- Change ``height`` property on ``body``'s widget (``RichTextField``) for + improved usability. See #403. + +1.0.0 - 2015-01-20 +------------------ + +- No changes. + +1.0.0-alpha.4 - 2015-01-29 +-------------------------- + +- Added experimental Docker support. See #374. + +- Allow restricting *add views* to specific contexts. This allows third party + developers to register new content types that are addable in specific + type of contexts, by specifying ``context=SomeContentType`` in their + *add view* registration and having ``type_info.addable=['SomeContentType']`` + in the type info. + +- For documents with duplicate titles that end in a number, append a counter + instead of incrementing their number. Fixes #245 + +- Update all requirements (except alembic) to their latest respective versions. + +1.0.0-alpha.3 - 2015-01-13 +-------------------------- + +- Explicitly implement ``pyramid.interfaces.IRequest`` for + ``kotti.request.Request``. This allows add-on packages to use + ``config.add_request_method`` (with reify) and + ``config.add_request_property`` without breaking the interfaces provided by + the request. Fixes #369 + +1.0.0-alpha.2 - 2015-01-01 +-------------------------- + +- Require ``kotti_tinymce==0.5.1``. This fixes #365. + +1.0.0-alpha - 2014-12-20 +------------------------ - Add a new scaffold based on Pyramid's ``pcreate``. To run the tests for the scaffold, you must invoke ``py.test`` with the ``--runslow`` option. This @@ -52,6 +567,12 @@ Change History corresponding deprecation in Pyramid 1.5. You should now use ``request.has_permission`` instead. +- Make all values in ``Node.path`` end in ``/``. This makes it consistent over + all nodes (*including* root) and correspond to the values of + ``request.resource_url``. As a side effect querying becomes easier. + However, this might need adjustments in your code if you were expecting the + old path values before. A migration step for DB upgrades is included. + 0.10b1 - 2014-07-11 ------------------- diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 14058d45e..70b17c4f7 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -12,7 +12,7 @@ below, and b) the resulting document is accepted into the canonical version control repository. Treatment of Account ---------------------- +-------------------- Contributor will not allow anyone other than the Contributor to use his or her username or source repository login to submit code to a @@ -100,3 +100,9 @@ Contributors - Fabien Castarède, 2013/08/08 - Natan Žabkar, 2013/08/28 - Ionică Bizău, 2014/04/09 +- Ichim Tiberiu, 2015/01/13 +- Martin Peeters, 2015/02/21 +- Davide Moro, 2015/04/02 +- Matt Russell, 2015/09/27 +- Sebastian Braß, 2015/10/28 +- Lukas Zdych, 2016/12/01 diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index e834e369f..8f060ea28 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -1,2 +1,2 @@ -Copyright (c) 2010-2014 Daniel Nouri and Contributors. +Copyright (c) 2010-2021 Daniel Nouri, Andreas Kaiser and Contributors. All Rights Reserved diff --git a/MANIFEST.in b/MANIFEST.in index d16503250..f8d97748b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,7 +1,27 @@ -recursive-include kotti/alembic * -recursive-include kotti/locale * -recursive-include kotti/scaffolds * -recursive-include kotti/static * -recursive-include kotti/templates * -recursive-include kotti/tests * -recursive-include kotti *.zcml +include *.rst +include *.txt +include Dockerfile +include app.ini +include development.ini +include pytest.ini +include tox.ini +include .coveragerc + +exclude .checkignore +exclude Makefile +exclude i18n.sh +recursive-exclude * *.pyc +recursive-exclude * __pycache__ +recursive-exclude * ._DS_Store +recursive-exclude website * + +recursive-include kotti * + +include docs/Makefile +include docs/_static/README +recursive-include docs *.rst +recursive-include docs *.py +recursive-include docs *.svg +recursive-include docs *.txt +prune docs/_build + diff --git a/Makefile b/Makefile index 09b0c233a..81267aada 100644 --- a/Makefile +++ b/Makefile @@ -1,23 +1,19 @@ -version = 2.7 -python = python$(version) +test: bin/pytest + bin/pytest -test: bin/py.test - bin/py.test -q -n4 - -bin/py.test: .pip.log *.py *.cfg - bin/python setup.py dev +bin/pytest: .pip.log *.py *.cfg + bin/pip install -e ".[testing]" @touch $@ -.pip.log: bin/python requirements.txt - bin/pip install -r requirements.txt --log .pip.log +.pip.log: bin/python + bin/pip install -e ".[development]" --log .pip.log bin/python: - virtualenv-$(version) --no-site-packages --distribute . + python -m venv . @touch $@ clean: - @rm -rfv bin/ include/ lib/ + @rm -rfv bin/ include/ lib/ share/ .Python .cache .eggs Kotti.db Kotti.egg-info .tox .PHONY: test clean - diff --git a/README.rst b/README.rst index 84a7490c4..a175c7c64 100644 --- a/README.rst +++ b/README.rst @@ -2,8 +2,31 @@ Kotti ===== -A user-friendly, light-weight and extensible web content management -system, based on Pyramid and SQLAlchemy. |build status|_ +|pypi|_ +|license|_ + +|build_status_stable_postgresql|_ +|build_status_stable_mysql|_ +|build_status_stable_sqlite|_ + +.. |pypi| image:: https://img.shields.io/pypi/v/Kotti.svg?style=flat-square +.. _pypi: https://pypi.python.org/pypi/Kotti/ + +.. |license| image:: https://img.shields.io/pypi/l/Kotti.svg?style=flat-square +.. _license: http://www.repoze.org/LICENSE.txt + +.. |build_status_stable_postgresql| image:: https://github.com/Kotti/Kotti/workflows/PostgreSQL/badge.svg?branch=stable +.. _build_status_stable_postgresql: https://github.com/Kotti/Kotti/actions?query=workflow%3APostgreSQL+branch%3Astable + +.. |build_status_stable_mysql| image:: https://github.com/Kotti/Kotti/workflows/MySQL/badge.svg?branch=stable +.. _build_status_stable_mysql: https://github.com/Kotti/Kotti/actions?query=workflow%3AMySQL+branch%3Astable + +.. |build_status_stable_sqlite| image:: https://github.com/Kotti/Kotti/workflows/SQLite/badge.svg?branch=stable +.. _build_status_stable_sqlite: https://github.com/Kotti/Kotti/actions?query=workflow%3ASQLite+branch%3Astable + + +Kotti is a high-level, Pythonic web application framework based on Pyramid_ and SQLAlchemy_. +It includes an extensible Content Management System called the Kotti CMS (see below). Kotti is most useful when you are developing applications that @@ -11,30 +34,29 @@ Kotti is most useful when you are developing applications that - use workflows, and/or - work with hierarchical data. -Built on top of a number of *best-of-breed* software components, most -notably Pyramid_ and SQLAlchemy_, Kotti introduces only a few concepts -of its own, thus hopefully keeping the learning curve flat for the -developer. +Built on top of a number of *best-of-breed* software components, +most notably Pyramid_ and SQLAlchemy_, +Kotti introduces only a few concepts of its own, +thus hopefully keeping the learning curve flat for the developer. + -.. |build status| image:: https://secure.travis-ci.org/Kotti/Kotti.png?branch=master -.. _build status: http://travis-ci.org/Kotti/Kotti .. _Pyramid: http://docs.pylonsproject.org/projects/pyramid/dev/ .. _SQLAlchemy: http://www.sqlalchemy.org/ Kotti CMS ========= -You can **try out Kotti** on `Kotti's demo page`_. +.. You can **try out the Kotti CMS** on `Kotti's demo page`_. -Kotti CMS is a content management system that's heavily inspired by -Plone_. Its **main features** are: +Kotti CMS is a content management system that's heavily inspired by Plone_. +Its **main features** are: - **User-friendliness**: editors can edit content where it appears; thus the edit interface is contextual and intuitive - **WYSIWYG editor**: includes a rich text editor -- **Responsive design**: Kotti builds on `Twitter Bootstrap`_, which +- **Responsive design**: Kotti builds on `Bootstrap`_, which looks good both on desktop and mobile - **Templating**: you can extend the CMS with your own look & feel @@ -52,16 +74,83 @@ Plone_. Its **main features** are: .. _Kotti's demo page: http://kottidemo.danielnouri.org/ .. _Plone: http://plone.org/ -.. _Twitter Bootstrap: http://twitter.github.com/bootstrap/ +.. _Bootstrap: http://getbootstrap.com/ + +License +======= + +Kotti is offered under the BSD-derived `Repoze Public License `_. + +Install +======= + +See `installation instructions`_. + +.. _installation instructions: https://kotti.readthedocs.io/en/latest/first_steps/installation.html Support and Documentation ========================= -`Click here to access Kotti's full documentation -`_ +Read Kotti's extensive `documentation `_ on `Read the Docs `_. -License -======= +If you have questions or need help, you can post on our `mailing list / forum `_ or join us on IRC: `#kotti on irc.freenode.net `_. + +If you think you found a bug, open an issue on our `Github bugtracker `_. + +Development +=========== + +|build_status_master_postgresql|_ +|build_status_master_mysql|_ +|build_status_master_sqlite|_ + +|coveralls|_ +|codacy|_ +|codeclimate|_ +|scrutinizer|_ +|requires.io|_ + +|gh_forks|_ +|gh_stars|_ + +Kotti is actively developed and maintained. +We adhere to `high quality coding standards`_, have an extensive test suite with `high coverage`_ and use `continuous integration`_. + +Contributions are always welcome, read our `contribution guidelines`_ and visit our `Github repository`_. + +.. _continuous integration: http://travis-ci.org/Kotti/Kotti + +.. |build_status_master_postgresql| image:: https://github.com/Kotti/Kotti/workflows/PostgreSQL/badge.svg?branch=master +.. _build_status_master_postgresql: https://github.com/Kotti/Kotti/actions?query=workflow%3APostgreSQL+branch%3Amaster + +.. |build_status_master_mysql| image:: https://github.com/Kotti/Kotti/workflows/MySQL/badge.svg?branch=master +.. _build_status_master_mysql: https://github.com/Kotti/Kotti/actions?query=workflow%3AMySQL+branch%3Amaster + +.. |build_status_master_sqlite| image:: https://github.com/Kotti/Kotti/workflows/SQLite/badge.svg?branch=master +.. _build_status_master_sqlite: https://github.com/Kotti/Kotti/actions?query=workflow%3ASQLite+branch%3Amaster + +.. |requires.io| image:: https://img.shields.io/requires/github/Kotti/Kotti.svg?style=flat-square +.. _requires.io: https://requires.io/github/Kotti/Kotti/requirements/?branch=master + +.. |gh_forks| image:: https://img.shields.io/github/forks/Kotti/Kotti.svg?style=flat-square +.. _gh_forks: https://github.com/Kotti/Kotti/network + +.. |gh_stars| image:: https://img.shields.io/github/stars/Kotti/Kotti.svg?style=flat-square +.. _gh_stars: https://github.com/Kotti/Kotti/stargazers + +.. |coveralls| image:: https://img.shields.io/coveralls/Kotti/Kotti.svg?style=flat-square +.. _coveralls: https://coveralls.io/r/Kotti/Kotti +.. _high coverage: https://coveralls.io/r/Kotti/Kotti + +.. |codacy| image:: https://api.codacy.com/project/badge/Grade/fb10cbc3497148d2945d61ce6ad2e4f5 +.. _codacy: https://www.codacy.com/app/disko/Kotti?utm_source=github.com&utm_medium=referral&utm_content=Kotti/Kotti&utm_campaign=Badge_Grade +.. _high quality coding standards: https://www.codacy.com/app/disko/Kotti?utm_source=github.com&utm_medium=referral&utm_content=Kotti/Kotti&utm_campaign=Badge_Grade + +.. |codeclimate| image:: https://api.codeclimate.com/v1/badges/3a4a61548fcc195e4ba1/maintainability +.. _codeclimate: https://codeclimate.com/github/Kotti/Kotti/maintainability + +.. |scrutinizer| image:: https://scrutinizer-ci.com/g/Kotti/Kotti/badges/quality-score.png?b=master +.. _scrutinizer: https://scrutinizer-ci.com/g/Kotti/Kotti/ -Kotti is offered under the BSD-derived `Repoze Public License -`_. +.. _contribution guidelines: https://kotti.readthedocs.io/en/latest/contributing.html +.. _Github repository: https://github.com/Kotti/Kotti diff --git a/app.ini b/app.ini index 12a836337..f6c0b22fb 100644 --- a/app.ini +++ b/app.ini @@ -7,37 +7,31 @@ use = egg:kotti # kotti.site_title: your site's title kotti.site_title = Yet another Kotti site - # kotti.secret: defines the initial password of the 'admin' user kotti.secret = qwerty - # sqlalchemy.url: the database that you want to use, defaults to SQLite # see http://www.sqlalchemy.org/docs/core/engines.html#database-urls sqlalchemy.url = sqlite:///%(here)s/Kotti.db - # mail.default_sender: configure the 'from' address of emails sent by Kotti mail.default_sender = yourname@yourhost - -# kotti.includes: include add-ons; an example: -# kotti.includes = kotti_twitter.include_search_widget - - # kotti.configurators: another hook for add-ons # kotti_tinymce should always be present (unless you don't want WYSIWYG editing) -kotti.configurators = kotti_tinymce.kotti_configure - +kotti.configurators = +# kotti_tinymce is optional in Kotti 2.x +# kotti_tinymce.kotti_configure +# kotti_image was part of kotti in 1.x. +# install with pip and add it to kotti.configurators: +# kotti_image.kotti_configure # kotti.asset_overrides: override Kotti's templates and resources; an example: # kotti.asset_overrides = mypackage:kotti-overrides/ - # pyramid.default_locale_name: set the user interface language pyramid.default_locale_name = en - pyramid.includes = pyramid_tm # pyramid_debugtoolbar diff --git a/development.ini b/development.ini index 76ed43710..61cba39eb 100644 --- a/development.ini +++ b/development.ini @@ -14,10 +14,19 @@ pyramid.default_locale_name = en sqlalchemy.url = sqlite:///%(here)s/Kotti.db #mail.default_sender = yourname@yourhost -kotti.configurators = kotti_tinymce.kotti_configure +kotti.configurators = + kotti_tinymce.kotti_configure +# kotti_image was part of kotti in 1.x. +# install with pip and add it to kotti.configurators: +# kotti_image.kotti_configure + kotti.site_title = My Kotti site kotti.secret = qwerty +#kotti.depot.0.name = localfs +#kotti.depot.0.backend = depot.io.local.LocalFileStorage +#kotti.depot.0.storage_path = %(here)s/filestore + [filter:fanstatic] use = egg:fanstatic#fanstatic @@ -28,7 +37,6 @@ pipeline = [server:main] use = egg:waitress#main -host = 127.0.0.1 port = 5000 [alembic] @@ -51,12 +59,12 @@ handlers = console [logger_kotti] level = DEBUG -handlers = +handlers = console qualname = kotti [logger_sqlalchemy] -level = INFO -handlers = +level = WARN +handlers = console qualname = sqlalchemy.engine # "level = INFO" logs SQL queries. # "level = DEBUG" logs SQL queries and results. diff --git a/docs/_static/callgraph-served-by-kotti.svg b/docs/_static/callgraph-served-by-kotti.svg new file mode 100644 index 000000000..904235529 --- /dev/null +++ b/docs/_static/callgraph-served-by-kotti.svg @@ -0,0 +1,504 @@ + + + + + + +G + +Generated by Python Call Graph v1.0.1 +http://pycallgraph.slowchop.com +cluster_kotti + +kotti + +cluster___main__ + +__main__ + + +kotti.security.is_user + +kotti.security.is_user +calls: 1 +time: 0.000010s + + +kotti.resources.DefaultRootCache.get_root + +kotti.resources.DefaultRootCache.get_root +calls: 1 +time: 0.466594s + + +kotti.resources.File.__parent__ + +kotti.resources.File.__parent__ +calls: 4 +time: 0.141308s + + +kotti.sqla.MutationList.replacer + +kotti.sqla.MutationList.replacer +calls: 4 +time: 0.000070s + + +kotti.security.<genexpr> + +kotti.security.<genexpr> +calls: 13 +time: 0.000448s + + +kotti.filedepot.Request.uploaded_file_response + +kotti.filedepot.Request.uploaded_file_response +calls: 1 +time: 0.030320s + + +kotti.filedepot.StoredFileResponse.__init__ + +kotti.filedepot.StoredFileResponse.__init__ +calls: 1 +time: 0.007179s + + +kotti.filedepot.Request.uploaded_file_response->kotti.filedepot.StoredFileResponse.__init__ + + +1 + + +kotti.security.list_groups_callback + +kotti.security.list_groups_callback +calls: 1 +time: 0.307940s + + +kotti.security.list_groups_callback->kotti.security.is_user + + +1 + + +kotti.security.list_groups + +kotti.security.list_groups +calls: 1 +time: 0.280317s + + +kotti.security.list_groups_callback->kotti.security.list_groups + + +1 + + +kotti.security.get_principals + +kotti.security.get_principals +calls: 4 +time: 0.000696s + + +kotti.security.list_groups_callback->kotti.security.get_principals + + +1 + + +kotti.get_settings + +kotti.get_settings +calls: 5 +time: 0.000491s + + +kotti.security._cachekey_list_groups_raw + +kotti.security._cachekey_list_groups_raw +calls: 12 +time: 0.000468s + + +kotti.sqla.NestedMutationDict.__init__ + +kotti.sqla.NestedMutationDict.__init__ +calls: 14 +time: 0.000457s + + +kotti.sqla.NestedMutationDict.__init__->kotti.sqla.NestedMutationDict.__init__ + + +7 + + +kotti.util.replacement + +kotti.util.replacement +calls: 19 +time: 0.319137s + + +kotti.util.replacement->kotti.security._cachekey_list_groups_raw + + +12 + + +kotti.security._cachekey_list_groups_ext + +kotti.security._cachekey_list_groups_ext +calls: 3 +time: 0.000088s + + +kotti.util.replacement->kotti.security._cachekey_list_groups_ext + + +3 + + +kotti.security.list_groups_raw + +kotti.security.list_groups_raw +calls: 12 +time: 0.002851s + + +kotti.util.replacement->kotti.security.list_groups_raw + + +12 + + +kotti.security.Principals.__getitem__ + +kotti.security.Principals.__getitem__ +calls: 3 +time: 0.026548s + + +kotti.util.replacement->kotti.security.Principals.__getitem__ + + +3 + + +kotti.security.Principals.<lambda> + +kotti.security.Principals.<lambda> +calls: 4 +time: 0.000025s + + +kotti.util.replacement->kotti.security.Principals.<lambda> + + +4 + + +kotti.security.list_groups_ext + +kotti.security.list_groups_ext +calls: 3 +time: 0.285032s + + +kotti.util.replacement->kotti.security.list_groups_ext + + +3 + + +kotti.util.request_container + +kotti.util.request_container +calls: 19 +time: 0.001191s + + +kotti.util.replacement->kotti.util.request_container + + +19 + + +kotti.filedepot.DBFileStorage.get + +kotti.filedepot.DBFileStorage.get +calls: 1 +time: 0.022635s + + +kotti.resources.DefaultRootCache.root_id + +kotti.resources.DefaultRootCache.root_id +calls: 1 +time: 0.308565s + + +kotti.sqla.coerce + +kotti.sqla.coerce +calls: 40 +time: 0.001440s + + +kotti.sqla.coerce->kotti.sqla.NestedMutationDict.__init__ + + +7 + + +kotti.sqla.MutationList.__init__ + +kotti.sqla.MutationList.__init__ +calls: 7 +time: 0.000101s + + +kotti.sqla.coerce->kotti.sqla.MutationList.__init__ + + +7 + + +kotti.views.file.inline_view + +kotti.views.file.inline_view +calls: 1 +time: 0.030461s + + +kotti.views.file.inline_view->kotti.filedepot.Request.uploaded_file_response + + +1 + + +kotti.resources.Document.__eq__ + +kotti.resources.Document.__eq__ +calls: 24 +time: 0.001784s + + +kotti.security.Document._get_acl + +kotti.security.Document._get_acl +calls: 1 +time: 0.000103s + + +kotti.filedepot.StoredFileResponse._get_type_and_encoding + +kotti.filedepot.StoredFileResponse._get_type_and_encoding +calls: 1 +time: 0.000135s + + +kotti.security.list_groups->kotti.util.replacement + + +1 + + +kotti.filedepot.TweenFactory.__call__ + +kotti.filedepot.TweenFactory.__call__ +calls: 1 +time: 1.253096s + + +kotti.resources.File.__eq__ + +kotti.resources.File.__eq__ +calls: 2 +time: 0.000176s + + +kotti.filedepot.StoredFileResponse.__init__->kotti.filedepot.StoredFileResponse._get_type_and_encoding + + +1 + + +kotti.filedepot.DBStoredFile.public_url + +kotti.filedepot.DBStoredFile.public_url +calls: 1 +time: 0.000007s + + +kotti.filedepot.StoredFileResponse.__init__->kotti.filedepot.DBStoredFile.public_url + + +1 + + +kotti.filedepot.type.generate_etag + +kotti.filedepot.type.generate_etag +calls: 1 +time: 0.000071s + + +kotti.filedepot.StoredFileResponse.__init__->kotti.filedepot.type.generate_etag + + +1 + + +kotti.security.get_principals->kotti.get_settings + + +4 + + +kotti.security.principals_factory + +kotti.security.principals_factory +calls: 4 +time: 0.000035s + + +kotti.security.get_principals->kotti.security.principals_factory + + +4 + + +kotti.sqla.ACLType.process_result_value + +kotti.sqla.ACLType.process_result_value +calls: 14 +time: 0.003174s + + +kotti.sqla.ACLType.process_result_value->kotti.sqla.ACLType.process_result_value + + +7 + + +kotti.sqla.JsonType.process_result_value + +kotti.sqla.JsonType.process_result_value +calls: 8 +time: 0.000935s + + +kotti.resources.DefaultRootCache.__call__ + +kotti.resources.DefaultRootCache.__call__ +calls: 1 +time: 0.466665s + + +kotti.resources.DefaultRootCache.__call__->kotti.resources.DefaultRootCache.get_root + + +1 + + +kotti.resources.Document.__parent__ + +kotti.resources.Document.__parent__ +calls: 9 +time: 0.128562s + + +kotti.filedepot.UploadedFileField.patched_processed_result_value + +kotti.filedepot.UploadedFileField.patched_processed_result_value +calls: 1 +time: 0.000571s + + +kotti.security.list_groups_raw->kotti.security.<genexpr> + + +13 + + +kotti.security.list_groups_ext->kotti.sqla.MutationList.replacer + + +2 + + +kotti.security.list_groups_ext->kotti.util.replacement + + +14 + + +kotti.security.list_groups_ext->kotti.security.get_principals + + +3 + + +kotti.security.File._get_acl + +kotti.security.File._get_acl +calls: 1 +time: 0.000097s + + +kotti.resources.get_root + +kotti.resources.get_root +calls: 1 +time: 0.466963s + + +kotti.resources.get_root->kotti.get_settings + + +1 + + +kotti.resources.get_root->kotti.resources.DefaultRootCache.__call__ + + +1 + + +kotti.filedepot.adjust_for_engine + +kotti.filedepot.adjust_for_engine +calls: 1 +time: 0.000034s + + +kotti.resources.Document.__getitem__ + +kotti.resources.Document.__getitem__ +calls: 3 +time: 0.416480s + + +__main__ + +__main__ +calls: 1 +time: 0.000000s + + +__main__->kotti.filedepot.TweenFactory.__call__ + + +1 + + + diff --git a/docs/_static/callgraph-served-by-tween.svg b/docs/_static/callgraph-served-by-tween.svg new file mode 100644 index 000000000..8274d53cd --- /dev/null +++ b/docs/_static/callgraph-served-by-tween.svg @@ -0,0 +1,121 @@ + + + + + + +G + +Generated by Python Call Graph v1.0.1 +http://pycallgraph.slowchop.com +cluster_kotti + +kotti + +cluster___main__ + +__main__ + + +kotti.filedepot.TweenFactory.__call__ + +kotti.filedepot.TweenFactory.__call__ +calls: 1 +time: 0.044785s + + +kotti.filedepot.DBStoredFile.public_url + +kotti.filedepot.DBStoredFile.public_url +calls: 2 +time: 0.000010s + + +kotti.filedepot.TweenFactory.__call__->kotti.filedepot.DBStoredFile.public_url + + +1 + + +kotti.filedepot.StoredFileResponse.__init__ + +kotti.filedepot.StoredFileResponse.__init__ +calls: 1 +time: 0.004096s + + +kotti.filedepot.TweenFactory.__call__->kotti.filedepot.StoredFileResponse.__init__ + + +1 + + +kotti.filedepot.DBFileStorage.get + +kotti.filedepot.DBFileStorage.get +calls: 1 +time: 0.039316s + + +kotti.filedepot.TweenFactory.__call__->kotti.filedepot.DBFileStorage.get + + +1 + + +kotti.filedepot.StoredFileResponse.__init__->kotti.filedepot.DBStoredFile.public_url + + +1 + + +kotti.filedepot.StoredFileResponse._get_type_and_encoding + +kotti.filedepot.StoredFileResponse._get_type_and_encoding +calls: 1 +time: 0.000050s + + +kotti.filedepot.StoredFileResponse.__init__->kotti.filedepot.StoredFileResponse._get_type_and_encoding + + +1 + + +kotti.filedepot.type.generate_etag + +kotti.filedepot.type.generate_etag +calls: 1 +time: 0.000063s + + +kotti.filedepot.StoredFileResponse.__init__->kotti.filedepot.type.generate_etag + + +1 + + +kotti.filedepot.adjust_for_engine + +kotti.filedepot.adjust_for_engine +calls: 1 +time: 0.000040s + + +__main__ + +__main__ +calls: 1 +time: 0.000000s + + +__main__->kotti.filedepot.TweenFactory.__call__ + + +1 + + + diff --git a/docs/api/index.rst b/docs/api/index.rst index 99045ac76..54aede10a 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -17,10 +17,13 @@ API Documentation kotti.populate kotti.request kotti.resources + kotti.filedepot + kotti.sanitizers kotti.security kotti.sqla kotti.testing kotti.tests + kotti.traversal kotti.util kotti.views/index kotti.workflow diff --git a/docs/api/kotti.events.rst b/docs/api/kotti.events.rst index 2e4e60548..2cf951c82 100644 --- a/docs/api/kotti.events.rst +++ b/docs/api/kotti.events.rst @@ -5,4 +5,4 @@ kotti.events .. automodule:: kotti.events :members: - + :member-order: bysource diff --git a/docs/api/kotti.fanstatic.rst b/docs/api/kotti.fanstatic.rst index 46eb8d3eb..ab42158df 100644 --- a/docs/api/kotti.fanstatic.rst +++ b/docs/api/kotti.fanstatic.rst @@ -5,4 +5,4 @@ kotti.fanstatic .. automodule:: kotti.fanstatic :members: - + :member-order: bysource diff --git a/docs/api/kotti.filedepot.rst b/docs/api/kotti.filedepot.rst new file mode 100644 index 000000000..916745900 --- /dev/null +++ b/docs/api/kotti.filedepot.rst @@ -0,0 +1,7 @@ +.. _api-kotti.filedepot: + +kotti.filedepot +--------------- + +.. automodule:: kotti.filedepot + :members: diff --git a/docs/api/kotti.message.rst b/docs/api/kotti.message.rst index 720cecae3..b0418a836 100644 --- a/docs/api/kotti.message.rst +++ b/docs/api/kotti.message.rst @@ -5,3 +5,4 @@ kotti.message .. automodule:: kotti.message :members: + :member-order: bysource diff --git a/docs/api/kotti.migrate.rst b/docs/api/kotti.migrate.rst index 0a6564aff..13f9ee09b 100644 --- a/docs/api/kotti.migrate.rst +++ b/docs/api/kotti.migrate.rst @@ -5,3 +5,4 @@ kotti.migrate .. automodule:: kotti.migrate :members: + :member-order: bysource diff --git a/docs/api/kotti.populate.rst b/docs/api/kotti.populate.rst index 0ed723290..37bf25ba6 100644 --- a/docs/api/kotti.populate.rst +++ b/docs/api/kotti.populate.rst @@ -5,3 +5,4 @@ kotti.populate .. automodule:: kotti.populate :members: + :member-order: bysource diff --git a/docs/api/kotti.request.rst b/docs/api/kotti.request.rst index 446b31c4e..31ae253d5 100644 --- a/docs/api/kotti.request.rst +++ b/docs/api/kotti.request.rst @@ -5,3 +5,4 @@ kotti.request .. automodule:: kotti.request :members: + :member-order: bysource diff --git a/docs/api/kotti.resources.rst b/docs/api/kotti.resources.rst index eb2165d96..1433abc0e 100644 --- a/docs/api/kotti.resources.rst +++ b/docs/api/kotti.resources.rst @@ -5,3 +5,4 @@ kotti.resources .. automodule:: kotti.resources :members: + :member-order: bysource diff --git a/docs/api/kotti.sanitizers.rst b/docs/api/kotti.sanitizers.rst new file mode 100644 index 000000000..991524fa3 --- /dev/null +++ b/docs/api/kotti.sanitizers.rst @@ -0,0 +1,8 @@ +.. _api-kotti.sanitizers: + +kotti.sanitizers +---------------- + +.. automodule:: kotti.sanitizers + :members: + :member-order: bysource diff --git a/docs/api/kotti.security.rst b/docs/api/kotti.security.rst index 918f85333..bdbddfa6d 100644 --- a/docs/api/kotti.security.rst +++ b/docs/api/kotti.security.rst @@ -5,3 +5,4 @@ kotti.security .. automodule:: kotti.security :members: + :member-order: bysource diff --git a/docs/api/kotti.sqla.rst b/docs/api/kotti.sqla.rst index 888d9ca09..2bfecb6bf 100644 --- a/docs/api/kotti.sqla.rst +++ b/docs/api/kotti.sqla.rst @@ -5,3 +5,4 @@ kotti.sqla .. automodule:: kotti.sqla :members: + :member-order: bysource diff --git a/docs/api/kotti.testing.rst b/docs/api/kotti.testing.rst index 4175782ac..0615fd848 100644 --- a/docs/api/kotti.testing.rst +++ b/docs/api/kotti.testing.rst @@ -5,3 +5,4 @@ kotti.testing .. automodule:: kotti.testing :members: + :member-order: bysource diff --git a/docs/api/kotti.tests.rst b/docs/api/kotti.tests.rst index de7d840ff..c69340676 100644 --- a/docs/api/kotti.tests.rst +++ b/docs/api/kotti.tests.rst @@ -5,3 +5,4 @@ kotti.tests .. automodule:: kotti.tests :members: + :member-order: bysource diff --git a/docs/api/kotti.traversal.rst b/docs/api/kotti.traversal.rst new file mode 100644 index 000000000..57492c761 --- /dev/null +++ b/docs/api/kotti.traversal.rst @@ -0,0 +1,8 @@ +.. _api-kotti.traversal: + +kotti.traversal +--------------- + +.. automodule:: kotti.traversal + :members: + :member-order: bysource diff --git a/docs/api/kotti.util.rst b/docs/api/kotti.util.rst index 4bd5b5195..42f2d4681 100644 --- a/docs/api/kotti.util.rst +++ b/docs/api/kotti.util.rst @@ -5,3 +5,4 @@ kotti.util .. automodule:: kotti.util :members: + :member-order: bysource diff --git a/docs/api/kotti.views/index.rst b/docs/api/kotti.views/index.rst index 080d8a043..557a2d971 100644 --- a/docs/api/kotti.views/index.rst +++ b/docs/api/kotti.views/index.rst @@ -5,6 +5,7 @@ kotti.views .. automodule:: kotti.views :members: + :member-order: bysource .. toctree:: :maxdepth: 3 @@ -13,7 +14,6 @@ kotti.views kotti.views.edit/index kotti.views.file kotti.views.form - kotti.views.image kotti.views.login kotti.views.site_setup kotti.views.slots diff --git a/docs/api/kotti.views/kotti.views.cache.rst b/docs/api/kotti.views/kotti.views.cache.rst index d1ab13156..d6f312d8d 100644 --- a/docs/api/kotti.views/kotti.views.cache.rst +++ b/docs/api/kotti.views/kotti.views.cache.rst @@ -5,3 +5,4 @@ kotti.views.cache .. automodule:: kotti.views.cache :members: + :member-order: bysource diff --git a/docs/api/kotti.views/kotti.views.edit/index.rst b/docs/api/kotti.views/kotti.views.edit/index.rst index 94f7c5587..57614d5ab 100644 --- a/docs/api/kotti.views/kotti.views.edit/index.rst +++ b/docs/api/kotti.views/kotti.views.edit/index.rst @@ -5,7 +5,7 @@ kotti.views.edit .. automodule:: kotti.views.edit :members: - + :member-order: bysource .. toctree:: :maxdepth: 3 diff --git a/docs/api/kotti.views/kotti.views.edit/kotti.views.edit.actions.rst b/docs/api/kotti.views/kotti.views.edit/kotti.views.edit.actions.rst index 1e5d1c7bc..a2fa0975c 100644 --- a/docs/api/kotti.views/kotti.views.edit/kotti.views.edit.actions.rst +++ b/docs/api/kotti.views/kotti.views.edit/kotti.views.edit.actions.rst @@ -5,3 +5,4 @@ kotti.views.edit.actions .. automodule:: kotti.views.edit.actions :members: + :member-order: bysource diff --git a/docs/api/kotti.views/kotti.views.edit/kotti.views.edit.content.rst b/docs/api/kotti.views/kotti.views.edit/kotti.views.edit.content.rst index c29b9c973..539569616 100644 --- a/docs/api/kotti.views/kotti.views.edit/kotti.views.edit.content.rst +++ b/docs/api/kotti.views/kotti.views.edit/kotti.views.edit.content.rst @@ -5,3 +5,4 @@ kotti.views.edit.content .. automodule:: kotti.views.edit.content :members: + :member-order: bysource diff --git a/docs/api/kotti.views/kotti.views.edit/kotti.views.edit.default_views.rst b/docs/api/kotti.views/kotti.views.edit/kotti.views.edit.default_views.rst index 06f6afa52..e83536873 100644 --- a/docs/api/kotti.views/kotti.views.edit/kotti.views.edit.default_views.rst +++ b/docs/api/kotti.views/kotti.views.edit/kotti.views.edit.default_views.rst @@ -5,3 +5,4 @@ kotti.views.edit.default_views .. automodule:: kotti.views.edit.default_views :members: + :member-order: bysource diff --git a/docs/api/kotti.views/kotti.views.file.rst b/docs/api/kotti.views/kotti.views.file.rst index d7df2c087..b92882aeb 100644 --- a/docs/api/kotti.views/kotti.views.file.rst +++ b/docs/api/kotti.views/kotti.views.file.rst @@ -5,3 +5,4 @@ kotti.views.file .. automodule:: kotti.views.file :members: + :member-order: bysource diff --git a/docs/api/kotti.views/kotti.views.form.rst b/docs/api/kotti.views/kotti.views.form.rst index d2d5fccd4..a47301825 100644 --- a/docs/api/kotti.views/kotti.views.form.rst +++ b/docs/api/kotti.views/kotti.views.form.rst @@ -5,3 +5,4 @@ kotti.views.form .. automodule:: kotti.views.form :members: + :member-order: bysource diff --git a/docs/api/kotti.views/kotti.views.image.rst b/docs/api/kotti.views/kotti.views.image.rst deleted file mode 100644 index 1869764af..000000000 --- a/docs/api/kotti.views/kotti.views.image.rst +++ /dev/null @@ -1,7 +0,0 @@ -.. _api-kotti.views.image: - -kotti.views.image ------------------ - -.. automodule:: kotti.views.image - :members: diff --git a/docs/api/kotti.views/kotti.views.login.rst b/docs/api/kotti.views/kotti.views.login.rst index e797bda69..ee74f058a 100644 --- a/docs/api/kotti.views/kotti.views.login.rst +++ b/docs/api/kotti.views/kotti.views.login.rst @@ -5,3 +5,4 @@ kotti.views.login .. automodule:: kotti.views.login :members: + :member-order: bysource diff --git a/docs/api/kotti.views/kotti.views.site_setup.rst b/docs/api/kotti.views/kotti.views.site_setup.rst index 08b7af745..d3443d38b 100644 --- a/docs/api/kotti.views/kotti.views.site_setup.rst +++ b/docs/api/kotti.views/kotti.views.site_setup.rst @@ -5,4 +5,4 @@ kotti.views.site_setup .. automodule:: kotti.views.site_setup :members: - + :member-order: bysource diff --git a/docs/api/kotti.views/kotti.views.slots.rst b/docs/api/kotti.views/kotti.views.slots.rst index 71e9ede0b..6e7ba93e6 100644 --- a/docs/api/kotti.views/kotti.views.slots.rst +++ b/docs/api/kotti.views/kotti.views.slots.rst @@ -5,3 +5,4 @@ kotti.views.slots .. automodule:: kotti.views.slots :members: + :member-order: bysource diff --git a/docs/api/kotti.views/kotti.views.users.rst b/docs/api/kotti.views/kotti.views.users.rst index 2a2d74544..9b5abf552 100644 --- a/docs/api/kotti.views/kotti.views.users.rst +++ b/docs/api/kotti.views/kotti.views.users.rst @@ -5,3 +5,4 @@ kotti.views.users .. automodule:: kotti.views.users :members: + :member-order: bysource diff --git a/docs/api/kotti.views/kotti.views.util.rst b/docs/api/kotti.views/kotti.views.util.rst index 9553fd507..c96188800 100644 --- a/docs/api/kotti.views/kotti.views.util.rst +++ b/docs/api/kotti.views/kotti.views.util.rst @@ -5,3 +5,4 @@ kotti.views.util .. automodule:: kotti.views.util :members: + :member-order: bysource diff --git a/docs/api/kotti.views/kotti.views.view.rst b/docs/api/kotti.views/kotti.views.view.rst index 9f442e245..8f89a066b 100644 --- a/docs/api/kotti.views/kotti.views.view.rst +++ b/docs/api/kotti.views/kotti.views.view.rst @@ -5,3 +5,4 @@ kotti.views.view .. automodule:: kotti.views.view :members: + :member-order: bysource diff --git a/docs/api/kotti.workflow.rst b/docs/api/kotti.workflow.rst index fc4008df8..242b79c61 100644 --- a/docs/api/kotti.workflow.rst +++ b/docs/api/kotti.workflow.rst @@ -5,3 +5,4 @@ kotti.workflow .. automodule:: kotti.workflow :members: + :member-order: bysource diff --git a/docs/conf.py b/docs/conf.py index bcf920dc1..48d52e1b5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,45 +11,46 @@ # All configuration values have a default; values that are commented out # serve to show the default. +import os import pkg_resources +from datetime import date # -- General configuration ----------------------------------------------------- -#needs_sphinx = '1.0' extensions = [ 'repoze.sphinx.autointerface', 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', 'sphinx.ext.graphviz', 'sphinx.ext.inheritance_diagram', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', ] -templates_path = ['_templates'] -source_suffix = '.rst' -#source_encoding = 'utf-8-sig' +copyright = u'2012-{0}, Kotti developers'.format(date.today().year) +exclude_patterns = ['_build', '_themes'] master_doc = 'index' project = u'Kotti' -copyright = u'2012-2014, Kotti developers' -version = pkg_resources.get_distribution("Kotti").version +pygments_style = 'sphinx' +source_suffix = '.rst' +templates_path = ['_templates'] +today_fmt = '%Y-%m-%d' # The full version, including alpha/beta/rc tags. +version = pkg_resources.get_distribution("Kotti").version release = version -#language = None -today_fmt = '%Y-%m-%d' -exclude_patterns = ['_build', '_themes'] -#default_role = None -#add_function_parentheses = True -#add_module_names = True -#show_authors = False -pygments_style = 'sphinx' -#modindex_common_prefix = [] +# add_function_parentheses = True +# add_module_names = True +# default_role = None +# language = None +# modindex_common_prefix = [] +# show_authors = False +# source_encoding = 'utf-8-sig' # -- Options for HTML output --------------------------------------------------- # on_rtd is whether we are on readthedocs.org -import os -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' +on_rtd = os.environ.get('READTHEDOCS') == 'True' if not on_rtd: # only import and set the theme if we're building docs locally import sphinx_rtd_theme @@ -58,51 +59,50 @@ # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None -#html_logo = None -#html_favicon = None -html_static_path = ['_static'] html_last_updated_fmt = '%Y-%m-%d' -#html_use_smartypants = True -#html_sidebars = {} -#html_additional_pages = {} -#html_domain_indices = True -#html_use_index = True -#html_split_index = False -#html_show_sourcelink = True -#html_show_sphinx = True -#html_show_copyright = True -#html_use_opensearch = '' -#html_file_suffix = None +html_static_path = ['_static'] htmlhelp_basename = 'Kottidoc' +# html_additional_pages = {} +# html_domain_indices = True +# html_favicon = None +# html_file_suffix = None +# html_logo = None +# html_short_title = None +# html_show_copyright = True +# html_show_sourcelink = True +# html_show_sphinx = True +# html_sidebars = {} +# html_split_index = False +# html_title = None +# html_use_index = True +# html_use_opensearch = '' +# html_use_smartypants = True # -- Options for LaTeX output -------------------------------------------------- -#latex_paper_size = 'a4' -#latex_font_size = '10pt' -#latex_documents = [ -# ('index', 'Kotti.tex', u'Kotti Documentation', -# u'Daniel Nouri', 'manual'), -#] -#latex_logo = None -#latex_use_parts = False -#latex_show_pagerefs = False -#latex_show_urls = False -#latex_preamble = '' -#latex_appendices = [] -#latex_domain_indices = True +# latex_appendices = [] +# latex_documents = [ +# ('index', 'Kotti.tex', u'Kotti Documentation', u'Daniel Nouri', 'manual'), +# ] +# latex_domain_indices = True +# latex_font_size = '10pt' +# latex_logo = None +# latex_paper_size = 'a4' +# latex_preamble = '' +# latex_show_pagerefs = False +# latex_show_urls = False +# latex_use_parts = False # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -#man_pages = [ -# ('index', 'kotti', u'Kotti Documentation', [u'Daniel Nouri'], 1) -#] +# man_pages = [ +# ('index', 'kotti', u'Kotti Documentation', [u'Daniel Nouri'], 1) +# ] # -- Options for inheritance diagrams ------------------------------------------ inheritance_graph_attrs = dict( @@ -120,9 +120,11 @@ # -- Options for Intersphinx --------------------------------------------------- intersphinx_mapping = { - 'colander': ('http://colander.readthedocs.org/en/latest/', None), - 'deform': ('http://deform.readthedocs.org/en/latest/', None), + 'bleach': ('https://bleach.readthedocs.io/en/latest/', None), + 'colander': ('https://colander.readthedocs.io/en/latest/', None), + 'deform': ('https://deform.readthedocs.io/en/latest/', None), + 'depot': ('https://depot.readthedocs.io/en/latest/', None), 'fanstatic': ('http://www.fanstatic.org/en/latest/', None), - 'pyramid': ('http://pyramid.readthedocs.org/en/latest/', None), - 'sqlalchemy': ('http://sqlalchemy.readthedocs.org/en/latest/', None), + 'pyramid': ('https://pyramid.readthedocs.io/en/latest/', None), + 'sqlalchemy': ('https://sqlalchemy.readthedocs.io/en/latest/', None), } diff --git a/docs/contributing.rst b/docs/contributing.rst index ce0bce9cd..d1ab211ee 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -30,9 +30,9 @@ To run and develop within your clone, do these steps: .. code-block:: bash - virtualenv . --no-site-packages + python3 -m venv . - bin/python setup.py develop + bin/pip install -e ".[testing]" This will create a new virtualenv "in place" and do the python develop steps to use the Kotti code in the repo. @@ -84,9 +84,9 @@ The rendered docs are built and hosted on readthedocs.org. .. _blockdiag: http://blockdiag.com .. _GitHub fork-a-repo help: https://help.github.com/articles/fork-a-repo .. _Kotti User Manual repo: https://github.com/Kotti/kotti_user_manual -.. _Kotti User Manual: https://kotti-user-manual.readthedocs.org +.. _Kotti User Manual: https://kotti-user-manual.readthedocs.io .. _Kotti: github.com/Kotti/Kotti .. _reStructuredText: http://sphinx-doc.org/rest.html -.. _Selenium: http://selenium-python.readthedocs.org -.. _Sphinx tool: http://sphinx.readthedocs.org -.. _virtualenvwrapper system: http://virtualenvwrapper.readthedocs.org +.. _Selenium: https://selenium-python.readthedocs.io +.. _Sphinx tool: https://sphinx.readthedocs.io +.. _virtualenvwrapper system: https://virtualenvwrapper.readthedocs.io diff --git a/docs/developing/advanced/add-to-edit-interface.rst b/docs/developing/advanced/add-to-edit-interface.rst new file mode 100644 index 000000000..0a126060a --- /dev/null +++ b/docs/developing/advanced/add-to-edit-interface.rst @@ -0,0 +1,72 @@ +.. _add-to-edit-interface: + +Adding links and actions to the edit interface +============================================== + +This document covers how to customize the available links and actions of the edit interface (the extra tabs and menus that appear after you log in). + +The basic building block is the link, ``kotti.util.Link``. Instantiate it as: + +.. code-block:: python + + link = Link('name', _(u'Title')) + +The name refers to a view name available on the context. + +There's also: + + * ``kotti.util.LinkParent``, which allows grouping of links + * ``kotti.util.LinkRenderer``, which, instead of generating a simple link, allows you to customize how it's rendered (you can insert anything there, even another submenu based on a ``LinkParent``). + * ``kotti.util.ActionButton``, very similar to a simple link, but generates a button instead. + +Adding a new option to the Administration menu +---------------------------------------------- + +Adding a new link as an option in the **Administration** menu, in the *Site Setup* section is easy. In your ``kotti_configure`` function, add: + +.. code-block:: python + + from kotti.util import Link + from kotti.views.site_setup import CONTROL_PANEL_LINKS + + def kotti_configure(settings): + link = Link('name', _(u'Title')) + CONTROL_PANEL_LINKS.append(link) + +Make a new section in the actions menu +-------------------------------------- +The *Set default view* section looks really nice. To add your own separated section in the **Action** menu and make that available to all content types: + +.. code-block:: python + + from kotti.util import LinkRenderer + from kotti.resources import default_actions + + def kotti_configure(settings): + default_actions.append(LinkRenderer("my-custom-submenu")) + +So far we've added a ``LinkRenderer`` to the ``default_actions`` which are used by all content inheriting ``Content``. This LinkRenderer will render a view and insert its result in the menu. + +.. code-block:: python + + @view_config( + name="my-custom-submenu", permission="edit", + renderer="mypackage:templates/edit/my-custom-submenu.pt") + def my_custom_submenu(context, request): + return {} + +And the template: + +.. code-block:: html + + +
  • + +
  • + + View title here + +
  • +
    >> from kotti.resources import File + >>> file = File.query.filter(File.name == 'file').one() + >>> request.resource_url(file, 'attachment-view') + 'http://localhost/foo/bar/file/attachment-view' + +When this URL is requested, a :class:`kotti.filedepot.StoredFileResponse` is returned:: + + >>> request.uploaded_file_response(file.data) + + +The request is processed in the same way as for every other type of content in Kotti. +It goes through the full traversal and view lookup machinery *with full permission checks*. + +Method 2 +~~~~~~~~ + +Often these permission checks do not need to be enforced strictly. +For such cases Kotti provides a "shortcut" in form of a Pyramid tween, that directly processes all requests under a certain path before they even reach Kotti. +This means: no traversal, no view lookup, no permission checks. +The URL for this method can be created very similarily:: + + >>> request.uploaded_file_url(file.data, 'attachment') + 'http://localhost//depot/dbfiles/68f31e97-a7f9-11e5-be07-c82a1403e6a7/download' + +Comparison +~~~~~~~~~~ + +Obviously method 2 is *a lot* faster than method 1 - typically at least by the factor of 3. + +If you take a look at the callgraphs, you'll understand where this difference comes from: + +========== ========== +|m1kotti|_ |m2kotti|_ +========== ========== + Method 1 Method 2 +========== ========== + +.. |m1kotti| image:: /_static/callgraph-served-by-kotti.svg + :width: 100% +.. _m1kotti: ../../_static/callgraph-served-by-kotti.svg +.. |m2kotti| image:: /_static/callgraph-served-by-tween.svg + :width: 100% +.. _m2kotti: ../../_static/callgraph-served-by-tween.svg + +The difference will be even more drastic, when you set up proper HTTP caching. +All responses for method 2 can be cached *forever*, because the URL will change when the file's content changes. + +Developing (with) File-like Content +----------------------------------- + +Add a Blob Field to your Model +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Adding a blob data attribute to your models can be as simple as:: + + from depot.fields.sqlalchemy import UploadedFileField + from kotti.resources import Content + + class Person(Content): + avatar = UploadedFileField() + +While you can directly assign a ``bytes`` value to the ``avatar`` column, the ``UploadedFileField`` column type works best when you assign a :class:`cgi.FieldStorage` instance as value:: + + from StringIO import StringIO + from kotti.util import _to_fieldstorage + + content = '...' + data = { + 'fp': StringIO(content), + 'filename': 'avatar.png', + 'mimetype': 'image/png', + 'size': len(content), + } + person = Person() + person.avatar = _to_fieldstorage(**data) + +Note that the ``data`` dictionary described here has the same format as the deserialized value of a ``deform.widget.FileUploadWidget``. +See :class:`kotti.views.edit.content.FileAddForm` and :class:`kotti.views.edit.content.FileEditForm` for a full example of how to add or edit a model with a blob field. + +Reading Blob Data +~~~~~~~~~~~~~~~~~ + +If you try directly to read data from an ``UploadedFileField`` you'll get a :class:`depot.fields.upload.UploadedFile` instance, which offers a dictionary-like interface to the stored file metadata and direct access to a stream with the stored file through the ``file`` attribute:: + + person = DBSession.query(Person).get(1) + blob = person.avatar.file.read() + +You should never write to the file stream directly. +Instead, you should assign a new value to the ``UploadedFileField`` column, as described in the previous section. + +Testing UploadedFileField Columns +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Because :class:`depot.manager.DepotManager` acts as a singleton, special care needs to be taken when testing features that involve saving data into ``UploadedFileField`` columns. + +``UploadedFileField`` columns require having at least one depot file storage configured. +You can use a fixture called ``filedepot`` to have a mock file storage available for your tests. + +If you're developing new depot file storages you should use the ``no_filedepots`` fixture, which resets the configured depots for the test run and restores the default depots back, as a teardown. + +Inheritance Issues with UploadedFileField Columns +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You should be aware that, presently, subclassing a model with an ``UploadedFileField`` column doesn't work properly. +As a workaround, add a ``__declare_last__`` classmethod in your superclass model, similar to the one below, where we're fixing the ``data`` column of the ``File`` class. :: + + from depot.fields.sqlalchemy import _SQLAMutationTracker + + class File(Content): + + data = UploadedFileField() + + @classmethod + def __declare_last__(cls): + event.listen(cls.data, 'set', _SQLAMutationTracker._field_set, retval=True) + + +Migrating data between two different storages +--------------------------------------------- + +Kotti provides a script that can migrate blob data from one configured stored to another and update the saved fields with the new locations. +It is not needed to do this if you just want to add a new torage, or replace the default one, but you can use it if you'd like to consolidate the blob data in one place only. +You can invoke the script with:: + + kotti-migrate-storage --from-storage --to-storage + +The storage names are those assigned in the configuration file designated in ````. +For example, let's assume you've started a website that has the default blob storage, the ``DBFileStorage`` named *dbfiles*. +You'd like to move all the existing blob data to a :class:`depot.io.local.LocalFileStorage` storage and make that the default. +First, add the ``LocalFileStorage`` depot, make it the default and place the old ``DBFileStorage`` in position *1*::: + + kotti.depot.0.backend = depot.io.local.LocalFileStorage + kotti.depot.0.name = localfs + kotti.depot.0.storage_path = /var/local/files + kotti.depot.1.backend = kotti.filedepot.DBFileStorage + kotti.depot.1.name = dbfiles + +Now you can invoke the migration with::: + + kotti-migrate-storage --from-storage dbfiles --to-storage localfs + +As always when dealing with migrations, make sure you backup your data first! + + +.. _Depot: https://depot.readthedocs.io/en/latest/ diff --git a/docs/developing/advanced/close-for-anonymous.rst b/docs/developing/advanced/close-to-anonymous.rst similarity index 87% rename from docs/developing/advanced/close-for-anonymous.rst rename to docs/developing/advanced/close-to-anonymous.rst index bd91f79f7..f42deefbf 100644 --- a/docs/developing/advanced/close-for-anonymous.rst +++ b/docs/developing/advanced/close-to-anonymous.rst @@ -1,7 +1,7 @@ -.. _close-for-anonymous: +.. _close-to-anonymous: -Close your site for anonymous users -=================================== +Close your site to anonymous users +================================== This recipe describes how to configure Kotti to require users to log in before they can view any of your site's pages. diff --git a/docs/developing/advanced/images.rst b/docs/developing/advanced/images.rst index 44f95976b..dd728cbdc 100644 --- a/docs/developing/advanced/images.rst +++ b/docs/developing/advanced/images.rst @@ -1,20 +1,8 @@ .. _images: -Image URLs -========== +Images +====== -Kotti provides on-the-fly image scaling by utilizing `plone.scale`_. +All image related functions were moved to `kotti_image`_ as of Kotti 1.3.0. -Images can be referenced by this URL schema: ``/path/to/image_content_object/image[/]/download]`` where ```` is a predefined image scale (see below). - -If the last URL path segment is ``download``, the image will be served with ``Content-disposition: attachment`` otherwise it will be served with ``Content-disposition: inline``. - -Predefined image scale sizes ----------------------------- - -You may define image scale sizes in your ``.ini`` file by setting values for ``kotti.image_scales.`` to values of the form ``x`` (e.g. ``kotti.image_scales.thumb = 160x120`` with the resulting scale name ``thumb``). - -``span1`` (60x120) to ``span12`` (1160x2320) are always defined (with values corresponding to the Twitter Bootstrap default grid sizes), but their values can be overwritten by setting ``kotti.image_scales.span`` to different values in your .ini file. - - -.. _plone.scale: http://packages.python.org/plone.scale/ +.. _kotti_image: https://pypi.python.org/pypi/kotti_image diff --git a/docs/developing/advanced/index.rst b/docs/developing/advanced/index.rst index cec0acc14..c45af45b2 100644 --- a/docs/developing/advanced/index.rst +++ b/docs/developing/advanced/index.rst @@ -7,10 +7,13 @@ Advanced Topics :maxdepth: 1 as-a-library - close-for-anonymous + close-to-anonymous default-views + add-to-edit-interface events frontpage-different-template images + blobs static-resource-management understanding-kotti-startup + sanitizers diff --git a/docs/developing/advanced/sanitizers.rst b/docs/developing/advanced/sanitizers.rst new file mode 100644 index 000000000..a14b4117f --- /dev/null +++ b/docs/developing/advanced/sanitizers.rst @@ -0,0 +1,73 @@ +.. _sanitizers: + +Sanitizers +========== + +Kotti provides a mechanism to *sanitize* arbitrary strings. + +You can configure *available* sanitizers via ``kotti.sanitizers``. +This setting takes a list of strings, with each specifying a ``name:callable`` pair. +``name`` is the name under which this sanitizer is registered. +``callable`` is a dotted path to a function taking an unsanitized string and returning a sanitized version of it. + +The default configuration is:: + + kotti.sanitizers = + xss_protection:kotti.sanitizers.xss_protection + minimal_html:kotti.sanitizers.minimal_html + no_html:kotti.sanitizers.no_html + +For thorough explaination of the included sanitizers see :mod:`kotti.sanitizers`. + +Explicit sanitization +--------------------- + +You can explicitly use any configured sanitizer like this:: + + from kotti.sanitizers import sanitize + + sanitzed = sanitize(unsanitized, 'xss_protection') + +The sanitize function is also available as a method of the :class:`kotti.views.util.TemplateAPI`. +This is just a convenience wrapper to ease usage in templates:: + + ${api.sanitize(context.foo, 'minimal_html')} + +Sanitize on write (implicit sanitization) +----------------------------------------- + +The second setting related to sanitization is ``kotti.sanitize_on_write``. +It defines, for the specified resource classes, the attributes that are sanitized and the sanitizers that will be used when the attributes are mutated and flushed. + +This setting takes a list of ``dotted_path:sanitizer_name(s)`` pairs. +``dotted_path`` is a dotted path to a resource class attribute that will be sanitized implicitly with the respective sanitizer(s) upon write access. +``sanitizer_name(s)`` is a comma separated list of available sanitizer names as configured above. + +Kotti will setup :ref:`listeners ` for the :class:`kotti.events.ObjectInsert` and :class:`kotti.events.ObjectUpdate` events for the given classes and attach a function that filters the respective attributes with the specified sanitizer. + +This means that *any* write access to configured attributes through your application (also within correctly setup command line scripts) will be sanitized *implicitly*. + +The default configuration is:: + + kotti.sanitize_on_write = + kotti.resources.Document.body:xss_protection + kotti.resources.Content.title:no_html + +You can also use multiple sanitizers:: + + kotti.sanitize_on_write = + kotti.resources.Document.body:xss_protection,some_other_sanitizer + +Implementing a custom sanitizer +------------------------------- + +A sanitizer is just a function that takes and returns a string. +It can be as simple as:: + + def no_dogs_allowed(html): + return html.replace('dogs', 'cats') + + no_dogs_allowed('

    I love dogs.

    ') + ... '

    I love cats.

    ' + +You can also look at :mod:`kotti.sanitizers` for examples. diff --git a/docs/developing/advanced/static-resource-management.rst b/docs/developing/advanced/static-resource-management.rst index c12d31da0..6b541441a 100644 --- a/docs/developing/advanced/static-resource-management.rst +++ b/docs/developing/advanced/static-resource-management.rst @@ -133,5 +133,5 @@ fanstatic: .. _Fanstatic: http://www.fanstatic.org/ -.. _as described in the corresponding Fanstatic documentation: http://fanstatic.readthedocs.org/en/latest/library.html -.. _Fanstatic's documentation: http://fanstatic.readthedocs.org/ +.. _as described in the corresponding Fanstatic documentation: https://fanstatic.readthedocs.io/en/latest/library.html +.. _Fanstatic's documentation: https://fanstatic.readthedocs.io/ diff --git a/docs/developing/basic/configuration.rst b/docs/developing/basic/configuration.rst index f56a73314..4782c3def 100644 --- a/docs/developing/basic/configuration.rst +++ b/docs/developing/basic/configuration.rst @@ -8,10 +8,9 @@ Configuration INI File -------- -Kotti is configured using an INI configuration file. The -:ref:`installation` section explains how to get hold of a sample -configuration file. The ``[app:kotti]`` section in it might look like -this: +Kotti is configured using an INI configuration file. +The :ref:`installation` section explains how to get hold of a sample configuration file. +The ``[app:kotti]`` section in it might look like this: .. code-block:: ini @@ -35,68 +34,66 @@ Various aspects of your site can be changed right here. Overview of settings -------------------- -This table provides an overview of available settings. All these -settings must go into the ``[app:kotti]`` section of your Paste Deploy -configuration file. - -============================ ================================================== -Setting Description -============================ ================================================== -**kotti.site_title** The title of your site -**kotti.secret** Secret token used for the initial admin password -kotti.secret2 Secret token used for email password reset token - -**sqlalchemy.url** `SQLAlchemy database URL`_ -**mail.default_sender** Sender address for outgoing email -mail.host Email host to send from - -pyramid.includes List of Python configuration hooks -kotti.available_types List of active content types -kotti.base_includes List of base Python configuration hooks -kotti.zcml_includes List of packages to include the ZCML from -kotti.configurators List of advanced functions for config -kotti.request_factory Override Kotti's default request factory -kotti.root_factory Override Kotti's default Pyramid *root factory* -kotti.populators List of functions to fill initial database -kotti.search_content Override Kotti's default search function - -kotti.asset_overrides Override Kotti's templates -kotti.templates.api Override ``api`` object available in templates -kotti.fanstatic.view_needed List of static resources used for public interface -kotti.fanstatic.edit_needed List of static resources used for edit interface - -kotti.authn_policy_factory Component used for authentication -kotti.authz_policy_factory Component used for authorization -kotti.session_factory Component used for sessions - -kotti.caching_policy_chooser Component for choosing the cache header policy -kotti.url_normalizer Component used for url normalization - -kotti.date_format Date format to use, default: ``medium`` -kotti.datetime_format Datetime format to use, default: ``medium`` -kotti.time_format Time format to use, default: ``medium`` -kotti.max_file_size Max size for file uploads, default: ```10`` (MB) - -pyramid.default_locale_name Set the user interface language, default ``en`` -============================ ================================================== - -Only the settings in bold letters required. The rest has defaults. - -Do take a look at the required settings (in bold) and adjust them in -your site's configuration. A few of the settings are less important, -and sometimes only used by developers, not integrators. +This table provides an overview of available settings. +All these settings must go into the ``[app:kotti]`` section of your Paste Deploy configuration file. + +Only the settings in bold letters required. +The rest has defaults. + +Do take a look at the required settings (in bold) and adjust them in your site's configuration. +A few of the settings are less important, and sometimes only used by developers, not integrators. + +===================================== ========================================= +Setting Description +===================================== ========================================= +**kotti.site_title** The title of your site +**kotti.secret** Secret token used for the initial admin password +kotti.secret2 Secret token used for email password reset token +**sqlalchemy.url** `SQLAlchemy database URL`_ +**mail.default_sender** Sender address for outgoing email +kotti.asset_overrides Override Kotti's templates +kotti.authn_policy_factory Component used for authentication +kotti.authz_policy_factory Component used for authorization +kotti.available_types List of active content types +kotti.base_includes List of base Python configuration hooks +kotti.caching_policy_chooser Component for choosing the cache header policy +kotti.configurators List of advanced functions for config +kotti.date_format Date format to use, default: ``medium`` +kotti.datetime_format Datetime format to use, default: ``medium`` +kotti.depot_mountpoint Configure the mountpoint for the blob storage. See :ref:`blobs` for details. +kotti.depot_replace_wsgi_file_wrapper Replace you WSGI server's file wrapper with :class:`pyramid.response.FileIter`. +kotti.depot.*.* Configure the blob storage. See :ref:`blobs` for details. +kotti.fanstatic.edit_needed List of static resources used for edit interface +kotti.fanstatic.view_needed List of static resources used for public interface +kotti.login_success_callback Override Kotti's default ``login_success_callback`` function +kotti.max_file_size Max size for file uploads, default: ``10`` (MB) +kotti.modification_date_excludes List of attributes in dotted name notation that should not trigger an update of ``modification_date`` on change +kotti.populators List of functions to fill initial database +kotti.request_factory Override Kotti's default request factory +kotti.reset_password_callback Override Kotti's default ``reset_password_callback`` function +kotti.root_factory Override Kotti's default Pyramid *root factory* +kotti.sanitize_on_write Configure :ref:`sanitizers` to be used on write access to resource objects +kotti.sanitizers Configure available :ref:`sanitizers` +kotti.search_content Override Kotti's default search function +kotti.session_factory Component used for sessions +kotti.templates.api Override ``api`` object available in templates +kotti.time_format Time format to use, default: ``medium`` +kotti.url_normalizer Component used for url normalization +kotti.zcml_includes List of packages to include the ZCML from +mail.host Email host to send from +pyramid.default_locale_name Set the user interface language, default ``en`` +pyramid.includes List of Python configuration hooks +===================================== ========================================= kotti.secret and kotti.secret2 ------------------------------ -The value of ``kotti.secret`` will define the initial password of the -``admin`` user. Thus, if you define ``kotti.secret = mysecret``, the -admin password will be ``mysecret``. Log in and change the password -at any time through the web interface. +The value of ``kotti.secret`` will define the initial password of the ``admin`` user. +Thus, if you define ``kotti.secret = mysecret``, the admin password will be ``mysecret``. +Log in and change the password at any time through the web interface. -The ``kotti.secret`` token is also used for signing browser session -cookies. The ``kotti.secret2`` token is used for signing the password -reset token. +The ``kotti.secret`` token is also used for signing browser session cookies. +The ``kotti.secret2`` token is used for signing the password reset token. Here's an example: @@ -112,21 +109,14 @@ Here's an example: Override templates (``kotti.asset_overrides``) ---------------------------------------------- -In your settings file, set ``kotti.asset_overrides`` to a list of -*asset specifications*. This allows you to set up a directory in your -package that will mirror Kotti's own and that allows you to override -Kotti's templates on a case by case basis. +In your settings file, set ``kotti.asset_overrides`` to a list of *asset specifications*. +This allows you to set up a directory in your package that will mirror Kotti's own and that allows you to override Kotti's templates on a case by case basis. -As an example, image that we wanted to override Kotti's master layout -template. Inside the Kotti source, the layout template is located at -``kotti/templates/view/master.pt``. To override this, we would add a -directory to our own package called ``kotti-overrides`` and therein -put our own version of the template so that the full path to our own -custom template is -``mypackage/kotti-overrides/templates/view/master.pt``. +As an example, image that we wanted to override Kotti's master layout template. +Inside the Kotti source, the layout template is located at ``kotti/templates/view/master.pt``. +To override this, we would add a directory to our own package called ``kotti-overrides`` and therein put our own version of the template so that the full path to our own custom template is ``mypackage/kotti-overrides/templates/view/master.pt``. -We can then register our ``kotti-overrides`` directory by use of the -``kotti.asset_overrides`` setting, like so: +We can then register our ``kotti-overrides`` directory by use of the ``kotti.asset_overrides`` setting, like so: .. code-block:: ini @@ -135,9 +125,8 @@ We can then register our ``kotti-overrides`` directory by use of the Use add-ons ----------- -Add-ons will usually include in their installation instructions which -settings one should modify to activate them. Configuration settings -that are used to activate add-ons are: +Add-ons will usually include in their installation instructions which settings one should modify to activate them. +Configuration settings that are used to activate add-ons are: - ``pyramid.includes`` - ``kotti.available_types`` @@ -149,40 +138,33 @@ that are used to activate add-ons are: pyramid.includes ```````````````` -``pyramid.includes`` defines a list of hooks that will be called when -your Kotti app starts up. This gives the opportunity to third party -packages to add registrations to the *Pyramid Configurator API* in -order to configure views and more. +``pyramid.includes`` defines a list of hooks that will be called when your Kotti app starts up. +This gives the opportunity to third party packages to add registrations to the *Pyramid Configurator API* in order to configure views and more. -Here's an example. Let's install the `kotti_twitter`_ extension and -add a Twitter profile widget to the right column of all pages. First -we install the package from PyPI: +Here's an example. +Let's install the `kotti_twitter`_ extension and add a Twitter profile widget to the right column of all pages. +First we install the package from PyPI: .. code-block:: bash bin/pip install kotti_twitter -Then we activate the add-on in our site by editing the -``pyramid.includes`` setting in the ``[app:kotti]`` section of our INI -file. (If a line with ``pyramid.includes`` does not exist, add it.) +Then we activate the add-on in our site by editing the ``pyramid.includes`` setting in the ``[app:kotti]`` section of our INI file (if a line with ``pyramid.includes`` does not exist, add it). .. code-block:: ini pyramid.includes = kotti_twitter.include_profile_widget -kotti_twitter also asks us to configure the Twitter widget itself, so -we add some more lines right where we were: +kotti_twitter also asks us to configure the Twitter widget itself, so we add some more lines right where we were: .. code-block:: ini kotti_twitter.profile_widget.user = dnouri kotti_twitter.profile_widget.loop = true -The order in which the includes are listed matters. For example, when -you add two slots on the right hand side, the order in which you list -them in ``pyramid.includes`` will control the order in which they will -appear. As an example, here's a configuration with which the search -widget will be displayed above the profile widget: +The order in which the includes are listed matters. +For example, when you add two slots on the right hand side, the order in which you list them in ``pyramid.includes`` will control the order in which they will appear. +As an example, here's a configuration with which the search widget will be displayed above the profile widget: .. code-block:: ini @@ -190,8 +172,7 @@ widget will be displayed above the profile widget: kotti_twitter.include_search_widget kotti_twitter.include_profile_widget -Read more about `including packages using 'pyramid.includes'`_ in -the Pyramid documentation. +Read more about `including packages using 'pyramid.includes'`_ in the Pyramid documentation. .. _including packages using 'pyramid.includes': http://readthedocs.org/docs/pyramid/en/1.3-branch/narr/environment.html#including-packages @@ -200,8 +181,8 @@ the Pyramid documentation. kotti.available_types ````````````````````` -The ``kotti.available_types`` setting defines the list of content -types available. The default configuration here is: +The ``kotti.available_types`` setting defines the list of content types available. +The default configuration here is: .. code-block:: ini @@ -227,25 +208,23 @@ The default configuration here is: kotti.populators = kotti.populate.populate -Populators are functions with no arguments that get called on system -startup. They may then make automatic changes to the database (before -calling ``transaction.commit()``). +Populators are functions with no arguments that get called on system startup. +They may then make automatic changes to the database (before calling ``transaction.commit()``). .. _kotti.search_content: kotti.search_content ```````````````````` -Kotti provides a simple search over the content types based on -kotti.resources.Content. The default configuration here is: +Kotti provides a simple search over the content types based on kotti.resources.Content. +The default configuration here is: .. code-block:: ini kotti.search_content = kotti.views.util.default_search_content -You can provide an own search function in an add-on and register this -in your INI file. The return value of the search function is a list of -dictionaries, each representing a search result: +You can provide an own search function in an add-on and register this in your INI file. +The return value of the search function is a list of dictionaries, each representing a search result: .. code-block:: python @@ -258,17 +237,15 @@ dictionaries, each representing a search result: ... ] -An add-on that defines an alternative search function is -`kotti_solr`_, which provides an integration with the `Solr`_ search -engine. +An add-on that defines an alternative search function is `kotti_solr`_, which provides an integration with the `Solr`_ search engine. .. _user interface language: Configure the user interface language ------------------------------------- -By default, Kotti will display its user interface in English. The -default configuration is: +By default, Kotti will display its user interface in English. +The default configuration is: .. code-block:: ini @@ -286,51 +263,42 @@ The list of available languages is `here Configure authentication and authorization ------------------------------------------ -You can override the authentication and authorization policy that -Kotti uses. By default, Kotti uses these factories: +You can override the authentication and authorization policy that Kotti uses. +By default, Kotti uses these factories: .. code-block:: ini kotti.authn_policy_factory = kotti.authtkt_factory kotti.authz_policy_factory = kotti.acl_factory -These settings correspond to -`pyramid.authentication.AuthTktAuthenticationPolicy`_ and -`pyramid.authorization.ACLAuthorizationPolicy`_ being used. +These settings correspond to `pyramid.authentication.AuthTktAuthenticationPolicy`_ and `pyramid.authorization.ACLAuthorizationPolicy`_ being used. Sessions -------- -The ``kotti.session_factory`` configuration variable allows the -overriding of the default session factory. By default, Kotti uses -``pyramid_beaker`` for sessions. +The ``kotti.session_factory`` configuration variable allows the overriding of the default session factory. +By default, Kotti uses ``pyramid_beaker`` for sessions. Caching ------- -You can override Kotti's default set of cache headers by changing the -``kotti.views.cache.caching_policies`` dictionary, which maps policies -to headers. E.g. the ``Cache Resource`` entry there caches all static -resources for 32 days. You can also choose which responses match to -which caching policy by overriding Kotti's default cache policy -chooser through the use of the ``kotti.caching_policy_chooser`` -configuration variable. The default is: +You can override Kotti's default set of cache headers by changing the ``kotti.views.cache.caching_policies`` dictionary, which maps policies to headers. +E.g. the ``Cache Resource`` entry there caches all static resources for 32 days. +You can also choose which responses match to which caching policy by overriding Kotti's default cache policy chooser through the use of the ``kotti.caching_policy_chooser`` configuration variable. +The default is: .. code-block:: ini kotti.caching_policy_chooser = kotti.views.cache.default_caching_policy_chooser -Url normalization +URL normalization ----------------- -Kotti normalizes document titles to URLs by replacing language specific -characters like umlauts or accented characters with its ascii equivalents. -You can change this default behavour by setting -``kotti.url_normalizer.map_non_ascii_characters`` configuration variable -to ``False``. If you do, Kotti will leave national characters in URLs. +Kotti normalizes document titles to URLs by replacing language specific characters like umlauts or accented characters with its ascii equivalents. +You can change this default behaviour by setting ``kotti.url_normalizer.map_non_ascii_characters`` configuration variable to ``False``. +If you do, Kotti will leave national characters in URLs. -You may also replace default component used for url normalization by setting -``kotti.url_normalizer`` configuation variable. +You may also replace default component used for url normalization by setting ``kotti.url_normalizer`` configuation variable. The default configuration here is: @@ -339,14 +307,11 @@ The default configuration here is: kotti.url_normalzier = kotti.url_normalizer.url_normalizer kotti.url_normalizer.map_non_ascii_characters = True - - Local navigation ---------------- Kotti provides a build in navigation widget, which is disabled by default. -To enable the navigation widget add the following to the ``pyramid.includes`` -setting: +To enable the navigation widget add the following to the ``pyramid.includes`` setting: .. code-block:: ini diff --git a/docs/developing/basic/deployment.rst b/docs/developing/basic/deployment.rst index d12da3431..574b7e37c 100644 --- a/docs/developing/basic/deployment.rst +++ b/docs/developing/basic/deployment.rst @@ -3,8 +3,8 @@ Deployment ========== -Kotti deployment is not different from deploying any other WSGI app. You have -a bunch of options on multiple layers: OS, RDBMS, Webserver, etc. +Kotti deployment is not different from deploying any other WSGI app. +You have a bunch of options on multiple layers: OS, RDBMS, Webserver, etc. This document assumes the following Stack: @@ -94,7 +94,9 @@ Create an ini file in ``/home/kotti/kotti.ini``:: socket = /home/kotti/.sock master = true chmod-socket = 666 - processes = 1 + processes = 2 + lazy = true # needed if want processes > 1 + lazy-apps = true Install Supervisor:: @@ -113,20 +115,19 @@ Reload the supervisor config:: supervisorctl reload -That's all. Your Kotti deployment should now happily serve pages. +That's all. +Your Kotti deployment should now happily serve pages. Fabfile ------- -**WARNING: this is only an example. Do not run this unmodified against a host -that is intended to do anything else or things WILL break!** +**WARNING: this is only an example. +Do not run this unmodified against a host that is intended to do anything else or things WILL break!** For your convenience there is a `fabric`_ file that automates all of the above. -If you don't know what fabric is and how it works read their documentation -first. +If you don't know what fabric is and how it works read their documentation first. -On your local machine make a separate virtualenv first and install the -``fabric`` and ``fabtools`` packages into that virtualenv:: +On your local machine make a separate virtualenv first and install the ``fabric`` and ``fabtools`` packages into that virtualenv:: mkvirtualenv kotti_deployment && cdvirtualenv pip install fabric fabtools @@ -135,11 +136,12 @@ Get the fabfile:: wget https://gist.github.com/gists/4079191/download -Read and modify the file to fit your needs. Then run it against your server:: +Read and modify the file to fit your needs. +Then run it against your server:: fab install_all -You're done. Everything is installed and configured to serve Kotti under -http://kotti.yourdomain.com/ +You're done. +Everything is installed and configured to serve Kotti under http://kotti.yourdomain.com/ .. _fabric: http://docs.fabfile.org/ diff --git a/docs/developing/basic/developer-manual.rst b/docs/developing/basic/developer-manual.rst index f7d5d44e5..bc9f79c3c 100644 --- a/docs/developing/basic/developer-manual.rst +++ b/docs/developing/basic/developer-manual.rst @@ -3,16 +3,14 @@ Developer manual ================ -Read the :ref:`Configuration` section first to understand which hooks -both integrators and developers can use to customize and extend Kotti. +Read the :ref:`Configuration` section first to understand which hooks both integrators and developers can use to customize and extend Kotti. .. contents:: Screencast tutorial ------------------- -Here's a screencast that guides you through the process of creating a -simple Kotti add-on for visitor comments: +Here's a screencast that guides you through the process of creating a simple Kotti add-on for visitor comments: .. raw:: html @@ -23,8 +21,8 @@ simple Kotti add-on for visitor comments: Content types ------------- -Defining your own content types is easy. The implementation of the -Document content type serves as an example here: +Defining your own content types is easy. +The implementation of the Document content type serves as an example here: .. code-block:: python @@ -47,12 +45,14 @@ Document content type serves as an example here: self.body = body self.mime_type = mime_type -You can configure the list of active content types in Kotti by -modifying the :ref:`kotti.available_types` setting. +The ``add_view`` parameter of the ``type_info`` attribute is the name of a view that can be used to construct a ``Document`` instance. +This view has to be available for all content types specified in ``addable_to`` parameter. +See the section below and the :ref:`adding-forms-and-a-view` section in the tutorial on how to define a view restricted to a specific context. -Note that when adding a relationship from your content type to another -Node, you will need to add a ``primaryjoin`` parameter to your -relationship. An example: +You can configure the list of active content types in Kotti by modifying the :ref:`kotti.available_types` setting. + +Note that when adding a relationship from your content type to another Node, you will need to add a ``primaryjoin`` parameter to your relationship. +An example: .. code-block:: python @@ -67,10 +67,8 @@ relationship. An example: Add views, subscribers and more ------------------------------- -:ref:`pyramid.includes` allows you to hook ``includeme`` functions -that you can use to add views, subscribers, and more aspects of Kotti. -An ``includeme`` function takes the *Pyramid Configurator API* object -as its sole argument. +:ref:`pyramid.includes` allows you to hook ``includeme`` functions that you can use to add views, subscribers, and more aspects of Kotti. +An ``includeme`` function takes the *Pyramid Configurator API* object as its sole argument. Here's an example that'll override the default view for Files: @@ -87,12 +85,10 @@ Here's an example that'll override the default view for Files: context=File, ) -To find out more about views and view registrations, please refer to -the `Pyramid documentation`_. +To find out more about views and view registrations, please refer to the `Pyramid documentation`_. -By adding the *dotted name string* of your ``includeme`` function to -the :ref:`pyramid.includes` setting, you ask Kotti to call it on -application start-up. An example: +By adding the *dotted name string* of your ``includeme`` function to the :ref:`pyramid.includes` setting, you ask Kotti to call it on application start-up. +An example: .. code-block:: ini @@ -115,13 +111,9 @@ Also see: ``kotti.configurators`` ----------------------- -Requiring users of your package to set all the configuration settings -by hand in the Paste Deploy INI file is not ideal. That's why Kotti -includes a configuration variable through which extending packages can -set all other INI settings through Python. Here's an example of a -function that programmatically modified ``kotti.base_includes`` and -``kotti.principals_factory`` which would otherwise be configured by -hand in the INI file: +Requiring users of your package to set all the configuration settings by hand in the Paste Deploy INI file is not ideal. +That's why Kotti includes a configuration variable through which extending packages can set all other INI settings through Python. +Here's an example of a function that programmatically modified ``kotti.base_includes`` and ``kotti.principals_factory`` which would otherwise be configured by hand in the INI file: .. code-block:: python @@ -141,14 +133,10 @@ And this is how your users would hook it up in their INI file: Security -------- -Kotti uses `Pyramid's security API`_, most notably its support -`inherited access control lists`_ support. On top of that, Kotti -defines *roles* and *groups* support: Users may be collected in -groups, and groups may be given roles, which in turn define -permissions. +Kotti uses `Pyramid's security API`_, most notably its support `inherited access control lists`_ support. +On top of that, Kotti defines *roles* and *groups* support: Users may be collected in groups, and groups may be given roles, which in turn define permissions. -The site root's ACL defines the default mapping of roles to their -permissions: +The site root's ACL defines the default mapping of roles to their permissions: .. code-block:: python @@ -159,37 +147,26 @@ permissions: ['Allow', 'role:owner', ['view', 'add', 'edit', 'manage']], ] -Every Node object has an ``__acl__`` attribute, allowing the -definition of localized row-level security. +Every Node object has an ``__acl__`` attribute, allowing the definition of localized row-level security. -The :func:`kotti.security.set_groups` function allows assigning roles -and groups to users in a given context. -:func:`kotti.security.list_groups` allows one to list the groups of a -given user. You may also set the list of groups globally on principal -objects, which are of type :class:`kotti.security.Principal`. +The :func:`kotti.security.set_groups` function allows assigning roles and groups to users in a given context. +:func:`kotti.security.list_groups` allows one to list the groups of a given user. +You may also set the list of groups globally on principal objects, which are of type :class:`kotti.security.Principal`. -Kotti delegates adding, deleting and search of user objects to an -interface it calls :class:`kotti.security.AbstractPrincipals`. You -can configure Kotti to use a different ``Principals`` implementation -by pointing the ``kotti.principals_factory`` configuration setting to -a different factory. The default setting here is: +Kotti delegates adding, deleting and search of user objects to an interface it calls :class:`kotti.security.AbstractPrincipals`. +You can configure Kotti to use a different ``Principals`` implementation by pointing the ``kotti.principals_factory`` configuration setting to a different factory. +The default setting here is: .. code-block:: ini kotti.principals_factory = kotti.security.principals_factory -There are views that you might want to override when you override the -principal factory. That is, if you use different columns in the -database, then you will probably want to make changes to the deform -schema as well. - -These views are :class:`kotti.views.users.UsersManage`, -:class:`kotti.views.users.UserManage` and -:class:`kotti.views.users.Preferences`. Notice that you should -override them using the standard way, that is, by overriding -``setup_users``, ``setup_user`` or ``prefs`` views. Then you can -override any sub-view used inside them as well as include any logic -for your usecase when it is called, if needed. +There are views that you might want to override when you override the principal factory. +That is, if you use different columns in the database, then you will probably want to make changes to the deform schema as well. + +These views are :class:`kotti.views.users.UsersManage`, :class:`kotti.views.users.UserManage` and :class:`kotti.views.users.Preferences`. +Notice that you should override them using the standard way, that is, by overriding ``setup-users``, ``setup-user`` or ``prefs`` views. +Then you can override any sub-view used inside them as well as include any logic for your usecase when it is called, if needed. .. _Pyramid's security API: http://docs.pylonsproject.org/projects/pyramid/dev/api/security.html .. _inherited access control lists: http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/security.html#acl-inheritance-and-location-awareness diff --git a/docs/developing/basic/security.rst b/docs/developing/basic/security.rst index 9da767f68..fa0eb6efd 100644 --- a/docs/developing/basic/security.rst +++ b/docs/developing/basic/security.rst @@ -3,7 +3,7 @@ Security ======== -Kotti security is based on the concepts of users, groups, roles and permissions. +Kotti security is based on the concepts of users, groups, roles, permissions and workflow. .. image:: /_static/user-group-role-permission.svg @@ -16,8 +16,7 @@ Group Permission A permission describes what is allowed on an object. - Permissions are never directly assigned to users or groups but always - aggregated in roles. + Permissions are never directly assigned to users or groups but always aggregated in roles. Role A Role is a collection of permissions. @@ -25,12 +24,244 @@ Role Users or groups can have global or local roles. Global Roles - Global roles are assigned to a user or group via Kotti's user management - screens. They apply to every object in a site. You should use them - very rarely, maybe only assign the "Adminsitrator" role to the - "Administrator" group. This assignment is present by default in a fresh - Kotti site. + Global roles are assigned to a user or group via Kotti's user management screens. + They apply to every object in a site. + You should use them very rarely, maybe only assign the "Adminsitrator" role to the "Administrator" group. + This assignment is present by default in a fresh Kotti site. Local Roles - Local roles are assigned to a user or group via the "Sharing" screen - of a content object. They apply only to this object and its children. + Local roles are assigned to a user or group via the "Sharing" screen of a content object. + They apply only to this object and its children. + +Workflow + The workflow keeps track of the current state of each object lifecycle to manage content security. + There is an initial state and you can move to other states thanks to transitions; each state defines a security matrix with roles and permissions. + By default Kotti provides a two-state workflow (private and public) for all object types except files and images. + Kotti's workflow implementation is based on `repoze.workflow`_. + +How to create a new role +------------------------ + +Small recipe you can use if you want to create a new role: + +.. code-block:: python + + from kotti.security import ( + Principal, + ROLES, + SHARING_ROLES, + set_roles, + set_sharing_roles, + set_user_management_roles, + ) + from kotti_yourpackage import _ + + + def add_role(role_id, role_title): + """ Add role in share view and user management views """ + UPDATED_ROLES = ROLES.copy() + UPDATED_ROLES[role_id] = Principal(role_id, + title=role_title) + UPDATED_SHARING_ROLES = list(SHARING_ROLES) + UPDATED_SHARING_ROLES.append(role_id) + set_roles(UPDATED_ROLES) + set_sharing_roles(UPDATED_SHARING_ROLES) + set_user_management_roles(UPDATED_SHARING_ROLES + ['role:admin']) + + + add_role(u'role:customer', _(u'Customer')) + +Practically you can add the code above to any file, as long as it is imported on application startup. +However, good practice would be to add it to your add on's ``__init__.py`` for small amounts of changes (like in the example) or to a separate file for larger amounts. + +Workflows +--------- + +You can use an XML file (zcml) in order to describe your workflow. +You can see an example here: `workflow.zcml`_. + +As you can see it is quite straightforward to add new states, transitions, permissions, etc. +You can easily turn the default 2-state website workflow into something completely different or turn your Kotti app into an intranet application. + +The default workflow definition is loaded from your project's ``.ini`` file (using the ``kotti.use_workflow`` setting). +The ``kotti.use_workflow`` setting's default value is: + +.. code-block:: ini + + kotti.use_workflow = kotti:workflow.zcml + +You can change change the default workflow for your site, register new workflows related to specific content types or disable it completely. + +How to disable the default workflow +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Kotti is shipped with a simple workflow definition based on private and public states. +If your particular use case does not require workflows at all, you can disable this feature with a non true value. For example: + +.. code-block:: ini + + kotti.use_workflow = 0 + +How to override the default workflow for all content types +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The default workflow is quite useful for websites, but sometimes you need something different. +Just point the ``kotti.use_workflow`` setting to your zcml file: + +.. code-block:: ini + + kotti.use_workflow = kotti_yourplugin:workflow.zcml + +The simplest way to deal with workflow definitions is: + +1. create a copy of the default workflow definition and +2. customize it (change permissions, add new states, permissions, transitions, initial state and so on). + +If you change workflow settings, you need to reset all your content's workflow states and thus the permissions for all objects under workflow control using the ``kotti-reset-workflow`` console script. + +kotti-reset-workflow command usage +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you change workflow settings you'll need to update security. + +.. code-block:: bash + + $ kotti-reset-workflow --help + Reset the workflow of all content objects in the database. + + This is useful when you want to migrate an existing database to + use a different workflow. When run, this script will reset all + your content objects to use the new workflow, while trying to + preserve workflow state information. + + For this command to work, all currently persisted states must map + directly to a state in the new workflow. As an example, if + there's a 'public' object in the database, the new workflow must + define 'public' also. + + If this is not the case, you may choose to reset all your content + objects to the new workflow's *initial state* by passing the + '--purge-existing' option. + + Usage: + kotti-reset-workflow [--purge-existing] + + Options: + -h --help Show this screen. + --purge-existing Reset all objects to new workflow's initial state. + +How to enable the standard workflow for images and files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Images and files are not associated with the default workflow. +If you need a workflow for these items you need to attach the ``IDefaultWorkflow`` marker interface. + +You can add the following lines in your includeme function: + +.. code-block:: python + + from zope.interface import implementer + from kotti.interfaces import IDefaultWorkflow + from kotti.resources import File + from kotti.resources import Image + ... + + def includeme(config): + ... + # enable workflow for images and files + implementer(IDefaultWorkflow)(Image) + implementer(IDefaultWorkflow)(File) + ... + +How to assign a different workflow to a content type +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We are going to use the default workflow for standard content types and a custom workflow for content types providing the ``ICustomContent`` marker interface. +All other content types will still use the default workflow. +Third party developers will be able to override our custom workflow without having to touch any line of code (just a ``.ini`` configuration file) + +Let's assume you are starting with a standard Kotti package created with ``pcreate -s kotti kotti_wf``. + +Four steps are needed: + +1. create a new marker interface ICustomContent, +2. change ``kotti_wf.resource`` (replace ``IDefaultWorkflow`` with our new ``ICustomContent``), +3. create the new workflow definition and +4. register your workflow definition. + +Create a new module ``kotti_wf/interfaces.py`` with this code. +This is **optional** but it doesn't hurt, the important thing is to omit the ``IDefaultWorkflow`` implementer from ``kotti_wf.resources``: + +.. code-block:: python + + from zope.interface import Interface + + + class ICustomContent(Interface): + """ Custom content marker interface """ + +Change your ``kotti_wf.resources`` module like so: + +.. code-block:: python + + from kotti.resources import Content + from zope.interface import implements + + from kotti_wf.interfaces import ICustomContent + + + class CustomContent(Content): + """ A custom content type. """ + + implements(ICustomContent) + +Here it is, our "custom" workflow definition assigned to our ``ICustomContent`` marker interface: + +.. code-block:: xml + + + + + + + + + + + + + + + + + + + + + + + + +Last you have to tell Kotti to register your new custom workflow including our ``zcml`` file: + +.. code-block:: ini + + kotti.zcml_includes = kotti_wf:workflow.zcml + +Special cases: + +* if you change workflow settings on a site with existing ``CustomContent`` instances, you need to update the workflow settings using the ``kotti-reset-workflow`` command. + +* if you assign a new workflow definition to a content that already provides the ``IDefaultWorkflow`` marker interface (by default all content types except files and images), you will have to create and attach on your workflow definition an ``elector`` function (it is just a function accepting a context and returning ``True`` or ``False``) + +.. _repoze.workflow: http://docs.repoze.org/workflow/ +.. _workflow.zcml: https://github.com/Kotti/Kotti/blob/master/kotti/workflow.zcml. diff --git a/docs/developing/basic/testing.rst b/docs/developing/basic/testing.rst index 443b850b9..69e2fdfef 100644 --- a/docs/developing/basic/testing.rst +++ b/docs/developing/basic/testing.rst @@ -3,15 +3,14 @@ Automated tests =============== -Kotti uses `pytest`_, `zope.testbrowser`_ and WebTest_ for automated -testing. +Kotti uses `pytest`_, `zope.testbrowser`_ and WebTest_ for automated testing. -Before you can run the tests, you must install Kotti's 'testing' -extras. Inside your Kotti checkout directory, do: +Before you can run the tests, you must install Kotti's 'testing' extras. +Inside your Kotti checkout directory, do: .. code-block:: bash - bin/python setup.py dev + bin/pip install -e .[testing] To then run Kotti's test suite, do: @@ -26,9 +25,7 @@ To then run Kotti's test suite, do: Using Kotti's test fixtures/funcargs in third party add-ons' tests ------------------------------------------------------------------ -To be able to use all of Kotti's fixtures and funcargs in your own package's -tests, you only need to "include" them with a line like this in your -``conftest.py`` file:: +To be able to use all of Kotti's fixtures and funcargs in your own package's tests, you only need to "include" them with a line like this in your ``conftest.py`` file:: pytest_plugins = "kotti" @@ -42,19 +39,11 @@ Available fixtures Continuous Integration ---------------------- -Kotti itself is tested against Python versions 2.6 and 2.7 as well as SQLite, -mySQL and PostgreSQL (in every possible combination of those) on every commit -(and pull request) via the excellent `GitHub`_ / `Travis CI`_ hook. +Kotti itself is tested against Python versions 3.5 and 3.6 as well as SQLite, mySQL and PostgreSQL (in every possible combination of those) on every commit (and pull request) via the excellent `GitHub`_ / `Travis CI`_ hook. -If you want your add-on packages' to be tested the same way with additional -testing against multiple versions of Kotti (including the current master), you -can add a ``.travis.yml`` file to your repo that looks similar to this: +If you want your add-on packages' to be tested the same way with additional testing against multiple versions of Kotti (including the current master), you can add a ``.travis.yml`` file to your repo that looks similar to this: https://raw.github.com/Kotti/kotti_media/master/.travis.yml. -The packages under http://kottipackages.xo7.de/ include all Kotti versions -released on `PyPI` (synced every night at 00:15 CET) and a package built from -the current master on GitHub (created every 15 minutes). - .. _GitHub: https://github.com/ .. _Travis CI: https://travis-ci.org/ .. _PyPI: http://pypi.python.org/pypi diff --git a/docs/first_steps/installation.rst b/docs/first_steps/installation.rst index 0e58643fe..cdbab702a 100644 --- a/docs/first_steps/installation.rst +++ b/docs/first_steps/installation.rst @@ -6,10 +6,10 @@ Installation Requirements ------------ -- Python 2.6 or 2.7 -- virtualenv_ -- ``build_essential`` and ``python-dev`` (on Debian or Ubuntu) -- or ``Xcode`` (on OSX) +- Python >= 3.5 +- ``build_essential`` and ``python-dev`` (on Debian or Ubuntu) or +- ``Xcode`` (on OS X) or +- equivalent build toolchain for your OS. Installation using ``virtualenv`` --------------------------------- @@ -18,26 +18,22 @@ It is recommended to install Kotti inside a virtualenv: .. parsed-literal:: - virtualenv mysite + python3 -m venv mysite cd mysite - bin/pip install -r https://raw.github.com/Kotti/Kotti/master/requirements.txt - bin/pip install git+https://github.com/Kotti/Kotti.git + bin/pip install Kotti -This will install the latest version of Kotti and all its requirements into your -virtualenv. +This will install the latest released version of Kotti and all its requirements into your virtualenv. -Kotti uses `Paste Deploy`_ for configuration and deployment. An -example configuration file is included with Kotti's source -distribution. Download it to your virtualenv directory (mysite): +Kotti uses `Paste Deploy`_ for configuration and deployment. +An example configuration file is included with Kotti's source distribution. +Download it to your virtualenv directory (mysite): .. parsed-literal:: - wget https://raw.github.com/Kotti/Kotti/master/app.ini + wget https://raw.github.com/Kotti/Kotti/stable/app.ini -See the list of `Kotti tags`_, perhaps to find the latest released -version. You can search the `Kotti listing on PyPI`_ also, for the -latest Kotti release (Kotti with a capital K is Kotti itself, -kotti_this and kotti_that are add-ons in the list on PyPI). +See the list of `Kotti tags`_, perhaps to find the latest released version. +You can search the `Kotti listing on PyPI`_ also, for the latest Kotti release (Kotti with a capital K is Kotti itself, kotti_this and kotti_that are add-ons in the list on PyPI). .. _Kotti tags: https://github.com/Kotti/Kotti/tags .. _Kotti listing on PyPI: https://pypi.python.org/pypi?%3Aaction=search&term=kotti&submit=search @@ -48,24 +44,20 @@ To run Kotti using the ``app.ini`` example configuration file: bin/pserve app.ini -This command runs Waitress, Pyramid's WSGI server, which Kotti uses as a -default server. You will see console output giving the local URL to view the -site in a browser. +This command runs Waitress, Pyramid's WSGI server, which Kotti uses as a default server. +You will see console output giving the local URL to view the site in a browser. -As you learn more, install other servers, with WSGI enabled, as needed. For -instance, for Apache, you may install the optional mod_wsgi module, or for -Nginx, you may use choose to use uWSGI. See the Pyramid documentation for a -variety of server and server configuration options. +As you learn more, install other servers, with WSGI enabled, as needed. +For instance, for Apache, you may install the optional mod_wsgi module, or for Nginx, you may use choose to use uWSGI. +See the Pyramid documentation for a variety of server and server configuration options. -The pserve command above uses SQLite as the default database. On first run, -Kotti will create a SQLite database called Kotti.db in your mysite directory. - -Kotti includes support for PostgreSQL, MySQL and SQLite (tested regularly), and -`other SQL databases`_. The default use of SQLite makes initial development -easy. Although SQLite may prove to be adequate for some deployments, Kotti is -flexible for installation of your choice of database during development or at -deployment. +The pserve command above uses SQLite as the default database. +On first run, Kotti will create a SQLite database called Kotti.db in your mysite directory. +Kotti includes support for PostgreSQL, MySQL and SQLite (tested regularly), and `other SQL databases`_. +The default use of SQLite makes initial development easy. +Although SQLite may prove to be adequate for some deployments, Kotti is flexible for installation of your choice of database during development or at deployment. .. _other SQL databases: http://www.sqlalchemy.org/docs/core/engines.html#supported-databases .. _virtualenv: http://pypi.python.org/pypi/virtualenv .. _Paste Deploy: http://pythonpaste.org/deploy/#the-config-file +.. _Docker: http://docker.io/ diff --git a/docs/first_steps/overview.rst b/docs/first_steps/overview.rst index 17ecee00f..9f72f4a0e 100644 --- a/docs/first_steps/overview.rst +++ b/docs/first_steps/overview.rst @@ -9,10 +9,7 @@ Kotti is most useful when you are developing CMS-like applications that - use workflows, and/or - work with hierarchical data. -Built on top of a number of *best-of-breed* software components, most -notably Pyramid_ and SQLAlchemy_, Kotti introduces only a few concepts -of its own, thus hopefully keeping the learning curve flat for the -developer. +Built on top of a number of *best-of-breed* software components, most notably Pyramid_ and SQLAlchemy_, Kotti introduces only a few concepts of its own, thus hopefully keeping the learning curve flat for the developer. .. _Pyramid: http://docs.pylonsproject.org/projects/pyramid/dev/ .. _SQLAlchemy: http://www.sqlalchemy.org/ @@ -20,31 +17,24 @@ developer. Features -------- -You can **try out the default installation** on `Kotti's demo page`_. +.. You can **try out the default installation** on `Kotti's demo page`_. -The Kotti CMS is a content management system that's heavily inspired -by Plone_. Its **main features** are: +The Kotti CMS is a content management system that's heavily inspired by Plone_. +Its **main features** are: -- **User-friendliness**: editors can edit content where it appears; - thus the edit interface is contextual and intuitive +- **User-friendliness**: editors can edit content where it appears; thus the edit interface is contextual and intuitive - **WYSIWYG editor**: includes a rich text editor -- **Responsive design**: Kotti builds on `Twitter Bootstrap`_, which - looks good both on desktop and mobile +- **Responsive design**: Kotti builds on `Twitter Bootstrap`_, which looks good both on desktop and mobile -- **Templating**: easily extend the CMS with your own look & feel with - little programming required (see :ref:`static-resource-management`) +- **Templating**: easily extend the CMS with your own look & feel with little programming required (see :ref:`static-resource-management`) -- **Add-ons**: install a variety of add-ons and customize them as well - as many aspects of the built-in CMS by use of an INI configuration - file (see :ref:`configuration`) +- **Add-ons**: install a variety of add-ons and customize them as well as many aspects of the built-in CMS by use of an INI configuration file (see :ref:`configuration`) -- **Security**: the advanced user and permissions management is - intuitive and scales to fit the requirements of large organizations +- **Security**: the advanced user and permissions management is intuitive and scales to fit the requirements of large organizations -- **Internationalized**: the user interface is fully translatable, - Unicode is used everywhere to store data (see :ref:`translations`) +- **Internationalized**: the user interface is fully translatable, Unicode is used everywhere to store data (see :ref:`translations`) .. _Kotti's demo page: http://kottidemo.danielnouri.org/ .. _Plone: http://plone.org/ @@ -53,54 +43,56 @@ by Plone_. Its **main features** are: For developers -------------- -For developers, Kotti delivers a strong foundation for building -different types of web applications that either extend or replace the -built-in CMS. +For developers, Kotti delivers a strong foundation for building different types of web applications that either extend or replace the built-in CMS. Developers can add and modify through a well-defined API: - views, - templates and layout (both via Pyramid_), - :ref:`content-types`, -- portlets (see :mod:`kotti.views.slots`), +- "portlets" (see :mod:`kotti.views.slots`), - access control and the user database (see :ref:`develop-security`), - workflows (via `repoze.workflow`_), - and much more. -Kotti has a **down-to-earth** API. Developers working with Kotti will -most of the time make direct use of the Pyramid_ and SQLAlchemy_ -libraries. Other notable components used but not enforced by Kotti -are Colander_ and Deform_ for forms, and Chameleon_ for templating. +Kotti has a **down-to-earth** API. +Developers working with Kotti will most of the time make direct use of the Pyramid_ and SQLAlchemy_ libraries. +Other notable components used but not enforced by Kotti are Colander_ and Deform_ for forms, and Chameleon_ for templating. -Kotti itself is `developed on Github`_. You can check out Kotti's -source code via its GitHub repository. Use this command: +Kotti itself is `developed on Github`_. +You can check out Kotti's source code via its GitHub repository. +Use this command: .. code-block:: bash git clone git@github.com:Kotti/Kotti -`Continuous testing`_ against different versions of Python and with -*PostgreSQL*, *MySQL* and *SQLite* and a complete test coverage make -Kotti a **stable** platform to work with. |build status|_ +`Continuous testing`_ against different versions of Python and with *PostgreSQL*, *MySQL* and *SQLite* and a complete test coverage make Kotti a **stable** platform to work with. + +|build_status_stable_postgresql|_ +|build_status_stable_mysql|_ +|build_status_stable_sqlite|_ Support ------- -- Python 2.6 or 2.7 -- Support for PostgreSQL, MySQL and SQLite (tested regularly), and a - list of `other SQL databases`_ +- Python 3.6-3.9 +- Support for PostgreSQL, MySQL and SQLite (tested regularly), and a list of `other SQL databases`_ - Support for WSGI and a `variety of web servers`_, including Apache - .. _repoze.workflow: http://docs.repoze.org/workflow/ -.. _Chameleon: http://chameleon.repoze.org/ +.. _Chameleon: https://chameleon.readthedocs.io/ .. _Colander: http://docs.pylonsproject.org/projects/colander/en/latest/ -.. _continuous testing: http://travis-ci.org/Kotti/Kotti +.. _continuous testing: https://github.com/Kotti/Kotti/actions .. _Deform: http://docs.pylonsproject.org/projects/deform/en/latest/ .. _developed on Github: https://github.com/Kotti/Kotti -.. |build status| image:: https://secure.travis-ci.org/Kotti/Kotti.png?branch=master -.. _build status: http://travis-ci.org/Kotti/Kotti +.. |build_status_stable_postgresql| image:: https://github.com/Kotti/Kotti/workflows/PostgreSQL/badge.svg?branch=stable +.. _build_status_stable_postgresql: https://github.com/Kotti/Kotti/actions?query=workflow%3APostgreSQL+branch%3Astable +.. |build_status_stable_mysql| image:: https://github.com/Kotti/Kotti/workflows/MySQL/badge.svg?branch=stable +.. _build_status_stable_mysql: https://github.com/Kotti/Kotti/actions?query=workflow%3AMySQL+branch%3Astable +.. |build_status_stable_sqlite| image:: https://github.com/Kotti/Kotti/workflows/SQLite/badge.svg?branch=stable +.. _build_status_stable_sqlite: https://github.com/Kotti/Kotti/actions?query=workflow%3ASQLite+branch%3Astable .. _installation: .. _other SQL databases: http://www.sqlalchemy.org/docs/core/engines.html#supported-databases .. _variety of web servers: http://wsgi.org/wsgi/Servers diff --git a/docs/first_steps/tut-1.rst b/docs/first_steps/tut-1.rst index 0471fd0cd..9df59b311 100644 --- a/docs/first_steps/tut-1.rst +++ b/docs/first_steps/tut-1.rst @@ -28,17 +28,22 @@ We will create the add-on as ``mysite/kotti_mysite``. Creating the Add-On Package --------------------------- -To create our add-on, we use the standard Pyramid tool ``pcreate``, with -``kotti_addon``, a scaffold that was installed as part of Kotti. +To create our add-on, we use the standard Python tool called +cookiecutter_, using the ``kotti-cookiecutter`` scaffolding. .. code-block:: bash - bin/pcreate -s kotti_addon kotti_mysite + pip install cookiecutter + cookiecutter https://github.com/Kotti/kotti-cookiecutter -The script will ask a number of questions. -It is safe to accept the defaults. +The script will ask a number of questions: provide a project name (e.g., ``Kotti mysite``) and accept the defaults. When finished, observe that a new directory called ``kotti_mysite`` was added to the current working directory, as ``mysite/kotti_mysite``. +See kotti-cookiecutter_ for further info about scaffolding options. + +.. _cookiecutter: https://github.com/audreyr/cookiecutter +.. _kotti-cookiecutter: https://github.com/Kotti/kotti-cookiecutter + Installing Our New Add-On ------------------------- @@ -83,11 +88,9 @@ And add ``kotti_mysite.kotti_configure`` to it: kotti_tinymce.kotti_configure kotti_mysite.kotti_configure - At this point, you should be able to restart the application, but you won't notice anything different. Let's make a simple CSS change and use it to see how Kotti manages static resources. - Static Resources ---------------- @@ -171,8 +174,7 @@ Notice how we add to the string ``kotti.fanstatic.view_needed``. This allows a handy use of += on different lines. After concatenation of the string parts, blanks will delimit them. -This ``kotti.fanstatic.view_needed`` setting, in turn, controls which resources -are loaded in the public interface (as compared to the edit interface). +This ``kotti.fanstatic.view_needed`` setting, in turn, controls which resources are loaded in the public interface (as compared to the edit interface). As you might have guessed, we could have also completely replaced Kotti's resources for the public interface by overriding the ``kotti.fanstatic.view_needed`` setting instead of adding to it, like this: diff --git a/docs/first_steps/tut-2.rst b/docs/first_steps/tut-2.rst index 5deefdd17..80970009a 100644 --- a/docs/first_steps/tut-2.rst +++ b/docs/first_steps/tut-2.rst @@ -10,7 +10,7 @@ Adding Models ------------- When creating our add-on, the scaffolding added the file ``kotti_mysite/kotti_mysite/resources.py``. -If you open `resources.py` you'll see that it already contains code for a sample content type ``CustomContent`` along with the following imports that we will use. +If you open ``resources.py`` you'll see that it already contains code for a sample content type ``CustomContent`` along with the following imports that we will use. .. code-block:: python @@ -19,8 +19,7 @@ If you open `resources.py` you'll see that it already contains code for a sample from sqlalchemy import ForeignKey from sqlalchemy import Integer - -Add the following definition for the ``Poll`` content type to `resources.py`. +Add the following definition for the ``Poll`` content type to ``resources.py``. .. code-block:: python @@ -40,14 +39,14 @@ Things to note here: - ``Poll`` derives from :class:`kotti.resources.Content`, which is the common base class for all content types. -- ``Poll`` declares a sqla.Column ``id``, which is required to hook it up with SQLAlchemy's inheritance. +- ``Poll`` declares a :class:`sqlalchemy.Column ` ``id``, which is required to hook it up with SQLAlchemy's inheritance. -- The type_info class attribute does essential configuration. - We refer to name and title, two properties already defined as part of - ``Content``, our base class. - The ``add_view`` defines the name of the add view, which we'll come to in a second. Finally, ``addable_to`` defines which content types we can add ``Poll`` items to. +- The ``type_info`` class attribute does essential configuration. + We refer to name and title, two properties already defined as part of ``Content``, our base class. + The ``add_view`` defines the name of the add view, which we'll come to in a second. + Finally, ``addable_to`` defines which content types we can add ``Poll`` items to. -- We do not need to define any additional sqlaColumn() properties, as the title +- We do not need to define any additional :class:`sqlalchemy.Column ` properties, as the ``title`` is the only property we need for this content type. We'll add another content class to hold the choices for the poll. @@ -79,6 +78,8 @@ Notable differences are: - The ``type_info`` defines the title, the ``add_view`` view, and that choices may only be added *into* ``Poll`` items, with the line ``addable_to=[u'Poll']``. +.. _adding-forms-and-a-view: + Adding Forms and a View ----------------------- @@ -86,7 +87,7 @@ Views (including forms) are typically put into a module called ``views``. The Kotti scaffolding further separates this into ``view`` and ``edit`` files inside a ``views`` directory. Open the file at ``kotti_mysite/kotti_mysite/views/edit.py``. -It already contains code for the `CustomContent` sample content type. +It already contains code for the ``CustomContent`` sample content type. We will take advantage of the imports already there. .. code-block:: python @@ -103,7 +104,9 @@ Some things to note: - Colander_ is the library that we use to define our schemas. Colander allows us to validate schemas against form data. + - Our class inherits from :class:`kotti.views.edit.ContentSchema` which itself inherits from :class:`colander.MappingSchema`. + - ``_`` is how we hook into i18n for translations. Add the following code to ``views/edit.py``: @@ -167,13 +170,12 @@ Add this to ``views/edit.py``: add = Choice item_type = u"Choice" - Using the ``AddFormView`` and ``EditFormView`` base classes from Kotti, these forms are simple to define. -We associate the schemas defined above, setting them as the schema_factory for each form, and we specify the content types to be added by each. +We associate the schemas defined above, setting them as the ``schema_factory`` for each form, and we specify the content types to be added by each. We use ``@view_config`` to add our views to the application. This takes advantage of a ``config.scan()`` call in ``__init__.py`` discussed below. -Notice that we can declare `permission`, `context`, and a `template` for each form, along with its `name`. +Notice that we can declare ``permission``, ``context``, and a ``template`` for each form, along with its ``name``. Wiring up the Content Types and Forms ------------------------------------- @@ -196,10 +198,9 @@ Open ``__init__.py`` and modify the ``kotti_configure`` method so that the ' kotti_mysite.fanstatic.css_and_js') ... - -Here, we've added our two content types to the site's available_types, a global +Here, we've added our two content types to the site's ``available_types``, a global registry. -We also removed the CustomContent content type included with the scaffolding. +We also removed the ``CustomContent`` content type included with the scaffolding. Notice the ``includeme`` method at the bottom of ``__init__.py``. It includes the call to ``config.scan()`` that we mentioned above while discussing the ``@view_config`` statements in our views. @@ -212,7 +213,6 @@ It includes the call to ``config.scan()`` that we mentioned above while discussi You can see the Pyramid documentation for scan_ for more information. - Adding a Poll and Choices to the site ------------------------------------- @@ -227,8 +227,8 @@ Login with the username *admin* and password *qwerty* and click on the Add menu You should see a few choices, namely the base Kotti classes ``Document``, ``File`` and ``Image`` and the Content Type we added, ``Poll``. Lets go ahead and click on ``Poll``. -For the question, let's write *What is your favourite color?*. -Now let's add three choices, *Red*, *Green* and *Blue* in the same way we added the poll. +For the question, let's write *"What is your favourite color?"*. +Now let's add three choices, *"Red"*, *"Green"* and *"Blue"* in the same way we added the poll. Remember that you must be in the context of the poll to add each choice. If we now go to the poll we added, we can see the question, but not our choices, which is definitely not what we wanted. @@ -243,6 +243,7 @@ Here is the code, added to ``view.py``. .. code-block:: python from kotti_mysite.fanstatic import css_and_js + from kotti_mysite.resources import Poll @view_defaults(context=Poll) @@ -253,14 +254,14 @@ Here is the code, added to ``view.py``. renderer='kotti_mysite:templates/poll.pt') def poll_view(self): css_and_js.need() - choices = self.context.values() + choices = self.context.children return { 'choices': choices, } -To find out if a Choice was added to the ``Poll`` we are currently viewing, we compare it's *parent_id* attribute with the *id* of the Poll - if they are the same, the ``Choice`` is a child of the ``Poll``. -To get all the appropriate choices, we do a simple database query, filtered as specified above. -Finally, we return a dictionary of all choices under the keyword *choices*. +Since we want to show all ``Choices`` added to a ``Poll`` we can simply use the ``children`` attribute. This will return a list of all the 'children' of a ``Poll`` which are exactly the ``Choices`` added to that particular ``Poll``. +The view returns a dictionary of all choices under the keyword *'choices'*. +The keywords a view returns are automatically available in it's template. Next on, we need a template to actually show our data. It could look something like this. @@ -276,30 +277,40 @@ Create a folder named ``templates`` and put the file ``poll.pt`` into it. The first 6 lines are needed so our template plays nicely with the master template (so we keep the add/edit bar, base site structure etc.). -The next line prints out the context.title (our question) inside the

    tag and then prints all choices (with links to the choice) as an unordered list. +The next line prints out the context.title (our question) inside the ``

    `` tag and then prints all choices (with links to the choice) as an unordered list. + +.. note:: + + We are using two 'magically available' attributes in the template - ``context`` and ``choices``. + + - ``context`` is automatically available in all templates and as the name implies it is the context of the view (in this case the ``Poll`` we are currently viewing). + + - ``choices`` is available because we sent it to the template in the Python part of the view. + You can of course send multiple variables to the template, you just need to return them in your Python code. With this, we are done with the second tutorial. -Restart the server instance, take a look at the new ``Poll`` view and play around with the template until you are completely satisfied with how our data is presented. -If you will work with templates for a while (or anytime you're developing basically) I'd recommend you use the pyramid *reload_templates* and *debug_templates* options as they save you a lot of time lost on server restarts. +Restart the application, take a look at the new ``Poll`` view and play around with the template until you are completely satisfied with how our data is presented. + +.. note:: + + If you will work with templates for a while (or any time you're developing basically) using the pyramid *'reload_templates'* and *'debug_templates'* options is recommended, as they allow you to see changes to the template without having to restart the application. + These options need to be put in your configuration INI under the *'[app:kotti]'* section. -.. code-block:: ini + .. code-block:: ini - pyramid.reload_templates = true - pyramid.debug_templates = true + [app:kotti] + pyramid.reload_templates = true + pyramid.debug_templates = true In the :ref:`next tutorial `, we will learn how to enable our users to actually vote for one of the ``Poll`` options. .. _SQLAlchemy: http://www.sqlalchemy.org/ -.. _Colander: http://colander.readthedocs.org/ +.. _Colander: https://colander.readthedocs.io/ .. _scan: http://docs.pylonsproject.org/docs/pyramid/en/latest/api/config.html#pyramid.config.Configurator.scan diff --git a/docs/first_steps/tut-3.rst b/docs/first_steps/tut-3.rst index e571db30d..55e59b0cb 100644 --- a/docs/first_steps/tut-3.rst +++ b/docs/first_steps/tut-3.rst @@ -11,30 +11,34 @@ Enabling voting on Poll Choices We will enable users to vote using a new view. When the user goes to that link, his or her vote will be saved and they will be redirected back to the Poll. -First, let's construct a new view, this time inside ``kotti_mysite/kotti_mysite/views/view.py``. -Add the following code to ``views/view.py``. +First, let's construct a new view. As before, add the following code to ``kotti_mysite/kotti_mysite/views/view.py``. .. code-block:: python + from kotti_mysite.resources import Choice + from pyramid.httpexceptions import HTTPFound + + @view_defaults(context=Choice) class ChoiceViews(BaseView): """ Views for :class:`kotti_mysite.resources.Choice` """ - @view_config(name='vote', permission='edit') + @view_config(name='vote', permission='view') def vote_view(self): self.context.votes += 1 - return HTTPFound(location=self.request.resource_url(self.context.parent)) + return HTTPFound( + location=self.request.resource_url(self.context.parent)) -The view will be called on the Choice content type, so the context is the Choice itself. -We add 1 to the current votes of the Choice, then we do a redirect using :class:`pyramid.httpexceptions.HTTPFound`. -The location is the parent of our context - the Poll in which our Choice resides. +The view will be called on the ``Choice`` content type, so the context is the ``Choice`` itself. +We add 1 to the current votes of the ``Choice``, then we do a redirect using :class:`pyramid.httpexceptions.HTTPFound`. +The location is the parent of our context - the ``Poll`` in which our ``Choice`` resides. -With this, we can now vote on a Choice by appending /vote at the end of the Choice URL. +With this, we can now vote on a ``Choice`` by appending ``/vote`` at the end of the ``Choice`` URL. Changing the Poll view so we see the votes ------------------------------------------ -First, we will add some extra content into our poll_view so we are able to show current votes of a Choice. +First, we will add some extra content into our ``poll_view`` so we are able to show the distribution of votes across all choices. .. code-block:: python :emphasize-lines: 4,7 @@ -48,29 +52,29 @@ First, we will add some extra content into our poll_view so we are able to show 'all_votes': all_votes } -Our view will now be able to get the sum of all votes in the poll via the *all_votes* variable. -We will also want to change the link to go to our new vote view. -Open ``poll.pt`` and change the link into +Our view will now be able to get the sum of all votes in the poll via the ``all_votes`` variable. +We will also want to change the choices list to link to our new vote view. +Open ``poll.pt`` and change the link into: .. code-block:: html :emphasize-lines: 3-5 ...
  • - + ${choice.title} (${choice.votes}/${all_votes})
  • ... -This will add the number of votes/all_votes after each choice and enable us to vote by clicking on the Choice. +This will add the number of votes/all_votes after each choice and enable us to vote by clicking on the choice. Fire up the server and go test it now. Adding an info block about voting on the view --------------------------------------------- -As you can see, the voting now works, but it doesn't look particulary good. -Let us at least add a nice information bubble when we vote alright? +As you can see, the voting now works, but it doesn't look particularly good. +Let us at least add a nice information bubble when we vote. The easiest way to go about that is to use ``request.session.flash``, which allows us to flash different messages (success, error, info etc.). Change the ``vote_view`` to include the the flash message before redirecting. @@ -85,12 +89,15 @@ Change the ``vote_view`` to include the the flash message before redirecting. return HTTPFound( location=self.request.resource_url(self.context.parent)) +.. note:: + + Don't forget that since we changed the Python code, we need to restart the application, even if we enabled template reloading and debugging! -As before, I encourage you to play around a bit more, as you learn much by trying our new things. +As before, you are encouraged to play around a bit more, as you learn much by trying out new things. A few ideas on what you could work on are: - Change the Choice content type so it has an extra description field that is not required (if you change database content, you will need to delete the database or do a migration). Then make a new Choice view that will list the extra information. + - Make sure only authenticated users can vote, anonymous users should see the results but when trying to vote, it should move them to the login page. Also make sure that each user can vote only once, and list all users who voted for the Choice on the Choice's view. - diff --git a/docs/index.rst b/docs/index.rst index a3bb51af5..63c27c501 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,12 +4,12 @@ Kotti Documentation =================== -Kotti is a user-friendly, nimble and extensible web content management system, -based on Pyramid and SQLAlchemy. +Kotti is a high-level, Pythonic web application framework based on Pyramid and SQLAlchemy. +It includes an extensible Content Management System called the Kotti CMS. If you are a user of a Kotti system, and either found this page through browsing or searching, or were referred here, you will likely want to go directly to the `Kotti User Manual`_. -The documentation below is for developers of Kotti. +The documentation below is for developers of Kotti or applications built on top of it. First Steps ----------- @@ -65,5 +65,5 @@ Future and Past Roadmap Changelog -.. _Kotti User Manual: http://kotti-user-manual.readthedocs.org/ +.. _Kotti User Manual: https://kotti-user-manual.readthedocs.io/ .. _Roadmap: https://github.com/Kotti/Kotti/milestones diff --git a/docs/todo.txt b/docs/todo.txt index c4c149fd8..784d253ef 100644 --- a/docs/todo.txt +++ b/docs/todo.txt @@ -14,18 +14,12 @@ Bigger tasks application exceptions. Possibly other introspectable features, like version numbers etc. -- Multilingual support (in core or kotti_multilingual add-on?), probably - based on annotations. - Have an idea for this list? Discuss at http://groups.google.com/group/kotti or #kotti on IRC. Smaller tasks ============= -- Many of the templates used in 'kotti/views/edit.py' have some legacy - cruft in them. Clean up. - - Move more navigation related functionality out of TemplateAPI and into snippets. @@ -37,8 +31,3 @@ Smaller tasks - Add ordering of children and hide/show(Content.in_navigation) to the contents view. - -Upstream tasks -============== - -- Release deform 0.9.6 to be able to use Button(css_class="btn-foo") feature. diff --git a/i18n.sh b/i18n.sh index f8f6a4800..eaa917db7 100755 --- a/i18n.sh +++ b/i18n.sh @@ -26,16 +26,17 @@ fi # no arguments, extract and update if [ $# -eq 0 ]; then echo "Extract messages" - pot-create "$SEARCH_PATH" -o "$LOCALES_PATH"/$DOMAIN.pot + pot-create "$SEARCH_PATH" -d $DOMAIN -o "$LOCALES_PATH"/$DOMAIN.pot echo "Update translations" for po in "$LOCALES_PATH"/*/LC_MESSAGES/$DOMAIN.po; do - msgmerge -o "$po" "$po" "$LOCALES_PATH"/$DOMAIN.pot + msgmerge --no-wrap -o "$po" "$po" "$LOCALES_PATH"/$DOMAIN.pot done echo "Compile message catalogs" for po in "$LOCALES_PATH"/*/LC_MESSAGES/*.po; do - msgfmt -o "${po%.*}.mo" "$po" + echo "Compiling file $po..." + msgfmt --statistics -o "${po%.*}.mo" "$po" done # first argument represents language identifier, create catalog diff --git a/kotti/__init__.py b/kotti/__init__.py index 44d010988..82408b3da 100644 --- a/kotti/__init__.py +++ b/kotti/__init__.py @@ -1,11 +1,6 @@ -import pkg_resources +import os.path -from sqlalchemy import engine_from_config -from sqlalchemy import MetaData -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import scoped_session -from sqlalchemy.orm import sessionmaker -from zope.sqlalchemy import ZopeTransactionExtension +import pkg_resources from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy from pyramid.config import Configurator @@ -13,31 +8,38 @@ from pyramid.threadlocal import get_current_registry from pyramid.util import DottedNameResolver from pyramid_beaker import session_factory_from_settings +from sqlalchemy import MetaData +from sqlalchemy import engine_from_config +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import scoped_session +from sqlalchemy.orm import sessionmaker +from zope.sqlalchemy import register from kotti.sqla import Base as KottiBase - metadata = MetaData() -DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) +DBSession = scoped_session(sessionmaker(autoflush=True)) +register(DBSession) Base = declarative_base(cls=KottiBase) Base.metadata = metadata Base.query = DBSession.query_property() -TRUE_VALUES = ('1', 'y', 'yes', 't', 'true') -FALSE_VALUES = ('0', 'n', 'no', 'f', 'false', 'none') +TRUE_VALUES = ("1", "y", "yes", "t", "true") +FALSE_VALUES = ("0", "n", "no", "f", "false", "none") def authtkt_factory(**settings): from kotti.security import list_groups_callback + kwargs = dict( - secret=settings['kotti.secret2'], - hashalg='sha512', + secret=settings["kotti.secret2"], + hashalg="sha512", callback=list_groups_callback, - ) + ) try: return AuthTktAuthenticationPolicy(**kwargs) except TypeError: # BBB with Pyramid < 1.4 - kwargs.pop('hashalg') + kwargs.pop("hashalg") return AuthTktAuthenticationPolicy(**kwargs) @@ -52,84 +54,110 @@ def beaker_session_factory(**settings): def none_factory(**kwargs): # pragma: no cover return None + # All of these can be set by passing them in the Paste Deploy settings: conf_defaults = { - 'kotti.templates.api': 'kotti.views.util.TemplateAPI', - 'kotti.configurators': '', - 'pyramid.includes': '', - 'kotti.includes': '', # BBB - 'kotti.base_includes': ' '.join([ - 'kotti', - 'kotti.events', - 'kotti.views', - 'kotti.views.cache', - 'kotti.views.view', - 'kotti.views.edit', - 'kotti.views.edit.actions', - 'kotti.views.edit.content', - 'kotti.views.edit.default_views', - 'kotti.views.edit.upload', - 'kotti.views.file', - 'kotti.views.image', - 'kotti.views.login', - 'kotti.views.navigation', - 'kotti.views.users', - ]), - 'kotti.zcml_includes': ' '.join([ - ]), - 'kotti.asset_overrides': '', - 'kotti.use_tables': '', - 'kotti.request_factory': 'kotti.request.Request', - 'kotti.root_factory': 'kotti.resources.default_get_root', - 'kotti.populators': 'kotti.populate.populate', - 'kotti.available_types': ' '.join([ - 'kotti.resources.Document', - 'kotti.resources.File', - 'kotti.resources.Image', - ]), - 'kotti.search_content': 'kotti.views.util.default_search_content', - 'kotti.authn_policy_factory': 'kotti.authtkt_factory', - 'kotti.authz_policy_factory': 'kotti.acl_factory', - 'kotti.session_factory': 'kotti.beaker_session_factory', - 'kotti.principals_factory': 'kotti.security.principals_factory', - 'kotti.caching_policy_chooser': ( - 'kotti.views.cache.default_caching_policy_chooser'), - 'kotti.url_normalizer': 'kotti.url_normalizer.url_normalizer', - 'kotti.url_normalizer.map_non_ascii_characters': True, - 'kotti.use_workflow': 'kotti:workflow.zcml', - 'kotti.date_format': 'medium', - 'kotti.datetime_format': 'medium', - 'kotti.time_format': 'medium', - 'kotti.max_file_size': '10', - 'kotti.fanstatic.edit_needed': 'kotti.fanstatic.edit_needed', - 'kotti.fanstatic.view_needed': 'kotti.fanstatic.view_needed', - 'kotti.static.edit_needed': '', # BBB - 'kotti.static.view_needed': '', # BBB - 'kotti.alembic_dirs': 'kotti:alembic', - 'kotti.register': 'False', - 'kotti.register.group': '', - 'kotti.register.role': '', - 'pyramid_deform.template_search_path': 'kotti:templates/deform', - } - -conf_dotted = set([ - 'kotti.templates.api', - 'kotti.configurators', - 'kotti.base_includes', - 'kotti.request_factory', - 'kotti.root_factory', - 'kotti.populators', - 'kotti.available_types', - 'kotti.search_content', - 'kotti.authn_policy_factory', - 'kotti.authz_policy_factory', - 'kotti.session_factory', - 'kotti.principals_factory', - 'kotti.caching_policy_chooser', - 'kotti.fanstatic.edit_needed', - 'kotti.fanstatic.view_needed', - 'kotti.url_normalizer', - ]) + "kotti.alembic_dirs": "kotti:alembic", + "kotti.asset_overrides": "", + "kotti.authn_policy_factory": "kotti.authtkt_factory", + "kotti.authz_policy_factory": "kotti.acl_factory", + "kotti.available_types": " ".join( + ["kotti.resources.Document", "kotti.resources.File"] + ), + "kotti.base_includes": " ".join( + [ + "kotti", + "kotti.traversal", + "kotti.filedepot", + "kotti.events", + "kotti.sanitizers", + "kotti.views", + "kotti.views.cache", + "kotti.views.view", + "kotti.views.edit", + "kotti.views.edit.actions", + "kotti.views.edit.content", + "kotti.views.edit.default_views", + "kotti.views.edit.upload", + "kotti.views.file", + "kotti.views.login", + "kotti.views.navigation", + "kotti.views.users", + ] + ), + "kotti.caching_policy_chooser": ( + "kotti.views.cache.default_caching_policy_chooser" + ), + "kotti.configurators": "", + "kotti.date_format": "medium", + "kotti.datetime_format": "medium", + "kotti.depot_mountpoint": "/depot", + "kotti.depot_replace_wsgi_file_wrapper": False, + "kotti.depot.0.backend": "kotti.filedepot.DBFileStorage", + "kotti.depot.0.name": "dbfiles", + "kotti.fanstatic.edit_needed": "kotti.fanstatic.edit_needed", + "kotti.fanstatic.view_needed": "kotti.fanstatic.view_needed", + "kotti.login_success_callback": "kotti.views.login.login_success_callback", + "kotti.max_file_size": "10", + "kotti.modification_date_excludes": " ".join(["kotti.resources.Node.position"]), + "kotti.populators": "kotti.populate.populate", + "kotti.principals_factory": "kotti.security.principals_factory", + "kotti.register": "False", + "kotti.register.group": "", + "kotti.register.role": "", + "kotti.request_factory": "kotti.request.Request", + "kotti.reset_password_callback": "kotti.views.login.reset_password_callback", # noqa + "kotti.root_factory": "kotti.resources.default_get_root", + "kotti.sanitizers": " ".join( + [ + "xss_protection:kotti.sanitizers.xss_protection", + "minimal_html:kotti.sanitizers.minimal_html", + "no_html:kotti.sanitizers.no_html", + ] + ), + "kotti.sanitize_on_write": " ".join( + [ + "kotti.resources.Document.body:xss_protection", + "kotti.resources.Content.title:no_html", + "kotti.resources.Content.description:no_html", + ] + ), + "kotti.search_content": "kotti.views.util.default_search_content", + "kotti.session_factory": "kotti.beaker_session_factory", + "kotti.static.edit_needed": "", # BBB + "kotti.static.view_needed": "", # BBB + "kotti.templates.api": "kotti.views.util.TemplateAPI", + "kotti.time_format": "medium", + "kotti.url_normalizer": "kotti.url_normalizer.url_normalizer", + "kotti.url_normalizer.map_non_ascii_characters": True, + "kotti.use_tables": "", + "kotti.use_workflow": "kotti:workflow.zcml", + "kotti.zcml_includes": " ".join([]), + "pyramid.includes": "", + "pyramid_deform.template_search_path": "kotti:templates/deform", +} + +conf_dotted = { + "kotti.authn_policy_factory", + "kotti.authz_policy_factory", + "kotti.available_types", + "kotti.base_includes", + "kotti.caching_policy_chooser", + "kotti.configurators", + "kotti.fanstatic.edit_needed", + "kotti.fanstatic.view_needed", + "kotti.login_success_callback", + "kotti.modification_date_excludes", + "kotti.populators", + "kotti.principals_factory", + "kotti.request_factory", + "kotti.reset_password_callback", + "kotti.root_factory", + "kotti.search_content", + "kotti.session_factory", + "kotti.templates.api", + "kotti.url_normalizer", +} def get_version(): @@ -141,16 +169,15 @@ def get_settings(): def _resolve_dotted(d, keys=conf_dotted): - resolved = d.copy() for key in keys: value = resolved[key] - if not isinstance(value, basestring): + if not isinstance(value, str): continue new_value = [] for dottedname in value.split(): - new_value.append(DottedNameResolver(None).resolve(dottedname)) + new_value.append(DottedNameResolver().resolve(dottedname)) resolved[key] = new_value return resolved @@ -161,8 +188,9 @@ def main(global_config, **settings): # application. from kotti.resources import initialize_sql + config = base_configure(global_config, **settings) - engine = engine_from_config(config.registry.settings, 'sqlalchemy.') + engine = engine_from_config(config.registry.settings) initialize_sql(engine) return config.make_wsgi_app() @@ -177,37 +205,37 @@ def base_configure(global_config, **settings): settings.setdefault(key, value) for key, value in settings.items(): - if key.startswith('kotti') and isinstance(value, basestring): - settings[key] = unicode(value, 'utf8') + if key.startswith("kotti") and isinstance(value, bytes): + settings[key] = value.decode("utf8") # Allow extending packages to change 'settings' w/ Python: - k = 'kotti.configurators' + k = "kotti.configurators" for func in _resolve_dotted(settings, keys=(k,))[k]: func(settings) settings = _resolve_dotted(settings) - secret1 = settings['kotti.secret'] - settings.setdefault('kotti.secret2', secret1) + secret1 = settings["kotti.secret"] + settings.setdefault("kotti.secret2", secret1) # We'll process ``pyramid_includes`` later by hand, to allow # overrides of configuration from ``kotti.base_includes``: - pyramid_includes = settings.pop('pyramid.includes', '') + pyramid_includes = settings.pop("pyramid.includes", "") config = Configurator( - request_factory=settings['kotti.request_factory'][0], - settings=settings) + request_factory=settings["kotti.request_factory"][0], settings=settings + ) config.begin() config.hook_zca() - config.include('pyramid_zcml') + config.include("pyramid_zcml") # Chameleon bindings were removed from Pyramid core since pyramid>=1.5a2 - config.include('pyramid_chameleon') + config.include("pyramid_chameleon") - config.registry.settings['pyramid.includes'] = pyramid_includes + config.registry.settings["pyramid.includes"] = pyramid_includes # Include modules listed in 'kotti.base_includes': - for module in settings['kotti.base_includes']: + for module in settings["kotti.base_includes"]: config.include(module) config.commit() @@ -217,7 +245,7 @@ def base_configure(global_config, **settings): for module in pyramid_includes.split(): config.include(module) - for name in settings['kotti.zcml_includes'].strip().split(): + for name in settings["kotti.zcml_includes"].strip().split(): config.load_zcml(name) config.commit() @@ -228,32 +256,38 @@ def base_configure(global_config, **settings): def includeme(config): + """ Pyramid includeme hook. + + :param config: app config + :type config: :class:`pyramid.config.Configurator` + """ + import kotti.views.util settings = config.get_settings() - authentication_policy = settings[ - 'kotti.authn_policy_factory'][0](**settings) - authorization_policy = settings[ - 'kotti.authz_policy_factory'][0](**settings) - session_factory = settings['kotti.session_factory'][0](**settings) + authentication_policy = settings["kotti.authn_policy_factory"][0](**settings) + authorization_policy = settings["kotti.authz_policy_factory"][0](**settings) + session_factory = settings["kotti.session_factory"][0](**settings) if authentication_policy: config.set_authentication_policy(authentication_policy) if authorization_policy: config.set_authorization_policy(authorization_policy) config.set_session_factory(session_factory) - config.add_subscriber( - kotti.views.util.add_renderer_globals, BeforeRender) + config.add_subscriber(kotti.views.util.add_renderer_globals, BeforeRender) - for override in [a.strip() - for a in settings['kotti.asset_overrides'].split() - if a.strip()]: - config.override_asset(to_override='kotti', override_with=override) + for override in [ + a.strip() for a in settings["kotti.asset_overrides"].split() if a.strip() + ]: + config.override_asset(to_override="kotti", override_with=override) - config.add_translation_dirs('kotti:locale') + config.add_translation_dirs(f"{os.path.dirname(__file__)}/locale") + # used to be + # config.add_translation_dirs("kotti:locale") + # which fails with recent pytest (works in non testing though) - workflow = settings['kotti.use_workflow'] + workflow = settings["kotti.use_workflow"] if workflow.lower() not in FALSE_VALUES: config.load_zcml(workflow) diff --git a/kotti/alembic/env.py b/kotti/alembic/env.py index bdc81a7f5..5e88a9a0a 100755 --- a/kotti/alembic/env.py +++ b/kotti/alembic/env.py @@ -1,6 +1,7 @@ -from alembic import context import traceback + import transaction +from alembic import context from zope.sqlalchemy import mark_changed from kotti import DBSession @@ -9,10 +10,9 @@ def run_migrations_online(): if DBSession.bind is None: - raise ValueError( - "\nYou must run Kotti's migration using the 'kotti-migrate' script" - "\nand not through 'alembic' directly." - ) + raise ValueError("You must run Kotti's migration using the " + "'kotti-migrate' script and not through 'alembic' " + "directly.") transaction.begin() connection = DBSession.connection() @@ -25,7 +25,7 @@ def run_migrations_online(): try: context.run_migrations() mark_changed(DBSession()) - except: + except: # noqa: E722 traceback.print_exc() transaction.abort() else: @@ -41,9 +41,7 @@ def run_migrations_online(): pass else: if offline_mode: # pragma: no cover - raise ValueError( - "\nNo support for Alembic's offline mode at this point." - "\nYou may want to write your own env.py script to use " - "\n'offline mode'." - ) + raise ValueError("No support for Alembic's offline mode at this point. " + "You may want to write your own env.py script to use " + "'offline mode'.") run_migrations_online() diff --git a/kotti/alembic/script.py.mako b/kotti/alembic/script.py.mako index 95702017e..a14b08c34 100644 --- a/kotti/alembic/script.py.mako +++ b/kotti/alembic/script.py.mako @@ -6,13 +6,14 @@ Create Date: ${create_date} """ +import sqlalchemy as sa +from alembic import op +${imports if imports else ""} + # revision identifiers, used by Alembic. revision = ${repr(up_revision)} down_revision = ${repr(down_revision)} -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} def upgrade(): ${upgrades if upgrades else "pass"} diff --git a/kotti/alembic/versions/1063d7178fa_add_node_path_column.py b/kotti/alembic/versions/1063d7178fa_add_node_path_column.py index 96ab3a3a3..ff4979f56 100644 --- a/kotti/alembic/versions/1063d7178fa_add_node_path_column.py +++ b/kotti/alembic/versions/1063d7178fa_add_node_path_column.py @@ -6,26 +6,32 @@ """ -# revision identifiers, used by Alembic. -revision = '1063d7178fa' -down_revision = '57fecf5dbd62' - from alembic import op import sqlalchemy as sa from pyramid.location import lineage +# revision identifiers, used by Alembic. +revision = '1063d7178fa' +down_revision = '57fecf5dbd62' + + def upgrade(): - op.add_column('nodes', - sa.Column('path', sa.Unicode(1000), index=True)) + from alembic.context import get_bind + + conn = get_bind() + if conn.engine.dialect.name == 'mysql': + op.add_column('nodes', sa.Column('path', sa.Unicode(1000))) + else: + op.add_column('nodes', sa.Column('path', sa.Unicode(1000), index=True)) from kotti import DBSession from kotti.resources import Node for node in DBSession.query(Node).with_polymorphic([Node]): reversed_lineage = reversed(tuple(lineage(node))) - node.path = u'/'.join( - node.__name__ for node in reversed_lineage) or u'/' + node.path = '/'.join( + node.__name__ for node in reversed_lineage) or '/' def downgrade(): diff --git a/kotti/alembic/versions/37a05f6246af_enlarge_name_title_path.py b/kotti/alembic/versions/37a05f6246af_enlarge_name_title_path.py new file mode 100644 index 000000000..53df38c28 --- /dev/null +++ b/kotti/alembic/versions/37a05f6246af_enlarge_name_title_path.py @@ -0,0 +1,44 @@ +"""Enlarged name title + +Revision ID: 37a05f6246af +Revises: 413fa5fcc581 +Create Date: 2015-05-15 17:12:07.631190 + +""" + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = '37a05f6246af' +down_revision = '413fa5fcc581' + + +def upgrade(): + op.alter_column('nodes', + 'title', + type_=sa.Unicode(250), + ) + op.alter_column('nodes', + 'name', + type_=sa.Unicode(250), + ) + op.alter_column('nodes', + 'path', + type_=sa.Unicode(2000), + ) + + +def downgrade(): + op.alter_column('nodes', + 'title', + type_=sa.Unicode(100), + ) + op.alter_column('nodes', + 'name', + type_=sa.Unicode(50), + ) + op.alter_column('nodes', + 'path', + type_=sa.Unicode(1000), + ) diff --git a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py new file mode 100644 index 000000000..e89da93ca --- /dev/null +++ b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py @@ -0,0 +1,90 @@ +"""Migrate binary file storage to filedepot + +Revision ID: 413fa5fcc581 +Revises: 1063d7178fa +Create Date: 2014-12-07 05:10:04.294222 + +""" + +import logging +import sys +import time + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = '413fa5fcc581' +down_revision = '559ce6eb0949' + +log = logging.getLogger('kotti') +log.addHandler(logging.StreamHandler(sys.stdout)) +log.setLevel(logging.INFO) + + +def upgrade(): + from depot.manager import DepotManager + from depot.fields.upload import UploadedFile + from sqlalchemy import bindparam, Unicode, Column + + from kotti import DBSession, metadata + + files = sa.Table('files', metadata) + files.c.data.type = sa.LargeBinary() # this restores to old column type + dn = DepotManager.get_default() + + _saved = [] + + def process(thing): + id, data, filename, mimetype = thing + uploaded_file = UploadedFile({'depot_name': dn, 'files': []}) + # noinspection PyProtectedMember + uploaded_file._thaw() + uploaded_file.process_content( + data, filename=filename, content_type=mimetype) + _saved.append({'nodeid': id, 'data': uploaded_file.encode()}) + log.info(f"Saved data for node id {id}") + + query = DBSession.query( + files.c.id, files.c.data, files.c.filename, files.c.mimetype + ).order_by(files.c.id).yield_per(10) + + window_size = 10 + window_idx = 0 + + log.info("Starting migration of blob data") + + now = time.time() + while True: + start, stop = window_size * window_idx, window_size * (window_idx + 1) + things = query.slice(start, stop).all() + if things is None: + break + for thing in things: + process(thing) + if len(things) < window_size: + break + window_idx += 1 + + log.info("Files written on disk, saving information to DB") + + op.drop_column('files', 'data') + op.add_column('files', Column('data', Unicode(4096))) + files.c.data.type = Unicode(4096) + + update = files.update().where(files.c.id == bindparam('nodeid')).\ + values({files.c.data: bindparam('data')}) + + def chunks(l, n): # noqa: E741 + for i in range(0, len(l), n): + yield l[i:i + n] + + for cdata in chunks(_saved, 10): + DBSession.execute(update, cdata) + + log.info("Blob migration completed in {} seconds".format( + int(time.time() - now))) + + +def downgrade(): + pass diff --git a/kotti/alembic/versions/4a3de0d0804a_add_foreignkey_indices.py b/kotti/alembic/versions/4a3de0d0804a_add_foreignkey_indices.py new file mode 100644 index 000000000..f58fad046 --- /dev/null +++ b/kotti/alembic/versions/4a3de0d0804a_add_foreignkey_indices.py @@ -0,0 +1,32 @@ +"""Add ForeignKey indices + +Revision ID: 4a3de0d0804a +Revises: 37a05f6246af +Create Date: 2015-08-31 12:37:26.493958 + +""" + +from alembic import op + +# revision identifiers, used by Alembic. +revision = '4a3de0d0804a' +down_revision = '37a05f6246af' + + +def upgrade(): + op.create_index( + 'ix_nodes_parent_id', 'nodes', ['parent_id', ]) + op.create_index( + 'ix_local_groups_node_id', 'local_groups', ['node_id', ]) + op.create_index( + 'ix_local_groups_principal_name', 'local_groups', ['principal_name', ]) + op.create_index( + 'ix_tags_to_contents_tag_id', 'tags_to_contents', ['tag_id', ]) + op.create_index( + 'ix_tags_to_contents_content_id', 'tags_to_contents', ['content_id', ]) + + +def downgrade(): + op.drop_index('ix_nodes_parent_id', 'nodes') + op.drop_index('ix_local_groups_node_id', 'local_groups') + op.drop_index('ix_local_groups_principal_name', 'local_groups') diff --git a/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py b/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py new file mode 100644 index 000000000..b82154f4e --- /dev/null +++ b/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py @@ -0,0 +1,40 @@ +"""Update Node.path column + +Revision ID: 559ce6eb0949 +Revises: 1063d7178fa +Create Date: 2014-12-10 13:20:29.374951 + +""" + +# revision identifiers, used by Alembic. +revision = '559ce6eb0949' +down_revision = '1063d7178fa' + + +def upgrade(): + + from kotti.resources import DBSession + + from alembic.context import get_bind + + conn = get_bind() + + if conn.engine.dialect.name == 'mysql': + update = "UPDATE nodes " \ + "SET path = concat(path, '/') " \ + "WHERE path NOT LIKE '%/'" + else: + update = "UPDATE nodes " \ + "SET path = path || '/' " \ + "WHERE path NOT LIKE '%/'" + DBSession.execute(update) + + +def downgrade(): + from kotti import DBSession + from kotti.resources import Node + + for node in DBSession.query(Node).with_polymorphic([Node]): + # remove trailing '/' from all nodes but root + if node.path != '/': + node.path = node.path[:-1] diff --git a/kotti/alembic/versions/814c4ec72f1_add_file_columns_to_images_table.py b/kotti/alembic/versions/814c4ec72f1_add_file_columns_to_images_table.py new file mode 100644 index 000000000..7fa9184e3 --- /dev/null +++ b/kotti/alembic/versions/814c4ec72f1_add_file_columns_to_images_table.py @@ -0,0 +1,58 @@ +"""Add file columns to images table. + +Revision ID: 814c4ec72f1 +Revises: 4a3de0d0804a +Create Date: 2015-12-14 17:37:36.723844 + +""" + +import sqlalchemy as sa +from alembic import op +from depot.fields.sqlalchemy import UploadedFileField + +from kotti import DBSession + +# revision identifiers, used by Alembic. + +revision = '814c4ec72f1' +down_revision = '4a3de0d0804a' + + +def upgrade(): + + op.drop_table('images') + op.create_table( + 'images', + sa.Column('id', sa.Integer(), sa.ForeignKey('contents.id'), + primary_key=True), + sa.Column('filename', sa.Unicode(100)), + sa.Column('mimetype', sa.String(100)), + sa.Column('size', sa.Integer()), + sa.Column('data', UploadedFileField())) + + DBSession.execute(""" + INSERT INTO images (id, filename, mimetype, size, data) + SELECT f.id, f.filename, f.mimetype, f.size, f.data + FROM files f INNER JOIN nodes n ON f.id = n.id + WHERE n.type = 'image'""") + + DBSession.execute(""" + DELETE FROM files + WHERE id IN (SELECT id FROM images)""") + + +def downgrade(): + DBSession.execute(""" + INSERT INTO files (id, filename, mimetype, size, data) + SELECT id, filename, mimetype, size, data + FROM images""") + op.drop_table('images') + op.create_table( + 'images', + sa.Column('id', sa.Integer(), sa.ForeignKey('files.id'), + primary_key=True)) + DBSession.execute(""" + INSERT INTO images (id) + SELECT id + FROM nodes + WHERE type = 'image'""") diff --git a/kotti/alembic/versions/9398ccf41c2_add_content_state_fo.py b/kotti/alembic/versions/9398ccf41c2_add_content_state_fo.py index 66da75571..ec96b1ec9 100644 --- a/kotti/alembic/versions/9398ccf41c2_add_content_state_fo.py +++ b/kotti/alembic/versions/9398ccf41c2_add_content_state_fo.py @@ -6,13 +6,13 @@ """ +from alembic import op +import sqlalchemy as sa + # revision identifiers, used by Alembic. revision = '9398ccf41c2' down_revision = None -from alembic import op -import sqlalchemy as sa - def upgrade(): op.add_column('contents', sa.Column('state', sa.String(50))) diff --git a/kotti/events.py b/kotti/events.py index dc06f69a9..e1aad281f 100644 --- a/kotti/events.py +++ b/kotti/events.py @@ -11,53 +11,50 @@ """ -from collections import defaultdict +from collections import OrderedDict from datetime import datetime -try: # pragma: no cover - from collections import OrderedDict - OrderedDict # pyflakes -except ImportError: # pragma: no cover - from ordereddict import OrderedDict import sqlalchemy.event -from sqlalchemy.orm import load_only import venusian -from sqlalchemy.orm import mapper from pyramid.location import lineage from pyramid.threadlocal import get_current_request -from zope.deprecation.deprecation import deprecated +from sqlalchemy.orm import load_only +from sqlalchemy.orm import mapper +from sqlalchemy_utils.functions import has_changes +from zope.deprecation import deprecated from kotti import DBSession +from kotti import get_settings from kotti.resources import Content from kotti.resources import LocalGroup from kotti.resources import Node from kotti.resources import Tag from kotti.resources import TagsToContents +from kotti.security import Principal from kotti.security import get_principals from kotti.security import list_groups from kotti.security import list_groups_raw -from kotti.security import Principal from kotti.security import set_groups from kotti.sqla import no_autoflush -class ObjectEvent(object): +class ObjectEvent: """Event related to an object.""" - def __init__(self, object, request=None): + def __init__(self, obj, request=None): """Constructor. - :param object: The (content) object related to the event. This is an + :param obj: The (content) object related to the event. This is an instance of :class:`kotti.resources.Node` or one its descendants for content related events, but it can be anything. - :type object: arbitrary + :type obj: arbitrary :param request: current request - :type request: :class:`pyramid.request.Request` + :type request: :class:`kotti.request.Request` """ - self.object = object + self.object = obj self.request = request @@ -78,21 +75,60 @@ class ObjectAfterDelete(ObjectEvent): .. deprecated:: 0.9 """ -deprecated('ObjectAfterDelete', - "The ObjectAfterDelete event is deprecated and will be no longer " - "available starting with Kotti 0.10.") + + +deprecated( + "ObjectAfterDelete", + "The ObjectAfterDelete event is deprecated and will be no longer " + "available starting with Kotti 0.10.", +) class UserDeleted(ObjectEvent): """This event is emitted when an user object is deleted from the DB.""" -class DispatcherDict(defaultdict, OrderedDict): - """Base class for dispatchers""" +class DispatcherDict(OrderedDict): + # Source: http://stackoverflow.com/a/6190500/562769 + def __init__(self, *a, **kw): + OrderedDict.__init__(self, *a, **kw) + self.default_factory = list + + def __getitem__(self, key): + try: + return OrderedDict.__getitem__(self, key) + except KeyError: + return self.__missing__(key) + + def __missing__(self, key): + if self.default_factory is None: + raise KeyError(key) + self[key] = value = self.default_factory() + return value + + def __reduce__(self): + if self.default_factory is None: + args = tuple() + else: + args = (self.default_factory,) + return type(self), args, None, None, self.items() + + def copy(self): + return self.__copy__() - def __init__(self, *args, **kwargs): - defaultdict.__init__(self, list) - OrderedDict.__init__(self, *args, **kwargs) + def __copy__(self): + return type(self)(self.default_factory, self) + + def __deepcopy__(self, memo): + import copy + + return type(self)(self.default_factory, copy.deepcopy(self.items())) + + def __repr__(self): + return "OrderedDefaultDict({}, {})".format( + self.default_factory, + OrderedDict.__repr__(self), + ) class Dispatcher(DispatcherDict): @@ -102,11 +138,11 @@ class Dispatcher(DispatcherDict): >>> class SubEvent(BaseEvent): pass >>> class UnrelatedEvent(object): pass >>> def base_listener(event): - ... print 'Called base listener' + ... print('Called base listener') >>> def sub_listener(event): - ... print 'Called sub listener' + ... print('Called sub listener') >>> def unrelated_listener(event): - ... print 'Called unrelated listener' + ... print('Called unrelated listener') ... return 1 >>> dispatcher = Dispatcher() @@ -125,6 +161,7 @@ class Dispatcher(DispatcherDict): Called unrelated listener [1] """ + def __call__(self, event): results = [] for event_type, handlers in self.items(): @@ -160,11 +197,13 @@ class ObjectEventDispatcher(DispatcherDict): >>> dispatcher(ObjectInsert(SubObject())) ['base', 'sub', 'all'] """ + def __call__(self, event): results = [] for (evtype, objtype), handlers in self.items(): - if (isinstance(event, evtype) and - (objtype is None or isinstance(event.object, objtype))): + if isinstance(event, evtype) and ( + objtype is None or isinstance(event.object, objtype) + ): for handler in handlers: results.append(handler(event)) return results @@ -175,12 +214,14 @@ def clear(): objectevent_listeners.clear() listeners[ObjectEvent].append(objectevent_listeners) + listeners = Dispatcher() notify = listeners.__call__ objectevent_listeners = ObjectEventDispatcher() clear() +# noinspection PyUnusedLocal,PyShadowingNames def _after_delete(mapper, connection, target): """ Trigger the Kotti event :class:``ObjectAfterDelete``. @@ -197,6 +238,7 @@ def _after_delete(mapper, connection, target): notify(ObjectAfterDelete(target, get_current_request())) +# noinspection PyUnusedLocal def _before_flush(session, flush_context, instances): """Trigger the following Kotti :class:``ObjectEvent`` events in this order: @@ -219,27 +261,27 @@ def _before_flush(session, flush_context, instances): def set_owner(event): """Set ``owner`` of the object that triggered the event. - :param event: event that trigerred this handler. + :param event: event that triggered this handler. :type event: :class:`ObjectInsert` """ obj, request = event.object, event.request - if request is not None and isinstance(obj, Node) and obj.owner is None: + if request is not None and isinstance(obj, Node): userid = request.authenticated_userid if userid is not None: - userid = unicode(userid) # Set owner metadata: - obj.owner = userid + if obj.owner is None: + obj.owner = userid # Add owner role for userid if it's not inherited already: - if u'role:owner' not in list_groups(userid, obj): - groups = list_groups_raw(userid, obj) | set([u'role:owner']) + if "role:owner" not in list_groups(userid, obj): + groups = list_groups_raw(userid, obj) | {"role:owner"} set_groups(userid, obj, groups) def set_creation_date(event): """Set ``creation_date`` of the object that triggered the event. - :param event: event that trigerred this handler. + :param event: event that triggered this handler. :type event: :class:`ObjectInsert` """ @@ -251,30 +293,40 @@ def set_creation_date(event): def set_modification_date(event): """Update ``modification_date`` of the object that triggered the event. - :param event: event that trigerred this handler. + :param event: event that triggered this handler. :type event: :class:`ObjectUpdate` """ - event.object.modification_date = datetime.now() + exclude = [] + for e in get_settings()["kotti.modification_date_excludes"]: + if isinstance(event.object, e.class_): + exclude.append(e.key) + if has_changes(event.object, exclude=exclude): + event.object.modification_date = datetime.now() + + +# noinspection PyUnusedLocal def delete_orphaned_tags(event): """Delete Tag instances / records when they are not associated with any content. - :param event: event that trigerred this handler. + :param event: event that triggered this handler. :type event: :class:`ObjectAfterDelete` """ + # noinspection PyUnresolvedReferences DBSession.query(Tag).filter(~Tag.content_tags.any()).delete( - synchronize_session=False) + synchronize_session=False + ) def cleanup_user_groups(event): """Remove a deleted group from the groups of a user/group and remove all local group entries of it. - :param event: event that trigerred this handler. + :param event: event that triggered this handler. :type event: :class:`UserDeleted` """ name = event.object.name @@ -285,30 +337,36 @@ def cleanup_user_groups(event): for user_or_group in users_groups: principals[user_or_group].groups.remove(name) - DBSession.query(LocalGroup).filter( - LocalGroup.principal_name == name).delete() + DBSession.query(LocalGroup).filter(LocalGroup.principal_name == name).delete() def reset_content_owner(event): """Reset the owner of the content from the deleted owner. - :param event: event that trigerred this handler. + :param event: event that triggered this handler. :type event: :class:`UserDeleted` """ - contents = DBSession.query(Content).filter( - Content.owner == event.object.name).all() + contents = DBSession.query(Content).filter(Content.owner == event.object.name).all() for content in contents: content.owner = None def _update_children_paths(old_parent_path, new_parent_path): - for child in DBSession.query(Node).options( - load_only('path', 'type')).filter( - Node.path.startswith(old_parent_path + '/')): + for child in ( + DBSession.query(Node) + .options(load_only("path", "type")) + .filter(Node.path.startswith(old_parent_path)) + .order_by(Node.path) + ): + if child.path == new_parent_path: + # The child is the node itself and has already be renamed. + # Nothing to do! + continue child.path = new_parent_path + child.path[len(old_parent_path):] +# noinspection PyUnusedLocal,SpellCheckingInspection @no_autoflush def _set_path_for_new_name(target, value, oldvalue, initiator): """Triggered whenever the Node's 'name' attribute is set. @@ -316,7 +374,7 @@ def _set_path_for_new_name(target, value, oldvalue, initiator): Is called with all kind of weird edge cases, e.g. name is 'None', parent is 'None' etc. """ - if getattr(target, '_kotti_set_path_for_new_name', False): + if getattr(target, "_kotti_set_path_for_new_name", False): # we're being called recursively (see below) return @@ -324,17 +382,20 @@ def _set_path_for_new_name(target, value, oldvalue, initiator): # Our name is about to be set to 'None', so skip. return - if target.__parent__ is None and value != '': + if target.__parent__ is None and value != "": # Our parent hasn't been set yet. Skip, unless we're the root # object (which always has an empty string as name). return old_path = target.path line = tuple(reversed(tuple(lineage(target)))) - target_path = u'/'.join(node.__name__ for node in line[:-1]) - target_path += u'/{0}'.format(value) + target_path = "/".join(node.__name__ for node in line[:-1]) + if target.__parent__ is None and value == "": + # We're a new root object + target_path = "/" + else: + target_path += f"/{value}/" target.path = target_path - # We need to set the name to value here so that the subsequent # UPDATE in _update_children_paths will include the new 'name' # already. We have to make sure that we don't end up in an @@ -344,13 +405,14 @@ def _set_path_for_new_name(target, value, oldvalue, initiator): try: target.name = value finally: + # noinspection PyProtectedMember del target._kotti_set_path_for_new_name if old_path and target.id is not None: _update_children_paths(old_path, target_path) else: for child in _all_children(target): - child.path = u'/'.join([child.__parent__.path, child.__name__]) + child.path = f"{child.__parent__.path}{child.__name__}/" def _all_children(item, _all=None): @@ -364,11 +426,12 @@ def _all_children(item, _all=None): return _all +# noinspection PyUnusedLocal,PyUnusedLocal,SpellCheckingInspection @no_autoflush def _set_path_for_new_parent(target, value, oldvalue, initiator): """Triggered whenever the Node's 'parent' attribute is set. """ - if value is None: + if value is None or value == oldvalue: # The parent is about to be set to 'None', so skip. return @@ -376,7 +439,7 @@ def _set_path_for_new_parent(target, value, oldvalue, initiator): # The object's name is still 'None', so skip. return - if value.__parent__ is None and value.__name__ != u'': + if value.__parent__ is None and value.__name__ != "": # Our parent doesn't have a parent, and it's not root either. return @@ -387,8 +450,8 @@ def _set_path_for_new_parent(target, value, oldvalue, initiator): # If any of our parents don't have a name yet, skip return - target_path = u'/'.join(node.__name__ for node in line) - target_path += u'/{0}'.format(target.__name__) + target_path = "/".join(node.__name__ for node in line) + target_path += f"/{target.__name__}/" target.path = target_path if old_path and target.id is not None: @@ -398,28 +461,29 @@ def _set_path_for_new_parent(target, value, oldvalue, initiator): # children. This is the case when we create an object with # children before we assign the object itself to a parent. for child in _all_children(target): - child.path = u'/'.join([child.__parent__.path, child.__name__]) + child.path = f"{child.__parent__.path}{child.__name__}/" -class subscribe(object): +# noinspection PyPep8Naming +class subscribe: """Function decorator to attach the decorated function as a handler for a Kotti event. Example:: from kotti.events import ObjectInsert from kotti.events import subscribe - from kotti.resurces import Document + from kotti.resources import Document @subscribe() def on_all_events(event): # this will be executed on *every* event - print "Some kind of event occured" + print("Some kind of event occured") @subscribe(ObjectInsert) def on_insert(event): # this will be executed on every object insert context = event.object request = event.request - print "Object insert" + print("Object insert") @subscribe(ObjectInsert, Document) def on_document_insert(event): @@ -427,7 +491,7 @@ def on_document_insert(event): # is an instance of Document context = event.object request = event.request - print "Document insert" + print("Document insert") """ @@ -446,6 +510,7 @@ def __init__(self, evttype=object, objtype=None): self.evttype = evttype self.objtype = objtype + # noinspection PyUnusedLocal def register(self, context, name, obj): if issubclass(self.evttype, ObjectEvent): objectevent_listeners[(self.evttype, self.objtype)].append(obj) @@ -454,7 +519,7 @@ def register(self, context, name, obj): def __call__(self, wrapped): - self.venusian.attach(wrapped, self.register, category='kotti') + self.venusian.attach(wrapped, self.register, category="kotti") return wrapped @@ -471,16 +536,17 @@ def wire_sqlalchemy(): # pragma: no cover return else: _WIRED_SQLALCHMEY = True - sqlalchemy.event.listen(mapper, 'after_delete', _after_delete) - sqlalchemy.event.listen(DBSession, 'before_flush', _before_flush) + sqlalchemy.event.listen(mapper, "after_delete", _after_delete) + sqlalchemy.event.listen(DBSession, "before_flush", _before_flush) # Update the 'path' attribute on changes to 'name' or 'parent' + sqlalchemy.event.listen(Node.name, "set", _set_path_for_new_name, propagate=True) sqlalchemy.event.listen( - Node.name, 'set', _set_path_for_new_name, propagate=True) - sqlalchemy.event.listen( - Node.parent, 'set', _set_path_for_new_parent, propagate=True) + Node.parent, "set", _set_path_for_new_parent, propagate=True + ) +# noinspection PyUnusedLocal def includeme(config): """ Pyramid includeme hook. @@ -494,29 +560,24 @@ def includeme(config): wire_sqlalchemy() # Set content owner on content creation - objectevent_listeners[ - (ObjectInsert, Content)].append(set_owner) + objectevent_listeners[(ObjectInsert, Content)].append(set_owner) # Set content creation date on content creation - objectevent_listeners[ - (ObjectInsert, Content)].append(set_creation_date) + objectevent_listeners[(ObjectInsert, Content)].append(set_creation_date) # Set content modification date on content updates - objectevent_listeners[ - (ObjectUpdate, Content)].append(set_modification_date) + objectevent_listeners[(ObjectUpdate, Content)].append(set_modification_date) # Delete orphaned tags after a tag association has ben deleted - objectevent_listeners[ - (ObjectAfterDelete, TagsToContents)].append(delete_orphaned_tags) + objectevent_listeners[(ObjectAfterDelete, TagsToContents)].append( + delete_orphaned_tags + ) # Initialze the workflow on content creation. - objectevent_listeners[ - (ObjectInsert, Content)].append(initialize_workflow) + objectevent_listeners[(ObjectInsert, Content)].append(initialize_workflow) # Perform some cleanup when a user or group is deleted - objectevent_listeners[ - (UserDeleted, Principal)].append(cleanup_user_groups) + objectevent_listeners[(UserDeleted, Principal)].append(cleanup_user_groups) # Remove the owner from content when the corresponding user is deleted - objectevent_listeners[ - (UserDeleted, Principal)].append(reset_content_owner) + objectevent_listeners[(UserDeleted, Principal)].append(reset_content_owner) diff --git a/kotti/fanstatic.py b/kotti/fanstatic.py index e94c37f0c..d58358f63 100644 --- a/kotti/fanstatic.py +++ b/kotti/fanstatic.py @@ -1,22 +1,21 @@ -# -*- coding: utf-8 -*- - -from __future__ import absolute_import +from typing import List +from typing import Optional +from typing import Union from fanstatic import Group from fanstatic import Library from fanstatic import Resource from js.angular import angular -from js.bootstrap import bootstrap_js from js.bootstrap import bootstrap_css -from js.html5shiv import html5shiv +from js.bootstrap import bootstrap_js from js.fineuploader import fineuploader +from js.html5shiv import html5shiv from js.jquery import jquery from js.jquery_form import jquery_form from js.jquery_tablednd import jquery_tablednd from js.jqueryui import bootstrap as jqueryui_bootstrap_theme from js.jqueryui_tagit import tagit as ui_tagit - # This is needed until ``kotti.views.form.deferred_tag_it_widget`` is converted # to a class with a ``requirements`` attribute (that would be auto_needed by # ``js.deform[_bootstrap]``). @@ -24,33 +23,22 @@ # Kotti's resources lib_kotti = Library("kotti", "static") -kotti_js = Resource( - lib_kotti, - "kotti.js", - minified="kotti.min.js", - bottom=True) contents_view_js = Resource( lib_kotti, "contents.js", - depends=[kotti_js, jquery_tablednd, ], + depends=[jquery_tablednd], minified="contents.min.js", - bottom=True) + bottom=True, +) base_css = Resource( lib_kotti, "base.css", depends=[bootstrap_css], minified="base.min.css", - dont_bundle=True) -edit_css = Resource( - lib_kotti, - "edit.css", - depends=[base_css], - minified="edit.min.css") -view_css = Resource( - lib_kotti, - "view.css", - depends=[base_css], - minified="view.min.css") + dont_bundle=True, +) +edit_css = Resource(lib_kotti, "edit.css", depends=[base_css], minified="edit.min.css") +view_css = Resource(lib_kotti, "view.css", depends=[base_css], minified="view.min.css") # Resources for content upload views upload_js = Resource( @@ -58,32 +46,37 @@ "upload.js", depends=[angular, fineuploader], # minified="upload.min.js", needs special minifying - bottom=True) + bottom=True, +) upload_css = Resource( - lib_kotti, - "upload.css", - depends=[base_css], - minified="upload.min.css") + lib_kotti, "upload.css", depends=[base_css], minified="upload.min.css" +) upload = Group([upload_js, upload_css]) -class NeededGroup(object): +class NeededGroup: """A collection of fanstatic resources that supports dynamic appending of resources after initialization""" - def __init__(self, resources=[]): + def __init__( + self, resources: Optional[List[Union[Resource, "NeededGroup"]]] = None + ): # noqa + + if resources is None: + resources = [] if not isinstance(resources, list): raise ValueError( "resources must be a list of fanstatic.Resource " - "and/or fanstatic.Group objects") + "and/or fanstatic.Group objects" + ) self.resources = [] for resource in resources: self.add(resource) - def add(self, resource): + def add(self, resource: Union["NeededGroup", Resource]): """resource may be a: - :class:`fanstatic.Resource` object or @@ -96,39 +89,19 @@ def add(self, resource): else: raise ValueError( "resource must be a NeededGroup," - "fanstatic.Resource or fanstatic.Group object") + "fanstatic.Resource or fanstatic.Group object" + ) def need(self): # pragma: no cover # this is tested in fanstatic itself; we should add browser tests # for `view_needed` and `edit_needed` (see below) Group(self.resources).need() -view_needed_css = NeededGroup([ - view_css, - ]) -view_needed_js = NeededGroup([ - jquery, - bootstrap_js, - html5shiv, - ]) -view_needed = NeededGroup([ - view_needed_css, - view_needed_js, - ]) - -edit_needed_css = NeededGroup([ - edit_css, - jqueryui_bootstrap_theme, - ]) -edit_needed_js = NeededGroup([ - jquery, - bootstrap_js, - html5shiv, - kotti_js, - jquery_form, - # deform_bootstrap_js, - ]) -edit_needed = NeededGroup([ - edit_needed_css, - edit_needed_js, - ]) + +view_needed_css = NeededGroup([view_css]) +view_needed_js = NeededGroup([jquery, bootstrap_js, html5shiv]) +view_needed = NeededGroup([view_needed_css, view_needed_js]) + +edit_needed_css = NeededGroup([edit_css, jqueryui_bootstrap_theme]) +edit_needed_js = NeededGroup([jquery, bootstrap_js, html5shiv, jquery_form]) +edit_needed = NeededGroup([edit_needed_css, edit_needed_js]) diff --git a/kotti/filedepot.py b/kotti/filedepot.py new file mode 100644 index 000000000..251fb9a75 --- /dev/null +++ b/kotti/filedepot.py @@ -0,0 +1,729 @@ +import logging +import mimetypes +import uuid +from cgi import FieldStorage +from datetime import datetime +from typing import Callable +from typing import Dict +from typing import List +from typing import Optional +from typing import Tuple +from typing import Union + +from depot.fields.sqlalchemy import _SQLAMutationTracker +from depot.fields.upload import UploadedFile +from depot.io.interfaces import FileStorage +from depot.io.memory import MemoryStoredFile +from depot.manager import DepotManager +from depot.utils import make_content_disposition +from pyramid import tweens +from pyramid.config import Configurator +from pyramid.httpexceptions import HTTPMovedPermanently +from pyramid.httpexceptions import HTTPNotFound +from pyramid.registry import Registry +from pyramid.response import FileIter +from pyramid.response import Response +from pyramid.response import _BLOCK_SIZE +from sqlalchemy import Column +from sqlalchemy import DateTime +from sqlalchemy import Integer +from sqlalchemy import LargeBinary +from sqlalchemy import String +from sqlalchemy import Unicode +from sqlalchemy import event +from sqlalchemy.engine.base import Connection +from sqlalchemy.orm import deferred +from sqlalchemy.orm.attributes import Event +from sqlalchemy.util.langhelpers import _symbol + +from kotti import Base +from kotti import DBSession +from kotti import get_settings +from kotti.events import ObjectInsert +from kotti.events import ObjectUpdate +from kotti.request import Request +from kotti.util import _to_fieldstorage +from kotti.util import camel_case_to_name +from kotti.util import command +from kotti.util import extract_from_settings + +_marker = object() + + +class DBStoredFile(Base): + """ :class:`depot.io.interfaces.StoredFile` implementation that stores + file data in SQL database. + + Can be used together with :class:`kotti.filedepot.DBFileStorage` to + implement blobs storage in the database. + """ + + __tablename__ = "blobs" + + #: Primary key column in the DB + #: (:class:`sqlalchemy.types.Integer`) + id = Column(Integer(), primary_key=True) + #: Unique file id given to this blob + #: (:class:`sqlalchemy.types.String`) + file_id = Column(String(36), index=True) + #: The original filename it had when it was uploaded. + #: (:class:`sqlalchemy.types.String`) + filename = Column(Unicode(100)) + #: MIME type of the blob + #: (:class:`sqlalchemy.types.String`) + content_type = Column(String(100)) + #: Size of the blob in bytes + #: (:class:`sqlalchemy.types.Integer`) + content_length = Column(Integer()) + #: Date / time the blob was created or last modified + #: (:class:`sqlalchemy.types.DateTime`) + last_modified = Column(DateTime()) + #: The binary data itself + #: (:class:`sqlalchemy.types.LargeBinary`) + data = deferred(Column("data", LargeBinary())) + + _cursor = 0 + _data = _marker + + public_url = None + + def __init__( + self, + file_id, + filename=None, + content_type=None, + last_modified=None, + content_length=None, + **kwds, + ): + self.file_id = file_id + self.filename = filename + self.content_type = content_type + self.last_modified = last_modified or datetime.now() + self.content_length = content_length + + for k, v in kwds.items(): + setattr(self, k, v) + + def read(self, n: int = -1) -> bytes: + """Reads ``n`` bytes from the file. + + If ``n`` is not specified or is ``-1`` the whole + file content is read in memory and returned + """ + if self._data is _marker: + file_id = DBSession.merge(self).file_id + self._data = ( + DBSession.query(DBStoredFile.data).filter_by(file_id=file_id).scalar() + ) + + if n == -1: + result = self._data[self._cursor :] + else: + result = self._data[self._cursor : self._cursor + n] + + self._cursor += len(result) + + return result + + @staticmethod + def close(*args, **kwargs) -> None: + """Implement :meth:`StoredFile.close`. + :class:`DBStoredFile` never closes. + """ + return + + @staticmethod + def closed() -> bool: + """Implement :meth:`StoredFile.closed`. + """ + return False + + @staticmethod + def writable() -> bool: + """Implement :meth:`StoredFile.writable`. + """ + return False + + @staticmethod + def seekable() -> bool: + """Implement :meth:`StoredFile.seekable`. + """ + return True + + def seek(self, offset: int, whence: int = 0) -> None: + """ Change stream position. + + Change the stream position to the given byte offset. The offset is + interpreted relative to the position indicated by whence. + + :param offset: Position for the cursor + :type offset: int + + :param whence: * 0 -- start of stream (the default); + offset should be zero or positive + * 1 -- current stream position; offset may be negative + * 2 -- end of stream; offset is usually negative + :type whence: int + """ + if whence == 0: + self._cursor = offset + elif whence in (1, 2): + self._cursor = self._cursor + offset + else: + raise ValueError("whence must be 0, 1 or 2") + + def tell(self) -> int: + """ Returns current position of file cursor + + :result: Current file cursor position. + :rtype: int + """ + return self._cursor + + @property + def name(self) -> str: + """Implement :meth:`StoredFile.name`. + + :result: the filename of the saved file + :rtype: string + """ + return self.filename + + @classmethod + def __declare_last__(cls) -> None: + """ Executed by SQLAlchemy as part of mapper configuration + + When the data changes, we want to reset the cursor position of target + instance, to allow proper streaming of data. + """ + event.listen(DBStoredFile.data, "set", handle_change_data) + + +# noinspection PyUnusedLocal +def handle_change_data( + target: DBStoredFile, + value: Optional[bytes], + oldvalue: Union[bytes, _symbol], + initiator: Event, +) -> None: + target._cursor = 0 + target._data = _marker + + +def set_metadata(event: Union[ObjectUpdate, ObjectInsert]) -> None: + """Set DBStoredFile metadata based on data + + :param event: event that triggered this handler. + :type event: :class:`ObjectInsert` or :class:`ObjectUpdate` + """ + obj = event.object + obj.content_length = obj.data and len(obj.data) or 0 + obj.last_modified = datetime.now() + + +class DBFileStorage(FileStorage): + """Implementation of :class:`depot.io.interfaces.FileStorage`, + + Uses `kotti.filedepot.DBStoredFile` to store blob data in an SQL database. + """ + + # noinspection PyMethodOverriding + @staticmethod + def get(file_id: str) -> DBStoredFile: + """Returns the file given by the file_id + + :param file_id: the unique id associated to the file + :type file_id: string + :result: a :class:`kotti.filedepot.DBStoredFile` instance + :rtype: :class:`kotti.filedepot.DBStoredFile` + """ + + f = DBSession.query(DBStoredFile).filter_by(file_id=file_id).first() + if f is None: + raise OSError + return f + + def create( + self, + content: Union[bytes, FieldStorage], + filename: Optional[str] = None, + content_type: Optional[str] = None, + ) -> str: + """Saves a new file and returns the file id + + :param content: can either be ``bytes``, another ``file object`` + or a :class:`cgi.FieldStorage`. When ``filename`` and + ``content_type`` parameters are not provided they are + deducted from the content itself. + + :param filename: filename for this file + :type filename: string + + :param content_type: Mimetype of this file + :type content_type: string + + :return: the unique ``file_id`` associated to this file + :rtype: string + """ + new_file_id = str(uuid.uuid1()) + content, filename, content_type = self.fileinfo(content, filename, content_type) + if hasattr(content, "read"): + content = content.read() + + fstore = DBStoredFile( + data=content, + file_id=new_file_id, + filename=filename, + content_type=content_type, + ) + DBSession.add(fstore) + return new_file_id + + def replace( + self, + file_or_id: Union[DBStoredFile, str], + content: bytes, + filename: Optional[str] = None, + content_type: Optional[str] = None, + ) -> None: + """Replaces an existing file, an ``IOError`` is raised if the file + didn't already exist. + + Given a :class:`StoredFile` or its ID it will replace the current + content with the provided ``content`` value. If ``filename`` and + ``content_type`` are provided or can be deducted by the ``content`` + itself they will also replace the previous values, otherwise + the current values are kept. + + :param file_or_id: can be either ``DBStoredFile`` or a ``file_id`` + + :param content: can either be ``bytes``, another ``file object`` + or a :class:`cgi.FieldStorage`. When ``filename`` and + ``content_type`` parameters are not provided they are + deducted from the content itself. + + :param filename: filename for this file + :type filename: string + + :param content_type: Mimetype of this file + :type content_type: string + """ + + file_id = self._get_file_id(file_or_id) + + content, filename, content_type = self.fileinfo(content, filename, content_type) + + fstore = self.get(file_id) + + if filename is not None: + fstore.filename = filename + if content_type is not None: + fstore.content_type = content_type + + if hasattr(content, "read"): + content = content.read() + + fstore.data = content + + def delete(self, file_or_id: str) -> None: + """Deletes a file. If the file didn't exist it will just do nothing. + + :param file_or_id: can be either ``DBStoredFile`` or a ``file_id`` + """ + + file_id = self._get_file_id(file_or_id) + + DBSession.query(DBStoredFile).filter_by(file_id=file_id).delete() + + def exists(self, file_or_id: str) -> bool: + """Returns if a file or its ID still exist. + + :return: Returns if a file or its ID still exist. + :rtype: bool + """ + + file_id = self._get_file_id(file_or_id) + + return bool(DBSession.query(DBStoredFile).filter_by(file_id=file_id).count()) + + @staticmethod + def list(*args) -> None: + raise NotImplementedError("list() method is unimplemented.") + + @staticmethod + def _get_file_id(file_or_id: Union[DBStoredFile, str]) -> str: + if hasattr(file_or_id, "file_id"): + return file_or_id.file_id + return file_or_id + + +# noinspection PyUnusedLocal +def migrate_storage(from_storage: str, to_storage: str) -> None: + log = logging.getLogger(__name__) + + old_default = DepotManager._default_depot + DepotManager._default_depot = to_storage + + for klass, props in _SQLAMutationTracker.mapped_entities.items(): + log.info("Migrating %r", klass) + + mapper = klass._sa_class_manager.mapper + + # use type column to avoid polymorphism issues, getting the same + # Node item multiple times. + type_ = camel_case_to_name(klass.__name__) + for instance in DBSession.query(klass).filter_by(type=type_): + for prop in props: + uf = getattr(instance, prop) + if not uf: + continue + pk = mapper.primary_key_from_instance(instance) + log.info("Migrating %s for %r with pk %r", prop, klass, pk) + + filename = uf["filename"] + content_type = uf["content_type"] + data = _to_fieldstorage( + fp=uf.file, + filename=filename, + mimetype=content_type, + size=uf.file.content_length, + ) + setattr(instance, prop, data) + + DepotManager._default_depot = old_default + + +def migrate_storages_command(): # pragma: no cover + __doc__ = """ Migrate blobs between two configured filedepot storages + + Usage: + kotti-migrate-storage \ + --from-storage \ + --to-storage + + Options: + -h --help Show this screen. + --from-storage The storage name that has blob data to migrate + --to-storage The storage name where we want to put the blobs + """ + return command( + lambda args: migrate_storage( + from_storage=args["--from-storage"], to_storage=args["--to-storage"] + ), + __doc__, + ) + + +class StoredFileResponse(Response): + """ A Response object that can be used to serve an UploadedFile instance. + + Code adapted from :class:`pyramid.response.FileResponse`. + """ + + def __init__( + self, + f: MemoryStoredFile, + request: Request, + disposition: str = "attachment", + cache_max_age: int = 604800, + content_type: None = None, + content_encoding: None = None, + ) -> None: + """ + :param f: the ``UploadedFile`` file field value. + :type f: :class:`depot.io.interfaces.StoredFile` + + :param request: Current request. + :type request: :class:`pyramid.request.Request` + + :param disposition: + :type disposition: + + :param cache_max_age: The number of seconds that should be used to HTTP + cache this response. + + :param content_type: The content_type of the response. + + :param content_encoding: The content_encoding of the response. + It's generally safe to leave this set to + ``None`` if you're serving a binary file. + This argument will be ignored if you also + leave ``content-type`` as ``None``. + """ + + if f.public_url: + raise HTTPMovedPermanently(f.public_url) + + content_encoding, content_type = self._get_type_and_encoding( + content_encoding, content_type, f + ) + + super().__init__( + conditional_response=True, + content_type=content_type, + content_encoding=content_encoding, + ) + + app_iter = None + if ( + request is not None + and not get_settings()["kotti.depot_replace_wsgi_file_wrapper"] + ): + environ = request.environ + if "wsgi.file_wrapper" in environ: + app_iter = environ["wsgi.file_wrapper"](f, _BLOCK_SIZE) + if app_iter is None: + app_iter = FileIter(f) + self.app_iter = app_iter + + # assignment of content_length must come after assignment of app_iter + self.content_length = f.content_length + self.last_modified = f.last_modified + + if cache_max_age is not None: + self.cache_expires = cache_max_age + self.cache_control.public = True + + self.etag = self.generate_etag(f) + + content_disposition = make_content_disposition(disposition, f.filename) + + self.content_disposition = content_disposition + + @staticmethod + def _get_type_and_encoding( + content_encoding: None, content_type: None, f: MemoryStoredFile + ) -> Tuple["NoneType", str]: + content_type = content_type or getattr(f, "content_type", None) + if content_type is None: + content_type, content_encoding = mimetypes.guess_type( + f.filename, strict=False + ) + if content_type is None: + content_type = "application/octet-stream" + # str-ifying content_type is a workaround for a bug in Python 2.7.7 + # on Windows where mimetypes.guess_type returns unicode for the + # content_type. + content_type = str(content_type) + return content_encoding, content_type + + @staticmethod + def generate_etag(f: MemoryStoredFile) -> str: + return f'"{f.last_modified}-{f.content_length}"' + + +def uploaded_file_response( + self: Request, + uploaded_file: UploadedFile, + disposition: str = "inline", + cache_max_age: int = 604800, +) -> StoredFileResponse: + return StoredFileResponse( + uploaded_file.file, self, disposition=disposition, cache_max_age=cache_max_age + ) + + +def uploaded_file_url(self, uploaded_file, disposition="inline"): + if disposition == "attachment": + suffix = "/download" + else: + suffix = "" + url = "{}/{}/{}{}".format( + self.application_url, + get_settings()["kotti.depot_mountpoint"][1:], + uploaded_file.path, + suffix, + ) + return url + + +class TweenFactory: + """Factory for a Pyramid tween in charge of serving Depot files. + + This is the Pyramid tween version of + :class:`depot.middleware.DepotMiddleware`. It does exactly the same as + Depot's WSGI middleware, but operates on a :class:`pyramid.request.Request` + object instead of the WSGI environment. + """ + + def __init__(self, handler: Optional[Callable], registry: Registry) -> None: + """ + :param handler: Downstream tween or main Pyramid request handler (Kotti) + :type handler: function + + :param registry: Application registry + :type registry: :class:`pyramid.registry.Registry` + """ + + self.mountpoint = registry.settings["kotti.depot_mountpoint"] + self.handler = handler + self.registry = registry + + DepotManager.set_middleware(self) + + def url_for(self, path): + return "/".join((self.mountpoint, path)) + + def __call__(self, request: Request) -> Response: + """ + :param request: Current request + :type request: :class:`kotti.request.Request` + + :return: Respone object + :rtype: :class:`pyramid.response.Response` + """ + + # Only handle GET and HEAD requests for the mountpoint. + # All other requests are passed to downstream handlers. + if request.method not in ("GET", "HEAD") or not request.path.startswith( + self.mountpoint + ): + response = self.handler(request) + return response + + # paths match this pattern + # ///[/download] + path = request.path.split("/") + if len(path) and not path[0]: + path = path[1:] + + if len(path) < 3: + response = HTTPNotFound() + return response + + __, depot, fileid = path[:3] + depot = DepotManager.get(depot) + if not depot: + response = HTTPNotFound() + return response + + try: + f = depot.get(fileid) + except (OSError, ValueError): + response = HTTPNotFound() + return response + + # if the file has a public_url, it's stored somewhere else (e.g. S3) + public_url = f.public_url + if public_url is not None: + response = HTTPMovedPermanently(public_url) + return response + + # file is not directly accessible for user agents, serve it ourselves + if path[-1] == "download": + disposition = "attachment" + else: + disposition = "inline" + response = StoredFileResponse(f, request, disposition=disposition) + return response + + +# noinspection PyUnusedLocal +def adjust_for_engine(conn: Connection, branch: bool) -> None: + # adjust for engine type + + if conn.engine.dialect.name == "mysql": # pragma: no cover + from sqlalchemy.dialects.mysql.base import LONGBLOB + + DBStoredFile.__table__.c.data.type = LONGBLOB() + + # sqlite's Unicode columns return a buffer which can't be encoded by + # a json encoder. We have to convert to a unicode string so that the value + # can be saved correctly by + # :class:`depot.fields.sqlalchemy.upload.UploadedFile` + + # noinspection PyUnusedLocal + def patched_processed_result_value(self, value, dialect): + if not value: + return None + return self._upload_type.decode(value) + + if conn.engine.dialect.name == "sqlite": # pragma: no cover + from depot.fields.sqlalchemy import UploadedFileField + + UploadedFileField.process_result_value = patched_processed_result_value + + +def extract_depot_settings( + prefix: Optional[str] = "kotti.depot.", settings: Optional[Dict[str, str]] = None +) -> List[Dict[str, str]]: # noqa + """ Merges items from a dictionary that have keys that start with `prefix` + to a list of dictionaries. + + :param prefix: A dotted string representing the prefix for the common values + :type prefix: string + + :param settings: A dictionary with settings. Result is extracted from this + :type settings: dict + + >>> settings = { + ... 'kotti.depot_mountpoint': '/depot', + ... 'kotti.depot.0.backend': 'kotti.filedepot.DBFileStorage', + ... 'kotti.depot.0.file_storage': 'var/files', + ... 'kotti.depot.0.name': 'local', + ... 'kotti.depot.1.backend': 'depot.io.gridfs.GridStorage', + ... 'kotti.depot.1.name': 'mongodb', + ... 'kotti.depot.1.uri': 'localhost://', + ... } + >>> res = extract_depot_settings('kotti.depot.', settings) + >>> print(sorted(res[0].items())) + [('backend', 'kotti.filedepot.DBFileStorage'), ('file_storage', 'var/files'), ('name', 'local')] + >>> print(sorted(res[1].items())) + [('backend', 'depot.io.gridfs.GridStorage'), ('name', 'mongodb'), ('uri', 'localhost://')] + """ + + extracted = {} + for k, v in extract_from_settings(prefix, settings).items(): + index, conf = k.split(".", 1) + index = int(index) + extracted.setdefault(index, {}) + extracted[index][conf] = v + + result = [] + for k in sorted(extracted.keys()): + result.append(extracted[k]) + + return result + + +def configure_filedepot(settings: Dict[str, str]) -> None: + config = extract_depot_settings("kotti.depot.", settings) + for conf in config: + name = conf.pop("name") + if name not in DepotManager._depots: + DepotManager.configure(name, conf, prefix="") + + +def includeme(config: Configurator) -> None: + """ Pyramid includeme hook. + + :param config: app config + :type config: :class:`pyramid.config.Configurator` + """ + + config.add_tween( + "kotti.filedepot.TweenFactory", over=tweens.MAIN, under=tweens.INGRESS + ) + config.add_request_method(uploaded_file_response, name="uploaded_file_response") + config.add_request_method(uploaded_file_url, name="uploaded_file_url") + + from kotti.events import objectevent_listeners + from kotti.events import ObjectInsert + from kotti.events import ObjectUpdate + + from sqlalchemy.event import listen + from sqlalchemy.engine import Engine + + listen(Engine, "engine_connect", adjust_for_engine) + + configure_filedepot(config.get_settings()) + + # Update file metadata on change of blob data + objectevent_listeners[(ObjectInsert, DBStoredFile)].append(set_metadata) + objectevent_listeners[(ObjectUpdate, DBStoredFile)].append(set_metadata) + + # depot's _SQLAMutationTracker._session_committed is executed on + # after_commit, that's too late for DBFileStorage to interact with the + # session + event.listen(DBSession, "before_commit", _SQLAMutationTracker._session_committed) diff --git a/kotti/interfaces.py b/kotti/interfaces.py index 58ea0c76d..ceb29d3c1 100644 --- a/kotti/interfaces.py +++ b/kotti/interfaces.py @@ -1,11 +1,8 @@ -# -*- coding: utf-8 -*- -""" -""" - +from pyramid.interfaces import ILocation from zope.interface import Interface -class INode(Interface): +class INode(ILocation): """Marker interface for all nodes (and subclasses)""" @@ -24,11 +21,6 @@ class IFile(IContent): (and subclasses thereof)""" -class IImage(IFile): - """Marker interface for all nodes of type Image - (and subclasses thereof)""" - - class IDefaultWorkflow(Interface): """Marker interface for content classes that want to use the default workflow""" diff --git a/kotti/locale/Kotti.pot b/kotti/locale/Kotti.pot index 877894b48..9b56f6039 100644 --- a/kotti/locale/Kotti.pot +++ b/kotti/locale/Kotti.pot @@ -1,29 +1,101 @@ -# +# # SOME DESCRIPTIVE TITLE # This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , 2014. +# FIRST AUTHOR , 2018. #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE 1.0\n" -"POT-Creation-Date: 2014-11-21 22:44+0100\n" +"POT-Creation-Date: 2018-01-17 13:51+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" +"Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: \n" +"Generated-By: Lingua 4.13\n" + +#: ./kotti/security.py:173 +msgid "Viewer" +msgstr "" + +#: ./kotti/security.py:174 +msgid "Editor" +msgstr "" + +#: ./kotti/security.py:175 +msgid "Owner" +msgstr "" + +#: ./kotti/security.py:176 +msgid "Admin" +msgstr "" + +#: ./kotti/resources.py:541 ./kotti/views/edit/actions.py:453 +msgid "Copy" +msgstr "" + +#: ./kotti/resources.py:542 ./kotti/views/edit/actions.py:454 +msgid "Cut" +msgstr "" + +#: ./kotti/resources.py:543 ./kotti/views/edit/actions.py:450 +msgid "Paste" +msgstr "" + +#: ./kotti/resources.py:544 ./kotti/templates/edit/rename.pt:31 +#: ./kotti/templates/edit/rename-nodes.pt:8 +#: ./kotti/templates/edit/rename-nodes.pt:42 ./kotti/views/edit/actions.py:455 +msgid "Rename" +msgstr "" + +#: ./kotti/resources.py:545 ./kotti/templates/edit/delete.pt:24 +#: ./kotti/templates/edit/delete-nodes.pt:9 +#: ./kotti/templates/edit/delete-nodes.pt:49 +#: ./kotti/templates/site-setup/delete-user.pt:24 ./kotti/views/users.py:454 +#: ./kotti/views/edit/actions.py:457 +msgid "Delete" +msgstr "" + +#: ./kotti/resources.py:556 ./kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "" + +#: ./kotti/resources.py:557 ./kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "" + +#: ./kotti/resources.py:558 +msgid "Share" +msgstr "" + +#: ./kotti/resources.py:559 ./kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "" + +#: ./kotti/resources.py:562 +msgid "Folder view" +msgstr "" + +#: ./kotti/resources.py:671 ./kotti/views/edit/content.py:61 +msgid "Document" +msgstr "" -#: ./kotti/populate.py:64 +#: ./kotti/resources.py:808 ./kotti/views/edit/content.py:73 +#: ./kotti/views/edit/content.py:87 +msgid "File" +msgstr "" + +#: ./kotti/populate.py:67 msgid "Welcome to Kotti" msgstr "" -#: ./kotti/populate.py:65 +#: ./kotti/populate.py:68 msgid "Congratulations! You have successfully installed Kotti." msgstr "" -#: ./kotti/populate.py:66 +#: ./kotti/populate.py:69 #, c-format msgid "" "\n" @@ -42,12 +114,12 @@ msgid "" "
    \n" "

    Configure

    \n" "

    \n" -" Find out how to configure your Kotti's title and many other settings using a\n" -" simple text file in your file system.\n" +" Find out how to configure your Kotti's title and many other\n" +" settings using a simple text file in your file system.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -55,7 +127,8 @@ msgid "" "
    \n" "

    Add-ons

    \n" "

    \n" -" A number of add-ons allow you to extend the functionality of your Kotti site.\n" +" A number of add-ons allow you to extend the functionality of your\n" +" Kotti site.\n" "

    \n" "

    \n" " \n" "

    Documentation

    \n" "

    \n" -" Wonder what more you can do with Kotti? What license it has? Read the\n" -" manual for more information.\n" +" Wonder what more you can do with Kotti?\n" +" What license it has?\n" +" Read the manual for more information.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/\">\n" " Documentation\n" " \n" "

    \n" @@ -80,17 +154,17 @@ msgid "" "
    \n" msgstr "" -#: ./kotti/populate.py:121 +#: ./kotti/populate.py:126 msgid "About" msgstr "" -#: ./kotti/populate.py:122 +#: ./kotti/populate.py:127 msgid "" "Our company is the leading manufacturer of foo widgets used in a wide variety" " of aviation and and industrial products." msgstr "" -#: ./kotti/populate.py:123 +#: ./kotti/populate.py:128 msgid "" "\n" "

    \n" @@ -112,777 +186,721 @@ msgid "" "\n" "\n" "

    \n" -" Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" -" \n" -" Copyright info.\n" -" Originally published in the\n" -" Extra EA-300\n" -" article.\n" +"Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +"\n" +"Copyright info.\n" +"Originally published in the\n" +" Extra EA-300\n" +"article.\n" "

    \n" msgstr "" -#: ./kotti/resources.py:610 ./kotti/views/edit/content.py:81 -msgid "Document" +#: ./kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." msgstr "" -#: ./kotti/resources.py:649 ./kotti/views/edit/content.py:113 -#: ./kotti/views/edit/content.py:62 -msgid "File" +#: ./kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." msgstr "" -#: ./kotti/resources.py:726 ./kotti/views/edit/content.py:144 -msgid "Image" +#: ./kotti/templates/editor-bar.pt:19 +msgid "View" msgstr "" -#: ./kotti/resources.py:500 -msgid "Folder view" +#: ./kotti/templates/editor-bar.pt:42 +msgid "Navigate" msgstr "" -#: ./kotti/resources.py:487 ./kotti/templates/view/folder.pt:17 -msgid "Contents" +#: ./kotti/templates/editor-bar.pt:53 +msgid "Preferences" msgstr "" -#: ./kotti/resources.py:488 ./kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" +#: ./kotti/templates/editor-bar.pt:58 +msgid "Site Setup" msgstr "" -#: ./kotti/resources.py:489 -msgid "Share" +#: ./kotti/templates/editor-bar.pt:69 +msgid "Logout" msgstr "" -#: ./kotti/resources.py:490 ./kotti/templates/actions-dropdown.pt:5 -msgid "Actions" +#: ./kotti/templates/default-view-selector.pt:3 +msgid "Set default view" msgstr "" -#: ./kotti/resources.py:491 ./kotti/views/edit/actions.py:445 -msgid "Copy" +#: ./kotti/templates/login.pt:16 +msgid "Login" msgstr "" -#: ./kotti/resources.py:492 ./kotti/views/edit/actions.py:446 -msgid "Cut" +#: ./kotti/templates/login.pt:21 ./kotti/templates/login.pt:59 +msgid "Username or email" msgstr "" -#: ./kotti/resources.py:493 ./kotti/views/edit/actions.py:442 -msgid "Paste" +#: ./kotti/templates/login.pt:26 ./kotti/views/users.py:220 +#: ./kotti/views/login.py:255 +msgid "Password" msgstr "" -#: ./kotti/resources.py:494 ./kotti/templates/edit/rename-nodes.pt:7 -#: ./kotti/templates/edit/rename-nodes.pt:55 -#: ./kotti/templates/edit/rename.pt:42 ./kotti/views/edit/actions.py:447 -msgid "Rename" +#: ./kotti/templates/login.pt:34 +msgid "Log in" msgstr "" -#: ./kotti/resources.py:495 ./kotti/templates/edit/delete-nodes.pt:8 -#: ./kotti/templates/edit/delete-nodes.pt:90 -#: ./kotti/templates/edit/delete.pt:28 -#: ./kotti/templates/site-setup/delete-user.pt:36 ./kotti/views/users.py:443 -#: ./kotti/views/edit/actions.py:449 -msgid "Delete" +#: ./kotti/templates/login.pt:45 +msgid "Forgot your password?" msgstr "" -#: ./kotti/security.py:165 -msgid "Viewer" +#. Used in sentence: "Fill out your username or email and click +#. ${reset_password} below to receive an email with a link to reset your +#. password." +#. +#: ./kotti/templates/login.pt:52 ./kotti/templates/login.pt:67 +msgid "Reset password" msgstr "" -#: ./kotti/security.py:166 -msgid "Editor" +#. Canonical text for ${reset_password} is: "Reset password" +#: ./kotti/templates/login.pt:50 +msgid "" +"Fill out your username or email and click ${reset_password} below to receive " +"an email with a link to reset your password." msgstr "" -#: ./kotti/security.py:167 -msgid "Owner" +#: ./kotti/templates/login.pt:78 +msgid "Not registered yet?" msgstr "" -#: ./kotti/security.py:168 -msgid "Admin" +#: ./kotti/templates/login.pt:85 +msgid "Register for an account on this site." msgstr "" -#: ./kotti/templates/add-dropdown.pt:5 -msgid "Add" +#: ./kotti/templates/forbidden.pt:10 +msgid "Forbidden" msgstr "" -#: ./kotti/templates/add-dropdown.pt:27 -msgid "Upload Content" +#: ./kotti/templates/add-dropdown.pt:3 +msgid "Add" msgstr "" -#: ./kotti/templates/default-view-selector.pt:5 -msgid "Set default view" +#: ./kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" msgstr "" -#: ./kotti/templates/editor-bar.pt:35 -msgid "View" +#: ./kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" msgstr "" -#: ./kotti/templates/editor-bar.pt:76 -msgid "Navigate" +#: ./kotti/templates/email-reset-password.pt:2 +msgid "Reset your password for ${site_title}." msgstr "" -#: ./kotti/templates/editor-bar.pt:98 -msgid "Preferences" +#: ./kotti/templates/email-reset-password.pt:4 +#: ./kotti/templates/email-set-password.pt:4 +msgid "Hello, ${user_title}!" msgstr "" -#: ./kotti/templates/editor-bar.pt:109 -msgid "Site Setup" +#: ./kotti/templates/email-reset-password.pt:5 +msgid "Click this link to reset your password at ${site_title}: ${url}" msgstr "" -#: ./kotti/templates/editor-bar.pt:131 -msgid "Logout" +#: ./kotti/templates/email-set-password.pt:2 +msgid "Your registration for ${site_title}" msgstr "" -#: ./kotti/templates/email-reset-password.pt:3 -msgid "Reset your password for ${site_title}." +#: ./kotti/templates/email-set-password.pt:5 +msgid "You are joining ${site_title}." msgstr "" -#: ./kotti/templates/email-reset-password.pt:7 #: ./kotti/templates/email-set-password.pt:6 -msgid "Hello, ${user_title}!" +msgid "Click here to set your password and log in: ${url}" msgstr "" -#: ./kotti/templates/email-reset-password.pt:10 -msgid "Click this link to reset your password at ${site_title}: ${url}" +#: ./kotti/templates/edit/share.pt:8 +msgid "Share ${title}" msgstr "" -#: ./kotti/templates/email-set-password.pt:3 -msgid "Your registration for ${site_title}" +#: ./kotti/templates/edit/share.pt:14 ./kotti/templates/edit/share.pt:20 +#: ./kotti/templates/edit/share.pt:27 ./kotti/templates/site-setup/users.pt:48 +msgid "Search users and groups" msgstr "" -#: ./kotti/templates/email-set-password.pt:9 -msgid "You are joining ${site_title}." +#: ./kotti/templates/edit/share.pt:34 ./kotti/templates/site-setup/users.pt:56 +msgid "Search" msgstr "" -#: ./kotti/templates/email-set-password.pt:12 -msgid "Click here to set your password and log in: ${url}" +#: ./kotti/templates/edit/share.pt:44 +msgid "Assign local roles" msgstr "" -#: ./kotti/templates/forbidden.pt:7 -msgid "Forbidden" +#: ./kotti/templates/edit/share.pt:49 ./kotti/templates/edit/change-state.pt:19 +#: ./kotti/templates/edit/delete-nodes.pt:22 +#: ./kotti/templates/edit/upload.pt:18 ./kotti/templates/edit/upload.pt:108 +#: ./kotti/templates/edit/contents.pt:35 ./kotti/templates/view/folder.pt:21 +#: ./kotti/templates/site-setup/users.pt:69 +msgid "Type" msgstr "" -#: ./kotti/templates/login.pt:16 -msgid "Login" +#: ./kotti/templates/edit/share.pt:50 ./kotti/templates/site-setup/users.pt:70 +#: ./kotti/views/users.py:215 +msgid "Name" msgstr "" -#: ./kotti/templates/login.pt:25 ./kotti/templates/login.pt:87 -msgid "Username or email" +#: ./kotti/templates/edit/share.pt:61 ./kotti/templates/site-setup/users.pt:81 +#: ./kotti/views/users.py:321 ./kotti/views/users.py:515 +#: ./kotti/views/users.py:550 +msgid "User" msgstr "" -#: ./kotti/templates/login.pt:34 ./kotti/views/login.py:213 -#: ./kotti/views/users.py:212 -msgid "Password" +#: ./kotti/templates/edit/share.pt:62 ./kotti/templates/site-setup/users.pt:82 +#: ./kotti/views/users.py:196 ./kotti/views/users.py:356 +#: ./kotti/views/users.py:515 ./kotti/views/users.py:550 +msgid "Group" msgstr "" -#: ./kotti/templates/login.pt:47 -msgid "Log in" +#: ./kotti/templates/edit/share.pt:65 ./kotti/templates/site-setup/users.pt:85 +msgid "Gravatar" msgstr "" -#: ./kotti/templates/login.pt:65 -msgid "Forgot your password?" +#: ./kotti/templates/edit/share.pt:70 ./kotti/templates/site-setup/users.pt:91 +msgid "Assign role" msgstr "" -#. Used in sentence: "Fill out your username or email and click -#. ${reset_password} below to receive an email with a link to reset your -#. password." -#. -#: ./kotti/templates/login.pt:77 ./kotti/templates/login.pt:100 -msgid "Reset password" +#: ./kotti/templates/edit/share.pt:85 ./kotti/templates/edit/change-state.pt:72 +#: ./kotti/templates/site-setup/users.pt:107 +msgid "Apply changes" msgstr "" -#. Canonical text for ${reset_password} is: "Reset password" -#: ./kotti/templates/login.pt:74 -msgid "" -"Fill out your username or email and click ${reset_password} below to receive " -"an email with a link to reset your password." +#: ./kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" msgstr "" -#: ./kotti/templates/login.pt:118 -msgid "Not registered yet?" +#: ./kotti/templates/edit/breadcrumbs.pt:8 +msgid "You are here:" msgstr "" -#: ./kotti/templates/login.pt:127 -msgid "Register for an account on this site." +#: ./kotti/templates/edit/delete.pt:9 +#: ./kotti/templates/site-setup/delete-user.pt:9 +msgid "Delete ${title}" msgstr "" -#: ./kotti/templates/workflow-dropdown.pt:19 -msgid "Make ${state}" +#: ./kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" msgstr "" -#: ./kotti/templates/edit/breadcrumbs.pt:5 -msgid "You are here:" +#: ./kotti/templates/edit/delete.pt:19 +#: ./kotti/templates/edit/change-state.pt:74 +#: ./kotti/templates/edit/delete-nodes.pt:44 +#: ./kotti/templates/edit/rename-nodes.pt:44 ./kotti/views/users.py:324 +#: ./kotti/views/users.py:359 ./kotti/views/users.py:453 +#: ./kotti/views/form.py:81 +msgid "Cancel" msgstr "" -#: ./kotti/templates/edit/change-state.pt:7 +#: ./kotti/templates/edit/change-state.pt:8 msgid "Change workflow state" msgstr "" -#: ./kotti/templates/edit/change-state.pt:14 +#: ./kotti/templates/edit/change-state.pt:12 msgid "An item's workflow state determines who can see, edit and/or manage it." msgstr "" -#: ./kotti/templates/edit/change-state.pt:26 -#: ./kotti/templates/edit/contents.pt:57 -#: ./kotti/templates/edit/delete-nodes.pt:33 -#: ./kotti/templates/view/folder.pt:28 ./kotti/views/users.py:196 -#: ./kotti/views/edit/content.py:31 +#: ./kotti/templates/edit/change-state.pt:18 +#: ./kotti/templates/edit/delete-nodes.pt:21 +#: ./kotti/templates/edit/contents.pt:34 ./kotti/templates/view/folder.pt:20 +#: ./kotti/views/users.py:204 ./kotti/views/edit/content.py:28 msgid "Title" msgstr "" -#: ./kotti/templates/edit/change-state.pt:29 -#: ./kotti/templates/edit/contents.pt:60 -#: ./kotti/templates/edit/delete-nodes.pt:36 ./kotti/templates/edit/share.pt:70 -#: ./kotti/templates/edit/upload.pt:23 ./kotti/templates/edit/upload.pt:167 -#: ./kotti/templates/site-setup/users.pt:105 -#: ./kotti/templates/view/folder.pt:31 -msgid "Type" -msgstr "" - -#: ./kotti/templates/edit/change-state.pt:32 -#: ./kotti/templates/edit/contents.pt:63 -#: ./kotti/templates/edit/delete-nodes.pt:39 +#: ./kotti/templates/edit/change-state.pt:20 +#: ./kotti/templates/edit/delete-nodes.pt:23 +#: ./kotti/templates/edit/contents.pt:36 msgid "State" msgstr "" -#: ./kotti/templates/edit/change-state.pt:35 -#: ./kotti/templates/edit/contents.pt:69 -#: ./kotti/templates/edit/delete-nodes.pt:42 -#: ./kotti/templates/view/folder.pt:34 +#: ./kotti/templates/edit/change-state.pt:21 +#: ./kotti/templates/edit/delete-nodes.pt:24 +#: ./kotti/templates/edit/contents.pt:38 ./kotti/templates/view/folder.pt:22 msgid "Creation Date" msgstr "" -#: ./kotti/templates/edit/change-state.pt:38 -#: ./kotti/templates/edit/contents.pt:72 -#: ./kotti/templates/edit/delete-nodes.pt:45 -#: ./kotti/templates/view/folder.pt:37 +#: ./kotti/templates/edit/change-state.pt:22 +#: ./kotti/templates/edit/delete-nodes.pt:25 +#: ./kotti/templates/edit/contents.pt:39 ./kotti/templates/view/folder.pt:23 msgid "Modification Date" msgstr "" -#: ./kotti/templates/edit/change-state.pt:79 -#: ./kotti/templates/edit/change-state.pt:90 +#: ./kotti/templates/edit/change-state.pt:28 +#: ./kotti/templates/edit/delete-nodes.pt:31 +#: ./kotti/templates/edit/contents.pt:45 +msgid "Select item" +msgstr "" + +#: ./kotti/templates/edit/change-state.pt:40 +#: ./kotti/templates/edit/change-state.pt:48 msgid "Include children" msgstr "" -#: ./kotti/templates/edit/change-state.pt:82 +#: ./kotti/templates/edit/change-state.pt:41 msgid "" "If checked, this will attempt to modify the status of all children in any " "selected documents and their subdocuments." msgstr "" -#: ./kotti/templates/edit/change-state.pt:100 +#: ./kotti/templates/edit/change-state.pt:54 msgid "Change state to" msgstr "" -#: ./kotti/templates/edit/change-state.pt:103 +#: ./kotti/templates/edit/change-state.pt:55 msgid "Select the new state where all chosen items should be set." msgstr "" -#: ./kotti/templates/edit/change-state.pt:111 +#: ./kotti/templates/edit/change-state.pt:60 msgid "No change" msgstr "" -#: ./kotti/templates/edit/change-state.pt:127 -#: ./kotti/templates/edit/share.pt:121 -#: ./kotti/templates/site-setup/users.pt:165 -msgid "Apply changes" -msgstr "" - -#: ./kotti/templates/edit/change-state.pt:130 -#: ./kotti/templates/edit/delete-nodes.pt:93 -#: ./kotti/templates/edit/rename-nodes.pt:58 ./kotti/views/form.py:78 -#: ./kotti/views/users.py:316 ./kotti/views/users.py:350 -#: ./kotti/views/users.py:442 -msgid "Cancel" -msgstr "" - -#: ./kotti/templates/edit/contents.pt:43 -msgid "No content items are contained here." -msgstr "" - -#: ./kotti/templates/edit/contents.pt:66 -msgid "Visibility" -msgstr "" - -#: ./kotti/templates/edit/contents.pt:125 -msgid "Visible" -msgstr "" - -#: ./kotti/templates/edit/contents.pt:134 -msgid "Hidden" -msgstr "" - -#: ./kotti/templates/edit/delete-nodes.pt:17 +#: ./kotti/templates/edit/delete-nodes.pt:13 msgid "Are you sure you want to delete the following items?" msgstr "" -#: ./kotti/templates/edit/delete.pt:8 -#: ./kotti/templates/site-setup/delete-user.pt:8 -msgid "Delete ${title}" -msgstr "" - -#: ./kotti/templates/edit/delete.pt:20 -msgid "Are you sure you want to delete ${title}?" -msgstr "" - -#: ./kotti/templates/edit/nav-tree-view.pt:7 -msgid "Navigate Site" +#: ./kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" msgstr "" -#: ./kotti/templates/edit/rename-nodes.pt:16 -#: ./kotti/templates/edit/rename.pt:14 +#: ./kotti/templates/edit/rename.pt:11 +#: ./kotti/templates/edit/rename-nodes.pt:13 msgid "" "Each content item has a name, which is used to create the url, and a title. " "Read the user manual about proper formatting of name and title if needed. You" " can change both below." msgstr "" -#: ./kotti/templates/edit/rename-nodes.pt:35 -#: ./kotti/templates/edit/rename.pt:26 +#: ./kotti/templates/edit/rename.pt:19 +#: ./kotti/templates/edit/rename-nodes.pt:26 +#: ./kotti/templates/edit/rename-nodes.pt:28 msgid "New name" msgstr "" -#: ./kotti/templates/edit/rename-nodes.pt:45 -#: ./kotti/templates/edit/rename.pt:35 +#: ./kotti/templates/edit/rename.pt:25 +#: ./kotti/templates/edit/rename-nodes.pt:34 +#: ./kotti/templates/edit/rename-nodes.pt:36 msgid "New title" msgstr "" -#: ./kotti/templates/edit/rename.pt:7 -msgid "Rename ${title}" +#: ./kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" msgstr "" -#: ./kotti/templates/edit/share.pt:7 -msgid "Share ${title}" +#: ./kotti/templates/edit/upload.pt:17 ./kotti/templates/edit/upload.pt:107 +msgid "Filename" msgstr "" -#: ./kotti/templates/edit/share.pt:19 ./kotti/templates/edit/share.pt:30 -#: ./kotti/templates/edit/share.pt:35 ./kotti/templates/site-setup/users.pt:68 -msgid "Search users and groups" +#: ./kotti/templates/edit/upload.pt:19 ./kotti/templates/edit/upload.pt:109 +msgid "Size" msgstr "" -#: ./kotti/templates/edit/share.pt:43 ./kotti/templates/site-setup/users.pt:81 -msgid "Search" +#: ./kotti/templates/edit/upload.pt:20 +msgid "Status" msgstr "" -#: ./kotti/templates/edit/share.pt:60 -msgid "Assign local roles" +#: ./kotti/templates/edit/upload.pt:21 +msgid "Progress" msgstr "" -#: ./kotti/templates/edit/share.pt:73 ./kotti/templates/site-setup/users.pt:108 -#: ./kotti/views/users.py:207 -msgid "Name" +#: ./kotti/templates/edit/upload.pt:84 +msgid "Select file(s) to upload..." msgstr "" -#: ./kotti/templates/edit/share.pt:91 ./kotti/templates/site-setup/users.pt:126 -#: ./kotti/views/users.py:313 ./kotti/views/users.py:501 -#: ./kotti/views/users.py:536 -msgid "User" +#: ./kotti/templates/edit/upload.pt:92 +msgid "Upload ${number} files." msgstr "" -#: ./kotti/templates/edit/share.pt:94 ./kotti/templates/site-setup/users.pt:129 -#: ./kotti/views/users.py:347 ./kotti/views/users.py:188 -#: ./kotti/views/users.py:501 ./kotti/views/users.py:536 -msgid "Group" +#: ./kotti/templates/edit/upload.pt:97 +msgid "Dismiss all errors" msgstr "" -#: ./kotti/templates/edit/share.pt:101 -#: ./kotti/templates/site-setup/users.pt:136 -msgid "Gravatar" +#: ./kotti/templates/edit/contents.pt:26 +msgid "No content items are contained here." msgstr "" -#: ./kotti/templates/edit/upload.pt:9 -msgid "Upload content from local file(s)" +#: ./kotti/templates/edit/contents.pt:33 +msgid "Select / deselect all" msgstr "" -#: ./kotti/templates/edit/upload.pt:20 ./kotti/templates/edit/upload.pt:164 -msgid "Filename" +#: ./kotti/templates/edit/contents.pt:37 +msgid "Visibility" msgstr "" -#: ./kotti/templates/edit/upload.pt:26 ./kotti/templates/edit/upload.pt:170 -msgid "Size" +#: ./kotti/templates/edit/contents.pt:69 +msgid "Visible" msgstr "" -#: ./kotti/templates/edit/upload.pt:29 -msgid "Status" +#: ./kotti/templates/edit/contents.pt:73 +msgid "Hidden" msgstr "" -#: ./kotti/templates/edit/upload.pt:32 -msgid "Progress" +#: ./kotti/templates/http-errors/notfound.pt:26 +msgid "Not Found" msgstr "" -#: ./kotti/templates/edit/upload.pt:124 -msgid "Select file(s) to upload..." +#: ./kotti/templates/http-errors/notfound.pt:27 +msgid "The resource could not be found" msgstr "" -#: ./kotti/templates/edit/upload.pt:134 -msgid "Upload ${number} files." +#: ./kotti/templates/view/search-results.pt:9 +msgid "Search Results" msgstr "" -#: ./kotti/templates/edit/upload.pt:144 -msgid "Dismiss all errors" +#: ./kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "" + +#: ./kotti/templates/view/tags.pt:7 +msgid "Tagged with:" msgstr "" -#: ./kotti/templates/site-setup/delete-user.pt:20 +#: ./kotti/templates/site-setup/delete-user.pt:15 msgid "Are you sure you want to delete ${type} ${title}?" msgstr "" -#: ./kotti/templates/site-setup/user.pt:13 +#: ./kotti/templates/site-setup/user.pt:12 msgid "Back to User Management" msgstr "" -#: ./kotti/templates/site-setup/user.pt:25 ./kotti/views/form.py:156 +#: ./kotti/templates/site-setup/user.pt:21 ./kotti/views/form.py:159 #, python-format msgid "Edit ${title}" msgstr "" -#: ./kotti/templates/site-setup/users.pt:15 +#: ./kotti/templates/site-setup/users.pt:12 msgid "Search user(s) / group(s)" msgstr "" -#: ./kotti/templates/site-setup/users.pt:24 +#: ./kotti/templates/site-setup/users.pt:17 msgid "Add user" msgstr "" -#: ./kotti/templates/site-setup/users.pt:33 +#: ./kotti/templates/site-setup/users.pt:22 msgid "Add group" msgstr "" -#: ./kotti/templates/site-setup/users.pt:52 +#: ./kotti/templates/site-setup/users.pt:34 msgid "Find users or groups" msgstr "" -#: ./kotti/templates/site-setup/users.pt:63 +#: ./kotti/templates/site-setup/users.pt:40 msgid "User- / groupname" msgstr "" -#: ./kotti/templates/site-setup/users.pt:70 +#: ./kotti/templates/site-setup/users.pt:50 msgid "Blank search text finds all." msgstr "" -#: ./kotti/templates/site-setup/users.pt:96 +#: ./kotti/templates/site-setup/users.pt:65 msgid "Assign global roles" msgstr "" -#: ./kotti/templates/site-setup/users.pt:180 +#: ./kotti/templates/site-setup/users.pt:117 msgid "Add new user" msgstr "" -#: ./kotti/templates/site-setup/users.pt:190 +#: ./kotti/templates/site-setup/users.pt:123 msgid "Add new group" msgstr "" -#: ./kotti/templates/view/search-results.pt:8 -msgid "Search Results" -msgstr "" - -#: ./kotti/templates/view/search.pt:5 -msgid "Search..." +#: ./kotti/views/users.py:70 ./kotti/views/form.py:82 +#: ./kotti/views/edit/actions.py:371 ./kotti/views/edit/actions.py:415 +msgid "Your changes have been saved." msgstr "" -#: ./kotti/templates/view/tags.pt:5 -msgid "Tagged with:" +#: ./kotti/views/users.py:72 ./kotti/views/users.py:477 +#: ./kotti/views/edit/actions.py:309 ./kotti/views/edit/actions.py:375 +#: ./kotti/views/edit/actions.py:417 ./kotti/views/edit/actions.py:421 +msgid "No changes were made." msgstr "" -#: ./kotti/tests/testing_view.pt:7 -msgid "Welcome, ${user}! You are logged in." +#: ./kotti/views/users.py:105 +msgid "No users or groups were found." msgstr "" -#: ./kotti/tests/testing_view.pt:14 -msgid "Welcome, you are not logged in." +#: ./kotti/views/users.py:153 +msgid "Invalid value" msgstr "" -#: ./kotti/views/form.py:79 ./kotti/views/users.py:69 -#: ./kotti/views/edit/actions.py:363 ./kotti/views/edit/actions.py:407 -msgid "Your changes have been saved." +#: ./kotti/views/users.py:159 +msgid "A user with that name already exists." msgstr "" -#: ./kotti/views/form.py:174 -msgid "Item was added." +#: ./kotti/views/users.py:166 +msgid "A user with that email already exists." msgstr "" -#: ./kotti/views/form.py:264 +#: ./kotti/views/users.py:189 #, python-format -msgid "Maximum file size: ${size}MB" +msgid "No such group: ${group}" msgstr "" -#: ./kotti/views/form.py:77 ./kotti/views/users.py:441 -msgid "Save" +#: ./kotti/views/users.py:207 ./kotti/views/login.py:71 +#: ./kotti/views/login.py:267 +msgid "Email" msgstr "" -#: ./kotti/views/form.py:198 -#, python-format -msgid "Add ${type} to ${title}." +#: ./kotti/views/users.py:227 +msgid "Active" msgstr "" -#: ./kotti/views/form.py:202 -#, python-format -msgid "Add ${type}." +#: ./kotti/views/users.py:228 +msgid "Untick this to deactivate the account." msgstr "" -#: ./kotti/views/login.py:238 -msgid "You have reset your password." +#: ./kotti/views/users.py:234 +msgid "Global roles" msgstr "" -#: ./kotti/views/login.py:200 -msgid "You have been logged out." +#: ./kotti/views/users.py:238 +msgid "Groups" msgstr "" -#: ./kotti/views/login.py:66 ./kotti/views/users.py:270 -msgid "Full name" +#: ./kotti/views/users.py:275 +msgid "" +"Leave this empty and tick the 'Send password registration' box below to have " +"the user set their own password." msgstr "" -#: ./kotti/views/login.py:69 -msgid "Username" +#: ./kotti/views/users.py:278 ./kotti/views/login.py:63 +msgid "Full name" msgstr "" -#: ./kotti/views/login.py:74 ./kotti/views/login.py:225 -#: ./kotti/views/users.py:199 -msgid "Email" +#: ./kotti/views/users.py:323 +msgid "Add User" msgstr "" -#: ./kotti/views/login.py:109 -msgid "" -"Congratulations! You are successfully registered. You should be receiving an " -"email with a link to set your password. Doing so will activate your account." +#: ./kotti/views/users.py:333 +msgid "Send password registration link." msgstr "" -#: ./kotti/views/login.py:124 +#: ./kotti/views/users.py:348 #, python-format -msgid "Register - ${title}" +msgid "${title} was added." msgstr "" -#: ./kotti/views/login.py:164 -msgid "Login failed." +#: ./kotti/views/users.py:358 +msgid "Add Group" msgstr "" -#: ./kotti/views/login.py:286 -#, python-format -msgid "Reset your password - ${title}." +#: ./kotti/views/users.py:388 ./kotti/views/site_setup.py:5 +msgid "User Management" msgstr "" -#: ./kotti/views/login.py:160 -#, python-format -msgid "Welcome, ${user}!" +#: ./kotti/views/users.py:452 ./kotti/views/form.py:80 +msgid "Save" msgstr "" -#: ./kotti/views/login.py:173 -msgid "" -"You should be receiving an email with a link to reset your password. Doing so" -" will activate your account." +#: ./kotti/views/users.py:519 +#, python-format +msgid "Edit ${principal_type} ${title}" msgstr "" -#: ./kotti/views/login.py:178 -msgid "That username or email is not known by this system." +#: ./kotti/views/users.py:547 +msgid "User was not found." msgstr "" -#: ./kotti/views/login.py:83 -msgid "Register" +#: ./kotti/views/users.py:557 +#, python-format +msgid "${principal_type} ${title} was deleted." msgstr "" -#: ./kotti/views/login.py:90 ./kotti/views/login.py:257 -msgid "There was an error." +#: ./kotti/views/users.py:565 +#, python-format +msgid "Delete ${principal_type} ${title}" msgstr "" -#: ./kotti/views/login.py:250 -msgid "Submit" +#: ./kotti/views/users.py:572 +msgid "No name was given." msgstr "" -#: ./kotti/views/login.py:279 -msgid "Your password reset token may have expired." +#: ./kotti/views/users.py:601 +#, python-format +msgid "My preferences - ${title}" msgstr "" -#: ./kotti/views/site_setup.py:6 ./kotti/views/users.py:378 -msgid "User Management" +#: ./kotti/views/form.py:177 +msgid "Item was added." msgstr "" -#: ./kotti/views/users.py:267 -msgid "" -"Leave this empty and tick the 'Send password registration' box below to have " -"the user set their own password." +#: ./kotti/views/form.py:201 +#, python-format +msgid "Add ${type} to ${title}." msgstr "" -#: ./kotti/views/users.py:587 +#: ./kotti/views/form.py:205 #, python-format -msgid "My preferences - ${title}" +msgid "Add ${type}." msgstr "" -#: ./kotti/views/users.py:145 -msgid "Invalid value" +#: ./kotti/views/form.py:295 +#, python-format +msgid "Maximum file size: ${size}MB" msgstr "" -#: ./kotti/views/users.py:151 -msgid "A user with that name already exists." +#: ./kotti/views/login.py:66 +msgid "Username" msgstr "" -#: ./kotti/views/users.py:158 -msgid "A user with that email already exists." +#: ./kotti/views/login.py:80 +msgid "Register" msgstr "" -#: ./kotti/views/users.py:181 -#, python-format -msgid "No such group: ${group}" +#: ./kotti/views/login.py:87 +msgid "There was an error." msgstr "" -#: ./kotti/views/users.py:219 -msgid "Active" +#: ./kotti/views/login.py:106 +msgid "" +"Congratulations! You are successfully registered. You should be receiving an " +"email with a link to set your password. Doing so will activate your account." msgstr "" -#: ./kotti/views/users.py:220 -msgid "Untick this to deactivate the account." +#: ./kotti/views/login.py:121 +#, python-format +msgid "Register - ${title}" msgstr "" -#: ./kotti/views/users.py:226 -msgid "Global roles" +#: ./kotti/views/login.py:152 +#, python-format +msgid "Welcome, ${user}!" msgstr "" -#: ./kotti/views/users.py:230 -msgid "Groups" +#: ./kotti/views/login.py:177 +msgid "" +"You should be receiving an email with a link to reset your password. Doing so" +" will activate your account." msgstr "" -#: ./kotti/views/users.py:315 -msgid "Add User" +#: ./kotti/views/login.py:210 +msgid "Login failed." msgstr "" -#: ./kotti/views/users.py:339 -#, python-format -msgid "${title} was added." +#: ./kotti/views/login.py:220 +msgid "That username or email is not known by this system." msgstr "" -#: ./kotti/views/users.py:349 -msgid "Add Group" +#: ./kotti/views/login.py:242 +msgid "You have been logged out." msgstr "" -#: ./kotti/views/users.py:465 ./kotti/views/users.py:71 -#: ./kotti/views/edit/actions.py:301 ./kotti/views/edit/actions.py:367 -#: ./kotti/views/edit/actions.py:413 ./kotti/views/edit/actions.py:409 -msgid "No changes were made." +#: ./kotti/views/login.py:280 +msgid "You have reset your password." msgstr "" -#: ./kotti/views/users.py:558 -msgid "No name was given." +#: ./kotti/views/login.py:299 +msgid "Set password" msgstr "" -#: ./kotti/views/users.py:98 -msgid "No users or groups were found." +#: ./kotti/views/login.py:327 +msgid "Your password reset token may have expired." msgstr "" -#: ./kotti/views/users.py:505 +#: ./kotti/views/login.py:334 #, python-format -msgid "Edit ${principal_type} ${title}" +msgid "Reset your password - ${title}." msgstr "" -#: ./kotti/views/users.py:533 -msgid "User was not found." +#: ./kotti/views/edit/default_views.py:86 +msgid "Default view" msgstr "" -#: ./kotti/views/users.py:324 -msgid "Send password registration link." +#: ./kotti/views/edit/default_views.py:107 +msgid "Default view has been reset to default." msgstr "" -#: ./kotti/views/users.py:543 -#, python-format -msgid "${principal_type} ${title} was deleted." +#: ./kotti/views/edit/default_views.py:114 +msgid "Default view has been set." msgstr "" -#: ./kotti/views/users.py:551 -#, python-format -msgid "Delete ${principal_type} ${title}" +#: ./kotti/views/edit/default_views.py:119 +msgid "Default view could not be set." msgstr "" -#: ./kotti/views/edit/actions.py:106 +#: ./kotti/views/edit/actions.py:102 #, python-format msgid "${title} was copied." msgstr "" -#: ./kotti/views/edit/actions.py:124 +#: ./kotti/views/edit/actions.py:120 #, python-format msgid "${title} was cut." msgstr "" -#: ./kotti/views/edit/actions.py:182 +#: ./kotti/views/edit/actions.py:155 #, python-format -msgid "${title} was moved." -msgstr "" - -#: ./kotti/views/edit/actions.py:270 ./kotti/views/edit/actions.py:295 -#, python-format -msgid "${title} was deleted." +msgid "${title} was pasted." msgstr "" #: ./kotti/views/edit/actions.py:158 -#, python-format -msgid "${title} was pasted." +msgid "Could not paste node. It no longer exists." msgstr "" -#: ./kotti/views/edit/actions.py:161 -msgid "Could not paste node. It no longer exists." +#: ./kotti/views/edit/actions.py:180 +#, python-format +msgid "${title} was moved." msgstr "" -#: ./kotti/views/edit/actions.py:224 +#: ./kotti/views/edit/actions.py:222 #, python-format msgid "${title} is now visible in the navigation." msgstr "" -#: ./kotti/views/edit/actions.py:227 +#: ./kotti/views/edit/actions.py:225 #, python-format msgid "${title} is no longer visible in the navigation." msgstr "" -#: ./kotti/views/edit/actions.py:292 +#: ./kotti/views/edit/actions.py:274 ./kotti/views/edit/actions.py:303 +#, python-format +msgid "${title} was deleted." +msgstr "" + +#: ./kotti/views/edit/actions.py:300 msgid "Nothing was deleted." msgstr "" -#: ./kotti/views/edit/actions.py:328 ./kotti/views/edit/actions.py:355 +#: ./kotti/views/edit/actions.py:336 ./kotti/views/edit/actions.py:363 msgid "Name and title are required." msgstr "" -#: ./kotti/views/edit/actions.py:332 +#: ./kotti/views/edit/actions.py:340 msgid "Item was renamed." msgstr "" -#: ./kotti/views/edit/actions.py:454 +#: ./kotti/views/edit/actions.py:461 +msgid "Change State" +msgstr "" + +#: ./kotti/views/edit/actions.py:462 msgid "Move up" msgstr "" -#: ./kotti/views/edit/actions.py:455 +#: ./kotti/views/edit/actions.py:463 msgid "Move down" msgstr "" -#: ./kotti/views/edit/actions.py:456 +#: ./kotti/views/edit/actions.py:464 msgid "Show" msgstr "" -#: ./kotti/views/edit/actions.py:457 +#: ./kotti/views/edit/actions.py:465 msgid "Hide" msgstr "" -#: ./kotti/views/edit/actions.py:496 +#: ./kotti/views/edit/actions.py:504 msgid "You have to select items to perform an action." msgstr "" -#: ./kotti/views/edit/actions.py:453 -msgid "Change State" -msgstr "" - -#: ./kotti/views/edit/content.py:35 +#: ./kotti/views/edit/content.py:34 msgid "Description" msgstr "" -#: ./kotti/views/edit/content.py:41 +#: ./kotti/views/edit/content.py:40 msgid "Tags" msgstr "" -#: ./kotti/views/edit/content.py:50 +#: ./kotti/views/edit/content.py:49 msgid "Body" msgstr "" - -#: ./kotti/views/edit/default_views.py:99 -msgid "Default view has been reset to default." -msgstr "" - -#: ./kotti/views/edit/default_views.py:78 -msgid "Default view" -msgstr "" - -#: ./kotti/views/edit/default_views.py:106 -msgid "Default view has been set." -msgstr "" - -#: ./kotti/views/edit/default_views.py:111 -msgid "Default view could not be set." -msgstr "" diff --git a/kotti/locale/cs/LC_MESSAGES/Kotti.mo b/kotti/locale/cs/LC_MESSAGES/Kotti.mo new file mode 100644 index 000000000..b01bdb487 Binary files /dev/null and b/kotti/locale/cs/LC_MESSAGES/Kotti.mo differ diff --git a/kotti/locale/cs/LC_MESSAGES/Kotti.po b/kotti/locale/cs/LC_MESSAGES/Kotti.po new file mode 100644 index 000000000..191c15d92 --- /dev/null +++ b/kotti/locale/cs/LC_MESSAGES/Kotti.po @@ -0,0 +1,958 @@ +msgid "" +msgstr "" +"Project-Id-Version: Kotti 1.3.1\n" +"POT-Creation-Date: 2018-01-17 13:51+0100\n" +"PO-Revision-Date: 2016-11-29 02:50+0100\n" +"Last-Translator: Lukas Zdych \n" +"Language-Team: Czech\n" +"Language: cs\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Lingua 4.9\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" +"X-Generator: Poedit 1.8.11\n" + +#: kotti/security.py:173 +msgid "Viewer" +msgstr "Může číst" + +#: kotti/security.py:174 +msgid "Editor" +msgstr "Může upravit" + +#: kotti/security.py:175 +msgid "Owner" +msgstr "Vlastník" + +#: kotti/security.py:176 +msgid "Admin" +msgstr "Správce" + +#: kotti/resources.py:541 kotti/views/edit/actions.py:453 +msgid "Copy" +msgstr "Kopírovat" + +#: kotti/resources.py:542 kotti/views/edit/actions.py:454 +msgid "Cut" +msgstr "Vyjmout" + +#: kotti/resources.py:543 kotti/views/edit/actions.py:450 +msgid "Paste" +msgstr "Vložit" + +#: kotti/resources.py:544 kotti/templates/edit/rename.pt:31 +#: kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:42 kotti/views/edit/actions.py:455 +msgid "Rename" +msgstr "Přejmenovat" + +#: kotti/resources.py:545 kotti/templates/edit/delete.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:49 +#: kotti/templates/site-setup/delete-user.pt:24 kotti/views/users.py:454 +#: kotti/views/edit/actions.py:457 +msgid "Delete" +msgstr "Odstranit" + +#: kotti/resources.py:556 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "Obsah" + +#: kotti/resources.py:557 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "Úpravy" + +#: kotti/resources.py:558 +msgid "Share" +msgstr "Sdílení" + +#: kotti/resources.py:559 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "Akce" + +#: kotti/resources.py:562 +msgid "Folder view" +msgstr "Zobrazení složky" + +#: kotti/resources.py:671 kotti/views/edit/content.py:61 +msgid "Document" +msgstr "Dokument" + +#: kotti/resources.py:808 kotti/views/edit/content.py:73 +#: kotti/views/edit/content.py:87 +msgid "File" +msgstr "Soubor" + +#: kotti/populate.py:67 +msgid "Welcome to Kotti" +msgstr "Vítejte v Kotti" + +#: kotti/populate.py:68 +msgid "Congratulations! You have successfully installed Kotti." +msgstr "Gratulujeme! Úspěšně jste nainstalovali Kotti." + +#: kotti/populate.py:69 +#, c-format +msgid "" +"\n" +"

    Log in

    \n" +"

    \n" +" You can log in to your site\n" +" and start changing its contents. If you haven't chosen a password for\n" +" your admin account yet, it'll likely be qwerty.\n" +"

    \n" +"

    \n" +" Once you're logged in, you'll see the grey editor bar below the top\n" +" navigation bar. It will allow you to switch between editing and viewing the\n" +" current page as it will appear to your visitors.\n" +"

    \n" +"
    \n" +"
    \n" +"

    Configure

    \n" +"

    \n" +" Find out how to configure your Kotti's title and many other\n" +" settings using a simple text file in your file system.\n" +"

    \n" +"

    \n" +" \n" +" Configuration manual\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    Add-ons

    \n" +"

    \n" +" A number of add-ons allow you to extend the functionality of your\n" +" Kotti site.\n" +"

    \n" +"

    \n" +" \n" +" Kotti add-ons\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    Documentation

    \n" +"

    \n" +" Wonder what more you can do with Kotti?\n" +" What license it has?\n" +" Read the manual for more information.\n" +"

    \n" +"

    \n" +" \n" +" Documentation\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +msgstr "" +"\n" +"

    Přihlášení

    \n" +"

    \n" +" Můžete se přihlásit do vašich stránek a začít upravovat jejich obsah. Pokud jste zatím nezměnili heslo pro váš účet správce, mělo by to být qwerty.\n" +"

    \n" +"

    \n" +" Jakmile se přihlásíte, uvidíte šedý editační pruh pod hlavní navigační lištou. Ten vám umožní přepínat mezi úpravami a zobrazením aktuální stránky, tak jak bude zobrazena vašim návštěvníkům.\n" +"

    \n" +"
    \n" +"
    \n" +"

    Nastavení

    \n" +"

    \n" +" Zjistěte, jak nastavit název vašich Kotti stránek a mnoho dalších nastavení pomocí jednoduchého textového souboru v souborovém systému.\n" +"

    \n" +"

    \n" +" \n" +" Návod k nastavení\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    Doplňky

    \n" +"

    \n" +" Řada doplňků vám umožní rozšířit funkčnost vašich Kotti stránek.\n" +"

    \n" +"

    \n" +" \n" +" Doplňky Kotti\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    Dokumentace

    \n" +"

    \n" +" Zajímalo by vás, čeho všeho můžete dosáhnout s Kotti? Jakou má licenci? Získejte více informací přečtením manuálu.\n" +"

    \n" +"

    \n" +" \n" +" Dokumentace\n" +" \n" +"

    \n" +"
    \n" +"
    \n" + +#: kotti/populate.py:126 +msgid "About" +msgstr "Informace" + +#: kotti/populate.py:127 +msgid "Our company is the leading manufacturer of foo widgets used in a wide variety of aviation and and industrial products." +msgstr "Naše společnost je přední výrobce komponent využívaných v letectví a průmyslové výrobě." + +#: kotti/populate.py:128 +msgid "" +"\n" +"

    \n" +" \"five\n" +"

    \n" +"\n" +"

    \n" +" Our worldwide headquarters:\n" +"

    \n" +"\n" +"
    \n" +" Foo World
    \n" +" 123 Nowhere Street, Suite 777
    \n" +" Omak, WA 98841 USA
    \n" +" +1-509-555-0100
    \n" +" widgets@foowrld.example.com\n" +"
    \n" +"\n" +"

    \n" +"Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +"\n" +"Copyright info.\n" +"Originally published in the\n" +" Extra EA-300\n" +"article.\n" +"

    \n" +msgstr "" +"\n" +"

    \n" +" \"five\n" +"

    \n" +"\n" +"

    \n" +" Naše mezinárodní centrála:\n" +"

    \n" +"\n" +"
    \n" +" Foo World
    \n" +" 123 Nowhere Street, Suite 777
    \n" +" Omak, WA 98841 USA
    \n" +" +1-509-555-0100
    \n" +" widgets@foowrld.example.com\n" +"
    \n" +"\n" +"

    \n" +"Foto credit: \"Northern Lights Formation\" by FlugKerl2.\n" +"\n" +"Copyright info.\n" +"Původně zveřejněno v článku \n" +" Extra EA-300.\n" +"

    \n" + +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "Vítejte, ${user}! Jste přihlášeni." + +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "Vítejte, nejste přihlášeni." + +#: kotti/templates/editor-bar.pt:19 +msgid "View" +msgstr "Zobrazení" + +#: kotti/templates/editor-bar.pt:42 +msgid "Navigate" +msgstr "Navigovat" + +#: kotti/templates/editor-bar.pt:53 +msgid "Preferences" +msgstr "Předvolby" + +#: kotti/templates/editor-bar.pt:58 +msgid "Site Setup" +msgstr "Nastavení Stránek" + +#: kotti/templates/editor-bar.pt:69 +msgid "Logout" +msgstr "Odhlasit se" + +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "Nastavit výchozí zobrazení" + +#: kotti/templates/login.pt:16 +msgid "Login" +msgstr "Přihlášení" + +#: kotti/templates/login.pt:21 kotti/templates/login.pt:59 +msgid "Username or email" +msgstr "Uživatelské jméno nebo e-mail" + +#: kotti/templates/login.pt:26 kotti/views/users.py:220 +#: kotti/views/login.py:255 +msgid "Password" +msgstr "Heslo" + +#: kotti/templates/login.pt:34 +msgid "Log in" +msgstr "Přihlásit se" + +#: kotti/templates/login.pt:45 +msgid "Forgot your password?" +msgstr "Zapoměli jste vaše heslo?" + +#. Used in sentence: "Fill out your username or email and click +#. ${reset_password} below to receive an email with a link to reset your +#. password." +#. +#: kotti/templates/login.pt:52 kotti/templates/login.pt:67 +msgid "Reset password" +msgstr "Obnovit heslo" + +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:50 +msgid "Fill out your username or email and click ${reset_password} below to receive an email with a link to reset your password." +msgstr "Vyplňte vaše uživatelské jméno nebo e-mail a klikněte níže na ${reset_password} pro zaslání emailu s odkazem pro nastavení hesla." + +#: kotti/templates/login.pt:78 +msgid "Not registered yet?" +msgstr "Ještě nemáte účet?" + +#: kotti/templates/login.pt:85 +msgid "Register for an account on this site." +msgstr "Zaregistrujte si uživatelský účet na těchto stránkách." + +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "Přístup zamítnut" + +#: kotti/templates/add-dropdown.pt:3 +msgid "Add" +msgstr "Přidat" + +#: kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" +msgstr "Nahrát Obsah" + +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "Změnit na ${state}" + +#: kotti/templates/email-reset-password.pt:2 +msgid "Reset your password for ${site_title}." +msgstr "Obnovení vašeho hesla pro ${site_title}." + +#: kotti/templates/email-reset-password.pt:4 +#: kotti/templates/email-set-password.pt:4 +msgid "Hello, ${user_title}!" +msgstr "Ahoj, ${user_title}!" + +#: kotti/templates/email-reset-password.pt:5 +msgid "Click this link to reset your password at ${site_title}: ${url}" +msgstr "Klikněte na tento odkaz pro obnovení vašeho hesla pro ${site_title}: ${url}" + +#: kotti/templates/email-set-password.pt:2 +msgid "Your registration for ${site_title}" +msgstr "Vaše registrace pro ${site_title}" + +#: kotti/templates/email-set-password.pt:5 +msgid "You are joining ${site_title}." +msgstr "Registrujete se k ${site_title}" + +#: kotti/templates/email-set-password.pt:6 +msgid "Click here to set your password and log in: ${url}" +msgstr "Klikněte zde pro nastavení vašeho hesla a přihlášení: ${url}" + +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" +msgstr "Sdílet ${title}" + +#: kotti/templates/edit/share.pt:14 kotti/templates/edit/share.pt:20 +#: kotti/templates/edit/share.pt:27 kotti/templates/site-setup/users.pt:48 +msgid "Search users and groups" +msgstr "Hledat uživatele a skupiny" + +#: kotti/templates/edit/share.pt:34 kotti/templates/site-setup/users.pt:56 +msgid "Search" +msgstr "Hledat" + +#: kotti/templates/edit/share.pt:44 +msgid "Assign local roles" +msgstr "Přiřadit lokální role" + +#: kotti/templates/edit/share.pt:49 kotti/templates/edit/change-state.pt:19 +#: kotti/templates/edit/delete-nodes.pt:22 kotti/templates/edit/upload.pt:18 +#: kotti/templates/edit/upload.pt:108 kotti/templates/edit/contents.pt:35 +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:69 +msgid "Type" +msgstr "Typ" + +#: kotti/templates/edit/share.pt:50 kotti/templates/site-setup/users.pt:70 +#: kotti/views/users.py:215 +msgid "Name" +msgstr "Jméno" + +#: kotti/templates/edit/share.pt:61 kotti/templates/site-setup/users.pt:81 +#: kotti/views/users.py:321 kotti/views/users.py:515 kotti/views/users.py:550 +msgid "User" +msgstr "Uživatel" + +#: kotti/templates/edit/share.pt:62 kotti/templates/site-setup/users.pt:82 +#: kotti/views/users.py:196 kotti/views/users.py:356 kotti/views/users.py:515 +#: kotti/views/users.py:550 +msgid "Group" +msgstr "Skupina" + +#: kotti/templates/edit/share.pt:65 kotti/templates/site-setup/users.pt:85 +msgid "Gravatar" +msgstr "Gravatar" + +#: kotti/templates/edit/share.pt:70 kotti/templates/site-setup/users.pt:91 +msgid "Assign role" +msgstr "Přiřadit roli" + +#: kotti/templates/edit/share.pt:85 kotti/templates/edit/change-state.pt:72 +#: kotti/templates/site-setup/users.pt:107 +msgid "Apply changes" +msgstr "Uložit změny" + +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "Procházet Stránky" + +#: kotti/templates/edit/breadcrumbs.pt:8 +msgid "You are here:" +msgstr "Nacházite se zde:" + +#: kotti/templates/edit/delete.pt:9 kotti/templates/site-setup/delete-user.pt:9 +msgid "Delete ${title}" +msgstr "Odstranit ${title}" + +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" +msgstr "Jste si jisti, že si přejete odstranit ${title}?" + +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:74 +#: kotti/templates/edit/delete-nodes.pt:44 +#: kotti/templates/edit/rename-nodes.pt:44 kotti/views/users.py:324 +#: kotti/views/users.py:359 kotti/views/users.py:453 kotti/views/form.py:81 +msgid "Cancel" +msgstr "Zrušit" + +#: kotti/templates/edit/change-state.pt:8 +msgid "Change workflow state" +msgstr "Změnit stav" + +#: kotti/templates/edit/change-state.pt:12 +msgid "An item's workflow state determines who can see, edit and/or manage it." +msgstr "Stav položky určuje kdo jí může vidět, upravit nebo spravovat." + +#: kotti/templates/edit/change-state.pt:18 +#: kotti/templates/edit/delete-nodes.pt:21 kotti/templates/edit/contents.pt:34 +#: kotti/templates/view/folder.pt:20 kotti/views/users.py:204 +#: kotti/views/edit/content.py:28 +msgid "Title" +msgstr "Název" + +#: kotti/templates/edit/change-state.pt:20 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:36 +msgid "State" +msgstr "Stav" + +#: kotti/templates/edit/change-state.pt:21 +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:38 +#: kotti/templates/view/folder.pt:22 +msgid "Creation Date" +msgstr "Datum vytvoření" + +#: kotti/templates/edit/change-state.pt:22 +#: kotti/templates/edit/delete-nodes.pt:25 kotti/templates/edit/contents.pt:39 +#: kotti/templates/view/folder.pt:23 +msgid "Modification Date" +msgstr "Poslední změna" + +#: kotti/templates/edit/change-state.pt:28 +#: kotti/templates/edit/delete-nodes.pt:31 kotti/templates/edit/contents.pt:45 +msgid "Select item" +msgstr "Vybrat položku" + +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:48 +msgid "Include children" +msgstr "Zahrnout pložky obsahu" + +#: kotti/templates/edit/change-state.pt:41 +msgid "If checked, this will attempt to modify the status of all children in any selected documents and their subdocuments." +msgstr "Je-li tato volba zaškrtnuta, systém se pokusí změnit stav všech vybraných dokumentů a obsahu v nich." + +#: kotti/templates/edit/change-state.pt:54 +msgid "Change state to" +msgstr "Změnit stav na" + +#: kotti/templates/edit/change-state.pt:55 +msgid "Select the new state where all chosen items should be set." +msgstr "Vyberte stav, který má být nastaven pro všechny vybrané položky." + +#: kotti/templates/edit/change-state.pt:60 +msgid "No change" +msgstr "Žádná změna" + +#: kotti/templates/edit/delete-nodes.pt:13 +msgid "Are you sure you want to delete the following items?" +msgstr "Jste si jisti, že si přejete odstranit následující položky?" + +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "Přejmenovat ${title}" + +#: kotti/templates/edit/rename.pt:11 kotti/templates/edit/rename-nodes.pt:13 +msgid "Each content item has a name, which is used to create the url, and a title. Read the user manual about proper formatting of name and title if needed. You can change both below." +msgstr "Každý obsah má jméno, které je použito k vytvoření URL a název. Pokud je potřebujete, přečtěte si manuál ohledně správného formátování jmen a názvů. Níže můžete obojí změnit." + +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +#: kotti/templates/edit/rename-nodes.pt:28 +msgid "New name" +msgstr "Nový název" + +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:34 +#: kotti/templates/edit/rename-nodes.pt:36 +msgid "New title" +msgstr "Nový název" + +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" +msgstr "Nahrát soubor(y) z pevného disku" + +#: kotti/templates/edit/upload.pt:17 kotti/templates/edit/upload.pt:107 +msgid "Filename" +msgstr "Název souboru" + +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:109 +msgid "Size" +msgstr "Velikost" + +#: kotti/templates/edit/upload.pt:20 +msgid "Status" +msgstr "Stav" + +#: kotti/templates/edit/upload.pt:21 +msgid "Progress" +msgstr "Průběh" + +#: kotti/templates/edit/upload.pt:84 +msgid "Select file(s) to upload..." +msgstr "Vybrat soubor(y) k nahrání…" + +#: kotti/templates/edit/upload.pt:92 +msgid "Upload ${number} files." +msgstr "Nahrát ${number} souborů." + +#: kotti/templates/edit/upload.pt:97 +msgid "Dismiss all errors" +msgstr "Skrýt všechny chyby" + +#: kotti/templates/edit/contents.pt:26 +msgid "No content items are contained here." +msgstr "Tato stránka neobsahuje žádné položky." + +#: kotti/templates/edit/contents.pt:33 +msgid "Select / deselect all" +msgstr "Vybrat vše / zrušit výběr všeho" + +#: kotti/templates/edit/contents.pt:37 +msgid "Visibility" +msgstr "Viditelnost" + +#: kotti/templates/edit/contents.pt:69 +msgid "Visible" +msgstr "Viditelné" + +#: kotti/templates/edit/contents.pt:73 +msgid "Hidden" +msgstr "Skryté" + +#: kotti/templates/http-errors/notfound.pt:26 +msgid "Not Found" +msgstr "Nenalezeno" + +#: kotti/templates/http-errors/notfound.pt:27 +msgid "The resource could not be found" +msgstr "Stránka nebyla nalezena" + +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "Výsledky Vyhledávání" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "Hledat…" + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "Oštítkováno" + +#: kotti/templates/site-setup/delete-user.pt:15 +msgid "Are you sure you want to delete ${type} ${title}?" +msgstr "Jste si jisti, že si přejete odstranit ${type} ${title}?" + +#: kotti/templates/site-setup/user.pt:12 +msgid "Back to User Management" +msgstr "Zpět na Správu Uživatelů" + +#: kotti/templates/site-setup/user.pt:21 kotti/views/form.py:159 +#, python-format +msgid "Edit ${title}" +msgstr "Upravit ${title}" + +#: kotti/templates/site-setup/users.pt:12 +msgid "Search user(s) / group(s)" +msgstr "Hledat uživatele / skupiny" + +#: kotti/templates/site-setup/users.pt:17 +msgid "Add user" +msgstr "Přidat uživatele" + +#: kotti/templates/site-setup/users.pt:22 +msgid "Add group" +msgstr "Přidat skupinu" + +#: kotti/templates/site-setup/users.pt:34 +msgid "Find users or groups" +msgstr "Najít uživatele nebo skupiny" + +#: kotti/templates/site-setup/users.pt:40 +msgid "User- / groupname" +msgstr "Uživatel- / název skupiny" + +#: kotti/templates/site-setup/users.pt:50 +msgid "Blank search text finds all." +msgstr "Při zadání prázdného vyhledávacího výrazu bude nalezeno vše." + +#: kotti/templates/site-setup/users.pt:65 +msgid "Assign global roles" +msgstr "Přiřadit globální role" + +#: kotti/templates/site-setup/users.pt:117 +msgid "Add new user" +msgstr "Přidat nového uživatele" + +#: kotti/templates/site-setup/users.pt:123 +msgid "Add new group" +msgstr "Přidat novou skupinu" + +#: kotti/views/users.py:70 kotti/views/form.py:82 +#: kotti/views/edit/actions.py:371 kotti/views/edit/actions.py:415 +msgid "Your changes have been saved." +msgstr "Vaše změny byly uloženy." + +#: kotti/views/users.py:72 kotti/views/users.py:477 +#: kotti/views/edit/actions.py:309 kotti/views/edit/actions.py:375 +#: kotti/views/edit/actions.py:417 kotti/views/edit/actions.py:421 +msgid "No changes were made." +msgstr "Nebyly provedeny žádné změny." + +#: kotti/views/users.py:105 +msgid "No users or groups were found." +msgstr "Nebyli nalezeni žádní uživatelé ani skupiny." + +#: kotti/views/users.py:153 +msgid "Invalid value" +msgstr "Neplatná hodnota" + +#: kotti/views/users.py:159 +msgid "A user with that name already exists." +msgstr "Uživatel s tímto jménem již existuje." + +#: kotti/views/users.py:166 +msgid "A user with that email already exists." +msgstr "Uživatel s touto e-mailovou adresou již existuje." + +#: kotti/views/users.py:189 +#, python-format +msgid "No such group: ${group}" +msgstr "Skupina ${group} neexistuje" + +#: kotti/views/users.py:207 kotti/views/login.py:71 kotti/views/login.py:267 +msgid "Email" +msgstr "E-mailová adresa" + +#: kotti/views/users.py:227 +msgid "Active" +msgstr "Aktivní" + +#: kotti/views/users.py:228 +msgid "Untick this to deactivate the account." +msgstr "Zde odškrtněte pro deaktivaci účtu." + +#: kotti/views/users.py:234 +msgid "Global roles" +msgstr "Globální role" + +#: kotti/views/users.py:238 +msgid "Groups" +msgstr "Skupiny" + +#: kotti/views/users.py:275 +msgid "Leave this empty and tick the 'Send password registration' box below to have the user set their own password." +msgstr "Ponechte toto prázdné a níže zaškrtněte pole ‘Odeslat registrační heslo’, aby si uživatel nastavil vlastní heslo." + +#: kotti/views/users.py:278 kotti/views/login.py:63 +msgid "Full name" +msgstr "Celé jméno" + +#: kotti/views/users.py:323 +msgid "Add User" +msgstr "Přidat Uživatele" + +#: kotti/views/users.py:333 +msgid "Send password registration link." +msgstr "Odeslat odkaz pro obnovení hesla." + +#: kotti/views/users.py:348 +#, python-format +msgid "${title} was added." +msgstr "${title} bylo přidáno." + +#: kotti/views/users.py:358 +msgid "Add Group" +msgstr "Přidat Skupinu" + +#: kotti/views/users.py:388 kotti/views/site_setup.py:5 +msgid "User Management" +msgstr "Správa Uživatelů" + +#: kotti/views/users.py:452 kotti/views/form.py:80 +msgid "Save" +msgstr "Uložit" + +#: kotti/views/users.py:519 +#, python-format +msgid "Edit ${principal_type} ${title}" +msgstr "Upravit ${principal_type} ${title}" + +#: kotti/views/users.py:547 +msgid "User was not found." +msgstr "Uživatel nebyl nalezen." + +#: kotti/views/users.py:557 +#, python-format +msgid "${principal_type} ${title} was deleted." +msgstr "${principal_type} ${title} byl odstraněn." + +#: kotti/views/users.py:565 +#, python-format +msgid "Delete ${principal_type} ${title}" +msgstr "Odstranit ${principal_type} ${title}" + +#: kotti/views/users.py:572 +msgid "No name was given." +msgstr "Nebylo zadáno jméno." + +#: kotti/views/users.py:601 +#, python-format +msgid "My preferences - ${title}" +msgstr "Mé předvolby - ${title}" + +#: kotti/views/form.py:177 +msgid "Item was added." +msgstr "Položka byla přidána." + +#: kotti/views/form.py:201 +#, python-format +msgid "Add ${type} to ${title}." +msgstr "Přidat ${type} do ${title}" + +#: kotti/views/form.py:205 +#, python-format +msgid "Add ${type}." +msgstr "Přidat ${type}." + +#: kotti/views/form.py:295 +#, python-format +msgid "Maximum file size: ${size}MB" +msgstr "Maximální velikost souboru: ${size}MB" + +#: kotti/views/login.py:66 +msgid "Username" +msgstr "Uživatelské jméno" + +#: kotti/views/login.py:80 +msgid "Register" +msgstr "Registrace" + +#: kotti/views/login.py:87 +msgid "There was an error." +msgstr "Vyskytly se chyby." + +#: kotti/views/login.py:106 +msgid "Congratulations! You are successfully registered. You should be receiving an email with a link to set your password. Doing so will activate your account." +msgstr "Gratulujeme! Byli jste úspěšně zaregistrováni. Měli byste obdržet emailovou zprávu s odkazem pro nastavení vašeho hesla. Tímto vám bude aktivován váš účet." + +#: kotti/views/login.py:121 +#, python-format +msgid "Register - ${title}" +msgstr "Registrace - ${title}" + +#: kotti/views/login.py:152 +#, python-format +msgid "Welcome, ${user}!" +msgstr "Vítejte, ${user}!" + +#: kotti/views/login.py:177 +msgid "You should be receiving an email with a link to reset your password. Doing so will activate your account." +msgstr "Právě byste měli obdržet emailovou zprávu s odkazem pro obnovení vašeho hesla. Tímto vám bude váš účet aktivován." + +#: kotti/views/login.py:210 +msgid "Login failed." +msgstr "Přihlášení se nezdařilo" + +#: kotti/views/login.py:220 +msgid "That username or email is not known by this system." +msgstr "Uživatelské jéno nebo e-mailová adresa v systému neexistuje." + +#: kotti/views/login.py:242 +msgid "You have been logged out." +msgstr "Byli jste odhlášeni." + +#: kotti/views/login.py:280 +msgid "You have reset your password." +msgstr "Vaše heslo bylo obnoveno." + +#: kotti/views/login.py:299 +msgid "Set password" +msgstr "Nastavit heslo" + +#: kotti/views/login.py:327 +msgid "Your password reset token may have expired." +msgstr "Vypadá to, že váš token pro obnovu hesla již vypršel." + +#: kotti/views/login.py:334 +#, python-format +msgid "Reset your password - ${title}." +msgstr "Obnova vašeho hesla - ${title}." + +#: kotti/views/edit/default_views.py:86 +msgid "Default view" +msgstr "Výchozí zobrazení" + +#: kotti/views/edit/default_views.py:107 +msgid "Default view has been reset to default." +msgstr "Výchozí zobrazení bylo změněno na výchozí hodnotu." + +#: kotti/views/edit/default_views.py:114 +msgid "Default view has been set." +msgstr "Výchozí zobrazení bylo nastaveno." + +#: kotti/views/edit/default_views.py:119 +msgid "Default view could not be set." +msgstr "Nebylo možné nastavit výchozí zobrazení." + +#: kotti/views/edit/actions.py:102 +#, python-format +msgid "${title} was copied." +msgstr "${title} bylo zkopírováno." + +#: kotti/views/edit/actions.py:120 +#, python-format +msgid "${title} was cut." +msgstr "${title} bylo vyjmuto." + +#: kotti/views/edit/actions.py:155 +#, python-format +msgid "${title} was pasted." +msgstr "${title} bylo vloženo." + +#: kotti/views/edit/actions.py:158 +msgid "Could not paste node. It no longer exists." +msgstr "Položku nebylo možné vložit. Položka již neexistuje." + +#: kotti/views/edit/actions.py:180 +#, python-format +msgid "${title} was moved." +msgstr "${title} bylo přesunuto." + +#: kotti/views/edit/actions.py:222 +#, python-format +msgid "${title} is now visible in the navigation." +msgstr "${title} je nyní zobrazeno v navigaci." + +#: kotti/views/edit/actions.py:225 +#, python-format +msgid "${title} is no longer visible in the navigation." +msgstr "${title} již není zobrazeno v navigaci." + +#: kotti/views/edit/actions.py:274 kotti/views/edit/actions.py:303 +#, python-format +msgid "${title} was deleted." +msgstr "${title} bylo odstraněno." + +#: kotti/views/edit/actions.py:300 +msgid "Nothing was deleted." +msgstr "Žádné položky nebyly odstraněny." + +#: kotti/views/edit/actions.py:336 kotti/views/edit/actions.py:363 +msgid "Name and title are required." +msgstr "Jměno a název jsou povinné." + +#: kotti/views/edit/actions.py:340 +msgid "Item was renamed." +msgstr "Položka byla přejmenována." + +#: kotti/views/edit/actions.py:461 +msgid "Change State" +msgstr "Změnit Stav" + +#: kotti/views/edit/actions.py:462 +msgid "Move up" +msgstr "Posonout nahoru" + +#: kotti/views/edit/actions.py:463 +msgid "Move down" +msgstr "Posunout dolů" + +#: kotti/views/edit/actions.py:464 +msgid "Show" +msgstr "Zobrazit" + +#: kotti/views/edit/actions.py:465 +msgid "Hide" +msgstr "Skrýt" + +#: kotti/views/edit/actions.py:504 +msgid "You have to select items to perform an action." +msgstr "Pro provedení akce je nutné vybrat položky." + +#: kotti/views/edit/content.py:34 +msgid "Description" +msgstr "Popis" + +#: kotti/views/edit/content.py:40 +msgid "Tags" +msgstr "Značky" + +#: kotti/views/edit/content.py:49 +msgid "Body" +msgstr "Hlavní text" + +#~ msgid "Submit" +#~ msgstr "Předložit" + +#~ msgid "Private" +#~ msgstr "Soukromé" + +#~ msgid "Public" +#~ msgstr "Zveřejněno" diff --git a/kotti/locale/de/LC_MESSAGES/Kotti.mo b/kotti/locale/de/LC_MESSAGES/Kotti.mo index f0e4abdc0..00ae9b3f8 100644 Binary files a/kotti/locale/de/LC_MESSAGES/Kotti.mo and b/kotti/locale/de/LC_MESSAGES/Kotti.mo differ diff --git a/kotti/locale/de/LC_MESSAGES/Kotti.po b/kotti/locale/de/LC_MESSAGES/Kotti.po index cb99a3992..97233484a 100644 --- a/kotti/locale/de/LC_MESSAGES/Kotti.po +++ b/kotti/locale/de/LC_MESSAGES/Kotti.po @@ -1,61 +1,120 @@ -# German translations for Kotti. -# Copyright (C) 2012 ORGANIZATION -# This file is distributed under the same license as the Kotti project. -# Christian Neumann , 2012. -# msgid "" msgstr "" -"Project-Id-Version: Kotti 0.7dev4\n" -"Report-Msgid-Bugs-To: kotti@googlegroups.com\n" -"POT-Creation-Date: 2014-11-21 21:56+0100\n" -"PO-Revision-Date: 2014-11-21 22:44+0100\n" +"Project-Id-Version: \n" +"POT-Creation-Date: 2018-01-17 13:51+0100\n" +"PO-Revision-Date: \n" "Last-Translator: Andreas Kaiser \n" -"Language-Team: de \n" +"Language-Team: \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"Generated-By: Babel 0.9.6\n" -"X-Generator: Poedit 1.6.9\n" +"X-Generator: Poedit 1.8.6\n" -#: kotti/populate.py:64 +#: kotti/security.py:173 +msgid "Viewer" +msgstr "Betrachter" + +#: kotti/security.py:174 +msgid "Editor" +msgstr "Bearbeiter" + +#: kotti/security.py:175 +msgid "Owner" +msgstr "Besitzer" + +#: kotti/security.py:176 +msgid "Admin" +msgstr "Administrator" + +#: kotti/resources.py:541 kotti/views/edit/actions.py:453 +msgid "Copy" +msgstr "Kopieren" + +#: kotti/resources.py:542 kotti/views/edit/actions.py:454 +msgid "Cut" +msgstr "Ausschneiden" + +#: kotti/resources.py:543 kotti/views/edit/actions.py:450 +msgid "Paste" +msgstr "Einfügen" + +#: kotti/resources.py:544 kotti/templates/edit/rename.pt:31 +#: kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:42 kotti/views/edit/actions.py:455 +msgid "Rename" +msgstr "Umbenennen" + +#: kotti/resources.py:545 kotti/templates/edit/delete.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:49 +#: kotti/templates/site-setup/delete-user.pt:24 kotti/views/users.py:454 +#: kotti/views/edit/actions.py:457 +msgid "Delete" +msgstr "Löschen" + +#: kotti/resources.py:556 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "Inhalt" + +#: kotti/resources.py:557 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "Bearbeiten" + +#: kotti/resources.py:558 +msgid "Share" +msgstr "Freigeben" + +#: kotti/resources.py:559 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "Aktionen" + +#: kotti/resources.py:562 +msgid "Folder view" +msgstr "Ordneransicht" + +#: kotti/resources.py:671 kotti/views/edit/content.py:61 +msgid "Document" +msgstr "Dokument" + +#: kotti/resources.py:808 kotti/views/edit/content.py:73 +#: kotti/views/edit/content.py:87 +msgid "File" +msgstr "Datei" + +#: kotti/populate.py:67 msgid "Welcome to Kotti" msgstr "Willkommen bei Kotti" -#: kotti/populate.py:65 +#: kotti/populate.py:68 msgid "Congratulations! You have successfully installed Kotti." msgstr "Herzlichen Glückwunsch! Sie haben Kotti erfolgreich installiert." -#: kotti/populate.py:66 +#: kotti/populate.py:69 #, c-format msgid "" "\n" "

    Log in

    \n" "

    \n" -" You can log in to your " -"site\n" +" You can log in to your site\n" " and start changing its contents. If you haven't chosen a password for\n" " your admin account yet, it'll likely be qwerty.\n" "

    \n" "

    \n" " Once you're logged in, you'll see the grey editor bar below the top\n" -" navigation bar. It will allow you to switch between editing and viewing " -"the\n" +" navigation bar. It will allow you to switch between editing and viewing the\n" " current page as it will appear to your visitors.\n" "

    \n" "
    \n" "
    \n" "

    Configure

    \n" "

    \n" -" Find out how to configure your Kotti's title and many other " -"settings using a\n" -" simple text file in your file system.\n" +" Find out how to configure your Kotti's title and many other\n" +" settings using a simple text file in your file system.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -63,13 +122,12 @@ msgid "" "
    \n" "

    Add-ons

    \n" "

    \n" -" A number of add-ons allow you to extend the functionality of " -"your Kotti site.\n" +" A number of add-ons allow you to extend the functionality of your\n" +" Kotti site.\n" "

    \n" "

    \n" " \n" +" href=\"http://pypi.python.org/pypi?%3Aaction=search&term=kotti\">\n" " Kotti add-ons\n" " \n" "

    \n" @@ -77,13 +135,13 @@ msgid "" "
    \n" "

    Documentation

    \n" "

    \n" -" Wonder what more you can do with Kotti? What license it has? " -"Read the\n" -" manual for more information.\n" +" Wonder what more you can do with Kotti?\n" +" What license it has?\n" +" Read the manual for more information.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/\">\n" " Documentation\n" " \n" "

    \n" @@ -93,28 +151,20 @@ msgstr "" "\n" "

    Anmelden

    \n" "

    \n" -" Sie können sich nun anmelden und mit der Bearbeitung der Inhalte Ihrer Seite beginnen. " -"Falls Sie bisher kein Passwort für Ihren \"admin\" Zugang festgelegt haben, " -"lautet dieses höchstwahrscheinlich qwerty.\n" +" Sie können sich nun anmelden und mit der Bearbeitung der Inhalte Ihrer Seite beginnen. Falls Sie bisher kein Passwort für Ihren \"admin\" Zugang festgelegt haben, lautet dieses höchstwahrscheinlich qwerty.\n" "

    \n" "

    \n" -" Nach der Anmeldung werden Sie eine graue Bearbeitungsleiste unter der " -"Hauptnavigation sehen. Dort können Sie zwischen Ansicht, Bearbeitung " -"wechseln, sowie weitere Funktionen ausführen.\n" +" Nach der Anmeldung werden Sie eine Bearbeitungsleiste unter der Hauptnavigation sehen. Dort können Sie zwischen Ansicht, Bearbeitung wechseln, sowie weitere Funktionen ausführen.\n" "

    \n" "
    \n" "
    \n" "

    Konfiguration

    \n" "

    \n" -" Finden Sie heraus, wie Sie den Titel Ihrer Installation und " -"vieles andere mehr durch die Bearbeitung einer einfachen Textdatei anpassen " -"können.\n" +" Finden Sie heraus, wie Sie den Titel Ihrer Installation und vieles andere mehr durch die Bearbeitung einer einfachen Textdatei anpassen können.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -122,13 +172,11 @@ msgstr "" "
    \n" "

    Erweiterungen

    \n" "

    \n" -" Mit Hilfe von Erweiterungen können Sie Ihrer Installation " -"zusätzliche Funktionen hinzufügen.\n" +" Mit Hilfe von Erweiterungen können Sie Ihrer Installation zusätzliche Funktionen hinzufügen.\n" "

    \n" "

    \n" " \n" +" href=\"http://pypi.python.org/pypi?%3Aaction=search&term=kotti\">\n" " Kotti Erweiterungen\n" " \n" "

    \n" @@ -136,39 +184,31 @@ msgstr "" "
    \n" "

    DoKumentation

    \n" "

    \n" -" Fragen Sie sich, was Sie noch alles mit Kotti machen können? " -"Unter welcher Lizenz es veröffentlich ist? Lesen Sie die Dokumentation für " -"diese und weitere Informationen.\n" +" Fragen Sie sich, was Sie noch alles mit Kotti machen können? Unter welcher Lizenz es veröffentlich ist? Lesen Sie die Dokumentation für diese und weitere Informationen.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/\">\n" " Dokumentation\n" " \n" "

    \n" "
    \n" "
    \n" -#: kotti/populate.py:121 +#: kotti/populate.py:126 msgid "About" msgstr "Über" -#: kotti/populate.py:122 -msgid "" -"Our company is the leading manufacturer of foo widgets used in a wide " -"variety of aviation and and industrial products." -msgstr "" -"Unsere Firma is der führende Hersteller von Foo Produkten, welche " -"gleichermaßen in in der Luft- und Raumfahrt, als auch in anderen " -"Industriesektoren eingesetzt werden." +#: kotti/populate.py:127 +msgid "Our company is the leading manufacturer of foo widgets used in a wide variety of aviation and and industrial products." +msgstr "Unsere Firma is der führende Hersteller von Foo Produkten, welche gleichermaßen in in der Luft- und Raumfahrt, als auch in anderen Industriesektoren eingesetzt werden." -#: kotti/populate.py:123 +#: kotti/populate.py:128 msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -185,20 +225,18 @@ msgid "" "\n" "\n" "

    \n" -" Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" -" \n" -" Copyright info.\n" -" Originally published in the\n" -" Extra EA-300\n" -" article.\n" +"Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +"\n" +"Copyright info.\n" +"Originally published in the\n" +" Extra EA-300\n" +"article.\n" "

    \n" msgstr "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -216,171 +254,63 @@ msgstr "" "\n" "

    \n" " Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" -" \n" +" \n" " Copyright info.\n" " Originally published in the\n" " Extra EA-300\n" " article.\n" "

    \n" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 -msgid "Document" -msgstr "Dokument" - -#: kotti/resources.py:649 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 -msgid "File" -msgstr "Datei" - -#: kotti/resources.py:726 kotti/views/edit/content.py:144 -msgid "Image" -msgstr "Bild" - -#: kotti/resources.py:500 -msgid "Folder view" -msgstr "Ordneransicht" - -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 -msgid "Contents" -msgstr "Inhalt" - -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" -msgstr "Bearbeiten" - -#: kotti/resources.py:489 -msgid "Share" -msgstr "Teilen" - -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" -msgstr "Aktionen" - -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 -msgid "Copy" -msgstr "Kopieren" - -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 -msgid "Cut" -msgstr "Ausschneiden" - -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 -msgid "Paste" -msgstr "Einfügen" - -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 -#: kotti/templates/edit/rename-nodes.pt:55 kotti/templates/edit/rename.pt:42 -#: kotti/views/edit/actions.py:447 -msgid "Rename" -msgstr "Umbenennen" - -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 -#: kotti/templates/edit/delete-nodes.pt:90 kotti/templates/edit/delete.pt:28 -#: kotti/templates/site-setup/delete-user.pt:36 kotti/views/users.py:443 -#: kotti/views/edit/actions.py:449 -msgid "Delete" -msgstr "Löschen" - -#: kotti/security.py:165 -msgid "Viewer" -msgstr "Betrachter" - -#: kotti/security.py:166 -msgid "Editor" -msgstr "Bearbeiter" - -#: kotti/security.py:167 -msgid "Owner" -msgstr "Besitzer" - -#: kotti/security.py:168 -msgid "Admin" -msgstr "Administrator" - -#: kotti/templates/add-dropdown.pt:5 -msgid "Add" -msgstr "Hinzufügen" - -#: kotti/templates/add-dropdown.pt:27 -msgid "Upload Content" -msgstr "Dateien hochladen" +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "Willkommen, ${user}! Sie sind angemeldet." -#: kotti/templates/default-view-selector.pt:5 -msgid "Set default view" -msgstr "Standardansicht setzen" +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "Willkommen! Sie sind nicht angemeldet." -#: kotti/templates/editor-bar.pt:35 +#: kotti/templates/editor-bar.pt:19 msgid "View" msgstr "Anzeigen" -#: kotti/templates/editor-bar.pt:76 +#: kotti/templates/editor-bar.pt:42 msgid "Navigate" msgstr "Navigieren" -#: kotti/templates/editor-bar.pt:98 +#: kotti/templates/editor-bar.pt:53 msgid "Preferences" msgstr "Einstellungen" -#: kotti/templates/editor-bar.pt:109 +#: kotti/templates/editor-bar.pt:58 msgid "Site Setup" msgstr "Seiten-Einstellungen" -#: kotti/templates/editor-bar.pt:131 +#: kotti/templates/editor-bar.pt:69 msgid "Logout" msgstr "Abmelden" -#: kotti/templates/email-reset-password.pt:3 -msgid "Reset your password for ${site_title}." -msgstr "Passwort zurücksetzen bei ${site_title}" - -#: kotti/templates/email-reset-password.pt:7 -#: kotti/templates/email-set-password.pt:6 -msgid "Hello, ${user_title}!" -msgstr "Hallo ${user_title}!" - -#: kotti/templates/email-reset-password.pt:10 -msgid "Click this link to reset your password at ${site_title}: ${url}" -msgstr "" -"Besuchen Sie bitte folgende Seite, um Ihr Passwort bei ${site_title} " -"zurückzusetzen: ${url}" - -#: kotti/templates/email-set-password.pt:3 -msgid "Your registration for ${site_title}" -msgstr "Ihre Anmeldung bei ${site_title}" - -#: kotti/templates/email-set-password.pt:9 -msgid "You are joining ${site_title}." -msgstr "Ihre Anmeldung bei ${site_title}" - -#: kotti/templates/email-set-password.pt:12 -msgid "Click here to set your password and log in: ${url}" -msgstr "" -"Besuchen Sie bitte folgende Seite, um Ihr Passwort zu setzen und sich " -"anzumelden: ${url}" - -#: kotti/templates/forbidden.pt:7 -msgid "Forbidden" -msgstr "Zugriff verweigert" +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "Standardansicht setzen" #: kotti/templates/login.pt:16 msgid "Login" msgstr "Anmelden" -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 +#: kotti/templates/login.pt:21 kotti/templates/login.pt:59 msgid "Username or email" msgstr "Benutzername oder E-Mail" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 -#: kotti/views/users.py:212 +#: kotti/templates/login.pt:26 kotti/views/users.py:220 +#: kotti/views/login.py:255 msgid "Password" msgstr "Passwort" -#: kotti/templates/login.pt:47 +#: kotti/templates/login.pt:34 msgid "Log in" msgstr "Anmelden" -#: kotti/templates/login.pt:65 +#: kotti/templates/login.pt:45 msgid "Forgot your password?" msgstr "Passwort vergessen?" @@ -388,612 +318,799 @@ msgstr "Passwort vergessen?" #. ${reset_password} below to receive an email with a link to reset your #. password." #. -#: kotti/templates/login.pt:77 kotti/templates/login.pt:100 +#: kotti/templates/login.pt:52 kotti/templates/login.pt:67 msgid "Reset password" msgstr "Passwort zurücksetzen" #. Canonical text for ${reset_password} is: "Reset password" -#: kotti/templates/login.pt:74 -msgid "" -"Fill out your username or email and click ${reset_password} below to receive " -"an email with a link to reset your password." -msgstr "" -"Geben Sie oben Ihren Benutzernamen oder Ihre E-Mail Adresse an und klicken " -"Sie auf ${reset_password}. Sie werden daraufhin eine E-Mail erhalten, die " -"einen Link zum zurücksetzen Ihres Passworts enthält." +#: kotti/templates/login.pt:50 +msgid "Fill out your username or email and click ${reset_password} below to receive an email with a link to reset your password." +msgstr "Geben Sie oben Ihren Benutzernamen oder Ihre E-Mail Adresse an und klicken Sie auf ${reset_password}. Sie werden daraufhin eine E-Mail erhalten, die einen Link zum zurücksetzen Ihres Passworts enthält." -#: kotti/templates/login.pt:118 +#: kotti/templates/login.pt:78 msgid "Not registered yet?" msgstr "Noch nicht registriert?" -#: kotti/templates/login.pt:127 +#: kotti/templates/login.pt:85 msgid "Register for an account on this site." msgstr "Einen Benutzer für den Zugang zu dieser Seite erstellen." -#: kotti/templates/workflow-dropdown.pt:19 +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "Zugriff verweigert" + +#: kotti/templates/add-dropdown.pt:3 +msgid "Add" +msgstr "Hinzufügen" + +#: kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" +msgstr "Dateien hochladen" + +#: kotti/templates/workflow-dropdown.pt:14 msgid "Make ${state}" msgstr "Setze ${state}" -#: kotti/templates/edit/breadcrumbs.pt:5 +#: kotti/templates/email-reset-password.pt:2 +msgid "Reset your password for ${site_title}." +msgstr "Passwort zurücksetzen bei ${site_title}" + +#: kotti/templates/email-reset-password.pt:4 +#: kotti/templates/email-set-password.pt:4 +msgid "Hello, ${user_title}!" +msgstr "Hallo ${user_title}!" + +#: kotti/templates/email-reset-password.pt:5 +msgid "Click this link to reset your password at ${site_title}: ${url}" +msgstr "Besuchen Sie bitte folgende Seite, um Ihr Passwort bei ${site_title} zurückzusetzen: ${url}" + +#: kotti/templates/email-set-password.pt:2 +msgid "Your registration for ${site_title}" +msgstr "Ihre Anmeldung bei ${site_title}" + +#: kotti/templates/email-set-password.pt:5 +msgid "You are joining ${site_title}." +msgstr "Ihre Anmeldung bei ${site_title}" + +#: kotti/templates/email-set-password.pt:6 +msgid "Click here to set your password and log in: ${url}" +msgstr "Besuchen Sie bitte folgende Seite, um Ihr Passwort zu setzen und sich anzumelden: ${url}" + +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" +msgstr "${title} freigeben" + +#: kotti/templates/edit/share.pt:14 kotti/templates/edit/share.pt:20 +#: kotti/templates/edit/share.pt:27 kotti/templates/site-setup/users.pt:48 +msgid "Search users and groups" +msgstr "Suche Benutzer und Gruppen" + +#: kotti/templates/edit/share.pt:34 kotti/templates/site-setup/users.pt:56 +msgid "Search" +msgstr "Suche" + +#: kotti/templates/edit/share.pt:44 +msgid "Assign local roles" +msgstr "Lokale Rollen zuweisen" + +#: kotti/templates/edit/share.pt:49 kotti/templates/edit/change-state.pt:19 +#: kotti/templates/edit/delete-nodes.pt:22 kotti/templates/edit/upload.pt:18 +#: kotti/templates/edit/upload.pt:108 kotti/templates/edit/contents.pt:35 +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:69 +msgid "Type" +msgstr "Typ" + +#: kotti/templates/edit/share.pt:50 kotti/templates/site-setup/users.pt:70 +#: kotti/views/users.py:215 +msgid "Name" +msgstr "Name" + +#: kotti/templates/edit/share.pt:61 kotti/templates/site-setup/users.pt:81 +#: kotti/views/users.py:321 kotti/views/users.py:515 kotti/views/users.py:550 +msgid "User" +msgstr "Benutzer" + +#: kotti/templates/edit/share.pt:62 kotti/templates/site-setup/users.pt:82 +#: kotti/views/users.py:196 kotti/views/users.py:356 kotti/views/users.py:515 +#: kotti/views/users.py:550 +msgid "Group" +msgstr "Gruppe" + +#: kotti/templates/edit/share.pt:65 kotti/templates/site-setup/users.pt:85 +msgid "Gravatar" +msgstr "Gravatar" + +#: kotti/templates/edit/share.pt:70 kotti/templates/site-setup/users.pt:91 +msgid "Assign role" +msgstr "Rolle zuweisen" + +#: kotti/templates/edit/share.pt:85 kotti/templates/edit/change-state.pt:72 +#: kotti/templates/site-setup/users.pt:107 +msgid "Apply changes" +msgstr "Änderungen übernehmen" + +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "Navigieren" + +#: kotti/templates/edit/breadcrumbs.pt:8 msgid "You are here:" msgstr "Sie sind hier:" -#: kotti/templates/edit/change-state.pt:7 +#: kotti/templates/edit/delete.pt:9 kotti/templates/site-setup/delete-user.pt:9 +msgid "Delete ${title}" +msgstr "Lösche ${title}" + +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" +msgstr "Sind Sie sicher, dass Sie ${title} löschen möchten?" + +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:74 +#: kotti/templates/edit/delete-nodes.pt:44 +#: kotti/templates/edit/rename-nodes.pt:44 kotti/views/users.py:324 +#: kotti/views/users.py:359 kotti/views/users.py:453 kotti/views/form.py:81 +msgid "Cancel" +msgstr "Abbrechen" + +#: kotti/templates/edit/change-state.pt:8 msgid "Change workflow state" msgstr "Workflowstatus ändern" -#: kotti/templates/edit/change-state.pt:14 +#: kotti/templates/edit/change-state.pt:12 msgid "An item's workflow state determines who can see, edit and/or manage it." -msgstr "" -"Der Workflowstatus eines Dokumentes bestimmt, wer es sehen, bearbeiten und/" -"oder verwalten kann." +msgstr "Der Workflowstatus eines Dokumentes bestimmt, wer es sehen, bearbeiten und/oder verwalten kann." -#: kotti/templates/edit/change-state.pt:26 kotti/templates/edit/contents.pt:57 -#: kotti/templates/edit/delete-nodes.pt:33 kotti/templates/view/folder.pt:28 -#: kotti/views/users.py:196 kotti/views/edit/content.py:31 +#: kotti/templates/edit/change-state.pt:18 +#: kotti/templates/edit/delete-nodes.pt:21 kotti/templates/edit/contents.pt:34 +#: kotti/templates/view/folder.pt:20 kotti/views/users.py:204 +#: kotti/views/edit/content.py:28 msgid "Title" msgstr "Titel" -#: kotti/templates/edit/change-state.pt:29 kotti/templates/edit/contents.pt:60 -#: kotti/templates/edit/delete-nodes.pt:36 kotti/templates/edit/share.pt:70 -#: kotti/templates/edit/upload.pt:23 kotti/templates/edit/upload.pt:167 -#: kotti/templates/site-setup/users.pt:105 kotti/templates/view/folder.pt:31 -msgid "Type" -msgstr "Typ" - -#: kotti/templates/edit/change-state.pt:32 kotti/templates/edit/contents.pt:63 -#: kotti/templates/edit/delete-nodes.pt:39 +#: kotti/templates/edit/change-state.pt:20 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:36 msgid "State" msgstr "Status" -#: kotti/templates/edit/change-state.pt:35 kotti/templates/edit/contents.pt:69 -#: kotti/templates/edit/delete-nodes.pt:42 kotti/templates/view/folder.pt:34 +#: kotti/templates/edit/change-state.pt:21 +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:38 +#: kotti/templates/view/folder.pt:22 msgid "Creation Date" msgstr "Erstellungsdatum" -#: kotti/templates/edit/change-state.pt:38 kotti/templates/edit/contents.pt:72 -#: kotti/templates/edit/delete-nodes.pt:45 kotti/templates/view/folder.pt:37 +#: kotti/templates/edit/change-state.pt:22 +#: kotti/templates/edit/delete-nodes.pt:25 kotti/templates/edit/contents.pt:39 +#: kotti/templates/view/folder.pt:23 msgid "Modification Date" msgstr "Bearbeitungsdatum" -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 +#: kotti/templates/edit/change-state.pt:28 +#: kotti/templates/edit/delete-nodes.pt:31 kotti/templates/edit/contents.pt:45 +msgid "Select item" +msgstr "Element wählen" + +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:48 msgid "Include children" msgstr "Unterordner einbeziehen" -#: kotti/templates/edit/change-state.pt:82 -msgid "" -"If checked, this will attempt to modify the status of all children in any " -"selected documents and their subdocuments." -msgstr "" -"Wenn Sie diese Option anwählen, wirkt sich die Statusänderung sowohl auf " -"alle ausgewählte als auch auf die darin enthaltenen Dokumente aus." +#: kotti/templates/edit/change-state.pt:41 +msgid "If checked, this will attempt to modify the status of all children in any selected documents and their subdocuments." +msgstr "Wenn Sie diese Option anwählen, wirkt sich die Statusänderung sowohl auf alle ausgewählte als auch auf die darin enthaltenen Dokumente aus." -#: kotti/templates/edit/change-state.pt:100 +#: kotti/templates/edit/change-state.pt:54 msgid "Change state to" msgstr "Ändere Status in" -#: kotti/templates/edit/change-state.pt:103 +#: kotti/templates/edit/change-state.pt:55 msgid "Select the new state where all chosen items should be set." -msgstr "" -"Wählen Sie den neuen Status, der für alle ausgwählten Dokumente gesetzt " -"werden soll." +msgstr "Wählen Sie den neuen Status, der für alle ausgwählten Dokumente gesetzt werden soll." -#: kotti/templates/edit/change-state.pt:111 +#: kotti/templates/edit/change-state.pt:60 msgid "No change" msgstr "Keine Änderungen." -#: kotti/templates/edit/change-state.pt:127 kotti/templates/edit/share.pt:121 -#: kotti/templates/site-setup/users.pt:165 -msgid "Apply changes" -msgstr "Änderungen übernehmen" - -#: kotti/templates/edit/change-state.pt:130 -#: kotti/templates/edit/delete-nodes.pt:93 -#: kotti/templates/edit/rename-nodes.pt:58 kotti/views/form.py:78 -#: kotti/views/users.py:316 kotti/views/users.py:350 kotti/views/users.py:442 -msgid "Cancel" -msgstr "Abbrechen" - -#: kotti/templates/edit/contents.pt:43 -msgid "No content items are contained here." -msgstr "Kein sichtbarer Inhalt." - -#: kotti/templates/edit/contents.pt:66 -msgid "Visibility" -msgstr "Sichtbarkeit" - -#: kotti/templates/edit/contents.pt:125 -msgid "Visible" -msgstr "Sichtbar" - -#: kotti/templates/edit/contents.pt:134 -msgid "Hidden" -msgstr "Unsichtbar" - -#: kotti/templates/edit/delete-nodes.pt:17 +#: kotti/templates/edit/delete-nodes.pt:13 msgid "Are you sure you want to delete the following items?" msgstr "Sind Sie sicher, dass Sie die folgenden Dokumente löschen möchten?" -#: kotti/templates/edit/delete.pt:8 -#: kotti/templates/site-setup/delete-user.pt:8 -msgid "Delete ${title}" -msgstr "Lösche ${title}" - -#: kotti/templates/edit/delete.pt:20 -msgid "Are you sure you want to delete ${title}?" -msgstr "Sind Sie sicher, dass Sie ${title} löschen möchten?" - -#: kotti/templates/edit/nav-tree-view.pt:7 -msgid "Navigate Site" -msgstr "Navigieren" +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "${title} umbenennen" -#: kotti/templates/edit/rename-nodes.pt:16 kotti/templates/edit/rename.pt:14 -msgid "" -"Each content item has a name, which is used to create the url, and a title. " -"Read the user manual about proper formatting of name and title if needed. " -"You can change both below." -msgstr "" -"Jeder Inhalt hat einen Namen, der auch Bestandteil der URL ist, und einen " -"Titel. Sie können beides ändern, indem Sie unten einen neuen Namen und/oder " -"Titel angeben." +#: kotti/templates/edit/rename.pt:11 kotti/templates/edit/rename-nodes.pt:13 +msgid "Each content item has a name, which is used to create the url, and a title. Read the user manual about proper formatting of name and title if needed. You can change both below." +msgstr "Jeder Inhalt hat einen Namen, der auch Bestandteil der URL ist, und einen Titel. Sie können beides ändern, indem Sie unten einen neuen Namen und/oder Titel angeben." -#: kotti/templates/edit/rename-nodes.pt:35 kotti/templates/edit/rename.pt:26 +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +#: kotti/templates/edit/rename-nodes.pt:28 msgid "New name" msgstr "Neuer Name" -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:34 +#: kotti/templates/edit/rename-nodes.pt:36 msgid "New title" msgstr "Neuer Titel" -#: kotti/templates/edit/rename.pt:7 -msgid "Rename ${title}" -msgstr "${title} umbenennen" +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" +msgstr "Inhalte aus lokalen Dateien hochladen" -#: kotti/templates/edit/share.pt:7 -msgid "Share ${title}" -msgstr "Teile ${title}" +#: kotti/templates/edit/upload.pt:17 kotti/templates/edit/upload.pt:107 +msgid "Filename" +msgstr "Dateiname" -#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:30 -#: kotti/templates/edit/share.pt:35 kotti/templates/site-setup/users.pt:68 -msgid "Search users and groups" -msgstr "Suche Benutzer und Gruppen" +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:109 +msgid "Size" +msgstr "Größe" -#: kotti/templates/edit/share.pt:43 kotti/templates/site-setup/users.pt:81 -msgid "Search" -msgstr "Suche" - -#: kotti/templates/edit/share.pt:60 -msgid "Assign local roles" -msgstr "Lokale Rollen zuweisen" - -#: kotti/templates/edit/share.pt:73 kotti/templates/site-setup/users.pt:108 -#: kotti/views/users.py:207 -msgid "Name" -msgstr "Name" - -#: kotti/templates/edit/share.pt:91 kotti/templates/site-setup/users.pt:126 -#: kotti/views/users.py:313 kotti/views/users.py:501 kotti/views/users.py:536 -msgid "User" -msgstr "Benutzer" - -#: kotti/templates/edit/share.pt:94 kotti/templates/site-setup/users.pt:129 -#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 -#: kotti/views/users.py:536 -msgid "Group" -msgstr "Gruppe" - -#: kotti/templates/edit/share.pt:101 kotti/templates/site-setup/users.pt:136 -msgid "Gravatar" -msgstr "Gravatar" - -#: kotti/templates/edit/upload.pt:9 -msgid "Upload content from local file(s)" -msgstr "Inhalte aus lokalen Dateien hochladen" - -#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:164 -msgid "Filename" -msgstr "Dateiname" - -#: kotti/templates/edit/upload.pt:26 kotti/templates/edit/upload.pt:170 -msgid "Size" -msgstr "Größe" - -#: kotti/templates/edit/upload.pt:29 +#: kotti/templates/edit/upload.pt:20 msgid "Status" msgstr "Status" -#: kotti/templates/edit/upload.pt:32 +#: kotti/templates/edit/upload.pt:21 msgid "Progress" msgstr "Fortschritt" -#: kotti/templates/edit/upload.pt:124 +#: kotti/templates/edit/upload.pt:84 msgid "Select file(s) to upload..." msgstr "Hochzuladene Dateien auswählen..." -#: kotti/templates/edit/upload.pt:134 +#: kotti/templates/edit/upload.pt:92 msgid "Upload ${number} files." msgstr "${number} Dateien hochladen" -#: kotti/templates/edit/upload.pt:144 +#: kotti/templates/edit/upload.pt:97 msgid "Dismiss all errors" msgstr "Alle Fehlermeldungen ausblenden" -#: kotti/templates/site-setup/delete-user.pt:20 +#: kotti/templates/edit/contents.pt:26 +msgid "No content items are contained here." +msgstr "Kein sichtbarer Inhalt." + +#: kotti/templates/edit/contents.pt:33 +msgid "Select / deselect all" +msgstr "Alles auswählen/abwählen" + +#: kotti/templates/edit/contents.pt:37 +msgid "Visibility" +msgstr "Sichtbarkeit" + +#: kotti/templates/edit/contents.pt:69 +msgid "Visible" +msgstr "Sichtbar" + +#: kotti/templates/edit/contents.pt:73 +msgid "Hidden" +msgstr "Unsichtbar" + +#: kotti/templates/http-errors/notfound.pt:26 +msgid "Not Found" +msgstr "Nicht gefunden" + +#: kotti/templates/http-errors/notfound.pt:27 +msgid "The resource could not be found" +msgstr "Die Ressource konnte nicht gefunden werden" + +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "Suchergebnisse" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "Suche..." + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "Verschlagwortet mit:" + +#: kotti/templates/site-setup/delete-user.pt:15 msgid "Are you sure you want to delete ${type} ${title}?" msgstr "Sind Sie sicher, dass Sie ${title} (${type}) löschen möchten?" -#: kotti/templates/site-setup/user.pt:13 +#: kotti/templates/site-setup/user.pt:12 msgid "Back to User Management" msgstr "Zurück zur Benutzerverwaltung" -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 +#: kotti/templates/site-setup/user.pt:21 kotti/views/form.py:159 #, python-format msgid "Edit ${title}" msgstr "Bearbeite ${title}" -#: kotti/templates/site-setup/users.pt:15 +#: kotti/templates/site-setup/users.pt:12 msgid "Search user(s) / group(s)" msgstr "Suche Benutzer / Gruppe(n)" -#: kotti/templates/site-setup/users.pt:24 +#: kotti/templates/site-setup/users.pt:17 msgid "Add user" msgstr "Neuer Benutzer" -#: kotti/templates/site-setup/users.pt:33 +#: kotti/templates/site-setup/users.pt:22 msgid "Add group" msgstr "Neue Gruppe" -#: kotti/templates/site-setup/users.pt:52 +#: kotti/templates/site-setup/users.pt:34 msgid "Find users or groups" msgstr "Suche Benutzer oder Gruppen" -#: kotti/templates/site-setup/users.pt:63 +#: kotti/templates/site-setup/users.pt:40 msgid "User- / groupname" msgstr "Benutzer- / Gruppenname" -#: kotti/templates/site-setup/users.pt:70 +#: kotti/templates/site-setup/users.pt:50 msgid "Blank search text finds all." msgstr "Suche mit leerem Eingabefeld liefert alle Benutzer." -#: kotti/templates/site-setup/users.pt:96 +#: kotti/templates/site-setup/users.pt:65 msgid "Assign global roles" msgstr "Globale Rollen zuweisen" -#: kotti/templates/site-setup/users.pt:180 +#: kotti/templates/site-setup/users.pt:117 msgid "Add new user" msgstr "Neuen Benutzer hinzufügen" -#: kotti/templates/site-setup/users.pt:190 +#: kotti/templates/site-setup/users.pt:123 msgid "Add new group" msgstr "Neue Gruppe hinzufügen" -#: kotti/templates/view/search-results.pt:8 -msgid "Search Results" -msgstr "Suchergebnisse" - -#: kotti/templates/view/search.pt:5 -msgid "Search..." -msgstr "Suche..." - -#: kotti/templates/view/tags.pt:5 -msgid "Tagged with:" -msgstr "Verschlagwortet mit:" - -#: kotti/tests/testing_view.pt:7 -msgid "Welcome, ${user}! You are logged in." -msgstr "Willkommen, ${user}! Sie sind angemeldet." - -#: kotti/tests/testing_view.pt:14 -msgid "Welcome, you are not logged in." -msgstr "Willkommen! Sie sind nicht angemeldet." - -#: kotti/views/form.py:79 kotti/views/users.py:69 -#: kotti/views/edit/actions.py:363 kotti/views/edit/actions.py:407 +#: kotti/views/users.py:70 kotti/views/form.py:82 +#: kotti/views/edit/actions.py:371 kotti/views/edit/actions.py:415 msgid "Your changes have been saved." msgstr "Ihre Änderungen wurden übernommen." -#: kotti/views/form.py:174 -msgid "Item was added." -msgstr "Dokument hinzugefügt." - -#: kotti/views/form.py:264 -#, python-format -msgid "Maximum file size: ${size}MB" -msgstr "Maximale Dateigröße: ${size}MB" - -#: kotti/views/form.py:77 kotti/views/users.py:441 -msgid "Save" -msgstr "Übernehmen" - -#: kotti/views/form.py:198 -#, python-format -msgid "Add ${type} to ${title}." -msgstr "${type} in ${title} erstellen" - -#: kotti/views/form.py:202 -#, python-format -msgid "Add ${type}." -msgstr "${type} erstellen" - -#: kotti/views/login.py:238 -msgid "You have reset your password." -msgstr "Ihr Passwort wurde erfolgreich zurückgesetzt." - -#: kotti/views/login.py:200 -msgid "You have been logged out." -msgstr "Sie wurden abgemeldet." - -#: kotti/views/login.py:66 kotti/views/users.py:270 -msgid "Full name" -msgstr "Vollständiger Name" - -#: kotti/views/login.py:69 -msgid "Username" -msgstr "Benutzername" - -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 -msgid "Email" -msgstr "E-Mail" - -#: kotti/views/login.py:109 -msgid "" -"Congratulations! You are successfully registered. You should be receiving an " -"email with a link to set your password. Doing so will activate your account." -msgstr "" -"Ihre Registrierung war erfolgreich. Sie sollten in Kürze eine E-Mail mit " -"einem Link zur Aktivierung und zum Setzen Ihres Passwortes erhalten." - -#: kotti/views/login.py:124 -#, python-format -msgid "Register - ${title}" -msgstr "Registrieren - ${title}" - -#: kotti/views/login.py:164 -msgid "Login failed." -msgstr "Anmeldung fehlgeschlagen." - -#: kotti/views/login.py:286 -#, python-format -msgid "Reset your password - ${title}." -msgstr "Passwort zurücksetzen - ${title}" - -#: kotti/views/login.py:160 -#, python-format -msgid "Welcome, ${user}!" -msgstr "Willkommen, ${user}!" - -#: kotti/views/login.py:173 -msgid "" -"You should be receiving an email with a link to reset your password. Doing " -"so will activate your account." -msgstr "" -"Ihre Registrierung war erfolgreich. Sie sollten in Kürze eine E-Mail mit " -"einem Link zur Aktivierung und zum Setzen Ihres Passwortes erhalten." - -#: kotti/views/login.py:178 -msgid "That username or email is not known by this system." -msgstr "Der angegebene Benutzername oder die E-Mail-Adresse ist nicht bekannt." - -#: kotti/views/login.py:83 -msgid "Register" -msgstr "Registrieren" - -#: kotti/views/login.py:90 kotti/views/login.py:257 -msgid "There was an error." -msgstr "Ein Fehler ist aufgetreten." - -#: kotti/views/login.py:250 -msgid "Submit" -msgstr "Absenden" - -#: kotti/views/login.py:279 -msgid "Your password reset token may have expired." -msgstr "Der Link zum Zurücksetzen Ihres Passwortes ist abgelaufen!" - -#: kotti/views/site_setup.py:6 kotti/views/users.py:378 -msgid "User Management" -msgstr "Benutzerverwaltung" - -#: kotti/views/users.py:267 -msgid "" -"Leave this empty and tick the 'Send password registration' box below to have " -"the user set their own password." -msgstr "" -"Lassen Sie die Passwort-Felder leer und aktivieren sie unten \"Schicke " -"Passwort-Registrierung\" um den Benutzer das Passwort selbst auswählen zu " -"lassen." +#: kotti/views/users.py:72 kotti/views/users.py:477 +#: kotti/views/edit/actions.py:309 kotti/views/edit/actions.py:375 +#: kotti/views/edit/actions.py:417 kotti/views/edit/actions.py:421 +msgid "No changes were made." +msgstr "Keine Änderungen." -#: kotti/views/users.py:587 -#, python-format -msgid "My preferences - ${title}" -msgstr "Meine Einstellungen - ${title}" +#: kotti/views/users.py:105 +msgid "No users or groups were found." +msgstr "Keine Benutzer oder Gruppen gefunden." -#: kotti/views/users.py:145 +#: kotti/views/users.py:153 msgid "Invalid value" msgstr "Ungültige Eingabe" -#: kotti/views/users.py:151 +#: kotti/views/users.py:159 msgid "A user with that name already exists." msgstr "Ein Benutzer mit diesem Namen existiert bereits." -#: kotti/views/users.py:158 +#: kotti/views/users.py:166 msgid "A user with that email already exists." msgstr "Ein Benutzer mit dieser E-Mail Adresse existiert bereits." -#: kotti/views/users.py:181 +#: kotti/views/users.py:189 #, python-format msgid "No such group: ${group}" msgstr "Unbekannte Gruppe: ${group}" -#: kotti/views/users.py:219 +#: kotti/views/users.py:207 kotti/views/login.py:71 kotti/views/login.py:267 +msgid "Email" +msgstr "E-Mail" + +#: kotti/views/users.py:227 msgid "Active" msgstr "Aktiv" -#: kotti/views/users.py:220 +#: kotti/views/users.py:228 msgid "Untick this to deactivate the account." msgstr "Abwählen um das Konto zu deaktivieren." -#: kotti/views/users.py:226 +#: kotti/views/users.py:234 msgid "Global roles" msgstr "Globale Rollen" -#: kotti/views/users.py:230 +#: kotti/views/users.py:238 msgid "Groups" msgstr "Gruppen" -#: kotti/views/users.py:315 +#: kotti/views/users.py:275 +msgid "Leave this empty and tick the 'Send password registration' box below to have the user set their own password." +msgstr "Lassen Sie die Passwort-Felder leer und aktivieren sie unten \"Schicke Passwort-Registrierung\" um den Benutzer das Passwort selbst auswählen zu lassen." + +#: kotti/views/users.py:278 kotti/views/login.py:63 +msgid "Full name" +msgstr "Vollständiger Name" + +#: kotti/views/users.py:323 msgid "Add User" msgstr "Neuer Benutzer" -#: kotti/views/users.py:339 +#: kotti/views/users.py:333 +msgid "Send password registration link." +msgstr "Schicke Passwort-Registrierungs-Link" + +#: kotti/views/users.py:348 #, python-format msgid "${title} was added." msgstr "${title} hinzugefügt." -#: kotti/views/users.py:349 +#: kotti/views/users.py:358 msgid "Add Group" msgstr "Neue Gruppe" -#: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:301 kotti/views/edit/actions.py:367 -#: kotti/views/edit/actions.py:413 kotti/views/edit/actions.py:409 -msgid "No changes were made." -msgstr "Keine Änderungen." - -#: kotti/views/users.py:558 -msgid "No name was given." -msgstr "Kein Name übergeben." +#: kotti/views/users.py:388 kotti/views/site_setup.py:5 +msgid "User Management" +msgstr "Benutzerverwaltung" -#: kotti/views/users.py:98 -msgid "No users or groups were found." -msgstr "Keine Benutzer oder Gruppen gefunden." +#: kotti/views/users.py:452 kotti/views/form.py:80 +msgid "Save" +msgstr "Übernehmen" -#: kotti/views/users.py:505 +#: kotti/views/users.py:519 #, python-format msgid "Edit ${principal_type} ${title}" msgstr "Bearbeite ${principal_type} ${title}" -#: kotti/views/users.py:533 +#: kotti/views/users.py:547 msgid "User was not found." msgstr "Keine Benutzer oder Gruppen gefunden." -#: kotti/views/users.py:324 -msgid "Send password registration link." -msgstr "Schicke Passwort-Registrierungs-Link" - -#: kotti/views/users.py:543 +#: kotti/views/users.py:557 #, python-format msgid "${principal_type} ${title} was deleted." msgstr "${principal_type} ${title} gelöscht." -#: kotti/views/users.py:551 +#: kotti/views/users.py:565 #, python-format msgid "Delete ${principal_type} ${title}" msgstr "Bearbeite ${principal_type} ${title}" -#: kotti/views/edit/actions.py:106 +#: kotti/views/users.py:572 +msgid "No name was given." +msgstr "Kein Name übergeben." + +#: kotti/views/users.py:601 #, python-format -msgid "${title} was copied." -msgstr "${title} kopiert." +msgid "My preferences - ${title}" +msgstr "Meine Einstellungen - ${title}" + +#: kotti/views/form.py:177 +msgid "Item was added." +msgstr "Dokument hinzugefügt." -#: kotti/views/edit/actions.py:124 +#: kotti/views/form.py:201 #, python-format -msgid "${title} was cut." -msgstr "${title} ausgeschnitten." +msgid "Add ${type} to ${title}." +msgstr "${type} in ${title} erstellen" -#: kotti/views/edit/actions.py:182 +#: kotti/views/form.py:205 #, python-format -msgid "${title} was moved." -msgstr "${title} verschoben." +msgid "Add ${type}." +msgstr "${type} erstellen" -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/form.py:295 #, python-format -msgid "${title} was deleted." -msgstr "${title} gelöscht." +msgid "Maximum file size: ${size}MB" +msgstr "Maximale Dateigröße: ${size}MB" -#: kotti/views/edit/actions.py:158 +#: kotti/views/login.py:66 +msgid "Username" +msgstr "Benutzername" + +#: kotti/views/login.py:80 +msgid "Register" +msgstr "Registrieren" + +#: kotti/views/login.py:87 +msgid "There was an error." +msgstr "Ein Fehler ist aufgetreten." + +#: kotti/views/login.py:106 +msgid "Congratulations! You are successfully registered. You should be receiving an email with a link to set your password. Doing so will activate your account." +msgstr "Ihre Registrierung war erfolgreich. Sie sollten in Kürze eine E-Mail mit einem Link zur Aktivierung und zum Setzen Ihres Passwortes erhalten." + +#: kotti/views/login.py:121 +#, python-format +msgid "Register - ${title}" +msgstr "Registrieren - ${title}" + +#: kotti/views/login.py:152 +#, python-format +msgid "Welcome, ${user}!" +msgstr "Willkommen, ${user}!" + +#: kotti/views/login.py:177 +msgid "You should be receiving an email with a link to reset your password. Doing so will activate your account." +msgstr "Ihre Registrierung war erfolgreich. Sie sollten in Kürze eine E-Mail mit einem Link zur Aktivierung und zum Setzen Ihres Passwortes erhalten." + +#: kotti/views/login.py:210 +msgid "Login failed." +msgstr "Anmeldung fehlgeschlagen." + +#: kotti/views/login.py:220 +msgid "That username or email is not known by this system." +msgstr "Der angegebene Benutzername oder die E-Mail-Adresse ist nicht bekannt." + +#: kotti/views/login.py:242 +msgid "You have been logged out." +msgstr "Sie wurden abgemeldet." + +#: kotti/views/login.py:280 +msgid "You have reset your password." +msgstr "Ihr Passwort wurde erfolgreich zurückgesetzt." + +#: kotti/views/login.py:299 +msgid "Set password" +msgstr "Passwort setzen" + +#: kotti/views/login.py:327 +msgid "Your password reset token may have expired." +msgstr "Der Link zum Zurücksetzen Ihres Passwortes ist abgelaufen!" + +#: kotti/views/login.py:334 +#, python-format +msgid "Reset your password - ${title}." +msgstr "Passwort zurücksetzen - ${title}" + +#: kotti/views/edit/default_views.py:86 +msgid "Default view" +msgstr "Standardansicht" + +#: kotti/views/edit/default_views.py:107 +msgid "Default view has been reset to default." +msgstr "Standardansicht wurde zurückgesetzt." + +#: kotti/views/edit/default_views.py:114 +msgid "Default view has been set." +msgstr "Standardansicht wurde gesetzt." + +#: kotti/views/edit/default_views.py:119 +msgid "Default view could not be set." +msgstr "Standardansicht konnte nicht geändert werden." + +#: kotti/views/edit/actions.py:102 +#, python-format +msgid "${title} was copied." +msgstr "${title} kopiert." + +#: kotti/views/edit/actions.py:120 +#, python-format +msgid "${title} was cut." +msgstr "${title} ausgeschnitten." + +#: kotti/views/edit/actions.py:155 #, python-format msgid "${title} was pasted." msgstr "${title} eingefügt." -#: kotti/views/edit/actions.py:161 +#: kotti/views/edit/actions.py:158 msgid "Could not paste node. It no longer exists." msgstr "Inhalt konnte nicht eingefügt werden, da er nicht mehr existiert." -#: kotti/views/edit/actions.py:224 +#: kotti/views/edit/actions.py:180 +#, python-format +msgid "${title} was moved." +msgstr "${title} verschoben." + +#: kotti/views/edit/actions.py:222 #, python-format msgid "${title} is now visible in the navigation." msgstr "${title} wird nun in der Navigation angezeigt." -#: kotti/views/edit/actions.py:227 +#: kotti/views/edit/actions.py:225 #, python-format msgid "${title} is no longer visible in the navigation." msgstr "${title} wird nun nicht mehr in der Navigation angezeigt." -#: kotti/views/edit/actions.py:292 +#: kotti/views/edit/actions.py:274 kotti/views/edit/actions.py:303 +#, python-format +msgid "${title} was deleted." +msgstr "${title} gelöscht." + +#: kotti/views/edit/actions.py:300 msgid "Nothing was deleted." msgstr "Nichts wurde gelöscht." -#: kotti/views/edit/actions.py:328 kotti/views/edit/actions.py:355 +#: kotti/views/edit/actions.py:336 kotti/views/edit/actions.py:363 msgid "Name and title are required." msgstr "Name und Titel werden benötigt." -#: kotti/views/edit/actions.py:332 +#: kotti/views/edit/actions.py:340 msgid "Item was renamed." msgstr "Objekt umbenannt." -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:461 +msgid "Change State" +msgstr "Status ändern" + +#: kotti/views/edit/actions.py:462 msgid "Move up" msgstr "Nach oben verschieben" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:463 msgid "Move down" msgstr "Nach unten verschieben" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:464 msgid "Show" msgstr "Anzeigen" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:465 msgid "Hide" msgstr "Verstecken" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:504 msgid "You have to select items to perform an action." msgstr "Sie müssen Dokumente auswählen, um eine Aktion auszuführen." -#: kotti/views/edit/actions.py:453 -msgid "Change State" -msgstr "Status ändern" - -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:34 msgid "Description" msgstr "Beschreibung" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:40 msgid "Tags" msgstr "Schlagworte:" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:49 msgid "Body" msgstr "Inhalt" -#: kotti/views/edit/default_views.py:99 -msgid "Default view has been reset to default." -msgstr "Standardansicht wurde zurückgesetzt." - -#: kotti/views/edit/default_views.py:78 -msgid "Default view" -msgstr "Standardansicht" - -#: kotti/views/edit/default_views.py:106 -msgid "Default view has been set." -msgstr "Standardansicht wurde gesetzt." - -#: kotti/views/edit/default_views.py:111 -msgid "Default view could not be set." -msgstr "Standardansicht konnte nicht geändert werden." +#~ msgid "Image" +#~ msgstr "Bild" + +#, fuzzy +#~ msgid "" +#~ "\n" +#~ "

    Log in

    \n" +#~ "

    \n" +#~ " You can log in to your site\n" +#~ " and start changing its contents. If you haven't chosen a password for\n" +#~ " your admin account yet, it'll likely be qwerty.\n" +#~ "

    \n" +#~ "

    \n" +#~ " Once you're logged in, you'll see the grey editor bar below the top\n" +#~ " navigation bar. It will allow you to switch between editing and viewing the\n" +#~ " current page as it will appear to your visitors.\n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Configure

    \n" +#~ "

    \n" +#~ " Find out how to configure your Kotti's title and many other settings using a\n" +#~ " simple text file in your file system.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Configuration manual\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Add-ons

    \n" +#~ "

    \n" +#~ " A number of add-ons allow you to extend the functionality of your Kotti site.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Kotti add-ons\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Documentation

    \n" +#~ "

    \n" +#~ " Wonder what more you can do with Kotti? What license it has? Read the\n" +#~ " manual for more information.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Documentation\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ msgstr "" +#~ "\n" +#~ "

    Anmelden

    \n" +#~ "

    \n" +#~ " Sie können sich nun anmelden und mit der Bearbeitung der Inhalte Ihrer Seite beginnen. Falls Sie bisher kein Passwort für Ihren \"admin\" Zugang festgelegt haben, lautet dieses höchstwahrscheinlich qwerty.\n" +#~ "

    \n" +#~ "

    \n" +#~ " Nach der Anmeldung werden Sie eine graue Bearbeitungsleiste unter der Hauptnavigation sehen. Dort können Sie zwischen Ansicht, Bearbeitung wechseln, sowie weitere Funktionen ausführen.\n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Konfiguration

    \n" +#~ "

    \n" +#~ " Finden Sie heraus, wie Sie den Titel Ihrer Installation und vieles andere mehr durch die Bearbeitung einer einfachen Textdatei anpassen können.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Configuration manual\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Erweiterungen

    \n" +#~ "

    \n" +#~ " Mit Hilfe von Erweiterungen können Sie Ihrer Installation zusätzliche Funktionen hinzufügen.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Kotti Erweiterungen\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    DoKumentation

    \n" +#~ "

    \n" +#~ " Fragen Sie sich, was Sie noch alles mit Kotti machen können? Unter welcher Lizenz es veröffentlich ist? Lesen Sie die Dokumentation für diese und weitere Informationen.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Dokumentation\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" + +#~ msgid "" +#~ "\n" +#~ "

    \n" +#~ " \"five\n" +#~ "

    \n" +#~ "\n" +#~ "

    \n" +#~ " Our worldwide headquarters:\n" +#~ "

    \n" +#~ "\n" +#~ "
    \n" +#~ " Foo World
    \n" +#~ " 123 Nowhere Street, Suite 777
    \n" +#~ " Omak, WA 98841 USA
    \n" +#~ " +1-509-555-0100
    \n" +#~ " widgets@foowrld.example.com\n" +#~ "
    \n" +#~ "\n" +#~ "

    \n" +#~ " Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +#~ " \n" +#~ " Copyright info.\n" +#~ " Originally published in the\n" +#~ " Extra EA-300\n" +#~ " article.\n" +#~ "

    \n" +#~ msgstr "" +#~ "\n" +#~ "

    \n" +#~ " \"five\n" +#~ "

    \n" +#~ "\n" +#~ "

    \n" +#~ " Unsere Konzernzentrale:\n" +#~ "

    \n" +#~ "\n" +#~ "
    \n" +#~ " Foo World
    \n" +#~ " 123 Nowhere Street, Suite 777
    \n" +#~ " Omak, WA 98841 USA
    \n" +#~ " +1-509-555-0100
    \n" +#~ " widgets@foowrld.example.com\n" +#~ "
    \n" +#~ "\n" +#~ "

    \n" +#~ " Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +#~ " \n" +#~ " Copyright info.\n" +#~ " Originally published in the\n" +#~ " Extra EA-300\n" +#~ " article.\n" +#~ "

    \n" + +#~ msgid "Submit" +#~ msgstr "Absenden" #~ msgid "Private" #~ msgstr "Privat" diff --git a/kotti/locale/en/LC_MESSAGES/Kotti.mo b/kotti/locale/en/LC_MESSAGES/Kotti.mo index c8f41979e..3f6810c79 100644 Binary files a/kotti/locale/en/LC_MESSAGES/Kotti.mo and b/kotti/locale/en/LC_MESSAGES/Kotti.mo differ diff --git a/kotti/locale/en/LC_MESSAGES/Kotti.po b/kotti/locale/en/LC_MESSAGES/Kotti.po index 8572739e7..10f0c98bc 100644 --- a/kotti/locale/en/LC_MESSAGES/Kotti.po +++ b/kotti/locale/en/LC_MESSAGES/Kotti.po @@ -1,60 +1,113 @@ -# English translations for Kotti. -# Copyright (C) 2012 ORGANIZATION -# This file is distributed under the same license as the Kotti project. -# FIRST AUTHOR , 2012. -# msgid "" msgstr "" -"Project-Id-Version: Kotti 0.4.4\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2014-11-21 21:56+0100\n" -"PO-Revision-Date: 2012-01-16 12:02+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: en \n" -"Language: \n" +"POT-Creation-Date: 2018-01-17 13:51+0100\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" -"Generated-By: Babel 0.9.6\n" -#: kotti/populate.py:64 +#: kotti/security.py:173 +msgid "Viewer" +msgstr "" + +#: kotti/security.py:174 +msgid "Editor" +msgstr "" + +#: kotti/security.py:175 +msgid "Owner" +msgstr "" + +#: kotti/security.py:176 +msgid "Admin" +msgstr "" + +#: kotti/resources.py:541 kotti/views/edit/actions.py:453 +msgid "Copy" +msgstr "" + +#: kotti/resources.py:542 kotti/views/edit/actions.py:454 +msgid "Cut" +msgstr "" + +#: kotti/resources.py:543 kotti/views/edit/actions.py:450 +msgid "Paste" +msgstr "" + +#: kotti/resources.py:544 kotti/templates/edit/rename.pt:31 +#: kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:42 kotti/views/edit/actions.py:455 +msgid "Rename" +msgstr "" + +#: kotti/resources.py:545 kotti/templates/edit/delete.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:49 +#: kotti/templates/site-setup/delete-user.pt:24 kotti/views/users.py:454 +#: kotti/views/edit/actions.py:457 +msgid "Delete" +msgstr "" + +#: kotti/resources.py:556 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "" + +#: kotti/resources.py:557 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "" + +#: kotti/resources.py:558 +msgid "Share" +msgstr "" + +#: kotti/resources.py:559 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "" + +#: kotti/resources.py:562 +msgid "Folder view" +msgstr "" + +#: kotti/resources.py:671 kotti/views/edit/content.py:61 +msgid "Document" +msgstr "" + +#: kotti/resources.py:808 kotti/views/edit/content.py:73 +#: kotti/views/edit/content.py:87 +msgid "File" +msgstr "" + +#: kotti/populate.py:67 msgid "Welcome to Kotti" msgstr "" -#: kotti/populate.py:65 +#: kotti/populate.py:68 msgid "Congratulations! You have successfully installed Kotti." msgstr "" -#: kotti/populate.py:66 +#: kotti/populate.py:69 #, c-format msgid "" "\n" "

    Log in

    \n" "

    \n" -" You can log in to your " -"site\n" +" You can log in to your site\n" " and start changing its contents. If you haven't chosen a password for\n" " your admin account yet, it'll likely be qwerty.\n" "

    \n" "

    \n" " Once you're logged in, you'll see the grey editor bar below the top\n" -" navigation bar. It will allow you to switch between editing and viewing " -"the\n" +" navigation bar. It will allow you to switch between editing and viewing the\n" " current page as it will appear to your visitors.\n" "

    \n" "
    \n" "
    \n" "

    Configure

    \n" "

    \n" -" Find out how to configure your Kotti's title and many other " -"settings using a\n" -" simple text file in your file system.\n" +" Find out how to configure your Kotti's title and many other\n" +" settings using a simple text file in your file system.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -62,13 +115,12 @@ msgid "" "
    \n" "

    Add-ons

    \n" "

    \n" -" A number of add-ons allow you to extend the functionality of " -"your Kotti site.\n" +" A number of add-ons allow you to extend the functionality of your\n" +" Kotti site.\n" "

    \n" "

    \n" " \n" +" href=\"http://pypi.python.org/pypi?%3Aaction=search&term=kotti\">\n" " Kotti add-ons\n" " \n" "

    \n" @@ -76,13 +128,13 @@ msgid "" "
    \n" "

    Documentation

    \n" "

    \n" -" Wonder what more you can do with Kotti? What license it has? " -"Read the\n" -" manual for more information.\n" +" Wonder what more you can do with Kotti?\n" +" What license it has?\n" +" Read the manual for more information.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/\">\n" " Documentation\n" " \n" "

    \n" @@ -90,23 +142,20 @@ msgid "" "
    \n" msgstr "" -#: kotti/populate.py:121 +#: kotti/populate.py:126 msgid "About" msgstr "" -#: kotti/populate.py:122 -msgid "" -"Our company is the leading manufacturer of foo widgets used in a wide " -"variety of aviation and and industrial products." +#: kotti/populate.py:127 +msgid "Our company is the leading manufacturer of foo widgets used in a wide variety of aviation and and industrial products." msgstr "" -#: kotti/populate.py:123 +#: kotti/populate.py:128 msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -123,760 +172,697 @@ msgid "" "\n" "\n" "

    \n" -" Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" -" \n" -" Copyright info.\n" -" Originally published in the\n" -" Extra EA-300\n" -" article.\n" +"Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +"\n" +"Copyright info.\n" +"Originally published in the\n" +" Extra EA-300\n" +"article.\n" "

    \n" msgstr "" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 -msgid "Document" +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." msgstr "" -#: kotti/resources.py:649 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 -msgid "File" +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." msgstr "" -#: kotti/resources.py:726 kotti/views/edit/content.py:144 -msgid "Image" +#: kotti/templates/editor-bar.pt:19 +msgid "View" msgstr "" -#: kotti/resources.py:500 -msgid "Folder view" +#: kotti/templates/editor-bar.pt:42 +msgid "Navigate" msgstr "" -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 -msgid "Contents" +#: kotti/templates/editor-bar.pt:53 +msgid "Preferences" msgstr "" -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" +#: kotti/templates/editor-bar.pt:58 +msgid "Site Setup" msgstr "" -#: kotti/resources.py:489 -msgid "Share" +#: kotti/templates/editor-bar.pt:69 +msgid "Logout" msgstr "" -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" msgstr "" -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 -msgid "Copy" +#: kotti/templates/login.pt:16 +msgid "Login" msgstr "" -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 -msgid "Cut" +#: kotti/templates/login.pt:21 kotti/templates/login.pt:59 +msgid "Username or email" msgstr "" -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 -msgid "Paste" +#: kotti/templates/login.pt:26 kotti/views/users.py:220 +#: kotti/views/login.py:255 +msgid "Password" msgstr "" -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 -#: kotti/templates/edit/rename-nodes.pt:55 kotti/templates/edit/rename.pt:42 -#: kotti/views/edit/actions.py:447 -msgid "Rename" +#: kotti/templates/login.pt:34 +msgid "Log in" msgstr "" -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 -#: kotti/templates/edit/delete-nodes.pt:90 kotti/templates/edit/delete.pt:28 -#: kotti/templates/site-setup/delete-user.pt:36 kotti/views/users.py:443 -#: kotti/views/edit/actions.py:449 -msgid "Delete" +#: kotti/templates/login.pt:45 +msgid "Forgot your password?" msgstr "" -#: kotti/security.py:165 -msgid "Viewer" +#. Used in sentence: "Fill out your username or email and click +#. ${reset_password} below to receive an email with a link to reset your +#. password." +#. +#: kotti/templates/login.pt:52 kotti/templates/login.pt:67 +msgid "Reset password" msgstr "" -#: kotti/security.py:166 -msgid "Editor" +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:50 +msgid "Fill out your username or email and click ${reset_password} below to receive an email with a link to reset your password." msgstr "" -#: kotti/security.py:167 -msgid "Owner" +#: kotti/templates/login.pt:78 +msgid "Not registered yet?" msgstr "" -#: kotti/security.py:168 -msgid "Admin" +#: kotti/templates/login.pt:85 +msgid "Register for an account on this site." msgstr "" -#: kotti/templates/add-dropdown.pt:5 -msgid "Add" +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" msgstr "" -#: kotti/templates/add-dropdown.pt:27 -msgid "Upload Content" +#: kotti/templates/add-dropdown.pt:3 +msgid "Add" msgstr "" -#: kotti/templates/default-view-selector.pt:5 -msgid "Set default view" +#: kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" msgstr "" -#: kotti/templates/editor-bar.pt:35 -msgid "View" +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" msgstr "" -#: kotti/templates/editor-bar.pt:76 -msgid "Navigate" +#: kotti/templates/email-reset-password.pt:2 +msgid "Reset your password for ${site_title}." msgstr "" -#: kotti/templates/editor-bar.pt:98 -msgid "Preferences" +#: kotti/templates/email-reset-password.pt:4 +#: kotti/templates/email-set-password.pt:4 +msgid "Hello, ${user_title}!" msgstr "" -#: kotti/templates/editor-bar.pt:109 -msgid "Site Setup" +#: kotti/templates/email-reset-password.pt:5 +msgid "Click this link to reset your password at ${site_title}: ${url}" msgstr "" -#: kotti/templates/editor-bar.pt:131 -msgid "Logout" +#: kotti/templates/email-set-password.pt:2 +msgid "Your registration for ${site_title}" msgstr "" -#: kotti/templates/email-reset-password.pt:3 -msgid "Reset your password for ${site_title}." +#: kotti/templates/email-set-password.pt:5 +msgid "You are joining ${site_title}." msgstr "" -#: kotti/templates/email-reset-password.pt:7 #: kotti/templates/email-set-password.pt:6 -msgid "Hello, ${user_title}!" +msgid "Click here to set your password and log in: ${url}" msgstr "" -#: kotti/templates/email-reset-password.pt:10 -msgid "Click this link to reset your password at ${site_title}: ${url}" +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" msgstr "" -#: kotti/templates/email-set-password.pt:3 -msgid "Your registration for ${site_title}" +#: kotti/templates/edit/share.pt:14 kotti/templates/edit/share.pt:20 +#: kotti/templates/edit/share.pt:27 kotti/templates/site-setup/users.pt:48 +msgid "Search users and groups" msgstr "" -#: kotti/templates/email-set-password.pt:9 -msgid "You are joining ${site_title}." +#: kotti/templates/edit/share.pt:34 kotti/templates/site-setup/users.pt:56 +msgid "Search" msgstr "" -#: kotti/templates/email-set-password.pt:12 -msgid "Click here to set your password and log in: ${url}" +#: kotti/templates/edit/share.pt:44 +msgid "Assign local roles" msgstr "" -#: kotti/templates/forbidden.pt:7 -msgid "Forbidden" +#: kotti/templates/edit/share.pt:49 kotti/templates/edit/change-state.pt:19 +#: kotti/templates/edit/delete-nodes.pt:22 kotti/templates/edit/upload.pt:18 +#: kotti/templates/edit/upload.pt:108 kotti/templates/edit/contents.pt:35 +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:69 +msgid "Type" msgstr "" -#: kotti/templates/login.pt:16 -msgid "Login" +#: kotti/templates/edit/share.pt:50 kotti/templates/site-setup/users.pt:70 +#: kotti/views/users.py:215 +msgid "Name" msgstr "" -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 -msgid "Username or email" +#: kotti/templates/edit/share.pt:61 kotti/templates/site-setup/users.pt:81 +#: kotti/views/users.py:321 kotti/views/users.py:515 kotti/views/users.py:550 +msgid "User" msgstr "" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 -#: kotti/views/users.py:212 -msgid "Password" +#: kotti/templates/edit/share.pt:62 kotti/templates/site-setup/users.pt:82 +#: kotti/views/users.py:196 kotti/views/users.py:356 kotti/views/users.py:515 +#: kotti/views/users.py:550 +msgid "Group" msgstr "" -#: kotti/templates/login.pt:47 -msgid "Log in" +#: kotti/templates/edit/share.pt:65 kotti/templates/site-setup/users.pt:85 +msgid "Gravatar" msgstr "" -#: kotti/templates/login.pt:65 -msgid "Forgot your password?" +#: kotti/templates/edit/share.pt:70 kotti/templates/site-setup/users.pt:91 +msgid "Assign role" msgstr "" -#. Used in sentence: "Fill out your username or email and click -#. ${reset_password} below to receive an email with a link to reset your -#. password." -#. -#: kotti/templates/login.pt:77 kotti/templates/login.pt:100 -msgid "Reset password" +#: kotti/templates/edit/share.pt:85 kotti/templates/edit/change-state.pt:72 +#: kotti/templates/site-setup/users.pt:107 +msgid "Apply changes" msgstr "" -#. Canonical text for ${reset_password} is: "Reset password" -#: kotti/templates/login.pt:74 -msgid "" -"Fill out your username or email and click ${reset_password} below to receive " -"an email with a link to reset your password." +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" msgstr "" -#: kotti/templates/login.pt:118 -msgid "Not registered yet?" +#: kotti/templates/edit/breadcrumbs.pt:8 +msgid "You are here:" msgstr "" -#: kotti/templates/login.pt:127 -msgid "Register for an account on this site." +#: kotti/templates/edit/delete.pt:9 kotti/templates/site-setup/delete-user.pt:9 +msgid "Delete ${title}" msgstr "" -#: kotti/templates/workflow-dropdown.pt:19 -msgid "Make ${state}" +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" msgstr "" -#: kotti/templates/edit/breadcrumbs.pt:5 -msgid "You are here:" +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:74 +#: kotti/templates/edit/delete-nodes.pt:44 +#: kotti/templates/edit/rename-nodes.pt:44 kotti/views/users.py:324 +#: kotti/views/users.py:359 kotti/views/users.py:453 kotti/views/form.py:81 +msgid "Cancel" msgstr "" -#: kotti/templates/edit/change-state.pt:7 +#: kotti/templates/edit/change-state.pt:8 msgid "Change workflow state" msgstr "" -#: kotti/templates/edit/change-state.pt:14 +#: kotti/templates/edit/change-state.pt:12 msgid "An item's workflow state determines who can see, edit and/or manage it." msgstr "" -#: kotti/templates/edit/change-state.pt:26 kotti/templates/edit/contents.pt:57 -#: kotti/templates/edit/delete-nodes.pt:33 kotti/templates/view/folder.pt:28 -#: kotti/views/users.py:196 kotti/views/edit/content.py:31 +#: kotti/templates/edit/change-state.pt:18 +#: kotti/templates/edit/delete-nodes.pt:21 kotti/templates/edit/contents.pt:34 +#: kotti/templates/view/folder.pt:20 kotti/views/users.py:204 +#: kotti/views/edit/content.py:28 msgid "Title" msgstr "" -#: kotti/templates/edit/change-state.pt:29 kotti/templates/edit/contents.pt:60 -#: kotti/templates/edit/delete-nodes.pt:36 kotti/templates/edit/share.pt:70 -#: kotti/templates/edit/upload.pt:23 kotti/templates/edit/upload.pt:167 -#: kotti/templates/site-setup/users.pt:105 kotti/templates/view/folder.pt:31 -msgid "Type" -msgstr "" - -#: kotti/templates/edit/change-state.pt:32 kotti/templates/edit/contents.pt:63 -#: kotti/templates/edit/delete-nodes.pt:39 +#: kotti/templates/edit/change-state.pt:20 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:36 msgid "State" msgstr "" -#: kotti/templates/edit/change-state.pt:35 kotti/templates/edit/contents.pt:69 -#: kotti/templates/edit/delete-nodes.pt:42 kotti/templates/view/folder.pt:34 +#: kotti/templates/edit/change-state.pt:21 +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:38 +#: kotti/templates/view/folder.pt:22 msgid "Creation Date" msgstr "" -#: kotti/templates/edit/change-state.pt:38 kotti/templates/edit/contents.pt:72 -#: kotti/templates/edit/delete-nodes.pt:45 kotti/templates/view/folder.pt:37 +#: kotti/templates/edit/change-state.pt:22 +#: kotti/templates/edit/delete-nodes.pt:25 kotti/templates/edit/contents.pt:39 +#: kotti/templates/view/folder.pt:23 msgid "Modification Date" msgstr "" -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 +#: kotti/templates/edit/change-state.pt:28 +#: kotti/templates/edit/delete-nodes.pt:31 kotti/templates/edit/contents.pt:45 +msgid "Select item" +msgstr "" + +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:48 msgid "Include children" msgstr "" -#: kotti/templates/edit/change-state.pt:82 -msgid "" -"If checked, this will attempt to modify the status of all children in any " -"selected documents and their subdocuments." +#: kotti/templates/edit/change-state.pt:41 +msgid "If checked, this will attempt to modify the status of all children in any selected documents and their subdocuments." msgstr "" -#: kotti/templates/edit/change-state.pt:100 +#: kotti/templates/edit/change-state.pt:54 msgid "Change state to" msgstr "" -#: kotti/templates/edit/change-state.pt:103 +#: kotti/templates/edit/change-state.pt:55 msgid "Select the new state where all chosen items should be set." msgstr "" -#: kotti/templates/edit/change-state.pt:111 +#: kotti/templates/edit/change-state.pt:60 msgid "No change" msgstr "" -#: kotti/templates/edit/change-state.pt:127 kotti/templates/edit/share.pt:121 -#: kotti/templates/site-setup/users.pt:165 -msgid "Apply changes" -msgstr "" - -#: kotti/templates/edit/change-state.pt:130 -#: kotti/templates/edit/delete-nodes.pt:93 -#: kotti/templates/edit/rename-nodes.pt:58 kotti/views/form.py:78 -#: kotti/views/users.py:316 kotti/views/users.py:350 kotti/views/users.py:442 -msgid "Cancel" -msgstr "" - -#: kotti/templates/edit/contents.pt:43 -msgid "No content items are contained here." -msgstr "" - -#: kotti/templates/edit/contents.pt:66 -msgid "Visibility" -msgstr "" - -#: kotti/templates/edit/contents.pt:125 -msgid "Visible" -msgstr "" - -#: kotti/templates/edit/contents.pt:134 -msgid "Hidden" -msgstr "" - -#: kotti/templates/edit/delete-nodes.pt:17 +#: kotti/templates/edit/delete-nodes.pt:13 msgid "Are you sure you want to delete the following items?" msgstr "" -#: kotti/templates/edit/delete.pt:8 -#: kotti/templates/site-setup/delete-user.pt:8 -msgid "Delete ${title}" -msgstr "" - -#: kotti/templates/edit/delete.pt:20 -msgid "Are you sure you want to delete ${title}?" -msgstr "" - -#: kotti/templates/edit/nav-tree-view.pt:7 -msgid "Navigate Site" +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" msgstr "" -#: kotti/templates/edit/rename-nodes.pt:16 kotti/templates/edit/rename.pt:14 -msgid "" -"Each content item has a name, which is used to create the url, and a title. " -"Read the user manual about proper formatting of name and title if needed. " -"You can change both below." +#: kotti/templates/edit/rename.pt:11 kotti/templates/edit/rename-nodes.pt:13 +msgid "Each content item has a name, which is used to create the url, and a title. Read the user manual about proper formatting of name and title if needed. You can change both below." msgstr "" -#: kotti/templates/edit/rename-nodes.pt:35 kotti/templates/edit/rename.pt:26 +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +#: kotti/templates/edit/rename-nodes.pt:28 msgid "New name" msgstr "" -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:34 +#: kotti/templates/edit/rename-nodes.pt:36 msgid "New title" msgstr "" -#: kotti/templates/edit/rename.pt:7 -msgid "Rename ${title}" +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" msgstr "" -#: kotti/templates/edit/share.pt:7 -msgid "Share ${title}" +#: kotti/templates/edit/upload.pt:17 kotti/templates/edit/upload.pt:107 +msgid "Filename" msgstr "" -#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:30 -#: kotti/templates/edit/share.pt:35 kotti/templates/site-setup/users.pt:68 -msgid "Search users and groups" +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:109 +msgid "Size" msgstr "" -#: kotti/templates/edit/share.pt:43 kotti/templates/site-setup/users.pt:81 -msgid "Search" +#: kotti/templates/edit/upload.pt:20 +msgid "Status" msgstr "" -#: kotti/templates/edit/share.pt:60 -msgid "Assign local roles" +#: kotti/templates/edit/upload.pt:21 +msgid "Progress" msgstr "" -#: kotti/templates/edit/share.pt:73 kotti/templates/site-setup/users.pt:108 -#: kotti/views/users.py:207 -msgid "Name" +#: kotti/templates/edit/upload.pt:84 +msgid "Select file(s) to upload..." msgstr "" -#: kotti/templates/edit/share.pt:91 kotti/templates/site-setup/users.pt:126 -#: kotti/views/users.py:313 kotti/views/users.py:501 kotti/views/users.py:536 -msgid "User" +#: kotti/templates/edit/upload.pt:92 +msgid "Upload ${number} files." msgstr "" -#: kotti/templates/edit/share.pt:94 kotti/templates/site-setup/users.pt:129 -#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 -#: kotti/views/users.py:536 -msgid "Group" +#: kotti/templates/edit/upload.pt:97 +msgid "Dismiss all errors" msgstr "" -#: kotti/templates/edit/share.pt:101 kotti/templates/site-setup/users.pt:136 -msgid "Gravatar" +#: kotti/templates/edit/contents.pt:26 +msgid "No content items are contained here." msgstr "" -#: kotti/templates/edit/upload.pt:9 -msgid "Upload content from local file(s)" +#: kotti/templates/edit/contents.pt:33 +msgid "Select / deselect all" msgstr "" -#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:164 -msgid "Filename" +#: kotti/templates/edit/contents.pt:37 +msgid "Visibility" msgstr "" -#: kotti/templates/edit/upload.pt:26 kotti/templates/edit/upload.pt:170 -msgid "Size" +#: kotti/templates/edit/contents.pt:69 +msgid "Visible" msgstr "" -#: kotti/templates/edit/upload.pt:29 -msgid "Status" +#: kotti/templates/edit/contents.pt:73 +msgid "Hidden" msgstr "" -#: kotti/templates/edit/upload.pt:32 -msgid "Progress" +#: kotti/templates/http-errors/notfound.pt:26 +msgid "Not Found" msgstr "" -#: kotti/templates/edit/upload.pt:124 -msgid "Select file(s) to upload..." +#: kotti/templates/http-errors/notfound.pt:27 +msgid "The resource could not be found" msgstr "" -#: kotti/templates/edit/upload.pt:134 -msgid "Upload ${number} files." +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" msgstr "" -#: kotti/templates/edit/upload.pt:144 -msgid "Dismiss all errors" +#: kotti/templates/view/search.pt:12 +msgid "Search..." msgstr "" -#: kotti/templates/site-setup/delete-user.pt:20 +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "" + +#: kotti/templates/site-setup/delete-user.pt:15 msgid "Are you sure you want to delete ${type} ${title}?" msgstr "" -#: kotti/templates/site-setup/user.pt:13 +#: kotti/templates/site-setup/user.pt:12 msgid "Back to User Management" msgstr "" -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 +#: kotti/templates/site-setup/user.pt:21 kotti/views/form.py:159 #, python-format msgid "Edit ${title}" msgstr "" -#: kotti/templates/site-setup/users.pt:15 +#: kotti/templates/site-setup/users.pt:12 msgid "Search user(s) / group(s)" msgstr "" -#: kotti/templates/site-setup/users.pt:24 +#: kotti/templates/site-setup/users.pt:17 msgid "Add user" msgstr "" -#: kotti/templates/site-setup/users.pt:33 +#: kotti/templates/site-setup/users.pt:22 msgid "Add group" msgstr "" -#: kotti/templates/site-setup/users.pt:52 +#: kotti/templates/site-setup/users.pt:34 msgid "Find users or groups" msgstr "" -#: kotti/templates/site-setup/users.pt:63 +#: kotti/templates/site-setup/users.pt:40 msgid "User- / groupname" msgstr "" -#: kotti/templates/site-setup/users.pt:70 +#: kotti/templates/site-setup/users.pt:50 msgid "Blank search text finds all." msgstr "" -#: kotti/templates/site-setup/users.pt:96 +#: kotti/templates/site-setup/users.pt:65 msgid "Assign global roles" msgstr "" -#: kotti/templates/site-setup/users.pt:180 +#: kotti/templates/site-setup/users.pt:117 msgid "Add new user" msgstr "" -#: kotti/templates/site-setup/users.pt:190 +#: kotti/templates/site-setup/users.pt:123 msgid "Add new group" msgstr "" -#: kotti/templates/view/search-results.pt:8 -msgid "Search Results" -msgstr "" - -#: kotti/templates/view/search.pt:5 -msgid "Search..." +#: kotti/views/users.py:70 kotti/views/form.py:82 +#: kotti/views/edit/actions.py:371 kotti/views/edit/actions.py:415 +msgid "Your changes have been saved." msgstr "" -#: kotti/templates/view/tags.pt:5 -msgid "Tagged with:" +#: kotti/views/users.py:72 kotti/views/users.py:477 +#: kotti/views/edit/actions.py:309 kotti/views/edit/actions.py:375 +#: kotti/views/edit/actions.py:417 kotti/views/edit/actions.py:421 +msgid "No changes were made." msgstr "" -#: kotti/tests/testing_view.pt:7 -msgid "Welcome, ${user}! You are logged in." +#: kotti/views/users.py:105 +msgid "No users or groups were found." msgstr "" -#: kotti/tests/testing_view.pt:14 -msgid "Welcome, you are not logged in." +#: kotti/views/users.py:153 +msgid "Invalid value" msgstr "" -#: kotti/views/form.py:79 kotti/views/users.py:69 -#: kotti/views/edit/actions.py:363 kotti/views/edit/actions.py:407 -msgid "Your changes have been saved." +#: kotti/views/users.py:159 +msgid "A user with that name already exists." msgstr "" -#: kotti/views/form.py:174 -msgid "Item was added." +#: kotti/views/users.py:166 +msgid "A user with that email already exists." msgstr "" -#: kotti/views/form.py:264 +#: kotti/views/users.py:189 #, python-format -msgid "Maximum file size: ${size}MB" +msgid "No such group: ${group}" msgstr "" -#: kotti/views/form.py:77 kotti/views/users.py:441 -msgid "Save" +#: kotti/views/users.py:207 kotti/views/login.py:71 kotti/views/login.py:267 +msgid "Email" msgstr "" -#: kotti/views/form.py:198 -#, python-format -msgid "Add ${type} to ${title}." +#: kotti/views/users.py:227 +msgid "Active" msgstr "" -#: kotti/views/form.py:202 -#, python-format -msgid "Add ${type}." +#: kotti/views/users.py:228 +msgid "Untick this to deactivate the account." msgstr "" -#: kotti/views/login.py:238 -msgid "You have reset your password." +#: kotti/views/users.py:234 +msgid "Global roles" msgstr "" -#: kotti/views/login.py:200 -msgid "You have been logged out." +#: kotti/views/users.py:238 +msgid "Groups" msgstr "" -#: kotti/views/login.py:66 kotti/views/users.py:270 -msgid "Full name" +#: kotti/views/users.py:275 +msgid "Leave this empty and tick the 'Send password registration' box below to have the user set their own password." msgstr "" -#: kotti/views/login.py:69 -msgid "Username" +#: kotti/views/users.py:278 kotti/views/login.py:63 +msgid "Full name" msgstr "" -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 -msgid "Email" +#: kotti/views/users.py:323 +msgid "Add User" msgstr "" -#: kotti/views/login.py:109 -msgid "" -"Congratulations! You are successfully registered. You should be receiving an " -"email with a link to set your password. Doing so will activate your account." +#: kotti/views/users.py:333 +msgid "Send password registration link." msgstr "" -#: kotti/views/login.py:124 +#: kotti/views/users.py:348 #, python-format -msgid "Register - ${title}" +msgid "${title} was added." msgstr "" -#: kotti/views/login.py:164 -msgid "Login failed." +#: kotti/views/users.py:358 +msgid "Add Group" msgstr "" -#: kotti/views/login.py:286 -#, python-format -msgid "Reset your password - ${title}." +#: kotti/views/users.py:388 kotti/views/site_setup.py:5 +msgid "User Management" msgstr "" -#: kotti/views/login.py:160 -#, python-format -msgid "Welcome, ${user}!" +#: kotti/views/users.py:452 kotti/views/form.py:80 +msgid "Save" msgstr "" -#: kotti/views/login.py:173 -msgid "" -"You should be receiving an email with a link to reset your password. Doing " -"so will activate your account." +#: kotti/views/users.py:519 +#, python-format +msgid "Edit ${principal_type} ${title}" msgstr "" -#: kotti/views/login.py:178 -msgid "That username or email is not known by this system." +#: kotti/views/users.py:547 +msgid "User was not found." msgstr "" -#: kotti/views/login.py:83 -msgid "Register" +#: kotti/views/users.py:557 +#, python-format +msgid "${principal_type} ${title} was deleted." msgstr "" -#: kotti/views/login.py:90 kotti/views/login.py:257 -msgid "There was an error." +#: kotti/views/users.py:565 +#, python-format +msgid "Delete ${principal_type} ${title}" msgstr "" -#: kotti/views/login.py:250 -msgid "Submit" +#: kotti/views/users.py:572 +msgid "No name was given." msgstr "" -#: kotti/views/login.py:279 -msgid "Your password reset token may have expired." +#: kotti/views/users.py:601 +#, python-format +msgid "My preferences - ${title}" msgstr "" -#: kotti/views/site_setup.py:6 kotti/views/users.py:378 -msgid "User Management" +#: kotti/views/form.py:177 +msgid "Item was added." msgstr "" -#: kotti/views/users.py:267 -msgid "" -"Leave this empty and tick the 'Send password registration' box below to have " -"the user set their own password." +#: kotti/views/form.py:201 +#, python-format +msgid "Add ${type} to ${title}." msgstr "" -#: kotti/views/users.py:587 +#: kotti/views/form.py:205 #, python-format -msgid "My preferences - ${title}" +msgid "Add ${type}." msgstr "" -#: kotti/views/users.py:145 -msgid "Invalid value" +#: kotti/views/form.py:295 +#, python-format +msgid "Maximum file size: ${size}MB" msgstr "" -#: kotti/views/users.py:151 -msgid "A user with that name already exists." +#: kotti/views/login.py:66 +msgid "Username" msgstr "" -#: kotti/views/users.py:158 -msgid "A user with that email already exists." +#: kotti/views/login.py:80 +msgid "Register" msgstr "" -#: kotti/views/users.py:181 -#, python-format -msgid "No such group: ${group}" +#: kotti/views/login.py:87 +msgid "There was an error." msgstr "" -#: kotti/views/users.py:219 -msgid "Active" +#: kotti/views/login.py:106 +msgid "Congratulations! You are successfully registered. You should be receiving an email with a link to set your password. Doing so will activate your account." msgstr "" -#: kotti/views/users.py:220 -msgid "Untick this to deactivate the account." +#: kotti/views/login.py:121 +#, python-format +msgid "Register - ${title}" msgstr "" -#: kotti/views/users.py:226 -msgid "Global roles" +#: kotti/views/login.py:152 +#, python-format +msgid "Welcome, ${user}!" msgstr "" -#: kotti/views/users.py:230 -msgid "Groups" +#: kotti/views/login.py:177 +msgid "You should be receiving an email with a link to reset your password. Doing so will activate your account." msgstr "" -#: kotti/views/users.py:315 -msgid "Add User" +#: kotti/views/login.py:210 +msgid "Login failed." msgstr "" -#: kotti/views/users.py:339 -#, python-format -msgid "${title} was added." +#: kotti/views/login.py:220 +msgid "That username or email is not known by this system." msgstr "" -#: kotti/views/users.py:349 -msgid "Add Group" +#: kotti/views/login.py:242 +msgid "You have been logged out." msgstr "" -#: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:301 kotti/views/edit/actions.py:367 -#: kotti/views/edit/actions.py:413 kotti/views/edit/actions.py:409 -msgid "No changes were made." +#: kotti/views/login.py:280 +msgid "You have reset your password." msgstr "" -#: kotti/views/users.py:558 -msgid "No name was given." +#: kotti/views/login.py:299 +msgid "Set password" msgstr "" -#: kotti/views/users.py:98 -msgid "No users or groups were found." +#: kotti/views/login.py:327 +msgid "Your password reset token may have expired." msgstr "" -#: kotti/views/users.py:505 +#: kotti/views/login.py:334 #, python-format -msgid "Edit ${principal_type} ${title}" +msgid "Reset your password - ${title}." msgstr "" -#: kotti/views/users.py:533 -msgid "User was not found." +#: kotti/views/edit/default_views.py:86 +msgid "Default view" msgstr "" -#: kotti/views/users.py:324 -msgid "Send password registration link." +#: kotti/views/edit/default_views.py:107 +msgid "Default view has been reset to default." msgstr "" -#: kotti/views/users.py:543 -#, python-format -msgid "${principal_type} ${title} was deleted." +#: kotti/views/edit/default_views.py:114 +msgid "Default view has been set." msgstr "" -#: kotti/views/users.py:551 -#, python-format -msgid "Delete ${principal_type} ${title}" +#: kotti/views/edit/default_views.py:119 +msgid "Default view could not be set." msgstr "" -#: kotti/views/edit/actions.py:106 +#: kotti/views/edit/actions.py:102 #, python-format msgid "${title} was copied." msgstr "" -#: kotti/views/edit/actions.py:124 +#: kotti/views/edit/actions.py:120 #, python-format msgid "${title} was cut." msgstr "" -#: kotti/views/edit/actions.py:182 -#, python-format -msgid "${title} was moved." -msgstr "" - -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/edit/actions.py:155 #, python-format -msgid "${title} was deleted." +msgid "${title} was pasted." msgstr "" #: kotti/views/edit/actions.py:158 -#, python-format -msgid "${title} was pasted." +msgid "Could not paste node. It no longer exists." msgstr "" -#: kotti/views/edit/actions.py:161 -msgid "Could not paste node. It no longer exists." +#: kotti/views/edit/actions.py:180 +#, python-format +msgid "${title} was moved." msgstr "" -#: kotti/views/edit/actions.py:224 +#: kotti/views/edit/actions.py:222 #, python-format msgid "${title} is now visible in the navigation." msgstr "" -#: kotti/views/edit/actions.py:227 +#: kotti/views/edit/actions.py:225 #, python-format msgid "${title} is no longer visible in the navigation." msgstr "" -#: kotti/views/edit/actions.py:292 +#: kotti/views/edit/actions.py:274 kotti/views/edit/actions.py:303 +#, python-format +msgid "${title} was deleted." +msgstr "" + +#: kotti/views/edit/actions.py:300 msgid "Nothing was deleted." msgstr "" -#: kotti/views/edit/actions.py:328 kotti/views/edit/actions.py:355 +#: kotti/views/edit/actions.py:336 kotti/views/edit/actions.py:363 msgid "Name and title are required." msgstr "" -#: kotti/views/edit/actions.py:332 +#: kotti/views/edit/actions.py:340 msgid "Item was renamed." msgstr "" -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:461 +msgid "Change State" +msgstr "" + +#: kotti/views/edit/actions.py:462 msgid "Move up" msgstr "" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:463 msgid "Move down" msgstr "" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:464 msgid "Show" msgstr "" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:465 msgid "Hide" msgstr "" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:504 msgid "You have to select items to perform an action." msgstr "" -#: kotti/views/edit/actions.py:453 -msgid "Change State" -msgstr "" - -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:34 msgid "Description" msgstr "" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:40 msgid "Tags" msgstr "" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:49 msgid "Body" msgstr "" - -#: kotti/views/edit/default_views.py:99 -msgid "Default view has been reset to default." -msgstr "" - -#: kotti/views/edit/default_views.py:78 -msgid "Default view" -msgstr "" - -#: kotti/views/edit/default_views.py:106 -msgid "Default view has been set." -msgstr "" - -#: kotti/views/edit/default_views.py:111 -msgid "Default view could not be set." -msgstr "" diff --git a/kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo b/kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo index 7c1e7faa5..b1a68d8b7 100644 Binary files a/kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo and b/kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo differ diff --git a/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po b/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po index 1c962e1da..a8b81ad0c 100644 --- a/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po +++ b/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po @@ -1,60 +1,120 @@ -# French (France) translations for Kotti. -# Copyright (C) 2013 ORGANIZATION -# This file is distributed under the same license as the Kotti project. -# Fabien , 2013. -# #, fuzzy msgid "" msgstr "" -"Project-Id-Version: Kotti 0.9a3dev\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2014-11-21 21:56+0100\n" -"PO-Revision-Date: 2013-09-22 20:21+0100\n" -"Last-Translator: Fabien Castarède \n" -"Language-Team: French\n" -"Language: \n" +"POT-Creation-Date: 2018-01-17 13:51+0100\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" +"Project-Id-Version: \n" +"PO-Revision-Date: \n" +"Last-Translator: castaf \n" +"Language-Team: \n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1)\n" -"Generated-By: Babel 0.9.6\n" +"Language: fr_FR\n" +"X-Generator: Poedit 2.2\n" -#: kotti/populate.py:64 +#: kotti/security.py:173 +msgid "Viewer" +msgstr "Lecteur" + +#: kotti/security.py:174 +msgid "Editor" +msgstr "Editeur" + +#: kotti/security.py:175 +msgid "Owner" +msgstr "Propriétaire" + +#: kotti/security.py:176 +msgid "Admin" +msgstr "Administrateur" + +#: kotti/resources.py:541 kotti/views/edit/actions.py:453 +msgid "Copy" +msgstr "Copier" + +#: kotti/resources.py:542 kotti/views/edit/actions.py:454 +msgid "Cut" +msgstr "Couper" + +#: kotti/resources.py:543 kotti/views/edit/actions.py:450 +msgid "Paste" +msgstr "Coller" + +#: kotti/resources.py:544 kotti/templates/edit/rename.pt:31 +#: kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:42 kotti/views/edit/actions.py:455 +msgid "Rename" +msgstr "Renommer" + +#: kotti/resources.py:545 kotti/templates/edit/delete.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:49 +#: kotti/templates/site-setup/delete-user.pt:24 kotti/views/users.py:454 +#: kotti/views/edit/actions.py:457 +msgid "Delete" +msgstr "Supprimer" + +#: kotti/resources.py:556 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "Contenus" + +#: kotti/resources.py:557 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "Modifier" + +#: kotti/resources.py:558 +msgid "Share" +msgstr "Permissions" + +#: kotti/resources.py:559 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "Actions" + +#: kotti/resources.py:562 +msgid "Folder view" +msgstr "Contenus" + +#: kotti/resources.py:671 kotti/views/edit/content.py:61 +msgid "Document" +msgstr "Document" + +#: kotti/resources.py:808 kotti/views/edit/content.py:73 +#: kotti/views/edit/content.py:87 +msgid "File" +msgstr "Fichier" + +#: kotti/populate.py:67 msgid "Welcome to Kotti" -msgstr "" +msgstr "Bienvenue dans Kotti" -#: kotti/populate.py:65 +#: kotti/populate.py:68 msgid "Congratulations! You have successfully installed Kotti." -msgstr "" +msgstr "Félicitations! Vous avez installé Kotti avec succès." -#: kotti/populate.py:66 +#: kotti/populate.py:69 #, c-format msgid "" "\n" "

    Log in

    \n" "

    \n" -" You can log in to your " -"site\n" +" You can log in to your site\n" " and start changing its contents. If you haven't chosen a password for\n" " your admin account yet, it'll likely be qwerty.\n" "

    \n" "

    \n" " Once you're logged in, you'll see the grey editor bar below the top\n" -" navigation bar. It will allow you to switch between editing and viewing " -"the\n" +" navigation bar. It will allow you to switch between editing and viewing the\n" " current page as it will appear to your visitors.\n" "

    \n" "
    \n" "
    \n" "

    Configure

    \n" "

    \n" -" Find out how to configure your Kotti's title and many other " -"settings using a\n" -" simple text file in your file system.\n" +" Find out how to configure your Kotti's title and many other\n" +" settings using a simple text file in your file system.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -62,13 +122,12 @@ msgid "" "
    \n" "

    Add-ons

    \n" "

    \n" -" A number of add-ons allow you to extend the functionality of " -"your Kotti site.\n" +" A number of add-ons allow you to extend the functionality of your\n" +" Kotti site.\n" "

    \n" "

    \n" " \n" +" href=\"http://pypi.python.org/pypi?%3Aaction=search&term=kotti\">\n" " Kotti add-ons\n" " \n" "

    \n" @@ -76,37 +135,86 @@ msgid "" "
    \n" "

    Documentation

    \n" "

    \n" -" Wonder what more you can do with Kotti? What license it has? " -"Read the\n" -" manual for more information.\n" +" Wonder what more you can do with Kotti?\n" +" What license it has?\n" +" Read the manual for more information.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/\">\n" " Documentation\n" " \n" "

    \n" "
    \n" "
    \n" msgstr "" +"\n" +"

    Identifiez vous

    \n" +"

    \n" +" Vous pouvez vous identifier sur votre site internet\n" +" et commencer à créer du contenu. Si vous n'avez pas choisi un mot de passe pour\n" +" votre compte administrateur, celui-ci devrait être qwerty.\n" +"

    \n" +"

    \n" +" Lorsque que vous serez identifié, vous verrez la barre grise d'édition en dessous de la\n" +" barre de navigation. Celle-ci vous permettra de basculer entre l'édition et la visualisation, telle qu'\n" +" elle apparaitra aux visiteurs.\n" +"

    \n" +"
    \n" +"
    \n" +"

    Configuration

    \n" +"

    \n" +" Trouvez comment configurer le titre de votre site ainsi que d'autres paramètres en utilisant\n" +" un éditeur de texte.\n" +"

    \n" +"

    \n" +" \n" +" Configuration manuelle\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    Extensions

    \n" +"

    \n" +" Des extensions vous permettent d'étendre les fonctionnalités de votre site internet Kotti.\n" +"

    \n" +"

    \n" +" \n" +" Extensions de Kotti\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    Documentation

    \n" +"

    \n" +" Découvrez tout ce que vous pouvez faire avec Kotti? La License de Kotti?Parcourez le\n" +" manuel pour plus d'informations.\n" +"

    \n" +"

    \n" +" \n" +" Documentation\n" +" \n" +"

    \n" +"
    \n" +"
    \n" -#: kotti/populate.py:121 +#: kotti/populate.py:126 msgid "About" -msgstr "" +msgstr "A propos" -#: kotti/populate.py:122 -msgid "" -"Our company is the leading manufacturer of foo widgets used in a wide " -"variety of aviation and and industrial products." -msgstr "" +#: kotti/populate.py:127 +msgid "Our company is the leading manufacturer of foo widgets used in a wide variety of aviation and and industrial products." +msgstr "Notre société est spécialisée dans la conception de pièces foo utilisées dans une grande variétée de produits." -#: kotti/populate.py:123 +#: kotti/populate.py:128 msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -123,794 +231,897 @@ msgid "" "\n" "\n" "

    \n" -" Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" -" \n" +"Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +"\n" +"Copyright info.\n" +"Originally published in the\n" +" Extra EA-300\n" +"article.\n" +"

    \n" +msgstr "" +"\n" +"

    \n" +" \"Cinq\n" +"

    \n" +"\n" +"

    \n" +" Nos bureaux:\n" +"

    \n" +"\n" +"
    \n" +" Foo World
    \n" +" 123 Rue inconnue, Boite 777
    \n" +" Omak, WA 98841 USA
    \n" +" +1-509-555-0100
    \n" +" widgets@foowrld.example.com\n" +"
    \n" +"\n" +"

    \n" +" Crédit de la photo: \"Northern Lights Formation\" by FlugKerl2.\n" +" \n" " Copyright info.\n" -" Originally published in the\n" +" Originalement publié sur\n" " Extra EA-300\n" " article.\n" "

    \n" -msgstr "" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 -msgid "Document" -msgstr "Document" +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "Bienvenue, ${user} ! Vous êtes connecté." -#: kotti/resources.py:649 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 -msgid "File" -msgstr "Fichier" +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "Bienvenue, vous n'êtes pas connecté." -#: kotti/resources.py:726 kotti/views/edit/content.py:144 -msgid "Image" -msgstr "Image" +#: kotti/templates/editor-bar.pt:19 +msgid "View" +msgstr "Voir" -#: kotti/resources.py:500 -msgid "Folder view" -msgstr "Contenus" +#: kotti/templates/editor-bar.pt:42 +msgid "Navigate" +msgstr "Parcourir" -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 -msgid "Contents" -msgstr "Contenus" +#: kotti/templates/editor-bar.pt:53 +msgid "Preferences" +msgstr "Préférences" -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" -msgstr "Modifier" +#: kotti/templates/editor-bar.pt:58 +msgid "Site Setup" +msgstr "Paramètres du Site" -#: kotti/resources.py:489 -msgid "Share" -msgstr "Permissions" +#: kotti/templates/editor-bar.pt:69 +msgid "Logout" +msgstr "Se déconnecter" -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" -msgstr "Actions" +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "Définir la vue par défaut" -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 -msgid "Copy" -msgstr "Copier" +#: kotti/templates/login.pt:16 +msgid "Login" +msgstr "Identifiant" -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 -msgid "Cut" -msgstr "Couper" +#: kotti/templates/login.pt:21 kotti/templates/login.pt:59 +msgid "Username or email" +msgstr "Identifiant ou e-mail" -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 -msgid "Paste" -msgstr "Coller" +#: kotti/templates/login.pt:26 kotti/views/users.py:220 +#: kotti/views/login.py:255 +msgid "Password" +msgstr "Mot de passe" -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 -#: kotti/templates/edit/rename-nodes.pt:55 kotti/templates/edit/rename.pt:42 -#: kotti/views/edit/actions.py:447 -msgid "Rename" -msgstr "Renommer" +#: kotti/templates/login.pt:34 +msgid "Log in" +msgstr "Se connecter" -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 -#: kotti/templates/edit/delete-nodes.pt:90 kotti/templates/edit/delete.pt:28 -#: kotti/templates/site-setup/delete-user.pt:36 kotti/views/users.py:443 -#: kotti/views/edit/actions.py:449 -msgid "Delete" -msgstr "Supprimer" +#: kotti/templates/login.pt:45 +msgid "Forgot your password?" +msgstr "Mot de passe oublié ?" -#: kotti/security.py:165 -msgid "Viewer" -msgstr "Lecteur" +#. Used in sentence: "Fill out your username or email and click +#. ${reset_password} below to receive an email with a link to reset your +#. password." +#. +#: kotti/templates/login.pt:52 kotti/templates/login.pt:67 +msgid "Reset password" +msgstr "Réinitialiser le mot de passe" -#: kotti/security.py:166 -msgid "Editor" -msgstr "Editeur" +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:50 +msgid "Fill out your username or email and click ${reset_password} below to receive an email with a link to reset your password." +msgstr "Indiquez votre identifiant ou votre adresse e-mail ci-dessous et cliquez sur ${reset_password} ci-dessous afin de recevoir un mail contenant le lien de réinitialisation de votre mot de passe." -#: kotti/security.py:167 -msgid "Owner" -msgstr "Propriétaire" +#: kotti/templates/login.pt:78 +msgid "Not registered yet?" +msgstr "Pas de compte ?" -#: kotti/security.py:168 -msgid "Admin" -msgstr "Administrateur" +#: kotti/templates/login.pt:85 +msgid "Register for an account on this site." +msgstr "Enregistrez-vous sur ce site." -#: kotti/templates/add-dropdown.pt:5 +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "Interdit" + +#: kotti/templates/add-dropdown.pt:3 msgid "Add" msgstr "Ajouter" -#: kotti/templates/add-dropdown.pt:27 +#: kotti/templates/add-dropdown.pt:13 msgid "Upload Content" msgstr "Téléverser des contenus" -#: kotti/templates/default-view-selector.pt:5 -msgid "Set default view" -msgstr "Définir la vue par défaut" - -#: kotti/templates/editor-bar.pt:35 -msgid "View" -msgstr "Voir" - -#: kotti/templates/editor-bar.pt:76 -msgid "Navigate" -msgstr "Parcourir" - -#: kotti/templates/editor-bar.pt:98 -msgid "Preferences" -msgstr "Préférences" - -#: kotti/templates/editor-bar.pt:109 -msgid "Site Setup" -msgstr "Paramètres du Site" - -#: kotti/templates/editor-bar.pt:131 -msgid "Logout" -msgstr "Se déconnecter" +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "Rendre ${state}" -#: kotti/templates/email-reset-password.pt:3 +#: kotti/templates/email-reset-password.pt:2 msgid "Reset your password for ${site_title}." msgstr "Réinitialiser votre mot de passe pour ${site_title}." -#: kotti/templates/email-reset-password.pt:7 -#: kotti/templates/email-set-password.pt:6 +#: kotti/templates/email-reset-password.pt:4 +#: kotti/templates/email-set-password.pt:4 msgid "Hello, ${user_title}!" msgstr "Bonjour, ${user_title} !" -#: kotti/templates/email-reset-password.pt:10 +#: kotti/templates/email-reset-password.pt:5 msgid "Click this link to reset your password at ${site_title}: ${url}" -msgstr "" -"Cliquez sur ce lien afin de réinitialiser votre mot de passe sur ${title} : " -"${url}" +msgstr "Cliquez sur ce lien afin de réinitialiser votre mot de passe sur ${site_title} : ${url}" -#: kotti/templates/email-set-password.pt:3 +#: kotti/templates/email-set-password.pt:2 msgid "Your registration for ${site_title}" msgstr "Votre inscription sur ${site_title}" -#: kotti/templates/email-set-password.pt:9 +#: kotti/templates/email-set-password.pt:5 msgid "You are joining ${site_title}." msgstr "Vous vous joignez à ${site_title}." -#: kotti/templates/email-set-password.pt:12 +#: kotti/templates/email-set-password.pt:6 msgid "Click here to set your password and log in: ${url}" -msgstr "" -"Cliquez ici afin de définir votre mot de passe et vous connecter : ${url}" +msgstr "Cliquez ici afin de définir votre mot de passe et vous connecter : ${url}" -#: kotti/templates/forbidden.pt:7 -msgid "Forbidden" -msgstr "Interdit" +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" +msgstr "Permissions pour ${title}" -#: kotti/templates/login.pt:16 -msgid "Login" -msgstr "Identifiant" +#: kotti/templates/edit/share.pt:14 kotti/templates/edit/share.pt:20 +#: kotti/templates/edit/share.pt:27 kotti/templates/site-setup/users.pt:48 +msgid "Search users and groups" +msgstr "Rechercher des utilisateurs ou des groupes" -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 -msgid "Username or email" -msgstr "Identifiant ou e-mail" +#: kotti/templates/edit/share.pt:34 kotti/templates/site-setup/users.pt:56 +msgid "Search" +msgstr "Rechercher" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 -#: kotti/views/users.py:212 -msgid "Password" -msgstr "Mot de passe" +#: kotti/templates/edit/share.pt:44 +msgid "Assign local roles" +msgstr "Assigner des rôles locaux" -#: kotti/templates/login.pt:47 -msgid "Log in" -msgstr "Se connecter" +#: kotti/templates/edit/share.pt:49 kotti/templates/edit/change-state.pt:19 +#: kotti/templates/edit/delete-nodes.pt:22 kotti/templates/edit/upload.pt:18 +#: kotti/templates/edit/upload.pt:108 kotti/templates/edit/contents.pt:35 +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:69 +msgid "Type" +msgstr "Type" -#: kotti/templates/login.pt:65 -msgid "Forgot your password?" -msgstr "Mot de passe oublié ?" +#: kotti/templates/edit/share.pt:50 kotti/templates/site-setup/users.pt:70 +#: kotti/views/users.py:215 +msgid "Name" +msgstr "Nom" -#. Used in sentence: "Fill out your username or email and click -#. ${reset_password} below to receive an email with a link to reset your -#. password." -#. -#: kotti/templates/login.pt:77 kotti/templates/login.pt:100 -msgid "Reset password" -msgstr "Réinitialiser le mot de passe" +#: kotti/templates/edit/share.pt:61 kotti/templates/site-setup/users.pt:81 +#: kotti/views/users.py:321 kotti/views/users.py:515 kotti/views/users.py:550 +msgid "User" +msgstr "Utilisateur" -#. Canonical text for ${reset_password} is: "Reset password" -#: kotti/templates/login.pt:74 -#, fuzzy -msgid "" -"Fill out your username or email and click ${reset_password} below to receive " -"an email with a link to reset your password." -msgstr "" -"Indiquez votre identifiant ou votre adresse e-mail ci-dessus et cliquez sur " -"${reset_password} ci-dessous afin de recevoir un mail contenant le lien de " -"réinitialisation de votre mot de passe." +#: kotti/templates/edit/share.pt:62 kotti/templates/site-setup/users.pt:82 +#: kotti/views/users.py:196 kotti/views/users.py:356 kotti/views/users.py:515 +#: kotti/views/users.py:550 +msgid "Group" +msgstr "Groupe" -#: kotti/templates/login.pt:118 -msgid "Not registered yet?" -msgstr "" +#: kotti/templates/edit/share.pt:65 kotti/templates/site-setup/users.pt:85 +msgid "Gravatar" +msgstr "Gravatar" -#: kotti/templates/login.pt:127 -#, fuzzy -msgid "Register for an account on this site." -msgstr "Pas encore inscrit ? ${register} sur ce site." +#: kotti/templates/edit/share.pt:70 kotti/templates/site-setup/users.pt:91 +msgid "Assign role" +msgstr "Assigner des rôles locaux" -#: kotti/templates/workflow-dropdown.pt:19 -msgid "Make ${state}" -msgstr "Rendre ${state}" +#: kotti/templates/edit/share.pt:85 kotti/templates/edit/change-state.pt:72 +#: kotti/templates/site-setup/users.pt:107 +msgid "Apply changes" +msgstr "Appliquer les modifications" -#: kotti/templates/edit/breadcrumbs.pt:5 +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "Parcourir le Site" + +#: kotti/templates/edit/breadcrumbs.pt:8 msgid "You are here:" msgstr "Vous êtes ici :" -#: kotti/templates/edit/change-state.pt:7 +#: kotti/templates/edit/delete.pt:9 kotti/templates/site-setup/delete-user.pt:9 +msgid "Delete ${title}" +msgstr "Supprimer ${title}" + +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" +msgstr "Etes-vous sûr de vouloir supprimer ${title} ?" + +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:74 +#: kotti/templates/edit/delete-nodes.pt:44 +#: kotti/templates/edit/rename-nodes.pt:44 kotti/views/users.py:324 +#: kotti/views/users.py:359 kotti/views/users.py:453 kotti/views/form.py:81 +msgid "Cancel" +msgstr "Annuler" + +#: kotti/templates/edit/change-state.pt:8 msgid "Change workflow state" msgstr "Modifier le statut de workflow" -#: kotti/templates/edit/change-state.pt:14 +#: kotti/templates/edit/change-state.pt:12 msgid "An item's workflow state determines who can see, edit and/or manage it." -msgstr "" -"Le statut de workflow d'un élément détermine qui peut le voir, l'éditer et/" -"ou le gérer." +msgstr "Le statut de workflow d'un élément détermine qui peut le voir, l'éditer et/ou le gérer." -#: kotti/templates/edit/change-state.pt:26 kotti/templates/edit/contents.pt:57 -#: kotti/templates/edit/delete-nodes.pt:33 kotti/templates/view/folder.pt:28 -#: kotti/views/users.py:196 kotti/views/edit/content.py:31 +#: kotti/templates/edit/change-state.pt:18 +#: kotti/templates/edit/delete-nodes.pt:21 kotti/templates/edit/contents.pt:34 +#: kotti/templates/view/folder.pt:20 kotti/views/users.py:204 +#: kotti/views/edit/content.py:28 msgid "Title" msgstr "Titre" -#: kotti/templates/edit/change-state.pt:29 kotti/templates/edit/contents.pt:60 -#: kotti/templates/edit/delete-nodes.pt:36 kotti/templates/edit/share.pt:70 -#: kotti/templates/edit/upload.pt:23 kotti/templates/edit/upload.pt:167 -#: kotti/templates/site-setup/users.pt:105 kotti/templates/view/folder.pt:31 -msgid "Type" -msgstr "Type" - -#: kotti/templates/edit/change-state.pt:32 kotti/templates/edit/contents.pt:63 -#: kotti/templates/edit/delete-nodes.pt:39 +#: kotti/templates/edit/change-state.pt:20 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:36 msgid "State" msgstr "Statut" -#: kotti/templates/edit/change-state.pt:35 kotti/templates/edit/contents.pt:69 -#: kotti/templates/edit/delete-nodes.pt:42 kotti/templates/view/folder.pt:34 +#: kotti/templates/edit/change-state.pt:21 +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:38 +#: kotti/templates/view/folder.pt:22 msgid "Creation Date" msgstr "Date de création" -#: kotti/templates/edit/change-state.pt:38 kotti/templates/edit/contents.pt:72 -#: kotti/templates/edit/delete-nodes.pt:45 kotti/templates/view/folder.pt:37 +#: kotti/templates/edit/change-state.pt:22 +#: kotti/templates/edit/delete-nodes.pt:25 kotti/templates/edit/contents.pt:39 +#: kotti/templates/view/folder.pt:23 msgid "Modification Date" msgstr "Date de modification" -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 +#: kotti/templates/edit/change-state.pt:28 +#: kotti/templates/edit/delete-nodes.pt:31 kotti/templates/edit/contents.pt:45 +msgid "Select item" +msgstr "" + +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:48 msgid "Include children" msgstr "Inclure les contenus enfants" -#: kotti/templates/edit/change-state.pt:82 -msgid "" -"If checked, this will attempt to modify the status of all children in any " -"selected documents and their subdocuments." -msgstr "" -"Si coché, cela modifiera le statut de tous les contenus enfants dans les " -"documents sélectionnés et leurs documents enfants." +#: kotti/templates/edit/change-state.pt:41 +msgid "If checked, this will attempt to modify the status of all children in any selected documents and their subdocuments." +msgstr "Si coché, cela modifiera le statut de tous les contenus enfants dans les documents sélectionnés et leurs documents enfants." -#: kotti/templates/edit/change-state.pt:100 +#: kotti/templates/edit/change-state.pt:54 msgid "Change state to" msgstr "Modifier le statut à" -#: kotti/templates/edit/change-state.pt:103 +#: kotti/templates/edit/change-state.pt:55 msgid "Select the new state where all chosen items should be set." msgstr "Sélectionner le nouveau statut à appliquer aux éléments choisis." -#: kotti/templates/edit/change-state.pt:111 +#: kotti/templates/edit/change-state.pt:60 msgid "No change" msgstr "Aucune modification" -#: kotti/templates/edit/change-state.pt:127 kotti/templates/edit/share.pt:121 -#: kotti/templates/site-setup/users.pt:165 -msgid "Apply changes" -msgstr "Appliquer les modifications" - -#: kotti/templates/edit/change-state.pt:130 -#: kotti/templates/edit/delete-nodes.pt:93 -#: kotti/templates/edit/rename-nodes.pt:58 kotti/views/form.py:78 -#: kotti/views/users.py:316 kotti/views/users.py:350 kotti/views/users.py:442 -msgid "Cancel" -msgstr "Annuler" - -#: kotti/templates/edit/contents.pt:43 -msgid "No content items are contained here." -msgstr "Aucun contenu ici." - -#: kotti/templates/edit/contents.pt:66 -msgid "Visibility" -msgstr "Visibilité" - -#: kotti/templates/edit/contents.pt:125 -msgid "Visible" -msgstr "Visible" - -#: kotti/templates/edit/contents.pt:134 -msgid "Hidden" -msgstr "Caché" - -#: kotti/templates/edit/delete-nodes.pt:17 +#: kotti/templates/edit/delete-nodes.pt:13 msgid "Are you sure you want to delete the following items?" msgstr "Etes-vous sûr de vouloir supprimer les éléments suivants ?" -#: kotti/templates/edit/delete.pt:8 -#: kotti/templates/site-setup/delete-user.pt:8 -msgid "Delete ${title}" -msgstr "Supprimer ${title}" - -#: kotti/templates/edit/delete.pt:20 -msgid "Are you sure you want to delete ${title}?" -msgstr "Etes-vous sûr de vouloir supprimer ${title} ?" - -#: kotti/templates/edit/nav-tree-view.pt:7 -msgid "Navigate Site" -msgstr "Parcourir le Site" +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "Renommer ${title}" -#: kotti/templates/edit/rename-nodes.pt:16 kotti/templates/edit/rename.pt:14 -msgid "" -"Each content item has a name, which is used to create the url, and a title. " -"Read the user manual about proper formatting of name and title if needed. " -"You can change both below." -msgstr "" -"Chaque contenu a un nom, qui est utilisé pour créer une url, et un titre. " -"Consultez, si besoin, le manuel à propos du format des nom et titre. Vous " -"pouvez modifier les deux." +#: kotti/templates/edit/rename.pt:11 kotti/templates/edit/rename-nodes.pt:13 +msgid "Each content item has a name, which is used to create the url, and a title. Read the user manual about proper formatting of name and title if needed. You can change both below." +msgstr "Chaque contenu a un nom, qui est utilisé pour créer une url, et un titre. Consultez, si besoin, le manuel à propos du format des nom et titre. Vous pouvez modifier les deux." -#: kotti/templates/edit/rename-nodes.pt:35 kotti/templates/edit/rename.pt:26 +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +#: kotti/templates/edit/rename-nodes.pt:28 msgid "New name" msgstr "Nouveau nom" -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:34 +#: kotti/templates/edit/rename-nodes.pt:36 msgid "New title" msgstr "Nouveau titre" -#: kotti/templates/edit/rename.pt:7 -msgid "Rename ${title}" -msgstr "Renommer ${title}" +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" +msgstr "Téléverser des contenus à partir de fichiers locaux" -#: kotti/templates/edit/share.pt:7 -msgid "Share ${title}" -msgstr "Permissions pour ${title}" +#: kotti/templates/edit/upload.pt:17 kotti/templates/edit/upload.pt:107 +msgid "Filename" +msgstr "Nom du fichier" -#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:30 -#: kotti/templates/edit/share.pt:35 kotti/templates/site-setup/users.pt:68 -msgid "Search users and groups" -msgstr "Rechercher des utilisateurs ou des groupes" +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:109 +msgid "Size" +msgstr "Poids" -#: kotti/templates/edit/share.pt:43 kotti/templates/site-setup/users.pt:81 -msgid "Search" -msgstr "Rechercher" +#: kotti/templates/edit/upload.pt:20 +msgid "Status" +msgstr "Statut" -#: kotti/templates/edit/share.pt:60 -msgid "Assign local roles" -msgstr "Assigner des rôles locaux" +#: kotti/templates/edit/upload.pt:21 +msgid "Progress" +msgstr "Progression" -#: kotti/templates/edit/share.pt:73 kotti/templates/site-setup/users.pt:108 -#: kotti/views/users.py:207 -msgid "Name" -msgstr "Nom" +#: kotti/templates/edit/upload.pt:84 +msgid "Select file(s) to upload..." +msgstr "Selectionner des fichiers à téléverser..." -#: kotti/templates/edit/share.pt:91 kotti/templates/site-setup/users.pt:126 -#: kotti/views/users.py:313 kotti/views/users.py:501 kotti/views/users.py:536 -msgid "User" -msgstr "Utilisateur" +#: kotti/templates/edit/upload.pt:92 +msgid "Upload ${number} files." +msgstr "Téléverser ${number} fichiers." -#: kotti/templates/edit/share.pt:94 kotti/templates/site-setup/users.pt:129 -#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 -#: kotti/views/users.py:536 -msgid "Group" -msgstr "Groupe" +#: kotti/templates/edit/upload.pt:97 +msgid "Dismiss all errors" +msgstr "Ignorer toutes les erreurs" -#: kotti/templates/edit/share.pt:101 kotti/templates/site-setup/users.pt:136 -msgid "Gravatar" -msgstr "Gravatar" +#: kotti/templates/edit/contents.pt:26 +msgid "No content items are contained here." +msgstr "Aucun contenu ici." -#: kotti/templates/edit/upload.pt:9 -msgid "Upload content from local file(s)" +#: kotti/templates/edit/contents.pt:33 +msgid "Select / deselect all" msgstr "" -#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:164 -#, fuzzy -msgid "Filename" -msgstr "Renommer" +#: kotti/templates/edit/contents.pt:37 +msgid "Visibility" +msgstr "Visibilité" -#: kotti/templates/edit/upload.pt:26 kotti/templates/edit/upload.pt:170 -msgid "Size" -msgstr "" +#: kotti/templates/edit/contents.pt:69 +msgid "Visible" +msgstr "Visible" -#: kotti/templates/edit/upload.pt:29 -msgid "Status" -msgstr "" +#: kotti/templates/edit/contents.pt:73 +msgid "Hidden" +msgstr "Caché" -#: kotti/templates/edit/upload.pt:32 -msgid "Progress" +#: kotti/templates/http-errors/notfound.pt:26 +msgid "Not Found" msgstr "" -#: kotti/templates/edit/upload.pt:124 -msgid "Select file(s) to upload..." +#: kotti/templates/http-errors/notfound.pt:27 +msgid "The resource could not be found" msgstr "" -#: kotti/templates/edit/upload.pt:134 -msgid "Upload ${number} files." -msgstr "" +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "Résultats de la recherche" -#: kotti/templates/edit/upload.pt:144 -msgid "Dismiss all errors" -msgstr "" +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "Rechercher..." + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "Mots-clés associés :" -#: kotti/templates/site-setup/delete-user.pt:20 +#: kotti/templates/site-setup/delete-user.pt:15 msgid "Are you sure you want to delete ${type} ${title}?" msgstr "Etes-vous sûr de vouloir supprimer ${type} ${title} ?" -#: kotti/templates/site-setup/user.pt:13 +#: kotti/templates/site-setup/user.pt:12 msgid "Back to User Management" msgstr "Retour à la gestion des Utilisateurs" -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 +#: kotti/templates/site-setup/user.pt:21 kotti/views/form.py:159 #, python-format msgid "Edit ${title}" msgstr "Modifier ${title}" -#: kotti/templates/site-setup/users.pt:15 -#, fuzzy +#: kotti/templates/site-setup/users.pt:12 msgid "Search user(s) / group(s)" msgstr "Rechercher des utilisateurs ou des groupes" -#: kotti/templates/site-setup/users.pt:24 +#: kotti/templates/site-setup/users.pt:17 msgid "Add user" msgstr "Ajouter un utilisateur" -#: kotti/templates/site-setup/users.pt:33 +#: kotti/templates/site-setup/users.pt:22 msgid "Add group" msgstr "Ajouter un groupe" -#: kotti/templates/site-setup/users.pt:52 -#, fuzzy +#: kotti/templates/site-setup/users.pt:34 msgid "Find users or groups" msgstr "Rechercher des utilisateurs ou des groupes" -#: kotti/templates/site-setup/users.pt:63 +#: kotti/templates/site-setup/users.pt:40 msgid "User- / groupname" -msgstr "" +msgstr "Utilisateur / groupe" -#: kotti/templates/site-setup/users.pt:70 -#, fuzzy +#: kotti/templates/site-setup/users.pt:50 msgid "Blank search text finds all." -msgstr "" -"Rechercher et modifier des utilisateurs (Laissez le champ vide pour les " -"afficher tous)." +msgstr "Rechercher et modifier des utilisateurs (Laissez le champ vide pour les afficher tous)." -#: kotti/templates/site-setup/users.pt:96 -#, fuzzy +#: kotti/templates/site-setup/users.pt:65 msgid "Assign global roles" msgstr "Assigner des rôles locaux" -#: kotti/templates/site-setup/users.pt:180 -#, fuzzy +#: kotti/templates/site-setup/users.pt:117 msgid "Add new user" msgstr "Ajouter un utilisateur" -#: kotti/templates/site-setup/users.pt:190 -#, fuzzy +#: kotti/templates/site-setup/users.pt:123 msgid "Add new group" msgstr "Ajouter un groupe" -#: kotti/templates/view/search-results.pt:8 -msgid "Search Results" -msgstr "Résultats de la recherche" - -#: kotti/templates/view/search.pt:5 -msgid "Search..." -msgstr "Rechercher..." - -#: kotti/templates/view/tags.pt:5 -msgid "Tagged with:" -msgstr "Mots-clés associés :" - -#: kotti/tests/testing_view.pt:7 -msgid "Welcome, ${user}! You are logged in." -msgstr "Bienvenue, ${user} ! Vous êtes connecté." - -#: kotti/tests/testing_view.pt:14 -msgid "Welcome, you are not logged in." -msgstr "Bienvenue, vous n'êtes pas connecté." - -#: kotti/views/form.py:79 kotti/views/users.py:69 -#: kotti/views/edit/actions.py:363 kotti/views/edit/actions.py:407 +#: kotti/views/users.py:70 kotti/views/form.py:82 +#: kotti/views/edit/actions.py:371 kotti/views/edit/actions.py:415 msgid "Your changes have been saved." msgstr "Vos modifications ont été enregistrées." -#: kotti/views/form.py:174 -msgid "Item was added." -msgstr "L'élément a été ajouté." - -#: kotti/views/form.py:264 -#, python-format -msgid "Maximum file size: ${size}MB" -msgstr "Taille maximum d'un fichier : ${size} MB" - -#: kotti/views/form.py:77 kotti/views/users.py:441 -msgid "Save" -msgstr "Enregistrer" - -#: kotti/views/form.py:198 -#, python-format -msgid "Add ${type} to ${title}." -msgstr "Ajouter un(e) ${type} dans ${title}." - -#: kotti/views/form.py:202 -#, python-format -msgid "Add ${type}." -msgstr "Ajouter ${type}." - -#: kotti/views/login.py:238 -msgid "You have reset your password." -msgstr "Vous avez réinitialisé votre mot de passe." - -#: kotti/views/login.py:200 -msgid "You have been logged out." -msgstr "Vous avez été déconnecté." - -#: kotti/views/login.py:66 kotti/views/users.py:270 -msgid "Full name" -msgstr "Nom complet" - -#: kotti/views/login.py:69 -msgid "Username" -msgstr "Identifiant" - -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 -msgid "Email" -msgstr "E-mail" - -#: kotti/views/login.py:109 -msgid "" -"Congratulations! You are successfully registered. You should be receiving an " -"email with a link to set your password. Doing so will activate your account." -msgstr "" -"Félicitations ! Vous venez de vous enregistrer avec succès. Vous devriez " -"recevoir un e-mail contenant le lien permettant d'initialiser votre mot de " -"passe, afin d'activer votre compte." - -#: kotti/views/login.py:124 -#, python-format -msgid "Register - ${title}" -msgstr "S'inscrire - ${title}" - -#: kotti/views/login.py:164 -msgid "Login failed." -msgstr "Connexion échouée." - -#: kotti/views/login.py:286 -#, python-format -msgid "Reset your password - ${title}." -msgstr "Réinitialiser votre mot de passe - ${title}" - -#: kotti/views/login.py:160 -#, python-format -msgid "Welcome, ${user}!" -msgstr "Bienvenue, ${user} !" - -#: kotti/views/login.py:173 -msgid "" -"You should be receiving an email with a link to reset your password. Doing " -"so will activate your account." -msgstr "" -"Vous devriez recevoir un e-mail contenant un lien d'initialisation de votre " -"mot de passe, afin d'activer votre compte." - -#: kotti/views/login.py:178 -msgid "That username or email is not known by this system." -msgstr "Cet identifiant ou cette adresse e-mail n'est pas connue du système" - -#: kotti/views/login.py:83 -msgid "Register" -msgstr "S'inscrire" - -#: kotti/views/login.py:90 kotti/views/login.py:257 -msgid "There was an error." -msgstr "Il y a une erreur." - -#: kotti/views/login.py:250 -msgid "Submit" -msgstr "Soumettre" - -#: kotti/views/login.py:279 -msgid "Your password reset token may have expired." -msgstr "Votre jeton de réinitialisation de votre mot de passe est expiré." - -#: kotti/views/site_setup.py:6 kotti/views/users.py:378 -msgid "User Management" -msgstr "Gestion des utilisateurs" - -#: kotti/views/users.py:267 -msgid "" -"Leave this empty and tick the 'Send password registration' box below to have " -"the user set their own password." -msgstr "" -"Laisser vide et cochez \"Envoyer un mot de passe d'inscription\" ci-dessous " -"pour que l'utilisateur définisse son propre mot de passe." +#: kotti/views/users.py:72 kotti/views/users.py:477 +#: kotti/views/edit/actions.py:309 kotti/views/edit/actions.py:375 +#: kotti/views/edit/actions.py:417 kotti/views/edit/actions.py:421 +msgid "No changes were made." +msgstr "Aucune modification a été effectuée." -#: kotti/views/users.py:587 -#, python-format -msgid "My preferences - ${title}" -msgstr "Mes préférences - ${title}" +#: kotti/views/users.py:105 +msgid "No users or groups were found." +msgstr "Aucun utilisateur ou groupe a été trouvé." -#: kotti/views/users.py:145 +#: kotti/views/users.py:153 msgid "Invalid value" msgstr "Valeur invalide" -#: kotti/views/users.py:151 +#: kotti/views/users.py:159 msgid "A user with that name already exists." msgstr "Un utilisateur avec ce nom existe déjà." -#: kotti/views/users.py:158 +#: kotti/views/users.py:166 msgid "A user with that email already exists." msgstr "Un utilisateur avec cette adresse e-mail existe déjà." -#: kotti/views/users.py:181 +#: kotti/views/users.py:189 #, python-format msgid "No such group: ${group}" msgstr "Aucun groupe : ${group}" -#: kotti/views/users.py:219 +#: kotti/views/users.py:207 kotti/views/login.py:71 kotti/views/login.py:267 +msgid "Email" +msgstr "E-mail" + +#: kotti/views/users.py:227 msgid "Active" msgstr "Actif" -#: kotti/views/users.py:220 +#: kotti/views/users.py:228 msgid "Untick this to deactivate the account." msgstr "Décocher cette option pour désactiver le compte." -#: kotti/views/users.py:226 +#: kotti/views/users.py:234 msgid "Global roles" msgstr "Rôle global" -#: kotti/views/users.py:230 +#: kotti/views/users.py:238 msgid "Groups" msgstr "Groupes" -#: kotti/views/users.py:315 +#: kotti/views/users.py:275 +msgid "Leave this empty and tick the 'Send password registration' box below to have the user set their own password." +msgstr "Laisser vide et cochez \"Envoyer un mot de passe d'inscription\" ci-dessous pour que l'utilisateur définisse son propre mot de passe." + +#: kotti/views/users.py:278 kotti/views/login.py:63 +msgid "Full name" +msgstr "Nom complet" + +#: kotti/views/users.py:323 msgid "Add User" msgstr "Ajouter un Utilisateur" -#: kotti/views/users.py:339 +#: kotti/views/users.py:333 +msgid "Send password registration link." +msgstr "Envoyer un lien d'initialisation de mot de passe" + +#: kotti/views/users.py:348 #, python-format msgid "${title} was added." msgstr "${title} a été ajouté." -#: kotti/views/users.py:349 +#: kotti/views/users.py:358 msgid "Add Group" msgstr "Ajouter un Groupe" -#: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:301 kotti/views/edit/actions.py:367 -#: kotti/views/edit/actions.py:413 kotti/views/edit/actions.py:409 -msgid "No changes were made." -msgstr "Aucune modification a été effectuée." - -#: kotti/views/users.py:558 -msgid "No name was given." -msgstr "Aucun nom a été indiqué." +#: kotti/views/users.py:388 kotti/views/site_setup.py:5 +msgid "User Management" +msgstr "Gestion des utilisateurs" -#: kotti/views/users.py:98 -msgid "No users or groups were found." -msgstr "Aucun utilisateur ou groupe a été trouvé." +#: kotti/views/users.py:452 kotti/views/form.py:80 +msgid "Save" +msgstr "Enregistrer" -#: kotti/views/users.py:505 +#: kotti/views/users.py:519 #, python-format msgid "Edit ${principal_type} ${title}" msgstr "Modifier ${principal_type} ${title}" -#: kotti/views/users.py:533 +#: kotti/views/users.py:547 msgid "User was not found." msgstr "Utilisateur non trouvé." -#: kotti/views/users.py:324 -msgid "Send password registration link." -msgstr "Envoyer un lien d'initialisation de mot de passe" - -#: kotti/views/users.py:543 +#: kotti/views/users.py:557 #, python-format msgid "${principal_type} ${title} was deleted." msgstr "${principal_type} ${title} a été supprimé." -#: kotti/views/users.py:551 +#: kotti/views/users.py:565 #, python-format msgid "Delete ${principal_type} ${title}" msgstr "Supprimer ${principal_type} ${title}" -#: kotti/views/edit/actions.py:106 +#: kotti/views/users.py:572 +msgid "No name was given." +msgstr "Aucun nom a été indiqué." + +#: kotti/views/users.py:601 #, python-format -msgid "${title} was copied." -msgstr "${title} a été copié." +msgid "My preferences - ${title}" +msgstr "Mes préférences - ${title}" + +#: kotti/views/form.py:177 +msgid "Item was added." +msgstr "L'élément a été ajouté." -#: kotti/views/edit/actions.py:124 +#: kotti/views/form.py:201 #, python-format -msgid "${title} was cut." -msgstr "${title} a été coupé." +msgid "Add ${type} to ${title}." +msgstr "Ajouter un(e) ${type} dans ${title}." -#: kotti/views/edit/actions.py:182 +#: kotti/views/form.py:205 #, python-format -msgid "${title} was moved." -msgstr "${title} a été déplacé." +msgid "Add ${type}." +msgstr "Ajouter ${type}." -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/form.py:295 #, python-format -msgid "${title} was deleted." -msgstr "${title} a été supprimé." +msgid "Maximum file size: ${size}MB" +msgstr "Taille maximum d'un fichier : ${size} MB" -#: kotti/views/edit/actions.py:158 +#: kotti/views/login.py:66 +msgid "Username" +msgstr "Identifiant" + +#: kotti/views/login.py:80 +msgid "Register" +msgstr "S'inscrire" + +#: kotti/views/login.py:87 +msgid "There was an error." +msgstr "Il y a une erreur." + +#: kotti/views/login.py:106 +msgid "Congratulations! You are successfully registered. You should be receiving an email with a link to set your password. Doing so will activate your account." +msgstr "Félicitations ! Vous venez de vous enregistrer avec succès. Vous devriez recevoir un e-mail contenant le lien permettant d'initialiser votre mot de passe, afin d'activer votre compte." + +#: kotti/views/login.py:121 +#, python-format +msgid "Register - ${title}" +msgstr "S'inscrire - ${title}" + +#: kotti/views/login.py:152 +#, python-format +msgid "Welcome, ${user}!" +msgstr "Bienvenue, ${user} !" + +#: kotti/views/login.py:177 +msgid "You should be receiving an email with a link to reset your password. Doing so will activate your account." +msgstr "Vous devriez recevoir un e-mail contenant un lien d'initialisation de votre mot de passe, afin d'activer votre compte." + +#: kotti/views/login.py:210 +msgid "Login failed." +msgstr "La connexion à échouée." + +#: kotti/views/login.py:220 +msgid "That username or email is not known by this system." +msgstr "Cet identifiant ou cette adresse e-mail n'est pas connue du système" + +#: kotti/views/login.py:242 +msgid "You have been logged out." +msgstr "Vous avez été déconnecté." + +#: kotti/views/login.py:280 +msgid "You have reset your password." +msgstr "Vous avez réinitialisé votre mot de passe." + +#: kotti/views/login.py:299 +msgid "Set password" +msgstr "Réinitialiser le mot de passe" + +#: kotti/views/login.py:327 +msgid "Your password reset token may have expired." +msgstr "Votre jeton de réinitialisation de votre mot de passe est expiré." + +#: kotti/views/login.py:334 +#, python-format +msgid "Reset your password - ${title}." +msgstr "Réinitialiser votre mot de passe - ${title}" + +#: kotti/views/edit/default_views.py:86 +msgid "Default view" +msgstr "Vue par défaut" + +#: kotti/views/edit/default_views.py:107 +msgid "Default view has been reset to default." +msgstr "La vue par défaut a été réinitialisée." + +#: kotti/views/edit/default_views.py:114 +msgid "Default view has been set." +msgstr "La vue par défaut a été définie." + +#: kotti/views/edit/default_views.py:119 +msgid "Default view could not be set." +msgstr "La vue par défaut ne peut pas être définie." + +#: kotti/views/edit/actions.py:102 +#, python-format +msgid "${title} was copied." +msgstr "${title} a été copié." + +#: kotti/views/edit/actions.py:120 +#, python-format +msgid "${title} was cut." +msgstr "${title} a été coupé." + +#: kotti/views/edit/actions.py:155 #, python-format msgid "${title} was pasted." msgstr "${title} a été collé." -#: kotti/views/edit/actions.py:161 +#: kotti/views/edit/actions.py:158 msgid "Could not paste node. It no longer exists." msgstr "Ne peut pas coller ce contenu. Il n'existe plus." -#: kotti/views/edit/actions.py:224 +#: kotti/views/edit/actions.py:180 +#, python-format +msgid "${title} was moved." +msgstr "${title} a été déplacé." + +#: kotti/views/edit/actions.py:222 #, python-format msgid "${title} is now visible in the navigation." msgstr "${title} est désormais visible dans la navigation." -#: kotti/views/edit/actions.py:227 +#: kotti/views/edit/actions.py:225 #, python-format msgid "${title} is no longer visible in the navigation." msgstr "${title} nest plus visible dans la navigation." -#: kotti/views/edit/actions.py:292 +#: kotti/views/edit/actions.py:274 kotti/views/edit/actions.py:303 +#, python-format +msgid "${title} was deleted." +msgstr "${title} a été supprimé." + +#: kotti/views/edit/actions.py:300 msgid "Nothing was deleted." msgstr "Rien n'a été supprimé." -#: kotti/views/edit/actions.py:328 kotti/views/edit/actions.py:355 +#: kotti/views/edit/actions.py:336 kotti/views/edit/actions.py:363 msgid "Name and title are required." msgstr "Le nom et le titre sont obligatoires." -#: kotti/views/edit/actions.py:332 +#: kotti/views/edit/actions.py:340 msgid "Item was renamed." msgstr "L'élément a été renommé." -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:461 +msgid "Change State" +msgstr "Modifier le statut" + +#: kotti/views/edit/actions.py:462 msgid "Move up" msgstr "Décaler vers le haut" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:463 msgid "Move down" msgstr "Décaler vers le bas" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:464 msgid "Show" msgstr "Rendre visible" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:465 msgid "Hide" msgstr "Cacher" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:504 msgid "You have to select items to perform an action." msgstr "Vous avez sélectionné des éléments afin de les traiter." -#: kotti/views/edit/actions.py:453 -msgid "Change State" -msgstr "Modifier le statut" - -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:34 msgid "Description" msgstr "Description" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:40 msgid "Tags" msgstr "Mots-clés" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:49 msgid "Body" msgstr "Contenu" -#: kotti/views/edit/default_views.py:99 -msgid "Default view has been reset to default." -msgstr "La vue par défaut a été réinitialisée." - -#: kotti/views/edit/default_views.py:78 -msgid "Default view" -msgstr "Vue par défaut" - -#: kotti/views/edit/default_views.py:106 -msgid "Default view has been set." -msgstr "La vue par défaut a été définie." - -#: kotti/views/edit/default_views.py:111 -msgid "Default view could not be set." -msgstr "La vue par défaut ne peut pas être définie." +#~ msgid "Image" +#~ msgstr "Image" + +#~ msgid "" +#~ "\n" +#~ "

    Log in

    \n" +#~ "

    \n" +#~ " You can log in to your site\n" +#~ " and start changing its contents. If you haven't chosen a password for\n" +#~ " your admin account yet, it'll likely be qwerty.\n" +#~ "

    \n" +#~ "

    \n" +#~ " Once you're logged in, you'll see the grey editor bar below the top\n" +#~ " navigation bar. It will allow you to switch between editing and viewing the\n" +#~ " current page as it will appear to your visitors.\n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Configure

    \n" +#~ "

    \n" +#~ " Find out how to configure your Kotti's title and many other settings using a\n" +#~ " simple text file in your file system.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Configuration manual\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Add-ons

    \n" +#~ "

    \n" +#~ " A number of add-ons allow you to extend the functionality of your Kotti site.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Kotti add-ons\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Documentation

    \n" +#~ "

    \n" +#~ " Wonder what more you can do with Kotti? What license it has? Read the\n" +#~ " manual for more information.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Documentation\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ msgstr "" +#~ "\n" +#~ "

    Identifiez vous

    \n" +#~ "

    \n" +#~ " Vous pouvez vous identifier sur votre site internet\n" +#~ " et commencer à créer du contenu. Si vous n'avez pas choisi un mot de passe pour\n" +#~ " votre compte administrateur, celui-ci devrait être qwerty.\n" +#~ "

    \n" +#~ "

    \n" +#~ " Lorsque que vous serez identifié, vous verrez la barre grise d'édition en dessous de la\n" +#~ " barre de navigation. Celle-ci vous permettra de basculer entre l'édition et la visualisation, telle qu'\n" +#~ " elle apparaitra aux visiteurs.\n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Configuration

    \n" +#~ "

    \n" +#~ " Trouvez comment configurer le titre de votre site ainsi que d'autres paramètres en utilisant\n" +#~ " un éditeur de texte.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Configuration manuelle\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Extensions

    \n" +#~ "

    \n" +#~ " Des extensions vous permettent d'étendre les fonctionnalités de votre site internet Kotti.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Extensions de Kotti\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Documentation

    \n" +#~ "

    \n" +#~ " Découvrez tout ce que vous pouvez faire avec Kotti? La License de Kotti?Parcourez le\n" +#~ " manuel pour plus d'informations.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Documentation\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" + +#~ msgid "" +#~ "\n" +#~ "

    \n" +#~ " \"five\n" +#~ "

    \n" +#~ "\n" +#~ "

    \n" +#~ " Our worldwide headquarters:\n" +#~ "

    \n" +#~ "\n" +#~ "
    \n" +#~ " Foo World
    \n" +#~ " 123 Nowhere Street, Suite 777
    \n" +#~ " Omak, WA 98841 USA
    \n" +#~ " +1-509-555-0100
    \n" +#~ " widgets@foowrld.example.com\n" +#~ "
    \n" +#~ "\n" +#~ "

    \n" +#~ " Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +#~ " \n" +#~ " Copyright info.\n" +#~ " Originally published in the\n" +#~ " Extra EA-300\n" +#~ " article.\n" +#~ "

    \n" +#~ msgstr "" +#~ "\n" +#~ "

    \n" +#~ " \"Cinq\n" +#~ "

    \n" +#~ "\n" +#~ "

    \n" +#~ " Nos bureaux:\n" +#~ "

    \n" +#~ "\n" +#~ "
    \n" +#~ " Foo World
    \n" +#~ " 123 Rue inconnue, Boite 777
    \n" +#~ " Omak, WA 98841 USA
    \n" +#~ " +1-509-555-0100
    \n" +#~ " widgets@foowrld.example.com\n" +#~ "
    \n" +#~ "\n" +#~ "

    \n" +#~ " Crédit de la photot: \"Northern Lights Formation\" by FlugKerl2.\n" +#~ " \n" +#~ " Copyright info.\n" +#~ " Originalement publié sur\n" +#~ " Extra EA-300\n" +#~ " article.\n" +#~ "

    \n" + +#~ msgid "Submit" +#~ msgstr "Soumettre" #~ msgid "Private" #~ msgstr "Privé" diff --git a/kotti/locale/it/LC_MESSAGES/Kotti.mo b/kotti/locale/it/LC_MESSAGES/Kotti.mo index 6ab94fe69..cde449ffa 100644 Binary files a/kotti/locale/it/LC_MESSAGES/Kotti.mo and b/kotti/locale/it/LC_MESSAGES/Kotti.mo differ diff --git a/kotti/locale/it/LC_MESSAGES/Kotti.po b/kotti/locale/it/LC_MESSAGES/Kotti.po index b93cd4156..724a2ef67 100644 --- a/kotti/locale/it/LC_MESSAGES/Kotti.po +++ b/kotti/locale/it/LC_MESSAGES/Kotti.po @@ -1,59 +1,113 @@ -# Italian translations for Kotti. -# Copyright (C) 2013 ORGANIZATION -# This file is distributed under the same license as the Kotti project. -# FIRST AUTHOR , 2013. msgid "" msgstr "" -"Project-Id-Version: Kotti 0.9.2dev\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2014-11-21 21:56+0100\n" -"PO-Revision-Date: 2014-01-19 11:14+0100\n" -"Last-Translator: Xavi Torné \n" -"Language-Team: it \n" -"Language: \n" +"POT-Creation-Date: 2018-01-17 13:51+0100\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" -"Generated-By: Babel 0.9.6\n" -#: kotti/populate.py:64 +#: kotti/security.py:173 +msgid "Viewer" +msgstr "Visualizzatore" + +#: kotti/security.py:174 +msgid "Editor" +msgstr "Editor" + +#: kotti/security.py:175 +msgid "Owner" +msgstr "Proprietario" + +#: kotti/security.py:176 +msgid "Admin" +msgstr "Admin" + +#: kotti/resources.py:541 kotti/views/edit/actions.py:453 +msgid "Copy" +msgstr "Copia" + +#: kotti/resources.py:542 kotti/views/edit/actions.py:454 +msgid "Cut" +msgstr "Taglia" + +#: kotti/resources.py:543 kotti/views/edit/actions.py:450 +msgid "Paste" +msgstr "Incolla" + +#: kotti/resources.py:544 kotti/templates/edit/rename.pt:31 +#: kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:42 kotti/views/edit/actions.py:455 +msgid "Rename" +msgstr "Rinomina" + +#: kotti/resources.py:545 kotti/templates/edit/delete.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:49 +#: kotti/templates/site-setup/delete-user.pt:24 kotti/views/users.py:454 +#: kotti/views/edit/actions.py:457 +msgid "Delete" +msgstr "Elimina" + +#: kotti/resources.py:556 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "Contenuti" + +#: kotti/resources.py:557 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "Modificare" + +#: kotti/resources.py:558 +msgid "Share" +msgstr "Condividi" + +#: kotti/resources.py:559 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "Azioni" + +#: kotti/resources.py:562 +msgid "Folder view" +msgstr "Vista della cartella" + +#: kotti/resources.py:671 kotti/views/edit/content.py:61 +msgid "Document" +msgstr "Documento" + +#: kotti/resources.py:808 kotti/views/edit/content.py:73 +#: kotti/views/edit/content.py:87 +msgid "File" +msgstr "File" + +#: kotti/populate.py:67 msgid "Welcome to Kotti" msgstr "Benvenuto in Kotti" -#: kotti/populate.py:65 +#: kotti/populate.py:68 msgid "Congratulations! You have successfully installed Kotti." msgstr "Congratulazioni! Avete installato Kotti con successo." -#: kotti/populate.py:66 -#, c-format +#: kotti/populate.py:69 +#, fuzzy, c-format msgid "" "\n" "

    Log in

    \n" "

    \n" -" You can log in to your " -"site\n" +" You can log in to your site\n" " and start changing its contents. If you haven't chosen a password for\n" " your admin account yet, it'll likely be qwerty.\n" "

    \n" "

    \n" " Once you're logged in, you'll see the grey editor bar below the top\n" -" navigation bar. It will allow you to switch between editing and viewing " -"the\n" +" navigation bar. It will allow you to switch between editing and viewing the\n" " current page as it will appear to your visitors.\n" "

    \n" "
    \n" "
    \n" "

    Configure

    \n" "

    \n" -" Find out how to configure your Kotti's title and many other " -"settings using a\n" -" simple text file in your file system.\n" +" Find out how to configure your Kotti's title and many other\n" +" settings using a simple text file in your file system.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -61,13 +115,12 @@ msgid "" "
    \n" "

    Add-ons

    \n" "

    \n" -" A number of add-ons allow you to extend the functionality of " -"your Kotti site.\n" +" A number of add-ons allow you to extend the functionality of your\n" +" Kotti site.\n" "

    \n" "

    \n" " \n" +" href=\"http://pypi.python.org/pypi?%3Aaction=search&term=kotti\">\n" " Kotti add-ons\n" " \n" "

    \n" @@ -75,13 +128,13 @@ msgid "" "
    \n" "

    Documentation

    \n" "

    \n" -" Wonder what more you can do with Kotti? What license it has? " -"Read the\n" -" manual for more information.\n" +" Wonder what more you can do with Kotti?\n" +" What license it has?\n" +" Read the manual for more information.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/\">\n" " Documentation\n" " \n" "

    \n" @@ -92,42 +145,32 @@ msgstr "" "

    Accedi

    \n" "

    \n" " Accedi al sito \n" -" e inizia a modificare i suoi contenuti. Se non hai ancora scelto una " -"password per\n" -" il tuo utente amministratore, allora sarà probabilemente qwerty.\n" +" e inizia a modificare i suoi contenuti. Se non hai ancora scelto una password per\n" +" il tuo utente amministratore, allora sarà probabilemente qwerty.\n" "

    \n" "

    \n" -" Quando sarai autenticato, vedrai la barra di editor grigia sotto la " -"barra di navigazione in alto.\n" -" Questa barra ti permetterà di passare dalla modifica alla " -"visualizzazione della pagina corrente così come apparirà a tuoi " -"visitatori.\n" +" Quando sarai autenticato, vedrai la barra di editor grigia sotto la barra di navigazione in alto.\n" +" Questa barra ti permetterà di passare dalla modifica alla visualizzazione della pagina corrente così come apparirà a tuoi visitatori.\n" "

    \n" "
    \n" "
    \n" "

    Configura

    \n" "

    \n" -" Scopri come configurare il titolo del tuo Kotti e molte altre " -"impostazion utilizzando un semplice file di testo sul vostro " -"sistema.\n" +" Scopri come configurare il titolo del tuo Kotti e molte altre impostazion utilizzando un semplice file di testo sul vostro sistema.\n" "

    \n" "

    \n" " Manuale di configurazione\n" +"href=\"https://kotti.readthedocs.io/en/latest/developing/basic/configuration.html\"> Manuale di configurazione\n" " \n" "

    \n" "
    \n" "
    \n" "

    Add-ons

    \n" "

    \n" -" Esistono molti add-ons che permettono di estendere le " -"funzionalità del tuo sito Kotti

    \n" +" Esistono molti add-ons che permettono di estendere le funzionalità del tuo sito Kotti

    \n" "

    \n" " \n" +" href=\"http://pypi.python.org/pypi?%3Aaction=search&term=kotti\">\n" " Add-ons di Kotti\n" " \n" "

    \n" @@ -135,36 +178,30 @@ msgstr "" "
    \n" "

    Documentazione

    \n" "

    \n" -" Ti chiedi cos'altro potresti fare con Kotti? Quale licenza ha? " -"Leggi il manuale per maggiori informazioni

    \n" +" Ti chiedi cos'altro potresti fare con Kotti? Quale licenza ha? Leggi il manuale per maggiori informazioni

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/\">\n" " Documentazione\n" " \n" "

    \n" "
    \n" "
    \n" -#: kotti/populate.py:121 +#: kotti/populate.py:126 msgid "About" msgstr "Chi siamo" -#: kotti/populate.py:122 -msgid "" -"Our company is the leading manufacturer of foo widgets used in a wide " -"variety of aviation and and industrial products." -msgstr "" -"La nostra azienda è il leader nella produzione di foo widget, usati in una " -"grande varietà di prodotti per l'aviazione e industriali." +#: kotti/populate.py:127 +msgid "Our company is the leading manufacturer of foo widgets used in a wide variety of aviation and and industrial products." +msgstr "La nostra azienda è il leader nella produzione di foo widget, usati in una grande varietà di prodotti per l'aviazione e industriali." -#: kotti/populate.py:123 +#: kotti/populate.py:128 msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -181,779 +218,811 @@ msgid "" "\n" "\n" "

    \n" -" Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" -" \n" -" Copyright info.\n" -" Originally published in the\n" -" Extra EA-300\n" -" article.\n" +"Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +"\n" +"Copyright info.\n" +"Originally published in the\n" +" Extra EA-300\n" +"article.\n" "

    \n" msgstr "" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 -msgid "Document" -msgstr "Documento" +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "Benvenuto, ${user}! Sei loggato." -#: kotti/resources.py:649 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 -msgid "File" -msgstr "File" +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "Benvenuto, non sei loggato." -#: kotti/resources.py:726 kotti/views/edit/content.py:144 -msgid "Image" -msgstr "Immagine" +#: kotti/templates/editor-bar.pt:19 +msgid "View" +msgstr "Vista" -#: kotti/resources.py:500 -msgid "Folder view" -msgstr "Vista della cartella" +#: kotti/templates/editor-bar.pt:42 +msgid "Navigate" +msgstr "Navigazione" -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 -msgid "Contents" -msgstr "Contenuti" +#: kotti/templates/editor-bar.pt:53 +msgid "Preferences" +msgstr "Preferenze" -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" -msgstr "Modificare" +#: kotti/templates/editor-bar.pt:58 +msgid "Site Setup" +msgstr "Configurazione del sito" -#: kotti/resources.py:489 -msgid "Share" -msgstr "Condividi" +#: kotti/templates/editor-bar.pt:69 +msgid "Logout" +msgstr "Esci" -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" -msgstr "Azioni" +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "Seleziona la vista predefinita" -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 -msgid "Copy" -msgstr "Copia" +#: kotti/templates/login.pt:16 +msgid "Login" +msgstr "Accedi" -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 -msgid "Cut" -msgstr "Taglia" +#: kotti/templates/login.pt:21 kotti/templates/login.pt:59 +msgid "Username or email" +msgstr "Username o email" -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 -msgid "Paste" -msgstr "Incolla" +#: kotti/templates/login.pt:26 kotti/views/users.py:220 +#: kotti/views/login.py:255 +msgid "Password" +msgstr "Password" -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 -#: kotti/templates/edit/rename-nodes.pt:55 kotti/templates/edit/rename.pt:42 -#: kotti/views/edit/actions.py:447 -msgid "Rename" -msgstr "Rinomina" +#: kotti/templates/login.pt:34 +msgid "Log in" +msgstr "Accedi" -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 -#: kotti/templates/edit/delete-nodes.pt:90 kotti/templates/edit/delete.pt:28 -#: kotti/templates/site-setup/delete-user.pt:36 kotti/views/users.py:443 -#: kotti/views/edit/actions.py:449 -msgid "Delete" -msgstr "Elimina" +#: kotti/templates/login.pt:45 +msgid "Forgot your password?" +msgstr "Hai dimenticato la tua password?" -#: kotti/security.py:165 -msgid "Viewer" -msgstr "Visualizzatore" +#. Used in sentence: "Fill out your username or email and click +#. ${reset_password} below to receive an email with a link to reset your +#. password." +#. +#: kotti/templates/login.pt:52 kotti/templates/login.pt:67 +msgid "Reset password" +msgstr "Resetta password" -#: kotti/security.py:166 -msgid "Editor" -msgstr "Editor" +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:50 +msgid "Fill out your username or email and click ${reset_password} below to receive an email with a link to reset your password." +msgstr "Inserisci il tuo username o email e premi ${reset_password} per ricevere una email con il link per resettare la tua password." -#: kotti/security.py:167 -msgid "Owner" -msgstr "Propietario" +#: kotti/templates/login.pt:78 +msgid "Not registered yet?" +msgstr "Non sei ancora registrato?" -#: kotti/security.py:168 -msgid "Admin" -msgstr "Admin" +#: kotti/templates/login.pt:85 +msgid "Register for an account on this site." +msgstr "Registrati per avere un account su questo sito." + +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "Vietato" -#: kotti/templates/add-dropdown.pt:5 +#: kotti/templates/add-dropdown.pt:3 msgid "Add" msgstr "Aggiungere" -#: kotti/templates/add-dropdown.pt:27 +#: kotti/templates/add-dropdown.pt:13 msgid "Upload Content" msgstr "Carica contenuto" -#: kotti/templates/default-view-selector.pt:5 -msgid "Set default view" -msgstr "Seleziona la vista predefinita" - -#: kotti/templates/editor-bar.pt:35 -msgid "View" -msgstr "Vista" - -#: kotti/templates/editor-bar.pt:76 -msgid "Navigate" -msgstr "Navigazione" - -#: kotti/templates/editor-bar.pt:98 -msgid "Preferences" -msgstr "Preferenze" - -#: kotti/templates/editor-bar.pt:109 -msgid "Site Setup" -msgstr "Configurazione del sito" - -#: kotti/templates/editor-bar.pt:131 -msgid "Logout" -msgstr "Esci" +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "Rendi ${state}" -#: kotti/templates/email-reset-password.pt:3 +#: kotti/templates/email-reset-password.pt:2 msgid "Reset your password for ${site_title}." msgstr "Resetta la tua password per ${site_title}." -#: kotti/templates/email-reset-password.pt:7 -#: kotti/templates/email-set-password.pt:6 +#: kotti/templates/email-reset-password.pt:4 +#: kotti/templates/email-set-password.pt:4 msgid "Hello, ${user_title}!" msgstr "Ciao, ${user_title}!" -#: kotti/templates/email-reset-password.pt:10 +#: kotti/templates/email-reset-password.pt:5 msgid "Click this link to reset your password at ${site_title}: ${url}" msgstr "Segui il link per resettare la tua password a ${site_title}: ${url}" -#: kotti/templates/email-set-password.pt:3 +#: kotti/templates/email-set-password.pt:2 msgid "Your registration for ${site_title}" msgstr "La tua registrazione per ${site_title}" -#: kotti/templates/email-set-password.pt:9 +#: kotti/templates/email-set-password.pt:5 msgid "You are joining ${site_title}." msgstr "Stai entrando in ${site_title}." -#: kotti/templates/email-set-password.pt:12 +#: kotti/templates/email-set-password.pt:6 msgid "Click here to set your password and log in: ${url}" msgstr "Premi qui per impostare la tua password e accedere: ${url}" -#: kotti/templates/forbidden.pt:7 -msgid "Forbidden" -msgstr "Vietato" +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" +msgstr "Condividi ${title}" -#: kotti/templates/login.pt:16 -msgid "Login" -msgstr "Accedi" +#: kotti/templates/edit/share.pt:14 kotti/templates/edit/share.pt:20 +#: kotti/templates/edit/share.pt:27 kotti/templates/site-setup/users.pt:48 +msgid "Search users and groups" +msgstr "Cerca utenti e gruppi" -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 -msgid "Username or email" -msgstr "Username o email" +#: kotti/templates/edit/share.pt:34 kotti/templates/site-setup/users.pt:56 +msgid "Search" +msgstr "Cerca" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 -#: kotti/views/users.py:212 -msgid "Password" -msgstr "Password" +#: kotti/templates/edit/share.pt:44 +msgid "Assign local roles" +msgstr "Assegna ruoli locali" -#: kotti/templates/login.pt:47 -msgid "Log in" -msgstr "Accedi" +#: kotti/templates/edit/share.pt:49 kotti/templates/edit/change-state.pt:19 +#: kotti/templates/edit/delete-nodes.pt:22 kotti/templates/edit/upload.pt:18 +#: kotti/templates/edit/upload.pt:108 kotti/templates/edit/contents.pt:35 +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:69 +msgid "Type" +msgstr "Tipo" -#: kotti/templates/login.pt:65 -msgid "Forgot your password?" -msgstr "Hai dimenticato la tua password?" +#: kotti/templates/edit/share.pt:50 kotti/templates/site-setup/users.pt:70 +#: kotti/views/users.py:215 +msgid "Name" +msgstr "Nome" -#. Used in sentence: "Fill out your username or email and click -#. ${reset_password} below to receive an email with a link to reset your -#. password." -#. -#: kotti/templates/login.pt:77 kotti/templates/login.pt:100 -msgid "Reset password" -msgstr "Resetta password" +#: kotti/templates/edit/share.pt:61 kotti/templates/site-setup/users.pt:81 +#: kotti/views/users.py:321 kotti/views/users.py:515 kotti/views/users.py:550 +msgid "User" +msgstr "Utente" -#. Canonical text for ${reset_password} is: "Reset password" -#: kotti/templates/login.pt:74 -msgid "" -"Fill out your username or email and click ${reset_password} below to receive " -"an email with a link to reset your password." -msgstr "" -"Inserisci il tuo username o email e premi ${reset_password} per ricevere una " -"email con il link per resettare la tua password." +#: kotti/templates/edit/share.pt:62 kotti/templates/site-setup/users.pt:82 +#: kotti/views/users.py:196 kotti/views/users.py:356 kotti/views/users.py:515 +#: kotti/views/users.py:550 +msgid "Group" +msgstr "Gruppo" -#: kotti/templates/login.pt:118 -msgid "Not registered yet?" -msgstr "Non sei ancora registrato?" +#: kotti/templates/edit/share.pt:65 kotti/templates/site-setup/users.pt:85 +msgid "Gravatar" +msgstr "Gravatar" -#: kotti/templates/login.pt:127 -msgid "Register for an account on this site." -msgstr "Registrati per avere un account su questo sito." +#: kotti/templates/edit/share.pt:70 kotti/templates/site-setup/users.pt:91 +#, fuzzy +msgid "Assign role" +msgstr "Assegna ruoli locali" -#: kotti/templates/workflow-dropdown.pt:19 -msgid "Make ${state}" -msgstr "Rendi ${state}" +#: kotti/templates/edit/share.pt:85 kotti/templates/edit/change-state.pt:72 +#: kotti/templates/site-setup/users.pt:107 +msgid "Apply changes" +msgstr "Applica le modifiche" + +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "Navigazione del Sito" -#: kotti/templates/edit/breadcrumbs.pt:5 +#: kotti/templates/edit/breadcrumbs.pt:8 msgid "You are here:" msgstr "Sei qua:" -#: kotti/templates/edit/change-state.pt:7 +#: kotti/templates/edit/delete.pt:9 kotti/templates/site-setup/delete-user.pt:9 +msgid "Delete ${title}" +msgstr "Elimina ${title}" + +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" +msgstr "Sei sicuro di voler eliminare ${title}?" + +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:74 +#: kotti/templates/edit/delete-nodes.pt:44 +#: kotti/templates/edit/rename-nodes.pt:44 kotti/views/users.py:324 +#: kotti/views/users.py:359 kotti/views/users.py:453 kotti/views/form.py:81 +msgid "Cancel" +msgstr "Cancella" + +#: kotti/templates/edit/change-state.pt:8 msgid "Change workflow state" msgstr "Cambia lo stato del workflow" -#: kotti/templates/edit/change-state.pt:14 +#: kotti/templates/edit/change-state.pt:12 msgid "An item's workflow state determines who can see, edit and/or manage it." -msgstr "" -"Lo stato del workflow di un item determina chi lo può vedere, editare e/o " -"modificare." +msgstr "Lo stato del workflow di un item determina chi lo può vedere, editare e/o modificare." -#: kotti/templates/edit/change-state.pt:26 kotti/templates/edit/contents.pt:57 -#: kotti/templates/edit/delete-nodes.pt:33 kotti/templates/view/folder.pt:28 -#: kotti/views/users.py:196 kotti/views/edit/content.py:31 +#: kotti/templates/edit/change-state.pt:18 +#: kotti/templates/edit/delete-nodes.pt:21 kotti/templates/edit/contents.pt:34 +#: kotti/templates/view/folder.pt:20 kotti/views/users.py:204 +#: kotti/views/edit/content.py:28 msgid "Title" msgstr "Titolo" -#: kotti/templates/edit/change-state.pt:29 kotti/templates/edit/contents.pt:60 -#: kotti/templates/edit/delete-nodes.pt:36 kotti/templates/edit/share.pt:70 -#: kotti/templates/edit/upload.pt:23 kotti/templates/edit/upload.pt:167 -#: kotti/templates/site-setup/users.pt:105 kotti/templates/view/folder.pt:31 -msgid "Type" -msgstr "Tipo" - -#: kotti/templates/edit/change-state.pt:32 kotti/templates/edit/contents.pt:63 -#: kotti/templates/edit/delete-nodes.pt:39 +#: kotti/templates/edit/change-state.pt:20 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:36 msgid "State" msgstr "Stato" -#: kotti/templates/edit/change-state.pt:35 kotti/templates/edit/contents.pt:69 -#: kotti/templates/edit/delete-nodes.pt:42 kotti/templates/view/folder.pt:34 +#: kotti/templates/edit/change-state.pt:21 +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:38 +#: kotti/templates/view/folder.pt:22 msgid "Creation Date" msgstr "Data di creazione" -#: kotti/templates/edit/change-state.pt:38 kotti/templates/edit/contents.pt:72 -#: kotti/templates/edit/delete-nodes.pt:45 kotti/templates/view/folder.pt:37 +#: kotti/templates/edit/change-state.pt:22 +#: kotti/templates/edit/delete-nodes.pt:25 kotti/templates/edit/contents.pt:39 +#: kotti/templates/view/folder.pt:23 msgid "Modification Date" msgstr "Data di modifica" -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 +#: kotti/templates/edit/change-state.pt:28 +#: kotti/templates/edit/delete-nodes.pt:31 kotti/templates/edit/contents.pt:45 +msgid "Select item" +msgstr "" + +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:48 msgid "Include children" msgstr "Includere figli" -#: kotti/templates/edit/change-state.pt:82 -msgid "" -"If checked, this will attempt to modify the status of all children in any " -"selected documents and their subdocuments." -msgstr "" -"Se selezionato, verrà modificato lo stato dei figli dei documenti " -"selezionati e i suoi sottodocumenti." +#: kotti/templates/edit/change-state.pt:41 +msgid "If checked, this will attempt to modify the status of all children in any selected documents and their subdocuments." +msgstr "Se selezionato, verrà modificato lo stato dei figli dei documenti selezionati e i suoi sottodocumenti." -#: kotti/templates/edit/change-state.pt:100 +#: kotti/templates/edit/change-state.pt:54 msgid "Change state to" msgstr "Cambia stato a" -#: kotti/templates/edit/change-state.pt:103 +#: kotti/templates/edit/change-state.pt:55 msgid "Select the new state where all chosen items should be set." msgstr "Seleziona il nuovo stato per tutti gli elementi selezionati." -#: kotti/templates/edit/change-state.pt:111 +#: kotti/templates/edit/change-state.pt:60 msgid "No change" msgstr "Nessuna modifica" -#: kotti/templates/edit/change-state.pt:127 kotti/templates/edit/share.pt:121 -#: kotti/templates/site-setup/users.pt:165 -msgid "Apply changes" -msgstr "Applica le modifiche" - -#: kotti/templates/edit/change-state.pt:130 -#: kotti/templates/edit/delete-nodes.pt:93 -#: kotti/templates/edit/rename-nodes.pt:58 kotti/views/form.py:78 -#: kotti/views/users.py:316 kotti/views/users.py:350 kotti/views/users.py:442 -msgid "Cancel" -msgstr "Cancella" - -#: kotti/templates/edit/contents.pt:43 -msgid "No content items are contained here." -msgstr "Non è presente nessun elemento di contenuto." - -#: kotti/templates/edit/contents.pt:66 -msgid "Visibility" -msgstr "Visibilità" - -#: kotti/templates/edit/contents.pt:125 -msgid "Visible" -msgstr "Visibile" - -#: kotti/templates/edit/contents.pt:134 -msgid "Hidden" -msgstr "Nascosto" - -#: kotti/templates/edit/delete-nodes.pt:17 +#: kotti/templates/edit/delete-nodes.pt:13 msgid "Are you sure you want to delete the following items?" msgstr "Sei sicuro di voler eliminare i seguenti elementi?" -#: kotti/templates/edit/delete.pt:8 -#: kotti/templates/site-setup/delete-user.pt:8 -msgid "Delete ${title}" -msgstr "Elimina ${title}" - -#: kotti/templates/edit/delete.pt:20 -msgid "Are you sure you want to delete ${title}?" -msgstr "Sei sicuro di voler eliminare ${title}?" - -#: kotti/templates/edit/nav-tree-view.pt:7 -msgid "Navigate Site" -msgstr "Navigazione del Sito" +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "Rinomina ${title}" -#: kotti/templates/edit/rename-nodes.pt:16 kotti/templates/edit/rename.pt:14 -msgid "" -"Each content item has a name, which is used to create the url, and a title. " -"Read the user manual about proper formatting of name and title if needed. " -"You can change both below." -msgstr "" -"Ogni contenuto ha un nome, che è usato per creare la url, e un titolo. Leggi " -"il manuale utente per saperne di più sul formato del nome e del titolo. Si " -"possono cambiare entrambi qui sotto." +#: kotti/templates/edit/rename.pt:11 kotti/templates/edit/rename-nodes.pt:13 +msgid "Each content item has a name, which is used to create the url, and a title. Read the user manual about proper formatting of name and title if needed. You can change both below." +msgstr "Ogni contenuto ha un nome, che è usato per creare la url, e un titolo. Leggi il manuale utente per saperne di più sul formato del nome e del titolo. Si possono cambiare entrambi qui sotto." -#: kotti/templates/edit/rename-nodes.pt:35 kotti/templates/edit/rename.pt:26 +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +#: kotti/templates/edit/rename-nodes.pt:28 msgid "New name" msgstr "Nuovo nome" -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:34 +#: kotti/templates/edit/rename-nodes.pt:36 msgid "New title" msgstr "Nuovo titolo" -#: kotti/templates/edit/rename.pt:7 -msgid "Rename ${title}" -msgstr "Rinomina ${title}" +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" +msgstr "Carica un contenuto da uno o più file locali" -#: kotti/templates/edit/share.pt:7 -msgid "Share ${title}" -msgstr "Condividi ${title}" +#: kotti/templates/edit/upload.pt:17 kotti/templates/edit/upload.pt:107 +msgid "Filename" +msgstr "Nome del file" -#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:30 -#: kotti/templates/edit/share.pt:35 kotti/templates/site-setup/users.pt:68 -msgid "Search users and groups" -msgstr "Cerca utenti e gruppi" +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:109 +msgid "Size" +msgstr "Dimensione" -#: kotti/templates/edit/share.pt:43 kotti/templates/site-setup/users.pt:81 -msgid "Search" -msgstr "Cerca" - -#: kotti/templates/edit/share.pt:60 -msgid "Assign local roles" -msgstr "Assegna ruoli locali" - -#: kotti/templates/edit/share.pt:73 kotti/templates/site-setup/users.pt:108 -#: kotti/views/users.py:207 -msgid "Name" -msgstr "Nome" - -#: kotti/templates/edit/share.pt:91 kotti/templates/site-setup/users.pt:126 -#: kotti/views/users.py:313 kotti/views/users.py:501 kotti/views/users.py:536 -msgid "User" -msgstr "Utente" - -#: kotti/templates/edit/share.pt:94 kotti/templates/site-setup/users.pt:129 -#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 -#: kotti/views/users.py:536 -msgid "Group" -msgstr "Gruppo" - -#: kotti/templates/edit/share.pt:101 kotti/templates/site-setup/users.pt:136 -msgid "Gravatar" -msgstr "Gravatar" - -#: kotti/templates/edit/upload.pt:9 -msgid "Upload content from local file(s)" -msgstr "Carica un contenuto da uno o più file locali" - -#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:164 -msgid "Filename" -msgstr "Nome del file" - -#: kotti/templates/edit/upload.pt:26 kotti/templates/edit/upload.pt:170 -msgid "Size" -msgstr "Dimensione" - -#: kotti/templates/edit/upload.pt:29 +#: kotti/templates/edit/upload.pt:20 msgid "Status" msgstr "Stato" -#: kotti/templates/edit/upload.pt:32 +#: kotti/templates/edit/upload.pt:21 msgid "Progress" msgstr "Avanzamento" -#: kotti/templates/edit/upload.pt:124 +#: kotti/templates/edit/upload.pt:84 msgid "Select file(s) to upload..." msgstr "" -#: kotti/templates/edit/upload.pt:134 +#: kotti/templates/edit/upload.pt:92 msgid "Upload ${number} files." msgstr "" -#: kotti/templates/edit/upload.pt:144 +#: kotti/templates/edit/upload.pt:97 msgid "Dismiss all errors" msgstr "" -#: kotti/templates/site-setup/delete-user.pt:20 +#: kotti/templates/edit/contents.pt:26 +msgid "No content items are contained here." +msgstr "Non è presente nessun elemento di contenuto." + +#: kotti/templates/edit/contents.pt:33 +msgid "Select / deselect all" +msgstr "" + +#: kotti/templates/edit/contents.pt:37 +msgid "Visibility" +msgstr "Visibilità" + +#: kotti/templates/edit/contents.pt:69 +msgid "Visible" +msgstr "Visibile" + +#: kotti/templates/edit/contents.pt:73 +msgid "Hidden" +msgstr "Nascosto" + +#: kotti/templates/http-errors/notfound.pt:26 +msgid "Not Found" +msgstr "" + +#: kotti/templates/http-errors/notfound.pt:27 +msgid "The resource could not be found" +msgstr "" + +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "Risultati di ricerca" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "Cerca..." + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "Taggati con:" + +#: kotti/templates/site-setup/delete-user.pt:15 msgid "Are you sure you want to delete ${type} ${title}?" msgstr "Sei sicuro di voler eliminare ${type} ${title}?" -#: kotti/templates/site-setup/user.pt:13 +#: kotti/templates/site-setup/user.pt:12 msgid "Back to User Management" msgstr "Torna alla gestione degli utenti" -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 +#: kotti/templates/site-setup/user.pt:21 kotti/views/form.py:159 #, python-format msgid "Edit ${title}" msgstr " Modificare ${title}" -#: kotti/templates/site-setup/users.pt:15 +#: kotti/templates/site-setup/users.pt:12 #, fuzzy msgid "Search user(s) / group(s)" msgstr "Cerca utente(i) / gruppo(i)" -#: kotti/templates/site-setup/users.pt:24 +#: kotti/templates/site-setup/users.pt:17 msgid "Add user" msgstr "Aggiungi un utente" -#: kotti/templates/site-setup/users.pt:33 +#: kotti/templates/site-setup/users.pt:22 msgid "Add group" msgstr "Aggiungi un gruppo" -#: kotti/templates/site-setup/users.pt:52 +#: kotti/templates/site-setup/users.pt:34 msgid "Find users or groups" msgstr "Cerca utenti o gruppi" -#: kotti/templates/site-setup/users.pt:63 +#: kotti/templates/site-setup/users.pt:40 msgid "User- / groupname" msgstr "" -#: kotti/templates/site-setup/users.pt:70 +#: kotti/templates/site-setup/users.pt:50 msgid "Blank search text finds all." msgstr "Lascia il testo vuoto per trovare tutti gli utenti." -#: kotti/templates/site-setup/users.pt:96 +#: kotti/templates/site-setup/users.pt:65 msgid "Assign global roles" msgstr "Assegna ruoli globali" -#: kotti/templates/site-setup/users.pt:180 +#: kotti/templates/site-setup/users.pt:117 msgid "Add new user" msgstr "Aggiungi un nuovo utente" -#: kotti/templates/site-setup/users.pt:190 +#: kotti/templates/site-setup/users.pt:123 msgid "Add new group" msgstr "Aggiungi un nuovo gruppo" -#: kotti/templates/view/search-results.pt:8 -msgid "Search Results" -msgstr "Risultati di ricerca" - -#: kotti/templates/view/search.pt:5 -msgid "Search..." -msgstr "Cerca..." - -#: kotti/templates/view/tags.pt:5 -msgid "Tagged with:" -msgstr "Taggati con:" - -#: kotti/tests/testing_view.pt:7 -msgid "Welcome, ${user}! You are logged in." -msgstr "Benvenuto, ${user}! Sei loggato." - -#: kotti/tests/testing_view.pt:14 -msgid "Welcome, you are not logged in." -msgstr "Benvenuto, non sei loggato." - -#: kotti/views/form.py:79 kotti/views/users.py:69 -#: kotti/views/edit/actions.py:363 kotti/views/edit/actions.py:407 +#: kotti/views/users.py:70 kotti/views/form.py:82 +#: kotti/views/edit/actions.py:371 kotti/views/edit/actions.py:415 msgid "Your changes have been saved." msgstr "Le tue modifiche sono state salvate." -#: kotti/views/form.py:174 -msgid "Item was added." -msgstr "L'elemento è stato aggiunto." - -#: kotti/views/form.py:264 -#, python-format -msgid "Maximum file size: ${size}MB" -msgstr "Dimensione massima del file: ${size}MB" - -#: kotti/views/form.py:77 kotti/views/users.py:441 -msgid "Save" -msgstr "Salva" - -#: kotti/views/form.py:198 -#, python-format -msgid "Add ${type} to ${title}." -msgstr "Aggiungere ${type} a ${title}." - -#: kotti/views/form.py:202 -#, python-format -msgid "Add ${type}." -msgstr "Aggiungere ${type}." - -#: kotti/views/login.py:238 -msgid "You have reset your password." -msgstr "Hai resettato la tua password." - -#: kotti/views/login.py:200 -msgid "You have been logged out." -msgstr "Sei uscito." - -#: kotti/views/login.py:66 kotti/views/users.py:270 -msgid "Full name" -msgstr "Nome completo" - -#: kotti/views/login.py:69 -msgid "Username" -msgstr "Username" - -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 -msgid "Email" -msgstr "Email" - -#: kotti/views/login.py:109 -msgid "" -"Congratulations! You are successfully registered. You should be receiving an " -"email with a link to set your password. Doing so will activate your account." -msgstr "" -"Complimenti! Sei registrato. Dovrebbe arrivarti una email con il link per " -"settare la tua password e attivare il tuo account." - -#: kotti/views/login.py:124 -#, python-format -msgid "Register - ${title}" -msgstr "Registrazione - ${title}" - -#: kotti/views/login.py:164 -msgid "Login failed." -msgstr "Errore nell'accesso." - -#: kotti/views/login.py:286 -#, python-format -msgid "Reset your password - ${title}." -msgstr "Resetta la tua password - ${title}." - -#: kotti/views/login.py:160 -#, python-format -msgid "Welcome, ${user}!" -msgstr "Benvenuto, ${user}!" - -#: kotti/views/login.py:173 -msgid "" -"You should be receiving an email with a link to reset your password. Doing " -"so will activate your account." -msgstr "" -"Dovresti ricevere una email con un link per resettare la tua password. " -"Clikkandolo si attiverà il tuo account." - -#: kotti/views/login.py:178 -msgid "That username or email is not known by this system." -msgstr "Questo username o email non è riconosciuto dal sistema." - -#: kotti/views/login.py:83 -msgid "Register" -msgstr "Registrazione" - -#: kotti/views/login.py:90 kotti/views/login.py:257 -msgid "There was an error." -msgstr "C'è stato un errore." - -#: kotti/views/login.py:250 -msgid "Submit" -msgstr "Invia" - -#: kotti/views/login.py:279 -msgid "Your password reset token may have expired." -msgstr "Il token per resettare la tua password è scaduto." - -#: kotti/views/site_setup.py:6 kotti/views/users.py:378 -msgid "User Management" -msgstr "Gestione utenti" - -#: kotti/views/users.py:267 -msgid "" -"Leave this empty and tick the 'Send password registration' box below to have " -"the user set their own password." -msgstr "" -"Lascia questo vuoto e seleziona il box 'Invia la password di registrazione' " -"per mandare all'utente la sua password." +#: kotti/views/users.py:72 kotti/views/users.py:477 +#: kotti/views/edit/actions.py:309 kotti/views/edit/actions.py:375 +#: kotti/views/edit/actions.py:417 kotti/views/edit/actions.py:421 +msgid "No changes were made." +msgstr "Non sono state fatte modifiche." -#: kotti/views/users.py:587 -#, python-format -msgid "My preferences - ${title}" -msgstr "Le mie preferenze - ${title}" +#: kotti/views/users.py:105 +msgid "No users or groups were found." +msgstr "Utenti o gruppo non trovati" -#: kotti/views/users.py:145 +#: kotti/views/users.py:153 msgid "Invalid value" msgstr "Valore invalido" -#: kotti/views/users.py:151 +#: kotti/views/users.py:159 msgid "A user with that name already exists." msgstr "Un utente con questo nome già esiste." -#: kotti/views/users.py:158 +#: kotti/views/users.py:166 msgid "A user with that email already exists." msgstr "Un utente con questa email già esiste." -#: kotti/views/users.py:181 +#: kotti/views/users.py:189 #, python-format msgid "No such group: ${group}" msgstr "Gruppo non esistente: ${group}" -#: kotti/views/users.py:219 +#: kotti/views/users.py:207 kotti/views/login.py:71 kotti/views/login.py:267 +msgid "Email" +msgstr "Email" + +#: kotti/views/users.py:227 msgid "Active" msgstr "Attivo" -#: kotti/views/users.py:220 +#: kotti/views/users.py:228 msgid "Untick this to deactivate the account." msgstr "Deseleziona questo per disattivare l'account." -#: kotti/views/users.py:226 +#: kotti/views/users.py:234 msgid "Global roles" msgstr "Ruoli globali" -#: kotti/views/users.py:230 +#: kotti/views/users.py:238 msgid "Groups" msgstr "Gruppi" -#: kotti/views/users.py:315 +#: kotti/views/users.py:275 +msgid "Leave this empty and tick the 'Send password registration' box below to have the user set their own password." +msgstr "Lascia questo vuoto e seleziona il box 'Invia la password di registrazione' per mandare all'utente la sua password." + +#: kotti/views/users.py:278 kotti/views/login.py:63 +msgid "Full name" +msgstr "Nome completo" + +#: kotti/views/users.py:323 msgid "Add User" msgstr "Aggiungi utente" -#: kotti/views/users.py:339 +#: kotti/views/users.py:333 +msgid "Send password registration link." +msgstr "Invia il link della password di registrazione." + +#: kotti/views/users.py:348 #, python-format msgid "${title} was added." msgstr "${title} è stato aggiunto." -#: kotti/views/users.py:349 +#: kotti/views/users.py:358 msgid "Add Group" msgstr "Aggiungi gruppo" -#: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:301 kotti/views/edit/actions.py:367 -#: kotti/views/edit/actions.py:413 kotti/views/edit/actions.py:409 -msgid "No changes were made." -msgstr "Non sono state fatte modifiche." - -#: kotti/views/users.py:558 -msgid "No name was given." -msgstr "Non è stato dato un nome." +#: kotti/views/users.py:388 kotti/views/site_setup.py:5 +msgid "User Management" +msgstr "Gestione utenti" -#: kotti/views/users.py:98 -msgid "No users or groups were found." -msgstr "Utenti o gruppo non trovati" +#: kotti/views/users.py:452 kotti/views/form.py:80 +msgid "Save" +msgstr "Salva" -#: kotti/views/users.py:505 +#: kotti/views/users.py:519 #, python-format msgid "Edit ${principal_type} ${title}" msgstr "Modificare ${principal_type} ${title}" -#: kotti/views/users.py:533 +#: kotti/views/users.py:547 msgid "User was not found." msgstr "Utente non trovato." -#: kotti/views/users.py:324 -msgid "Send password registration link." -msgstr "Invia il link della password di registrazione." - -#: kotti/views/users.py:543 +#: kotti/views/users.py:557 #, python-format msgid "${principal_type} ${title} was deleted." msgstr "${principal_type} ${title} è stato cancellato." -#: kotti/views/users.py:551 +#: kotti/views/users.py:565 #, python-format msgid "Delete ${principal_type} ${title}" msgstr "Elimina ${principal_type} ${title}" -#: kotti/views/edit/actions.py:106 +#: kotti/views/users.py:572 +msgid "No name was given." +msgstr "Non è stato dato un nome." + +#: kotti/views/users.py:601 #, python-format -msgid "${title} was copied." -msgstr "${title} è stato copiato." +msgid "My preferences - ${title}" +msgstr "Le mie preferenze - ${title}" -#: kotti/views/edit/actions.py:124 +#: kotti/views/form.py:177 +msgid "Item was added." +msgstr "L'elemento è stato aggiunto." + +#: kotti/views/form.py:201 #, python-format -msgid "${title} was cut." -msgstr "${title} è stato tagliato." +msgid "Add ${type} to ${title}." +msgstr "Aggiungere ${type} a ${title}." -#: kotti/views/edit/actions.py:182 +#: kotti/views/form.py:205 #, python-format -msgid "${title} was moved." -msgstr "${title} è stato spostato." +msgid "Add ${type}." +msgstr "Aggiungere ${type}." -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/form.py:295 #, python-format -msgid "${title} was deleted." -msgstr "${title} è stato cancellato." +msgid "Maximum file size: ${size}MB" +msgstr "Dimensione massima del file: ${size}MB" -#: kotti/views/edit/actions.py:158 +#: kotti/views/login.py:66 +msgid "Username" +msgstr "Username" + +#: kotti/views/login.py:80 +msgid "Register" +msgstr "Registrazione" + +#: kotti/views/login.py:87 +msgid "There was an error." +msgstr "C'è stato un errore." + +#: kotti/views/login.py:106 +msgid "Congratulations! You are successfully registered. You should be receiving an email with a link to set your password. Doing so will activate your account." +msgstr "Complimenti! Sei registrato. Dovrebbe arrivarti una email con il link per settare la tua password e attivare il tuo account." + +#: kotti/views/login.py:121 +#, python-format +msgid "Register - ${title}" +msgstr "Registrazione - ${title}" + +#: kotti/views/login.py:152 +#, python-format +msgid "Welcome, ${user}!" +msgstr "Benvenuto, ${user}!" + +#: kotti/views/login.py:177 +msgid "You should be receiving an email with a link to reset your password. Doing so will activate your account." +msgstr "Dovresti ricevere una email con un link per resettare la tua password. Clikkandolo si attiverà il tuo account." + +#: kotti/views/login.py:210 +msgid "Login failed." +msgstr "Errore nell'accesso." + +#: kotti/views/login.py:220 +msgid "That username or email is not known by this system." +msgstr "Questo username o email non è riconosciuto dal sistema." + +#: kotti/views/login.py:242 +msgid "You have been logged out." +msgstr "Sei uscito." + +#: kotti/views/login.py:280 +msgid "You have reset your password." +msgstr "Hai resettato la tua password." + +#: kotti/views/login.py:299 +#, fuzzy +msgid "Set password" +msgstr "Resetta password" + +#: kotti/views/login.py:327 +msgid "Your password reset token may have expired." +msgstr "Il token per resettare la tua password è scaduto." + +#: kotti/views/login.py:334 +#, python-format +msgid "Reset your password - ${title}." +msgstr "Resetta la tua password - ${title}." + +#: kotti/views/edit/default_views.py:86 +msgid "Default view" +msgstr "Vista predefinita" + +#: kotti/views/edit/default_views.py:107 +msgid "Default view has been reset to default." +msgstr "La vista predefinita è stata modificata a predefinita." + +#: kotti/views/edit/default_views.py:114 +msgid "Default view has been set." +msgstr "E' stata selezionata la vista predefinita." + +#: kotti/views/edit/default_views.py:119 +msgid "Default view could not be set." +msgstr "La vista predefinita non può essere selezionata." + +#: kotti/views/edit/actions.py:102 +#, python-format +msgid "${title} was copied." +msgstr "${title} è stato copiato." + +#: kotti/views/edit/actions.py:120 +#, python-format +msgid "${title} was cut." +msgstr "${title} è stato tagliato." + +#: kotti/views/edit/actions.py:155 #, python-format msgid "${title} was pasted." msgstr "${title} è stato incollato." -#: kotti/views/edit/actions.py:161 +#: kotti/views/edit/actions.py:158 msgid "Could not paste node. It no longer exists." msgstr "Non si può incollare l'elemento. L'elemento non è più esistente." -#: kotti/views/edit/actions.py:224 +#: kotti/views/edit/actions.py:180 +#, python-format +msgid "${title} was moved." +msgstr "${title} è stato spostato." + +#: kotti/views/edit/actions.py:222 #, python-format msgid "${title} is now visible in the navigation." msgstr "${title} è ora visibile nella navigazione." -#: kotti/views/edit/actions.py:227 +#: kotti/views/edit/actions.py:225 #, python-format msgid "${title} is no longer visible in the navigation." msgstr "${title} non è più visibile nella navigazione." -#: kotti/views/edit/actions.py:292 +#: kotti/views/edit/actions.py:274 kotti/views/edit/actions.py:303 +#, python-format +msgid "${title} was deleted." +msgstr "${title} è stato cancellato." + +#: kotti/views/edit/actions.py:300 msgid "Nothing was deleted." msgstr "Non è stato cancellato niente." -#: kotti/views/edit/actions.py:328 kotti/views/edit/actions.py:355 +#: kotti/views/edit/actions.py:336 kotti/views/edit/actions.py:363 msgid "Name and title are required." msgstr "Nome e titolo sono obbligatori." -#: kotti/views/edit/actions.py:332 +#: kotti/views/edit/actions.py:340 msgid "Item was renamed." msgstr "L'elemento è stato rinominato" -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:461 +msgid "Change State" +msgstr "Cambia stato" + +#: kotti/views/edit/actions.py:462 msgid "Move up" msgstr "Muovere su" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:463 msgid "Move down" msgstr "Muovi giù" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:464 msgid "Show" msgstr "Mostra" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:465 msgid "Hide" msgstr "Nascondi" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:504 msgid "You have to select items to perform an action." msgstr "Devi selezionare degli elementi per fare quest'azione." -#: kotti/views/edit/actions.py:453 -msgid "Change State" -msgstr "Cambia stato" - -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:34 msgid "Description" msgstr "Descrizione" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:40 msgid "Tags" msgstr "Tags" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:49 msgid "Body" msgstr "Contenuto" -#: kotti/views/edit/default_views.py:99 -msgid "Default view has been reset to default." -msgstr "La vista predefinita è stata modificata a predefinita." +#~ msgid "Image" +#~ msgstr "Immagine" -#: kotti/views/edit/default_views.py:78 -msgid "Default view" -msgstr "Vista predefinita" - -#: kotti/views/edit/default_views.py:106 -msgid "Default view has been set." -msgstr "E' stata selezionata la vista predefinita." - -#: kotti/views/edit/default_views.py:111 -msgid "Default view could not be set." -msgstr "La vista predefinita non può essere selezionata." +#, fuzzy +#~ msgid "" +#~ "\n" +#~ "

    Log in

    \n" +#~ "

    \n" +#~ " You can log in to your site\n" +#~ " and start changing its contents. If you haven't chosen a password for\n" +#~ " your admin account yet, it'll likely be qwerty.\n" +#~ "

    \n" +#~ "

    \n" +#~ " Once you're logged in, you'll see the grey editor bar below the top\n" +#~ " navigation bar. It will allow you to switch between editing and viewing the\n" +#~ " current page as it will appear to your visitors.\n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Configure

    \n" +#~ "

    \n" +#~ " Find out how to configure your Kotti's title and many other settings using a\n" +#~ " simple text file in your file system.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Configuration manual\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Add-ons

    \n" +#~ "

    \n" +#~ " A number of add-ons allow you to extend the functionality of your Kotti site.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Kotti add-ons\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Documentation

    \n" +#~ "

    \n" +#~ " Wonder what more you can do with Kotti? What license it has? Read the\n" +#~ " manual for more information.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Documentation\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ msgstr "" +#~ "\n" +#~ "

    Accedi

    \n" +#~ "

    \n" +#~ " Accedi al sito \n" +#~ " e inizia a modificare i suoi contenuti. Se non hai ancora scelto una password per\n" +#~ " il tuo utente amministratore, allora sarà probabilemente qwerty.\n" +#~ "

    \n" +#~ "

    \n" +#~ " Quando sarai autenticato, vedrai la barra di editor grigia sotto la barra di navigazione in alto.\n" +#~ " Questa barra ti permetterà di passare dalla modifica alla visualizzazione della pagina corrente così come apparirà a tuoi visitatori.\n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Configura

    \n" +#~ "

    \n" +#~ " Scopri come configurare il titolo del tuo Kotti e molte altre impostazion utilizzando un semplice file di testo sul vostro sistema.\n" +#~ "

    \n" +#~ "

    \n" +#~ " Manuale di configurazione\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Add-ons

    \n" +#~ "

    \n" +#~ " Esistono molti add-ons che permettono di estendere le funzionalità del tuo sito Kotti

    \n" +#~ "

    \n" +#~ " \n" +#~ " Add-ons di Kotti\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Documentazione

    \n" +#~ "

    \n" +#~ " Ti chiedi cos'altro potresti fare con Kotti? Quale licenza ha? Leggi il manuale per maggiori informazioni

    \n" +#~ "

    \n" +#~ " \n" +#~ " Documentazione\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" + +#~ msgid "Submit" +#~ msgstr "Invia" #~ msgid "Private" #~ msgstr "Privato" diff --git a/kotti/locale/ja/LC_MESSAGES/Kotti.mo b/kotti/locale/ja/LC_MESSAGES/Kotti.mo index ddfb26549..f0035d143 100644 Binary files a/kotti/locale/ja/LC_MESSAGES/Kotti.mo and b/kotti/locale/ja/LC_MESSAGES/Kotti.mo differ diff --git a/kotti/locale/ja/LC_MESSAGES/Kotti.po b/kotti/locale/ja/LC_MESSAGES/Kotti.po index 4b0403afc..9f85dd09a 100644 --- a/kotti/locale/ja/LC_MESSAGES/Kotti.po +++ b/kotti/locale/ja/LC_MESSAGES/Kotti.po @@ -1,61 +1,113 @@ -# Japanese translations for Kotti. -# Copyright (C) 2012 ORGANIZATION -# This file is distributed under the same license as the Kotti project. -# OCHIAI, Gouji , 2012-2013. -# MORIMOTO, Tetsuya , 2012. -# msgid "" msgstr "" -"Project-Id-Version: Kotti 0.7.0\n" -"Report-Msgid-Bugs-To: kotti@googlegroups.com\n" -"POT-Creation-Date: 2014-11-21 21:56+0100\n" -"PO-Revision-Date: 2013-09-19 02:45+0900\n" -"Last-Translator: OCHIAI, Gouji \n" -"Language-Team: ja \n" -"Language: \n" +"POT-Creation-Date: 2018-01-17 13:51+0100\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0\n" -"Generated-By: Babel 0.9.6\n" -#: kotti/populate.py:64 +#: kotti/security.py:173 +msgid "Viewer" +msgstr "閲覧者" + +#: kotti/security.py:174 +msgid "Editor" +msgstr "編集者" + +#: kotti/security.py:175 +msgid "Owner" +msgstr "所有者" + +#: kotti/security.py:176 +msgid "Admin" +msgstr "管理者" + +#: kotti/resources.py:541 kotti/views/edit/actions.py:453 +msgid "Copy" +msgstr "コピー" + +#: kotti/resources.py:542 kotti/views/edit/actions.py:454 +msgid "Cut" +msgstr "切り取り" + +#: kotti/resources.py:543 kotti/views/edit/actions.py:450 +msgid "Paste" +msgstr "貼り付け" + +#: kotti/resources.py:544 kotti/templates/edit/rename.pt:31 +#: kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:42 kotti/views/edit/actions.py:455 +msgid "Rename" +msgstr "名称変更" + +#: kotti/resources.py:545 kotti/templates/edit/delete.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:49 +#: kotti/templates/site-setup/delete-user.pt:24 kotti/views/users.py:454 +#: kotti/views/edit/actions.py:457 +msgid "Delete" +msgstr "削除" + +#: kotti/resources.py:556 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "コンテンツ" + +#: kotti/resources.py:557 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "編集" + +#: kotti/resources.py:558 +msgid "Share" +msgstr "共有" + +#: kotti/resources.py:559 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "アクション" + +#: kotti/resources.py:562 +msgid "Folder view" +msgstr "フォルダー表示" + +#: kotti/resources.py:671 kotti/views/edit/content.py:61 +msgid "Document" +msgstr "ドキュメント" + +#: kotti/resources.py:808 kotti/views/edit/content.py:73 +#: kotti/views/edit/content.py:87 +msgid "File" +msgstr "ファイル" + +#: kotti/populate.py:67 msgid "Welcome to Kotti" -msgstr "" +msgstr "Kotti へようこそ" -#: kotti/populate.py:65 +#: kotti/populate.py:68 msgid "Congratulations! You have successfully installed Kotti." -msgstr "" +msgstr "おめでとうございます!Kotti のインストールに成功しました!" -#: kotti/populate.py:66 +#: kotti/populate.py:69 #, c-format msgid "" "\n" "

    Log in

    \n" "

    \n" -" You can log in to your " -"site\n" +" You can log in to your site\n" " and start changing its contents. If you haven't chosen a password for\n" " your admin account yet, it'll likely be qwerty.\n" "

    \n" "

    \n" " Once you're logged in, you'll see the grey editor bar below the top\n" -" navigation bar. It will allow you to switch between editing and viewing " -"the\n" +" navigation bar. It will allow you to switch between editing and viewing the\n" " current page as it will appear to your visitors.\n" "

    \n" "
    \n" "
    \n" "

    Configure

    \n" "

    \n" -" Find out how to configure your Kotti's title and many other " -"settings using a\n" -" simple text file in your file system.\n" +" Find out how to configure your Kotti's title and many other\n" +" settings using a simple text file in your file system.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -63,13 +115,12 @@ msgid "" "
    \n" "

    Add-ons

    \n" "

    \n" -" A number of add-ons allow you to extend the functionality of " -"your Kotti site.\n" +" A number of add-ons allow you to extend the functionality of your\n" +" Kotti site.\n" "

    \n" "

    \n" " \n" +" href=\"http://pypi.python.org/pypi?%3Aaction=search&term=kotti\">\n" " Kotti add-ons\n" " \n" "

    \n" @@ -77,37 +128,84 @@ msgid "" "
    \n" "

    Documentation

    \n" "

    \n" -" Wonder what more you can do with Kotti? What license it has? " -"Read the\n" -" manual for more information.\n" +" Wonder what more you can do with Kotti?\n" +" What license it has?\n" +" Read the manual for more information.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/\">\n" " Documentation\n" " \n" "

    \n" "
    \n" "
    \n" msgstr "" +"\n" +"

    ログイン

    \n" +"

    \n" +" サイトに ログイン してコンテンツを変更しながら始めます。\n" +" admin ユーザーのパスワードを変更していないなら、パスワードは qwerty でログインできるでしょう。\n" +"

    \n" +"

    \n" +" ログインすると、上部のナビゲーションバーの下に灰色のエディターバーがあります。\n" +" エディターバーを使って現在のページの編集モードと訪問者に表示される閲覧モードを切り替えます。\n" +"

    \n" +"
    \n" +"
    \n" +"

    設定

    \n" +"

    \n" +" ファイルシステム上のテキストファイルを使って Kotti のタイトルやその他多くの項目の設定方法を調べます。\n" +"

    \n" +"

    \n" +" \n" +" 設定マニュアル\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    アドオン

    \n" +"

    \n" +" Kotti の機能を拡張する数多くのアドオンがあります。\n" +"

    \n" +"

    \n" +" \n" +" Kotti アドオン\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    ドキュメント

    \n" +"

    \n" +" Kotti で何ができるか知りたくないですか?\n" +" どんなラインセンスなんだろう?\n" +" 詳細はマニュアルを読んでください。\n" +"

    \n" +"

    \n" +" \n" +" ドキュメント\n" +" \n" +"

    \n" +"
    \n" +"
    \n" -#: kotti/populate.py:121 +#: kotti/populate.py:126 msgid "About" -msgstr "" +msgstr "概要" -#: kotti/populate.py:122 -msgid "" -"Our company is the leading manufacturer of foo widgets used in a wide " -"variety of aviation and and industrial products." -msgstr "" +#: kotti/populate.py:127 +msgid "Our company is the leading manufacturer of foo widgets used in a wide variety of aviation and and industrial products." +msgstr "当社は航空および工業製品に幅広く利用されている foo ウィジェットの大手製造業者です。" -#: kotti/populate.py:123 +#: kotti/populate.py:128 msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -124,792 +222,731 @@ msgid "" "\n" "\n" "

    \n" -" Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" -" \n" -" Copyright info.\n" -" Originally published in the\n" -" Extra EA-300\n" -" article.\n" +"Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +"\n" +"Copyright info.\n" +"Originally published in the\n" +" Extra EA-300\n" +"article.\n" "

    \n" msgstr "" +"\n" +"

    \n" +" \"five\n" +"

    \n" +"\n" +"

    \n" +" 本社所在地:\n" +"

    \n" +"\n" +"
    \n" +" Foo World
    \n" +" 123 Nowhere Street, Suite 777
    \n" +" Omak, WA 98841 USA
    \n" +" +1-509-555-0100
    \n" +" widgets@foowrld.example.com\n" +"
    \n" +"\n" +"

    \n" +"写真の著作権: FlugKerl2 氏の \"Northern Lights Formation\"\n" +"\n" +"著作権に関する情報 は\n" +" Extra EA-300 記事で公開されています。\n" +"

    \n" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 -msgid "Document" -msgstr "ドキュメント" +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "ようこそ ${user}! ログインしています。" -#: kotti/resources.py:649 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 -msgid "File" -msgstr "ファイル" +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "ようこそ。ログインしていません。" -#: kotti/resources.py:726 kotti/views/edit/content.py:144 -msgid "Image" -msgstr "画像" +#: kotti/templates/editor-bar.pt:19 +msgid "View" +msgstr "表示" -#: kotti/resources.py:500 -msgid "Folder view" -msgstr "フォルダー表示" +#: kotti/templates/editor-bar.pt:42 +msgid "Navigate" +msgstr "ナビゲート" -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 -msgid "Contents" -msgstr "コンテンツ" +#: kotti/templates/editor-bar.pt:53 +msgid "Preferences" +msgstr "設定" -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" -msgstr "編集" +#: kotti/templates/editor-bar.pt:58 +msgid "Site Setup" +msgstr "サイト設定" -#: kotti/resources.py:489 -msgid "Share" -msgstr "共有" +#: kotti/templates/editor-bar.pt:69 +msgid "Logout" +msgstr "ログアウト" -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" -msgstr "アクション" +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "表示方法の設定" -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 -msgid "Copy" -msgstr "コピー" +#: kotti/templates/login.pt:16 +msgid "Login" +msgstr "ログイン" -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 -msgid "Cut" -msgstr "切り取り" +#: kotti/templates/login.pt:21 kotti/templates/login.pt:59 +msgid "Username or email" +msgstr "ユーザー名またはメールアドレス" -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 -msgid "Paste" -msgstr "貼り付け" +#: kotti/templates/login.pt:26 kotti/views/users.py:220 +#: kotti/views/login.py:255 +msgid "Password" +msgstr "パスワード" -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 -#: kotti/templates/edit/rename-nodes.pt:55 kotti/templates/edit/rename.pt:42 -#: kotti/views/edit/actions.py:447 -msgid "Rename" -msgstr "名称変更" +#: kotti/templates/login.pt:34 +msgid "Log in" +msgstr "ログイン" -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 -#: kotti/templates/edit/delete-nodes.pt:90 kotti/templates/edit/delete.pt:28 -#: kotti/templates/site-setup/delete-user.pt:36 kotti/views/users.py:443 -#: kotti/views/edit/actions.py:449 -msgid "Delete" -msgstr "削除" +#: kotti/templates/login.pt:45 +msgid "Forgot your password?" +msgstr "パスワードを忘れた?" -#: kotti/security.py:165 -msgid "Viewer" -msgstr "閲覧者" +#. Used in sentence: "Fill out your username or email and click +#. ${reset_password} below to receive an email with a link to reset your +#. password." +#. +#: kotti/templates/login.pt:52 kotti/templates/login.pt:67 +msgid "Reset password" +msgstr "パスワードをリセット" -#: kotti/security.py:166 -msgid "Editor" -msgstr "編集者" +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:50 +msgid "Fill out your username or email and click ${reset_password} below to receive an email with a link to reset your password." +msgstr "ユーザー名かメールアドレスを入力して、下にある ${reset_password} をクリックしてください。パスワードをリセットするためのリンクが書かれたメールが届きます。" -#: kotti/security.py:167 -msgid "Owner" -msgstr "所有者" +#: kotti/templates/login.pt:78 +msgid "Not registered yet?" +msgstr "まだ登録していませんか?" -#: kotti/security.py:168 -msgid "Admin" -msgstr "管理者" +#: kotti/templates/login.pt:85 +msgid "Register for an account on this site." +msgstr "このサイトにアカウントを登録しましょう。" -#: kotti/templates/add-dropdown.pt:5 +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "表示権限がありません" + +#: kotti/templates/add-dropdown.pt:3 msgid "Add" msgstr "追加" -#: kotti/templates/add-dropdown.pt:27 +#: kotti/templates/add-dropdown.pt:13 msgid "Upload Content" msgstr "アップロード" -#: kotti/templates/default-view-selector.pt:5 -msgid "Set default view" -msgstr "表示方法の設定" - -#: kotti/templates/editor-bar.pt:35 -msgid "View" -msgstr "表示" - -#: kotti/templates/editor-bar.pt:76 -msgid "Navigate" -msgstr "ナビゲート" - -#: kotti/templates/editor-bar.pt:98 -msgid "Preferences" -msgstr "設定" - -#: kotti/templates/editor-bar.pt:109 -msgid "Site Setup" -msgstr "サイト設定" - -#: kotti/templates/editor-bar.pt:131 -msgid "Logout" -msgstr "ログアウト" +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "${state} にする" -#: kotti/templates/email-reset-password.pt:3 +#: kotti/templates/email-reset-password.pt:2 msgid "Reset your password for ${site_title}." msgstr "${site_title} のパスワードをリセット" -#: kotti/templates/email-reset-password.pt:7 -#: kotti/templates/email-set-password.pt:6 +#: kotti/templates/email-reset-password.pt:4 +#: kotti/templates/email-set-password.pt:4 msgid "Hello, ${user_title}!" msgstr "こんにちは ${user_title}!" -#: kotti/templates/email-reset-password.pt:10 +#: kotti/templates/email-reset-password.pt:5 msgid "Click this link to reset your password at ${site_title}: ${url}" -msgstr "" -"${site_title} のパスワードをリセットするにはこのリンクをクリックしてくださ" -"い: ${url}" +msgstr "${site_title} のパスワードをリセットするにはこのリンクをクリックしてください: ${url}" -#: kotti/templates/email-set-password.pt:3 +#: kotti/templates/email-set-password.pt:2 msgid "Your registration for ${site_title}" msgstr "${site_title} へのアカウント登録" -#: kotti/templates/email-set-password.pt:9 +#: kotti/templates/email-set-password.pt:5 msgid "You are joining ${site_title}." msgstr "あなたは ${site_title} に参加しようとしています。" -#: kotti/templates/email-set-password.pt:12 +#: kotti/templates/email-set-password.pt:6 msgid "Click here to set your password and log in: ${url}" -msgstr "" -"パスワードをセットしてログインするにはこのリンクをクリックしてください: " -"${url}" +msgstr "パスワードをセットしてログインするにはこのリンクをクリックしてください: ${url}" -#: kotti/templates/forbidden.pt:7 -msgid "Forbidden" -msgstr "表示権限がありません" +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" +msgstr "${title} を共有" -#: kotti/templates/login.pt:16 -msgid "Login" -msgstr "ログイン" +#: kotti/templates/edit/share.pt:14 kotti/templates/edit/share.pt:20 +#: kotti/templates/edit/share.pt:27 kotti/templates/site-setup/users.pt:48 +msgid "Search users and groups" +msgstr "ユーザーとグループを検索" -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 -msgid "Username or email" -msgstr "ユーザー名またはメールアドレス" +#: kotti/templates/edit/share.pt:34 kotti/templates/site-setup/users.pt:56 +msgid "Search" +msgstr "検索" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 -#: kotti/views/users.py:212 -msgid "Password" -msgstr "パスワード" +#: kotti/templates/edit/share.pt:44 +msgid "Assign local roles" +msgstr "ローカルロールを指定する" -#: kotti/templates/login.pt:47 -msgid "Log in" -msgstr "ログイン" +#: kotti/templates/edit/share.pt:49 kotti/templates/edit/change-state.pt:19 +#: kotti/templates/edit/delete-nodes.pt:22 kotti/templates/edit/upload.pt:18 +#: kotti/templates/edit/upload.pt:108 kotti/templates/edit/contents.pt:35 +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:69 +msgid "Type" +msgstr "種類" -#: kotti/templates/login.pt:65 -msgid "Forgot your password?" -msgstr "パスワードを忘れた?" +#: kotti/templates/edit/share.pt:50 kotti/templates/site-setup/users.pt:70 +#: kotti/views/users.py:215 +msgid "Name" +msgstr "名称" -#. Used in sentence: "Fill out your username or email and click -#. ${reset_password} below to receive an email with a link to reset your -#. password." -#. -#: kotti/templates/login.pt:77 kotti/templates/login.pt:100 -msgid "Reset password" -msgstr "パスワードをリセット" +#: kotti/templates/edit/share.pt:61 kotti/templates/site-setup/users.pt:81 +#: kotti/views/users.py:321 kotti/views/users.py:515 kotti/views/users.py:550 +msgid "User" +msgstr "ユーザー" -#. Canonical text for ${reset_password} is: "Reset password" -#: kotti/templates/login.pt:74 -#, fuzzy -msgid "" -"Fill out your username or email and click ${reset_password} below to receive " -"an email with a link to reset your password." -msgstr "" -"上の入力欄のユーザー名かメールアドレスを埋めて、下にある ${reset_password} を" -"クリックしてください。パスワードをリセットするためのリンクが書かれたメールが" -"送信されます。" +#: kotti/templates/edit/share.pt:62 kotti/templates/site-setup/users.pt:82 +#: kotti/views/users.py:196 kotti/views/users.py:356 kotti/views/users.py:515 +#: kotti/views/users.py:550 +msgid "Group" +msgstr "グループ" -#: kotti/templates/login.pt:118 -msgid "Not registered yet?" -msgstr "" +#: kotti/templates/edit/share.pt:65 kotti/templates/site-setup/users.pt:85 +msgid "Gravatar" +msgstr "Gravatar" -#: kotti/templates/login.pt:127 -#, fuzzy -msgid "Register for an account on this site." -msgstr "" -"まだ登録していませんか? このサイトにアカウントを ${register} しましょう。" +#: kotti/templates/edit/share.pt:70 kotti/templates/site-setup/users.pt:91 +msgid "Assign role" +msgstr "ロールを指定する" -#: kotti/templates/workflow-dropdown.pt:19 -msgid "Make ${state}" -msgstr "${state} にする" +#: kotti/templates/edit/share.pt:85 kotti/templates/edit/change-state.pt:72 +#: kotti/templates/site-setup/users.pt:107 +msgid "Apply changes" +msgstr "変更を適用" + +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "ナビゲート" -#: kotti/templates/edit/breadcrumbs.pt:5 +#: kotti/templates/edit/breadcrumbs.pt:8 msgid "You are here:" msgstr "今の場所:" -#: kotti/templates/edit/change-state.pt:7 +#: kotti/templates/edit/delete.pt:9 kotti/templates/site-setup/delete-user.pt:9 +msgid "Delete ${title}" +msgstr "${title} を削除する" + +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" +msgstr "本当に ${title} を削除しますか?" + +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:74 +#: kotti/templates/edit/delete-nodes.pt:44 +#: kotti/templates/edit/rename-nodes.pt:44 kotti/views/users.py:324 +#: kotti/views/users.py:359 kotti/views/users.py:453 kotti/views/form.py:81 +msgid "Cancel" +msgstr "キャンセル" + +#: kotti/templates/edit/change-state.pt:8 msgid "Change workflow state" msgstr "ワークフローの状態を変更" -#: kotti/templates/edit/change-state.pt:14 +#: kotti/templates/edit/change-state.pt:12 msgid "An item's workflow state determines who can see, edit and/or manage it." -msgstr "" -"アイテムを誰が閲覧できるか、編集できるか、管理できるかといった、ワークフロー" -"の状態を変更します。" +msgstr "アイテムを誰が閲覧できるか、編集できるか、管理できるかといった、ワークフローの状態を変更します。" -#: kotti/templates/edit/change-state.pt:26 kotti/templates/edit/contents.pt:57 -#: kotti/templates/edit/delete-nodes.pt:33 kotti/templates/view/folder.pt:28 -#: kotti/views/users.py:196 kotti/views/edit/content.py:31 +#: kotti/templates/edit/change-state.pt:18 +#: kotti/templates/edit/delete-nodes.pt:21 kotti/templates/edit/contents.pt:34 +#: kotti/templates/view/folder.pt:20 kotti/views/users.py:204 +#: kotti/views/edit/content.py:28 msgid "Title" msgstr "タイトル" -#: kotti/templates/edit/change-state.pt:29 kotti/templates/edit/contents.pt:60 -#: kotti/templates/edit/delete-nodes.pt:36 kotti/templates/edit/share.pt:70 -#: kotti/templates/edit/upload.pt:23 kotti/templates/edit/upload.pt:167 -#: kotti/templates/site-setup/users.pt:105 kotti/templates/view/folder.pt:31 -msgid "Type" -msgstr "種類" - -#: kotti/templates/edit/change-state.pt:32 kotti/templates/edit/contents.pt:63 -#: kotti/templates/edit/delete-nodes.pt:39 +#: kotti/templates/edit/change-state.pt:20 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:36 msgid "State" msgstr "状態" -#: kotti/templates/edit/change-state.pt:35 kotti/templates/edit/contents.pt:69 -#: kotti/templates/edit/delete-nodes.pt:42 kotti/templates/view/folder.pt:34 +#: kotti/templates/edit/change-state.pt:21 +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:38 +#: kotti/templates/view/folder.pt:22 msgid "Creation Date" msgstr "作成日" -#: kotti/templates/edit/change-state.pt:38 kotti/templates/edit/contents.pt:72 -#: kotti/templates/edit/delete-nodes.pt:45 kotti/templates/view/folder.pt:37 +#: kotti/templates/edit/change-state.pt:22 +#: kotti/templates/edit/delete-nodes.pt:25 kotti/templates/edit/contents.pt:39 +#: kotti/templates/view/folder.pt:23 msgid "Modification Date" msgstr "変更日" -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 +#: kotti/templates/edit/change-state.pt:28 +#: kotti/templates/edit/delete-nodes.pt:31 kotti/templates/edit/contents.pt:45 +msgid "Select item" +msgstr "アイテムを選択する" + +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:48 msgid "Include children" msgstr "配下のアイテムを含める" -#: kotti/templates/edit/change-state.pt:82 -msgid "" -"If checked, this will attempt to modify the status of all children in any " -"selected documents and their subdocuments." -msgstr "" -"チェックすると、状態の変更が選択したドキュメントや、その配下のドキュメントの" -"すべての配下のアイテムに適用されます。" +#: kotti/templates/edit/change-state.pt:41 +msgid "If checked, this will attempt to modify the status of all children in any selected documents and their subdocuments." +msgstr "チェックすると、状態の変更が選択したドキュメントや、その配下のドキュメントのすべての配下のアイテムに適用されます。" -#: kotti/templates/edit/change-state.pt:100 +#: kotti/templates/edit/change-state.pt:54 msgid "Change state to" msgstr "変更後の状態" -#: kotti/templates/edit/change-state.pt:103 +#: kotti/templates/edit/change-state.pt:55 msgid "Select the new state where all chosen items should be set." msgstr "新しく設定する状態を選択して下さい。" -#: kotti/templates/edit/change-state.pt:111 +#: kotti/templates/edit/change-state.pt:60 msgid "No change" msgstr "変更しない" -#: kotti/templates/edit/change-state.pt:127 kotti/templates/edit/share.pt:121 -#: kotti/templates/site-setup/users.pt:165 -msgid "Apply changes" -msgstr "変更を適用" - -#: kotti/templates/edit/change-state.pt:130 -#: kotti/templates/edit/delete-nodes.pt:93 -#: kotti/templates/edit/rename-nodes.pt:58 kotti/views/form.py:78 -#: kotti/views/users.py:316 kotti/views/users.py:350 kotti/views/users.py:442 -msgid "Cancel" -msgstr "キャンセル" - -#: kotti/templates/edit/contents.pt:43 -msgid "No content items are contained here." -msgstr "ここにはコンテンツアイテムが含まれていません。" - -#: kotti/templates/edit/contents.pt:66 -msgid "Visibility" -msgstr "表示状態" - -#: kotti/templates/edit/contents.pt:125 -msgid "Visible" -msgstr "表示中" - -#: kotti/templates/edit/contents.pt:134 -msgid "Hidden" -msgstr "非表示" - -#: kotti/templates/edit/delete-nodes.pt:17 +#: kotti/templates/edit/delete-nodes.pt:13 msgid "Are you sure you want to delete the following items?" msgstr "本当に以下のアイテムを削除しますか?" -#: kotti/templates/edit/delete.pt:8 -#: kotti/templates/site-setup/delete-user.pt:8 -msgid "Delete ${title}" -msgstr "${title} を削除する" - -#: kotti/templates/edit/delete.pt:20 -msgid "Are you sure you want to delete ${title}?" -msgstr "本当に ${title} を削除しますか?" - -#: kotti/templates/edit/nav-tree-view.pt:7 -msgid "Navigate Site" -msgstr "ナビゲート" +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "${title} の名称変更" -#: kotti/templates/edit/rename-nodes.pt:16 kotti/templates/edit/rename.pt:14 -msgid "" -"Each content item has a name, which is used to create the url, and a title. " -"Read the user manual about proper formatting of name and title if needed. " -"You can change both below." -msgstr "" -"それぞれのコンテンツには名称とタイトルがあり、名称は URL を生成するのに使用し" -"ます。下のフォームでは、その両方を変更することができます。" +#: kotti/templates/edit/rename.pt:11 kotti/templates/edit/rename-nodes.pt:13 +msgid "Each content item has a name, which is used to create the url, and a title. Read the user manual about proper formatting of name and title if needed. You can change both below." +msgstr "それぞれのコンテンツには名称とタイトルがあり、名称は URL を生成するのに使用します。下のフォームでは、その両方を変更することができます。" -#: kotti/templates/edit/rename-nodes.pt:35 kotti/templates/edit/rename.pt:26 +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +#: kotti/templates/edit/rename-nodes.pt:28 msgid "New name" msgstr "新しい名称" -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:34 +#: kotti/templates/edit/rename-nodes.pt:36 msgid "New title" msgstr "新しいタイトル" -#: kotti/templates/edit/rename.pt:7 -msgid "Rename ${title}" -msgstr "${title} の名称変更" - -#: kotti/templates/edit/share.pt:7 -msgid "Share ${title}" -msgstr "${title} を共有" - -#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:30 -#: kotti/templates/edit/share.pt:35 kotti/templates/site-setup/users.pt:68 -msgid "Search users and groups" -msgstr "ユーザーとグループを検索" - -#: kotti/templates/edit/share.pt:43 kotti/templates/site-setup/users.pt:81 -msgid "Search" -msgstr "検索" - -#: kotti/templates/edit/share.pt:60 -msgid "Assign local roles" -msgstr "ローカルロールに割り当て" - -#: kotti/templates/edit/share.pt:73 kotti/templates/site-setup/users.pt:108 -#: kotti/views/users.py:207 -msgid "Name" -msgstr "名称" - -#: kotti/templates/edit/share.pt:91 kotti/templates/site-setup/users.pt:126 -#: kotti/views/users.py:313 kotti/views/users.py:501 kotti/views/users.py:536 -msgid "User" -msgstr "ユーザー" - -#: kotti/templates/edit/share.pt:94 kotti/templates/site-setup/users.pt:129 -#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 -#: kotti/views/users.py:536 -msgid "Group" -msgstr "グループ" - -#: kotti/templates/edit/share.pt:101 kotti/templates/site-setup/users.pt:136 -msgid "Gravatar" -msgstr "Gravatar" - -#: kotti/templates/edit/upload.pt:9 +#: kotti/templates/edit/upload.pt:10 msgid "Upload content from local file(s)" -msgstr "" +msgstr "ローカルファイルからコンテンツをアップロードする" -#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:164 -#, fuzzy +#: kotti/templates/edit/upload.pt:17 kotti/templates/edit/upload.pt:107 msgid "Filename" -msgstr "名称変更" +msgstr "ファイル名" -#: kotti/templates/edit/upload.pt:26 kotti/templates/edit/upload.pt:170 +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:109 msgid "Size" -msgstr "" +msgstr "サイズ" -#: kotti/templates/edit/upload.pt:29 +#: kotti/templates/edit/upload.pt:20 msgid "Status" -msgstr "" +msgstr "ステータス" -#: kotti/templates/edit/upload.pt:32 +#: kotti/templates/edit/upload.pt:21 msgid "Progress" -msgstr "" +msgstr "進捗" -#: kotti/templates/edit/upload.pt:124 +#: kotti/templates/edit/upload.pt:84 msgid "Select file(s) to upload..." -msgstr "" +msgstr "アップロードするファイルを選択する ..." -#: kotti/templates/edit/upload.pt:134 +#: kotti/templates/edit/upload.pt:92 msgid "Upload ${number} files." -msgstr "" +msgstr "${number} 個のファイルをアップロードする。" -#: kotti/templates/edit/upload.pt:144 +#: kotti/templates/edit/upload.pt:97 msgid "Dismiss all errors" -msgstr "" +msgstr "全てのエラーを非表示にする" + +#: kotti/templates/edit/contents.pt:26 +msgid "No content items are contained here." +msgstr "ここにはコンテンツアイテムが含まれていません。" + +#: kotti/templates/edit/contents.pt:33 +msgid "Select / deselect all" +msgstr "全選択 / 全解除" + +#: kotti/templates/edit/contents.pt:37 +msgid "Visibility" +msgstr "表示状態" + +#: kotti/templates/edit/contents.pt:69 +msgid "Visible" +msgstr "表示中" + +#: kotti/templates/edit/contents.pt:73 +msgid "Hidden" +msgstr "非表示" + +#: kotti/templates/http-errors/notfound.pt:26 +msgid "Not Found" +msgstr "存在しません" + +#: kotti/templates/http-errors/notfound.pt:27 +msgid "The resource could not be found" +msgstr "リソースが見つかりません" + +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "検索結果" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "検索..." + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "付与されたタグ:" -#: kotti/templates/site-setup/delete-user.pt:20 +#: kotti/templates/site-setup/delete-user.pt:15 msgid "Are you sure you want to delete ${type} ${title}?" msgstr "本当に ${type} ${title} を削除しますか?" -#: kotti/templates/site-setup/user.pt:13 +#: kotti/templates/site-setup/user.pt:12 msgid "Back to User Management" msgstr "ユーザー管理に戻る" -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 +#: kotti/templates/site-setup/user.pt:21 kotti/views/form.py:159 #, python-format msgid "Edit ${title}" msgstr "${title} を編集" -#: kotti/templates/site-setup/users.pt:15 -#, fuzzy +#: kotti/templates/site-setup/users.pt:12 msgid "Search user(s) / group(s)" msgstr "ユーザーとグループを検索" -#: kotti/templates/site-setup/users.pt:24 +#: kotti/templates/site-setup/users.pt:17 msgid "Add user" msgstr "ユーザーを追加" -#: kotti/templates/site-setup/users.pt:33 +#: kotti/templates/site-setup/users.pt:22 msgid "Add group" msgstr "グループを追加" -#: kotti/templates/site-setup/users.pt:52 -#, fuzzy +#: kotti/templates/site-setup/users.pt:34 msgid "Find users or groups" -msgstr "ユーザーとグループを検索" +msgstr "ユーザーとグループを探す" -#: kotti/templates/site-setup/users.pt:63 +#: kotti/templates/site-setup/users.pt:40 msgid "User- / groupname" -msgstr "" +msgstr "ユーザー名 / グループ名" -#: kotti/templates/site-setup/users.pt:70 -#, fuzzy +#: kotti/templates/site-setup/users.pt:50 msgid "Blank search text finds all." -msgstr "ユーザーを検索して変更する (空文字列での検索で全部表示) 。" +msgstr "空白で検索するとすべて表示します。" -#: kotti/templates/site-setup/users.pt:96 -#, fuzzy +#: kotti/templates/site-setup/users.pt:65 msgid "Assign global roles" -msgstr "ローカルロールに割り当て" +msgstr "グローバルロールを指定する" -#: kotti/templates/site-setup/users.pt:180 -#, fuzzy +#: kotti/templates/site-setup/users.pt:117 msgid "Add new user" msgstr "ユーザーを追加" -#: kotti/templates/site-setup/users.pt:190 -#, fuzzy +#: kotti/templates/site-setup/users.pt:123 msgid "Add new group" msgstr "グループを追加" -#: kotti/templates/view/search-results.pt:8 -msgid "Search Results" -msgstr "検索結果" - -#: kotti/templates/view/search.pt:5 -msgid "Search..." -msgstr "検索..." - -#: kotti/templates/view/tags.pt:5 -msgid "Tagged with:" -msgstr "付与されたタグ:" - -#: kotti/tests/testing_view.pt:7 -msgid "Welcome, ${user}! You are logged in." -msgstr "ようこそ ${user}! ログインしています。" - -#: kotti/tests/testing_view.pt:14 -msgid "Welcome, you are not logged in." -msgstr "ようこそ。ログインしていません。" - -#: kotti/views/form.py:79 kotti/views/users.py:69 -#: kotti/views/edit/actions.py:363 kotti/views/edit/actions.py:407 +#: kotti/views/users.py:70 kotti/views/form.py:82 +#: kotti/views/edit/actions.py:371 kotti/views/edit/actions.py:415 msgid "Your changes have been saved." msgstr "変更内容を保存しました。" -#: kotti/views/form.py:174 -msgid "Item was added." -msgstr "${title} を追加しました。" - -#: kotti/views/form.py:264 -#, python-format -msgid "Maximum file size: ${size}MB" -msgstr "最大ファイルサイズ: ${size}MB" - -#: kotti/views/form.py:77 kotti/views/users.py:441 -msgid "Save" -msgstr "保存" - -#: kotti/views/form.py:198 -#, python-format -msgid "Add ${type} to ${title}." -msgstr "${title} に ${type} を追加する。" - -#: kotti/views/form.py:202 -#, python-format -msgid "Add ${type}." -msgstr "${type} を追加する。" - -#: kotti/views/login.py:238 -msgid "You have reset your password." -msgstr "パスワードのリセットに成功しました。" - -#: kotti/views/login.py:200 -msgid "You have been logged out." -msgstr "ログアウトしました。" - -#: kotti/views/login.py:66 kotti/views/users.py:270 -msgid "Full name" -msgstr "フルネーム" - -#: kotti/views/login.py:69 -msgid "Username" -msgstr "ユーザー名" - -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 -msgid "Email" -msgstr "メールアドレス" - -#: kotti/views/login.py:109 -msgid "" -"Congratulations! You are successfully registered. You should be receiving an " -"email with a link to set your password. Doing so will activate your account." -msgstr "" -"おめでとうございます! 登録が完了しました。Eメールのリンクから、すぐにパスワー" -"ドを設定して下さい。" - -#: kotti/views/login.py:124 -#, python-format -msgid "Register - ${title}" -msgstr "登録 - ${title}" - -#: kotti/views/login.py:164 -msgid "Login failed." -msgstr "ログインに失敗しました。" - -#: kotti/views/login.py:286 -#, python-format -msgid "Reset your password - ${title}." -msgstr "パスワードのリセット - ${title}" - -#: kotti/views/login.py:160 -#, python-format -msgid "Welcome, ${user}!" -msgstr "ようこそ ${user}!" - -#: kotti/views/login.py:173 -msgid "" -"You should be receiving an email with a link to reset your password. Doing " -"so will activate your account." -msgstr "" -"まもなくパスワードをリセットするためのリンクが記載されたメールが送信されま" -"す。" - -#: kotti/views/login.py:178 -msgid "That username or email is not known by this system." -msgstr "ユーザー名もメールアドレスも見つかりませんでした。" - -#: kotti/views/login.py:83 -msgid "Register" -msgstr "登録" - -#: kotti/views/login.py:90 kotti/views/login.py:257 -msgid "There was an error." -msgstr "エラーが発生しました。" - -#: kotti/views/login.py:250 -msgid "Submit" -msgstr "送信" - -#: kotti/views/login.py:279 -msgid "Your password reset token may have expired." -msgstr "パスワードをリセットするためのトークンが期限切れです。" - -#: kotti/views/site_setup.py:6 kotti/views/users.py:378 -msgid "User Management" -msgstr "ユーザー管理" - -#: kotti/views/users.py:267 -msgid "" -"Leave this empty and tick the 'Send password registration' box below to have " -"the user set their own password." -msgstr "" -"この項目を空にして下の 'パスワード登録を送信する' をチェックすると、ユーザー" -"自身にパスワードを設定させられます。" +#: kotti/views/users.py:72 kotti/views/users.py:477 +#: kotti/views/edit/actions.py:309 kotti/views/edit/actions.py:375 +#: kotti/views/edit/actions.py:417 kotti/views/edit/actions.py:421 +msgid "No changes were made." +msgstr "変更箇所がありませんでした。" -#: kotti/views/users.py:587 -#, python-format -msgid "My preferences - ${title}" -msgstr "個人設定 - ${title}" +#: kotti/views/users.py:105 +msgid "No users or groups were found." +msgstr "ユーザーもグループも見つかりませんでした。" -#: kotti/views/users.py:145 +#: kotti/views/users.py:153 msgid "Invalid value" msgstr "無効な値" -#: kotti/views/users.py:151 +#: kotti/views/users.py:159 msgid "A user with that name already exists." msgstr "同じ名称のユーザーがすでに存在します。" -#: kotti/views/users.py:158 +#: kotti/views/users.py:166 msgid "A user with that email already exists." msgstr "そのメールアドレスをもつユーザーがすでに存在します。" -#: kotti/views/users.py:181 +#: kotti/views/users.py:189 #, python-format msgid "No such group: ${group}" msgstr "グループが見つかりません: ${group}" -#: kotti/views/users.py:219 +#: kotti/views/users.py:207 kotti/views/login.py:71 kotti/views/login.py:267 +msgid "Email" +msgstr "メールアドレス" + +#: kotti/views/users.py:227 msgid "Active" msgstr "有効" -#: kotti/views/users.py:220 +#: kotti/views/users.py:228 msgid "Untick this to deactivate the account." msgstr "チェックを外すと、このアカウントは無効になります。" -#: kotti/views/users.py:226 +#: kotti/views/users.py:234 msgid "Global roles" msgstr "グローバルロール" -#: kotti/views/users.py:230 +#: kotti/views/users.py:238 msgid "Groups" msgstr "グループ" -#: kotti/views/users.py:315 +#: kotti/views/users.py:275 +msgid "Leave this empty and tick the 'Send password registration' box below to have the user set their own password." +msgstr "この項目を空にして下の 'パスワード登録を送信する' をチェックすると、ユーザー自身にパスワードを設定させられます。" + +#: kotti/views/users.py:278 kotti/views/login.py:63 +msgid "Full name" +msgstr "フルネーム" + +#: kotti/views/users.py:323 msgid "Add User" msgstr "ユーザーを追加" -#: kotti/views/users.py:339 +#: kotti/views/users.py:333 +msgid "Send password registration link." +msgstr "パスワード登録を送信する。" + +#: kotti/views/users.py:348 #, python-format msgid "${title} was added." msgstr "${title} を追加しました。" -#: kotti/views/users.py:349 +#: kotti/views/users.py:358 msgid "Add Group" msgstr "グループ追加" -#: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:301 kotti/views/edit/actions.py:367 -#: kotti/views/edit/actions.py:413 kotti/views/edit/actions.py:409 -msgid "No changes were made." -msgstr "変更箇所がありませんでした。" - -#: kotti/views/users.py:558 -msgid "No name was given." -msgstr "名前がありません。" +#: kotti/views/users.py:388 kotti/views/site_setup.py:5 +msgid "User Management" +msgstr "ユーザー管理" -#: kotti/views/users.py:98 -msgid "No users or groups were found." -msgstr "ユーザーもグループも見つかりませんでした。" +#: kotti/views/users.py:452 kotti/views/form.py:80 +msgid "Save" +msgstr "保存" -#: kotti/views/users.py:505 +#: kotti/views/users.py:519 #, python-format msgid "Edit ${principal_type} ${title}" msgstr "${principal_type} ${title} を編集" -#: kotti/views/users.py:533 +#: kotti/views/users.py:547 msgid "User was not found." msgstr "ユーザーは見つかりませんでした。" -#: kotti/views/users.py:324 -msgid "Send password registration link." -msgstr "パスワード登録を送信する。" - -#: kotti/views/users.py:543 +#: kotti/views/users.py:557 #, python-format msgid "${principal_type} ${title} was deleted." msgstr "${principal_type} ${title} を削除しました。" -#: kotti/views/users.py:551 +#: kotti/views/users.py:565 #, python-format msgid "Delete ${principal_type} ${title}" msgstr "${principal_type} ${title} を削除" -#: kotti/views/edit/actions.py:106 +#: kotti/views/users.py:572 +msgid "No name was given." +msgstr "名前がありません。" + +#: kotti/views/users.py:601 #, python-format -msgid "${title} was copied." -msgstr "${title} をコピーしました。" +msgid "My preferences - ${title}" +msgstr "個人設定 - ${title}" -#: kotti/views/edit/actions.py:124 +#: kotti/views/form.py:177 +msgid "Item was added." +msgstr "アイテムを追加しました。" + +#: kotti/views/form.py:201 #, python-format -msgid "${title} was cut." -msgstr "${title} を切り取りました。" +msgid "Add ${type} to ${title}." +msgstr "${title} に ${type} を追加する。" -#: kotti/views/edit/actions.py:182 +#: kotti/views/form.py:205 #, python-format -msgid "${title} was moved." -msgstr "${title} を移動しました。" +msgid "Add ${type}." +msgstr "${type} を追加する。" -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/form.py:295 #, python-format -msgid "${title} was deleted." -msgstr "${title} を削除しました。" +msgid "Maximum file size: ${size}MB" +msgstr "最大ファイルサイズ: ${size}MB" -#: kotti/views/edit/actions.py:158 +#: kotti/views/login.py:66 +msgid "Username" +msgstr "ユーザー名" + +#: kotti/views/login.py:80 +msgid "Register" +msgstr "登録" + +#: kotti/views/login.py:87 +msgid "There was an error." +msgstr "エラーが発生しました。" + +#: kotti/views/login.py:106 +msgid "Congratulations! You are successfully registered. You should be receiving an email with a link to set your password. Doing so will activate your account." +msgstr "おめでとうございます! 登録が完了しました。Eメールのリンクから、すぐにパスワードを設定して下さい。" + +#: kotti/views/login.py:121 +#, python-format +msgid "Register - ${title}" +msgstr "登録 - ${title}" + +#: kotti/views/login.py:152 +#, python-format +msgid "Welcome, ${user}!" +msgstr "ようこそ ${user}!" + +#: kotti/views/login.py:177 +msgid "You should be receiving an email with a link to reset your password. Doing so will activate your account." +msgstr "まもなくパスワードをリセットするためのリンクが記載されたメールが送信されます。" + +#: kotti/views/login.py:210 +msgid "Login failed." +msgstr "ログインに失敗しました。" + +#: kotti/views/login.py:220 +msgid "That username or email is not known by this system." +msgstr "ユーザー名もメールアドレスも見つかりませんでした。" + +#: kotti/views/login.py:242 +msgid "You have been logged out." +msgstr "ログアウトしました。" + +#: kotti/views/login.py:280 +msgid "You have reset your password." +msgstr "パスワードのリセットに成功しました。" + +#: kotti/views/login.py:299 +msgid "Set password" +msgstr "パスワードを設定" + +#: kotti/views/login.py:327 +msgid "Your password reset token may have expired." +msgstr "パスワードをリセットするためのトークンが期限切れです。" + +#: kotti/views/login.py:334 +#, python-format +msgid "Reset your password - ${title}." +msgstr "パスワードのリセット - ${title}" + +#: kotti/views/edit/default_views.py:86 +msgid "Default view" +msgstr "デフォルト表示" + +#: kotti/views/edit/default_views.py:107 +msgid "Default view has been reset to default." +msgstr "デフォルト表示は既定値にリセットされました。" + +#: kotti/views/edit/default_views.py:114 +msgid "Default view has been set." +msgstr "デフォルト表示が設定されました。" + +#: kotti/views/edit/default_views.py:119 +msgid "Default view could not be set." +msgstr "デフォルト表示は設定できませんでした。" + +#: kotti/views/edit/actions.py:102 +#, python-format +msgid "${title} was copied." +msgstr "${title} をコピーしました。" + +#: kotti/views/edit/actions.py:120 +#, python-format +msgid "${title} was cut." +msgstr "${title} を切り取りました。" + +#: kotti/views/edit/actions.py:155 #, python-format msgid "${title} was pasted." msgstr "${title} を貼り付けました。" -#: kotti/views/edit/actions.py:161 +#: kotti/views/edit/actions.py:158 msgid "Could not paste node. It no longer exists." msgstr "ノードを張り付けできません。何も存在していません。" -#: kotti/views/edit/actions.py:224 +#: kotti/views/edit/actions.py:180 +#, python-format +msgid "${title} was moved." +msgstr "${title} を移動しました。" + +#: kotti/views/edit/actions.py:222 #, python-format msgid "${title} is now visible in the navigation." msgstr "${title} がナビゲーションに表示されるようになりました。" -#: kotti/views/edit/actions.py:227 +#: kotti/views/edit/actions.py:225 #, python-format msgid "${title} is no longer visible in the navigation." msgstr "${title} がナビゲーションでは非表示になりました。" -#: kotti/views/edit/actions.py:292 +#: kotti/views/edit/actions.py:274 kotti/views/edit/actions.py:303 +#, python-format +msgid "${title} was deleted." +msgstr "${title} を削除しました。" + +#: kotti/views/edit/actions.py:300 msgid "Nothing was deleted." msgstr "削除対象がありません。" -#: kotti/views/edit/actions.py:328 kotti/views/edit/actions.py:355 +#: kotti/views/edit/actions.py:336 kotti/views/edit/actions.py:363 msgid "Name and title are required." msgstr "名称とタイトルは必須です。" -#: kotti/views/edit/actions.py:332 +#: kotti/views/edit/actions.py:340 msgid "Item was renamed." msgstr "名称変更しました" -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:461 +msgid "Change State" +msgstr "状態を変更" + +#: kotti/views/edit/actions.py:462 msgid "Move up" msgstr "上に移動する" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:463 msgid "Move down" msgstr "下に移動する" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:464 msgid "Show" msgstr "表示中にする" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:465 msgid "Hide" msgstr "非表示にする" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:504 msgid "You have to select items to perform an action." msgstr "アクションを適用するアイテムを選択してください。" -#: kotti/views/edit/actions.py:453 -msgid "Change State" -msgstr "状態を変更" - -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:34 msgid "Description" msgstr "説明" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:40 msgid "Tags" msgstr "タグ" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:49 msgid "Body" msgstr "本文" -#: kotti/views/edit/default_views.py:99 -msgid "Default view has been reset to default." -msgstr "デフォルト表示は既定値にリセットされました。" - -#: kotti/views/edit/default_views.py:78 -msgid "Default view" -msgstr "デフォルト表示" - -#: kotti/views/edit/default_views.py:106 -msgid "Default view has been set." -msgstr "デフォルト表示が設定されました。" +#~ msgid "Image" +#~ msgstr "画像" -#: kotti/views/edit/default_views.py:111 -msgid "Default view could not be set." -msgstr "デフォルト表示は設定できませんでした。" +#~ msgid "Submit" +#~ msgstr "送信" #~ msgid "Private" #~ msgstr "プライベート" diff --git a/kotti/locale/ko/LC_MESSAGES/Kotti.mo b/kotti/locale/ko/LC_MESSAGES/Kotti.mo index 149baa219..530eaeff6 100644 Binary files a/kotti/locale/ko/LC_MESSAGES/Kotti.mo and b/kotti/locale/ko/LC_MESSAGES/Kotti.mo differ diff --git a/kotti/locale/ko/LC_MESSAGES/Kotti.po b/kotti/locale/ko/LC_MESSAGES/Kotti.po index f713edaad..eda4adcf7 100644 --- a/kotti/locale/ko/LC_MESSAGES/Kotti.po +++ b/kotti/locale/ko/LC_MESSAGES/Kotti.po @@ -1,60 +1,113 @@ -# Translations template for Kotti. -# Copyright (C) 2014 ORGANIZATION -# This file is distributed under the same license as the Kotti project. -# mete0r , 2014. -# #, fuzzy msgid "" msgstr "" -"Project-Id-Version: Kotti 0.10b2dev\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2014-11-21 21:56+0100\n" -"PO-Revision-Date: 2014-10-19 08:22+0900\n" -"Last-Translator: mete0r \n" -"Language-Team: Korean\n" -"Language: ko\n" +"POT-Creation-Date: 2018-01-17 13:51+0100\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.6\n" -"Plural-Forms: nplurals=1; plural=0;\n" -#: kotti/populate.py:64 +#: kotti/security.py:173 +msgid "Viewer" +msgstr "열람자" + +#: kotti/security.py:174 +msgid "Editor" +msgstr "편집자" + +#: kotti/security.py:175 +msgid "Owner" +msgstr "소유자" + +#: kotti/security.py:176 +msgid "Admin" +msgstr "관리자" + +#: kotti/resources.py:541 kotti/views/edit/actions.py:453 +msgid "Copy" +msgstr "복사" + +#: kotti/resources.py:542 kotti/views/edit/actions.py:454 +msgid "Cut" +msgstr "잘라내기" + +#: kotti/resources.py:543 kotti/views/edit/actions.py:450 +msgid "Paste" +msgstr "붙여넣기" + +#: kotti/resources.py:544 kotti/templates/edit/rename.pt:31 +#: kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:42 kotti/views/edit/actions.py:455 +msgid "Rename" +msgstr "이름 바꾸기" + +#: kotti/resources.py:545 kotti/templates/edit/delete.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:49 +#: kotti/templates/site-setup/delete-user.pt:24 kotti/views/users.py:454 +#: kotti/views/edit/actions.py:457 +msgid "Delete" +msgstr "삭제" + +#: kotti/resources.py:556 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "컨텐츠 항목" + +#: kotti/resources.py:557 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "편집" + +#: kotti/resources.py:558 +msgid "Share" +msgstr "공유" + +#: kotti/resources.py:559 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "동작" + +#: kotti/resources.py:562 +msgid "Folder view" +msgstr "폴더로 보임" + +#: kotti/resources.py:671 kotti/views/edit/content.py:61 +msgid "Document" +msgstr "문서" + +#: kotti/resources.py:808 kotti/views/edit/content.py:73 +#: kotti/views/edit/content.py:87 +msgid "File" +msgstr "파일" + +#: kotti/populate.py:67 msgid "Welcome to Kotti" msgstr "Kotti에 오신 것을 환영합니다" -#: kotti/populate.py:65 +#: kotti/populate.py:68 msgid "Congratulations! You have successfully installed Kotti." msgstr "축하합니다! 성공적으로 Kotti를 설치했습니다." -#: kotti/populate.py:66 -#, c-format +#: kotti/populate.py:69 +#, fuzzy, c-format msgid "" "\n" "

    Log in

    \n" "

    \n" -" You can log in to your " -"site\n" +" You can log in to your site\n" " and start changing its contents. If you haven't chosen a password for\n" " your admin account yet, it'll likely be qwerty.\n" "

    \n" "

    \n" " Once you're logged in, you'll see the grey editor bar below the top\n" -" navigation bar. It will allow you to switch between editing and viewing " -"the\n" +" navigation bar. It will allow you to switch between editing and viewing the\n" " current page as it will appear to your visitors.\n" "

    \n" "
    \n" "
    \n" "

    Configure

    \n" "

    \n" -" Find out how to configure your Kotti's title and many other " -"settings using a\n" -" simple text file in your file system.\n" +" Find out how to configure your Kotti's title and many other\n" +" settings using a simple text file in your file system.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -62,13 +115,12 @@ msgid "" "
    \n" "

    Add-ons

    \n" "

    \n" -" A number of add-ons allow you to extend the functionality of " -"your Kotti site.\n" +" A number of add-ons allow you to extend the functionality of your\n" +" Kotti site.\n" "

    \n" "

    \n" " \n" +" href=\"http://pypi.python.org/pypi?%3Aaction=search&term=kotti\">\n" " Kotti add-ons\n" " \n" "

    \n" @@ -76,13 +128,13 @@ msgid "" "
    \n" "

    Documentation

    \n" "

    \n" -" Wonder what more you can do with Kotti? What license it has? " -"Read the\n" -" manual for more information.\n" +" Wonder what more you can do with Kotti?\n" +" What license it has?\n" +" Read the manual for more information.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/\">\n" " Documentation\n" " \n" "

    \n" @@ -92,25 +144,20 @@ msgstr "" "\n" "

    로그인

    \n" "

    \n" -" 로그인하여 컨텐츠를 편집" -"할 수 있습니다.아직 관리자 암호를 바꾸지 않았다면, qwerty일 수 있습" -"니다.\n" +" 로그인하여 컨텐츠를 편집할 수 있습니다.아직 관리자 암호를 바꾸지 않았다면, qwerty일 수 있습니다.\n" "

    \n" "

    \n" -" 로그인하면, 화면 위 네비게이션 줄 아래 회색 편집 줄이 보입니다. 그것으로 " -"현재 페이지를 편집하는 것과 방문객처럼 보는 것 사이를 전환할 수 있습니다.\n" +" 로그인하면, 화면 위 네비게이션 줄 아래 회색 편집 줄이 보입니다. 그것으로 현재 페이지를 편집하는 것과 방문객처럼 보는 것 사이를 전환할 수 있습니다.\n" "

    \n" "
    \n" "
    \n" "

    설정

    \n" "

    \n" -" Kotti의 제목과 여러 다른 설정을 파일시스템 상의 간단한 텍스트 파" -"일로 설정하는 법을 알아보세요.\n" +" Kotti의 제목과 여러 다른 설정을 파일시스템 상의 간단한 텍스트 파일로 설정하는 법을 알아보세요.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/developing/configuration.html\">\n" " 설정 매뉴얼\n" " \n" "

    \n" @@ -118,13 +165,11 @@ msgstr "" "
    \n" "

    애드온

    \n" "

    \n" -" 몇가지 애드온으로 당신의 Kotti 사이트 기능을 확장할 수 있습니" -"다.\n" +" 몇가지 애드온으로 당신의 Kotti 사이트 기능을 확장할 수 있습니다.\n" "

    \n" "

    \n" " \n" +" href=\"http://pypi.python.org/pypi?%3Aaction=search&term=kotti\">\n" " Kotti 애드온\n" " \n" "

    \n" @@ -132,37 +177,32 @@ msgstr "" "
    \n" "

    기술문헌

    \n" "

    \n" -" Kotti로 무엇을 더 할 수 있는지 궁금하십니까? 사용권은 무엇이" -"죠? 좀 더 알아보려면 매뉴얼을 읽으십시오.\n" +" Kotti로 무엇을 더 할 수 있는지 궁금하십니까? 사용권은 무엇이죠? 좀 더 알아보려면 매뉴얼을 읽으십시오.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/\">\n" " 기술문헌\n" " \n" "

    \n" "
    \n" "
    \n" -#: kotti/populate.py:121 +#: kotti/populate.py:126 msgid "About" msgstr "이 사이트에 대하여" -#: kotti/populate.py:122 -msgid "" -"Our company is the leading manufacturer of foo widgets used in a wide " -"variety of aviation and and industrial products." -msgstr "" -"우리 회사는 다양한 범위의 항공 및 산업 제품들에 사용되는 foo 위젯의 선두 제조" -"업체입니다." +#: kotti/populate.py:127 +msgid "Our company is the leading manufacturer of foo widgets used in a wide variety of aviation and and industrial products." +msgstr "우리 회사는 다양한 범위의 항공 및 산업 제품들에 사용되는 foo 위젯의 선두 제조업체입니다." -#: kotti/populate.py:123 +#: kotti/populate.py:128 +#, fuzzy msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -179,20 +219,18 @@ msgid "" "\n" "\n" "

    \n" -" Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" -" \n" -" Copyright info.\n" -" Originally published in the\n" -" Extra EA-300\n" -" article.\n" +"Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +"\n" +"Copyright info.\n" +"Originally published in the\n" +" Extra EA-300\n" +"article.\n" "

    \n" msgstr "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -210,774 +248,866 @@ msgstr "" "\n" "

    \n" " 사진 크레딧: \"Northern Lights Formation\" by FlugKerl2.\n" -" \n" +" \n" " 저작권 정보.\n" " Originally published in the\n" " Extra EA-300\n" " article.\n" "

    \n" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 -msgid "Document" -msgstr "문서" +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "${user}님, 환영합니다! 로그인되었습니다." -#: kotti/resources.py:649 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 -msgid "File" -msgstr "파일" +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "환영합니다, 로그인되지 않았습니다." + +#: kotti/templates/editor-bar.pt:19 +msgid "View" +msgstr "보기" -#: kotti/resources.py:726 kotti/views/edit/content.py:144 -msgid "Image" -msgstr "화상" +#: kotti/templates/editor-bar.pt:42 +msgid "Navigate" +msgstr "둘러보기" -#: kotti/resources.py:500 -msgid "Folder view" -msgstr "폴더로 보임" +#: kotti/templates/editor-bar.pt:53 +msgid "Preferences" +msgstr "기본 설정" -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 -msgid "Contents" -msgstr "컨텐츠 항목" +#: kotti/templates/editor-bar.pt:58 +msgid "Site Setup" +msgstr "사이트 설정" -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" -msgstr "편집" +#: kotti/templates/editor-bar.pt:69 +msgid "Logout" +msgstr "로그아웃" -#: kotti/resources.py:489 -msgid "Share" -msgstr "공유" +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "보이는 방식 설정" -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" -msgstr "동작" +#: kotti/templates/login.pt:16 +msgid "Login" +msgstr "로그인" -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 -msgid "Copy" -msgstr "복사" +#: kotti/templates/login.pt:21 kotti/templates/login.pt:59 +msgid "Username or email" +msgstr "사용자 이름 혹은 전자메일" -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 -msgid "Cut" -msgstr "잘라내기" +#: kotti/templates/login.pt:26 kotti/views/users.py:220 +#: kotti/views/login.py:255 +msgid "Password" +msgstr "암호" -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 -msgid "Paste" -msgstr "붙여넣기" +#: kotti/templates/login.pt:34 +msgid "Log in" +msgstr "로그인" -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 -#: kotti/templates/edit/rename-nodes.pt:55 kotti/templates/edit/rename.pt:42 -#: kotti/views/edit/actions.py:447 -msgid "Rename" -msgstr "이름 바꾸기" +#: kotti/templates/login.pt:45 +msgid "Forgot your password?" +msgstr "암호를 잊으셨습니까?" -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 -#: kotti/templates/edit/delete-nodes.pt:90 kotti/templates/edit/delete.pt:28 -#: kotti/templates/site-setup/delete-user.pt:36 kotti/views/users.py:443 -#: kotti/views/edit/actions.py:449 -msgid "Delete" -msgstr "삭제" +#. Used in sentence: "Fill out your username or email and click +#. ${reset_password} below to receive an email with a link to reset your +#. password." +#. +#: kotti/templates/login.pt:52 kotti/templates/login.pt:67 +msgid "Reset password" +msgstr "암호 재설정" -#: kotti/security.py:165 -msgid "Viewer" -msgstr "열람자" +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:50 +msgid "Fill out your username or email and click ${reset_password} below to receive an email with a link to reset your password." +msgstr "사용자 이름이나 전자메일을 입력한 뒤 아래의 ${reset_password}를 누르면 암호를 재설정할 수 있는 링크를 전자메일로 받으실 수 있습니다." -#: kotti/security.py:166 -msgid "Editor" -msgstr "편집자" +#: kotti/templates/login.pt:78 +msgid "Not registered yet?" +msgstr "아직 등록하지 않으셨습니까?" -#: kotti/security.py:167 -msgid "Owner" -msgstr "소유자" +#: kotti/templates/login.pt:85 +msgid "Register for an account on this site." +msgstr "이 사이트에 계정을 등록하십시오." -#: kotti/security.py:168 -msgid "Admin" -msgstr "관리자" +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "금지됨" -#: kotti/templates/add-dropdown.pt:5 +#: kotti/templates/add-dropdown.pt:3 msgid "Add" msgstr "추가" -#: kotti/templates/add-dropdown.pt:27 +#: kotti/templates/add-dropdown.pt:13 msgid "Upload Content" msgstr "컨텐트 올리기" -#: kotti/templates/default-view-selector.pt:5 -msgid "Set default view" -msgstr "보이는 방식 설정" - -#: kotti/templates/editor-bar.pt:35 -msgid "View" -msgstr "보기" - -#: kotti/templates/editor-bar.pt:76 -msgid "Navigate" -msgstr "둘러보기" - -#: kotti/templates/editor-bar.pt:98 -msgid "Preferences" -msgstr "기본 설정" - -#: kotti/templates/editor-bar.pt:109 -msgid "Site Setup" -msgstr "사이트 설정" - -#: kotti/templates/editor-bar.pt:131 -msgid "Logout" -msgstr "로그아웃" +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "\"${state}\"로(으로) 바꿈" -#: kotti/templates/email-reset-password.pt:3 +#: kotti/templates/email-reset-password.pt:2 msgid "Reset your password for ${site_title}." msgstr "${site_title}의 암호를 재설정하십시오." -#: kotti/templates/email-reset-password.pt:7 -#: kotti/templates/email-set-password.pt:6 +#: kotti/templates/email-reset-password.pt:4 +#: kotti/templates/email-set-password.pt:4 msgid "Hello, ${user_title}!" msgstr "${user_title}님, 안녕하세요!" -#: kotti/templates/email-reset-password.pt:10 +#: kotti/templates/email-reset-password.pt:5 msgid "Click this link to reset your password at ${site_title}: ${url}" msgstr "이 링크를 눌러 ${site_title}에서의 암호를 설정하십시오: ${url}" -#: kotti/templates/email-set-password.pt:3 +#: kotti/templates/email-set-password.pt:2 msgid "Your registration for ${site_title}" msgstr "${site_title} 가입" -#: kotti/templates/email-set-password.pt:9 +#: kotti/templates/email-set-password.pt:5 msgid "You are joining ${site_title}." msgstr "${site_title}에 가입 중입니다." -#: kotti/templates/email-set-password.pt:12 +#: kotti/templates/email-set-password.pt:6 msgid "Click here to set your password and log in: ${url}" msgstr "여기를 눌러 암호를 설정 후 로그인하십시오: ${url}" -#: kotti/templates/forbidden.pt:7 -msgid "Forbidden" -msgstr "금지됨" +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" +msgstr "\"${title}\" 공유하기" -#: kotti/templates/login.pt:16 -msgid "Login" -msgstr "로그인" +#: kotti/templates/edit/share.pt:14 kotti/templates/edit/share.pt:20 +#: kotti/templates/edit/share.pt:27 kotti/templates/site-setup/users.pt:48 +msgid "Search users and groups" +msgstr "사용자와 그룹 검색" -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 -msgid "Username or email" -msgstr "사용자 이름 혹은 전자메일" +#: kotti/templates/edit/share.pt:34 kotti/templates/site-setup/users.pt:56 +msgid "Search" +msgstr "검색" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 -#: kotti/views/users.py:212 -msgid "Password" -msgstr "암호" +#: kotti/templates/edit/share.pt:44 +msgid "Assign local roles" +msgstr "컨텐츠별 역할 부여" -#: kotti/templates/login.pt:47 -msgid "Log in" -msgstr "로그인" +#: kotti/templates/edit/share.pt:49 kotti/templates/edit/change-state.pt:19 +#: kotti/templates/edit/delete-nodes.pt:22 kotti/templates/edit/upload.pt:18 +#: kotti/templates/edit/upload.pt:108 kotti/templates/edit/contents.pt:35 +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:69 +msgid "Type" +msgstr "유형" -#: kotti/templates/login.pt:65 -msgid "Forgot your password?" -msgstr "암호를 잊으셨습니까?" +#: kotti/templates/edit/share.pt:50 kotti/templates/site-setup/users.pt:70 +#: kotti/views/users.py:215 +msgid "Name" +msgstr "이름" -#. Used in sentence: "Fill out your username or email and click -#. ${reset_password} below to receive an email with a link to reset your -#. password." -#. -#: kotti/templates/login.pt:77 kotti/templates/login.pt:100 -msgid "Reset password" -msgstr "암호 재설정" +#: kotti/templates/edit/share.pt:61 kotti/templates/site-setup/users.pt:81 +#: kotti/views/users.py:321 kotti/views/users.py:515 kotti/views/users.py:550 +msgid "User" +msgstr "사용자" -#. Canonical text for ${reset_password} is: "Reset password" -#: kotti/templates/login.pt:74 -msgid "" -"Fill out your username or email and click ${reset_password} below to receive " -"an email with a link to reset your password." +#: kotti/templates/edit/share.pt:62 kotti/templates/site-setup/users.pt:82 +#: kotti/views/users.py:196 kotti/views/users.py:356 kotti/views/users.py:515 +#: kotti/views/users.py:550 +msgid "Group" +msgstr "그룹" + +#: kotti/templates/edit/share.pt:65 kotti/templates/site-setup/users.pt:85 +msgid "Gravatar" msgstr "" -"사용자 이름이나 전자메일을 입력한 뒤 아래의 ${reset_password}를 누르면 암호" -"를 재설정할 수 있는 링크를 전자메일로 받으실 수 있습니다." -#: kotti/templates/login.pt:118 -msgid "Not registered yet?" -msgstr "아직 등록하지 않으셨습니까?" +#: kotti/templates/edit/share.pt:70 kotti/templates/site-setup/users.pt:91 +#, fuzzy +msgid "Assign role" +msgstr "컨텐츠별 역할 부여" -#: kotti/templates/login.pt:127 -msgid "Register for an account on this site." -msgstr "이 사이트에 계정을 등록하십시오." +#: kotti/templates/edit/share.pt:85 kotti/templates/edit/change-state.pt:72 +#: kotti/templates/site-setup/users.pt:107 +msgid "Apply changes" +msgstr "바꾸기 적용" -#: kotti/templates/workflow-dropdown.pt:19 -msgid "Make ${state}" -msgstr "\"${state}\"로(으로) 바꿈" +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "사이트 둘러보기" -#: kotti/templates/edit/breadcrumbs.pt:5 +#: kotti/templates/edit/breadcrumbs.pt:8 msgid "You are here:" msgstr "현재 위치:" -#: kotti/templates/edit/change-state.pt:7 +#: kotti/templates/edit/delete.pt:9 kotti/templates/site-setup/delete-user.pt:9 +msgid "Delete ${title}" +msgstr "\"${title}\" 삭제" + +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" +msgstr "\"${title}\"을(를) 정말 지우겠습니까?" + +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:74 +#: kotti/templates/edit/delete-nodes.pt:44 +#: kotti/templates/edit/rename-nodes.pt:44 kotti/views/users.py:324 +#: kotti/views/users.py:359 kotti/views/users.py:453 kotti/views/form.py:81 +msgid "Cancel" +msgstr "취소" + +#: kotti/templates/edit/change-state.pt:8 msgid "Change workflow state" msgstr "작업 상태 바꾸기" -#: kotti/templates/edit/change-state.pt:14 +#: kotti/templates/edit/change-state.pt:12 msgid "An item's workflow state determines who can see, edit and/or manage it." -msgstr "" -"항목의 작업 상태에 따라 항목을 누가 읽거나 고치고, 관리할 수 있는지 달라집니" -"다." +msgstr "항목의 작업 상태에 따라 항목을 누가 읽거나 고치고, 관리할 수 있는지 달라집니다." -#: kotti/templates/edit/change-state.pt:26 kotti/templates/edit/contents.pt:57 -#: kotti/templates/edit/delete-nodes.pt:33 kotti/templates/view/folder.pt:28 -#: kotti/views/users.py:196 kotti/views/edit/content.py:31 +#: kotti/templates/edit/change-state.pt:18 +#: kotti/templates/edit/delete-nodes.pt:21 kotti/templates/edit/contents.pt:34 +#: kotti/templates/view/folder.pt:20 kotti/views/users.py:204 +#: kotti/views/edit/content.py:28 msgid "Title" msgstr "제목" -#: kotti/templates/edit/change-state.pt:29 kotti/templates/edit/contents.pt:60 -#: kotti/templates/edit/delete-nodes.pt:36 kotti/templates/edit/share.pt:70 -#: kotti/templates/edit/upload.pt:23 kotti/templates/edit/upload.pt:167 -#: kotti/templates/site-setup/users.pt:105 kotti/templates/view/folder.pt:31 -msgid "Type" -msgstr "유형" - -#: kotti/templates/edit/change-state.pt:32 kotti/templates/edit/contents.pt:63 -#: kotti/templates/edit/delete-nodes.pt:39 +#: kotti/templates/edit/change-state.pt:20 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:36 msgid "State" msgstr "작업 상태" -#: kotti/templates/edit/change-state.pt:35 kotti/templates/edit/contents.pt:69 -#: kotti/templates/edit/delete-nodes.pt:42 kotti/templates/view/folder.pt:34 +#: kotti/templates/edit/change-state.pt:21 +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:38 +#: kotti/templates/view/folder.pt:22 msgid "Creation Date" msgstr "만든 날" -#: kotti/templates/edit/change-state.pt:38 kotti/templates/edit/contents.pt:72 -#: kotti/templates/edit/delete-nodes.pt:45 kotti/templates/view/folder.pt:37 +#: kotti/templates/edit/change-state.pt:22 +#: kotti/templates/edit/delete-nodes.pt:25 kotti/templates/edit/contents.pt:39 +#: kotti/templates/view/folder.pt:23 msgid "Modification Date" msgstr "바꾼 날" -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 +#: kotti/templates/edit/change-state.pt:28 +#: kotti/templates/edit/delete-nodes.pt:31 kotti/templates/edit/contents.pt:45 +msgid "Select item" +msgstr "" + +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:48 msgid "Include children" msgstr "하위 항목 포함 여부" -#: kotti/templates/edit/change-state.pt:82 -msgid "" -"If checked, this will attempt to modify the status of all children in any " -"selected documents and their subdocuments." +#: kotti/templates/edit/change-state.pt:41 +msgid "If checked, this will attempt to modify the status of all children in any selected documents and their subdocuments." msgstr "체크하면, 고른 문서와 그 하위 문서 모두를 바꿉니다." -#: kotti/templates/edit/change-state.pt:100 +#: kotti/templates/edit/change-state.pt:54 msgid "Change state to" msgstr "바꿀 상태" -#: kotti/templates/edit/change-state.pt:103 +#: kotti/templates/edit/change-state.pt:55 msgid "Select the new state where all chosen items should be set." msgstr "고른 항목 모두가 갖게 될 상태를 선택하십시오." -#: kotti/templates/edit/change-state.pt:111 +#: kotti/templates/edit/change-state.pt:60 msgid "No change" msgstr "바꾸지 않음" -#: kotti/templates/edit/change-state.pt:127 kotti/templates/edit/share.pt:121 -#: kotti/templates/site-setup/users.pt:165 -msgid "Apply changes" -msgstr "바꾸기 적용" - -#: kotti/templates/edit/change-state.pt:130 -#: kotti/templates/edit/delete-nodes.pt:93 -#: kotti/templates/edit/rename-nodes.pt:58 kotti/views/form.py:78 -#: kotti/views/users.py:316 kotti/views/users.py:350 kotti/views/users.py:442 -msgid "Cancel" -msgstr "취소" - -#: kotti/templates/edit/contents.pt:43 -msgid "No content items are contained here." -msgstr "여기엔 하위 항목이 없습니다." - -#: kotti/templates/edit/contents.pt:66 -msgid "Visibility" -msgstr "가시성" - -#: kotti/templates/edit/contents.pt:125 -msgid "Visible" -msgstr "보임" - -#: kotti/templates/edit/contents.pt:134 -msgid "Hidden" -msgstr "숨김" - -#: kotti/templates/edit/delete-nodes.pt:17 +#: kotti/templates/edit/delete-nodes.pt:13 msgid "Are you sure you want to delete the following items?" msgstr "다음 항목을 정말 지우겠습니까?" -#: kotti/templates/edit/delete.pt:8 -#: kotti/templates/site-setup/delete-user.pt:8 -msgid "Delete ${title}" -msgstr "\"${title}\" 삭제" - -#: kotti/templates/edit/delete.pt:20 -msgid "Are you sure you want to delete ${title}?" -msgstr "\"${title}\"을(를) 정말 지우겠습니까?" - -#: kotti/templates/edit/nav-tree-view.pt:7 -msgid "Navigate Site" -msgstr "사이트 둘러보기" +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "\"${title}\" 이름 바꾸기" -#: kotti/templates/edit/rename-nodes.pt:16 kotti/templates/edit/rename.pt:14 -msgid "" -"Each content item has a name, which is used to create the url, and a title. " -"Read the user manual about proper formatting of name and title if needed. " -"You can change both below." -msgstr "" -"각 컨텐트 항목은 이름을 가지며, 이는 URL과 제목을 만드는데 사용됩니다. 이름" -"과 제목의 올바른 형식에 대해 알려면 사용자 매뉴얼을 읽으십시오. 둘 다 아래에" -"서 바꿀 수 있습니다." +#: kotti/templates/edit/rename.pt:11 kotti/templates/edit/rename-nodes.pt:13 +msgid "Each content item has a name, which is used to create the url, and a title. Read the user manual about proper formatting of name and title if needed. You can change both below." +msgstr "각 컨텐트 항목은 이름을 가지며, 이는 URL과 제목을 만드는데 사용됩니다. 이름과 제목의 올바른 형식에 대해 알려면 사용자 매뉴얼을 읽으십시오. 둘 다 아래에서 바꿀 수 있습니다." -#: kotti/templates/edit/rename-nodes.pt:35 kotti/templates/edit/rename.pt:26 +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +#: kotti/templates/edit/rename-nodes.pt:28 msgid "New name" msgstr "새 이름" -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:34 +#: kotti/templates/edit/rename-nodes.pt:36 msgid "New title" msgstr "새 제목" -#: kotti/templates/edit/rename.pt:7 -msgid "Rename ${title}" -msgstr "\"${title}\" 이름 바꾸기" +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" +msgstr "내 컴퓨터에서 컨텐트 올리기" -#: kotti/templates/edit/share.pt:7 -msgid "Share ${title}" -msgstr "\"${title}\" 공유하기" +#: kotti/templates/edit/upload.pt:17 kotti/templates/edit/upload.pt:107 +msgid "Filename" +msgstr "파일 이름" -#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:30 -#: kotti/templates/edit/share.pt:35 kotti/templates/site-setup/users.pt:68 -msgid "Search users and groups" -msgstr "사용자와 그룹 검색" - -#: kotti/templates/edit/share.pt:43 kotti/templates/site-setup/users.pt:81 -msgid "Search" -msgstr "검색" - -#: kotti/templates/edit/share.pt:60 -msgid "Assign local roles" -msgstr "컨텐츠별 역할 부여" - -#: kotti/templates/edit/share.pt:73 kotti/templates/site-setup/users.pt:108 -#: kotti/views/users.py:207 -msgid "Name" -msgstr "이름" - -#: kotti/templates/edit/share.pt:91 kotti/templates/site-setup/users.pt:126 -#: kotti/views/users.py:313 kotti/views/users.py:501 kotti/views/users.py:536 -msgid "User" -msgstr "사용자" - -#: kotti/templates/edit/share.pt:94 kotti/templates/site-setup/users.pt:129 -#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 -#: kotti/views/users.py:536 -msgid "Group" -msgstr "그룹" - -#: kotti/templates/edit/share.pt:101 kotti/templates/site-setup/users.pt:136 -msgid "Gravatar" -msgstr "" - -#: kotti/templates/edit/upload.pt:9 -msgid "Upload content from local file(s)" -msgstr "내 컴퓨터에서 컨텐트 올리기" - -#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:164 -msgid "Filename" -msgstr "파일 이름" - -#: kotti/templates/edit/upload.pt:26 kotti/templates/edit/upload.pt:170 +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:109 msgid "Size" msgstr "크기" -#: kotti/templates/edit/upload.pt:29 +#: kotti/templates/edit/upload.pt:20 msgid "Status" msgstr "상태" -#: kotti/templates/edit/upload.pt:32 +#: kotti/templates/edit/upload.pt:21 msgid "Progress" msgstr "진행률" -#: kotti/templates/edit/upload.pt:124 +#: kotti/templates/edit/upload.pt:84 msgid "Select file(s) to upload..." msgstr "" -#: kotti/templates/edit/upload.pt:134 +#: kotti/templates/edit/upload.pt:92 msgid "Upload ${number} files." msgstr "" -#: kotti/templates/edit/upload.pt:144 +#: kotti/templates/edit/upload.pt:97 msgid "Dismiss all errors" msgstr "" -#: kotti/templates/site-setup/delete-user.pt:20 +#: kotti/templates/edit/contents.pt:26 +msgid "No content items are contained here." +msgstr "여기엔 하위 항목이 없습니다." + +#: kotti/templates/edit/contents.pt:33 +msgid "Select / deselect all" +msgstr "" + +#: kotti/templates/edit/contents.pt:37 +msgid "Visibility" +msgstr "가시성" + +#: kotti/templates/edit/contents.pt:69 +msgid "Visible" +msgstr "보임" + +#: kotti/templates/edit/contents.pt:73 +msgid "Hidden" +msgstr "숨김" + +#: kotti/templates/http-errors/notfound.pt:26 +msgid "Not Found" +msgstr "" + +#: kotti/templates/http-errors/notfound.pt:27 +msgid "The resource could not be found" +msgstr "" + +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "검색결과" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "검색..." + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "태그:" + +#: kotti/templates/site-setup/delete-user.pt:15 msgid "Are you sure you want to delete ${type} ${title}?" msgstr "${type} \"${title}\"을 정말 지우겠습니까?" -#: kotti/templates/site-setup/user.pt:13 +#: kotti/templates/site-setup/user.pt:12 msgid "Back to User Management" msgstr "사용자 관리로 되돌아가기" -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 +#: kotti/templates/site-setup/user.pt:21 kotti/views/form.py:159 #, python-format msgid "Edit ${title}" msgstr "\"${title}\" 편집" -#: kotti/templates/site-setup/users.pt:15 +#: kotti/templates/site-setup/users.pt:12 #, fuzzy msgid "Search user(s) / group(s)" msgstr "사용자/그룹 검색" -#: kotti/templates/site-setup/users.pt:24 +#: kotti/templates/site-setup/users.pt:17 msgid "Add user" msgstr "사용자 추가" -#: kotti/templates/site-setup/users.pt:33 +#: kotti/templates/site-setup/users.pt:22 msgid "Add group" msgstr "그룹 추가" -#: kotti/templates/site-setup/users.pt:52 +#: kotti/templates/site-setup/users.pt:34 msgid "Find users or groups" msgstr "사용자나 그룹 찾기" -#: kotti/templates/site-setup/users.pt:63 +#: kotti/templates/site-setup/users.pt:40 msgid "User- / groupname" msgstr "사용자나 그룹 이름" -#: kotti/templates/site-setup/users.pt:70 +#: kotti/templates/site-setup/users.pt:50 msgid "Blank search text finds all." msgstr "비워두면 모두 찾습니다." -#: kotti/templates/site-setup/users.pt:96 +#: kotti/templates/site-setup/users.pt:65 msgid "Assign global roles" msgstr "사이트 전체 역할 부여" -#: kotti/templates/site-setup/users.pt:180 +#: kotti/templates/site-setup/users.pt:117 msgid "Add new user" msgstr "새 사용자 추가" -#: kotti/templates/site-setup/users.pt:190 +#: kotti/templates/site-setup/users.pt:123 msgid "Add new group" msgstr "새 그룹 추가" -#: kotti/templates/view/search-results.pt:8 -msgid "Search Results" -msgstr "검색결과" - -#: kotti/templates/view/search.pt:5 -msgid "Search..." -msgstr "검색..." - -#: kotti/templates/view/tags.pt:5 -msgid "Tagged with:" -msgstr "태그:" - -#: kotti/tests/testing_view.pt:7 -msgid "Welcome, ${user}! You are logged in." -msgstr "${user}님, 환영합니다! 로그인되었습니다." - -#: kotti/tests/testing_view.pt:14 -msgid "Welcome, you are not logged in." -msgstr "환영합니다, 로그인되지 않았습니다." - -#: kotti/views/form.py:79 kotti/views/users.py:69 -#: kotti/views/edit/actions.py:363 kotti/views/edit/actions.py:407 +#: kotti/views/users.py:70 kotti/views/form.py:82 +#: kotti/views/edit/actions.py:371 kotti/views/edit/actions.py:415 msgid "Your changes have been saved." msgstr "수정사항이 저장되었습니다." -#: kotti/views/form.py:174 -msgid "Item was added." -msgstr "항목이 추가되었습니다." - -#: kotti/views/form.py:264 -#, python-format -msgid "Maximum file size: ${size}MB" -msgstr "최대 파일 크기: ${size}MB" - -#: kotti/views/form.py:77 kotti/views/users.py:441 -msgid "Save" -msgstr "저장" - -#: kotti/views/form.py:198 -#, python-format -msgid "Add ${type} to ${title}." -msgstr "${type}을(를) \"${title}\"에 추가합니다." - -#: kotti/views/form.py:202 -#, python-format -msgid "Add ${type}." -msgstr "${type}을(를) 추가합니다." - -#: kotti/views/login.py:238 -msgid "You have reset your password." -msgstr "암호를 재설정했습니다." - -#: kotti/views/login.py:200 -msgid "You have been logged out." -msgstr "로그아웃되었습니다." - -#: kotti/views/login.py:66 kotti/views/users.py:270 -msgid "Full name" -msgstr "전체 이름" - -#: kotti/views/login.py:69 -msgid "Username" -msgstr "사용자 이름" - -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 -msgid "Email" -msgstr "전자메일" - -#: kotti/views/login.py:109 -msgid "" -"Congratulations! You are successfully registered. You should be receiving an " -"email with a link to set your password. Doing so will activate your account." -msgstr "" -"축하합니다! 성공적으로 등록되었습니다. 전자메일로 암호를 설정하는 전자메일을 " -"받게 됩니다. 암호 설정 후 계정이 활성화됩니다." - -#: kotti/views/login.py:124 -#, python-format -msgid "Register - ${title}" -msgstr "등록 - ${title}" - -#: kotti/views/login.py:164 -msgid "Login failed." -msgstr "로그인이 실패했습니다." - -#: kotti/views/login.py:286 -#, python-format -msgid "Reset your password - ${title}." -msgstr "암호를 재설정하세요 - ${title}." - -#: kotti/views/login.py:160 -#, python-format -msgid "Welcome, ${user}!" -msgstr "${user}님, 환영합니다!" - -#: kotti/views/login.py:173 -msgid "" -"You should be receiving an email with a link to reset your password. Doing " -"so will activate your account." -msgstr "" -"암호를 재설정할 링크를 전자메일로 받게 됩니다. 설정하면 계정이 활성화됩니다." - -#: kotti/views/login.py:178 -msgid "That username or email is not known by this system." -msgstr "이 시스템에 없는 사용자 이름이나 전자메일입니다." - -#: kotti/views/login.py:83 -msgid "Register" -msgstr "등록하기" - -#: kotti/views/login.py:90 kotti/views/login.py:257 -msgid "There was an error." -msgstr "오류가 있습니다." - -#: kotti/views/login.py:250 -msgid "Submit" -msgstr "제출" - -#: kotti/views/login.py:279 -msgid "Your password reset token may have expired." -msgstr "암호 재설정 토큰이 만료된 것 같습니다." - -#: kotti/views/site_setup.py:6 kotti/views/users.py:378 -msgid "User Management" -msgstr "사용자 관리" - -#: kotti/views/users.py:267 -msgid "" -"Leave this empty and tick the 'Send password registration' box below to have " -"the user set their own password." -msgstr "" -"여길 비워두고 아래 '암호 등록 링크 발송'을 체크하면 사용자가 직접 암호를 설정" -"합니다." +#: kotti/views/users.py:72 kotti/views/users.py:477 +#: kotti/views/edit/actions.py:309 kotti/views/edit/actions.py:375 +#: kotti/views/edit/actions.py:417 kotti/views/edit/actions.py:421 +msgid "No changes were made." +msgstr "아무 것도 바뀌지 않았습니다." -#: kotti/views/users.py:587 -#, python-format -msgid "My preferences - ${title}" -msgstr "나의 선호사항" +#: kotti/views/users.py:105 +msgid "No users or groups were found." +msgstr "사용자나 그룹이 발견되지 않았습니다." -#: kotti/views/users.py:145 +#: kotti/views/users.py:153 msgid "Invalid value" msgstr "잘못된 값입니다." -#: kotti/views/users.py:151 +#: kotti/views/users.py:159 msgid "A user with that name already exists." msgstr "그 이름을 쓰는 사용자가 이미 있습니다." -#: kotti/views/users.py:158 +#: kotti/views/users.py:166 msgid "A user with that email already exists." msgstr "그 전자메일을 쓰는 사용자가 이미 있습니다." -#: kotti/views/users.py:181 +#: kotti/views/users.py:189 #, python-format msgid "No such group: ${group}" msgstr "그런 그룹이 없습니다: ${group}" -#: kotti/views/users.py:219 +#: kotti/views/users.py:207 kotti/views/login.py:71 kotti/views/login.py:267 +msgid "Email" +msgstr "전자메일" + +#: kotti/views/users.py:227 msgid "Active" msgstr "활성" -#: kotti/views/users.py:220 +#: kotti/views/users.py:228 msgid "Untick this to deactivate the account." msgstr "계정을 비활성화하려면 체크를 해제하십시오." -#: kotti/views/users.py:226 +#: kotti/views/users.py:234 msgid "Global roles" msgstr "전역 역할" -#: kotti/views/users.py:230 +#: kotti/views/users.py:238 msgid "Groups" msgstr "그룹" -#: kotti/views/users.py:315 +#: kotti/views/users.py:275 +msgid "Leave this empty and tick the 'Send password registration' box below to have the user set their own password." +msgstr "여길 비워두고 아래 '암호 등록 링크 발송'을 체크하면 사용자가 직접 암호를 설정합니다." + +#: kotti/views/users.py:278 kotti/views/login.py:63 +msgid "Full name" +msgstr "전체 이름" + +#: kotti/views/users.py:323 msgid "Add User" msgstr "사용자 추가" -#: kotti/views/users.py:339 +#: kotti/views/users.py:333 +msgid "Send password registration link." +msgstr "암호 등록 링크를 발송" + +#: kotti/views/users.py:348 #, python-format msgid "${title} was added." msgstr "\"${title}\"이(가) 추가되었습니다." -#: kotti/views/users.py:349 +#: kotti/views/users.py:358 msgid "Add Group" msgstr "그룹 추가" -#: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:301 kotti/views/edit/actions.py:367 -#: kotti/views/edit/actions.py:413 kotti/views/edit/actions.py:409 -msgid "No changes were made." -msgstr "아무 것도 바뀌지 않았습니다." - -#: kotti/views/users.py:558 -msgid "No name was given." -msgstr "이름이 주어지지 않았습니다." +#: kotti/views/users.py:388 kotti/views/site_setup.py:5 +msgid "User Management" +msgstr "사용자 관리" -#: kotti/views/users.py:98 -msgid "No users or groups were found." -msgstr "사용자나 그룹이 발견되지 않았습니다." +#: kotti/views/users.py:452 kotti/views/form.py:80 +msgid "Save" +msgstr "저장" -#: kotti/views/users.py:505 +#: kotti/views/users.py:519 #, python-format msgid "Edit ${principal_type} ${title}" msgstr "${principal_type} \"${title}\" 편집" -#: kotti/views/users.py:533 +#: kotti/views/users.py:547 msgid "User was not found." msgstr "사용자가 없습니다." -#: kotti/views/users.py:324 -msgid "Send password registration link." -msgstr "암호 등록 링크를 발송" - -#: kotti/views/users.py:543 +#: kotti/views/users.py:557 #, python-format msgid "${principal_type} ${title} was deleted." msgstr "${principal_type} \"${title}\"이(가) 지워졌습니다." -#: kotti/views/users.py:551 +#: kotti/views/users.py:565 #, python-format msgid "Delete ${principal_type} ${title}" msgstr "${principal_type} \"${title}\" 삭제" -#: kotti/views/edit/actions.py:106 +#: kotti/views/users.py:572 +msgid "No name was given." +msgstr "이름이 주어지지 않았습니다." + +#: kotti/views/users.py:601 #, python-format -msgid "${title} was copied." -msgstr "\"${title}\"이(가) 복사되었습니다." +msgid "My preferences - ${title}" +msgstr "나의 선호사항" -#: kotti/views/edit/actions.py:124 +#: kotti/views/form.py:177 +msgid "Item was added." +msgstr "항목이 추가되었습니다." + +#: kotti/views/form.py:201 #, python-format -msgid "${title} was cut." -msgstr "\"${title}\"이(가) 잘라내어졌습니다." +msgid "Add ${type} to ${title}." +msgstr "${type}을(를) \"${title}\"에 추가합니다." -#: kotti/views/edit/actions.py:182 +#: kotti/views/form.py:205 #, python-format -msgid "${title} was moved." -msgstr "\"${title}\"이(가) 옮겨졌습니다." +msgid "Add ${type}." +msgstr "${type}을(를) 추가합니다." -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/form.py:295 #, python-format -msgid "${title} was deleted." -msgstr "\"${title}\"이(가) 지워졌습니다." +msgid "Maximum file size: ${size}MB" +msgstr "최대 파일 크기: ${size}MB" -#: kotti/views/edit/actions.py:158 +#: kotti/views/login.py:66 +msgid "Username" +msgstr "사용자 이름" + +#: kotti/views/login.py:80 +msgid "Register" +msgstr "등록하기" + +#: kotti/views/login.py:87 +msgid "There was an error." +msgstr "오류가 있습니다." + +#: kotti/views/login.py:106 +msgid "Congratulations! You are successfully registered. You should be receiving an email with a link to set your password. Doing so will activate your account." +msgstr "축하합니다! 성공적으로 등록되었습니다. 전자메일로 암호를 설정하는 전자메일을 받게 됩니다. 암호 설정 후 계정이 활성화됩니다." + +#: kotti/views/login.py:121 +#, python-format +msgid "Register - ${title}" +msgstr "등록 - ${title}" + +#: kotti/views/login.py:152 +#, python-format +msgid "Welcome, ${user}!" +msgstr "${user}님, 환영합니다!" + +#: kotti/views/login.py:177 +msgid "You should be receiving an email with a link to reset your password. Doing so will activate your account." +msgstr "암호를 재설정할 링크를 전자메일로 받게 됩니다. 설정하면 계정이 활성화됩니다." + +#: kotti/views/login.py:210 +msgid "Login failed." +msgstr "로그인이 실패했습니다." + +#: kotti/views/login.py:220 +msgid "That username or email is not known by this system." +msgstr "이 시스템에 없는 사용자 이름이나 전자메일입니다." + +#: kotti/views/login.py:242 +msgid "You have been logged out." +msgstr "로그아웃되었습니다." + +#: kotti/views/login.py:280 +msgid "You have reset your password." +msgstr "암호를 재설정했습니다." + +#: kotti/views/login.py:299 +#, fuzzy +msgid "Set password" +msgstr "암호 재설정" + +#: kotti/views/login.py:327 +msgid "Your password reset token may have expired." +msgstr "암호 재설정 토큰이 만료된 것 같습니다." + +#: kotti/views/login.py:334 +#, python-format +msgid "Reset your password - ${title}." +msgstr "암호를 재설정하세요 - ${title}." + +#: kotti/views/edit/default_views.py:86 +msgid "Default view" +msgstr "기본형으로 보임" + +#: kotti/views/edit/default_views.py:107 +msgid "Default view has been reset to default." +msgstr "기본형 보기로 재설정되었습니다." + +#: kotti/views/edit/default_views.py:114 +msgid "Default view has been set." +msgstr "보이는 방식이 설정되었습니다." + +#: kotti/views/edit/default_views.py:119 +msgid "Default view could not be set." +msgstr "보이는 방식을 설정할 수 없었습니다." + +#: kotti/views/edit/actions.py:102 +#, python-format +msgid "${title} was copied." +msgstr "\"${title}\"이(가) 복사되었습니다." + +#: kotti/views/edit/actions.py:120 +#, python-format +msgid "${title} was cut." +msgstr "\"${title}\"이(가) 잘라내어졌습니다." + +#: kotti/views/edit/actions.py:155 #, python-format msgid "${title} was pasted." msgstr "\"${title}\"이(가) 붙여넣어졌습니다." -#: kotti/views/edit/actions.py:161 +#: kotti/views/edit/actions.py:158 msgid "Could not paste node. It no longer exists." msgstr "노드를 붙여넣을 수 없습니다. 그 노드가 존재하지 않습니다." -#: kotti/views/edit/actions.py:224 +#: kotti/views/edit/actions.py:180 +#, python-format +msgid "${title} was moved." +msgstr "\"${title}\"이(가) 옮겨졌습니다." + +#: kotti/views/edit/actions.py:222 #, python-format msgid "${title} is now visible in the navigation." msgstr "\"${title}\"이(가) 이제 둘러보기에 나타납니다." -#: kotti/views/edit/actions.py:227 +#: kotti/views/edit/actions.py:225 #, python-format msgid "${title} is no longer visible in the navigation." msgstr "\"${title}\"이(가) 이젠 둘러보기에 나타나지 않습니다." -#: kotti/views/edit/actions.py:292 +#: kotti/views/edit/actions.py:274 kotti/views/edit/actions.py:303 +#, python-format +msgid "${title} was deleted." +msgstr "\"${title}\"이(가) 지워졌습니다." + +#: kotti/views/edit/actions.py:300 msgid "Nothing was deleted." msgstr "아무것도 지워지지 않았습니다." -#: kotti/views/edit/actions.py:328 kotti/views/edit/actions.py:355 +#: kotti/views/edit/actions.py:336 kotti/views/edit/actions.py:363 msgid "Name and title are required." msgstr "이름과 제목이 필요합니다." -#: kotti/views/edit/actions.py:332 +#: kotti/views/edit/actions.py:340 msgid "Item was renamed." msgstr "항목이 재명명되었습니다." -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:461 +msgid "Change State" +msgstr "작업 상태 바꾸기" + +#: kotti/views/edit/actions.py:462 msgid "Move up" msgstr "위로 옮김" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:463 msgid "Move down" msgstr "아래로 옮김" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:464 msgid "Show" msgstr "보이기" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:465 msgid "Hide" msgstr "숨기기" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:504 msgid "You have to select items to perform an action." msgstr "적용할 항목을 선택해야합니다." -#: kotti/views/edit/actions.py:453 -msgid "Change State" -msgstr "작업 상태 바꾸기" - -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:34 msgid "Description" msgstr "설명" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:40 msgid "Tags" msgstr "태그" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:49 msgid "Body" msgstr "본문" -#: kotti/views/edit/default_views.py:99 -msgid "Default view has been reset to default." -msgstr "기본형 보기로 재설정되었습니다." +#~ msgid "Image" +#~ msgstr "화상" -#: kotti/views/edit/default_views.py:78 -msgid "Default view" -msgstr "기본형으로 보임" - -#: kotti/views/edit/default_views.py:106 -msgid "Default view has been set." -msgstr "보이는 방식이 설정되었습니다." - -#: kotti/views/edit/default_views.py:111 -msgid "Default view could not be set." -msgstr "보이는 방식을 설정할 수 없었습니다." +#, fuzzy +#~ msgid "" +#~ "\n" +#~ "

    Log in

    \n" +#~ "

    \n" +#~ " You can log in to your site\n" +#~ " and start changing its contents. If you haven't chosen a password for\n" +#~ " your admin account yet, it'll likely be qwerty.\n" +#~ "

    \n" +#~ "

    \n" +#~ " Once you're logged in, you'll see the grey editor bar below the top\n" +#~ " navigation bar. It will allow you to switch between editing and viewing the\n" +#~ " current page as it will appear to your visitors.\n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Configure

    \n" +#~ "

    \n" +#~ " Find out how to configure your Kotti's title and many other settings using a\n" +#~ " simple text file in your file system.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Configuration manual\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Add-ons

    \n" +#~ "

    \n" +#~ " A number of add-ons allow you to extend the functionality of your Kotti site.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Kotti add-ons\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    Documentation

    \n" +#~ "

    \n" +#~ " Wonder what more you can do with Kotti? What license it has? Read the\n" +#~ " manual for more information.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Documentation\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ msgstr "" +#~ "\n" +#~ "

    로그인

    \n" +#~ "

    \n" +#~ " 로그인하여 컨텐츠를 편집할 수 있습니다.아직 관리자 암호를 바꾸지 않았다면, qwerty일 수 있습니다.\n" +#~ "

    \n" +#~ "

    \n" +#~ " 로그인하면, 화면 위 네비게이션 줄 아래 회색 편집 줄이 보입니다. 그것으로 현재 페이지를 편집하는 것과 방문객처럼 보는 것 사이를 전환할 수 있습니다.\n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    설정

    \n" +#~ "

    \n" +#~ " Kotti의 제목과 여러 다른 설정을 파일시스템 상의 간단한 텍스트 파일로 설정하는 법을 알아보세요.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " 설정 매뉴얼\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    애드온

    \n" +#~ "

    \n" +#~ " 몇가지 애드온으로 당신의 Kotti 사이트 기능을 확장할 수 있습니다.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " Kotti 애드온\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" +#~ "

    기술문헌

    \n" +#~ "

    \n" +#~ " Kotti로 무엇을 더 할 수 있는지 궁금하십니까? 사용권은 무엇이죠? 좀 더 알아보려면 매뉴얼을 읽으십시오.\n" +#~ "

    \n" +#~ "

    \n" +#~ " \n" +#~ " 기술문헌\n" +#~ " \n" +#~ "

    \n" +#~ "
    \n" +#~ "
    \n" + +#~ msgid "" +#~ "\n" +#~ "

    \n" +#~ " \"five\n" +#~ "

    \n" +#~ "\n" +#~ "

    \n" +#~ " Our worldwide headquarters:\n" +#~ "

    \n" +#~ "\n" +#~ "
    \n" +#~ " Foo World
    \n" +#~ " 123 Nowhere Street, Suite 777
    \n" +#~ " Omak, WA 98841 USA
    \n" +#~ " +1-509-555-0100
    \n" +#~ " widgets@foowrld.example.com\n" +#~ "
    \n" +#~ "\n" +#~ "

    \n" +#~ " Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +#~ " \n" +#~ " Copyright info.\n" +#~ " Originally published in the\n" +#~ " Extra EA-300\n" +#~ " article.\n" +#~ "

    \n" +#~ msgstr "" +#~ "\n" +#~ "

    \n" +#~ " \"five\n" +#~ "

    \n" +#~ "\n" +#~ "

    \n" +#~ " 본사:\n" +#~ "

    \n" +#~ "\n" +#~ "
    \n" +#~ " Foo World
    \n" +#~ " 123 Nowhere Street, Suite 777
    \n" +#~ " Omak, WA 98841 USA
    \n" +#~ " +1-509-555-0100
    \n" +#~ " widgets@foowrld.example.com\n" +#~ "
    \n" +#~ "\n" +#~ "

    \n" +#~ " 사진 크레딧: \"Northern Lights Formation\" by FlugKerl2.\n" +#~ " \n" +#~ " 저작권 정보.\n" +#~ " Originally published in the\n" +#~ " Extra EA-300\n" +#~ " article.\n" +#~ "

    \n" + +#~ msgid "Submit" +#~ msgstr "제출" #~ msgid "Private" #~ msgstr "내부 전용" diff --git a/kotti/locale/nl/LC_MESSAGES/Kotti.mo b/kotti/locale/nl/LC_MESSAGES/Kotti.mo index cffeb201e..3c2a8e718 100644 Binary files a/kotti/locale/nl/LC_MESSAGES/Kotti.mo and b/kotti/locale/nl/LC_MESSAGES/Kotti.mo differ diff --git a/kotti/locale/nl/LC_MESSAGES/Kotti.po b/kotti/locale/nl/LC_MESSAGES/Kotti.po index 8d2f1f5c4..36879dcb5 100644 --- a/kotti/locale/nl/LC_MESSAGES/Kotti.po +++ b/kotti/locale/nl/LC_MESSAGES/Kotti.po @@ -1,60 +1,113 @@ -# Dutch translations for Kotti. -# Copyright (C) 2012 ORGANIZATION -# This file is distributed under the same license as the Kotti project. -# Wim Boucquaert , 2012. -# msgid "" msgstr "" -"Project-Id-Version: Kotti 0.4.4\n" -"Report-Msgid-Bugs-To: kotti@googlegroups.com\n" -"POT-Creation-Date: 2014-11-21 21:56+0100\n" -"PO-Revision-Date: 2014-02-13 21:51+0100\n" -"Last-Translator: Wim Boucquaert \n" -"Language-Team: nl \n" -"Language: \n" +"POT-Creation-Date: 2018-01-17 13:51+0100\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" -"Generated-By: Babel 0.9.6\n" -#: kotti/populate.py:64 +#: kotti/security.py:173 +msgid "Viewer" +msgstr "Lezer" + +#: kotti/security.py:174 +msgid "Editor" +msgstr "Editor" + +#: kotti/security.py:175 +msgid "Owner" +msgstr "Eigenaar" + +#: kotti/security.py:176 +msgid "Admin" +msgstr "Beheerder" + +#: kotti/resources.py:541 kotti/views/edit/actions.py:453 +msgid "Copy" +msgstr "Kopiëren" + +#: kotti/resources.py:542 kotti/views/edit/actions.py:454 +msgid "Cut" +msgstr "Knippen" + +#: kotti/resources.py:543 kotti/views/edit/actions.py:450 +msgid "Paste" +msgstr "Plakken" + +#: kotti/resources.py:544 kotti/templates/edit/rename.pt:31 +#: kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:42 kotti/views/edit/actions.py:455 +msgid "Rename" +msgstr "Hernoemen" + +#: kotti/resources.py:545 kotti/templates/edit/delete.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:49 +#: kotti/templates/site-setup/delete-user.pt:24 kotti/views/users.py:454 +#: kotti/views/edit/actions.py:457 +msgid "Delete" +msgstr "Verwijderen" + +#: kotti/resources.py:556 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "Inhoud" + +#: kotti/resources.py:557 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "Bewerken" + +#: kotti/resources.py:558 +msgid "Share" +msgstr "Delen" + +#: kotti/resources.py:559 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "Acties" + +#: kotti/resources.py:562 +msgid "Folder view" +msgstr "Map weergave" + +#: kotti/resources.py:671 kotti/views/edit/content.py:61 +msgid "Document" +msgstr "Document" + +#: kotti/resources.py:808 kotti/views/edit/content.py:73 +#: kotti/views/edit/content.py:87 +msgid "File" +msgstr "Bestand" + +#: kotti/populate.py:67 msgid "Welcome to Kotti" msgstr "" -#: kotti/populate.py:65 +#: kotti/populate.py:68 msgid "Congratulations! You have successfully installed Kotti." msgstr "" -#: kotti/populate.py:66 +#: kotti/populate.py:69 #, c-format msgid "" "\n" "

    Log in

    \n" "

    \n" -" You can log in to your " -"site\n" +" You can log in to your site\n" " and start changing its contents. If you haven't chosen a password for\n" " your admin account yet, it'll likely be qwerty.\n" "

    \n" "

    \n" " Once you're logged in, you'll see the grey editor bar below the top\n" -" navigation bar. It will allow you to switch between editing and viewing " -"the\n" +" navigation bar. It will allow you to switch between editing and viewing the\n" " current page as it will appear to your visitors.\n" "

    \n" "
    \n" "
    \n" "

    Configure

    \n" "

    \n" -" Find out how to configure your Kotti's title and many other " -"settings using a\n" -" simple text file in your file system.\n" +" Find out how to configure your Kotti's title and many other\n" +" settings using a simple text file in your file system.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -62,13 +115,12 @@ msgid "" "
    \n" "

    Add-ons

    \n" "

    \n" -" A number of add-ons allow you to extend the functionality of " -"your Kotti site.\n" +" A number of add-ons allow you to extend the functionality of your\n" +" Kotti site.\n" "

    \n" "

    \n" " \n" +" href=\"http://pypi.python.org/pypi?%3Aaction=search&term=kotti\">\n" " Kotti add-ons\n" " \n" "

    \n" @@ -76,13 +128,13 @@ msgid "" "
    \n" "

    Documentation

    \n" "

    \n" -" Wonder what more you can do with Kotti? What license it has? " -"Read the\n" -" manual for more information.\n" +" Wonder what more you can do with Kotti?\n" +" What license it has?\n" +" Read the manual for more information.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/\">\n" " Documentation\n" " \n" "

    \n" @@ -90,23 +142,20 @@ msgid "" "
    \n" msgstr "" -#: kotti/populate.py:121 +#: kotti/populate.py:126 msgid "About" msgstr "" -#: kotti/populate.py:122 -msgid "" -"Our company is the leading manufacturer of foo widgets used in a wide " -"variety of aviation and and industrial products." +#: kotti/populate.py:127 +msgid "Our company is the leading manufacturer of foo widgets used in a wide variety of aviation and and industrial products." msgstr "" -#: kotti/populate.py:123 +#: kotti/populate.py:128 msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -123,172 +172,65 @@ msgid "" "\n" "\n" "

    \n" -" Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" -" \n" -" Copyright info.\n" -" Originally published in the\n" -" Extra EA-300\n" -" article.\n" +"Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +"\n" +"Copyright info.\n" +"Originally published in the\n" +" Extra EA-300\n" +"article.\n" "

    \n" msgstr "" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 -msgid "Document" -msgstr "Document" - -#: kotti/resources.py:649 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 -msgid "File" -msgstr "Bestand" - -#: kotti/resources.py:726 kotti/views/edit/content.py:144 -msgid "Image" -msgstr "Afbeelding" - -#: kotti/resources.py:500 -msgid "Folder view" -msgstr "Map weergave" - -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 -msgid "Contents" -msgstr "Inhoud" - -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" -msgstr "Bewerken" - -#: kotti/resources.py:489 -msgid "Share" -msgstr "Delen" - -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" -msgstr "Acties" - -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 -msgid "Copy" -msgstr "Kopiëren" - -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 -msgid "Cut" -msgstr "Knippen" - -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 -msgid "Paste" -msgstr "Plakken" - -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 -#: kotti/templates/edit/rename-nodes.pt:55 kotti/templates/edit/rename.pt:42 -#: kotti/views/edit/actions.py:447 -msgid "Rename" -msgstr "Hernoemen" - -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 -#: kotti/templates/edit/delete-nodes.pt:90 kotti/templates/edit/delete.pt:28 -#: kotti/templates/site-setup/delete-user.pt:36 kotti/views/users.py:443 -#: kotti/views/edit/actions.py:449 -msgid "Delete" -msgstr "Verwijderen" - -#: kotti/security.py:165 -msgid "Viewer" -msgstr "Lezer" - -#: kotti/security.py:166 -msgid "Editor" -msgstr "Editor" - -#: kotti/security.py:167 -msgid "Owner" -msgstr "Eigenaar" - -#: kotti/security.py:168 -msgid "Admin" -msgstr "Beheerder" - -#: kotti/templates/add-dropdown.pt:5 -msgid "Add" -msgstr "Toevoegen" - -#: kotti/templates/add-dropdown.pt:27 -msgid "Upload Content" -msgstr "Inhoud Opladen" +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "Welkom, ${user}! U bent nu aangemeld." -#: kotti/templates/default-view-selector.pt:5 -msgid "Set default view" -msgstr "Zet standaard weergave" +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "Wilkom, je bent niet aangemeld." -#: kotti/templates/editor-bar.pt:35 +#: kotti/templates/editor-bar.pt:19 msgid "View" msgstr "Weergeven" -#: kotti/templates/editor-bar.pt:76 +#: kotti/templates/editor-bar.pt:42 msgid "Navigate" msgstr "Navigeren" -#: kotti/templates/editor-bar.pt:98 +#: kotti/templates/editor-bar.pt:53 msgid "Preferences" msgstr "Voorkeuren" -#: kotti/templates/editor-bar.pt:109 +#: kotti/templates/editor-bar.pt:58 msgid "Site Setup" msgstr "Site Instellingen" -#: kotti/templates/editor-bar.pt:131 +#: kotti/templates/editor-bar.pt:69 msgid "Logout" msgstr "Afmelden" -#: kotti/templates/email-reset-password.pt:3 -msgid "Reset your password for ${site_title}." -msgstr "Herstel je wachtwoord voor ${site_title}." - -#: kotti/templates/email-reset-password.pt:7 -#: kotti/templates/email-set-password.pt:6 -msgid "Hello, ${user_title}!" -msgstr "Welkom, ${user}!" - -#: kotti/templates/email-reset-password.pt:10 -msgid "Click this link to reset your password at ${site_title}: ${url}" -msgstr "" -"Klik deze link om je wachtwoord opnieuw in te stellen voor ${site_title}: " -"${url}" - -#: kotti/templates/email-set-password.pt:3 -msgid "Your registration for ${site_title}" -msgstr "Je registratie voor ${site_title}" - -#: kotti/templates/email-set-password.pt:9 -msgid "You are joining ${site_title}." -msgstr "Je treedt toe tot ${site_title}." - -#: kotti/templates/email-set-password.pt:12 -msgid "Click here to set your password and log in: ${url}" -msgstr "" -"Klik hier om je wachtwoord opnieuw in te stellen en aan te melden: ${url}" - -#: kotti/templates/forbidden.pt:7 -msgid "Forbidden" -msgstr "Verboden" +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "Zet standaard weergave" #: kotti/templates/login.pt:16 msgid "Login" msgstr "Aanmelden" -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 +#: kotti/templates/login.pt:21 kotti/templates/login.pt:59 msgid "Username or email" msgstr "Gebuikersnaam of e-mail" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 -#: kotti/views/users.py:212 +#: kotti/templates/login.pt:26 kotti/views/users.py:220 +#: kotti/views/login.py:255 msgid "Password" msgstr "Wachtwoord" -#: kotti/templates/login.pt:47 +#: kotti/templates/login.pt:34 msgid "Log in" msgstr "Aanmelden" -#: kotti/templates/login.pt:65 +#: kotti/templates/login.pt:45 msgid "Forgot your password?" msgstr "Wachtwoord vergeten?" @@ -296,621 +238,651 @@ msgstr "Wachtwoord vergeten?" #. ${reset_password} below to receive an email with a link to reset your #. password." #. -#: kotti/templates/login.pt:77 kotti/templates/login.pt:100 +#: kotti/templates/login.pt:52 kotti/templates/login.pt:67 msgid "Reset password" msgstr "Wachtwoord herstellen" #. Canonical text for ${reset_password} is: "Reset password" -#: kotti/templates/login.pt:74 +#: kotti/templates/login.pt:50 #, fuzzy -msgid "" -"Fill out your username or email and click ${reset_password} below to receive " -"an email with a link to reset your password." -msgstr "" -"Vul je gebruikersnaam of e-mailadres in en klik op ${reset_password} " -"hieronder om een e-mail te ontvangen om je wachtwoord te herstellen." +msgid "Fill out your username or email and click ${reset_password} below to receive an email with a link to reset your password." +msgstr "Vul je gebruikersnaam of e-mailadres in en klik op ${reset_password} hieronder om een e-mail te ontvangen om je wachtwoord te herstellen." -#: kotti/templates/login.pt:118 +#: kotti/templates/login.pt:78 msgid "Not registered yet?" msgstr "" -#: kotti/templates/login.pt:127 +#: kotti/templates/login.pt:85 #, fuzzy msgid "Register for an account on this site." msgstr "Nog niet geregistreerd? ${register} voor een account op deze site." -#: kotti/templates/workflow-dropdown.pt:19 +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "Verboden" + +#: kotti/templates/add-dropdown.pt:3 +msgid "Add" +msgstr "Toevoegen" + +#: kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" +msgstr "Inhoud Opladen" + +#: kotti/templates/workflow-dropdown.pt:14 msgid "Make ${state}" msgstr "Maak ${state}" -#: kotti/templates/edit/breadcrumbs.pt:5 +#: kotti/templates/email-reset-password.pt:2 +msgid "Reset your password for ${site_title}." +msgstr "Herstel je wachtwoord voor ${site_title}." + +#: kotti/templates/email-reset-password.pt:4 +#: kotti/templates/email-set-password.pt:4 +msgid "Hello, ${user_title}!" +msgstr "Welkom, ${user}!" + +#: kotti/templates/email-reset-password.pt:5 +msgid "Click this link to reset your password at ${site_title}: ${url}" +msgstr "Klik deze link om je wachtwoord opnieuw in te stellen voor ${site_title}: ${url}" + +#: kotti/templates/email-set-password.pt:2 +msgid "Your registration for ${site_title}" +msgstr "Je registratie voor ${site_title}" + +#: kotti/templates/email-set-password.pt:5 +msgid "You are joining ${site_title}." +msgstr "Je treedt toe tot ${site_title}." + +#: kotti/templates/email-set-password.pt:6 +msgid "Click here to set your password and log in: ${url}" +msgstr "Klik hier om je wachtwoord opnieuw in te stellen en aan te melden: ${url}" + +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" +msgstr "Delen ${title}" + +#: kotti/templates/edit/share.pt:14 kotti/templates/edit/share.pt:20 +#: kotti/templates/edit/share.pt:27 kotti/templates/site-setup/users.pt:48 +msgid "Search users and groups" +msgstr "Zoeken naar gebruikers en groepen" + +#: kotti/templates/edit/share.pt:34 kotti/templates/site-setup/users.pt:56 +msgid "Search" +msgstr "Zoeken" + +#: kotti/templates/edit/share.pt:44 +msgid "Assign local roles" +msgstr "Locale rollen toewijzen" + +#: kotti/templates/edit/share.pt:49 kotti/templates/edit/change-state.pt:19 +#: kotti/templates/edit/delete-nodes.pt:22 kotti/templates/edit/upload.pt:18 +#: kotti/templates/edit/upload.pt:108 kotti/templates/edit/contents.pt:35 +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:69 +msgid "Type" +msgstr "Type" + +#: kotti/templates/edit/share.pt:50 kotti/templates/site-setup/users.pt:70 +#: kotti/views/users.py:215 +msgid "Name" +msgstr "Naam" + +#: kotti/templates/edit/share.pt:61 kotti/templates/site-setup/users.pt:81 +#: kotti/views/users.py:321 kotti/views/users.py:515 kotti/views/users.py:550 +msgid "User" +msgstr "gebruiker" + +#: kotti/templates/edit/share.pt:62 kotti/templates/site-setup/users.pt:82 +#: kotti/views/users.py:196 kotti/views/users.py:356 kotti/views/users.py:515 +#: kotti/views/users.py:550 +msgid "Group" +msgstr "Groep" + +#: kotti/templates/edit/share.pt:65 kotti/templates/site-setup/users.pt:85 +msgid "Gravatar" +msgstr "Gravatar" + +#: kotti/templates/edit/share.pt:70 kotti/templates/site-setup/users.pt:91 +#, fuzzy +msgid "Assign role" +msgstr "Locale rollen toewijzen" + +#: kotti/templates/edit/share.pt:85 kotti/templates/edit/change-state.pt:72 +#: kotti/templates/site-setup/users.pt:107 +msgid "Apply changes" +msgstr "Wijzigingen toepassen" + +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "Site Navigeren" + +#: kotti/templates/edit/breadcrumbs.pt:8 msgid "You are here:" msgstr "U bent hier:" -#: kotti/templates/edit/change-state.pt:7 +#: kotti/templates/edit/delete.pt:9 kotti/templates/site-setup/delete-user.pt:9 +msgid "Delete ${title}" +msgstr "Verwijder ${title}" + +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" +msgstr "Ben je zeker dat je ${title} wil verwijderen?" + +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:74 +#: kotti/templates/edit/delete-nodes.pt:44 +#: kotti/templates/edit/rename-nodes.pt:44 kotti/views/users.py:324 +#: kotti/views/users.py:359 kotti/views/users.py:453 kotti/views/form.py:81 +msgid "Cancel" +msgstr "Annuleren" + +#: kotti/templates/edit/change-state.pt:8 msgid "Change workflow state" msgstr "Wijzig de workflow status" -#: kotti/templates/edit/change-state.pt:14 +#: kotti/templates/edit/change-state.pt:12 msgid "An item's workflow state determines who can see, edit and/or manage it." -msgstr "" -"Een item zijn workflow status bepaalt wie het kan zien, bewerken en/of " -"beheren." +msgstr "Een item zijn workflow status bepaalt wie het kan zien, bewerken en/of beheren." -#: kotti/templates/edit/change-state.pt:26 kotti/templates/edit/contents.pt:57 -#: kotti/templates/edit/delete-nodes.pt:33 kotti/templates/view/folder.pt:28 -#: kotti/views/users.py:196 kotti/views/edit/content.py:31 +#: kotti/templates/edit/change-state.pt:18 +#: kotti/templates/edit/delete-nodes.pt:21 kotti/templates/edit/contents.pt:34 +#: kotti/templates/view/folder.pt:20 kotti/views/users.py:204 +#: kotti/views/edit/content.py:28 msgid "Title" msgstr "Titel" -#: kotti/templates/edit/change-state.pt:29 kotti/templates/edit/contents.pt:60 -#: kotti/templates/edit/delete-nodes.pt:36 kotti/templates/edit/share.pt:70 -#: kotti/templates/edit/upload.pt:23 kotti/templates/edit/upload.pt:167 -#: kotti/templates/site-setup/users.pt:105 kotti/templates/view/folder.pt:31 -msgid "Type" -msgstr "Type" - -#: kotti/templates/edit/change-state.pt:32 kotti/templates/edit/contents.pt:63 -#: kotti/templates/edit/delete-nodes.pt:39 +#: kotti/templates/edit/change-state.pt:20 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:36 msgid "State" msgstr "Status" -#: kotti/templates/edit/change-state.pt:35 kotti/templates/edit/contents.pt:69 -#: kotti/templates/edit/delete-nodes.pt:42 kotti/templates/view/folder.pt:34 +#: kotti/templates/edit/change-state.pt:21 +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:38 +#: kotti/templates/view/folder.pt:22 msgid "Creation Date" msgstr "Aanmaak datum" -#: kotti/templates/edit/change-state.pt:38 kotti/templates/edit/contents.pt:72 -#: kotti/templates/edit/delete-nodes.pt:45 kotti/templates/view/folder.pt:37 +#: kotti/templates/edit/change-state.pt:22 +#: kotti/templates/edit/delete-nodes.pt:25 kotti/templates/edit/contents.pt:39 +#: kotti/templates/view/folder.pt:23 msgid "Modification Date" msgstr "Wijzigingsdatum" -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 +#: kotti/templates/edit/change-state.pt:28 +#: kotti/templates/edit/delete-nodes.pt:31 kotti/templates/edit/contents.pt:45 +msgid "Select item" +msgstr "" + +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:48 msgid "Include children" msgstr "Inclusief onderliggende" -#: kotti/templates/edit/change-state.pt:82 -msgid "" -"If checked, this will attempt to modify the status of all children in any " -"selected documents and their subdocuments." -msgstr "" -"Indien aangevinkt zal een poging ondernomen worden om de status van alle " -"onderliggende items te wijzigen van de geselecteerd documenten en hun " -"onderliggende documenten." +#: kotti/templates/edit/change-state.pt:41 +msgid "If checked, this will attempt to modify the status of all children in any selected documents and their subdocuments." +msgstr "Indien aangevinkt zal een poging ondernomen worden om de status van alle onderliggende items te wijzigen van de geselecteerd documenten en hun onderliggende documenten." -#: kotti/templates/edit/change-state.pt:100 +#: kotti/templates/edit/change-state.pt:54 msgid "Change state to" msgstr "Wij status naar" -#: kotti/templates/edit/change-state.pt:103 +#: kotti/templates/edit/change-state.pt:55 msgid "Select the new state where all chosen items should be set." -msgstr "" -"Selecteer de nieuwe status waarin alle geselecteerde items dienen gezet te " -"worden." +msgstr "Selecteer de nieuwe status waarin alle geselecteerde items dienen gezet te worden." -#: kotti/templates/edit/change-state.pt:111 +#: kotti/templates/edit/change-state.pt:60 msgid "No change" msgstr "Geen wijzigingen" -#: kotti/templates/edit/change-state.pt:127 kotti/templates/edit/share.pt:121 -#: kotti/templates/site-setup/users.pt:165 -msgid "Apply changes" -msgstr "Wijzigingen toepassen" - -#: kotti/templates/edit/change-state.pt:130 -#: kotti/templates/edit/delete-nodes.pt:93 -#: kotti/templates/edit/rename-nodes.pt:58 kotti/views/form.py:78 -#: kotti/views/users.py:316 kotti/views/users.py:350 kotti/views/users.py:442 -msgid "Cancel" -msgstr "Annuleren" - -#: kotti/templates/edit/contents.pt:43 -msgid "No content items are contained here." -msgstr "Bevat geen content items." - -#: kotti/templates/edit/contents.pt:66 -msgid "Visibility" -msgstr "Zichtbaarheid" - -#: kotti/templates/edit/contents.pt:125 -msgid "Visible" -msgstr "Zichtbaar" - -#: kotti/templates/edit/contents.pt:134 -msgid "Hidden" -msgstr "Verborgen" - -#: kotti/templates/edit/delete-nodes.pt:17 +#: kotti/templates/edit/delete-nodes.pt:13 msgid "Are you sure you want to delete the following items?" msgstr "Ben je zeker dat je de volgende items wil verwijderen?" -#: kotti/templates/edit/delete.pt:8 -#: kotti/templates/site-setup/delete-user.pt:8 -msgid "Delete ${title}" -msgstr "Verwijder ${title}" - -#: kotti/templates/edit/delete.pt:20 -msgid "Are you sure you want to delete ${title}?" -msgstr "Ben je zeker dat je ${title} wil verwijderen?" - -#: kotti/templates/edit/nav-tree-view.pt:7 -msgid "Navigate Site" -msgstr "Site Navigeren" +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "Hernoemen ${title}" -#: kotti/templates/edit/rename-nodes.pt:16 kotti/templates/edit/rename.pt:14 -msgid "" -"Each content item has a name, which is used to create the url, and a title. " -"Read the user manual about proper formatting of name and title if needed. " -"You can change both below." -msgstr "" -"Elk inhoudsitem heeft een naam die gebruikt wordt om een url en titel aan te " -"maken. Lees de gebruikershandleiding inzake correcte naamgeving voor titel " -"en naam. Je kan beiden hieronder aanpassen." +#: kotti/templates/edit/rename.pt:11 kotti/templates/edit/rename-nodes.pt:13 +msgid "Each content item has a name, which is used to create the url, and a title. Read the user manual about proper formatting of name and title if needed. You can change both below." +msgstr "Elk inhoudsitem heeft een naam die gebruikt wordt om een url en titel aan te maken. Lees de gebruikershandleiding inzake correcte naamgeving voor titel en naam. Je kan beiden hieronder aanpassen." -#: kotti/templates/edit/rename-nodes.pt:35 kotti/templates/edit/rename.pt:26 +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +#: kotti/templates/edit/rename-nodes.pt:28 msgid "New name" msgstr "Nieuwe naam" -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:34 +#: kotti/templates/edit/rename-nodes.pt:36 msgid "New title" msgstr "Nieuwe titel" -#: kotti/templates/edit/rename.pt:7 -msgid "Rename ${title}" -msgstr "Hernoemen ${title}" - -#: kotti/templates/edit/share.pt:7 -msgid "Share ${title}" -msgstr "Delen ${title}" - -#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:30 -#: kotti/templates/edit/share.pt:35 kotti/templates/site-setup/users.pt:68 -msgid "Search users and groups" -msgstr "Zoeken naar gebruikers en groepen" - -#: kotti/templates/edit/share.pt:43 kotti/templates/site-setup/users.pt:81 -msgid "Search" -msgstr "Zoeken" - -#: kotti/templates/edit/share.pt:60 -msgid "Assign local roles" -msgstr "Locale rollen toewijzen" - -#: kotti/templates/edit/share.pt:73 kotti/templates/site-setup/users.pt:108 -#: kotti/views/users.py:207 -msgid "Name" -msgstr "Naam" - -#: kotti/templates/edit/share.pt:91 kotti/templates/site-setup/users.pt:126 -#: kotti/views/users.py:313 kotti/views/users.py:501 kotti/views/users.py:536 -msgid "User" -msgstr "gebruiker" - -#: kotti/templates/edit/share.pt:94 kotti/templates/site-setup/users.pt:129 -#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 -#: kotti/views/users.py:536 -msgid "Group" -msgstr "Groep" - -#: kotti/templates/edit/share.pt:101 kotti/templates/site-setup/users.pt:136 -msgid "Gravatar" -msgstr "Gravatar" - -#: kotti/templates/edit/upload.pt:9 +#: kotti/templates/edit/upload.pt:10 msgid "Upload content from local file(s)" msgstr "" -#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:164 +#: kotti/templates/edit/upload.pt:17 kotti/templates/edit/upload.pt:107 #, fuzzy msgid "Filename" msgstr "Hernoemen" -#: kotti/templates/edit/upload.pt:26 kotti/templates/edit/upload.pt:170 +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:109 msgid "Size" msgstr "" -#: kotti/templates/edit/upload.pt:29 +#: kotti/templates/edit/upload.pt:20 msgid "Status" msgstr "" -#: kotti/templates/edit/upload.pt:32 +#: kotti/templates/edit/upload.pt:21 msgid "Progress" msgstr "" -#: kotti/templates/edit/upload.pt:124 +#: kotti/templates/edit/upload.pt:84 msgid "Select file(s) to upload..." msgstr "" -#: kotti/templates/edit/upload.pt:134 +#: kotti/templates/edit/upload.pt:92 msgid "Upload ${number} files." msgstr "" -#: kotti/templates/edit/upload.pt:144 +#: kotti/templates/edit/upload.pt:97 msgid "Dismiss all errors" msgstr "" -#: kotti/templates/site-setup/delete-user.pt:20 +#: kotti/templates/edit/contents.pt:26 +msgid "No content items are contained here." +msgstr "Bevat geen content items." + +#: kotti/templates/edit/contents.pt:33 +msgid "Select / deselect all" +msgstr "" + +#: kotti/templates/edit/contents.pt:37 +msgid "Visibility" +msgstr "Zichtbaarheid" + +#: kotti/templates/edit/contents.pt:69 +msgid "Visible" +msgstr "Zichtbaar" + +#: kotti/templates/edit/contents.pt:73 +msgid "Hidden" +msgstr "Verborgen" + +#: kotti/templates/http-errors/notfound.pt:26 +msgid "Not Found" +msgstr "" + +#: kotti/templates/http-errors/notfound.pt:27 +msgid "The resource could not be found" +msgstr "" + +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "Zoek Resultaten" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "Zoeken..." + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "Getagd met:" + +#: kotti/templates/site-setup/delete-user.pt:15 msgid "Are you sure you want to delete ${type} ${title}?" msgstr "Ben je zeker dat je ${type} ${title} wil verwijderen?" -#: kotti/templates/site-setup/user.pt:13 +#: kotti/templates/site-setup/user.pt:12 msgid "Back to User Management" msgstr "Terug naar gebruiksersbeheer" -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 +#: kotti/templates/site-setup/user.pt:21 kotti/views/form.py:159 #, python-format msgid "Edit ${title}" msgstr "Bewerken ${title}" -#: kotti/templates/site-setup/users.pt:15 +#: kotti/templates/site-setup/users.pt:12 #, fuzzy msgid "Search user(s) / group(s)" msgstr "Zoeken naar gebruikers en groepen" -#: kotti/templates/site-setup/users.pt:24 +#: kotti/templates/site-setup/users.pt:17 msgid "Add user" msgstr "Gebruiker toevoegen" -#: kotti/templates/site-setup/users.pt:33 +#: kotti/templates/site-setup/users.pt:22 msgid "Add group" msgstr "Groep toevoegen" -#: kotti/templates/site-setup/users.pt:52 +#: kotti/templates/site-setup/users.pt:34 #, fuzzy msgid "Find users or groups" msgstr "Zoeken naar gebruikers en groepen" -#: kotti/templates/site-setup/users.pt:63 +#: kotti/templates/site-setup/users.pt:40 msgid "User- / groupname" msgstr "" -#: kotti/templates/site-setup/users.pt:70 +#: kotti/templates/site-setup/users.pt:50 #, fuzzy msgid "Blank search text finds all." -msgstr "" -"Zoek en wijzig gebruikers (laat tekstveld leeg om alle gebruikers te vinden." +msgstr "Zoek en wijzig gebruikers (laat tekstveld leeg om alle gebruikers te vinden." -#: kotti/templates/site-setup/users.pt:96 +#: kotti/templates/site-setup/users.pt:65 #, fuzzy msgid "Assign global roles" msgstr "Locale rollen toewijzen" -#: kotti/templates/site-setup/users.pt:180 +#: kotti/templates/site-setup/users.pt:117 #, fuzzy msgid "Add new user" msgstr "Gebruiker toevoegen" -#: kotti/templates/site-setup/users.pt:190 +#: kotti/templates/site-setup/users.pt:123 #, fuzzy msgid "Add new group" msgstr "Groep toevoegen" -#: kotti/templates/view/search-results.pt:8 -msgid "Search Results" -msgstr "Zoek Resultaten" - -#: kotti/templates/view/search.pt:5 -msgid "Search..." -msgstr "Zoeken..." - -#: kotti/templates/view/tags.pt:5 -msgid "Tagged with:" -msgstr "Getagd met:" - -#: kotti/tests/testing_view.pt:7 -msgid "Welcome, ${user}! You are logged in." -msgstr "Welkom, ${user}! U bent nu aangemeld." - -#: kotti/tests/testing_view.pt:14 -msgid "Welcome, you are not logged in." -msgstr "Wilkom, je bent niet aangemeld." - -#: kotti/views/form.py:79 kotti/views/users.py:69 -#: kotti/views/edit/actions.py:363 kotti/views/edit/actions.py:407 +#: kotti/views/users.py:70 kotti/views/form.py:82 +#: kotti/views/edit/actions.py:371 kotti/views/edit/actions.py:415 msgid "Your changes have been saved." msgstr "Je wijzigingen werden opgeslagen." -#: kotti/views/form.py:174 -msgid "Item was added." -msgstr "Item werd toegevoegd." - -#: kotti/views/form.py:264 -#, python-format -msgid "Maximum file size: ${size}MB" -msgstr "Maximale bestandsgrootte: ${size}MB" - -#: kotti/views/form.py:77 kotti/views/users.py:441 -msgid "Save" -msgstr "Opslaan" - -#: kotti/views/form.py:198 -#, python-format -msgid "Add ${type} to ${title}." -msgstr "Voeg ${type} aan ${title} toe." - -#: kotti/views/form.py:202 -#, python-format -msgid "Add ${type}." -msgstr "Voeg ${type} toe." - -#: kotti/views/login.py:238 -msgid "You have reset your password." -msgstr "Je wachtwoord werd hersteld." - -#: kotti/views/login.py:200 -msgid "You have been logged out." -msgstr "Je bent afgemeld." - -#: kotti/views/login.py:66 kotti/views/users.py:270 -msgid "Full name" -msgstr "Volledige naam" - -#: kotti/views/login.py:69 -msgid "Username" -msgstr "Gebruikersnaam" - -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 -msgid "Email" -msgstr "E-mailadres" - -#: kotti/views/login.py:109 -msgid "" -"Congratulations! You are successfully registered. You should be receiving an " -"email with a link to set your password. Doing so will activate your account." -msgstr "" -"Proficiat! Je bent geregistreerd. Je zal een e-mail ontvangen met een link " -"om je wachtwoord in te stellen. Deze handeling zal je account activeren." - -#: kotti/views/login.py:124 -#, python-format -msgid "Register - ${title}" -msgstr "Refistreer - ${title}" - -#: kotti/views/login.py:164 -msgid "Login failed." -msgstr "Aanmelden mislukt." - -#: kotti/views/login.py:286 -#, python-format -msgid "Reset your password - ${title}." -msgstr "Herstel je wachtwoord - ${title}." - -#: kotti/views/login.py:160 -#, python-format -msgid "Welcome, ${user}!" -msgstr "Welkom, ${user}!" - -#: kotti/views/login.py:173 -msgid "" -"You should be receiving an email with a link to reset your password. Doing " -"so will activate your account." -msgstr "" -"Je zal een e-mail ontvangen met een link om je wachtwoord te herstellen. Via " -"deze link activeer je je account." - -#: kotti/views/login.py:178 -msgid "That username or email is not known by this system." -msgstr "Deze gebruikersnaam of e-mailadres is ons onbekend." - -#: kotti/views/login.py:83 -msgid "Register" -msgstr "Registreer" - -#: kotti/views/login.py:90 kotti/views/login.py:257 -msgid "There was an error." -msgstr "Er trad een fout op." - -#: kotti/views/login.py:250 -msgid "Submit" -msgstr "Verzenden" - -#: kotti/views/login.py:279 -msgid "Your password reset token may have expired." -msgstr "Je wachtwoord herstellen link is mogelijks vervallen." - -#: kotti/views/site_setup.py:6 kotti/views/users.py:378 -msgid "User Management" -msgstr "Gebruikersbeheer" - -#: kotti/views/users.py:267 -msgid "" -"Leave this empty and tick the 'Send password registration' box below to have " -"the user set their own password." -msgstr "" -"Laat dit veld leeg en vink de optie 'Verstuur wachtwoord registratie' " -"hieronder aan zodat de gebruiker zelf een wachtwoord kan instellen." +#: kotti/views/users.py:72 kotti/views/users.py:477 +#: kotti/views/edit/actions.py:309 kotti/views/edit/actions.py:375 +#: kotti/views/edit/actions.py:417 kotti/views/edit/actions.py:421 +msgid "No changes were made." +msgstr "Geen wijzigingen aangebracht." -#: kotti/views/users.py:587 -#, python-format -msgid "My preferences - ${title}" -msgstr "Mijn instellingen - ${title}" +#: kotti/views/users.py:105 +msgid "No users or groups were found." +msgstr "Geen gebruikers of groepen werden gevonden." -#: kotti/views/users.py:145 +#: kotti/views/users.py:153 msgid "Invalid value" msgstr "Ongeldige waarde" -#: kotti/views/users.py:151 +#: kotti/views/users.py:159 msgid "A user with that name already exists." msgstr "Een gebruiker met deze naam bestaat reeds." -#: kotti/views/users.py:158 +#: kotti/views/users.py:166 msgid "A user with that email already exists." msgstr "Een gebruiker met deze naam bestaat reeds." -#: kotti/views/users.py:181 +#: kotti/views/users.py:189 #, python-format msgid "No such group: ${group}" msgstr "Onbekende groep: ${group}" -#: kotti/views/users.py:219 +#: kotti/views/users.py:207 kotti/views/login.py:71 kotti/views/login.py:267 +msgid "Email" +msgstr "E-mailadres" + +#: kotti/views/users.py:227 msgid "Active" msgstr "Actief" -#: kotti/views/users.py:220 +#: kotti/views/users.py:228 msgid "Untick this to deactivate the account." msgstr "Uitvinken om je account te de-activeren." -#: kotti/views/users.py:226 +#: kotti/views/users.py:234 msgid "Global roles" msgstr "Globale rollen" -#: kotti/views/users.py:230 +#: kotti/views/users.py:238 msgid "Groups" msgstr "Groepen" -#: kotti/views/users.py:315 +#: kotti/views/users.py:275 +msgid "Leave this empty and tick the 'Send password registration' box below to have the user set their own password." +msgstr "Laat dit veld leeg en vink de optie 'Verstuur wachtwoord registratie' hieronder aan zodat de gebruiker zelf een wachtwoord kan instellen." + +#: kotti/views/users.py:278 kotti/views/login.py:63 +msgid "Full name" +msgstr "Volledige naam" + +#: kotti/views/users.py:323 msgid "Add User" msgstr "Gebruiker toevoegen" -#: kotti/views/users.py:339 +#: kotti/views/users.py:333 +msgid "Send password registration link." +msgstr "Verstuur wachtwoord registratie link." + +#: kotti/views/users.py:348 #, python-format msgid "${title} was added." msgstr "${title} werd toegevoegd." -#: kotti/views/users.py:349 +#: kotti/views/users.py:358 msgid "Add Group" msgstr "Groep toevoegen" -#: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:301 kotti/views/edit/actions.py:367 -#: kotti/views/edit/actions.py:413 kotti/views/edit/actions.py:409 -msgid "No changes were made." -msgstr "Geen wijzigingen aangebracht." - -#: kotti/views/users.py:558 -msgid "No name was given." -msgstr "Er werd geen naam ingevoerd." +#: kotti/views/users.py:388 kotti/views/site_setup.py:5 +msgid "User Management" +msgstr "Gebruikersbeheer" -#: kotti/views/users.py:98 -msgid "No users or groups were found." -msgstr "Geen gebruikers of groepen werden gevonden." +#: kotti/views/users.py:452 kotti/views/form.py:80 +msgid "Save" +msgstr "Opslaan" -#: kotti/views/users.py:505 +#: kotti/views/users.py:519 #, python-format msgid "Edit ${principal_type} ${title}" msgstr "Bewerk ${principal_type} ${title}" -#: kotti/views/users.py:533 +#: kotti/views/users.py:547 msgid "User was not found." msgstr "De gebruiker werd niet gevonden." -#: kotti/views/users.py:324 -msgid "Send password registration link." -msgstr "Verstuur wachtwoord registratie link." - -#: kotti/views/users.py:543 +#: kotti/views/users.py:557 #, python-format msgid "${principal_type} ${title} was deleted." msgstr "${principal_type} ${title} werd verwijderd." -#: kotti/views/users.py:551 +#: kotti/views/users.py:565 #, python-format msgid "Delete ${principal_type} ${title}" msgstr "Verwijder ${principal_type} ${title}" -#: kotti/views/edit/actions.py:106 +#: kotti/views/users.py:572 +msgid "No name was given." +msgstr "Er werd geen naam ingevoerd." + +#: kotti/views/users.py:601 #, python-format -msgid "${title} was copied." -msgstr "${title} werd gekopieerd." +msgid "My preferences - ${title}" +msgstr "Mijn instellingen - ${title}" + +#: kotti/views/form.py:177 +msgid "Item was added." +msgstr "Item werd toegevoegd." -#: kotti/views/edit/actions.py:124 +#: kotti/views/form.py:201 #, python-format -msgid "${title} was cut." -msgstr "${title} werd geknipt." +msgid "Add ${type} to ${title}." +msgstr "Voeg ${type} aan ${title} toe." -#: kotti/views/edit/actions.py:182 +#: kotti/views/form.py:205 #, python-format -msgid "${title} was moved." -msgstr "${title} werd verplaatst." +msgid "Add ${type}." +msgstr "Voeg ${type} toe." -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/form.py:295 #, python-format -msgid "${title} was deleted." -msgstr "${title} werd verwijderd." +msgid "Maximum file size: ${size}MB" +msgstr "Maximale bestandsgrootte: ${size}MB" -#: kotti/views/edit/actions.py:158 +#: kotti/views/login.py:66 +msgid "Username" +msgstr "Gebruikersnaam" + +#: kotti/views/login.py:80 +msgid "Register" +msgstr "Registreer" + +#: kotti/views/login.py:87 +msgid "There was an error." +msgstr "Er trad een fout op." + +#: kotti/views/login.py:106 +msgid "Congratulations! You are successfully registered. You should be receiving an email with a link to set your password. Doing so will activate your account." +msgstr "Proficiat! Je bent geregistreerd. Je zal een e-mail ontvangen met een link om je wachtwoord in te stellen. Deze handeling zal je account activeren." + +#: kotti/views/login.py:121 +#, python-format +msgid "Register - ${title}" +msgstr "Refistreer - ${title}" + +#: kotti/views/login.py:152 +#, python-format +msgid "Welcome, ${user}!" +msgstr "Welkom, ${user}!" + +#: kotti/views/login.py:177 +msgid "You should be receiving an email with a link to reset your password. Doing so will activate your account." +msgstr "Je zal een e-mail ontvangen met een link om je wachtwoord te herstellen. Via deze link activeer je je account." + +#: kotti/views/login.py:210 +msgid "Login failed." +msgstr "Aanmelden mislukt." + +#: kotti/views/login.py:220 +msgid "That username or email is not known by this system." +msgstr "Deze gebruikersnaam of e-mailadres is ons onbekend." + +#: kotti/views/login.py:242 +msgid "You have been logged out." +msgstr "Je bent afgemeld." + +#: kotti/views/login.py:280 +msgid "You have reset your password." +msgstr "Je wachtwoord werd hersteld." + +#: kotti/views/login.py:299 +#, fuzzy +msgid "Set password" +msgstr "Wachtwoord herstellen" + +#: kotti/views/login.py:327 +msgid "Your password reset token may have expired." +msgstr "Je wachtwoord herstellen link is mogelijks vervallen." + +#: kotti/views/login.py:334 +#, python-format +msgid "Reset your password - ${title}." +msgstr "Herstel je wachtwoord - ${title}." + +#: kotti/views/edit/default_views.py:86 +msgid "Default view" +msgstr "Standaard weergave" + +#: kotti/views/edit/default_views.py:107 +msgid "Default view has been reset to default." +msgstr "Standaard weergave werd hersteld naar standaard." + +#: kotti/views/edit/default_views.py:114 +msgid "Default view has been set." +msgstr "Standaard weergave is ingesteld." + +#: kotti/views/edit/default_views.py:119 +msgid "Default view could not be set." +msgstr "Standaar weergave kon niet ingesteld worden." + +#: kotti/views/edit/actions.py:102 +#, python-format +msgid "${title} was copied." +msgstr "${title} werd gekopieerd." + +#: kotti/views/edit/actions.py:120 +#, python-format +msgid "${title} was cut." +msgstr "${title} werd geknipt." + +#: kotti/views/edit/actions.py:155 #, python-format msgid "${title} was pasted." msgstr "${title} werd geplakt." -#: kotti/views/edit/actions.py:161 +#: kotti/views/edit/actions.py:158 msgid "Could not paste node. It no longer exists." msgstr "De node kon niet geplakt worden. Hij bestaat niet meer." -#: kotti/views/edit/actions.py:224 +#: kotti/views/edit/actions.py:180 +#, python-format +msgid "${title} was moved." +msgstr "${title} werd verplaatst." + +#: kotti/views/edit/actions.py:222 #, python-format msgid "${title} is now visible in the navigation." msgstr "${title} is nu zichtbaar in de navigatie." -#: kotti/views/edit/actions.py:227 +#: kotti/views/edit/actions.py:225 #, python-format msgid "${title} is no longer visible in the navigation." msgstr "${title} is niet langer zichtbaar in de navigatie." -#: kotti/views/edit/actions.py:292 +#: kotti/views/edit/actions.py:274 kotti/views/edit/actions.py:303 +#, python-format +msgid "${title} was deleted." +msgstr "${title} werd verwijderd." + +#: kotti/views/edit/actions.py:300 msgid "Nothing was deleted." msgstr "Er werd niks verwijderd." -#: kotti/views/edit/actions.py:328 kotti/views/edit/actions.py:355 +#: kotti/views/edit/actions.py:336 kotti/views/edit/actions.py:363 msgid "Name and title are required." msgstr "Naam en titel zijn vereist." -#: kotti/views/edit/actions.py:332 +#: kotti/views/edit/actions.py:340 msgid "Item was renamed." msgstr "Item werd hernoemd." -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:461 +msgid "Change State" +msgstr "Wijzig Status." + +#: kotti/views/edit/actions.py:462 msgid "Move up" msgstr "Naar boven" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:463 msgid "Move down" msgstr "Naar beneden" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:464 msgid "Show" msgstr "Weergeven" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:465 msgid "Hide" msgstr "Verbergen" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:504 msgid "You have to select items to perform an action." msgstr "Je dient items te selecteren om acties uit te voeren." -#: kotti/views/edit/actions.py:453 -msgid "Change State" -msgstr "Wijzig Status." - -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:34 msgid "Description" msgstr "Beschrijving" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:40 msgid "Tags" msgstr "Tags" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:49 msgid "Body" msgstr "Inhoud" -#: kotti/views/edit/default_views.py:99 -msgid "Default view has been reset to default." -msgstr "Standaard weergave werd hersteld naar standaard." - -#: kotti/views/edit/default_views.py:78 -msgid "Default view" -msgstr "Standaard weergave" - -#: kotti/views/edit/default_views.py:106 -msgid "Default view has been set." -msgstr "Standaard weergave is ingesteld." +#~ msgid "Image" +#~ msgstr "Afbeelding" -#: kotti/views/edit/default_views.py:111 -msgid "Default view could not be set." -msgstr "Standaar weergave kon niet ingesteld worden." +#~ msgid "Submit" +#~ msgstr "Verzenden" #~ msgid "Private" #~ msgstr "Privaat" diff --git a/kotti/locale/pl/LC_MESSAGES/Kotti.mo b/kotti/locale/pl/LC_MESSAGES/Kotti.mo new file mode 100644 index 000000000..9f0b00c97 Binary files /dev/null and b/kotti/locale/pl/LC_MESSAGES/Kotti.mo differ diff --git a/kotti/locale/pl/LC_MESSAGES/Kotti.po b/kotti/locale/pl/LC_MESSAGES/Kotti.po new file mode 100644 index 000000000..c0f712f78 --- /dev/null +++ b/kotti/locale/pl/LC_MESSAGES/Kotti.po @@ -0,0 +1,949 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2018-01-17 13:51+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" + +#: kotti/security.py:173 +msgid "Viewer" +msgstr "Czytelnik" + +#: kotti/security.py:174 +msgid "Editor" +msgstr "Edytor" + +#: kotti/security.py:175 +msgid "Owner" +msgstr "Właściciel" + +#: kotti/security.py:176 +msgid "Admin" +msgstr "Administrator" + +#: kotti/resources.py:541 kotti/views/edit/actions.py:453 +msgid "Copy" +msgstr "Kopiuj" + +#: kotti/resources.py:542 kotti/views/edit/actions.py:454 +msgid "Cut" +msgstr "Wytnij" + +#: kotti/resources.py:543 kotti/views/edit/actions.py:450 +msgid "Paste" +msgstr "Wklej" + +#: kotti/resources.py:544 kotti/templates/edit/rename.pt:31 +#: kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:42 kotti/views/edit/actions.py:455 +msgid "Rename" +msgstr "Zmień nazwę" + +#: kotti/resources.py:545 kotti/templates/edit/delete.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:49 +#: kotti/templates/site-setup/delete-user.pt:24 kotti/views/users.py:454 +#: kotti/views/edit/actions.py:457 +msgid "Delete" +msgstr "Usuń" + +#: kotti/resources.py:556 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "Zawartość" + +#: kotti/resources.py:557 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "Edytuj" + +#: kotti/resources.py:558 +msgid "Share" +msgstr "Udostępnij" + +#: kotti/resources.py:559 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "Akcje" + +#: kotti/resources.py:562 +msgid "Folder view" +msgstr "Widok katalogów" + +#: kotti/resources.py:671 kotti/views/edit/content.py:61 +msgid "Document" +msgstr "Dokument" + +#: kotti/resources.py:808 kotti/views/edit/content.py:73 +#: kotti/views/edit/content.py:87 +msgid "File" +msgstr "Plik" + +#: kotti/populate.py:67 +msgid "Welcome to Kotti" +msgstr "Witaj w Kotti" + +#: kotti/populate.py:68 +msgid "Congratulations! You have successfully installed Kotti." +msgstr "Gratulacje! Właśnie zainstalowałeś Kotti." + +#: kotti/populate.py:69 +#, c-format +msgid "" +"\n" +"

    Log in

    \n" +"

    \n" +" You can log in to your site\n" +" and start changing its contents. If you haven't chosen a password for\n" +" your admin account yet, it'll likely be qwerty.\n" +"

    \n" +"

    \n" +" Once you're logged in, you'll see the grey editor bar below the top\n" +" navigation bar. It will allow you to switch between editing and viewing the\n" +" current page as it will appear to your visitors.\n" +"

    \n" +"
    \n" +"
    \n" +"

    Configure

    \n" +"

    \n" +" Find out how to configure your Kotti's title and many other\n" +" settings using a simple text file in your file system.\n" +"

    \n" +"

    \n" +" \n" +" Configuration manual\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    Add-ons

    \n" +"

    \n" +" A number of add-ons allow you to extend the functionality of your\n" +" Kotti site.\n" +"

    \n" +"

    \n" +" \n" +" Kotti add-ons\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    Documentation

    \n" +"

    \n" +" Wonder what more you can do with Kotti?\n" +" What license it has?\n" +" Read the manual for more information.\n" +"

    \n" +"

    \n" +" \n" +" Documentation\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +msgstr "" +"\n" +"

    Zaloguj się

    \n" +"

    \n" +" Możesz zalogować się do twojego serwisu\n" +" i zmieniać jego zawartość. Jeżeli jeszcze nie ustaliłeś hasła dla\n" +" konta administratora, domyślnie jest to qwerty.\n" +"

    \n" +"

    \n" +" Po zalogowaniu zobaczysz szare menu edycyjne poniżej\n" +" menu nawigacyjnego. Umożliwia ono przełączanie się między edycją a wyglądem aktualnej strony widzianej z perspektywy użytkowników.\n" +"

    \n" +"
    \n" +"
    \n" +"

    Konfiguracja

    \n" +"

    \n" +" Dowiedz się jak skonfigurować tytył i inne ustawienia używając prostego pliku tekstowego w systemie plików.\n" +"

    \n" +"

    \n" +" \n" +" Podręcznik konfiguracji\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    Wtyczki

    \n" +"

    \n" +" Wtyczki pozwalają na rozszerzenie funkcjonalności twojego serwisu opartego na Kotti.\n" +"

    \n" +"

    \n" +" \n" +" Wtyczki do Kotti\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    Dokumentacja

    \n" +"

    \n" +" Zastanawiasz się co jeszcze możesz zrobić korzystając z Kotti? Jaką ma licencję? Przeczytaj podręcznik aby dowiedzieć się więcej.\n" +"

    \n" +"

    \n" +" \n" +" Dokumentacja\n" +" \n" +"

    \n" +"
    \n" +"
    \n" + +#: kotti/populate.py:126 +msgid "About" +msgstr "Informacje" + +#: kotti/populate.py:127 +msgid "Our company is the leading manufacturer of foo widgets used in a wide variety of aviation and and industrial products." +msgstr "Nasza firma jest wiodącym producentem urządzeń foo używanych w szerokim asortymencie produktów dla lotnictwa i przemysłu." + +#: kotti/populate.py:128 +msgid "" +"\n" +"

    \n" +" \"five\n" +"

    \n" +"\n" +"

    \n" +" Our worldwide headquarters:\n" +"

    \n" +"\n" +"
    \n" +" Foo World
    \n" +" 123 Nowhere Street, Suite 777
    \n" +" Omak, WA 98841 USA
    \n" +" +1-509-555-0100
    \n" +" widgets@foowrld.example.com\n" +"
    \n" +"\n" +"

    \n" +"Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +"\n" +"Copyright info.\n" +"Originally published in the\n" +" Extra EA-300\n" +"article.\n" +"

    \n" +msgstr "" +"\n" +"

    \n" +" \"pięć\n" +"

    \n" +"\n" +"

    \n" +" Nasza siedziba:\n" +"

    \n" +"\n" +"
    \n" +" Foo World
    \n" +" 123 Nowhere Street, Suite 777
    \n" +" Omak, WA 98841 USA
    \n" +" +1-509-555-0100
    \n" +" widgets@foowrld.example.com\n" +"
    \n" +"\n" +"

    \n" +" Zdjęcie: \"Northern Lights Formation\" autorstwa FlugKerl2.\n" +" \n" +" Prawa autorskie.\n" +" Oryginalnie opublikowane w artykule\n" +" Extra EA-300.\n" +"

    \n" + +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "Witaj, ${user}! Jesteś zalogowany." + +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "Witaj, nie jesteś zalogowany." + +#: kotti/templates/editor-bar.pt:19 +msgid "View" +msgstr "Widok" + +#: kotti/templates/editor-bar.pt:42 +msgid "Navigate" +msgstr "Nawigacja" + +#: kotti/templates/editor-bar.pt:53 +msgid "Preferences" +msgstr "Ustawienia" + +#: kotti/templates/editor-bar.pt:58 +msgid "Site Setup" +msgstr "Ustawienia strony" + +#: kotti/templates/editor-bar.pt:69 +msgid "Logout" +msgstr "Wyloguj się" + +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "Ustaw domyślny widok" + +#: kotti/templates/login.pt:16 +msgid "Login" +msgstr "Logowanie" + +#: kotti/templates/login.pt:21 kotti/templates/login.pt:59 +msgid "Username or email" +msgstr "Nazwa użytkownika lub email" + +#: kotti/templates/login.pt:26 kotti/views/users.py:220 +#: kotti/views/login.py:255 +msgid "Password" +msgstr "Hasło" + +#: kotti/templates/login.pt:34 +msgid "Log in" +msgstr "Zaloguj się" + +#: kotti/templates/login.pt:45 +msgid "Forgot your password?" +msgstr "Zapomniałeś hasła?" + +#. Used in sentence: "Fill out your username or email and click +#. ${reset_password} below to receive an email with a link to reset your +#. password." +#. +#: kotti/templates/login.pt:52 kotti/templates/login.pt:67 +msgid "Reset password" +msgstr "Reset hasła" + +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:50 +msgid "Fill out your username or email and click ${reset_password} below to receive an email with a link to reset your password." +msgstr "Uzupełnij pole z nazwą użytkownika lub email i kliknij na ${reset_password} poniżej, aby otrzymać email z linkiem do zresetowania hasła." + +#: kotti/templates/login.pt:78 +msgid "Not registered yet?" +msgstr "Nie jesteś jeszcze zarejestrowany?" + +#: kotti/templates/login.pt:85 +msgid "Register for an account on this site." +msgstr "Zarejestruj się na stronie." + +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "Brak dostępu" + +#: kotti/templates/add-dropdown.pt:3 +msgid "Add" +msgstr "Dodaj" + +#: kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" +msgstr "Dołącz treści" + +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "Zrób ${state}" + +#: kotti/templates/email-reset-password.pt:2 +msgid "Reset your password for ${site_title}." +msgstr "Zresetuj swoje hasło do ${site_title}." + +#: kotti/templates/email-reset-password.pt:4 +#: kotti/templates/email-set-password.pt:4 +msgid "Hello, ${user_title}!" +msgstr "Witaj, ${user_title}!" + +#: kotti/templates/email-reset-password.pt:5 +msgid "Click this link to reset your password at ${site_title}: ${url}" +msgstr "Kliknij na link, aby zresetować hasło do ${site_title}: ${url}" + +#: kotti/templates/email-set-password.pt:2 +msgid "Your registration for ${site_title}" +msgstr "Rejestracja na ${site_title}" + +#: kotti/templates/email-set-password.pt:5 +msgid "You are joining ${site_title}." +msgstr "Rejestrujesz się na ${site_title}." + +#: kotti/templates/email-set-password.pt:6 +msgid "Click here to set your password and log in: ${url}" +msgstr "Kliknij tutuaj, aby ustawić hasło i zalogować się do: ${url}" + +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" +msgstr "Udostępnij ${title}" + +#: kotti/templates/edit/share.pt:14 kotti/templates/edit/share.pt:20 +#: kotti/templates/edit/share.pt:27 kotti/templates/site-setup/users.pt:48 +msgid "Search users and groups" +msgstr "Szukaj użytkowników i grup" + +#: kotti/templates/edit/share.pt:34 kotti/templates/site-setup/users.pt:56 +msgid "Search" +msgstr "Szukaj" + +#: kotti/templates/edit/share.pt:44 +msgid "Assign local roles" +msgstr "Przypisz lokalne role" + +#: kotti/templates/edit/share.pt:49 kotti/templates/edit/change-state.pt:19 +#: kotti/templates/edit/delete-nodes.pt:22 kotti/templates/edit/upload.pt:18 +#: kotti/templates/edit/upload.pt:108 kotti/templates/edit/contents.pt:35 +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:69 +msgid "Type" +msgstr "Typ" + +#: kotti/templates/edit/share.pt:50 kotti/templates/site-setup/users.pt:70 +#: kotti/views/users.py:215 +msgid "Name" +msgstr "Nazwa" + +#: kotti/templates/edit/share.pt:61 kotti/templates/site-setup/users.pt:81 +#: kotti/views/users.py:321 kotti/views/users.py:515 kotti/views/users.py:550 +msgid "User" +msgstr "Użytkownik" + +#: kotti/templates/edit/share.pt:62 kotti/templates/site-setup/users.pt:82 +#: kotti/views/users.py:196 kotti/views/users.py:356 kotti/views/users.py:515 +#: kotti/views/users.py:550 +msgid "Group" +msgstr "Grupa" + +#: kotti/templates/edit/share.pt:65 kotti/templates/site-setup/users.pt:85 +msgid "Gravatar" +msgstr "Gravatar" + +#: kotti/templates/edit/share.pt:70 kotti/templates/site-setup/users.pt:91 +msgid "Assign role" +msgstr "Przypisz rolę" + +#: kotti/templates/edit/share.pt:85 kotti/templates/edit/change-state.pt:72 +#: kotti/templates/site-setup/users.pt:107 +msgid "Apply changes" +msgstr "Zapisz zmiany" + +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "Nawigacja strony" + +#: kotti/templates/edit/breadcrumbs.pt:8 +msgid "You are here:" +msgstr "Jesteś tutaj:" + +#: kotti/templates/edit/delete.pt:9 kotti/templates/site-setup/delete-user.pt:9 +msgid "Delete ${title}" +msgstr "Usuń ${title}" + +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" +msgstr "Czy jesteś pewien, że chcesz usunąć ${title}?" + +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:74 +#: kotti/templates/edit/delete-nodes.pt:44 +#: kotti/templates/edit/rename-nodes.pt:44 kotti/views/users.py:324 +#: kotti/views/users.py:359 kotti/views/users.py:453 kotti/views/form.py:81 +msgid "Cancel" +msgstr "Anuluj" + +#: kotti/templates/edit/change-state.pt:8 +msgid "Change workflow state" +msgstr "Zmień stan publikacji" + +#: kotti/templates/edit/change-state.pt:12 +msgid "An item's workflow state determines who can see, edit and/or manage it." +msgstr "Stan publikacji elementu określa kto może go zobaczyć, edytować i/lub zarządzać nim." + +#: kotti/templates/edit/change-state.pt:18 +#: kotti/templates/edit/delete-nodes.pt:21 kotti/templates/edit/contents.pt:34 +#: kotti/templates/view/folder.pt:20 kotti/views/users.py:204 +#: kotti/views/edit/content.py:28 +msgid "Title" +msgstr "Tytuł" + +#: kotti/templates/edit/change-state.pt:20 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:36 +msgid "State" +msgstr "Stan" + +#: kotti/templates/edit/change-state.pt:21 +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:38 +#: kotti/templates/view/folder.pt:22 +msgid "Creation Date" +msgstr "Data utworzenia" + +#: kotti/templates/edit/change-state.pt:22 +#: kotti/templates/edit/delete-nodes.pt:25 kotti/templates/edit/contents.pt:39 +#: kotti/templates/view/folder.pt:23 +msgid "Modification Date" +msgstr "Data modyfikacji" + +#: kotti/templates/edit/change-state.pt:28 +#: kotti/templates/edit/delete-nodes.pt:31 kotti/templates/edit/contents.pt:45 +msgid "Select item" +msgstr "Wybierz obiekt" + +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:48 +msgid "Include children" +msgstr "Załącz potomka" + +#: kotti/templates/edit/change-state.pt:41 +msgid "If checked, this will attempt to modify the status of all children in any selected documents and their subdocuments." +msgstr "Jeśli zaznaczysz, zmiana obejmie status wszystkich potomków zaznaczonych dokumentów, oraz ich poddokumenty." + +#: kotti/templates/edit/change-state.pt:54 +msgid "Change state to" +msgstr "Zmień stan na" + +#: kotti/templates/edit/change-state.pt:55 +msgid "Select the new state where all chosen items should be set." +msgstr "Zaznacz nowy stan, gdzie wszystkie wybrane elementy zostaną zmienione." + +#: kotti/templates/edit/change-state.pt:60 +msgid "No change" +msgstr "Bez zmian" + +#: kotti/templates/edit/delete-nodes.pt:13 +msgid "Are you sure you want to delete the following items?" +msgstr "Czy jesteś pewien, że chcesz usunąć wybrane elementy?" + +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "Zmień nazwę ${title}" + +#: kotti/templates/edit/rename.pt:11 kotti/templates/edit/rename-nodes.pt:13 +msgid "Each content item has a name, which is used to create the url, and a title. Read the user manual about proper formatting of name and title if needed. You can change both below." +msgstr "Każda element ma swoje imię, które używane jest do tworzenia url'a i tytułu. W razie potrzeby przeczytaj poradnik użytkownika o poprawnym formatowaniu nazwy i tytułu. Możesz zmienić obie rzeczy niżej." + +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +#: kotti/templates/edit/rename-nodes.pt:28 +msgid "New name" +msgstr "Nowa nazwa" + +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:34 +#: kotti/templates/edit/rename-nodes.pt:36 +msgid "New title" +msgstr "Nowy tytuł" + +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" +msgstr "Załącz zawartość z lokalnego pliku(ów)" + +#: kotti/templates/edit/upload.pt:17 kotti/templates/edit/upload.pt:107 +msgid "Filename" +msgstr "Nazwa pliku" + +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:109 +msgid "Size" +msgstr "Rozmiar" + +#: kotti/templates/edit/upload.pt:20 +msgid "Status" +msgstr "Status" + +#: kotti/templates/edit/upload.pt:21 +msgid "Progress" +msgstr "Postęp" + +#: kotti/templates/edit/upload.pt:84 +msgid "Select file(s) to upload..." +msgstr "Wybierz plik(i) do przesłania..." + +#: kotti/templates/edit/upload.pt:92 +msgid "Upload ${number} files." +msgstr "Prześlij ${number} plików." + +#: kotti/templates/edit/upload.pt:97 +msgid "Dismiss all errors" +msgstr "Zignoruj wszystkie błędy" + +#: kotti/templates/edit/contents.pt:26 +msgid "No content items are contained here." +msgstr "Brak elementów potomnych." + +#: kotti/templates/edit/contents.pt:33 +msgid "Select / deselect all" +msgstr "Zaznacz / odznacz wszystko" + +#: kotti/templates/edit/contents.pt:37 +msgid "Visibility" +msgstr "Widoczność" + +#: kotti/templates/edit/contents.pt:69 +msgid "Visible" +msgstr "Widoczny" + +#: kotti/templates/edit/contents.pt:73 +msgid "Hidden" +msgstr "Ukryty" + +#: kotti/templates/http-errors/notfound.pt:26 +msgid "Not Found" +msgstr "Nie znaleziono" + +#: kotti/templates/http-errors/notfound.pt:27 +msgid "The resource could not be found" +msgstr "Zasób nie został znaleziony" + +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "Wyniki wyszukania" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "Szukaj..." + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "Otagowane z:" + +#: kotti/templates/site-setup/delete-user.pt:15 +msgid "Are you sure you want to delete ${type} ${title}?" +msgstr "Czy jesteś pewien, że chesz usunąć ${type} ${title}?" + +#: kotti/templates/site-setup/user.pt:12 +msgid "Back to User Management" +msgstr "Powrót do zarządzania użytkownikami" + +#: kotti/templates/site-setup/user.pt:21 kotti/views/form.py:159 +#, python-format +msgid "Edit ${title}" +msgstr "Edycja ${title}" + +#: kotti/templates/site-setup/users.pt:12 +msgid "Search user(s) / group(s)" +msgstr "Szukaj użytkownika(ów) / grupę(y)" + +#: kotti/templates/site-setup/users.pt:17 +msgid "Add user" +msgstr "Dodaj użytkownika" + +#: kotti/templates/site-setup/users.pt:22 +msgid "Add group" +msgstr "Dodaj grupę" + +#: kotti/templates/site-setup/users.pt:34 +msgid "Find users or groups" +msgstr "Znajdź użytkowników lub grupy" + +#: kotti/templates/site-setup/users.pt:40 +msgid "User- / groupname" +msgstr "Użytkownik- / nazwa grupy" + +#: kotti/templates/site-setup/users.pt:50 +msgid "Blank search text finds all." +msgstr "Zostaw puste, aby znaleźć wszystko." + +#: kotti/templates/site-setup/users.pt:65 +msgid "Assign global roles" +msgstr "Przypisz globalne role" + +#: kotti/templates/site-setup/users.pt:117 +msgid "Add new user" +msgstr "Dodaj nowego użytkownika" + +#: kotti/templates/site-setup/users.pt:123 +msgid "Add new group" +msgstr "Dodaj nową grupę" + +#: kotti/views/users.py:70 kotti/views/form.py:82 +#: kotti/views/edit/actions.py:371 kotti/views/edit/actions.py:415 +msgid "Your changes have been saved." +msgstr "Twoje zmiany zostały zapisane." + +#: kotti/views/users.py:72 kotti/views/users.py:477 +#: kotti/views/edit/actions.py:309 kotti/views/edit/actions.py:375 +#: kotti/views/edit/actions.py:417 kotti/views/edit/actions.py:421 +msgid "No changes were made." +msgstr "Nie wykonano żadnych zmian." + +#: kotti/views/users.py:105 +msgid "No users or groups were found." +msgstr "Nie znaleziono użytkowników ani grup." + +#: kotti/views/users.py:153 +msgid "Invalid value" +msgstr "Niepoprawna wartość" + +#: kotti/views/users.py:159 +msgid "A user with that name already exists." +msgstr "Użytkownik z takim imieniem już istnieje." + +#: kotti/views/users.py:166 +msgid "A user with that email already exists." +msgstr "Użytkownik z takim adresem email już istnieje." + +#: kotti/views/users.py:189 +#, python-format +msgid "No such group: ${group}" +msgstr "Nie ma takiej grupy: ${group}" + +#: kotti/views/users.py:207 kotti/views/login.py:71 kotti/views/login.py:267 +msgid "Email" +msgstr "Email" + +#: kotti/views/users.py:227 +msgid "Active" +msgstr "Aktywne" + +#: kotti/views/users.py:228 +msgid "Untick this to deactivate the account." +msgstr "Odznacz, aby dezaktywować konto." + +#: kotti/views/users.py:234 +msgid "Global roles" +msgstr "Globalne role" + +#: kotti/views/users.py:238 +msgid "Groups" +msgstr "Grupy" + +#: kotti/views/users.py:275 +msgid "Leave this empty and tick the 'Send password registration' box below to have the user set their own password." +msgstr "Zostaw to puste i zaznacz 'Wyślij link do rejestracji' poniżej, aby użytkownik sam ustawił sobie hasło." + +#: kotti/views/users.py:278 kotti/views/login.py:63 +msgid "Full name" +msgstr "Imię i nazwisko" + +#: kotti/views/users.py:323 +msgid "Add User" +msgstr "Dodaj użytkownika" + +#: kotti/views/users.py:333 +msgid "Send password registration link." +msgstr "Wyślij link do rejestracji na email." + +#: kotti/views/users.py:348 +#, python-format +msgid "${title} was added." +msgstr "${title} został dodany." + +#: kotti/views/users.py:358 +msgid "Add Group" +msgstr "Dodaj grupę" + +#: kotti/views/users.py:388 kotti/views/site_setup.py:5 +msgid "User Management" +msgstr "Zarządzanie użytkownikami" + +#: kotti/views/users.py:452 kotti/views/form.py:80 +msgid "Save" +msgstr "Zapisz" + +#: kotti/views/users.py:519 +#, python-format +msgid "Edit ${principal_type} ${title}" +msgstr "Edycja ${principal_type} ${title}" + +#: kotti/views/users.py:547 +msgid "User was not found." +msgstr "Użytkownik nie został odnaleziony." + +#: kotti/views/users.py:557 +#, python-format +msgid "${principal_type} ${title} was deleted." +msgstr "${principal_type} ${title} został usunięty." + +#: kotti/views/users.py:565 +#, python-format +msgid "Delete ${principal_type} ${title}" +msgstr "Usuń ${principal_type} ${title}" + +#: kotti/views/users.py:572 +msgid "No name was given." +msgstr "Nie przekazano nazwy." + +#: kotti/views/users.py:601 +#, python-format +msgid "My preferences - ${title}" +msgstr "Moje ustawienia - ${title}" + +#: kotti/views/form.py:177 +msgid "Item was added." +msgstr "Element został dodany." + +#: kotti/views/form.py:201 +#, python-format +msgid "Add ${type} to ${title}." +msgstr "Dodaj ${type} do ${title}." + +#: kotti/views/form.py:205 +#, python-format +msgid "Add ${type}." +msgstr "Dodaj ${type}." + +#: kotti/views/form.py:295 +#, python-format +msgid "Maximum file size: ${size}MB" +msgstr "Maksymalny rozmiar pliku: ${size}MB" + +#: kotti/views/login.py:66 +msgid "Username" +msgstr "Nazwa użytkownika" + +#: kotti/views/login.py:80 +msgid "Register" +msgstr "Zarejestruj" + +#: kotti/views/login.py:87 +msgid "There was an error." +msgstr "Wystąpił błąd." + +#: kotti/views/login.py:106 +msgid "Congratulations! You are successfully registered. You should be receiving an email with a link to set your password. Doing so will activate your account." +msgstr "Gratulacje! Zarejestrowałeś się. Powinieneś otrzymać email z linkiem do ustawienia swego hasła. Robiąc to aktywujesz konto." + +#: kotti/views/login.py:121 +#, python-format +msgid "Register - ${title}" +msgstr "Zarejestruj - ${title}" + +#: kotti/views/login.py:152 +#, python-format +msgid "Welcome, ${user}!" +msgstr "Witaj, ${user}!" + +#: kotti/views/login.py:177 +msgid "You should be receiving an email with a link to reset your password. Doing so will activate your account." +msgstr "Powinieneś otrzymać email z linkiem do resetu hasła. Resetując hasło aktywujesz swoje konto." + +#: kotti/views/login.py:210 +msgid "Login failed." +msgstr "Nie udało się zalogować." + +#: kotti/views/login.py:220 +msgid "That username or email is not known by this system." +msgstr "Nieznana nazwa użytkownika lub email." + +#: kotti/views/login.py:242 +msgid "You have been logged out." +msgstr "Zostałeś wylogowany." + +#: kotti/views/login.py:280 +msgid "You have reset your password." +msgstr "Zresetowałeś swoje hasło." + +#: kotti/views/login.py:299 +msgid "Set password" +msgstr "Ustaw hasło" + +#: kotti/views/login.py:327 +msgid "Your password reset token may have expired." +msgstr "Twój token do resetu hasła mógł stracić ważność." + +#: kotti/views/login.py:334 +#, python-format +msgid "Reset your password - ${title}." +msgstr "Resetowanie hasła - ${title}." + +#: kotti/views/edit/default_views.py:86 +msgid "Default view" +msgstr "Domyślny widok" + +#: kotti/views/edit/default_views.py:107 +msgid "Default view has been reset to default." +msgstr "Zresetowano widok na domyślny." + +#: kotti/views/edit/default_views.py:114 +msgid "Default view has been set." +msgstr "Ustawiono domyślny widok." + +#: kotti/views/edit/default_views.py:119 +msgid "Default view could not be set." +msgstr "Domyślny widok nie mógł zostać ustawiony." + +#: kotti/views/edit/actions.py:102 +#, python-format +msgid "${title} was copied." +msgstr "${title} został skopiowany." + +#: kotti/views/edit/actions.py:120 +#, python-format +msgid "${title} was cut." +msgstr "${title} został wycięty." + +#: kotti/views/edit/actions.py:155 +#, python-format +msgid "${title} was pasted." +msgstr "${title} został wklejony." + +#: kotti/views/edit/actions.py:158 +msgid "Could not paste node. It no longer exists." +msgstr "Nie można wkleić. Element już nie istnieje." + +#: kotti/views/edit/actions.py:180 +#, python-format +msgid "${title} was moved." +msgstr "${title} został przeniesiony." + +#: kotti/views/edit/actions.py:222 +#, python-format +msgid "${title} is now visible in the navigation." +msgstr "${title} jest teraz widoczny w nawigacji." + +#: kotti/views/edit/actions.py:225 +#, python-format +msgid "${title} is no longer visible in the navigation." +msgstr "${title} nie jest już widoczny w nawigacji." + +#: kotti/views/edit/actions.py:274 kotti/views/edit/actions.py:303 +#, python-format +msgid "${title} was deleted." +msgstr "${title} został usunięty." + +#: kotti/views/edit/actions.py:300 +msgid "Nothing was deleted." +msgstr "Nic nie zostało usunięte" + +#: kotti/views/edit/actions.py:336 kotti/views/edit/actions.py:363 +msgid "Name and title are required." +msgstr "Nazwa i tytuł są obowiązkowe." + +#: kotti/views/edit/actions.py:340 +msgid "Item was renamed." +msgstr "Zmieniono nazwę elementu." + +#: kotti/views/edit/actions.py:461 +msgid "Change State" +msgstr "Zmień stan" + +#: kotti/views/edit/actions.py:462 +msgid "Move up" +msgstr "W górę" + +#: kotti/views/edit/actions.py:463 +msgid "Move down" +msgstr "W dół" + +#: kotti/views/edit/actions.py:464 +msgid "Show" +msgstr "Pokaż" + +#: kotti/views/edit/actions.py:465 +msgid "Hide" +msgstr "Ukryj" + +#: kotti/views/edit/actions.py:504 +msgid "You have to select items to perform an action." +msgstr "Musisz zaznaczyć elementy, aby wykonać akcję." + +#: kotti/views/edit/content.py:34 +msgid "Description" +msgstr "Opis" + +#: kotti/views/edit/content.py:40 +msgid "Tags" +msgstr "Tagi" + +#: kotti/views/edit/content.py:49 +msgid "Body" +msgstr "Treść" + +#~ msgid "Image" +#~ msgstr "Obraz" + +#~ msgid "Submit" +#~ msgstr "Zatwierdź" diff --git a/kotti/locale/pt/LC_MESSAGES/Kotti.mo b/kotti/locale/pt/LC_MESSAGES/Kotti.mo index eaf4bd6fe..dde197a9e 100644 Binary files a/kotti/locale/pt/LC_MESSAGES/Kotti.mo and b/kotti/locale/pt/LC_MESSAGES/Kotti.mo differ diff --git a/kotti/locale/pt/LC_MESSAGES/Kotti.po b/kotti/locale/pt/LC_MESSAGES/Kotti.po index 0f59bc137..df084a2af 100644 --- a/kotti/locale/pt/LC_MESSAGES/Kotti.po +++ b/kotti/locale/pt/LC_MESSAGES/Kotti.po @@ -1,60 +1,113 @@ -# Portuguese translations for Kotti. -# Copyright (C) 2012 ORGANIZATION -# This file is distributed under the same license as the Kotti project. -# Nuno Teixeira , 2012. -# msgid "" msgstr "" -"Project-Id-Version: Kotti 0.8\n" -"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2014-11-21 21:56+0100\n" -"PO-Revision-Date: 2012-01-16 12:02+0000\n" -"Last-Translator: Nuno Teixeira \n" -"Language-Team: pt \n" -"Language: \n" +"POT-Creation-Date: 2018-01-17 13:51+0100\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" -"Generated-By: Babel 0.9.6\n" -#: kotti/populate.py:64 +#: kotti/security.py:173 +msgid "Viewer" +msgstr "Leitor" + +#: kotti/security.py:174 +msgid "Editor" +msgstr "Editor" + +#: kotti/security.py:175 +msgid "Owner" +msgstr "Dono" + +#: kotti/security.py:176 +msgid "Admin" +msgstr "Admin" + +#: kotti/resources.py:541 kotti/views/edit/actions.py:453 +msgid "Copy" +msgstr "Copiar" + +#: kotti/resources.py:542 kotti/views/edit/actions.py:454 +msgid "Cut" +msgstr "Cortar" + +#: kotti/resources.py:543 kotti/views/edit/actions.py:450 +msgid "Paste" +msgstr "Colar" + +#: kotti/resources.py:544 kotti/templates/edit/rename.pt:31 +#: kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:42 kotti/views/edit/actions.py:455 +msgid "Rename" +msgstr "Renomear" + +#: kotti/resources.py:545 kotti/templates/edit/delete.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:49 +#: kotti/templates/site-setup/delete-user.pt:24 kotti/views/users.py:454 +#: kotti/views/edit/actions.py:457 +msgid "Delete" +msgstr "Apagar" + +#: kotti/resources.py:556 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "Conteúdo" + +#: kotti/resources.py:557 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "Editar" + +#: kotti/resources.py:558 +msgid "Share" +msgstr "Partilha" + +#: kotti/resources.py:559 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "Ações" + +#: kotti/resources.py:562 +msgid "Folder view" +msgstr "Vista de pasta" + +#: kotti/resources.py:671 kotti/views/edit/content.py:61 +msgid "Document" +msgstr "Documento" + +#: kotti/resources.py:808 kotti/views/edit/content.py:73 +#: kotti/views/edit/content.py:87 +msgid "File" +msgstr "Ficheiro" + +#: kotti/populate.py:67 msgid "Welcome to Kotti" msgstr "" -#: kotti/populate.py:65 +#: kotti/populate.py:68 msgid "Congratulations! You have successfully installed Kotti." msgstr "" -#: kotti/populate.py:66 +#: kotti/populate.py:69 #, c-format msgid "" "\n" "

    Log in

    \n" "

    \n" -" You can log in to your " -"site\n" +" You can log in to your site\n" " and start changing its contents. If you haven't chosen a password for\n" " your admin account yet, it'll likely be qwerty.\n" "

    \n" "

    \n" " Once you're logged in, you'll see the grey editor bar below the top\n" -" navigation bar. It will allow you to switch between editing and viewing " -"the\n" +" navigation bar. It will allow you to switch between editing and viewing the\n" " current page as it will appear to your visitors.\n" "

    \n" "
    \n" "
    \n" "

    Configure

    \n" "

    \n" -" Find out how to configure your Kotti's title and many other " -"settings using a\n" -" simple text file in your file system.\n" +" Find out how to configure your Kotti's title and many other\n" +" settings using a simple text file in your file system.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -62,13 +115,12 @@ msgid "" "
    \n" "

    Add-ons

    \n" "

    \n" -" A number of add-ons allow you to extend the functionality of " -"your Kotti site.\n" +" A number of add-ons allow you to extend the functionality of your\n" +" Kotti site.\n" "

    \n" "

    \n" " \n" +" href=\"http://pypi.python.org/pypi?%3Aaction=search&term=kotti\">\n" " Kotti add-ons\n" " \n" "

    \n" @@ -76,13 +128,13 @@ msgid "" "
    \n" "

    Documentation

    \n" "

    \n" -" Wonder what more you can do with Kotti? What license it has? " -"Read the\n" -" manual for more information.\n" +" Wonder what more you can do with Kotti?\n" +" What license it has?\n" +" Read the manual for more information.\n" "

    \n" "

    \n" " \n" +" href=\"https://kotti.readthedocs.io/en/latest/\">\n" " Documentation\n" " \n" "

    \n" @@ -90,23 +142,20 @@ msgid "" "
    \n" msgstr "" -#: kotti/populate.py:121 +#: kotti/populate.py:126 msgid "About" msgstr "" -#: kotti/populate.py:122 -msgid "" -"Our company is the leading manufacturer of foo widgets used in a wide " -"variety of aviation and and industrial products." +#: kotti/populate.py:127 +msgid "Our company is the leading manufacturer of foo widgets used in a wide variety of aviation and and industrial products." msgstr "" -#: kotti/populate.py:123 +#: kotti/populate.py:128 msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -123,812 +172,741 @@ msgid "" "\n" "\n" "

    \n" -" Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" -" \n" -" Copyright info.\n" -" Originally published in the\n" -" Extra EA-300\n" -" article.\n" +"Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +"\n" +"Copyright info.\n" +"Originally published in the\n" +" Extra EA-300\n" +"article.\n" "

    \n" msgstr "" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 -msgid "Document" -msgstr "Documento" +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "Bem-vindo, ${user}! Você está autenticado." -#: kotti/resources.py:649 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 -msgid "File" -msgstr "Ficheiro" +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "Bem-vindo, você não está autenticado." -#: kotti/resources.py:726 kotti/views/edit/content.py:144 -msgid "Image" -msgstr "Imagem" +#: kotti/templates/editor-bar.pt:19 +msgid "View" +msgstr "Ver" -#: kotti/resources.py:500 -msgid "Folder view" -msgstr "Vista de pasta" +#: kotti/templates/editor-bar.pt:42 +msgid "Navigate" +msgstr "Navegar" -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 -msgid "Contents" -msgstr "Conteúdo" +#: kotti/templates/editor-bar.pt:53 +msgid "Preferences" +msgstr "Preferências" -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" -msgstr "Editar" +#: kotti/templates/editor-bar.pt:58 +msgid "Site Setup" +msgstr "Configurar Sítio" -#: kotti/resources.py:489 -msgid "Share" -msgstr "Partilha" +#: kotti/templates/editor-bar.pt:69 +msgid "Logout" +msgstr "Sair" -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" -msgstr "Ações" +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "Definir vista por defeito" -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 -msgid "Copy" -msgstr "Copiar" +#: kotti/templates/login.pt:16 +msgid "Login" +msgstr "Entrar" -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 -msgid "Cut" -msgstr "Cortar" +#: kotti/templates/login.pt:21 kotti/templates/login.pt:59 +msgid "Username or email" +msgstr "Utilizador ou e-mail" -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 -msgid "Paste" -msgstr "Colar" +#: kotti/templates/login.pt:26 kotti/views/users.py:220 +#: kotti/views/login.py:255 +msgid "Password" +msgstr "Palavra passe" -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 -#: kotti/templates/edit/rename-nodes.pt:55 kotti/templates/edit/rename.pt:42 -#: kotti/views/edit/actions.py:447 -msgid "Rename" -msgstr "Renomear" +#: kotti/templates/login.pt:34 +msgid "Log in" +msgstr "Entrar" -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 -#: kotti/templates/edit/delete-nodes.pt:90 kotti/templates/edit/delete.pt:28 -#: kotti/templates/site-setup/delete-user.pt:36 kotti/views/users.py:443 -#: kotti/views/edit/actions.py:449 -msgid "Delete" -msgstr "Apagar" +#: kotti/templates/login.pt:45 +msgid "Forgot your password?" +msgstr "Esqueceu-se da palavra passe?" -#: kotti/security.py:165 -msgid "Viewer" -msgstr "Leitor" +#. Used in sentence: "Fill out your username or email and click +#. ${reset_password} below to receive an email with a link to reset your +#. password." +#. +#: kotti/templates/login.pt:52 kotti/templates/login.pt:67 +msgid "Reset password" +msgstr "Repor a palavra passe" -#: kotti/security.py:166 -msgid "Editor" -msgstr "Editor" +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:50 +#, fuzzy +msgid "Fill out your username or email and click ${reset_password} below to receive an email with a link to reset your password." +msgstr "Preencha o seu nome de utilizador com o seu e-mail e clique em ${reset_password} para receber um e-mail com uma ligação para reiniciar a sua palavra passe." -#: kotti/security.py:167 -msgid "Owner" -msgstr "Dono" +#: kotti/templates/login.pt:78 +msgid "Not registered yet?" +msgstr "" -#: kotti/security.py:168 -msgid "Admin" -msgstr "Admin" +#: kotti/templates/login.pt:85 +#, fuzzy +msgid "Register for an account on this site." +msgstr "Não se encontra registado? ${register} para criar uma conta." + +#: kotti/templates/forbidden.pt:10 +#, fuzzy +msgid "Forbidden" +msgstr "Escondido" -#: kotti/templates/add-dropdown.pt:5 +#: kotti/templates/add-dropdown.pt:3 msgid "Add" msgstr "Adicionar" -#: kotti/templates/add-dropdown.pt:27 +#: kotti/templates/add-dropdown.pt:13 msgid "Upload Content" msgstr "Colocar Conteúdo" -#: kotti/templates/default-view-selector.pt:5 -msgid "Set default view" -msgstr "Definir vista por defeito" - -#: kotti/templates/editor-bar.pt:35 -msgid "View" -msgstr "Ver" - -#: kotti/templates/editor-bar.pt:76 -msgid "Navigate" -msgstr "Navegar" - -#: kotti/templates/editor-bar.pt:98 -msgid "Preferences" -msgstr "Preferências" - -#: kotti/templates/editor-bar.pt:109 -msgid "Site Setup" -msgstr "Configurar Sítio" - -#: kotti/templates/editor-bar.pt:131 -msgid "Logout" -msgstr "Sair" +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "Tornar ${state}" -#: kotti/templates/email-reset-password.pt:3 +#: kotti/templates/email-reset-password.pt:2 #, fuzzy msgid "Reset your password for ${site_title}." msgstr "Reiniciar a sua palavra passe em ${site_title}" -#: kotti/templates/email-reset-password.pt:7 -#: kotti/templates/email-set-password.pt:6 +#: kotti/templates/email-reset-password.pt:4 +#: kotti/templates/email-set-password.pt:4 msgid "Hello, ${user_title}!" msgstr "Bem-vindo, ${user_title}!" -#: kotti/templates/email-reset-password.pt:10 +#: kotti/templates/email-reset-password.pt:5 msgid "Click this link to reset your password at ${site_title}: ${url}" -msgstr "" -"Clique aqui para reiniciar a sua palavra passe em ${site_title}: ${url}" +msgstr "Clique aqui para reiniciar a sua palavra passe em ${site_title}: ${url}" -#: kotti/templates/email-set-password.pt:3 +#: kotti/templates/email-set-password.pt:2 msgid "Your registration for ${site_title}" msgstr "O seu registo em ${site_title}" -#: kotti/templates/email-set-password.pt:9 +#: kotti/templates/email-set-password.pt:5 #, fuzzy msgid "You are joining ${site_title}." msgstr "O seu registo em ${site_title}" -#: kotti/templates/email-set-password.pt:12 +#: kotti/templates/email-set-password.pt:6 msgid "Click here to set your password and log in: ${url}" msgstr "Clique aqui para definir a sua palavra passe e para entrar: ${url}" -#: kotti/templates/forbidden.pt:7 -#, fuzzy -msgid "Forbidden" -msgstr "Escondido" +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" +msgstr "Partilhar ${title}" -#: kotti/templates/login.pt:16 -msgid "Login" -msgstr "Entrar" +#: kotti/templates/edit/share.pt:14 kotti/templates/edit/share.pt:20 +#: kotti/templates/edit/share.pt:27 kotti/templates/site-setup/users.pt:48 +msgid "Search users and groups" +msgstr "Procurar utilizadores e grupos" -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 -msgid "Username or email" -msgstr "Utilizador ou e-mail" +#: kotti/templates/edit/share.pt:34 kotti/templates/site-setup/users.pt:56 +msgid "Search" +msgstr "Procurar" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 -#: kotti/views/users.py:212 -msgid "Password" -msgstr "Palavra passe" +#: kotti/templates/edit/share.pt:44 +msgid "Assign local roles" +msgstr "Atribuir papéis locais" -#: kotti/templates/login.pt:47 -msgid "Log in" -msgstr "Entrar" +#: kotti/templates/edit/share.pt:49 kotti/templates/edit/change-state.pt:19 +#: kotti/templates/edit/delete-nodes.pt:22 kotti/templates/edit/upload.pt:18 +#: kotti/templates/edit/upload.pt:108 kotti/templates/edit/contents.pt:35 +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:69 +msgid "Type" +msgstr "Tipo" -#: kotti/templates/login.pt:65 -msgid "Forgot your password?" -msgstr "Esqueceu-se da palavra passe?" +#: kotti/templates/edit/share.pt:50 kotti/templates/site-setup/users.pt:70 +#: kotti/views/users.py:215 +msgid "Name" +msgstr "Nome" -#. Used in sentence: "Fill out your username or email and click -#. ${reset_password} below to receive an email with a link to reset your -#. password." -#. -#: kotti/templates/login.pt:77 kotti/templates/login.pt:100 -msgid "Reset password" -msgstr "Repor a palavra passe" +#: kotti/templates/edit/share.pt:61 kotti/templates/site-setup/users.pt:81 +#: kotti/views/users.py:321 kotti/views/users.py:515 kotti/views/users.py:550 +msgid "User" +msgstr "Utilizador" -#. Canonical text for ${reset_password} is: "Reset password" -#: kotti/templates/login.pt:74 -#, fuzzy -msgid "" -"Fill out your username or email and click ${reset_password} below to receive " -"an email with a link to reset your password." -msgstr "" -"Preencha o seu nome de utilizador com o seu e-mail e clique em " -"${reset_password} para receber um e-mail com uma ligação para reiniciar a " -"sua palavra passe." +#: kotti/templates/edit/share.pt:62 kotti/templates/site-setup/users.pt:82 +#: kotti/views/users.py:196 kotti/views/users.py:356 kotti/views/users.py:515 +#: kotti/views/users.py:550 +msgid "Group" +msgstr "Grupo" -#: kotti/templates/login.pt:118 -msgid "Not registered yet?" -msgstr "" +#: kotti/templates/edit/share.pt:65 kotti/templates/site-setup/users.pt:85 +msgid "Gravatar" +msgstr "Gravatar" -#: kotti/templates/login.pt:127 +#: kotti/templates/edit/share.pt:70 kotti/templates/site-setup/users.pt:91 #, fuzzy -msgid "Register for an account on this site." -msgstr "Não se encontra registado? ${register} para criar uma conta." +msgid "Assign role" +msgstr "Atribuir papéis locais" -#: kotti/templates/workflow-dropdown.pt:19 -msgid "Make ${state}" -msgstr "Tornar ${state}" +#: kotti/templates/edit/share.pt:85 kotti/templates/edit/change-state.pt:72 +#: kotti/templates/site-setup/users.pt:107 +msgid "Apply changes" +msgstr "Aplicar alterações" + +#: kotti/templates/edit/nav-tree-view.pt:8 +#, fuzzy +msgid "Navigate Site" +msgstr "Navegar" -#: kotti/templates/edit/breadcrumbs.pt:5 +#: kotti/templates/edit/breadcrumbs.pt:8 msgid "You are here:" msgstr "Você está aqui:" -#: kotti/templates/edit/change-state.pt:7 +#: kotti/templates/edit/delete.pt:9 kotti/templates/site-setup/delete-user.pt:9 +msgid "Delete ${title}" +msgstr "Apagar ${title}" + +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" +msgstr "Deseja realmente eliminar ${title}?" + +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:74 +#: kotti/templates/edit/delete-nodes.pt:44 +#: kotti/templates/edit/rename-nodes.pt:44 kotti/views/users.py:324 +#: kotti/views/users.py:359 kotti/views/users.py:453 kotti/views/form.py:81 +msgid "Cancel" +msgstr "Cancelar" + +#: kotti/templates/edit/change-state.pt:8 msgid "Change workflow state" msgstr "Modificar estado de workflow" -#: kotti/templates/edit/change-state.pt:14 +#: kotti/templates/edit/change-state.pt:12 msgid "An item's workflow state determines who can see, edit and/or manage it." -msgstr "" -"O estado de workflow de um item define quem o pode ver, edit e/ou " -"administrar." +msgstr "O estado de workflow de um item define quem o pode ver, edit e/ou administrar." -#: kotti/templates/edit/change-state.pt:26 kotti/templates/edit/contents.pt:57 -#: kotti/templates/edit/delete-nodes.pt:33 kotti/templates/view/folder.pt:28 -#: kotti/views/users.py:196 kotti/views/edit/content.py:31 +#: kotti/templates/edit/change-state.pt:18 +#: kotti/templates/edit/delete-nodes.pt:21 kotti/templates/edit/contents.pt:34 +#: kotti/templates/view/folder.pt:20 kotti/views/users.py:204 +#: kotti/views/edit/content.py:28 msgid "Title" msgstr "Título" -#: kotti/templates/edit/change-state.pt:29 kotti/templates/edit/contents.pt:60 -#: kotti/templates/edit/delete-nodes.pt:36 kotti/templates/edit/share.pt:70 -#: kotti/templates/edit/upload.pt:23 kotti/templates/edit/upload.pt:167 -#: kotti/templates/site-setup/users.pt:105 kotti/templates/view/folder.pt:31 -msgid "Type" -msgstr "Tipo" - -#: kotti/templates/edit/change-state.pt:32 kotti/templates/edit/contents.pt:63 -#: kotti/templates/edit/delete-nodes.pt:39 +#: kotti/templates/edit/change-state.pt:20 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:36 msgid "State" msgstr "Estado" -#: kotti/templates/edit/change-state.pt:35 kotti/templates/edit/contents.pt:69 -#: kotti/templates/edit/delete-nodes.pt:42 kotti/templates/view/folder.pt:34 +#: kotti/templates/edit/change-state.pt:21 +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:38 +#: kotti/templates/view/folder.pt:22 msgid "Creation Date" msgstr "Data de Criação" -#: kotti/templates/edit/change-state.pt:38 kotti/templates/edit/contents.pt:72 -#: kotti/templates/edit/delete-nodes.pt:45 kotti/templates/view/folder.pt:37 +#: kotti/templates/edit/change-state.pt:22 +#: kotti/templates/edit/delete-nodes.pt:25 kotti/templates/edit/contents.pt:39 +#: kotti/templates/view/folder.pt:23 msgid "Modification Date" msgstr "Data de Modificação" -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 +#: kotti/templates/edit/change-state.pt:28 +#: kotti/templates/edit/delete-nodes.pt:31 kotti/templates/edit/contents.pt:45 +msgid "Select item" +msgstr "" + +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:48 msgid "Include children" msgstr "Incluir nós interiores" -#: kotti/templates/edit/change-state.pt:82 -msgid "" -"If checked, this will attempt to modify the status of all children in any " -"selected documents and their subdocuments." -msgstr "" -"Se estiver selecionado irá tentar alterar o estado dos " -"documentosselecionados e dos seus subdocumentos." +#: kotti/templates/edit/change-state.pt:41 +msgid "If checked, this will attempt to modify the status of all children in any selected documents and their subdocuments." +msgstr "Se estiver selecionado irá tentar alterar o estado dos documentosselecionados e dos seus subdocumentos." -#: kotti/templates/edit/change-state.pt:100 +#: kotti/templates/edit/change-state.pt:54 msgid "Change state to" msgstr "Mudar estado para" -#: kotti/templates/edit/change-state.pt:103 +#: kotti/templates/edit/change-state.pt:55 #, fuzzy msgid "Select the new state where all chosen items should be set." msgstr "Selecione o novo estado para os items selecionados." -#: kotti/templates/edit/change-state.pt:111 +#: kotti/templates/edit/change-state.pt:60 msgid "No change" msgstr "Não foram efectuadas nenhumas alterações" -#: kotti/templates/edit/change-state.pt:127 kotti/templates/edit/share.pt:121 -#: kotti/templates/site-setup/users.pt:165 -msgid "Apply changes" -msgstr "Aplicar alterações" - -#: kotti/templates/edit/change-state.pt:130 -#: kotti/templates/edit/delete-nodes.pt:93 -#: kotti/templates/edit/rename-nodes.pt:58 kotti/views/form.py:78 -#: kotti/views/users.py:316 kotti/views/users.py:350 kotti/views/users.py:442 -msgid "Cancel" -msgstr "Cancelar" - -#: kotti/templates/edit/contents.pt:43 -msgid "No content items are contained here." -msgstr "Não existem items nesta pasta." - -#: kotti/templates/edit/contents.pt:66 -msgid "Visibility" -msgstr "Visibilidade" - -#: kotti/templates/edit/contents.pt:125 -msgid "Visible" -msgstr "Visível" - -#: kotti/templates/edit/contents.pt:134 -#, fuzzy -msgid "Hidden" -msgstr "Escondido" - -#: kotti/templates/edit/delete-nodes.pt:17 +#: kotti/templates/edit/delete-nodes.pt:13 msgid "Are you sure you want to delete the following items?" msgstr "Deseja realmente eliminar os items seguintes?" -#: kotti/templates/edit/delete.pt:8 -#: kotti/templates/site-setup/delete-user.pt:8 -msgid "Delete ${title}" -msgstr "Apagar ${title}" - -#: kotti/templates/edit/delete.pt:20 -msgid "Are you sure you want to delete ${title}?" -msgstr "Deseja realmente eliminar ${title}?" - -#: kotti/templates/edit/nav-tree-view.pt:7 -#, fuzzy -msgid "Navigate Site" -msgstr "Navegar" +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "Renomear ${title}" -#: kotti/templates/edit/rename-nodes.pt:16 kotti/templates/edit/rename.pt:14 +#: kotti/templates/edit/rename.pt:11 kotti/templates/edit/rename-nodes.pt:13 #, fuzzy -msgid "" -"Each content item has a name, which is used to create the url, and a title. " -"Read the user manual about proper formatting of name and title if needed. " -"You can change both below." -msgstr "" -"Cada item tem um nome que será usado para criar o url e o título. Poderá " -"alterá-los colocando os novos detalhes abaixo." +msgid "Each content item has a name, which is used to create the url, and a title. Read the user manual about proper formatting of name and title if needed. You can change both below." +msgstr "Cada item tem um nome que será usado para criar o url e o título. Poderá alterá-los colocando os novos detalhes abaixo." -#: kotti/templates/edit/rename-nodes.pt:35 kotti/templates/edit/rename.pt:26 +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +#: kotti/templates/edit/rename-nodes.pt:28 msgid "New name" msgstr "Novo nome" -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:34 +#: kotti/templates/edit/rename-nodes.pt:36 msgid "New title" msgstr "Novo título" -#: kotti/templates/edit/rename.pt:7 -msgid "Rename ${title}" -msgstr "Renomear ${title}" +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" +msgstr "" -#: kotti/templates/edit/share.pt:7 -msgid "Share ${title}" -msgstr "Partilhar ${title}" - -#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:30 -#: kotti/templates/edit/share.pt:35 kotti/templates/site-setup/users.pt:68 -msgid "Search users and groups" -msgstr "Procurar utilizadores e grupos" - -#: kotti/templates/edit/share.pt:43 kotti/templates/site-setup/users.pt:81 -msgid "Search" -msgstr "Procurar" - -#: kotti/templates/edit/share.pt:60 -msgid "Assign local roles" -msgstr "Atribuir papéis locais" - -#: kotti/templates/edit/share.pt:73 kotti/templates/site-setup/users.pt:108 -#: kotti/views/users.py:207 -msgid "Name" -msgstr "Nome" - -#: kotti/templates/edit/share.pt:91 kotti/templates/site-setup/users.pt:126 -#: kotti/views/users.py:313 kotti/views/users.py:501 kotti/views/users.py:536 -msgid "User" -msgstr "Utilizador" - -#: kotti/templates/edit/share.pt:94 kotti/templates/site-setup/users.pt:129 -#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 -#: kotti/views/users.py:536 -msgid "Group" -msgstr "Grupo" - -#: kotti/templates/edit/share.pt:101 kotti/templates/site-setup/users.pt:136 -msgid "Gravatar" -msgstr "Gravatar" - -#: kotti/templates/edit/upload.pt:9 -msgid "Upload content from local file(s)" -msgstr "" - -#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:164 +#: kotti/templates/edit/upload.pt:17 kotti/templates/edit/upload.pt:107 #, fuzzy msgid "Filename" msgstr "Renomear" -#: kotti/templates/edit/upload.pt:26 kotti/templates/edit/upload.pt:170 +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:109 msgid "Size" msgstr "" -#: kotti/templates/edit/upload.pt:29 +#: kotti/templates/edit/upload.pt:20 msgid "Status" msgstr "" -#: kotti/templates/edit/upload.pt:32 +#: kotti/templates/edit/upload.pt:21 msgid "Progress" msgstr "" -#: kotti/templates/edit/upload.pt:124 +#: kotti/templates/edit/upload.pt:84 msgid "Select file(s) to upload..." msgstr "" -#: kotti/templates/edit/upload.pt:134 +#: kotti/templates/edit/upload.pt:92 msgid "Upload ${number} files." msgstr "" -#: kotti/templates/edit/upload.pt:144 +#: kotti/templates/edit/upload.pt:97 msgid "Dismiss all errors" msgstr "" -#: kotti/templates/site-setup/delete-user.pt:20 +#: kotti/templates/edit/contents.pt:26 +msgid "No content items are contained here." +msgstr "Não existem items nesta pasta." + +#: kotti/templates/edit/contents.pt:33 +msgid "Select / deselect all" +msgstr "" + +#: kotti/templates/edit/contents.pt:37 +msgid "Visibility" +msgstr "Visibilidade" + +#: kotti/templates/edit/contents.pt:69 +msgid "Visible" +msgstr "Visível" + +#: kotti/templates/edit/contents.pt:73 +#, fuzzy +msgid "Hidden" +msgstr "Escondido" + +#: kotti/templates/http-errors/notfound.pt:26 +msgid "Not Found" +msgstr "" + +#: kotti/templates/http-errors/notfound.pt:27 +msgid "The resource could not be found" +msgstr "" + +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "Resultados da pesquisa" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "Procurar..." + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "Marcado como:" + +#: kotti/templates/site-setup/delete-user.pt:15 msgid "Are you sure you want to delete ${type} ${title}?" msgstr "Deseja realmente eliminar ${type} ${title}?" -#: kotti/templates/site-setup/user.pt:13 +#: kotti/templates/site-setup/user.pt:12 msgid "Back to User Management" msgstr "Voltar à gestão de utilizadores" -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 +#: kotti/templates/site-setup/user.pt:21 kotti/views/form.py:159 #, python-format msgid "Edit ${title}" msgstr "Editar ${title}" -#: kotti/templates/site-setup/users.pt:15 +#: kotti/templates/site-setup/users.pt:12 #, fuzzy msgid "Search user(s) / group(s)" msgstr "Procurar utilizadores e grupos" -#: kotti/templates/site-setup/users.pt:24 +#: kotti/templates/site-setup/users.pt:17 msgid "Add user" msgstr "Adicionar utilizador" -#: kotti/templates/site-setup/users.pt:33 +#: kotti/templates/site-setup/users.pt:22 msgid "Add group" msgstr "Adicionar grupo" -#: kotti/templates/site-setup/users.pt:52 +#: kotti/templates/site-setup/users.pt:34 #, fuzzy msgid "Find users or groups" msgstr "Procurar utilizadores e grupos" -#: kotti/templates/site-setup/users.pt:63 +#: kotti/templates/site-setup/users.pt:40 msgid "User- / groupname" msgstr "" -#: kotti/templates/site-setup/users.pt:70 +#: kotti/templates/site-setup/users.pt:50 #, fuzzy msgid "Blank search text finds all." msgstr "" "Encontre e modifique utilizadores (Uma pesquisa em branco retorna\"\n" "\" todos os utilizadores)" -#: kotti/templates/site-setup/users.pt:96 +#: kotti/templates/site-setup/users.pt:65 #, fuzzy msgid "Assign global roles" msgstr "Atribuir papéis locais" -#: kotti/templates/site-setup/users.pt:180 +#: kotti/templates/site-setup/users.pt:117 #, fuzzy msgid "Add new user" msgstr "Adicionar utilizador" -#: kotti/templates/site-setup/users.pt:190 +#: kotti/templates/site-setup/users.pt:123 #, fuzzy msgid "Add new group" msgstr "Adicionar grupo" -#: kotti/templates/view/search-results.pt:8 -msgid "Search Results" -msgstr "Resultados da pesquisa" - -#: kotti/templates/view/search.pt:5 -msgid "Search..." -msgstr "Procurar..." - -#: kotti/templates/view/tags.pt:5 -msgid "Tagged with:" -msgstr "Marcado como:" - -#: kotti/tests/testing_view.pt:7 -msgid "Welcome, ${user}! You are logged in." -msgstr "Bem-vindo, ${user}! Você está autenticado." - -#: kotti/tests/testing_view.pt:14 -msgid "Welcome, you are not logged in." -msgstr "Bem-vindo, você não está autenticado." - -#: kotti/views/form.py:79 kotti/views/users.py:69 -#: kotti/views/edit/actions.py:363 kotti/views/edit/actions.py:407 +#: kotti/views/users.py:70 kotti/views/form.py:82 +#: kotti/views/edit/actions.py:371 kotti/views/edit/actions.py:415 msgid "Your changes have been saved." msgstr "As suas alterações foram guardadas." -#: kotti/views/form.py:174 -#, fuzzy -msgid "Item was added." -msgstr "${title} adicionado." - -#: kotti/views/form.py:264 -#, python-format -msgid "Maximum file size: ${size}MB" -msgstr "Tamanho máximo do ficheiro: ${size}MB" - -#: kotti/views/form.py:77 kotti/views/users.py:441 -msgid "Save" -msgstr "Guardar" - -#: kotti/views/form.py:198 -#, fuzzy, python-format -msgid "Add ${type} to ${title}." -msgstr "Adicionar ${type} em ${title}" - -#: kotti/views/form.py:202 -#, fuzzy, python-format -msgid "Add ${type}." -msgstr "Adicionar ${type}" - -#: kotti/views/login.py:238 -#, fuzzy -msgid "You have reset your password." -msgstr "Você reinicializou a palavra passe com sucesso" - -#: kotti/views/login.py:200 -msgid "You have been logged out." -msgstr "Você desligou a sessão" - -#: kotti/views/login.py:66 kotti/views/users.py:270 +#: kotti/views/users.py:72 kotti/views/users.py:477 +#: kotti/views/edit/actions.py:309 kotti/views/edit/actions.py:375 +#: kotti/views/edit/actions.py:417 kotti/views/edit/actions.py:421 #, fuzzy -msgid "Full name" -msgstr "Nome completo" - -#: kotti/views/login.py:69 -msgid "Username" -msgstr "Nome utilizador" - -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 -msgid "Email" -msgstr "E-mail" - -#: kotti/views/login.py:109 -#, fuzzy -msgid "" -"Congratulations! You are successfully registered. You should be receiving an " -"email with a link to set your password. Doing so will activate your account." -msgstr "" -"Parabéns! Você está registado com sucesso. Irá receber um e-mail com uma " -"ligação temporária para reiniciar a sua palavra passe." - -#: kotti/views/login.py:124 -#, fuzzy, python-format -msgid "Register - ${title}" -msgstr "Registar ${title}" - -#: kotti/views/login.py:164 -msgid "Login failed." -msgstr "Erro de autenticação" - -#: kotti/views/login.py:286 -#, fuzzy, python-format -msgid "Reset your password - ${title}." -msgstr "Reiniciar a sua palavra passe - ${title}" - -#: kotti/views/login.py:160 -#, python-format -msgid "Welcome, ${user}!" -msgstr "Bem-vindo, ${user}!" - -#: kotti/views/login.py:173 -#, fuzzy -msgid "" -"You should be receiving an email with a link to reset your password. Doing " -"so will activate your account." -msgstr "" -"Você deverá receber um e-mail com uma ligação temporária para reiniciar a " -"sua palavra passe." +msgid "No changes were made." +msgstr "Não foram efectuadas nenhumas alterações" -#: kotti/views/login.py:178 +#: kotti/views/users.py:105 #, fuzzy -msgid "That username or email is not known by this system." -msgstr "Este utilizador ou e-mail não são reconhecidos." - -#: kotti/views/login.py:83 -msgid "Register" -msgstr "Registar-se" - -#: kotti/views/login.py:90 kotti/views/login.py:257 -msgid "There was an error." -msgstr "Ocorreu um erro." - -#: kotti/views/login.py:250 -msgid "Submit" -msgstr "Enviar" - -#: kotti/views/login.py:279 -msgid "Your password reset token may have expired." -msgstr "A seu pedido para alterar a palavra passe expirou." - -#: kotti/views/site_setup.py:6 kotti/views/users.py:378 -msgid "User Management" -msgstr "Gestão de Utilizadores" - -#: kotti/views/users.py:267 -msgid "" -"Leave this empty and tick the 'Send password registration' box below to have " -"the user set their own password." -msgstr "" -"Deixe este campo por preencher e escolha a opção 'Enviar ligação para " -"registo' abaixo para permitir ao utilizador escolher a sua palavra passe." - -#: kotti/views/users.py:587 -#, python-format -msgid "My preferences - ${title}" -msgstr "Minhas preferências - ${title}" +msgid "No users or groups were found." +msgstr "Utilizadores e grupos não encontrados." -#: kotti/views/users.py:145 +#: kotti/views/users.py:153 msgid "Invalid value" msgstr "Valor inválido" -#: kotti/views/users.py:151 +#: kotti/views/users.py:159 msgid "A user with that name already exists." msgstr "Esse utilizador com esse nome já existe." -#: kotti/views/users.py:158 +#: kotti/views/users.py:166 msgid "A user with that email already exists." msgstr "Esse utilizador com esse e-mail já existe." -#: kotti/views/users.py:181 +#: kotti/views/users.py:189 #, python-format msgid "No such group: ${group}" msgstr "Grupo não encontrado: ${group}" -#: kotti/views/users.py:219 +#: kotti/views/users.py:207 kotti/views/login.py:71 kotti/views/login.py:267 +msgid "Email" +msgstr "E-mail" + +#: kotti/views/users.py:227 msgid "Active" msgstr "Activo" -#: kotti/views/users.py:220 +#: kotti/views/users.py:228 msgid "Untick this to deactivate the account." msgstr "Retirar para desactivar a conta." -#: kotti/views/users.py:226 +#: kotti/views/users.py:234 msgid "Global roles" msgstr "Papéis globais" -#: kotti/views/users.py:230 +#: kotti/views/users.py:238 msgid "Groups" msgstr "Grupos" -#: kotti/views/users.py:315 +#: kotti/views/users.py:275 +msgid "Leave this empty and tick the 'Send password registration' box below to have the user set their own password." +msgstr "Deixe este campo por preencher e escolha a opção 'Enviar ligação para registo' abaixo para permitir ao utilizador escolher a sua palavra passe." + +#: kotti/views/users.py:278 kotti/views/login.py:63 +#, fuzzy +msgid "Full name" +msgstr "Nome completo" + +#: kotti/views/users.py:323 msgid "Add User" msgstr "Adicionar Utilizador" -#: kotti/views/users.py:339 +#: kotti/views/users.py:333 +#, fuzzy +msgid "Send password registration link." +msgstr "Enviar ligação para registo de palavra passe" + +#: kotti/views/users.py:348 #, fuzzy, python-format msgid "${title} was added." msgstr "${title} adicionado." -#: kotti/views/users.py:349 +#: kotti/views/users.py:358 msgid "Add Group" msgstr "Adicionar Grupo" -#: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:301 kotti/views/edit/actions.py:367 -#: kotti/views/edit/actions.py:413 kotti/views/edit/actions.py:409 -#, fuzzy -msgid "No changes were made." -msgstr "Não foram efectuadas nenhumas alterações" - -#: kotti/views/users.py:558 -#, fuzzy -msgid "No name was given." -msgstr "Nenhum nome foi indicado." +#: kotti/views/users.py:388 kotti/views/site_setup.py:5 +msgid "User Management" +msgstr "Gestão de Utilizadores" -#: kotti/views/users.py:98 -#, fuzzy -msgid "No users or groups were found." -msgstr "Utilizadores e grupos não encontrados." +#: kotti/views/users.py:452 kotti/views/form.py:80 +msgid "Save" +msgstr "Guardar" -#: kotti/views/users.py:505 +#: kotti/views/users.py:519 #, fuzzy, python-format msgid "Edit ${principal_type} ${title}" msgstr "Editar ${principal_type} - ${title}" -#: kotti/views/users.py:533 +#: kotti/views/users.py:547 #, fuzzy msgid "User was not found." msgstr "Utilizador não encontrado." -#: kotti/views/users.py:324 -#, fuzzy -msgid "Send password registration link." -msgstr "Enviar ligação para registo de palavra passe" - -#: kotti/views/users.py:543 +#: kotti/views/users.py:557 #, fuzzy, python-format msgid "${principal_type} ${title} was deleted." msgstr "${principal_type} ${title} apagado." -#: kotti/views/users.py:551 +#: kotti/views/users.py:565 #, fuzzy, python-format msgid "Delete ${principal_type} ${title}" msgstr "Apagar ${principal_type} - ${title}" -#: kotti/views/edit/actions.py:106 +#: kotti/views/users.py:572 +#, fuzzy +msgid "No name was given." +msgstr "Nenhum nome foi indicado." + +#: kotti/views/users.py:601 +#, python-format +msgid "My preferences - ${title}" +msgstr "Minhas preferências - ${title}" + +#: kotti/views/form.py:177 +#, fuzzy +msgid "Item was added." +msgstr "${title} adicionado." + +#: kotti/views/form.py:201 #, fuzzy, python-format -msgid "${title} was copied." -msgstr "${title} copiado." +msgid "Add ${type} to ${title}." +msgstr "Adicionar ${type} em ${title}" -#: kotti/views/edit/actions.py:124 +#: kotti/views/form.py:205 #, fuzzy, python-format -msgid "${title} was cut." -msgstr "${title} cortado." +msgid "Add ${type}." +msgstr "Adicionar ${type}" + +#: kotti/views/form.py:295 +#, python-format +msgid "Maximum file size: ${size}MB" +msgstr "Tamanho máximo do ficheiro: ${size}MB" -#: kotti/views/edit/actions.py:182 +#: kotti/views/login.py:66 +msgid "Username" +msgstr "Nome utilizador" + +#: kotti/views/login.py:80 +msgid "Register" +msgstr "Registar-se" + +#: kotti/views/login.py:87 +msgid "There was an error." +msgstr "Ocorreu um erro." + +#: kotti/views/login.py:106 +#, fuzzy +msgid "Congratulations! You are successfully registered. You should be receiving an email with a link to set your password. Doing so will activate your account." +msgstr "Parabéns! Você está registado com sucesso. Irá receber um e-mail com uma ligação temporária para reiniciar a sua palavra passe." + +#: kotti/views/login.py:121 #, fuzzy, python-format -msgid "${title} was moved." -msgstr "${title} movido." +msgid "Register - ${title}" +msgstr "Registar ${title}" -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/login.py:152 +#, python-format +msgid "Welcome, ${user}!" +msgstr "Bem-vindo, ${user}!" + +#: kotti/views/login.py:177 +#, fuzzy +msgid "You should be receiving an email with a link to reset your password. Doing so will activate your account." +msgstr "Você deverá receber um e-mail com uma ligação temporária para reiniciar a sua palavra passe." + +#: kotti/views/login.py:210 +msgid "Login failed." +msgstr "Erro de autenticação" + +#: kotti/views/login.py:220 +#, fuzzy +msgid "That username or email is not known by this system." +msgstr "Este utilizador ou e-mail não são reconhecidos." + +#: kotti/views/login.py:242 +msgid "You have been logged out." +msgstr "Você desligou a sessão" + +#: kotti/views/login.py:280 +#, fuzzy +msgid "You have reset your password." +msgstr "Você reinicializou a palavra passe com sucesso" + +#: kotti/views/login.py:299 +#, fuzzy +msgid "Set password" +msgstr "Repor a palavra passe" + +#: kotti/views/login.py:327 +msgid "Your password reset token may have expired." +msgstr "A seu pedido para alterar a palavra passe expirou." + +#: kotti/views/login.py:334 #, fuzzy, python-format -msgid "${title} was deleted." -msgstr "${title} apagado." +msgid "Reset your password - ${title}." +msgstr "Reiniciar a sua palavra passe - ${title}" -#: kotti/views/edit/actions.py:158 +#: kotti/views/edit/default_views.py:86 +msgid "Default view" +msgstr "Vista por defeito" + +#: kotti/views/edit/default_views.py:107 +msgid "Default view has been reset to default." +msgstr "A vista original foi indicada como a vista por defeito." + +#: kotti/views/edit/default_views.py:114 +msgid "Default view has been set." +msgstr "Vista por defeito foi alterada." + +#: kotti/views/edit/default_views.py:119 +msgid "Default view could not be set." +msgstr "Vista por defeito não pode ser alterada." + +#: kotti/views/edit/actions.py:102 +#, fuzzy, python-format +msgid "${title} was copied." +msgstr "${title} copiado." + +#: kotti/views/edit/actions.py:120 +#, fuzzy, python-format +msgid "${title} was cut." +msgstr "${title} cortado." + +#: kotti/views/edit/actions.py:155 #, fuzzy, python-format msgid "${title} was pasted." msgstr "${title} colado." -#: kotti/views/edit/actions.py:161 +#: kotti/views/edit/actions.py:158 #, fuzzy msgid "Could not paste node. It no longer exists." msgstr "Nãp é possivel copiar. A cópia já não está disponivel." -#: kotti/views/edit/actions.py:224 +#: kotti/views/edit/actions.py:180 +#, fuzzy, python-format +msgid "${title} was moved." +msgstr "${title} movido." + +#: kotti/views/edit/actions.py:222 #, python-format msgid "${title} is now visible in the navigation." msgstr "${title} encontra-se visivel na navegação." -#: kotti/views/edit/actions.py:227 +#: kotti/views/edit/actions.py:225 #, python-format msgid "${title} is no longer visible in the navigation." msgstr "${title} já não está visivel na navegação." -#: kotti/views/edit/actions.py:292 +#: kotti/views/edit/actions.py:274 kotti/views/edit/actions.py:303 +#, fuzzy, python-format +msgid "${title} was deleted." +msgstr "${title} apagado." + +#: kotti/views/edit/actions.py:300 #, fuzzy msgid "Nothing was deleted." msgstr "Nada foi apagado." -#: kotti/views/edit/actions.py:328 kotti/views/edit/actions.py:355 +#: kotti/views/edit/actions.py:336 kotti/views/edit/actions.py:363 msgid "Name and title are required." msgstr "Nome e título são obrigatórios." -#: kotti/views/edit/actions.py:332 +#: kotti/views/edit/actions.py:340 #, fuzzy msgid "Item was renamed." msgstr "Item renomeado" -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:461 +msgid "Change State" +msgstr "Alterar Estado" + +#: kotti/views/edit/actions.py:462 msgid "Move up" msgstr "Mover para cima" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:463 msgid "Move down" msgstr "Mover para baixo" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:464 msgid "Show" msgstr "Mostrar" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:465 msgid "Hide" msgstr "Esconder" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:504 #, fuzzy msgid "You have to select items to perform an action." msgstr "Terá de escolher items para executar a ação." -#: kotti/views/edit/actions.py:453 -msgid "Change State" -msgstr "Alterar Estado" - -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:34 msgid "Description" msgstr "Descrição" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:40 msgid "Tags" msgstr "Etiquetas" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:49 msgid "Body" msgstr "Corpo" -#: kotti/views/edit/default_views.py:99 -msgid "Default view has been reset to default." -msgstr "A vista original foi indicada como a vista por defeito." - -#: kotti/views/edit/default_views.py:78 -msgid "Default view" -msgstr "Vista por defeito" +#~ msgid "Image" +#~ msgstr "Imagem" -#: kotti/views/edit/default_views.py:106 -msgid "Default view has been set." -msgstr "Vista por defeito foi alterada." - -#: kotti/views/edit/default_views.py:111 -msgid "Default view could not be set." -msgstr "Vista por defeito não pode ser alterada." +#~ msgid "Submit" +#~ msgstr "Enviar" #~ msgid "Private" #~ msgstr "Privado" diff --git a/kotti/locale/sv/LC_MESSAGES/Kotti.mo b/kotti/locale/sv/LC_MESSAGES/Kotti.mo new file mode 100644 index 000000000..5bdfaff84 Binary files /dev/null and b/kotti/locale/sv/LC_MESSAGES/Kotti.mo differ diff --git a/kotti/locale/sv/LC_MESSAGES/Kotti.po b/kotti/locale/sv/LC_MESSAGES/Kotti.po new file mode 100644 index 000000000..0e9cca42b --- /dev/null +++ b/kotti/locale/sv/LC_MESSAGES/Kotti.po @@ -0,0 +1,961 @@ +# +# Swedish translations for PACKAGE package +# This file is distributed under the same license as the PACKAGE package. +# Anders Nylund , 2016. +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE 1.0\n" +"POT-Creation-Date: 2018-01-17 13:51+0100\n" +"PO-Revision-Date: 2016-10-05 21:34+0300\n" +"Last-Translator: Anders Nylund \n" +"Language-Team: Swedish\n" +"Language: sv\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Lingua 4.7\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: kotti/security.py:173 +msgid "Viewer" +msgstr "Läsare" + +#: kotti/security.py:174 +msgid "Editor" +msgstr "Redaktör" + +#: kotti/security.py:175 +msgid "Owner" +msgstr "Ägare" + +#: kotti/security.py:176 +msgid "Admin" +msgstr "Administratör" + +#: kotti/resources.py:541 kotti/views/edit/actions.py:453 +msgid "Copy" +msgstr "Kopiera" + +#: kotti/resources.py:542 kotti/views/edit/actions.py:454 +msgid "Cut" +msgstr "Klipp ut" + +#: kotti/resources.py:543 kotti/views/edit/actions.py:450 +msgid "Paste" +msgstr "Klistra in" + +#: kotti/resources.py:544 kotti/templates/edit/rename.pt:31 +#: kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:42 kotti/views/edit/actions.py:455 +msgid "Rename" +msgstr "Döp om" + +#: kotti/resources.py:545 kotti/templates/edit/delete.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:49 +#: kotti/templates/site-setup/delete-user.pt:24 kotti/views/users.py:454 +#: kotti/views/edit/actions.py:457 +msgid "Delete" +msgstr "Radera" + +#: kotti/resources.py:556 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "Innehåll" + +#: kotti/resources.py:557 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "Redigera" + +#: kotti/resources.py:558 +msgid "Share" +msgstr "Dela" + +#: kotti/resources.py:559 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "Handlingar" + +#: kotti/resources.py:562 +msgid "Folder view" +msgstr "Mapp vy" + +#: kotti/resources.py:671 kotti/views/edit/content.py:61 +msgid "Document" +msgstr "Dokument" + +#: kotti/resources.py:808 kotti/views/edit/content.py:73 +#: kotti/views/edit/content.py:87 +msgid "File" +msgstr "Fil" + +#: kotti/populate.py:67 +msgid "Welcome to Kotti" +msgstr "Välkommen till Kotti" + +#: kotti/populate.py:68 +msgid "Congratulations! You have successfully installed Kotti." +msgstr "Gratulerar! Du har nu installerat Kotti." + +#: kotti/populate.py:69 +#, fuzzy, c-format +msgid "" +"\n" +"

    Log in

    \n" +"

    \n" +" You can log in to your site\n" +" and start changing its contents. If you haven't chosen a password for\n" +" your admin account yet, it'll likely be qwerty.\n" +"

    \n" +"

    \n" +" Once you're logged in, you'll see the grey editor bar below the top\n" +" navigation bar. It will allow you to switch between editing and viewing the\n" +" current page as it will appear to your visitors.\n" +"

    \n" +"
    \n" +"
    \n" +"

    Configure

    \n" +"

    \n" +" Find out how to configure your Kotti's title and many other\n" +" settings using a simple text file in your file system.\n" +"

    \n" +"

    \n" +" \n" +" Configuration manual\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    Add-ons

    \n" +"

    \n" +" A number of add-ons allow you to extend the functionality of your\n" +" Kotti site.\n" +"

    \n" +"

    \n" +" \n" +" Kotti add-ons\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    Documentation

    \n" +"

    \n" +" Wonder what more you can do with Kotti?\n" +" What license it has?\n" +" Read the manual for more information.\n" +"

    \n" +"

    \n" +" \n" +" Documentation\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +msgstr "" +"\n" +"

    Logga in

    \n" +"

    \n" +" Du kan logga in till din site\n" +" och börja ändra dess innehåll. Om du inte har valt ett lösenord för\n" +" ditt administratorkonto, så är det sannolikt qwerty.\n" +"

    \n" +"

    \n" +" Så fort du har loggat in kommer du att se en grå administrationsbalk \n" +" under toppen. Med hjälp av den kan du redigera siten eller\n" +" titta på den som den kommer att se ut för dina besökare.\n" +"

    \n" +"
    \n" +"
    \n" +"

    Konfigurering

    \n" +"

    \n" +" Lär dig hur du ställer in din sites rubrik och många andra \n" +" inställningar genom att redigera en enkel textfil i ditt filsystem.\n" +"

    \n" +"

    \n" +" \n" +" Konfigurationsmanual\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    Tillägg

    \n" +"

    \n" +" Ett antal tillägg som du kan använda för att utöka funktionen i din\n" +" Kotti site.\n" +"

    \n" +"

    \n" +" \n" +" Kotti tillägg\n" +" \n" +"

    \n" +"
    \n" +"
    \n" +"

    Dokumentation

    \n" +"

    \n" +" Undrar du vad mer du kan göra med Kotti?\n" +" Vilken licens har den?\n" +" Läs manualen för mer information.\n" +"

    \n" +"

    \n" +" \n" +" Dokumentation\n" +" \n" +"

    \n" +"
    \n" +"
    \n" + +#: kotti/populate.py:126 +msgid "About" +msgstr "Om" + +#: kotti/populate.py:127 +msgid "Our company is the leading manufacturer of foo widgets used in a wide variety of aviation and and industrial products." +msgstr "Vårt företag är den ledande tillverkaren av foo grunkor som används i en mängd olika tillämpningar inom flyg och industri." + +#: kotti/populate.py:128 +msgid "" +"\n" +"

    \n" +" \"five\n" +"

    \n" +"\n" +"

    \n" +" Our worldwide headquarters:\n" +"

    \n" +"\n" +"
    \n" +" Foo World
    \n" +" 123 Nowhere Street, Suite 777
    \n" +" Omak, WA 98841 USA
    \n" +" +1-509-555-0100
    \n" +" widgets@foowrld.example.com\n" +"
    \n" +"\n" +"

    \n" +"Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +"\n" +"Copyright info.\n" +"Originally published in the\n" +" Extra EA-300\n" +"article.\n" +"

    \n" +msgstr "" +"\n" +"

    \n" +" \"fem\n" +"

    \n" +"\n" +"

    \n" +" Vårt globala högkvarter:\n" +"

    \n" +"\n" +"
    \n" +" Foo World
    \n" +" 123 Nowhere Street, Suite 777
    \n" +" Omak, WA 98841 USA
    \n" +" +1-509-555-0100
    \n" +" grunkor@foowrld.example.com\n" +"
    \n" +"\n" +"

    \n" +"Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" +"\n" +"Copyright info.\n" +"Originally published in the\n" +" Extra EA-300\n" +"article.\n" +"

    \n" + +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "Välkommern, ${user}!. Du är inloggad." + +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "Välkommen, du är inte inloggad." + +#: kotti/templates/editor-bar.pt:19 +msgid "View" +msgstr "Visa" + +#: kotti/templates/editor-bar.pt:42 +msgid "Navigate" +msgstr "Navigera" + +#: kotti/templates/editor-bar.pt:53 +msgid "Preferences" +msgstr "Preferenser" + +#: kotti/templates/editor-bar.pt:58 +msgid "Site Setup" +msgstr "Site inställningar" + +#: kotti/templates/editor-bar.pt:69 +msgid "Logout" +msgstr "Logga ut" + +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "Sätt standardvy" + +#: kotti/templates/login.pt:16 +msgid "Login" +msgstr "Inloggning" + +#: kotti/templates/login.pt:21 kotti/templates/login.pt:59 +msgid "Username or email" +msgstr "Användarnamn eller epost" + +#: kotti/templates/login.pt:26 kotti/views/users.py:220 +#: kotti/views/login.py:255 +msgid "Password" +msgstr "Lösenord" + +#: kotti/templates/login.pt:34 +msgid "Log in" +msgstr "Logga in" + +#: kotti/templates/login.pt:45 +msgid "Forgot your password?" +msgstr "Har du glömt ditt lösenord?" + +#. Used in sentence: "Fill out your username or email and click +#. ${reset_password} below to receive an email with a link to reset your +#. password." +#. +#: kotti/templates/login.pt:52 kotti/templates/login.pt:67 +msgid "Reset password" +msgstr "Återställ lösenord" + +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:50 +msgid "Fill out your username or email and click ${reset_password} below to receive an email with a link to reset your password." +msgstr "Fyll i ditt användarnamn och din epostadress och klicka ${reset_password} nedanom för att få ett epostmeddelande med en länk för att återställa ditt lösenord." + +#: kotti/templates/login.pt:78 +msgid "Not registered yet?" +msgstr "Inte ännu registrerad?" + +#: kotti/templates/login.pt:85 +msgid "Register for an account on this site." +msgstr "Registrera ett konto på den här siten" + +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "Förbjudet" + +#: kotti/templates/add-dropdown.pt:3 +msgid "Add" +msgstr "Lägg till" + +#: kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" +msgstr "Ladda upp innehåll" + +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "Gör ${state}" + +#: kotti/templates/email-reset-password.pt:2 +msgid "Reset your password for ${site_title}." +msgstr "Återställ ditt lösenord för ${site_title}." + +#: kotti/templates/email-reset-password.pt:4 +#: kotti/templates/email-set-password.pt:4 +msgid "Hello, ${user_title}!" +msgstr "Hej, ${user_title}!" + +#: kotti/templates/email-reset-password.pt:5 +msgid "Click this link to reset your password at ${site_title}: ${url}" +msgstr "Klicka den här länken för att återställa ditt lösenord för ${site_title}: ${url}" + +#: kotti/templates/email-set-password.pt:2 +msgid "Your registration for ${site_title}" +msgstr "Din registrering för ${site_title}" + +#: kotti/templates/email-set-password.pt:5 +msgid "You are joining ${site_title}." +msgstr "Du ansluter dig till ${site_title}." + +#: kotti/templates/email-set-password.pt:6 +msgid "Click here to set your password and log in: ${url}" +msgstr "Klicka här för att ställa in ditt lösenord och logga in: ${url}" + +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" +msgstr "Dela ${title}" + +#: kotti/templates/edit/share.pt:14 kotti/templates/edit/share.pt:20 +#: kotti/templates/edit/share.pt:27 kotti/templates/site-setup/users.pt:48 +msgid "Search users and groups" +msgstr "Sök efter användare och grupper" + +#: kotti/templates/edit/share.pt:34 kotti/templates/site-setup/users.pt:56 +msgid "Search" +msgstr "Sök" + +#: kotti/templates/edit/share.pt:44 +msgid "Assign local roles" +msgstr "Tilldela lokala roller" + +#: kotti/templates/edit/share.pt:49 kotti/templates/edit/change-state.pt:19 +#: kotti/templates/edit/delete-nodes.pt:22 kotti/templates/edit/upload.pt:18 +#: kotti/templates/edit/upload.pt:108 kotti/templates/edit/contents.pt:35 +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:69 +msgid "Type" +msgstr "Typ" + +#: kotti/templates/edit/share.pt:50 kotti/templates/site-setup/users.pt:70 +#: kotti/views/users.py:215 +msgid "Name" +msgstr "Namn" + +#: kotti/templates/edit/share.pt:61 kotti/templates/site-setup/users.pt:81 +#: kotti/views/users.py:321 kotti/views/users.py:515 kotti/views/users.py:550 +msgid "User" +msgstr "Användare" + +#: kotti/templates/edit/share.pt:62 kotti/templates/site-setup/users.pt:82 +#: kotti/views/users.py:196 kotti/views/users.py:356 kotti/views/users.py:515 +#: kotti/views/users.py:550 +msgid "Group" +msgstr "Grupp" + +#: kotti/templates/edit/share.pt:65 kotti/templates/site-setup/users.pt:85 +msgid "Gravatar" +msgstr "Gravatar" + +#: kotti/templates/edit/share.pt:70 kotti/templates/site-setup/users.pt:91 +msgid "Assign role" +msgstr "Tilldela roll" + +#: kotti/templates/edit/share.pt:85 kotti/templates/edit/change-state.pt:72 +#: kotti/templates/site-setup/users.pt:107 +msgid "Apply changes" +msgstr "Ta ändringarna i bruk" + +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "Navigera siten" + +#: kotti/templates/edit/breadcrumbs.pt:8 +msgid "You are here:" +msgstr "Du är här:" + +#: kotti/templates/edit/delete.pt:9 kotti/templates/site-setup/delete-user.pt:9 +msgid "Delete ${title}" +msgstr "Radera ${title}" + +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" +msgstr "Är du säker på att du vill radera ${title}?" + +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:74 +#: kotti/templates/edit/delete-nodes.pt:44 +#: kotti/templates/edit/rename-nodes.pt:44 kotti/views/users.py:324 +#: kotti/views/users.py:359 kotti/views/users.py:453 kotti/views/form.py:81 +msgid "Cancel" +msgstr "Avbryt" + +#: kotti/templates/edit/change-state.pt:8 +msgid "Change workflow state" +msgstr "Ändra 'workflow' inställning" + +#: kotti/templates/edit/change-state.pt:12 +msgid "An item's workflow state determines who can see, edit and/or manage it." +msgstr "Ett dokuments 'workflow' inställning bestämmer vem som kan se, redigera och/eller hantera det." + +#: kotti/templates/edit/change-state.pt:18 +#: kotti/templates/edit/delete-nodes.pt:21 kotti/templates/edit/contents.pt:34 +#: kotti/templates/view/folder.pt:20 kotti/views/users.py:204 +#: kotti/views/edit/content.py:28 +msgid "Title" +msgstr "Titel" + +#: kotti/templates/edit/change-state.pt:20 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:36 +msgid "State" +msgstr "Läge" + +#: kotti/templates/edit/change-state.pt:21 +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:38 +#: kotti/templates/view/folder.pt:22 +msgid "Creation Date" +msgstr "Skapat" + +#: kotti/templates/edit/change-state.pt:22 +#: kotti/templates/edit/delete-nodes.pt:25 kotti/templates/edit/contents.pt:39 +#: kotti/templates/view/folder.pt:23 +msgid "Modification Date" +msgstr "Ändrat" + +#: kotti/templates/edit/change-state.pt:28 +#: kotti/templates/edit/delete-nodes.pt:31 kotti/templates/edit/contents.pt:45 +msgid "Select item" +msgstr "Välj dokument" + +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:48 +msgid "Include children" +msgstr "Inkludera underdokument" + +#: kotti/templates/edit/change-state.pt:41 +msgid "If checked, this will attempt to modify the status of all children in any selected documents and their subdocuments." +msgstr "Det här kommer, om det är valt, att försöka rekursivt modifiera alla underdokument under valda dokument" + +#: kotti/templates/edit/change-state.pt:54 +msgid "Change state to" +msgstr "Ändra läge till" + +#: kotti/templates/edit/change-state.pt:55 +msgid "Select the new state where all chosen items should be set." +msgstr "Välj nytt läge för alla valda dokument" + +#: kotti/templates/edit/change-state.pt:60 +msgid "No change" +msgstr "Ingen ändring" + +#: kotti/templates/edit/delete-nodes.pt:13 +msgid "Are you sure you want to delete the following items?" +msgstr "Är du säker på att du vill radera följande dokument?" + +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "Döp om ${title}" + +#: kotti/templates/edit/rename.pt:11 kotti/templates/edit/rename-nodes.pt:13 +msgid "Each content item has a name, which is used to create the url, and a title. Read the user manual about proper formatting of name and title if needed. You can change both below." +msgstr "Varje dokument har ett namn som används för att skapa ett url och en titel. Läs vid behov om korrekt formattering av namn och titel i användarmanualen. Nedan kan du ändra bägge." + +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +#: kotti/templates/edit/rename-nodes.pt:28 +msgid "New name" +msgstr "Nytt namn" + +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:34 +#: kotti/templates/edit/rename-nodes.pt:36 +msgid "New title" +msgstr "Ny rubrik" + +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" +msgstr "Ladda upp innehåll från lokal fil/filer" + +#: kotti/templates/edit/upload.pt:17 kotti/templates/edit/upload.pt:107 +msgid "Filename" +msgstr "Filnamn" + +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:109 +msgid "Size" +msgstr "Storlek" + +#: kotti/templates/edit/upload.pt:20 +msgid "Status" +msgstr "Status" + +#: kotti/templates/edit/upload.pt:21 +msgid "Progress" +msgstr "Framsteg" + +#: kotti/templates/edit/upload.pt:84 +msgid "Select file(s) to upload..." +msgstr "Välj fil/filer för uppladdning" + +#: kotti/templates/edit/upload.pt:92 +msgid "Upload ${number} files." +msgstr "Ladda upp ${number} filer." + +#: kotti/templates/edit/upload.pt:97 +msgid "Dismiss all errors" +msgstr "Avfärda alla fel" + +#: kotti/templates/edit/contents.pt:26 +msgid "No content items are contained here." +msgstr "Här finns inga dokument" + +#: kotti/templates/edit/contents.pt:33 +msgid "Select / deselect all" +msgstr "Välj / väl bort alla" + +#: kotti/templates/edit/contents.pt:37 +msgid "Visibility" +msgstr "Synlighet" + +#: kotti/templates/edit/contents.pt:69 +msgid "Visible" +msgstr "Synliga" + +#: kotti/templates/edit/contents.pt:73 +msgid "Hidden" +msgstr "Dolda" + +#: kotti/templates/http-errors/notfound.pt:26 +msgid "Not Found" +msgstr "Hittas ej" + +#: kotti/templates/http-errors/notfound.pt:27 +msgid "The resource could not be found" +msgstr "Resursen kan ej hittas" + +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "Sökresultat" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "Sök..." + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "Taggad med:" + +#: kotti/templates/site-setup/delete-user.pt:15 +msgid "Are you sure you want to delete ${type} ${title}?" +msgstr "Är du säker på att du vill radera ${type} ${title}?" + +#: kotti/templates/site-setup/user.pt:12 +msgid "Back to User Management" +msgstr "Tillbaka till hantering av användare" + +#: kotti/templates/site-setup/user.pt:21 kotti/views/form.py:159 +#, python-format +msgid "Edit ${title}" +msgstr "Redigera ${title}" + +#: kotti/templates/site-setup/users.pt:12 +msgid "Search user(s) / group(s)" +msgstr "Sök användare och grupp(er)" + +#: kotti/templates/site-setup/users.pt:17 +msgid "Add user" +msgstr "Lägg till användare" + +#: kotti/templates/site-setup/users.pt:22 +msgid "Add group" +msgstr "Lägg till grupp" + +#: kotti/templates/site-setup/users.pt:34 +msgid "Find users or groups" +msgstr "Hitta användare eller grupper" + +#: kotti/templates/site-setup/users.pt:40 +msgid "User- / groupname" +msgstr "Användare- /gruppnamn" + +#: kotti/templates/site-setup/users.pt:50 +msgid "Blank search text finds all." +msgstr "Tom söktext matchar allt." + +#: kotti/templates/site-setup/users.pt:65 +msgid "Assign global roles" +msgstr "Tilldela globala roller" + +#: kotti/templates/site-setup/users.pt:117 +msgid "Add new user" +msgstr "Lägg till ny användare" + +#: kotti/templates/site-setup/users.pt:123 +msgid "Add new group" +msgstr "Lägg till ny grupp" + +#: kotti/views/users.py:70 kotti/views/form.py:82 +#: kotti/views/edit/actions.py:371 kotti/views/edit/actions.py:415 +msgid "Your changes have been saved." +msgstr "Dina ändringar har blivit sparade." + +#: kotti/views/users.py:72 kotti/views/users.py:477 +#: kotti/views/edit/actions.py:309 kotti/views/edit/actions.py:375 +#: kotti/views/edit/actions.py:417 kotti/views/edit/actions.py:421 +msgid "No changes were made." +msgstr "Inga ändringar har gjorts." + +#: kotti/views/users.py:105 +msgid "No users or groups were found." +msgstr "Inga användare eller grupper hittades." + +#: kotti/views/users.py:153 +msgid "Invalid value" +msgstr "Felaktigt värde" + +#: kotti/views/users.py:159 +msgid "A user with that name already exists." +msgstr "En användare med det namnet finns redan." + +#: kotti/views/users.py:166 +msgid "A user with that email already exists." +msgstr "En användare med den epostadressen finns redan." + +#: kotti/views/users.py:189 +#, python-format +msgid "No such group: ${group}" +msgstr "Ingen sådan grupp: ${group}" + +#: kotti/views/users.py:207 kotti/views/login.py:71 kotti/views/login.py:267 +msgid "Email" +msgstr "Epost" + +#: kotti/views/users.py:227 +msgid "Active" +msgstr "Aktiv" + +#: kotti/views/users.py:228 +msgid "Untick this to deactivate the account." +msgstr "Ta bort det här valet för att inaktivera kontot." + +#: kotti/views/users.py:234 +msgid "Global roles" +msgstr "Globala roller" + +#: kotti/views/users.py:238 +msgid "Groups" +msgstr "Grupper" + +#: kotti/views/users.py:275 +msgid "Leave this empty and tick the 'Send password registration' box below to have the user set their own password." +msgstr "Lämna det här tomt och välj 'Sänd länk för lösenordsregistrering' nedan för att låta användaren själv välja lösenord." + +#: kotti/views/users.py:278 kotti/views/login.py:63 +msgid "Full name" +msgstr "Fullständigt namn" + +#: kotti/views/users.py:323 +msgid "Add User" +msgstr "Lägg till användare" + +#: kotti/views/users.py:333 +msgid "Send password registration link." +msgstr "Sänd länk för lösenordsregistrering." + +#: kotti/views/users.py:348 +#, python-format +msgid "${title} was added." +msgstr "${title} lades till." + +#: kotti/views/users.py:358 +msgid "Add Group" +msgstr "Lägg till grupp" + +#: kotti/views/users.py:388 kotti/views/site_setup.py:5 +msgid "User Management" +msgstr "Hantera användare" + +#: kotti/views/users.py:452 kotti/views/form.py:80 +msgid "Save" +msgstr "Spara" + +#: kotti/views/users.py:519 +#, python-format +msgid "Edit ${principal_type} ${title}" +msgstr "Redigera ${principal_type} ${title}" + +#: kotti/views/users.py:547 +msgid "User was not found." +msgstr "Användaren kunde inte hittas." + +#: kotti/views/users.py:557 +#, python-format +msgid "${principal_type} ${title} was deleted." +msgstr "${principal_type} ${title} raderades." + +#: kotti/views/users.py:565 +#, python-format +msgid "Delete ${principal_type} ${title}" +msgstr "Radera ${principal_type} ${title}" + +#: kotti/views/users.py:572 +msgid "No name was given." +msgstr "Inget namn angavs." + +#: kotti/views/users.py:601 +#, python-format +msgid "My preferences - ${title}" +msgstr "Mina preferenser - ${title}" + +#: kotti/views/form.py:177 +msgid "Item was added." +msgstr "Dokumentet har lagts till." + +#: kotti/views/form.py:201 +#, python-format +msgid "Add ${type} to ${title}." +msgstr "Lägg till ${type} till ${title}." + +#: kotti/views/form.py:205 +#, python-format +msgid "Add ${type}." +msgstr "Lägg till ${type}." + +#: kotti/views/form.py:295 +#, python-format +msgid "Maximum file size: ${size}MB" +msgstr "Maximal filstorlek: ${size}MB" + +#: kotti/views/login.py:66 +msgid "Username" +msgstr "Användarnamn" + +#: kotti/views/login.py:80 +msgid "Register" +msgstr "Registrera" + +#: kotti/views/login.py:87 +msgid "There was an error." +msgstr "Ett fel uppstod." + +#: kotti/views/login.py:106 +msgid "Congratulations! You are successfully registered. You should be receiving an email with a link to set your password. Doing so will activate your account." +msgstr "Gratulerar! Du har registrerat dig. Du bör ha fått ett epostmeddelande med en länk för att ställa in ditt lösenord. Genom att göra det aktiverar du ditt konto-" + +#: kotti/views/login.py:121 +#, python-format +msgid "Register - ${title}" +msgstr "Registrara - ${title}" + +#: kotti/views/login.py:152 +#, python-format +msgid "Welcome, ${user}!" +msgstr "Välkommen, ${user}!" + +#: kotti/views/login.py:177 +msgid "You should be receiving an email with a link to reset your password. Doing so will activate your account." +msgstr "Du bör ha fått ett epostmeddelande med en länk för att ställa in ditt lösenord. Genom att göra det aktiverar du ditt konto-" + +#: kotti/views/login.py:210 +msgid "Login failed." +msgstr "Inloggningen misslyckades." + +#: kotti/views/login.py:220 +msgid "That username or email is not known by this system." +msgstr "Användarnamnet eller epostadressen är inte bekanta för systemet." + +#: kotti/views/login.py:242 +msgid "You have been logged out." +msgstr "Du har loggats ut." + +#: kotti/views/login.py:280 +msgid "You have reset your password." +msgstr "Du har återställt ditt lösenord." + +#: kotti/views/login.py:299 +msgid "Set password" +msgstr "Ställ in lösenord" + +#: kotti/views/login.py:327 +msgid "Your password reset token may have expired." +msgstr "Lösenordsåterställningen kan vara föråldrad." + +#: kotti/views/login.py:334 +#, python-format +msgid "Reset your password - ${title}." +msgstr "Återställ ditt lösenord - ${title}." + +#: kotti/views/edit/default_views.py:86 +msgid "Default view" +msgstr "Standardvy" + +#: kotti/views/edit/default_views.py:107 +msgid "Default view has been reset to default." +msgstr "Standardvyn har återställts till förvald inställning." + +#: kotti/views/edit/default_views.py:114 +msgid "Default view has been set." +msgstr "Standadvyn har ställts in." + +#: kotti/views/edit/default_views.py:119 +msgid "Default view could not be set." +msgstr "Standardvyn kunde inte ställas in." + +#: kotti/views/edit/actions.py:102 +#, python-format +msgid "${title} was copied." +msgstr "${title} har kopierats." + +#: kotti/views/edit/actions.py:120 +#, python-format +msgid "${title} was cut." +msgstr "${title} har klippts ut." + +#: kotti/views/edit/actions.py:155 +#, python-format +msgid "${title} was pasted." +msgstr "${title} har klistrats in." + +#: kotti/views/edit/actions.py:158 +msgid "Could not paste node. It no longer exists." +msgstr "Kunde inte klistra in noden. Den finns inte längre." + +#: kotti/views/edit/actions.py:180 +#, python-format +msgid "${title} was moved." +msgstr "${title} har flyttats." + +#: kotti/views/edit/actions.py:222 +#, python-format +msgid "${title} is now visible in the navigation." +msgstr "${title} är ny synlig i navigationsträdet" + +#: kotti/views/edit/actions.py:225 +#, python-format +msgid "${title} is no longer visible in the navigation." +msgstr "${title} visas inte längre i navigationsträdet" + +#: kotti/views/edit/actions.py:274 kotti/views/edit/actions.py:303 +#, python-format +msgid "${title} was deleted." +msgstr "${title} har raderats." + +#: kotti/views/edit/actions.py:300 +msgid "Nothing was deleted." +msgstr "Ingenting raderades." + +#: kotti/views/edit/actions.py:336 kotti/views/edit/actions.py:363 +msgid "Name and title are required." +msgstr "Namn och rubrik krävs" + +#: kotti/views/edit/actions.py:340 +msgid "Item was renamed." +msgstr "Dokumentet har döpts om." + +#: kotti/views/edit/actions.py:461 +msgid "Change State" +msgstr "Ändra läge" + +#: kotti/views/edit/actions.py:462 +msgid "Move up" +msgstr "Flytta upp" + +#: kotti/views/edit/actions.py:463 +msgid "Move down" +msgstr "Flytta ner" + +#: kotti/views/edit/actions.py:464 +msgid "Show" +msgstr "Visa" + +#: kotti/views/edit/actions.py:465 +msgid "Hide" +msgstr "Göm" + +#: kotti/views/edit/actions.py:504 +msgid "You have to select items to perform an action." +msgstr "Du måste välja något att utföra operationen på först" + +#: kotti/views/edit/content.py:34 +msgid "Description" +msgstr "Beskrivning" + +#: kotti/views/edit/content.py:40 +msgid "Tags" +msgstr "Taggar" + +#: kotti/views/edit/content.py:49 +msgid "Body" +msgstr "Brödtext" diff --git a/kotti/message.py b/kotti/message.py index a4f2dcc2f..0491359c6 100644 --- a/kotti/message.py +++ b/kotti/message.py @@ -1,6 +1,9 @@ import hashlib import time -import urllib +from typing import Dict +from typing import List +from typing import Optional +from urllib.parse import urlencode from html2text import HTML2Text from pyramid.renderers import render @@ -8,20 +11,8 @@ from pyramid_mailer.message import Message from kotti import get_settings - - -SET_PASSWORD_SUBJECT = u"Your registration for %(site_title)s" -SET_PASSWORD_BODY = u"""Hello, %(user_title)s! - -You've just been invited to join %(site_title)s. Click here to set -your password and log in: %(url)s. -""" - -RESET_PASSWORD_SUBJECT = u"Reset your password for %(site_title)s." -RESET_PASSWORD_BODY = u"""Hello, %(user_title)s! - -Click this link to reset your password at %(site_title)s: %(url)s. -""" +from kotti.request import Request +from kotti.security import Principal _inject_mailer = [] @@ -33,53 +24,60 @@ def get_mailer(): return Mailer.from_settings(get_settings()) # pragma: no cover -def make_token(user, seconds=None): - secret = get_settings()['kotti.secret2'] +def make_token(user: Principal, seconds: Optional[float] = None) -> str: + secret = get_settings()["kotti.secret2"] if seconds is None: seconds = time.time() - token = '%s:%s:%s' % (user.name, secret, seconds) - return '%s:%s' % (hashlib.sha224(token).hexdigest(), seconds) + token = f"{user.name}:{secret}:{seconds}" + return "{}:{}".format(hashlib.sha224(token.encode("utf8")).hexdigest(), seconds) -def validate_token(user, token, valid_hrs=24): +def validate_token(user: Principal, token: str, valid_hrs: int = 24) -> bool: """ - >>> from kotti.testing import setUp, tearDown - >>> ignore = setUp() - >>> class User(object): - ... pass - >>> daniel = User() - >>> daniel.name = u'daniel' - >>> alice = User() - >>> alice.name = u'alice' - >>> token = make_token(daniel) - >>> validate_token(daniel, token) - True - >>> validate_token(alice, token) - False - >>> validate_token(daniel, 'foo') - False - >>> token = make_token(daniel, seconds=time.time() - 100000) - >>> validate_token(daniel, token) - False - >>> validate_token(daniel, token, valid_hrs=48) - True - >>> tearDown() + >>> from kotti.testing import setUp, tearDown + >>> ignore = setUp() + >>> class User(object): + ... pass + >>> daniel = User() + >>> daniel.name = u'daniel' + >>> alice = User() + >>> alice.name = u'alice' + >>> token = make_token(daniel) + >>> validate_token(daniel, token) + True + >>> validate_token(alice, token) + False + >>> validate_token(daniel, 'foo') + False + >>> token = make_token(daniel, seconds=time.time() - 100000) + >>> validate_token(daniel, token) + False + >>> validate_token(daniel, token, valid_hrs=48) + True + >>> tearDown() """ try: - seconds = float(token.split(':')[1]) + seconds = float(token.split(":")[1]) except (IndexError, ValueError): return False - if token == make_token(user, seconds): - if time.time() - seconds < 60 * 60 * valid_hrs: - return True + if ( + token == make_token(user, seconds) + and time.time() - seconds < 60 * 60 * valid_hrs + ): + return True return False -def send_email(request, recipients, template_name, template_vars={}): +def send_email( + request: Request, + recipients: List[str], + template_name: str, + template_vars: Optional[Dict[str, str]] = None, +) -> None: """ General email sender. :param request: current request. - :type request: :class:`pyramid.request.Request` + :type request: :class:`kotti.request.Request` :param recipients: list of email addresses. Each email should be a string like: u'"John Doe" '. @@ -93,40 +91,38 @@ def send_email(request, recipients, template_name, template_vars={}): :type template_vars: dict """ + if template_vars is None: + template_vars = {} + text = render(template_name, template_vars, request) - subject, htmlbody = text.strip().split('\n', 1) - subject = subject.replace('Subject:', '', 1).strip() + subject, htmlbody = text.strip().split("\n", 1) + subject = subject.replace("Subject:", "", 1).strip() html2text = HTML2Text() html2text.body_width = 0 textbody = html2text.handle(htmlbody).strip() message = Message( - recipients=recipients, - subject=subject, - body=textbody, - html=htmlbody, - ) + recipients=recipients, subject=subject, body=textbody, html=htmlbody + ) mailer = get_mailer() mailer.send(message) -def email_set_password(user, request, - template_name='kotti:templates/email-set-password.pt', - add_query=None): - site_title = get_settings()['kotti.site_title'] +def email_set_password( + user: Principal, + request: Request, + template_name: Optional[str] = "kotti:templates/email-set-password.pt", # noqa + add_query: Optional[Dict[str, str]] = None, +) -> None: + site_title = get_settings()["kotti.site_title"] token = make_token(user) - user.confirm_token = unicode(token) - set_password_query = {'token': token, 'email': user.email} + user.confirm_token = token + set_password_query = {"token": token, "email": user.email} if add_query: set_password_query.update(add_query) - url = '%s/@@set-password?%s' % ( - request.application_url, - urllib.urlencode(set_password_query), - ) - variables = dict( - user_title=user.title, - site_title=site_title, - url=url, - ) - recipients = [u'"%s" <%s>' % (user.title, user.email)] # XXX naive? + url = "{}/@@set-password?{}".format( + request.application_url, urlencode(set_password_query) + ) + variables = dict(user_title=user.title, site_title=site_title, url=url) + recipients = [f'"{user.title}" <{user.email}>'] # XXX naive? send_email(request, recipients, template_name, variables) diff --git a/kotti/migrate.py b/kotti/migrate.py index 981ea46a0..d2f44cdb7 100644 --- a/kotti/migrate.py +++ b/kotti/migrate.py @@ -38,120 +38,130 @@ def kotti_configure(settings): ``kotti-migrate`` commands 'list_all', 'upgrade_all' and 'stamp_heads' will then include the add-on. """ -from __future__ import absolute_import - import os -import pkg_resources +from typing import Callable +from typing import List +import pkg_resources from alembic.config import Config from alembic.environment import EnvironmentContext from alembic.script import ScriptDirectory from alembic.util import load_python_file from zope.sqlalchemy import mark_changed +from kotti import DBSession from kotti import conf_defaults from kotti import get_settings -from kotti import DBSession from kotti.util import command - -KOTTI_SCRIPT_DIR = pkg_resources.resource_filename('kotti', 'alembic') -DEFAULT_LOCATION = 'kotti:alembic' +KOTTI_SCRIPT_DIR = pkg_resources.resource_filename("kotti", "alembic") +DEFAULT_LOCATION = "kotti:alembic" class ScriptDirectoryWithDefaultEnvPy(ScriptDirectory): @property - def env_py_location(self): - loc = super(ScriptDirectoryWithDefaultEnvPy, self).env_py_location + def env_py_location(self) -> str: + loc = super().env_py_location if not os.path.exists(loc): - loc = os.path.join(KOTTI_SCRIPT_DIR, 'env.py') + loc = os.path.join(KOTTI_SCRIPT_DIR, "env.py") return loc - def run_env(self): + def run_env(self) -> None: dir_, filename = self.env_py_location.rsplit(os.path.sep, 1) load_python_file(dir_, filename) -class PackageEnvironment(object): - def __init__(self, location): +class PackageEnvironment: + def __init__(self, location: str) -> None: self.location = location self.config = self._make_config(location) self.script_dir = self._make_script_dir(self.config) @property - def pkg_name(self): - return self.location.split(':')[0] + def pkg_name(self) -> str: + return self.location.split(":")[0] @property - def version_table(self): - return '{0}_alembic_version'.format(self.pkg_name) + def version_table(self) -> str: + return f"{self.pkg_name}_alembic_version" - def run_env(self, fn, **kw): - with EnvironmentContext(self.config, self.script_dir, fn=fn, - version_table=self.version_table, **kw): + def run_env(self, fn: Callable, **kw) -> None: + with EnvironmentContext( + self.config, self.script_dir, fn=fn, version_table=self.version_table, **kw + ): self.script_dir.run_env() - def _make_config(self, location): + @staticmethod + def _make_config(location: str) -> Config: cfg = Config() cfg.set_main_option("script_location", location) - cfg.set_main_option("sqlalchemy.url", get_settings()['sqlalchemy.url']) + cfg.set_main_option("sqlalchemy.url", get_settings()["sqlalchemy.url"]) return cfg - def _make_script_dir(self, alembic_cfg): + @staticmethod + def _make_script_dir(alembic_cfg: Config) -> ScriptDirectoryWithDefaultEnvPy: script_dir = ScriptDirectory.from_config(alembic_cfg) script_dir.__class__ = ScriptDirectoryWithDefaultEnvPy # O_o return script_dir -def get_locations(): - conf_str = get_settings()['kotti.alembic_dirs'] - return [l.strip() for l in conf_str.split() if l.strip()] +def get_locations() -> List[str]: + conf_str = get_settings()["kotti.alembic_dirs"] + return [line.strip() for line in conf_str.split() if line.strip()] + + +def stamp_head(location: str = DEFAULT_LOCATION, revision: None = None) -> None: + env = PackageEnvironment(location) -def stamp_head(location=DEFAULT_LOCATION, revision=None): def do_stamp(rev, context, revision=revision): - current = context._current_rev() + if revision is None: revision = context.script.get_current_head() - elif revision == 'None': + elif revision == "None": revision = None - context._update_current_rev(current, revision) + + context.stamp(env.script_dir, revision) + mark_changed(DBSession()) return [] - PackageEnvironment(location).run_env(do_stamp) + env.run_env(do_stamp) -def stamp_heads(): +def stamp_heads() -> None: for location in get_locations(): stamp_head(location) -def upgrade(location=DEFAULT_LOCATION): +def upgrade(location=DEFAULT_LOCATION, revision=None): # We don't want to fire any kind of events during a migration, # because "migrations are a low-level thing". from kotti import events + events.clear() pkg_env = PackageEnvironment(location) - revision = pkg_env.script_dir.get_current_head() - print(u'Upgrading {0}:'.format(pkg_env.location)) + if revision is None: + revision = pkg_env.script_dir.get_current_head() + print(f"Upgrading {pkg_env.location}:") + + def upgrade(heads, context): + # alembic supports multiple heads, we don't. + # initial revision is () in alembic >= 0.7 + rev = heads[0] if heads else None - def upgrade(rev, context): if rev == revision: - print(u' - already up to date.') + print(" - already up to date.") return [] - print(u' - upgrading from {0} to {1}...'.format( - rev, revision)) + + print(f" - upgrading from {rev} to {revision}...") + return context.script._upgrade_revs(revision, rev) - pkg_env.run_env( - upgrade, - starting_rev=None, - destination_rev=revision, - ) - print + pkg_env.run_env(upgrade, starting_rev=None, destination_rev=revision) + print() def upgrade_all(): @@ -160,22 +170,24 @@ def upgrade_all(): def list_all(): - pkg_envs = [PackageEnvironment(l) for l in get_locations()] + pkg_envs = [PackageEnvironment(location) for location in get_locations()] for pkg_env in pkg_envs: - print(u'{0}:'.format(pkg_env.pkg_name)) + print(f"{pkg_env.pkg_name}:") for script in pkg_env.script_dir.walk_revisions(): - print(u" - {0} -> {1}: {2}".format( - script.down_revision, - script.revision, - script.doc, - )) + print( + " - {} -> {}: {}".format( + script.down_revision, script.revision, script.doc + ) + ) def current_revision(rev, context): - print(u" - current revision: {0}".format(rev)) + rev = rev[0] if rev else None + print(f" - current revision: {rev}") return [] + pkg_env.run_env(current_revision) - print + print() def kotti_migrate_command(): @@ -183,7 +195,7 @@ def kotti_migrate_command(): Usage: kotti-migrate list_all - kotti-migrate upgrade [--scripts=] + kotti-migrate upgrade [--scripts=] [--rev=] kotti-migrate upgrade_all kotti-migrate stamp_head [--scripts=] [--rev=] @@ -219,22 +231,24 @@ def kotti_migrate_command(): # 'includeme' function). save_conf_defaults = conf_defaults.copy() - os.environ['KOTTI_DISABLE_POPULATORS'] = '1' - conf_defaults['kotti.root_factory'] = [lambda req: None] + os.environ["KOTTI_DISABLE_POPULATORS"] = "1" + conf_defaults["kotti.root_factory"] = [lambda req: None] def callback(arguments): args = () - args_with_location = (arguments['--scripts'] or DEFAULT_LOCATION,) - if arguments['list_all']: + args_with_location = (arguments["--scripts"] or DEFAULT_LOCATION,) + if arguments["list_all"]: func = list_all - elif arguments['upgrade']: + elif arguments["upgrade"]: func = upgrade - args = args_with_location - elif arguments['upgrade_all']: + args = args_with_location + (arguments["--rev"],) + elif arguments["upgrade_all"]: func = upgrade_all - elif arguments['stamp_head']: + elif arguments["stamp_head"]: func = stamp_head - args = args_with_location + (arguments['--rev'],) + args = args_with_location + (arguments["--rev"],) + else: + raise ValueError("Unknown command") func(*args) try: @@ -242,4 +256,4 @@ def callback(arguments): finally: conf_defaults.clear() conf_defaults.update(save_conf_defaults) - del os.environ['KOTTI_DISABLE_POPULATORS'] + del os.environ["KOTTI_DISABLE_POPULATORS"] diff --git a/kotti/populate.py b/kotti/populate.py index 94a19ec35..9c5aed594 100644 --- a/kotti/populate.py +++ b/kotti/populate.py @@ -8,10 +8,10 @@ from kotti import get_settings from kotti.resources import DBSession -from kotti.resources import Node from kotti.resources import Document -from kotti.security import get_principals +from kotti.resources import Node from kotti.security import SITE_ACL +from kotti.security import get_principals from kotti.util import _ from kotti.workflow import get_workflow @@ -23,12 +23,12 @@ def populate_users(): """ principals = get_principals() - if u'admin' not in principals: - principals[u'admin'] = { - 'name': u'admin', - 'password': get_settings()['kotti.secret'], - 'title': u"Administrator", - 'groups': [u'role:admin'], + if "admin" not in principals: + principals["admin"] = { + "name": "admin", + "password": get_settings()["kotti.secret"], + "title": "Administrator", + "groups": ["role:admin"], } @@ -39,31 +39,36 @@ def populate(): """ lrm = LocalizerRequestMixin() lrm.registry = get_current_registry() - lrm.locale_name = get_settings()['pyramid.default_locale_name'] + # noinspection PyPropertyAccess + lrm.locale_name = get_settings()["pyramid.default_locale_name"] localizer = lrm.localizer - if DBSession.query(Node).count() == 0: - localized_root_attrs = dict( - [(k, localizer.translate(v)) for k, v in _ROOT_ATTRS.iteritems()]) + if DBSession.query(Node.id).count() == 0: + localized_root_attrs = { + k: localizer.translate(v) for k, v in _ROOT_ATTRS.items() + } root = Document(**localized_root_attrs) root.__acl__ = SITE_ACL DBSession.add(root) - localized_about_attrs = dict( - [(k, localizer.translate(v)) for k, v in _ABOUT_ATTRS.iteritems()]) - root['about'] = Document(**localized_about_attrs) + localized_about_attrs = { + k: localizer.translate(v) for k, v in _ABOUT_ATTRS.items() + } + root["about"] = Document(**localized_about_attrs) wf = get_workflow(root) if wf is not None: DBSession.flush() # Initializes workflow - wf.transition_to_state(root, None, u'public') + wf.transition_to_state(root, None, "public") populate_users() + _ROOT_ATTRS = dict( - name=u'', # (at the time of writing) root must have empty name! - title=_(u'Welcome to Kotti'), - description=_(u'Congratulations! You have successfully installed Kotti.'), - body=_(u""" + name="", # (at the time of writing) root must have empty name! + title=_("Welcome to Kotti"), + description=_("Congratulations! You have successfully installed Kotti."), + body=_( + """

    Log in

    You can log in to your site @@ -79,12 +84,12 @@ def populate():

    Configure

    - Find out how to configure your Kotti's title and many other settings using a - simple text file in your file system. + Find out how to configure your Kotti's title and many other + settings using a simple text file in your file system.

    + href="https://kotti.readthedocs.io/en/latest/developing/basic/configuration.html"> Configuration manual

    @@ -92,7 +97,8 @@ def populate():
    -""")) +""" + ), +) _ABOUT_ATTRS = dict( - title=_(u'About'), - description=_(u'Our company is the leading manufacturer of foo widgets used in a wide variety of aviation and and industrial products.'), - body=_(u""" + title=_("About"), + description=_( + "Our company is the leading manufacturer of foo widgets used in a wide variety of aviation and and industrial products." + ), # noqa + body=_( + """

    five colorful Extra EA300 airplanes flying in formation

    - Photo credit: "Northern Lights Formation" by FlugKerl2. - - Copyright info. - Originally published in the - Extra EA-300 - article. +Photo credit: "Northern Lights Formation" by FlugKerl2. + +Copyright info. +Originally published in the + Extra EA-300 +article.

    -""")) +""" + ), +) diff --git a/kotti/request.py b/kotti/request.py index d1e274287..77647de69 100644 --- a/kotti/request.py +++ b/kotti/request.py @@ -1,10 +1,19 @@ -# -*- coding: utf-8 -*- +from typing import Optional +from typing import Union +import pyramid.request from pyramid.decorator import reify -from pyramid.request import Request as BaseRequest +from pyramid.interfaces import IRequest +from pyramid.security import Allowed +from pyramid.security import Denied +from zope.interface import implementer +from kotti.security import Principal +from kotti.security import get_user -class Request(BaseRequest): + +@implementer(IRequest) +class Request(pyramid.request.Request): """ Kotti subclasses :class:`pyramid.request.Request` to make additional attributes / methods available on request objects and override Pyramid's :meth:`pyramid.request.Request.has_permission`. The latter is needed to @@ -13,7 +22,7 @@ class Request(BaseRequest): """ @reify - def user(self): + def user(self) -> Optional[Principal]: """ Add the authenticated user to the request object. :result: the currently authenticated user @@ -21,11 +30,11 @@ def user(self): the custom principals database defined in the ``kotti.principals_factory`` setting """ - - from kotti.security import get_user return get_user(self) - def has_permission(self, permission, context=None): + def has_permission( + self, permission: str, context: object = None + ) -> Union[Allowed, Denied]: """ Check if the current request has the given permission on the current or explicitly passed context. This is different from :meth:`pyramid.request.Request.has_permission`` in that a context other @@ -46,4 +55,4 @@ def has_permission(self, permission, context=None): from kotti.security import authz_context with authz_context(context, self): - return BaseRequest.has_permission(self, permission, context) + return super().has_permission(permission, context) diff --git a/kotti/resources.py b/kotti/resources.py index 25786b06e..772b84c69 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -1,103 +1,138 @@ """ The :mod:`~kotti.resources` module contains all the classes for Kotti's -persistance layer, which is based on SQLAlchemy. +persistence layer, which is based on SQLAlchemy. Inheritance Diagram ------------------- .. inheritance-diagram:: kotti.resources """ - +import abc +import datetime import os import warnings +from cgi import FieldStorage +from collections.abc import MutableMapping +from copy import copy from fnmatch import fnmatch -from UserDict import DictMixin - +from io import BufferedReader +from io import BytesIO +from typing import Any +from typing import Iterable +from typing import List +from typing import Optional +from typing import Union + +from depot.fields.sqlalchemy import UploadedFileField +from depot.fields.sqlalchemy import _SQLAMutationTracker +from depot.fields.upload import UploadedFile +from pyramid.decorator import reify from pyramid.traversal import resource_path from sqlalchemy import Boolean from sqlalchemy import Column from sqlalchemy import DateTime from sqlalchemy import ForeignKey from sqlalchemy import Integer -from sqlalchemy import LargeBinary from sqlalchemy import String from sqlalchemy import Unicode from sqlalchemy import UnicodeText from sqlalchemy import UniqueConstraint +from sqlalchemy import bindparam +from sqlalchemy import event +from sqlalchemy import inspect +from sqlalchemy.engine.base import Engine from sqlalchemy.ext.associationproxy import association_proxy +from sqlalchemy.ext.declarative import DeclarativeMeta +from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.ext.hybrid import hybrid_property +from sqlalchemy.ext.orderinglist import OrderingList from sqlalchemy.ext.orderinglist import ordering_list from sqlalchemy.orm import backref -from sqlalchemy.orm import deferred from sqlalchemy.orm import object_mapper from sqlalchemy.orm import relation +from sqlalchemy.orm.attributes import Event from sqlalchemy.orm.exc import NoResultFound +from sqlalchemy.orm.scoping import scoped_session from sqlalchemy.sql import and_ from sqlalchemy.sql import select from sqlalchemy.util import classproperty +from sqlalchemy.util.langhelpers import _symbol from transaction import commit -from zope.interface import implements +from zope.interface import implementer -from kotti import _resolve_dotted from kotti import Base from kotti import DBSession +from kotti import TRUE_VALUES +from kotti import _resolve_dotted from kotti import get_settings from kotti import metadata -from kotti import TRUE_VALUES from kotti.interfaces import IContent from kotti.interfaces import IDefaultWorkflow from kotti.interfaces import IDocument from kotti.interfaces import IFile -from kotti.interfaces import IImage from kotti.interfaces import INode from kotti.migrate import stamp_heads -from kotti.security import has_permission +from kotti.request import Request from kotti.security import PersistentACLMixin from kotti.security import view_permitted from kotti.sqla import ACLType from kotti.sqla import JsonType from kotti.sqla import MutationList from kotti.sqla import NestedMutationDict -from kotti.util import _ -from kotti.util import camel_case_to_name -from kotti.util import get_paste_items +from kotti.sqla import bakery from kotti.util import Link from kotti.util import LinkParent from kotti.util import LinkRenderer +from kotti.util import _ +from kotti.util import _to_fieldstorage +from kotti.util import camel_case_to_name +from kotti.util import get_paste_items -class ContainerMixin(object, DictMixin): +class ContainerMixin(MutableMapping): """Containers form the API of a Node that's used for subitem access and in traversal. """ - def __setitem__(self, key, node): - key = node.name = unicode(key) + def __iter__(self): + return iter(self.children) + + def __len__(self): + return len(self.keys()) + + def __setitem__(self, key: str, node: "Node") -> None: + node.name = key self.children.append(node) + self.children.reorder() - def __delitem__(self, key): - node = self[unicode(key)] + def __delitem__(self, key: str) -> None: + node = self[key] self.children.remove(node) DBSession.delete(node) - def keys(self): + def keys(self) -> List[str]: """ - :result: A list of children names. + :result: children names :rtype: list """ return [child.name for child in self.children] - def __getitem__(self, path): - DBSession()._autoflush() + def values(self) -> OrderingList: + return self.children - if not hasattr(path, '__iter__'): + def __getitem__(self, path: Union[str, Iterable[str]]) -> "Node": + db_session = DBSession() + db_session._autoflush() + + # if not hasattr(path, '__iter__'): + if isinstance(path, str): path = (path,) - path = [unicode(p) for p in path] + path = [p for p in path] # Optimization: don't query children if self._children already there: - if '_children' in self.__dict__: - first, rest = path[0], path[1:] + if "_children" in self.__dict__: + rest = path[1:] try: [child] = filter(lambda ch: ch.name == path[0], self._children) except ValueError: @@ -107,10 +142,19 @@ def __getitem__(self, path): else: return child + baked_query = bakery(lambda session: session.query(Node)) + if len(path) == 1: try: - return DBSession.query(Node).filter_by( - name=path[0], parent=self).one() + baked_query += lambda q: q.filter( + Node.name == bindparam("name"), + Node.parent_id == bindparam("parent_id"), + ) + return ( + baked_query(db_session) + .params(name=path[0], parent_id=self.id) + .one() + ) except NoResultFound: raise KeyError(path) @@ -124,76 +168,95 @@ def __getitem__(self, path): conditions.append(alias.c.parent_id == old_alias.c.id) conditions.append(alias.c.name == name) expr = select([alias.c.id], and_(*conditions)) - row = DBSession.execute(expr).fetchone() + row = db_session.execute(expr).fetchone() if row is None: raise KeyError(path) - return DBSession.query(Node).get(row.id) + return baked_query(db_session).get(row.id) @hybrid_property def children(self): - """Return *all* child nodes without considering permissions.""" + """ + :result: *all* child nodes without considering permissions. + :rtype: list + """ return self._children - def children_with_permission(self, request, permission='view'): - """ - Return only those children for which the user initiating - the request has the asked permission. + def children_with_permission( + self, request: Request, permission: str = "view" + ) -> "List[Node]": + """Return only those children for which the user initiating the + request has the asked permission. :param request: current request - :type request: :class:`pyramid.request.Request` + :type request: :class:`kotti.request.Request` + :param permission: The permission for which you want the allowed children :type permission: str + :result: List of child nodes :rtype: list """ - return [ - c for c in self.children - if has_permission(permission, c, request) - ] + return [c for c in self.children if request.has_permission(permission, c)] class LocalGroup(Base): + """Local groups allow the assignment of groups or roles to principals + (users or groups) **for a certain context** (i.e. a :class:`Node` in the + content tree). + """ - __tablename__ = 'local_groups' - __table_args__ = ( - UniqueConstraint('node_id', 'principal_name', 'group_name'), - ) + __tablename__ = "local_groups" + __table_args__ = (UniqueConstraint("node_id", "principal_name", "group_name"),) + #: Primary key for the node in the DB + #: (:class:`sqlalchemy.types.Integer`) id = Column(Integer(), primary_key=True) - node_id = Column(ForeignKey('nodes.id')) - principal_name = Column(Unicode(100)) + #: ID of the node for this assignment + #: (:class:`sqlalchemy.types.Integer`) + node_id = Column(ForeignKey("nodes.id"), index=True) + #: Name of the principal (user or group) + #: (:class:`sqlalchemy.types.Unicode`) + principal_name = Column(Unicode(100), index=True) + #: Name of the assigned group or role + #: (:class:`sqlalchemy.types.Unicode`) group_name = Column(Unicode(100)) - def __init__(self, node, principal_name, group_name): + def __init__(self, node: "Node", principal_name: str, group_name: str): self.node = node self.principal_name = principal_name self.group_name = group_name - def copy(self, **kwargs): - kwargs.setdefault('node', self.node) - kwargs.setdefault('principal_name', self.principal_name) - kwargs.setdefault('group_name', self.group_name) + def copy(self, **kwargs) -> "LocalGroup": + kwargs.setdefault("node", self.node) + kwargs.setdefault("principal_name", self.principal_name) + kwargs.setdefault("group_name", self.group_name) return self.__class__(**kwargs) + def __repr__(self): + return "<{} {} => {} at {}>".format( + self.__class__.__name__, + self.principal_name, + self.group_name, + resource_path(self.node), + ) -class Node(Base, ContainerMixin, PersistentACLMixin): - """Basic node in the persistance hierarchy. - """ - implements(INode) +class NodeMeta(DeclarativeMeta, abc.ABCMeta): + """ """ - __table_args__ = ( - UniqueConstraint('parent_id', 'name'), - ) + +@implementer(INode) +class Node(Base, ContainerMixin, PersistentACLMixin, metaclass=NodeMeta): + """Basic node in the persistance hierarchy.""" + + __table_args__ = (UniqueConstraint("parent_id", "name"),) __mapper_args__ = dict( - polymorphic_on='type', - polymorphic_identity='node', - with_polymorphic='*', - ) + polymorphic_on="type", polymorphic_identity="node", with_polymorphic="*" + ) #: Primary key for the node in the DB #: (:class:`sqlalchemy.types.Integer`) @@ -203,46 +266,52 @@ class Node(Base, ContainerMixin, PersistentACLMixin): type = Column(String(30), nullable=False) #: ID of the node's parent #: (:class:`sqlalchemy.types.Integer`) - parent_id = Column(ForeignKey('nodes.id')) + parent_id = Column(ForeignKey("nodes.id"), index=True) #: Position of the node within its container / parent #: (:class:`sqlalchemy.types.Integer`) position = Column(Integer()) _acl = Column(MutationList.as_mutable(ACLType)) #: Name of the node as used in the URL #: (:class:`sqlalchemy.types.Unicode`) - name = Column(Unicode(50), nullable=False) - #: Title of the node, e.g. as shown in serach results + name = Column(Unicode(250), nullable=False) + #: Title of the node, e.g. as shown in search results #: (:class:`sqlalchemy.types.Unicode`) - title = Column(Unicode(100)) - #: Annotations can be used to store arbitray data in a nested dictionary + title = Column(Unicode(250)) + #: Annotations can be used to store arbitrary data in a nested dictionary #: (:class:`kotti.sqla.NestedMustationDict`) annotations = Column(NestedMutationDict.as_mutable(JsonType)) #: The path can be used to efficiently filter for child objects #: (:class:`sqlalchemy.types.Unicode`). - path = Column(Unicode(1000), index=True) + path = Column(Unicode(2000), index=True) parent = relation( - 'Node', + "Node", remote_side=[id], backref=backref( - '_children', - collection_class=ordering_list('position'), + "_children", + collection_class=ordering_list("position", reorder_on_append=True), order_by=[position], - cascade='all', - ) + cascade="all", + ), ) local_groups = relation( - LocalGroup, - backref=backref('node'), - cascade='all', - ) + LocalGroup, backref=backref("node"), cascade="all", lazy="joined" + ) + + __hash__ = Base.__hash__ - def __init__(self, name=None, parent=None, title=u"", annotations=None, - **kwargs): + def __init__( + self, + name: str = None, + parent: "Node" = None, + title: str = "", + annotations: dict = None, + **kwargs, + ): """Constructor""" - super(Node, self).__init__(**kwargs) + super().__init__(**kwargs) if annotations is None: annotations = {} @@ -263,20 +332,32 @@ def __parent__(self): def __parent__(self, value): self.parent = value - def __repr__(self): - return '<%s %s at %s>' % ( - self.__class__.__name__, self.id, resource_path(self)) + def __repr__(self) -> str: + return "<{} {} at {}>".format( + self.__class__.__name__, self.id, resource_path(self) + ) - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: return isinstance(other, Node) and self.id == other.id - def __ne__(self, other): + def __ne__(self, other: Any) -> bool: return not self == other copy_properties_blacklist = ( - 'id', 'parent', 'parent_id', '_children', 'local_groups', '_tags') + "id", + "parent", + "parent_id", + "_children", + "local_groups", + "_tags", + ) + + def clear(self) -> None: + + for node in DBSession.query(Node).filter(Node.parent == self): + DBSession.delete(node) - def copy(self, **kwargs): + def copy(self, **kwargs) -> "Node": """ :result: A copy of the current instance :rtype: :class:`~kotti.resources.Node` @@ -295,19 +376,20 @@ def copy(self, **kwargs): return copy -class TypeInfo(object): +class TypeInfo: """TypeInfo instances contain information about the type of a node. - You can pass arbitrary keyword arguments in the constructor, they - will become instance attributes. The most common are: - - - name - - title - - add_view - - addable_to - - edit_links - - selectable_default_views - - uploadable_mimetypes + You can pass arbitrary keyword arguments in the constructor, they + will become instance attributes. The most common are: + + - name + - title + - add_view + - addable_to + - edit_links + - selectable_default_views + - uploadable_mimetypes + - add_permission """ addable_to = () @@ -316,23 +398,29 @@ class TypeInfo(object): edit_links = () action_links = () # BBB - def __init__(self, **kwargs): - if 'action_links' in kwargs: - msg = ("'action_links' is deprecated as of Kotti 0.10. " - "'edit_links' includes 'action_links' and should " - "be used instead.") + def __init__(self, **kwargs) -> None: + if "action_links" in kwargs: + msg = ( + "'action_links' is deprecated as of Kotti 1.0.0. " + "'edit_links' includes 'action_links' and should " + "be used instead." + ) - edit_links = kwargs.get('edit_links') + edit_links = kwargs.get("edit_links") last_link = edit_links[-1] if edit_links else None if isinstance(last_link, LinkParent): - last_link.children.extend(kwargs['action_links']) + last_link.children.extend(kwargs["action_links"]) warnings.warn(msg, DeprecationWarning) else: raise ValueError(msg) + # default value for add_permission should be 'add' + if "add_permission" not in kwargs: + kwargs["add_permission"] = "add" + self.__dict__.update(kwargs) - def copy(self, **kwargs): + def copy(self, **kwargs) -> "TypeInfo": """ :result: a copy of the current TypeInfo instance @@ -340,11 +428,12 @@ def copy(self, **kwargs): """ d = self.__dict__.copy() + d["selectable_default_views"] = copy(self.selectable_default_views) d.update(kwargs) return TypeInfo(**d) - def addable(self, context, request): + def addable(self, context: "Content", request: Optional[Request]) -> bool: """ :param context: @@ -353,19 +442,21 @@ def addable(self, context, request): :class:`~kotti.resources.TypeInfo`) :param request: current request - :type request: :class:`pyramid.request.Request` + :type request: :class:`kotti.request.Request` :result: True if the type described in 'self' may be added to 'context', False otherwise. :rtype: Boolean """ - if view_permitted(context, request, self.add_view): - return context.type_info.name in self.addable_to + if self.add_view is None: + return False + if context.type_info.name in self.addable_to: + return bool(view_permitted(context, request, self.add_view)) else: return False - def add_selectable_default_view(self, name, title): + def add_selectable_default_view(self, name: str, title: str) -> None: """Add a view to the list of default views selectable by the user in the UI. @@ -373,12 +464,12 @@ def add_selectable_default_view(self, name, title): :type name: str :param title: Title for the view for display in the UI. - :type title: unicode or TranslationString + :type title: str or TranslationString """ self.selectable_default_views.append((name, title)) - def is_uploadable_mimetype(self, mimetype): - """ Check if uploads of the given MIME type are allowed. + def is_uploadable_mimetype(self, mimetype: str) -> int: + """Check if uploads of the given MIME type are allowed. :param mimetype: MIME type :type mimetype: str @@ -392,9 +483,8 @@ def is_uploadable_mimetype(self, mimetype): match_score = 0 for mt in self.uploadable_mimetypes: - if fnmatch(mimetype, mt): - if len(mt) > match_score: - match_score = len(mt) + if fnmatch(mimetype, mt) and len(mt) > match_score: + match_score = len(mt) return match_score @@ -414,11 +504,11 @@ class Tag(Base): #: :class:`sqlalchemy.types.Unicode` title = Column(Unicode(100), unique=True, nullable=False) - def __repr__(self): - return "" % self.title + def __repr__(self) -> str: + return f"" @property - def items(self): + def items(self) -> List[Node]: """ :result: @@ -429,36 +519,35 @@ def items(self): class TagsToContents(Base): - """Tags to contents mapping - """ + """Tags to contents mapping""" - __tablename__ = 'tags_to_contents' + __tablename__ = "tags_to_contents" #: Foreign key referencing :attr:`Tag.id` #: (:class:`sqlalchemy.types.Integer`) - tag_id = Column(Integer, ForeignKey('tags.id'), primary_key=True) + tag_id = Column(ForeignKey("tags.id"), primary_key=True, index=True) #: Foreign key referencing :attr:`Content.id` #: (:class:`sqlalchemy.types.Integer`) - content_id = Column(Integer, ForeignKey('contents.id'), primary_key=True) + content_id = Column(ForeignKey("contents.id"), primary_key=True, index=True) #: Relation that adds a ``content_tags`` :func:`sqlalchemy.orm.backref` #: to :class:`~kotti.resources.Tag` instances to allow easy access to all #: content tagged with that tag. #: (:func:`sqlalchemy.orm.relationship`) - tag = relation(Tag, backref=backref('content_tags', cascade='all')) + tag = relation(Tag, backref=backref("content_tags", cascade="all"), lazy="joined") #: Ordering position of the tag #: :class:`sqlalchemy.types.Integer` position = Column(Integer, nullable=False) #: title of the associated :class:`~kotti.resources.Tag` instance #: (:class:`sqlalchemy.ext.associationproxy.association_proxy`) - title = association_proxy('tag', 'title') + title = association_proxy("tag", "title") @classmethod - def _tag_find_or_create(cls, title): + def _tag_find_or_create(cls, title: str) -> "TagsToContents": """ Find or create a tag with the given title. :param title: Title of the tag to find or create. - :type title: unicode + :type title: str :result: :rtype: :class:`~kotti.resources.TagsToContents` """ @@ -470,48 +559,48 @@ def _tag_find_or_create(cls, title): return cls(tag=tag) -def _not_root(context, request): - return not context is get_root() +# noinspection PyUnusedLocal +def _not_root(context: Node, request: Request) -> bool: + return context is not get_root() + +default_actions = [ + Link("copy", title=_("Copy")), + Link("cut", title=_("Cut"), predicate=_not_root), + Link("paste", title=_("Paste"), predicate=get_paste_items), + Link("rename", title=_("Rename"), predicate=_not_root), + Link("delete", title=_("Delete"), predicate=_not_root), + LinkRenderer("default-view-selector"), +] default_type_info = TypeInfo( - name=u'Content', - title=u'type_info title missing', # BBB + name="Content", + title="type_info title missing", # BBB add_view=None, addable_to=[], edit_links=[ - Link('contents', title=_(u'Contents')), - Link('edit', title=_(u'Edit')), - Link('share', title=_(u'Share')), - LinkParent(title=_(u'Actions'), children=[ - Link('copy', title=_(u'Copy')), - Link('cut', title=_(u'Cut'), predicate=_not_root), - Link('paste', title=_(u'Paste'), predicate=get_paste_items), - Link('rename', title=_(u'Rename'), predicate=_not_root), - Link('delete', title=_(u'Delete'), predicate=_not_root), - LinkRenderer('default-view-selector'), - ]), - ], - selectable_default_views=[ - ("folder_view", _(u"Folder view")), - ], - ) + Link("contents", title=_("Contents")), + Link("edit", title=_("Edit")), + Link("share", title=_("Share")), + LinkParent(title=_("Actions"), children=default_actions), + ], + selectable_default_views=[("folder_view", _("Folder view"))], +) +@implementer(IContent) class Content(Node): """Content adds some attributes to :class:`~kotti.resources.Node` that are - useful for content objects in a CMS. + useful for content objects in a CMS. """ - implements(IContent) - @classproperty def __mapper_args__(cls): return dict(polymorphic_identity=camel_case_to_name(cls.__name__)) #: Primary key column in the DB #: (:class:`sqlalchemy.types.Integer`) - id = Column(Integer, ForeignKey('nodes.id'), primary_key=True) + id = Column(ForeignKey(Node.id), primary_key=True) #: Name of the view that should be displayed to the user when #: visiting an URL without a explicit view name appended #: (:class:`sqlalchemy.types.String`) @@ -541,27 +630,35 @@ def __mapper_args__(cls): in_navigation = Column(Boolean()) _tags = relation( TagsToContents, - backref=backref('item'), + backref=backref("item"), order_by=[TagsToContents.position], collection_class=ordering_list("position"), - cascade='all, delete-orphan', - ) + cascade="all, delete-orphan", + ) #: Tags assigned to the content object (list of str) tags = association_proxy( - '_tags', - 'title', - creator=TagsToContents._tag_find_or_create, - ) + "_tags", "title", creator=TagsToContents._tag_find_or_create + ) #: type_info is a class attribute (:class:`TypeInfo`) type_info = default_type_info - def __init__(self, name=None, parent=None, title=u"", annotations=None, - default_view=None, description=u"", language=None, - owner=None, creation_date=None, modification_date=None, - in_navigation=True, tags=None, **kwargs): - - super(Content, self).__init__( - name, parent, title, annotations, **kwargs) + def __init__( + self, + name: Optional[str] = None, + parent: Optional[Node] = None, + title: Optional[str] = "", + annotations: Optional[dict] = None, + default_view: Optional[str] = None, + description: Optional[str] = "", + language: Optional[str] = None, + owner: Optional[str] = None, + creation_date: Optional[datetime.datetime] = None, + modification_date: Optional[datetime.datetime] = None, + in_navigation: Optional[bool] = True, + tags: Optional[List[str]] = None, + **kwargs, + ): + super().__init__(name, parent, title, annotations, **kwargs) self.default_view = default_view self.description = description @@ -573,25 +670,24 @@ def __init__(self, name=None, parent=None, title=u"", annotations=None, self.modification_date = modification_date self.tags = tags or [] - def copy(self, **kwargs): + def copy(self, **kwargs) -> "Content": # Same as `Node.copy` with additional tag support. - kwargs['tags'] = self.tags - return super(Content, self).copy(**kwargs) + kwargs["tags"] = self.tags + return super().copy(**kwargs) +@implementer(IDocument, IDefaultWorkflow) class Document(Content): """Document extends :class:`~kotti.resources.Content` with a body and its - mime_type. In addition Document and its descendants implement - :class:`~kotti.interfaces.IDefaultWorkflow` and therefore - are associated with the default workflow (at least in - unmodified Kotti installations). + mime_type. In addition Document and its descendants implement + :class:`~kotti.interfaces.IDefaultWorkflow` and therefore + are associated with the default workflow (at least in + unmodified Kotti installations). """ - implements(IDocument, IDefaultWorkflow) - #: Primary key column in the DB #: (:class:`sqlalchemy.types.Integer`) - id = Column(Integer(), ForeignKey('contents.id'), primary_key=True) + id = Column(ForeignKey(Content.id), primary_key=True) #: Body text of the Document #: (:class:`sqlalchemy.types.Unicode`) body = Column(UnicodeText()) @@ -602,33 +698,30 @@ class Document(Content): #: type_info is a class attribute #: (:class:`~kotti.resources.TypeInfo`) type_info = Content.type_info.copy( - name=u'Document', - title=_(u'Document'), - add_view=u'add_document', - addable_to=[u'Document'], - ) - - def __init__(self, body=u"", mime_type='text/html', **kwargs): + name="Document", + title=_("Document"), + add_view="add_document", + addable_to=["Document"], + ) - super(Document, self).__init__(**kwargs) + def __init__( + self, body: Optional[str] = "", mime_type: Optional[str] = "text/html", **kwargs + ): + super().__init__(**kwargs) self.body = body self.mime_type = mime_type -class File(Content): - """File adds some attributes to :class:`~kotti.resources.Content` that are - useful for storing binary data. - """ +# noinspection PyMethodParameters +class SaveDataMixin: + """The classmethods must not be implemented on a class that inherits + from ``Base`` with ``SQLAlchemy>=1.0``, otherwise that class cannot be + subclassed further. - implements(IFile) + See http://stackoverflow.com/questions/30433960/how-to-use-declare-last-in-sqlalchemy-1-0 # noqa + """ - #: Primary key column in the DB - #: (:class:`sqlalchemy.types.Integer`) - id = Column(Integer(), ForeignKey('contents.id'), primary_key=True) - #: The binary data itself - #: (:class:`sqlalchemy.types.LargeBinary`) - data = deferred(Column("data", LargeBinary())) #: The filename is used in the attachment view to give downloads #: the original filename it had when it was uploaded. #: (:class:`sqlalchemy.types.Unicode`) @@ -640,119 +733,224 @@ class File(Content): #: (:class:`sqlalchemy.types.Integer`) size = Column(Integer()) - type_info = Content.type_info.copy( - name=u'File', - title=_(u'File'), - add_view=u'add_file', - addable_to=[u'Document'], - selectable_default_views=[], - uploadable_mimetypes=['*', ], - ) + #: Filedepot mapped blob + #: (:class:`depot.fileds.sqlalchemy.UploadedFileField`) + @declared_attr + def data(cls) -> Column: + return cls.__table__.c.get("data", Column(UploadedFileField(cls.data_filters))) - def __init__(self, data=None, filename=None, mimetype=None, size=None, - **kwargs): + data_filters = () - super(File, self).__init__(**kwargs) + @classmethod + def __declare_last__(cls) -> None: + """Unconfigure the event set in _SQLAMutationTracker, + we have _save_data""" + + mapper = cls._sa_class_manager.mapper + args = (mapper.attrs["data"], "set", _SQLAMutationTracker._field_set) + if event.contains(*args): + event.remove(*args) + + # Declaring the event on the class attribute instead of mapper property + # enables proper registration on its subclasses + event.listen(cls.data, "set", cls._save_data, retval=True) + + @staticmethod + def _save_data( + target: "File", + value: Optional[ + Union[FieldStorage, bytes, UploadedFile, BufferedReader] + ], # noqa + oldvalue: Optional[Union[UploadedFile, _symbol]], + initiator: Event, + ) -> Optional[UploadedFile]: + """Refresh metadata and save the binary data to the data field. + + :param target: The File instance + :type target: :class:`kotti.resources.File` or subclass + + :param value: The container for binary data + :type value: A :class:`cgi.FieldStorage` instance + """ - self.data = data - self.filename = filename - self.mimetype = mimetype - self.size = size + if isinstance(value, bytes): + fp = BytesIO(value) + value = _to_fieldstorage( + fp=fp, + filename=target.filename, + mimetype=target.mimetype, + size=len(value), + ) + + newvalue = _SQLAMutationTracker._field_set(target, value, oldvalue, initiator) + + if newvalue is None: + return + + target.filename = newvalue.filename + target.mimetype = newvalue.content_type + target.size = newvalue.file.content_length + + return newvalue @classmethod def from_field_storage(cls, fs): - """ Create and return an instance of this class from a file upload + """Create and return an instance of this class from a file upload through a webbrowser. :param fs: FieldStorage instance as found in a - :class:`pyramid.request.Request`'s ``POST`` MultiDict. + :class:`kotti.request.Request`'s ``POST`` MultiDict. :type fs: :class:`cgi.FieldStorage` :result: The created instance. :rtype: :class:`kotti.resources.File` """ - data = fs.file.read() - filename = fs.filename - mimetype = fs.type - size = len(data) + if not cls.type_info.is_uploadable_mimetype(fs.type): + raise ValueError(f"Unsupported MIME type: {fs.type}") - if not cls.type_info.is_uploadable_mimetype(mimetype): - raise ValueError("Unsupported MIME type: %s" % mimetype) + return cls(data=fs) - return cls(data=data, filename=filename, mimetype=mimetype, size=size) + def __init__( + self, + data: Optional[Union[bytes, BufferedReader, FieldStorage]] = None, # noqa + filename: Optional[str] = None, + mimetype: Optional[str] = None, + size: Optional[int] = None, + **kwargs, + ) -> None: + super().__init__(**kwargs) + + self.filename = filename + self.mimetype = mimetype + self.size = size + self.data = data + + def copy(self, **kwargs) -> "File": + """Same as `Content.copy` with additional data support. ``data`` needs + some special attention, because we don't want the same depot file to be + assigned to multiple content nodes. + """ + _copy = super().copy(**kwargs) + _copy.data = self.data.file.read() + return _copy -class Image(File): - """Image doesn't add anything to :class:`~kotti.resources.File`, but images - have different views, that e.g. support on the fly scaling. - """ - implements(IImage) +@implementer(IFile) +class File(SaveDataMixin, Content): + """File adds some attributes to :class:`~kotti.resources.Content` that are + useful for storing binary data. + """ - id = Column(Integer(), ForeignKey('files.id'), primary_key=True) + #: Primary key column in the DB + #: (:class:`sqlalchemy.types.Integer`) + id = Column(ForeignKey(Content.id), primary_key=True) - type_info = File.type_info.copy( - name=u'Image', - title=_(u'Image'), - add_view=u'add_image', - addable_to=[u'Document'], + type_info = Content.type_info.copy( + name="File", + title=_("File"), + add_view="add_file", + addable_to=["Document"], selectable_default_views=[], - uploadable_mimetypes=['image/*', ], - ) + uploadable_mimetypes=["*"], + ) -def get_root(request=None): +def get_root(request: Optional[Request] = None) -> Node: """Call the function defined by the ``kotti.root_factory`` setting and return its result. :param request: current request (optional) - :type request: :class:`pyramid.request.Request` + :type request: :class:`kotti.request.Request` :result: a node in the node tree :rtype: :class:`~kotti.resources.Node` or descendant; """ - return get_settings()['kotti.root_factory'][0](request) + return get_settings()["kotti.root_factory"][0](request) -def default_get_root(request=None): - """Default implementation for :func:`~kotti.resources.get_root`. +class DefaultRootCache: + """ Default implementation for :func:`~kotti.resources.get_root` """ - :param request: Current request (optional) - :type request: :class:`pyramid.request.Request` + _root = None - :result: Node in the object tree that has no parent. - :rtype: :class:`~kotti.resources.Node` or descendant; in a fresh Kotti site - with Kotti's :func:`default populator ` - this will be an instance of :class:`~kotti.resources.Document`. - """ - return DBSession.query(Node).filter(Node.parent_id == None).one() + # noinspection PyComparisonWithNone,PyPep8 + @reify + def root_id(self) -> int: + """Query for the one node without a parent and return its id. + :result: The root node's id. + :rtype: int + """ + + query = bakery( + lambda session: session.query(Node) + .with_polymorphic(Node) + .add_columns(Node.id) + .enable_eagerloads(False) + .filter(Node.parent_id == None) + ) + + return query(DBSession()).one().id + + def get_root(self) -> Node: + """Query for the root node by its id. This enables SQLAlchemy's + session cache (query is executed only once per session). + :result: The root node. + :rtype: :class:`Node`. + """ + + if self._root is None or inspect(self._root).detached: + self._root = ( + DBSession.query(Node) + .with_polymorphic(Node) + .enable_eagerloads(False) + .enable_eagerloads("local_groups") + .filter(Node.id == self.root_id) + .one() + ) + + return self._root + + def __call__(self, request: Optional[Request] = None) -> Node: + """Default implementation for :func:`~kotti.resources.get_root` + :param request: Current request (optional) + :type request: :class:`kotti.request.Request` + :result: Node in the object tree that has no parent. + :rtype: :class:`~kotti.resources.Node` or descendant; + in a fresh Kotti site with Kotti's + :func:`default populator ` this will + be an instance of :class:`~kotti.resources.Document`. + """ + + return self.get_root() + + +default_get_root = DefaultRootCache() -def _adjust_for_engine(engine): - if engine.dialect.name == 'mysql': # pragma: no cover - from sqlalchemy.dialects.mysql.base import LONGBLOB - File.__table__.c.data.type = LONGBLOB() +def _adjust_for_engine(engine: Engine) -> None: + if engine.dialect.name == "mysql": # pragma: no cover # We disable the Node.path index for Mysql; in some conditions # the index can't be created for columns even with 767 bytes, # the maximum default size for column indexes - Node.__table__.indexes = set( - index for index in Node.__table__.indexes - if index.name != u"ix_nodes_path") + Node.__table__.indexes = { + index for index in Node.__table__.indexes if index.name != "ix_nodes_path" + } -def initialize_sql(engine, drop_all=False): +def initialize_sql(engine: Engine, drop_all: bool = False) -> scoped_session: DBSession.registry.clear() DBSession.configure(bind=engine) metadata.bind = engine - if drop_all or os.environ.get('KOTTI_TEST_DB_STRING'): + if drop_all or os.environ.get("KOTTI_TEST_DB_STRING"): metadata.reflect() metadata.drop_all(engine) # Allow users of Kotti to cherry pick the tables that they want to use: settings = _resolve_dotted(get_settings()) - tables = settings['kotti.use_tables'].strip() or None + tables = settings["kotti.use_tables"].strip() or None if tables: tables = [metadata.tables[name] for name in tables.split()] @@ -764,8 +962,8 @@ def initialize_sql(engine, drop_all=False): stamp_heads() metadata.create_all(engine, tables=tables) - if os.environ.get('KOTTI_DISABLE_POPULATORS', '0') not in TRUE_VALUES: - for populate in settings['kotti.populators']: + if os.environ.get("KOTTI_DISABLE_POPULATORS", "0") not in TRUE_VALUES: + for populate in settings["kotti.populators"]: populate() commit() diff --git a/kotti/sanitizers.py b/kotti/sanitizers.py new file mode 100644 index 000000000..b2b683556 --- /dev/null +++ b/kotti/sanitizers.py @@ -0,0 +1,165 @@ +""" For a high level introduction and available configuration options +see :ref:`sanitizers`. +""" +from typing import Dict +from typing import Union + +from bleach import clean +from bleach_allowlist import all_styles +from bleach_allowlist import generally_xss_safe +from bleach_allowlist import markdown_attrs +from bleach_allowlist import markdown_tags +from bleach_allowlist import print_attrs +from bleach_allowlist import print_tags +from pyramid.config import Configurator +from pyramid.util import DottedNameResolver + +from kotti import get_settings +from kotti.events import ObjectInsert +from kotti.events import ObjectUpdate +from kotti.events import objectevent_listeners + + +def sanitize(html: str, sanitizer: str) -> str: + """ Sanitize HTML + + :param html: HTML to be sanitized + :type html: basestring + + :param sanitizer: name of the sanitizer to use + :type sanitizer: str + + :result: sanitized HTML + :rtype: str + """ + + sanitized = get_settings()["kotti.sanitizers"][sanitizer](html) + + return sanitized + + +def xss_protection(html: str) -> str: + """ Sanitizer that removes tags that are not considered XSS safe. See + ``bleach_whitelist.generally_xss_unsafe`` for a complete list of tags that + are removed. Attributes and styles are left untouched. + + :param html: HTML to be sanitized + :type html: basestring + + :result: sanitized HTML + :rtype: str + """ + + sanitized = clean( + html, + tags=generally_xss_safe, + attributes=lambda self, key, value: True, + styles=all_styles, + strip=True, + ) + + return sanitized + + +def minimal_html(html: str) -> str: + """ Sanitizer that only leaves a basic set of tags and attributes. See + ``bleach_whitelist.markdown_tags``, ``bleach_whitelist.print_tags``, + ``bleach_whitelist.markdown_attrs``, ``bleach_whitelist.print_attrs`` for a + complete list of tags and attributes that are allowed. All styles are + completely removed. + + :param html: HTML to be sanitized + :type html: basestring + + :result: sanitized HTML + :rtype: str + """ + + attributes = dict( + zip( + list(markdown_attrs.keys()) + list(print_attrs.keys()), + list(markdown_attrs.values()) + list(print_attrs.values()), + ) + ) + + sanitized = clean( + html, + tags=markdown_tags + print_tags, + attributes=attributes, + styles=[], + strip=True, + ) + + return sanitized + + +def no_html(html: str) -> str: + """ Sanitizer that removes **all** tags. + + :param html: HTML to be sanitized + :type html: basestring + + :result: plain text + :rtype: str + """ + + sanitized = clean(html, tags=[], attributes={}, styles=[], strip=True) + + return sanitized + + +def _setup_sanitizers(settings: Dict[str, Union[str, bool]]) -> None: + + # step 1: resolve sanitizer functions and make ``kotti.sanitizers`` a + # dictionary containing resolved functions + + if not isinstance(settings["kotti.sanitizers"], str): + return + + sanitizers = {} + + for s in settings["kotti.sanitizers"].split(): + name, dottedname = s.split(":") + sanitizers[name.strip()] = DottedNameResolver().resolve(dottedname) + + settings["kotti.sanitizers"] = sanitizers + + +def _setup_listeners(settings): + + # step 2: setup listeners + + for s in settings["kotti.sanitize_on_write"].split(): + dotted, sanitizers = s.split(":") + + classname, attributename = dotted.rsplit(".", 1) + _class = DottedNameResolver().resolve(classname) + + def _create_handler(attributename, sanitizers): + def handler(event): + value = getattr(event.object, attributename) + if value is None: + return + for sanitizer_name in sanitizers.split(","): + value = settings["kotti.sanitizers"][sanitizer_name](value) + setattr(event.object, attributename, value) + + return handler + + objectevent_listeners[(ObjectInsert, _class)].append( + _create_handler(attributename, sanitizers) + ) + objectevent_listeners[(ObjectUpdate, _class)].append( + _create_handler(attributename, sanitizers) + ) + + +def includeme(config: Configurator) -> None: + """ Pyramid includeme hook. + + :param config: app config + :type config: :class:`pyramid.config.Configurator` + """ + + _setup_sanitizers(config.registry.settings) + _setup_listeners(config.registry.settings) diff --git a/kotti/scaffolds/__init__.py b/kotti/scaffolds/__init__.py deleted file mode 100644 index 819b379cb..000000000 --- a/kotti/scaffolds/__init__.py +++ /dev/null @@ -1,100 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Created on 2014-11-17 -:author: Andreas Kaiser (disko) - -Scaffolds for Kotti. - -The methods of :class:`KottiTemplate` are marked with ``pragma: no cover`` -because they are not directly touched during testsuite runs, but only within -subprocesses which seems to be not recorded by coverage. -""" - -import datetime -import os - -from pyramid.decorator import reify -from pyramid.scaffolds import PyramidTemplate -from textwrap import dedent -from usersettings import Settings - - -class KottiTemplate(PyramidTemplate): - """ Base class for Kotti Templates """ - - @reify - def _settings(self): # pragma: no cover - s = Settings('org.pylonsproject.kotti.ScaffoldDefaults') - s.add_setting("author", unicode, default='') - s.add_setting("email", str, default='') - s.add_setting("gh_user", str, '') - s.load_settings() # loads anything that might be saved - - return s - - def _get(self, key, caption): # pragma: no cover - - env = os.environ.get(key) - if env is not None: - return env - - s = self._settings - s[key] = raw_input(u'{} [{}]: '.format(caption, s[key])) or s[key] - - try: - s.save_settings() - except OSError: - self.out("Your answers were not saved for later use.") - - return s[key] - - def pre(self, command, output_dir, vars): # pragma: no cover - """ Overrides :meth:`pyramid.scaffolds.PyramidTemplate.pre`, adding - several variables to the default variables list. - - :param command: Command that invoked the template - :type command: :class:`pyramid.scripts.pcreate.PCreateCommand` - - :param output_dir: Full filesystem path where the created package will - be created - :type output_dir: str - - :param vars: Dictionary of vars passed to the templates for rendering - :type vars: dict - """ - - vars['project_line'] = '*' * len(vars['project']) - vars['date'] = datetime.date.today().strftime('%Y-%m-%d') - - vars['author'] = self._get('author', 'Author name') - vars['email'] = self._get('email', 'Author email') - vars['gh_user'] = self._get('gh_user', 'Github username') - - return PyramidTemplate.pre(self, command, output_dir, vars) - - def post(self, command, output_dir, vars): # pragma: no cover - """ Overrides :meth:`pyramid.scaffolds.PyramidTemplate.post`, to - print some info after a successful scaffolding rendering.""" - - separator = "=" * 79 - msg = dedent( - """ - %(separator)s - Welcome to Kotti! - - Documentation: http://kotti.readthedocs.org/ - Development: https://github.com/Kotti/Kotti/ - Issues: https://github.com/Kotti/Kotti/issues?state=open - IRC: irc://irc.freenode.net/#kotti - Mailing List: https://groups.google.com/group/kotti - %(separator)s - """ % {'separator': separator}) - - self.out(msg) - - -class KottiPackageTemplate(KottiTemplate): - - _template_dir = 'package' - summary = 'A Kotti package that can be used for a project or an add on' diff --git a/kotti/scaffolds/package/+dot+coveragerc_tmpl b/kotti/scaffolds/package/+dot+coveragerc_tmpl deleted file mode 100644 index 01a74fed3..000000000 --- a/kotti/scaffolds/package/+dot+coveragerc_tmpl +++ /dev/null @@ -1,2 +0,0 @@ -[run] -omit = {{package}}/tests/* diff --git a/kotti/scaffolds/package/+dot+gitignore b/kotti/scaffolds/package/+dot+gitignore deleted file mode 100644 index 52e6aa54d..000000000 --- a/kotti/scaffolds/package/+dot+gitignore +++ /dev/null @@ -1,31 +0,0 @@ -*.egg -*.egg-info/ -*.log -*.manifest -*.py[cod] -*.so -*.spec -.cache -.coverage -.installed.cfg -.Python -.tox/ -__pycache__/ -build/ -coverage.xml -develop-eggs/ -dist/ -docs/_build/ -downloads/ -eggs/ -env/ -htmlcov/ -lib/ -lib64/ -nosetests.xml -parts/ -pip-delete-this-directory.txt -pip-log.txt -sdist/ -target/ -var/ diff --git a/kotti/scaffolds/package/+dot+travis.yml_tmpl b/kotti/scaffolds/package/+dot+travis.yml_tmpl deleted file mode 100644 index 6710de218..000000000 --- a/kotti/scaffolds/package/+dot+travis.yml_tmpl +++ /dev/null @@ -1,18 +0,0 @@ -language: python -python: - - "2.6" - - "2.7" -env: - - KOTTI_TEST_DB_STRING=postgresql://postgres@localhost:5432/{{package}}_testing - - KOTTI_TEST_DB_STRING=mysql+oursql://root@localhost:3306/{{package}}_testing - - KOTTI_TEST_DB_STRING=sqlite:// -install: - - pip install "pip==1.3.1" # fix issue with fanstatic==1.0a - - pip install psycopg2 oursql - - pip install -e . -r https://raw.github.com/Kotti/Kotti/0.10b1/requirements.txt - - pip install "Kotti==0.10b1" - - pip install "wsgi_intercept==0.5.1" - - python setup.py devbefore_script: - - psql -c 'create database {{package}}_testing;' -U postgres - - mysql -e 'create database {{package}}_testing;' -script: py.test --tb=native --cov=kotti --cov-report=term-missing diff --git a/kotti/scaffolds/package/+package+/__init__.py_tmpl b/kotti/scaffolds/package/+package+/__init__.py_tmpl deleted file mode 100644 index 675f116a7..000000000 --- a/kotti/scaffolds/package/+package+/__init__.py_tmpl +++ /dev/null @@ -1,43 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Created on {{date}} -:author: {{author}} ({{email}}) -""" - -from kotti.resources import File -from pyramid.i18n import TranslationStringFactory - -_ = TranslationStringFactory('{{package}}') - - -def kotti_configure(settings): - """ Add a line like this to you .ini file:: - - kotti.configurators = - {{package}}.kotti_configure - - to enable the ``{{package}}`` add-on. - - :param settings: Kotti configuration dictionary. - :type settings: dict - """ - - settings['pyramid.includes'] += ' {{package}}' - settings['kotti.available_types'] += ' {{package}}.resources.CustomContent' - settings['kotti.fanstatic.view_needed'] += ' {{package}}.fanstatic.css_and_js' - File.type_info.addable_to.append('CustomContent') - - -def includeme(config): - """ Don't add this to your ``pyramid_includes``, but add the - ``kotti_configure`` above to your ``kotti.configurators`` instead. - - :param config: Pyramid configurator object. - :type config: :class:`pyramid.config.Configurator` - """ - - config.add_translation_dirs('{{package}}:locale') - config.add_static_view('static-{{package}}', '{{package}}:static') - - config.scan(__name__) diff --git a/kotti/scaffolds/package/+package+/alembic/script.py.mako b/kotti/scaffolds/package/+package+/alembic/script.py.mako deleted file mode 100644 index 95702017e..000000000 --- a/kotti/scaffolds/package/+package+/alembic/script.py.mako +++ /dev/null @@ -1,22 +0,0 @@ -"""${message} - -Revision ID: ${up_revision} -Revises: ${down_revision} -Create Date: ${create_date} - -""" - -# revision identifiers, used by Alembic. -revision = ${repr(up_revision)} -down_revision = ${repr(down_revision)} - -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} - -def upgrade(): - ${upgrades if upgrades else "pass"} - - -def downgrade(): - ${downgrades if downgrades else "pass"} diff --git a/kotti/scaffolds/package/+package+/alembic/versions/.keep b/kotti/scaffolds/package/+package+/alembic/versions/.keep deleted file mode 100644 index b5e8af4e0..000000000 --- a/kotti/scaffolds/package/+package+/alembic/versions/.keep +++ /dev/null @@ -1,2 +0,0 @@ -# Don't delete. A file is necessary to have an otherwise empty folder -# added to a git repository. diff --git a/kotti/scaffolds/package/+package+/fanstatic.py_tmpl b/kotti/scaffolds/package/+package+/fanstatic.py_tmpl deleted file mode 100644 index 8d0f89fa6..000000000 --- a/kotti/scaffolds/package/+package+/fanstatic.py_tmpl +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Created on {{date}} -:author: {{author}} ({{email}}) -""" - -from __future__ import absolute_import - -from fanstatic import Group -from fanstatic import Library -from fanstatic import Resource - - -library = Library("{{package}}", "static") - -css = Resource( - library, - "styles.css", - minified="styles.min.css") -js = Resource( - library, - "scripts.js", - minified="scripts.min.js") - -css_and_js = Group([css, js]) diff --git a/kotti/scaffolds/package/+package+/locale/.keep b/kotti/scaffolds/package/+package+/locale/.keep deleted file mode 100644 index b5e8af4e0..000000000 --- a/kotti/scaffolds/package/+package+/locale/.keep +++ /dev/null @@ -1,2 +0,0 @@ -# Don't delete. A file is necessary to have an otherwise empty folder -# added to a git repository. diff --git a/kotti/scaffolds/package/+package+/resources.py_tmpl b/kotti/scaffolds/package/+package+/resources.py_tmpl deleted file mode 100644 index c545f729c..000000000 --- a/kotti/scaffolds/package/+package+/resources.py_tmpl +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Created on {{date}} -:author: {{author}} ({{email}}) -""" - -from kotti.interfaces import IDefaultWorkflow -from kotti.resources import Content -from sqlalchemy import Column -from sqlalchemy import ForeignKey -from sqlalchemy import Integer -from sqlalchemy import Unicode -from zope.interface import implements - -from {{package}} import _ - - -class CustomContent(Content): - """ A custom content type. """ - - implements(IDefaultWorkflow) - - id = Column(Integer, ForeignKey('contents.id'), primary_key=True) - custom_attribute = Column(Unicode(1000)) - - type_info = Content.type_info.copy( - name=u'CustomContent', - title=_(u'CustomContent'), - add_view=u'add_custom_content', - addable_to=[u'Document'], - selectable_default_views=[ - ("alternative-view", _(u"Alternative view")), - ], - ) - - def __init__(self, custom_attribute=None, **kwargs): - """ Constructor - - :param custom_attribute: A very custom attribute - :type custom_attribute: unicode - - :param **kwargs: Arguments that are passed to the base class(es) - :type **kwargs: see :class:`kotti.resources.Content` - """ - - super(CustomContent, self).__init__(**kwargs) - - self.custom_attribute = custom_attribute diff --git a/kotti/scaffolds/package/+package+/static/scripts.js b/kotti/scaffolds/package/+package+/static/scripts.js deleted file mode 100644 index bf8cd0baf..000000000 --- a/kotti/scaffolds/package/+package+/static/scripts.js +++ /dev/null @@ -1 +0,0 @@ -/* JS */ diff --git a/kotti/scaffolds/package/+package+/static/scripts.min.js b/kotti/scaffolds/package/+package+/static/scripts.min.js deleted file mode 100644 index bf8cd0baf..000000000 --- a/kotti/scaffolds/package/+package+/static/scripts.min.js +++ /dev/null @@ -1 +0,0 @@ -/* JS */ diff --git a/kotti/scaffolds/package/+package+/static/styles.css b/kotti/scaffolds/package/+package+/static/styles.css deleted file mode 100644 index f077569bd..000000000 --- a/kotti/scaffolds/package/+package+/static/styles.css +++ /dev/null @@ -1 +0,0 @@ -/* CSS */ diff --git a/kotti/scaffolds/package/+package+/static/styles.min.css b/kotti/scaffolds/package/+package+/static/styles.min.css deleted file mode 100644 index f077569bd..000000000 --- a/kotti/scaffolds/package/+package+/static/styles.min.css +++ /dev/null @@ -1 +0,0 @@ -/* CSS */ diff --git a/kotti/scaffolds/package/+package+/templates/custom-content-alternative.pt_tmpl b/kotti/scaffolds/package/+package+/templates/custom-content-alternative.pt_tmpl deleted file mode 100644 index b54067052..000000000 --- a/kotti/scaffolds/package/+package+/templates/custom-content-alternative.pt_tmpl +++ /dev/null @@ -1,26 +0,0 @@ - - - -
    - -
    -
    -

    ${context.title}

    -
    -
    - ${context.description} -
    - -
    - -
    - diff --git a/kotti/scaffolds/package/+package+/templates/custom-content-default.pt_tmpl b/kotti/scaffolds/package/+package+/templates/custom-content-default.pt_tmpl deleted file mode 100644 index 6157f5393..000000000 --- a/kotti/scaffolds/package/+package+/templates/custom-content-default.pt_tmpl +++ /dev/null @@ -1,20 +0,0 @@ - - - -
    - -

    ${context.title}

    -

    - ${context.description} -

    -
    - -
    - diff --git a/kotti/scaffolds/package/+package+/tests/conftest.py_tmpl b/kotti/scaffolds/package/+package+/tests/conftest.py_tmpl deleted file mode 100644 index 9f46d9105..000000000 --- a/kotti/scaffolds/package/+package+/tests/conftest.py_tmpl +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Created on {{date}} -:author: {{author}} ({{email}}) -""" - -pytest_plugins = "kotti" - -from pytest import fixture - - -@fixture(scope='session') -def custom_settings(): - import {{package}}.resources - {{package}}.resources # make pyflakes happy - return { - 'kotti.configurators': 'kotti_tinymce.kotti_configure ' - '{{package}}.kotti_configure'} diff --git a/kotti/scaffolds/package/+package+/tests/test_functional.py_tmpl b/kotti/scaffolds/package/+package+/tests/test_functional.py_tmpl deleted file mode 100644 index 590e64edb..000000000 --- a/kotti/scaffolds/package/+package+/tests/test_functional.py_tmpl +++ /dev/null @@ -1,50 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Created on {{date}} -:author: {{author}} ({{email}}) -""" - -from pytest import mark - - -def test_login_required(webtest, root): - resp = webtest.get('/add_custom_content') - assert resp.status_code == 302 - - -@mark.user('admin') -def test_add(webtest, root): - - resp = webtest.get('/add_custom_content') - - # submit empty form - form = resp.forms['deform'] - resp = form.submit('save') - assert 'There was a problem' in resp.body - - # submit valid form - form = resp.forms['deform'] - form['title'] = 'My Custom Content' - form['custom_attribute'] = 'My Custom Attribute Value' - resp = form.submit('save') - assert resp.status_code == 302 - resp = resp.follow() - assert 'Item was added.' in resp.body - - -@mark.user('admin') -def test_edit(webtest, root): - - from {{package}}.resources import CustomContent - - root['cc'] = CustomContent(title=u'Content Title') - - resp = webtest.get('/cc/@@edit') - form = resp.forms['deform'] - assert form['title'].value == u'Content Title' - assert form['custom_attribute'].value == u'' - form['custom_attribute'] = u'Bazinga' - resp = form.submit('save').maybe_follow() - assert u'Your changes have been saved.' in resp.body - assert u'Bazinga' in resp.body diff --git a/kotti/scaffolds/package/+package+/tests/test_resources.py_tmpl b/kotti/scaffolds/package/+package+/tests/test_resources.py_tmpl deleted file mode 100644 index c2bf6ea3c..000000000 --- a/kotti/scaffolds/package/+package+/tests/test_resources.py_tmpl +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Created on {{date}} -:author: {{author}} ({{email}}) -""" - -from pytest import raises - - -def test_model(root, db_session): - from {{package}}.resources import CustomContent - - cc = CustomContent() - assert cc.custom_attribute is None - - cc = CustomContent(custom_attribute=u'Foo') - assert cc.custom_attribute == u'Foo' - - root['cc'] = cc = CustomContent() - assert cc.name == 'cc' - - with raises(TypeError): - cc = CustomContent(doesnotexist=u'Foo') diff --git a/kotti/scaffolds/package/+package+/tests/test_view.py_tmpl b/kotti/scaffolds/package/+package+/tests/test_view.py_tmpl deleted file mode 100644 index d7aa09d66..000000000 --- a/kotti/scaffolds/package/+package+/tests/test_view.py_tmpl +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Created on {{date}} -:author: {{author}} ({{email}}) -""" - -from pytest import fixture - - -@fixture -def dummy_content(root): - - from {{package}}.resources import CustomContent - - root['cc'] = cc = CustomContent( - title=u'My content', - description=u'My very custom content is custom', - custom_attribute='Lorem ipsum' - ) - - return cc - - -def test_view(dummy_content, dummy_request): - - from {{package}}.views.view import CustomContentViews - - views = CustomContentViews(dummy_content, dummy_request) - - default = views.default_view() - assert 'foo' in default - - alternative = views.alternative_view() - assert alternative['foo'] == u'bar' diff --git a/kotti/scaffolds/package/+package+/views/__init__.py_tmpl b/kotti/scaffolds/package/+package+/views/__init__.py_tmpl deleted file mode 100644 index a95087b4b..000000000 --- a/kotti/scaffolds/package/+package+/views/__init__.py_tmpl +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Created on {{date}} -:author: {{author}} ({{email}}) -""" - - -class BaseView(object): - """ Base class for views """ - - def __init__(self, context, request): - """ Constructor - - :param context: Context of the view - :type context: :class:`kotti.resources.Content` - - :param request: Current request - :type request: :class:`pyramid.request.Request` - """ - - super(BaseView, self).__init__() - - self.context = context - self.request = request diff --git a/kotti/scaffolds/package/+package+/views/edit.py_tmpl b/kotti/scaffolds/package/+package+/views/edit.py_tmpl deleted file mode 100644 index df58fd09d..000000000 --- a/kotti/scaffolds/package/+package+/views/edit.py_tmpl +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Created on {{date}} -:author: {{author}} ({{email}}) -""" - -import colander -from kotti.views.edit import ContentSchema -from kotti.views.form import AddFormView -from kotti.views.form import EditFormView -from pyramid.view import view_config - -from {{package}} import _ -from {{package}}.resources import CustomContent - - -class CustomContentSchema(ContentSchema): - """ Schema for CustomContent. """ - - custom_attribute = colander.SchemaNode( - colander.String(), - title=_(u"Custom attribute")) - - -@view_config(name=CustomContent.type_info.add_view, permission='add', - renderer='kotti:templates/edit/node.pt') -class CustomContentAddForm(AddFormView): - """ Form to add a new instance of CustomContent. """ - - schema_factory = CustomContentSchema - add = CustomContent - item_type = _(u"CustomContent") - - -@view_config(name='edit', context=CustomContent, permission='edit', - renderer='kotti:templates/edit/node.pt') -class CustomContentEditForm(EditFormView): - """ Form to edit existing calendars. """ - - schema_factory = CustomContentSchema diff --git a/kotti/scaffolds/package/+package+/views/view.py_tmpl b/kotti/scaffolds/package/+package+/views/view.py_tmpl deleted file mode 100644 index f6e7970ce..000000000 --- a/kotti/scaffolds/package/+package+/views/view.py_tmpl +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Created on {{date}} -:author: {{author}} ({{email}}) -""" - -from pyramid.view import view_config -from pyramid.view import view_defaults - -from {{package}} import _ -from {{package}}.resources import CustomContent -from {{package}}.fanstatic import css_and_js -from {{package}}.views import BaseView - - -@view_defaults(context=CustomContent, permission='view') -class CustomContentViews(BaseView): - """ Views for :class:`{{package}}.resources.CustomContent` """ - - @view_config(name='view', permission='view', - renderer='{{package}}:templates/custom-content-default.pt') - def default_view(self): - """ Default view for :class:`{{package}}.resources.CustomContent` - - :result: Dictionary needed to render the template. - :rtype: dict - """ - - return { - 'foo': _(u'bar'), - } - - @view_config(name='alternative-view', permission='view', - renderer='{{package}}:templates/custom-content-alternative.pt') - def alternative_view(self): - """ Alternative view for :class:`{{package}}.resources.CustomContent`. - This view requires the JS / CSS resources defined in - :mod:`{{package}}.fanstatic`. - - :result: Dictionary needed to render the template. - :rtype: dict - """ - - css_and_js.need() - - return { - 'foo': _(u'bar'), - } diff --git a/kotti/scaffolds/package/CHANGES.rst_tmpl b/kotti/scaffolds/package/CHANGES.rst_tmpl deleted file mode 100644 index f386d9be8..000000000 --- a/kotti/scaffolds/package/CHANGES.rst_tmpl +++ /dev/null @@ -1,7 +0,0 @@ -History -======= - -0.1 - unreleased ----------------- - -- Create package with ``pcreate -s kotti {{project}}``. diff --git a/kotti/scaffolds/package/LICENSE.txt_tmpl b/kotti/scaffolds/package/LICENSE.txt_tmpl deleted file mode 100644 index 7ef40f26e..000000000 --- a/kotti/scaffolds/package/LICENSE.txt_tmpl +++ /dev/null @@ -1,40 +0,0 @@ -{{project}} is supplied under this license ("RPL", a BSD-derived license): - - A copyright notice accompanies this license document that identifies - the copyright holders. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - 1. Redistributions in source code must retain the accompanying - copyright notice, this list of conditions, and the following - disclaimer. - - 2. Redistributions in binary form must reproduce the accompanying - copyright notice, this list of conditions, and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - 3. Names of the copyright holders must not be used to endorse or - promote products derived from this software without prior - written permission from the copyright holders. - - 4. If any files are modified, you must cause the modified files to - carry prominent notices stating that you changed the files and - the date of any change. - - Disclaimer - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND - ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - SUCH DAMAGE. diff --git a/kotti/scaffolds/package/README.rst_tmpl b/kotti/scaffolds/package/README.rst_tmpl deleted file mode 100644 index 523912d65..000000000 --- a/kotti/scaffolds/package/README.rst_tmpl +++ /dev/null @@ -1,45 +0,0 @@ -{{project}} -{{project_line}} - -This is an extension to Kotti that allows to add foo to your site. - -|build status|_ - -`Find out more about Kotti`_ - -Development happens at https://github.com/{{gh_user}}/{{package}} - -.. |build status| image:: https://secure.travis-ci.org/{{gh_user}}/{{package}}.png?branch=master -.. _build status: http://travis-ci.org/{{gh_user}}/{{package}} -.. _Find out more about Kotti: http://pypi.python.org/pypi/Kotti - -Setup -===== - -To enable the extension in your Kotti site, activate the configurator:: - - kotti.configurators = - {{package}}.kotti_configure - -Database upgrade -================ - -If you are upgrading from a previous version you might have to migrate your -database. The migration is performed with `alembic`_ and Kotti's console script -``kotti-migrate``. To migrate, run -``kotti-migrate upgrade_all --scripts={{package}}:alembic``. - -For integration of alembic in your environment please refer to the -`alembic documentation`_. If you have problems with the upgrade, -please create a new issue in the `tracker`_. - -Development -=========== - -Contributions to {{project}} are highly welcome. -Just clone its `Github repository`_ and submit your contributions as pull requests. - -.. _alembic: http://pypi.python.org/pypi/alembic -.. _alembic documentation: http://alembic.readthedocs.org/en/latest/index.html -.. _tracker: https://github.com/{{gh_user}}/{{package}}/issues -.. _Github repository: https://github.com/{{gh_user}}/{{package}} diff --git a/kotti/scaffolds/package/development.ini_tmpl b/kotti/scaffolds/package/development.ini_tmpl deleted file mode 100644 index 49047ea6e..000000000 --- a/kotti/scaffolds/package/development.ini_tmpl +++ /dev/null @@ -1,76 +0,0 @@ -[app:kotti] -use = egg:kotti - -pyramid.reload_templates = true -pyramid.debug_templates = true -pyramid.debug_authorization = false -pyramid.debug_notfound = true -pyramid.debug_routematch = false -pyramid.includes = - pyramid_debugtoolbar - pyramid_tm -pyramid.default_locale_name = en - -sqlalchemy.url = sqlite:///%(here)s/{{package}}.db -#mail.default_sender = yourname@yourhost - -kotti.configurators = - kotti_tinymce.kotti_configure - {{package}}.kotti_configure -kotti.site_title = {{project}} site -kotti.secret = qwerty - -[filter:fanstatic] -use = egg:fanstatic#fanstatic - -[pipeline:main] -pipeline = - fanstatic - kotti - -[server:main] -use = egg:waitress#main -host = 127.0.0.1 -port = 5000 - -[alembic] -script_location = {{package}}:alembic - -# Begin logging configuration - -[loggers] -keys = root, {{package}}, sqlalchemy - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = INFO -handlers = console - -[logger_{{package}}] -level = DEBUG -handlers = -qualname = {{package}} - -[logger_sqlalchemy] -level = INFO -handlers = -qualname = sqlalchemy.engine -# "level = INFO" logs SQL queries. -# "level = DEBUG" logs SQL queries and results. -# "level = WARN" logs neither. (Recommended for production systems.) - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s - -# End logging configuration diff --git a/kotti/scaffolds/package/i18n.sh_tmpl b/kotti/scaffolds/package/i18n.sh_tmpl deleted file mode 100755 index 16815b793..000000000 --- a/kotti/scaffolds/package/i18n.sh_tmpl +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash -# Usage: -# Initial catalog creation (lang is the language identifier): -# ./i18n.sh lang -# Updating translation and compile catalog: -# ./i18n.sh - -# configuration -DOMAIN="{{package}}" -SEARCH_PATH="{{package}}" -LOCALES_PATH="{{package}}/locale" -# end configuration - -# create locales folder if not exists -if [ ! -d "$LOCALES_PATH" ]; then - echo "Locales directory not exists, create" - mkdir -p "$LOCALES_PATH" -fi - -# create pot if not exists -if [ ! -f "$LOCALES_PATH"/$DOMAIN.pot ]; then - echo "Create pot file" - touch "$LOCALES_PATH"/$DOMAIN.pot -fi - -# no arguments, extract and update -if [ $# -eq 0 ]; then - echo "Extract messages" - pot-create "$SEARCH_PATH" -o "$LOCALES_PATH"/$DOMAIN.pot - - echo "Update translations" - for po in "$LOCALES_PATH"/*/LC_MESSAGES/$DOMAIN.po; do - msgmerge -o "$po" "$po" "$LOCALES_PATH"/$DOMAIN.pot - done - - echo "Compile message catalogs" - for po in "$LOCALES_PATH"/*/LC_MESSAGES/*.po; do - msgfmt -o "${po%.*}.mo" "$po" - done - -# first argument represents language identifier, create catalog -else - cd "$LOCALES_PATH" - mkdir -p $1/LC_MESSAGES - msginit -i $DOMAIN.pot -o $1/LC_MESSAGES/$DOMAIN.po -l $1 -fi diff --git a/kotti/scaffolds/package/requirements.txt b/kotti/scaffolds/package/requirements.txt deleted file mode 120000 index 5f8116083..000000000 --- a/kotti/scaffolds/package/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -../../../requirements.txt \ No newline at end of file diff --git a/kotti/scaffolds/package/rtd.txt b/kotti/scaffolds/package/rtd.txt deleted file mode 100644 index 2e2ff10c5..000000000 --- a/kotti/scaffolds/package/rtd.txt +++ /dev/null @@ -1,6 +0,0 @@ -repoze.sphinx.autointerface -setuptools-git # needed to make "python setup.py install" on rtd. -pytest # needed for testing apidocs - -# include requirements.txt --r requirements.txt diff --git a/kotti/scaffolds/package/setup.cfg_tmpl b/kotti/scaffolds/package/setup.cfg_tmpl deleted file mode 100644 index 13b1fe203..000000000 --- a/kotti/scaffolds/package/setup.cfg_tmpl +++ /dev/null @@ -1,24 +0,0 @@ -[egg_info] -tag_build = dev -tag_svn_revision = true - -[aliases] -dev = develop easy_install kotti[testing] kotti[development] -docs = develop easy_install kotti[docs] - -[pytest] -addopts = - --doctest-modules - --doctest-glob='*.txt' - --capture=no - --cov={{package}} - --cov-report=term-missing - --cov-config .coveragerc - --ignore={{package}}/templates/ - --strict - --pep8 - {{package}}/tests -python_files = test*py -markers = - user: mark test to be run as the given user -pep8ignore = E501 E122 E123 E125 E128 E711 diff --git a/kotti/scaffolds/package/setup.py_tmpl b/kotti/scaffolds/package/setup.py_tmpl deleted file mode 100644 index bf1e71483..000000000 --- a/kotti/scaffolds/package/setup.py_tmpl +++ /dev/null @@ -1,55 +0,0 @@ -import os - -from setuptools import setup -from setuptools import find_packages - -here = os.path.abspath(os.path.dirname(__file__)) -try: - README = open(os.path.join(here, 'README.rst')).read() -except IOError: - README = '' -try: - CHANGES = open(os.path.join(here, 'CHANGES.rst')).read() -except IOError: - CHANGES = '' - -version = '0.1dev' - -install_requires = [ - 'Kotti>=0.10b1', -] - - -setup( - name='{{project}}', - version=version, - description="Add on for Kotti", - long_description='\n\n'.join([README, CHANGES]), - classifiers=[ - "Programming Language :: Python", - "Programming Language :: Python :: 2.6", - "Programming Language :: Python :: 2.7", - "Framework :: Pylons", - "Topic :: Internet :: WWW/HTTP", - "Topic :: Internet :: WWW/HTTP :: Dynamic Content", - "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", - "License :: Repoze Public License", - ], - author='Kotti developers', - author_email='kotti@googlegroups.com', - url='https://github.com/{{gh_user}}/{{package}}', - keywords='kotti web cms wcms pylons pyramid sqlalchemy bootstrap', - license="BSD-derived (http://www.repoze.org/LICENSE.txt)", - packages=find_packages(), - include_package_data=True, - zip_safe=False, - install_requires=install_requires, - tests_require=[], - dependency_links=[], - entry_points={ - 'fanstatic.libraries': [ - '{{package}} = {{package}}.fanstatic:library', - ], - }, - extras_require={}, -) diff --git a/kotti/security.py b/kotti/security.py index 27f3041d0..1c61082bf 100644 --- a/kotti/security.py +++ b/kotti/security.py @@ -1,66 +1,57 @@ -from __future__ import with_statement +from collections.abc import MutableMapping from contextlib import contextmanager from datetime import datetime -from UserDict import DictMixin +from typing import Dict +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import Set +from typing import Tuple +from typing import Union import bcrypt +from pyramid.location import lineage +from pyramid.security import PermitsResult +from pyramid.security import view_execution_permitted from sqlalchemy import Boolean from sqlalchemy import Column from sqlalchemy import DateTime from sqlalchemy import Integer from sqlalchemy import Unicode +from sqlalchemy import bindparam from sqlalchemy import func -from sqlalchemy.sql.expression import or_ from sqlalchemy.orm.exc import NoResultFound -from pyramid.location import lineage -from pyramid.security import view_execution_permitted -from zope.deprecation.deprecation import deprecated +from sqlalchemy.orm.query import Query +from sqlalchemy.sql.expression import and_ +from sqlalchemy.sql.expression import or_ -from kotti import get_settings -from kotti import DBSession from kotti import Base -from kotti.sqla import MutationList +from kotti import DBSession +from kotti import get_settings from kotti.sqla import JsonType +from kotti.sqla import MutationList +from kotti.sqla import bakery +from kotti.util import DontCache from kotti.util import _ from kotti.util import request_cache -from kotti.util import DontCache - -def get_principals(): - return get_settings()['kotti.principals_factory'][0]() +def has_permission( + permission: str, context: "Node", request: "Request" +) -> PermitsResult: + """ Default permission checker """ + return request.has_permission(permission, context=context) -@request_cache(lambda request: None) -def get_user(request): - userid = request.authenticated_userid - return get_principals().get(userid) - - -def has_permission(permission, context, request): - """ Check if the current request has a permission on the given context. - - .. deprecated:: 0.9 - :param permission: permission to check for - :type permission: str - - :param context: context that should be checked for the given permission - :type context: :class:``kotti.resources.Node`` - - :param request: current request - :type request: :class:`pyramid.request.Request` - - :result: ``True`` if request has the permission, ``False`` else - :rtype: bool - """ +def get_principals() -> "Principals": + return get_settings()["kotti.principals_factory"][0]() - return request.has_permission(permission, context) - -deprecated(u'has_permission', - u"kotti.security.has_permission is deprecated as of Kotti 1.0 and " - u"will be no longer available starting with Kotti 2.0. " - u"Please use the has_permission method of request instead.") +# @request_cache(lambda request: None) +def get_user(request: "Request") -> Optional["Principal"]: + userid = request.unauthenticated_userid + return get_principals().get(userid) class Principal(Base): @@ -82,10 +73,6 @@ class Principal(Base): receiver of the email. This attribute should be set to 'None' once confirmation has succeeded. """ - __tablename__ = 'principals' - __mapper_args__ = dict( - order_by='principals.name', - ) id = Column(Integer, primary_key=True) name = Column(Unicode(100), unique=True) @@ -98,8 +85,19 @@ class Principal(Base): creation_date = Column(DateTime(), nullable=False) last_login_date = Column(DateTime()) - def __init__(self, name, password=None, active=True, confirm_token=None, - title=u"", email=None, groups=None): + __tablename__ = "principals" + # __mapper_args__ = dict(order_by=name) + + def __init__( + self, + name: str, + password: Optional[str] = None, + active: Optional[bool] = True, + confirm_token: Optional[str] = None, + title: Optional[str] = "", + email: Optional[str] = None, + groups: Optional[List[str]] = None, + ): self.name = name if password is not None: password = get_principals().hash_password(password) @@ -115,10 +113,10 @@ def __init__(self, name, password=None, active=True, confirm_token=None, self.last_login_date = None def __repr__(self): # pragma: no cover - return '' % self.name + return f"" -class AbstractPrincipals(object): +class AbstractPrincipals: """This class serves as documentation and defines what methods are expected from a Principals database. @@ -134,11 +132,12 @@ class AbstractPrincipals(object): Use the 'kotti.principals' settings variable to override Kotti's default Principals implementation with your own. """ - def __getitem__(self, name): + + def __getitem__(self, name: str): """Return the Principal object with the id 'name'. """ - def __setitem__(self, name, principal): + def __setitem__(self, name: str, principal: Union[Principal, dict]): """Add a given Principal object to the database. 'name' is expected to the the same as 'principal.name'. @@ -146,56 +145,57 @@ def __setitem__(self, name, principal): 'principal' may also be a dict of attributes. """ - def __delitem__(self, name): + def __delitem__(self, name: str) -> None: """Remove the principal with the given name from the database. """ - def keys(self): + def keys(self) -> List[str]: """Return a list of principal ids that are in the database. """ - def search(self, **kwargs): + def search(self, **kwargs) -> List[Principal]: """Return an iterable with principal objects that correspond to the search arguments passed in. This example would return all principals with the id 'bob': - get_principals().search(name=u'bob') + get_principals().search(name='bob') Here, we ask for all principals that have 'bob' in either their 'name' or their 'title'. We pass '*bob*' instead of 'bob' to indicate that we want case-insensitive substring matching: - get_principals().search(name=u'*bob*', title=u'*bob*') + get_principals().search(name='*bob*', title='*bob*') This call should fail with AttributeError unless there's a 'foo' attribute on principal objects that supports search: - get_principals().search(name=u'bob', foo=u'bar') + get_principals().search(name='bob', foo='bar') """ - def hash_password(self, password): + def hash_password(self, password: str) -> str: """Return a hash of the given password. This is what's stored in the database as 'principal.password'. """ - def validate_password(self, clear, hashed): + def validate_password(self, clear: str, hashed: str) -> bool: """Returns True if the clear text password matches the hash. """ + ROLES = { - u'role:viewer': Principal(u'role:viewer', title=_(u'Viewer')), - u'role:editor': Principal(u'role:editor', title=_(u'Editor')), - u'role:owner': Principal(u'role:owner', title=_(u'Owner')), - u'role:admin': Principal(u'role:admin', title=_(u'Admin')), - } + "role:viewer": Principal("role:viewer", title=_("Viewer")), + "role:editor": Principal("role:editor", title=_("Editor")), + "role:owner": Principal("role:owner", title=_("Owner")), + "role:admin": Principal("role:admin", title=_("Admin")), +} _DEFAULT_ROLES = ROLES.copy() # These roles are visible in the sharing tab -SHARING_ROLES = [u'role:viewer', u'role:editor', u'role:owner'] -USER_MANAGEMENT_ROLES = SHARING_ROLES + ['role:admin'] +SHARING_ROLES = ["role:viewer", "role:editor", "role:owner"] +USER_MANAGEMENT_ROLES = SHARING_ROLES + ["role:admin"] _DEFAULT_SHARING_ROLES = SHARING_ROLES[:] _DEFAULT_USER_MANAGEMENT_ROLES = USER_MANAGEMENT_ROLES[:] @@ -203,63 +203,65 @@ def validate_password(self, clear, hashed): # that this is only really useful if you're _not_ using workflow. If # you are, then you should look at the permissions in workflow.zcml. SITE_ACL = [ - ['Allow', 'system.Everyone', ['view']], - ['Allow', 'role:viewer', ['view']], - ['Allow', 'role:editor', ['view', 'add', 'edit', 'state_change']], - ['Allow', 'role:owner', ['view', 'add', 'edit', 'manage', 'state_change']], - ] + ["Allow", "system.Everyone", ["view"]], + ["Allow", "role:viewer", ["view"]], + ["Allow", "role:editor", ["view", "add", "edit", "state_change"]], + ["Allow", "role:owner", ["view", "add", "edit", "manage", "state_change"]], +] -def set_roles(roles_dict): +def set_roles(roles_dict: Dict[str, Principal]) -> None: ROLES.clear() ROLES.update(roles_dict) -def set_sharing_roles(role_names): +def set_sharing_roles(role_names: List[str]) -> None: SHARING_ROLES[:] = role_names -def set_user_management_roles(role_names): +def set_user_management_roles(role_names: List[str]) -> None: USER_MANAGEMENT_ROLES[:] = role_names -def reset_roles(): +def reset_roles() -> None: ROLES.clear() ROLES.update(_DEFAULT_ROLES) -def reset_sharing_roles(): +def reset_sharing_roles() -> None: SHARING_ROLES[:] = _DEFAULT_SHARING_ROLES -def reset_user_management_roles(): +def reset_user_management_roles() -> None: USER_MANAGEMENT_ROLES[:] = _DEFAULT_USER_MANAGEMENT_ROLES -def reset(): +def reset() -> None: reset_roles() reset_sharing_roles() reset_user_management_roles() -class PersistentACLMixin(object): - def _get_acl(self): +class PersistentACLMixin: + def _get_acl(self) -> MutationList: if self._acl is None: - raise AttributeError('__acl__') + raise AttributeError("__acl__") return self._acl - def _set_acl(self, value): + def _set_acl(self, value) -> None: self._acl = value - def _del_acl(self): + def _del_acl(self) -> None: self._acl = None __acl__ = property(_get_acl, _set_acl, _del_acl) -def _cachekey_list_groups_raw(name, context): - context_id = context is not None and getattr(context, 'id', id(context)) - return (name, context_id) +def _cachekey_list_groups_raw( + name: str, context: "Node" +) -> Tuple[str, Union[int, "NoneType"]]: # noqa + context_id = context is not None and getattr(context, "id", id(context)) + return name, context_id @request_cache(_cachekey_list_groups_raw) @@ -269,18 +271,17 @@ def list_groups_raw(name, context): Only groups defined in context will be considered, therefore no global or inherited groups are returned. """ - from kotti.resources import LocalGroup + from kotti.resources import Node if isinstance(context, Node): - return set( - r[0] for r in DBSession.query(LocalGroup.group_name).filter( - LocalGroup.node_id == context.id).filter( - LocalGroup.principal_name == name).all()) + return { + r.group_name for r in context.local_groups if r.principal_name == name + } return set() -def list_groups(name, context=None): +def list_groups(name: str, context: Optional["Node"] = None) -> List[str]: """List groups for principal with a given ``name``. The optional ``context`` argument may be passed to check the list @@ -289,17 +290,22 @@ def list_groups(name, context=None): return list_groups_ext(name, context)[0] -def _cachekey_list_groups_ext(name, context=None, _seen=None, _inherited=None): +def _cachekey_list_groups_ext( + name: str, + context: Optional["Node"] = None, + _seen: Optional[Set[str]] = None, + _inherited: Optional[Set[str]] = None, +) -> Tuple[str, Union[int, "NoneType"]]: # noqa if _seen is not None or _inherited is not None: raise DontCache else: - context_id = getattr(context, 'id', id(context)) - return (name, context_id) + context_id = getattr(context, "id", id(context)) + return name, context_id @request_cache(_cachekey_list_groups_ext) def list_groups_ext(name, context=None, _seen=None, _inherited=None): - name = unicode(name) + name = name groups = set() recursing = _inherited is not None _inherited = _inherited or set() @@ -312,14 +318,13 @@ def list_groups_ext(name, context=None, _seen=None, _inherited=None): _inherited.update(principal.groups) if _seen is None: - _seen = set([name]) + _seen = {name} # Add local groups: if context is not None: items = lineage(context) for idx, item in enumerate(items): - group_names = [i for i in list_groups_raw(name, item) - if i not in _seen] + group_names = [i for i in list_groups_raw(name, item) if i not in _seen] groups.update(group_names) if recursing or idx != 0: _inherited.update(group_names) @@ -327,57 +332,63 @@ def list_groups_ext(name, context=None, _seen=None, _inherited=None): new_groups = groups - _seen _seen.update(new_groups) for group_name in new_groups: - g, i = list_groups_ext( - group_name, context, _seen=_seen, _inherited=_inherited) + g, i = list_groups_ext(group_name, context, _seen=_seen, _inherited=_inherited) groups.update(g) _inherited.update(i) return list(groups), list(_inherited) -def set_groups(name, context, groups_to_set=()): +def set_groups(name: str, context: "Node", groups_to_set: Iterable[str] = ()) -> None: """Set the list of groups for principal with given ``name`` and in given ``context``. """ - name = unicode(name) + from kotti.resources import LocalGroup - DBSession.query(LocalGroup).filter( - LocalGroup.node_id == context.id).filter( - LocalGroup.principal_name == name).delete() - for group_name in groups_to_set: - DBSession.add(LocalGroup(context, name, unicode(group_name))) + context.local_groups = [ + # keep groups for "other" principals + lg + for lg in context.local_groups + if lg.principal_name != name + ] + [ + # reset groups for given principal + LocalGroup(context, name, group_name) + for group_name in groups_to_set + ] -def list_groups_callback(name, request): +def list_groups_callback(name: str, request: "Request") -> Optional[List[str]]: """ List the groups for the principal identified by ``name``. Consider - ``authz_context`` to support assigment of local roles to groups. """ + ``authz_context`` to support assignment of local roles to groups. """ if not is_user(name): return None # Disallow logging in with groups if name in get_principals(): context = request.environ.get( - 'authz_context', getattr(request, 'context', None)) + "authz_context", getattr(request, "context", None) + ) if context is None: # SA events don't have request.context available from kotti.resources import get_root + context = get_root(request) return list_groups(name, context) @contextmanager -def authz_context(context, request): - before = request.environ.pop('authz_context', None) - request.environ['authz_context'] = context +def authz_context(context: object, request: "Request"): + before = request.environ.pop("authz_context", None) + request.environ["authz_context"] = context try: yield finally: - del request.environ['authz_context'] + del request.environ["authz_context"] if before is not None: - request.environ['authz_context'] = before + request.environ["authz_context"] = before @contextmanager -def request_method(request, method): +def request_method(request: "Request", method: str): before = request.method request.method = method try: @@ -386,33 +397,41 @@ def request_method(request, method): request.method = before -def view_permitted(context, request, name='', method='GET'): +def view_permitted( + context: object, + request: "Request", + name: Optional[str] = "", + method: Optional[str] = "GET", +) -> PermitsResult: with authz_context(context, request): with request_method(request, method): return view_execution_permitted(context, request, name) -def principals_with_local_roles(context, inherit=True): +def principals_with_local_roles( + context: "Node", inherit: Optional[bool] = True +) -> List[str]: """Return a list of principal names that have local roles in the context. """ - from resources import LocalGroup + principals = set() items = [context] + if inherit: items = lineage(context) + for item in items: principals.update( - r[0] for r in - DBSession.query(LocalGroup.principal_name).filter( - LocalGroup.node_id == item.id).group_by( - LocalGroup.principal_name).all() - if not r[0].startswith('role:') - ) + r.principal_name + for r in item.local_groups + if not r.principal_name.startswith("role:") + ) + return list(principals) -def map_principals_with_local_roles(context): +def map_principals_with_local_roles(context: "Node"): principals = get_principals() value = [] for principal_name in principals_with_local_roles(context): @@ -426,13 +445,13 @@ def map_principals_with_local_roles(context): return sorted(value, key=lambda t: t[0].name) -def is_user(principal): - if not isinstance(principal, basestring): +def is_user(principal: Union[Principal, str]) -> bool: + if not isinstance(principal, str): principal = principal.name - return ':' not in principal + return ":" not in principal -class Principals(DictMixin): +class Principals(MutableMapping): """Kotti's default principal database. Look at 'AbstractPrincipals' for documentation. @@ -440,70 +459,116 @@ class Principals(DictMixin): This is a default implementation that may be replaced by using the 'kotti.principals' settings variable. """ + factory = Principal + @classmethod + def _principal_by_name(cls, name: str) -> Principal: + query = bakery( + lambda session: session.query(cls.factory).filter( + cls.factory.name == bindparam("name") + ) + ) + return query(DBSession()).params(name=name).one() + @request_cache(lambda self, name: name) def __getitem__(self, name): - name = unicode(name) + if name is None or not isinstance(name, str): + raise KeyError(name) + # avoid calls to the DB for roles + # (they're not stored in the ``principals`` table) + if name.startswith("role:"): + raise KeyError(name) try: - return DBSession.query( - self.factory).filter(self.factory.name == name).one() + return self._principal_by_name(name) + # return DBSession.query( + # self.factory).filter(self.factory.name == name).one() except NoResultFound: raise KeyError(name) - def __setitem__(self, name, principal): - name = unicode(name) + def __setitem__(self, name: str, principal: Union[Principal, dict]) -> None: + name = name if isinstance(principal, dict): principal = self.factory(**principal) DBSession.add(principal) - def __delitem__(self, name): - name = unicode(name) + def __delitem__(self, name: str) -> None: + name = name try: - principal = DBSession.query( - self.factory).filter(self.factory.name == name).one() + principal = self._principal_by_name(name) DBSession.delete(principal) except NoResultFound: raise KeyError(name) - def iterkeys(self): + def __iter__(self) -> Iterator[str]: + yield from self.keys() + + def __len__(self): + return len(self.keys()) + + def iterkeys(self) -> Iterator[str]: for (principal_name,) in DBSession.query(self.factory.name): yield principal_name - def keys(self): + def keys(self) -> List[str]: return list(self.iterkeys()) - def search(self, **kwargs): + def search(self, match: Optional[str] = "any", **kwargs) -> Query: + """ Search the principal database. + + :param match: ``any`` to return all principals matching any search + param, ``all`` to return only principals matching + all params + :type match: str + + :param kwargs: Search conditions, e.g. ``name='bob', active=True``. + :type kwargs: varying. + + :result: SQLAlchemy query object + :rtype: :class:`sqlalchemy.orm.query.Query`` + """ + if not kwargs: return [] filters = [] + for key, value in kwargs.items(): col = getattr(self.factory, key) - if '*' in value: - value = value.replace('*', '%').lower() + if isinstance(value, str) and "*" in value: + value = value.replace("*", "%").lower() filters.append(func.lower(col).like(value)) else: filters.append(col == value) query = DBSession.query(self.factory) - query = query.filter(or_(*filters)) + + if match == "any": + query = query.filter(or_(*filters)) + elif match == "all": + query = query.filter(and_(*filters)) + else: + raise ValueError('match must be either "any" or "all".') + return query log_rounds = 10 - def hash_password(self, password, hashed=None): - if hashed is None: - hashed = bcrypt.gensalt(self.log_rounds) - return unicode( - bcrypt.hashpw(password.encode('utf-8'), hashed.encode('utf-8'))) + def hash_password(self, password: str, salt: Optional[str] = None) -> str: + if salt is None: + salt = bcrypt.gensalt(self.log_rounds) + if isinstance(password, str): + password = password.encode("utf-8") + if isinstance(salt, str): + salt = salt.encode("utf-8") + return bcrypt.hashpw(password, salt).decode("utf-8") - def validate_password(self, clear, hashed): + def validate_password(self, clear: str, hashed: str) -> bool: try: return self.hash_password(clear, hashed) == hashed except ValueError: return False -def principals_factory(): +def principals_factory() -> Principals: return Principals() diff --git a/kotti/sqla.py b/kotti/sqla.py index 287a37f46..e4d16f1a2 100644 --- a/kotti/sqla.py +++ b/kotti/sqla.py @@ -1,18 +1,20 @@ -# -*- coding: utf-8 -*- - """ Inheritance Diagram ------------------- .. inheritance-diagram:: kotti.sqla """ - from pyramid.compat import json from pyramid.security import ALL_PERMISSIONS from pyramid.security import Allow -from sqlalchemy.types import TypeDecorator, TEXT -from sqlalchemy.ext.mutable import Mutable +from sqlalchemy.ext import baked from sqlalchemy.ext.declarative import declared_attr +from sqlalchemy.ext.mutable import Mutable +from sqlalchemy.types import Text +from sqlalchemy.types import TypeDecorator + +bakery = baked.bakery() +baked.bake_lazy_loaders() def dump_default(obj): @@ -28,28 +30,34 @@ def no_autoflush(func): def wrapper(*args, **kwargs): with DBSession.no_autoflush: return func(*args, **kwargs) + return wrapper class JsonType(TypeDecorator): """http://www.sqlalchemy.org/docs/core/types.html#marshal-json-strings """ - impl = TEXT - def process_bind_param(self, value, dialect): + impl = Text + + # noinspection PyMethodOverriding + @staticmethod + def process_bind_param(value, dialect): if value is not None: value = json.dumps(value, default=dump_default) return value - def process_result_value(self, value, dialect): + # noinspection PyMethodOverriding + @staticmethod + def process_result_value(value, dialect): if value is not None: value = json.loads(value) return value class ACLType(JsonType): - ALL_PERMISSIONS_SERIALIZED = '__ALL_PERMISSIONS__' - DEFAULT_ACE = (Allow, 'role:admin', ALL_PERMISSIONS) + ALL_PERMISSIONS_SERIALIZED = "__ALL_PERMISSIONS__" + DEFAULT_ACE = (Allow, "role:admin", ALL_PERMISSIONS) def process_bind_param(self, value, dialect): if value is not None: @@ -57,10 +65,10 @@ def process_bind_param(self, value, dialect): for ace in value: if ace[2] == ALL_PERMISSIONS: ace[2] = self.ALL_PERMISSIONS_SERIALIZED - return super(ACLType, self).process_bind_param(value, dialect) + return super().process_bind_param(value, dialect) def process_result_value(self, value, dialect): - acl = super(ACLType, self).process_result_value(value, dialect) + acl = super().process_result_value(value, dialect) if acl is not None: for ace in acl: if ace[2] == self.ALL_PERMISSIONS_SERIALIZED: @@ -71,9 +79,12 @@ def process_result_value(self, value, dialect): class MutationDict(Mutable): """http://www.sqlalchemy.org/docs/orm/extensions/mutable.html """ + + _wraps = dict + def __init__(self, data): self._d = data - super(MutationDict, self).__init__() + super().__init__() @classmethod def coerce(cls, key, value): @@ -85,15 +96,23 @@ def coerce(cls, key, value): return value def __json__(self, request=None): - return dict([(key, value.__json__(request)) - if hasattr(value, '__json__') else (key, value) - for key, value in self._d.items()]) + return dict( + [ + (key, value.__json__(request)) + if hasattr(value, "__json__") + else (key, value) + for key, value in self._d.items() + ] + ) class MutationList(Mutable): + + _wraps = list + def __init__(self, data): self._d = data - super(MutationList, self).__init__() + super().__init__() @classmethod def coerce(cls, key, value): @@ -108,8 +127,10 @@ def __radd__(self, other): return other + self._d def __json__(self, request=None): - return [item.__json__(request) if hasattr(item, '__json__') else item - for item in self._d] + return [ + item.__json__(request) if hasattr(item, "__json__") else item + for item in self._d + ] def _make_mutable_method_wrapper(wrapper_class, methodname, mutates): @@ -119,48 +140,50 @@ def replacer(self, *args, **kwargs): if mutates: self.changed() return value + replacer.__name__ = methodname return replacer for wrapper_class in (MutationDict, MutationList): for methodname, mutates in ( - ('__iter__', False), - ('__len__', False), - ('__eq__', False), - ('__add__', False), - ('__getitem__', False), - ('__getslice__', False), - ('__repr__', False), - ('get', False), - ('keys', False), - - ('__setitem__', True), - ('__delitem__', True), - ('__setslice__', True), - ('__delslice__', True), - ('append', True), - ('clear', True), - ('extend', True), - ('insert', True), - ('pop', True), - ('setdefault', True), - ('update', True), - ('remove', True), - ): - setattr( - wrapper_class, methodname, - _make_mutable_method_wrapper( - wrapper_class, methodname, mutates), + ("__iter__", False), + ("__len__", False), + ("__eq__", False), + ("__add__", False), + ("__getitem__", False), + ("__getslice__", False), + ("__repr__", False), + ("get", False), + ("keys", False), + ("__setitem__", True), + ("__delitem__", True), + ("__setslice__", True), + ("__delslice__", True), + ("append", True), + ("clear", True), + ("extend", True), + ("insert", True), + ("pop", True), + ("setdefault", True), + ("update", True), + ("remove", True), + ): + # Only wrap methods that do exist on the wrapped type! + if getattr(wrapper_class._wraps, methodname, False): + setattr( + wrapper_class, + methodname, + _make_mutable_method_wrapper(wrapper_class, methodname, mutates), ) -class NestedMixin(object): +class NestedMixin: __parent__ = None def __init__(self, *args, **kwargs): - self.__parent__ = kwargs.pop('__parent__', None) - super(NestedMixin, self).__init__(*args, **kwargs) + self.__parent__ = kwargs.pop("__parent__", None) + super().__init__(*args, **kwargs) def __getitem__(self, key): value = self._d.__getitem__(key) @@ -170,7 +193,7 @@ def changed(self): if self.__parent__ is not None: self.__parent__.changed() else: - super(NestedMixin, self).changed() + super().changed() def try_wrap(self, value): for typ, wrapper in MUTATION_WRAPPERS.items(): @@ -189,21 +212,19 @@ def setdefault(self, key, default): default = NestedMutationList(default, __parent__=self) elif isinstance(default, dict): default = NestedMutationDict(default, __parent__=self) - return super(NestedMutationDict, self).setdefault(key, default) + return super().setdefault(key, default) class NestedMutationList(NestedMixin, MutationList): pass -MUTATION_WRAPPERS = { - dict: NestedMutationDict, - list: NestedMutationList, - } +MUTATION_WRAPPERS = {dict: NestedMutationDict, list: NestedMutationList} -class Base(object): +class Base: @declared_attr def __tablename__(cls): from kotti.util import camel_case_to_name # prevent circ import - return '{0}s'.format(camel_case_to_name(cls.__name__)) + + return "{}s".format(camel_case_to_name(cls.__name__)) diff --git a/kotti/static/favicon.ico b/kotti/static/favicon.ico index 71f837c9e..cd1cc8e97 100644 Binary files a/kotti/static/favicon.ico and b/kotti/static/favicon.ico differ diff --git a/kotti/static/icon-transparent.png b/kotti/static/icon-transparent.png new file mode 100644 index 000000000..043d34278 Binary files /dev/null and b/kotti/static/icon-transparent.png differ diff --git a/kotti/static/icon.png b/kotti/static/icon.png new file mode 100644 index 000000000..72e189728 Binary files /dev/null and b/kotti/static/icon.png differ diff --git a/kotti/static/kotti.js b/kotti/static/kotti.js deleted file mode 100644 index 8a9290a00..000000000 --- a/kotti/static/kotti.js +++ /dev/null @@ -1,63 +0,0 @@ -// JSLint options: -/*global deform, jQuery, tinyMCE*/ -/*jslint browser:true*/ - -"use strict"; -var kotti = { - dom_changed_handlers: [] -}; -var jq = jQuery; - -(function ($) { - - $.fn.find2 = function (selector) { - // A find() that also return matches on the root element(s) - return this.filter(selector).add(this.find(selector)); - }; - - kotti.dirty_forms = function (node) { - var forms = $("form").not("[class~=dirty-ignore]"), - initial = forms.serialize(); - - $(window).unbind('beforeunload'); - forms.submit(function () { $(window).unbind('beforeunload'); }); - if (tinyMCE !== undefined) { - tinyMCE.triggerSave(true); - } - $(window).bind("beforeunload", function () { - if (tinyMCE !== undefined) { - tinyMCE.triggerSave(true); - } - if ($("form").serialize() !== initial) { - return "Your changes have not been saved.\nAre you sure you want to leave this page?"; - } - return null; - }); - }; - - kotti.dom_changed = function (node) { - $.each(kotti.dom_changed_handlers, function (index, func) { - func(node); - }); - }; - - kotti.main = function (handlers) { - var node = $('html'); - if (!handlers) { - handlers = [ - ]; - } - $.each(handlers, function (index, func) { - kotti.dom_changed_handlers.push(func); - }); - kotti.dom_changed(node); - }; - - // deform might be undefined, e.g. in kotti_tinymce's kottibrowser - if (window.deform) { - deform.load(); - } - - kotti.main(); - -}(jQuery)); diff --git a/kotti/static/kotti.min.js b/kotti/static/kotti.min.js deleted file mode 100644 index 1cbfa0e1f..000000000 --- a/kotti/static/kotti.min.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";var kotti={dom_changed_handlers:[]};var jq=jQuery;(function(a){a.fn.find2=function(b){return this.filter(b).add(this.find(b))};kotti.dirty_forms=function(d){var b=a("form").not("[class~=dirty-ignore]"),c=b.serialize();a(window).unbind("beforeunload");b.submit(function(){a(window).unbind("beforeunload")});if(tinyMCE!==undefined){tinyMCE.triggerSave(true)}a(window).bind("beforeunload",function(){if(tinyMCE!==undefined){tinyMCE.triggerSave(true)}if(a("form").serialize()!==c){return"Your changes have not been saved.\nAre you sure you want to leave this page?"}return null})};kotti.dom_changed=function(b){a.each(kotti.dom_changed_handlers,function(c,d){d(b)})};kotti.main=function(b){var c=a("html");if(!b){b=[]}a.each(b,function(d,e){kotti.dom_changed_handlers.push(e)});kotti.dom_changed(c)};if(window.deform){deform.load()}kotti.main()}(jQuery)); \ No newline at end of file diff --git a/kotti/static/logo-transparent.png b/kotti/static/logo-transparent.png new file mode 100644 index 000000000..9b7999405 Binary files /dev/null and b/kotti/static/logo-transparent.png differ diff --git a/kotti/static/logo.png b/kotti/static/logo.png new file mode 100644 index 000000000..fa66c5970 Binary files /dev/null and b/kotti/static/logo.png differ diff --git a/kotti/static/upload.js b/kotti/static/upload.js index f12b01ba0..040846d5c 100644 --- a/kotti/static/upload.js +++ b/kotti/static/upload.js @@ -1,143 +1,153 @@ -/*jshint multistr:true */ +// JSLint options: +/*global $, angular, document, qq*/ +"use strict"; var app = angular.module('kotti', []); // copied from https://gist.github.com/thomseddon/3511330 -app.filter('bytes', function() { - return function(bytes, precision) { +app.filter('bytes', function () { + return function (bytes, precision) { + + if (isNaN(parseFloat(bytes)) || !isFinite(bytes)) { + return '-'; + } + if (typeof precision === 'undefined') { + precision = 1; + } + + var units = ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'], + number = Math.floor(Math.log(bytes) / Math.log(1024)); + if (!isFinite(number)) { + number = 0; + } + + return (bytes / Math.pow(1024, Math.floor(number))).toFixed(precision) + ' ' + units[number]; + }; +}); - if (isNaN(parseFloat(bytes)) || !isFinite(bytes)) return '-'; - if (typeof precision === 'undefined') precision = 1; +function UploadController($scope, $http, $log) { - var units = ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB']; - var number = Math.floor(Math.log(bytes) / Math.log(1024)); - if (!isFinite(number)) number = 0; + $log.info("Initializing UploadController..."); - return (bytes/Math.pow(1024, Math.floor(number))).toFixed(precision) + ' ' + units[number]; - }; -}); + $scope.files = []; + $scope.errors = []; + $scope.numFilesWaiting = 0; + + $scope.uploadAll = function () { + $scope.uploader.uploadStoredFiles(); + }; + $scope.dismissError = function (file) { + $scope.errors.splice($scope.errors.indexOf(file), 1); + }; -function UploadController ($scope, $http, $log) { - - $log.info("Initializing UploadController..."); - - $scope.files = []; - $scope.errors = []; - $scope.num_files_waiting = 0; - - $scope.uploadAll = function() { - $scope.uploader.uploadStoredFiles(); - }; - - $scope.dismissError = function(file) { - $scope.errors.splice($scope.errors.indexOf(file), 1); - }; - - $scope.apply = function(fn) { - var phase = this.$root.$$phase; - if(phase == '$apply' || phase == '$digest') { - if(fn && (typeof(fn) === 'function')) { - fn(); - } - } else { - this.$apply(fn); - } - }; - - $scope.uploader = new qq.FineUploaderBasic({ - debug: true, - autoUpload: false, - button: $('#btn-select-files')[0], - element: document.getElementById('uploader'), - request: { - endpoint: null - }, - callbacks: { - onValidate: function(fileOrBlobData) { - $log.info("onValidate"); - }, - onSubmit: function(id, name) { - $log.info("onSubmit"); - $scope.apply(function() { - var file = $scope.uploader.getFile(id); - $http.get( - $scope.endpoints.content_types, - {params: {mimetype: file.type}}) - .success(function(data, status, headers, config) { - - var content_types = data.content_types; - - var file = { - id: id, - name: name, - size: $scope.uploader.getSize(id), - file: $scope.uploader.getFile(id) - }; - - if (content_types.length === 0) { - // todo: display meaningful error message - file.status = 'Error'; - file.error = 'There is no content type in this context that knows create items from that file type.'; - $scope.errors.splice(id, 0, file); - return false; + $scope.apply = function (fn) { + var phase = this.$root.$$phase; + if (phase === '$apply' || phase === '$digest') { + if (fn && (typeof (fn) === 'function')) { + fn(); } + } else { + this.$apply(fn); + } + }; + + $scope.uploader = new qq.FineUploaderBasic({ + debug: true, + autoUpload: false, + button: $('#btn-select-files')[0], + element: document.getElementById('uploader'), + request: { + endpoint: null + }, + callbacks: { + //onValidate: function (fileOrBlobData) { + onValidate: function () { + $log.info("onValidate"); + }, + onSubmit: function (id, name) { + $log.info("onSubmit"); + $scope.apply(function () { + var file = $scope.uploader.getFile(id); + $http.get( + $scope.endpoints.content_types, + {params: {mimetype: file.type}} + //).success(function (data, status, headers, config) { + ).success(function (data) { + var contentTypes = data.content_types, + file = { + id: id, + name: name, + size: $scope.uploader.getSize(id), + file: $scope.uploader.getFile(id) + }; + + if (contentTypes.length === 0) { + // todo: display meaningful error message + file.status = 'Error'; + file.error = 'There is no content type in this context that knows create items from that file type.'; + $scope.errors.splice(id, 0, file); + return false; + } + + file.status = 'ready for upload'; + file.transfered = {bytes: 0, percent: 0}; + file.allowedTypes = contentTypes; + file.desiredType = contentTypes[0]; + + $scope.files.splice(id, 0, file); + $scope.numFilesWaiting += 1; + }); + }); + }, + //onUpload: function (id, name) { + onUpload: function (id) { + $log.info("onUpload"); + $scope.apply(function () { + $scope.files[id].status = 'uploading'; + $scope.uploader.setParams({ + content_type: $scope.files[id].desiredType.name + }, id); + $scope.numFilesWaiting -= 1; + }); + }, + onProgress: function (id, name, uploadedBytes, totalBytes) { + $scope.apply(function () { + $scope.files[id].transfered.bytes = uploadedBytes; + $scope.files[id].transfered.percent = Math.round(uploadedBytes / totalBytes * 100); + }); + }, + //onCancel: function (id, name) { + onCancel: function (id) { + $log.info("onCancel"); + $scope.apply(function () { + $scope.files[id].status = 'cancelled'; + }); + }, + //onError: function (id, name, errorReason) { + onError: function (id) { + $log.info("onError"); + $scope.apply(function () { + $scope.files[id].status = 'failed'; + }); + return false; + }, + onComplete: function (id, name, response) { + $log.info("onComplete"); + // debugger; + $scope.apply(function () { + if ($scope.files[id].status === 'uploading') { + $scope.files[id].status = 'complete'; + $scope.files[id].url = response.url; + } + }); + } + } + }); + + $scope.$watch('endpoints', function (endpoints) { + $scope.uploader.setEndpoint(endpoints.upload); + }); - file.status = 'ready for upload'; - file.transfered = {bytes: 0, percent: 0}; - file.allowed_types = content_types; - file.desired_type = content_types[0]; - - $scope.files.splice(id, 0, file); - $scope.num_files_waiting ++; - }); - - }); - }, - onUpload: function(id, name){ - $log.info("onUpload"); - $scope.apply(function() { - $scope.files[id].status = 'uploading'; - $scope.uploader.setParams( - {content_type: $scope.files[id].desired_type.name}, id); - $scope.num_files_waiting --; - }); - }, - onProgress: function(id, name, uploadedBytes, totalBytes){ - $scope.apply(function() { - $scope.files[id].transfered.bytes = uploadedBytes; - $scope.files[id].transfered.percent = Math.round(uploadedBytes / totalBytes * 100); - }); - }, - onCancel: function(id, name) { - $log.info("onCancel"); - $scope.apply(function() { - $scope.files[id].status = 'cancelled'; - }); - }, - onError: function(id, name, errorReason) { - $log.info("onError"); - $scope.apply(function() { - $scope.files[id].status = 'failed'; - }); - return false; - }, - onComplete: function(id, name, response){ - $log.info("onComplete"); - // debugger; - $scope.apply(function() { - if ($scope.files[id].status == 'uploading') { - $scope.files[id].status = 'complete'; - $scope.files[id].url = response.url; - } - }); - } - } - }); - - $scope.$watch('endpoints', function(endpoints) { - $scope.uploader.setEndpoint(endpoints.upload); - }); - - $log.info("UploadController initialized."); + $log.info("UploadController initialized."); } diff --git a/kotti/templates/actions-dropdown.pt b/kotti/templates/actions-dropdown.pt index 6de774026..3ebe16df7 100644 --- a/kotti/templates/actions-dropdown.pt +++ b/kotti/templates/actions-dropdown.pt @@ -9,6 +9,6 @@ ${link.title} -
  • +
  • diff --git a/kotti/templates/default-view-selector.pt b/kotti/templates/default-view-selector.pt index 18bc429bf..cf9736359 100644 --- a/kotti/templates/default-view-selector.pt +++ b/kotti/templates/default-view-selector.pt @@ -9,7 +9,7 @@ + tal:attributes="href request.resource_url(context, 'set-default-view', query={'view_name': v.name})"> ${v.title} diff --git a/kotti/templates/deform/tag_it.pt b/kotti/templates/deform/tag_it.pt index 636fd7dfb..cd9d32cd8 100644 --- a/kotti/templates/deform/tag_it.pt +++ b/kotti/templates/deform/tag_it.pt @@ -1,5 +1,5 @@ - + -