From 911277193cc36a9d6a6f793379320e57fc3c8d57 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Wed, 10 Dec 2014 13:27:35 +0100 Subject: [PATCH 001/600] Resolve #320 and add related migration step. --- .../559ce6eb0949_update_node_path_column.py | 30 ++++++++++ kotti/events.py | 19 +++--- kotti/tests/test_node.py | 58 +++++++++---------- 3 files changed, 71 insertions(+), 36 deletions(-) create mode 100644 kotti/alembic/versions/559ce6eb0949_update_node_path_column.py 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..022b55a05 --- /dev/null +++ b/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py @@ -0,0 +1,30 @@ +"""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 import DBSession + from kotti.resources import Node + + for node in DBSession.query(Node).with_polymorphic([Node]): + # append '/' to all nodes but root + node.path != u'/' and node.path += u'/' + + +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 + node.path == u'/' or node.path[:-1] diff --git a/kotti/events.py b/kotti/events.py index dc06f69a9..f884933a7 100644 --- a/kotti/events.py +++ b/kotti/events.py @@ -305,7 +305,7 @@ def reset_content_owner(event): 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 + '/')): + Node.path.startswith(old_parent_path)): child.path = new_parent_path + child.path[len(old_parent_path):] @@ -324,7 +324,7 @@ 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 != u'': # Our parent hasn't been set yet. Skip, unless we're the root # object (which always has an empty string as name). return @@ -332,9 +332,12 @@ def _set_path_for_new_name(target, value, oldvalue, initiator): 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) + if target.__parent__ is None and value == u'': + # We're a new root object + target_path = u'/' + else: + target_path += u'/{0}/'.format(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 @@ -350,7 +353,8 @@ def _set_path_for_new_name(target, value, oldvalue, initiator): _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 = u'{0}{1}/'.format(child.__parent__.path, + child.__name__) def _all_children(item, _all=None): @@ -388,7 +392,7 @@ def _set_path_for_new_parent(target, value, oldvalue, initiator): return target_path = u'/'.join(node.__name__ for node in line) - target_path += u'/{0}'.format(target.__name__) + target_path += u'/{0}/'.format(target.__name__) target.path = target_path if old_path and target.id is not None: @@ -398,7 +402,8 @@ 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 = u'{0}{1}/'.format(child.__parent__.path, + child.__name__) class subscribe(object): diff --git a/kotti/tests/test_node.py b/kotti/tests/test_node.py index 1c51d1f38..94f972202 100644 --- a/kotti/tests/test_node.py +++ b/kotti/tests/test_node.py @@ -250,16 +250,16 @@ def test_attribute(self, db_session, root, events): assert root.path == "/" child = root['child-1'] = Node() - assert child.path == u'/child-1' + assert child.path == u'/child-1/' subchild = root['child-1']['subchild'] = Node() - assert subchild.path == '/child-1/subchild' + assert subchild.path == '/child-1/subchild/' def test_object_moved(self, db_session, root, events): from kotti.resources import Node child = root['child-1'] = Node() subchild = child['subchild'] = Node() subchild.parent = root - assert subchild.path == '/subchild' + assert subchild.path == '/subchild/' @mark.parametrize("flush", [True, False]) def test_parent_moved(self, db_session, root, events, flush): @@ -271,9 +271,9 @@ def test_parent_moved(self, db_session, root, events, flush): if flush: db_session.flush() - assert subchild.path == '/child-1/child-2/subchild' + assert subchild.path == '/child-1/child-2/subchild/' child2.parent = root - assert subchild.path == '/child-2/subchild' + assert subchild.path == '/child-2/subchild/' def test_object_renamed(self, db_session, root, events): from kotti.resources import Node @@ -281,7 +281,7 @@ def test_object_renamed(self, db_session, root, events): subchild = child['subchild'] = Node() subchild.name = u'renamed' - assert subchild.path == '/child-1/renamed' + assert subchild.path == '/child-1/renamed/' @mark.parametrize("flush", [True, False]) def test_parent_renamed(self, db_session, root, events, flush): @@ -294,12 +294,12 @@ def test_parent_renamed(self, db_session, root, events, flush): db_session.flush() child2.name = u'renamed' - assert subchild.path == '/child-1/renamed/subchild' - + assert subchild.path == '/child-1/renamed/subchild/' child1.name = u'renamed-1' - assert child1.path == '/renamed-1' - assert child2.path == '/renamed-1/renamed' - assert subchild.path == '/renamed-1/renamed/subchild' + assert child2.path == '/renamed-1/renamed/' + assert subchild.path == '/renamed-1/renamed/subchild/' + # THIS FAILS, child1.path being '/renamed-1/1/' and I have no clue why + assert child1.path == '/renamed-1/' @mark.parametrize("flush", [True, False]) def test_parent_copied(self, db_session, root, events, flush): @@ -313,25 +313,25 @@ def test_parent_copied(self, db_session, root, events, flush): c1copy = root['c1copy'] = c1.copy() - assert c1copy.path == '/c1copy' - assert c1copy['c2'].path == '/c1copy/c2' - assert c1copy['c2']['c3'].path == '/c1copy/c2/c3' + assert c1copy.path == '/c1copy/' + assert c1copy['c2'].path == '/c1copy/c2/' + assert c1copy['c2']['c3'].path == '/c1copy/c2/c3/' c2copy = c2['c2copy'] = c2.copy() - assert c2copy.path == '/c1/c2/c2copy' - assert c2copy['c3'].path == '/c1/c2/c2copy/c3' + assert c2copy.path == '/c1/c2/c2copy/' + assert c2copy['c3'].path == '/c1/c2/c2copy/c3/' def test_children_append(self, db_session, root, events): from kotti.resources import Node child = Node(u'child-1') root.children.append(child) - assert child.path == '/child-1' + assert child.path == '/child-1/' child2 = Node(u'child-2') child.children.append(child2) - assert child2.path == '/child-1/child-2' + assert child2.path == '/child-1/child-2/' def test_replace_root(self, db_session, root, events): from kotti.resources import Node @@ -350,17 +350,17 @@ def test_query_api(self, db_session, root, events): Node.path.startswith(u'/')).count() == 4 assert db_session.query(Node).filter( - Node.path.startswith(u'/child-1')).count() == 3 + Node.path.startswith(u'/child-1/')).count() == 3 objs = db_session.query(Node).filter( - Node.path.startswith(u'/child-1/child-2')).all() + Node.path.startswith(u'/child-1/child-2/')).all() assert len(objs) == 2 assert subchild in objs assert child2 in objs db_session.query(Node).filter( - Node.path.startswith(u'/child-1/child-3')).count() == 0 + Node.path.startswith(u'/child-1/child-3/')).count() == 0 def test_add_child_to_unnamed_parent(self, db_session, root, events): from kotti.resources import Node @@ -369,9 +369,9 @@ def test_add_child_to_unnamed_parent(self, db_session, root, events): child2 = child1['child-2'] = Node() assert child2.__parent__.__parent__ is parent root['parent'] = parent - assert parent.path == u'/parent' - assert child1.path == u'/parent/child-1' - assert child2.path == u'/parent/child-1/child-2' + assert parent.path == u'/parent/' + assert child1.path == u'/parent/child-1/' + assert child2.path == u'/parent/child-1/child-2/' def test_add_child_to_unrooted_parent(self, db_session, root, events): from kotti.resources import Node @@ -379,9 +379,9 @@ def test_add_child_to_unrooted_parent(self, db_session, root, events): child1 = parent['child-1'] = Node() child2 = child1['child-2'] = Node() root['parent'] = parent - assert parent.path == u'/parent' - assert child1.path == u'/parent/child-1' - assert child2.path == u'/parent/child-1/child-2' + assert parent.path == u'/parent/' + assert child1.path == u'/parent/child-1/' + assert child2.path == u'/parent/child-1/child-2/' def test_node_lineage_not_loaded_new_name(self, db_session, root, events): @@ -396,7 +396,7 @@ def test_node_lineage_not_loaded_new_name(self, db_session, root, events): child2 = db_session.query(Node).get(child2_id) child3 = Node('child-3', parent=child2) - assert child3.path == u'/parent/child-1/child-2/child-3' + assert child3.path == u'/parent/child-1/child-2/child-3/' def test_node_lineage_not_loaded_new_parent(self, db_session, root, events): @@ -425,7 +425,7 @@ def test_node_lineage_not_loaded_new_parent(self, db_session, root, events): child3.parent = child2 child2.parent = child1 - assert child3.path == u"/parent/child-1/child-2/child-3" + assert child3.path == u"/parent/child-1/child-2/child-3/" class TestLocalGroup: From caee4efeb2cee9ce850cf78ff1dba4392190e187 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Wed, 10 Dec 2014 14:01:33 +0100 Subject: [PATCH 002/600] Fix migration bug. --- .../versions/559ce6eb0949_update_node_path_column.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py b/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py index 022b55a05..e39b7134c 100644 --- a/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py +++ b/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py @@ -18,7 +18,8 @@ def upgrade(): for node in DBSession.query(Node).with_polymorphic([Node]): # append '/' to all nodes but root - node.path != u'/' and node.path += u'/' + if node.path != u'/': + node.path += u'/' def downgrade(): @@ -27,4 +28,5 @@ def downgrade(): for node in DBSession.query(Node).with_polymorphic([Node]): # remove trailing '/' from all nodes but root - node.path == u'/' or node.path[:-1] + if node.path != u'/': + node.path = node.path[:-1] From 0071d4092c02bdfc5d93a8f0547dfc36e553b50a Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Thu, 18 Dec 2014 12:26:16 -0800 Subject: [PATCH 003/600] Fix #292; A better Link.selected(): it can select a link with the @@ prefix; doesn't get confused by Links a shorter version of their own name --- kotti/tests/test_util.py | 47 ++++++++++++++++++++++++++++++++++++++++ kotti/util.py | 22 ++++++++++++++----- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/kotti/tests/test_util.py b/kotti/tests/test_util.py index 6524f2aa8..e3a5f0a9b 100644 --- a/kotti/tests/test_util.py +++ b/kotti/tests/test_util.py @@ -100,3 +100,50 @@ def test_getattr(self): item = TemplateStructure(u'123') assert item.split('2') == [u'1', u'3'] + + +class TestLink: + + def test_link_selected(self): + from kotti.util import Link + from kotti.testing import DummyRequest + + req = DummyRequest() + req.url = "http://example.com/@@manage" + + assert Link('manage').selected(Mock(__name__=None), req) + + req.url = "http://example.com/@@manage_cats" + assert not Link('manage').selected(Mock(__name__=None), req) + + def test_link_selected_no_view_markers(self): + from kotti.util import Link + from kotti.testing import DummyRequest + from mock import Mock + + req = DummyRequest() + root = Mock(__name__=None) + manage = Mock(__name__='manage', + __parent__=Mock(__name__=None)) + + req.url = "http://example.com/manage" + assert Link('manage').selected(root, req) + + req.url = "http://example.com/manage/" + assert not Link('manage').selected(root, req) + + req.url = "http://example.com/" + assert Link('').selected(root, req) + + req.url = "http://example.com/manage/" + link = Link('') + assert link.selected(manage, req) + + req.url = "http://example.com/manage" + assert not link.selected(manage, req) + + req.url = "http://example.com/" + assert link.selected(root, req) + + req.url = "http://example.com" + assert link.selected(root, req) diff --git a/kotti/util.py b/kotti/util.py index 375078fac..e4084c614 100644 --- a/kotti/util.py +++ b/kotti/util.py @@ -9,6 +9,7 @@ import re import urllib +from urlparse import urlparse, urlunparse from docopt import docopt from pyramid.i18n import get_localizer @@ -97,8 +98,21 @@ def __call__(self, context, request): ) def selected(self, context, request): - return urllib.unquote(request.url).startswith( - self.url(context, request)) + """ Returns True if the Link's url, based on its name, + matches the request url + + If the link name is '', it will be selected for all urls ending in '/' + """ + parsed = urlparse(urllib.unquote(request.url)) + + # insert view markers @@ in last component of the path + path = parsed.path.split('/') + if not '@@' in path[-1]: + path[-1] = '@@' + path[-1] + path = '/'.join(path) + url = urlunparse((parsed[0], parsed[1], path, '', '', '')) + + return url == self.url(context, request) def permitted(self, context, request): from kotti.security import view_permitted @@ -169,10 +183,6 @@ def __init__(self, name, title=None, predicate=None): def url(self, context, request): return resource_url(context, request) + '@@' + self.name - def selected(self, context, request): - return urllib.unquote(request.url).startswith( - self.url(context, request)) - def __eq__(self, other): return isinstance(other, Link) and repr(self) == repr(other) From e74ee14d25478b1505590ae92667362b22dbe2f2 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Mon, 15 Dec 2014 09:35:13 +0100 Subject: [PATCH 004/600] Only give out a warning instead of failing completely if scaffold's save_settings fails. --- kotti/scaffolds/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kotti/scaffolds/__init__.py b/kotti/scaffolds/__init__.py index 9242676e1..819b379cb 100644 --- a/kotti/scaffolds/__init__.py +++ b/kotti/scaffolds/__init__.py @@ -42,7 +42,10 @@ def _get(self, key, caption): # pragma: no cover s = self._settings s[key] = raw_input(u'{} [{}]: '.format(caption, s[key])) or s[key] - s.save_settings() + try: + s.save_settings() + except OSError: + self.out("Your answers were not saved for later use.") return s[key] From 0dcdfd2f42a045753d9cea4531a579b95d7507b8 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Thu, 18 Dec 2014 12:26:16 -0800 Subject: [PATCH 005/600] Fix #292; A better Link.selected(): it can select a link with the @@ prefix; doesn't get confused by Links a shorter version of their own name --- kotti/tests/test_util.py | 47 ++++++++++++++++++++++++++++++++++++++++ kotti/util.py | 22 ++++++++++++++----- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/kotti/tests/test_util.py b/kotti/tests/test_util.py index 6524f2aa8..e3a5f0a9b 100644 --- a/kotti/tests/test_util.py +++ b/kotti/tests/test_util.py @@ -100,3 +100,50 @@ def test_getattr(self): item = TemplateStructure(u'123') assert item.split('2') == [u'1', u'3'] + + +class TestLink: + + def test_link_selected(self): + from kotti.util import Link + from kotti.testing import DummyRequest + + req = DummyRequest() + req.url = "http://example.com/@@manage" + + assert Link('manage').selected(Mock(__name__=None), req) + + req.url = "http://example.com/@@manage_cats" + assert not Link('manage').selected(Mock(__name__=None), req) + + def test_link_selected_no_view_markers(self): + from kotti.util import Link + from kotti.testing import DummyRequest + from mock import Mock + + req = DummyRequest() + root = Mock(__name__=None) + manage = Mock(__name__='manage', + __parent__=Mock(__name__=None)) + + req.url = "http://example.com/manage" + assert Link('manage').selected(root, req) + + req.url = "http://example.com/manage/" + assert not Link('manage').selected(root, req) + + req.url = "http://example.com/" + assert Link('').selected(root, req) + + req.url = "http://example.com/manage/" + link = Link('') + assert link.selected(manage, req) + + req.url = "http://example.com/manage" + assert not link.selected(manage, req) + + req.url = "http://example.com/" + assert link.selected(root, req) + + req.url = "http://example.com" + assert link.selected(root, req) diff --git a/kotti/util.py b/kotti/util.py index 375078fac..e4084c614 100644 --- a/kotti/util.py +++ b/kotti/util.py @@ -9,6 +9,7 @@ import re import urllib +from urlparse import urlparse, urlunparse from docopt import docopt from pyramid.i18n import get_localizer @@ -97,8 +98,21 @@ def __call__(self, context, request): ) def selected(self, context, request): - return urllib.unquote(request.url).startswith( - self.url(context, request)) + """ Returns True if the Link's url, based on its name, + matches the request url + + If the link name is '', it will be selected for all urls ending in '/' + """ + parsed = urlparse(urllib.unquote(request.url)) + + # insert view markers @@ in last component of the path + path = parsed.path.split('/') + if not '@@' in path[-1]: + path[-1] = '@@' + path[-1] + path = '/'.join(path) + url = urlunparse((parsed[0], parsed[1], path, '', '', '')) + + return url == self.url(context, request) def permitted(self, context, request): from kotti.security import view_permitted @@ -169,10 +183,6 @@ def __init__(self, name, title=None, predicate=None): def url(self, context, request): return resource_url(context, request) + '@@' + self.name - def selected(self, context, request): - return urllib.unquote(request.url).startswith( - self.url(context, request)) - def __eq__(self, other): return isinstance(other, Link) and repr(self) == repr(other) From f58017f1fef44f93d0f15b5a00c62c3b966ea54e Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Fri, 19 Dec 2014 13:56:36 +0100 Subject: [PATCH 006/600] Add special handling in ``_update_children_paths`` when the "child" is the node being renamed, in which case the path has already been updated. --- CHANGES.txt | 6 ++++++ kotti/events.py | 4 ++++ kotti/tests/test_node.py | 1 - 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7cd85d875..a700bbea0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -52,6 +52,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/kotti/events.py b/kotti/events.py index f884933a7..877c3b0f3 100644 --- a/kotti/events.py +++ b/kotti/events.py @@ -306,6 +306,10 @@ 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)): + 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):] diff --git a/kotti/tests/test_node.py b/kotti/tests/test_node.py index 94f972202..1c948d297 100644 --- a/kotti/tests/test_node.py +++ b/kotti/tests/test_node.py @@ -298,7 +298,6 @@ def test_parent_renamed(self, db_session, root, events, flush): child1.name = u'renamed-1' assert child2.path == '/renamed-1/renamed/' assert subchild.path == '/renamed-1/renamed/subchild/' - # THIS FAILS, child1.path being '/renamed-1/1/' and I have no clue why assert child1.path == '/renamed-1/' @mark.parametrize("flush", [True, False]) From 6acdfd1b8eb1b1107f672ee382518c0817483f51 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Fri, 19 Dec 2014 14:28:29 +0100 Subject: [PATCH 007/600] Fix #364. [ci skip] --- kotti/locale/Kotti.pot | 112 +++++++++++----------- kotti/locale/de/LC_MESSAGES/Kotti.mo | Bin 18610 -> 15091 bytes kotti/locale/de/LC_MESSAGES/Kotti.po | 114 +++++++++++------------ kotti/locale/en/LC_MESSAGES/Kotti.mo | Bin 449 -> 449 bytes kotti/locale/en/LC_MESSAGES/Kotti.po | 112 +++++++++++----------- kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo | Bin 11596 -> 11596 bytes kotti/locale/fr_FR/LC_MESSAGES/Kotti.po | 112 +++++++++++----------- kotti/locale/it/LC_MESSAGES/Kotti.mo | Bin 16093 -> 12656 bytes kotti/locale/it/LC_MESSAGES/Kotti.po | 118 ++++++++++++------------ kotti/locale/ja/LC_MESSAGES/Kotti.mo | Bin 12364 -> 12364 bytes kotti/locale/ja/LC_MESSAGES/Kotti.po | 112 +++++++++++----------- kotti/locale/ko/LC_MESSAGES/Kotti.mo | Bin 18321 -> 14928 bytes kotti/locale/ko/LC_MESSAGES/Kotti.po | 114 +++++++++++------------ kotti/locale/nl/LC_MESSAGES/Kotti.mo | Bin 11253 -> 11253 bytes kotti/locale/nl/LC_MESSAGES/Kotti.po | 112 +++++++++++----------- kotti/locale/pt/LC_MESSAGES/Kotti.mo | Bin 7711 -> 7711 bytes kotti/locale/pt/LC_MESSAGES/Kotti.po | 112 +++++++++++----------- kotti/populate.py | 2 +- 18 files changed, 510 insertions(+), 510 deletions(-) diff --git a/kotti/locale/Kotti.pot b/kotti/locale/Kotti.pot index 877894b48..e9361f4dc 100644 --- a/kotti/locale/Kotti.pot +++ b/kotti/locale/Kotti.pot @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE 1.0\n" -"POT-Creation-Date: 2014-11-21 22:44+0100\n" +"POT-Creation-Date: 2014-12-19 14:27+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" @@ -47,7 +47,7 @@ msgid "" "

\n" "

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

\n" @@ -121,78 +121,78 @@ msgid "" "

\n" msgstr "" -#: ./kotti/resources.py:610 ./kotti/views/edit/content.py:81 +#: ./kotti/resources.py:606 ./kotti/views/edit/content.py:81 msgid "Document" msgstr "" -#: ./kotti/resources.py:649 ./kotti/views/edit/content.py:113 +#: ./kotti/resources.py:645 ./kotti/views/edit/content.py:113 #: ./kotti/views/edit/content.py:62 msgid "File" msgstr "" -#: ./kotti/resources.py:726 ./kotti/views/edit/content.py:144 +#: ./kotti/resources.py:697 ./kotti/views/edit/content.py:144 msgid "Image" msgstr "" -#: ./kotti/resources.py:500 +#: ./kotti/resources.py:496 msgid "Folder view" msgstr "" -#: ./kotti/resources.py:487 ./kotti/templates/view/folder.pt:17 +#: ./kotti/resources.py:483 ./kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "" -#: ./kotti/resources.py:488 ./kotti/templates/edit/nav-tree.pt:10 +#: ./kotti/resources.py:484 ./kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "" -#: ./kotti/resources.py:489 +#: ./kotti/resources.py:485 msgid "Share" msgstr "" -#: ./kotti/resources.py:490 ./kotti/templates/actions-dropdown.pt:5 +#: ./kotti/resources.py:486 ./kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "" -#: ./kotti/resources.py:491 ./kotti/views/edit/actions.py:445 +#: ./kotti/resources.py:487 ./kotti/views/edit/actions.py:446 msgid "Copy" msgstr "" -#: ./kotti/resources.py:492 ./kotti/views/edit/actions.py:446 +#: ./kotti/resources.py:488 ./kotti/views/edit/actions.py:447 msgid "Cut" msgstr "" -#: ./kotti/resources.py:493 ./kotti/views/edit/actions.py:442 +#: ./kotti/resources.py:489 ./kotti/views/edit/actions.py:443 msgid "Paste" msgstr "" -#: ./kotti/resources.py:494 ./kotti/templates/edit/rename-nodes.pt:7 +#: ./kotti/resources.py:490 ./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 +#: ./kotti/templates/edit/rename.pt:42 ./kotti/views/edit/actions.py:448 msgid "Rename" msgstr "" -#: ./kotti/resources.py:495 ./kotti/templates/edit/delete-nodes.pt:8 +#: ./kotti/resources.py:491 ./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 +#: ./kotti/views/edit/actions.py:450 msgid "Delete" msgstr "" -#: ./kotti/security.py:165 +#: ./kotti/security.py:189 msgid "Viewer" msgstr "" -#: ./kotti/security.py:166 +#: ./kotti/security.py:190 msgid "Editor" msgstr "" -#: ./kotti/security.py:167 +#: ./kotti/security.py:191 msgid "Owner" msgstr "" -#: ./kotti/security.py:168 +#: ./kotti/security.py:192 msgid "Admin" msgstr "" @@ -265,7 +265,7 @@ msgstr "" msgid "Username or email" msgstr "" -#: ./kotti/templates/login.pt:34 ./kotti/views/login.py:213 +#: ./kotti/templates/login.pt:34 ./kotti/views/login.py:212 #: ./kotti/views/users.py:212 msgid "Password" msgstr "" @@ -587,7 +587,7 @@ 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/edit/actions.py:364 ./kotti/views/edit/actions.py:408 msgid "Your changes have been saved." msgstr "" @@ -614,75 +614,75 @@ msgstr "" msgid "Add ${type}." msgstr "" -#: ./kotti/views/login.py:238 +#: ./kotti/views/login.py:237 msgid "You have reset your password." msgstr "" -#: ./kotti/views/login.py:200 +#: ./kotti/views/login.py:199 msgid "You have been logged out." msgstr "" -#: ./kotti/views/login.py:66 ./kotti/views/users.py:270 +#: ./kotti/views/login.py:65 ./kotti/views/users.py:270 msgid "Full name" msgstr "" -#: ./kotti/views/login.py:69 +#: ./kotti/views/login.py:68 msgid "Username" msgstr "" -#: ./kotti/views/login.py:74 ./kotti/views/login.py:225 +#: ./kotti/views/login.py:73 ./kotti/views/login.py:224 #: ./kotti/views/users.py:199 msgid "Email" msgstr "" -#: ./kotti/views/login.py:109 +#: ./kotti/views/login.py:108 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 +#: ./kotti/views/login.py:123 #, python-format msgid "Register - ${title}" msgstr "" -#: ./kotti/views/login.py:164 +#: ./kotti/views/login.py:163 msgid "Login failed." msgstr "" -#: ./kotti/views/login.py:286 +#: ./kotti/views/login.py:285 #, python-format msgid "Reset your password - ${title}." msgstr "" -#: ./kotti/views/login.py:160 +#: ./kotti/views/login.py:159 #, python-format msgid "Welcome, ${user}!" msgstr "" -#: ./kotti/views/login.py:173 +#: ./kotti/views/login.py:172 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 +#: ./kotti/views/login.py:177 msgid "That username or email is not known by this system." msgstr "" -#: ./kotti/views/login.py:83 +#: ./kotti/views/login.py:82 msgid "Register" msgstr "" -#: ./kotti/views/login.py:90 ./kotti/views/login.py:257 +#: ./kotti/views/login.py:89 ./kotti/views/login.py:256 msgid "There was an error." msgstr "" -#: ./kotti/views/login.py:250 +#: ./kotti/views/login.py:249 msgid "Submit" msgstr "" -#: ./kotti/views/login.py:279 +#: ./kotti/views/login.py:278 msgid "Your password reset token may have expired." msgstr "" @@ -748,8 +748,8 @@ 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 +#: ./kotti/views/edit/actions.py:302 ./kotti/views/edit/actions.py:368 +#: ./kotti/views/edit/actions.py:414 ./kotti/views/edit/actions.py:410 msgid "No changes were made." msgstr "" @@ -794,68 +794,68 @@ msgstr "" msgid "${title} was cut." msgstr "" -#: ./kotti/views/edit/actions.py:182 +#: ./kotti/views/edit/actions.py:183 #, python-format msgid "${title} was moved." msgstr "" -#: ./kotti/views/edit/actions.py:270 ./kotti/views/edit/actions.py:295 +#: ./kotti/views/edit/actions.py:271 ./kotti/views/edit/actions.py:296 #, python-format msgid "${title} was deleted." msgstr "" -#: ./kotti/views/edit/actions.py:158 +#: ./kotti/views/edit/actions.py:159 #, python-format msgid "${title} was pasted." msgstr "" -#: ./kotti/views/edit/actions.py:161 +#: ./kotti/views/edit/actions.py:162 msgid "Could not paste node. It no longer exists." msgstr "" -#: ./kotti/views/edit/actions.py:224 +#: ./kotti/views/edit/actions.py:225 #, python-format msgid "${title} is now visible in the navigation." msgstr "" -#: ./kotti/views/edit/actions.py:227 +#: ./kotti/views/edit/actions.py:228 #, python-format msgid "${title} is no longer visible in the navigation." msgstr "" -#: ./kotti/views/edit/actions.py:292 +#: ./kotti/views/edit/actions.py:293 msgid "Nothing was deleted." msgstr "" -#: ./kotti/views/edit/actions.py:328 ./kotti/views/edit/actions.py:355 +#: ./kotti/views/edit/actions.py:329 ./kotti/views/edit/actions.py:356 msgid "Name and title are required." msgstr "" -#: ./kotti/views/edit/actions.py:332 +#: ./kotti/views/edit/actions.py:333 msgid "Item was renamed." msgstr "" -#: ./kotti/views/edit/actions.py:454 +#: ./kotti/views/edit/actions.py:455 msgid "Move up" msgstr "" -#: ./kotti/views/edit/actions.py:455 +#: ./kotti/views/edit/actions.py:456 msgid "Move down" msgstr "" -#: ./kotti/views/edit/actions.py:456 +#: ./kotti/views/edit/actions.py:457 msgid "Show" msgstr "" -#: ./kotti/views/edit/actions.py:457 +#: ./kotti/views/edit/actions.py:458 msgid "Hide" msgstr "" -#: ./kotti/views/edit/actions.py:496 +#: ./kotti/views/edit/actions.py:497 msgid "You have to select items to perform an action." msgstr "" -#: ./kotti/views/edit/actions.py:453 +#: ./kotti/views/edit/actions.py:454 msgid "Change State" msgstr "" diff --git a/kotti/locale/de/LC_MESSAGES/Kotti.mo b/kotti/locale/de/LC_MESSAGES/Kotti.mo index f0e4abdc0f3bbc1f406cc95791e7a3a3e36bb105..2ab463a51f33ac5ce5fee7973dccf11e5954a620 100644 GIT binary patch delta 3875 zcmZA3dvwor9LMp`a+|TS&BjRk2{UYKn`OqBX|dcwWMZx%44b*+`juR!^bT}Nf_xOCix6kME{(QdQ?eHqU`!k#? zA=TF!%0c2uqPT`J_f)UL59MNzF+tUgxrD7~-wHM+9_xn~(+1Nq6mzfvE<`RdYcUK< z?fK8_`STdX@tfGv7{>&1H+`CRSPN6ICZ;3(noMkjy|FHi#o9Ox{c)acFTp_CD^b^N zvgdcBo?nJN@F?m!KStY{{!Kk92^>hlB%Fj?W40iJG)J%@oU!(Lq%&E##6QFl29ql#F?0l zn!&fI4!^hU8>qFtZIAzEeT;fet){-$%QQh{A{~{fOyo^AS*Rs<##4!+vJ5rD{iqwt zP!}FUW#9~I#5Yhg{0kYosln(wVl(vMFw_#wM-AMIdd?cu^Gh%Qce-uIlu^-zpP)Ma z+#cIDz+X%M-MnfB_2(*o6-bSCX#KtD{8ZKM-6ZQs)Jm6d=V8K_haMeT(-sHNGDwedsLK*~`A zJcqjP8fIZ74#R$=*NgkwlK*rnsWI+@7N9@vCD;wUs5L&1b@4iC^V~zF{BP7!#KpP; z8;H6;AGH_eq3+*`y8kdnVma!%s}7YNRBmAauH>n@aU<$OQI0x(0X5Uhs9pafYQ}f) zEIvTJj>n&IQ(S?*4-IPVA6bK0=OEfGQ3G(2sOZ6+u@+{aHc4+($D^z{=%qait7FXs zcWLUNI%^kBt(7fmRP{Pqo7^z5l(aD0QPyYdPOuP-6WQcX9k1)J*0l zxh_R5!AcCo4X6%FQEOd>>gYJ?ExU*sXb3w!6q{l={hI_TyxJxcqj4hYL4~Nb-i*r7 zcGT;1#QGI#KzHzIypMx1yuG`Jrl2xdh+5*^sOKC)-FFa(b``@U3k;_3+loL zsOuh~W)_mnw*VtinHz^1=pxiVECt9X#jLRBkD!+HYgC4>qn_*Ef&6Q2f;+e;T3|!k zJy9Kv#zr_5bwdFvg{x5Ki|z4Ur~w|fp2pU+D=-Rcl7G!S7Co4X;h3F5{^O`j=Rg-M zK`&lMy@q-GY3qdR(1RDy4>gO4gvvG|jL?UL1o_Hbeuofy3GEFHM*S=${!@JG&k=p+ z>)SJITwhtw?~6npq4Y%)vxropFEN#vKs-UPx6DdH+0iFjZ-|znsw}aU`KZm8L1==` zaDJ-FLR;Z~2vbO~;mr$#mSi5$nHWoGuhbw^_7U2ADoXV_Vul)&oy5z8GFw%&Z`u&r z;6Zx-2U95`RHnH2J{+pPMNG5D_<)(+L@#2XJtk(`y7ot!t@p+VThFwPw@$`)Y+e1E zTpHQL24WJCPrOT{5F-c`Ez>xnpBj{{L|0WPt6hA5vS-@*8tVeo2H$1d2XF_GMXVr3 z>%l7F#42JBp$)g6P}xlAoqvhgLa2CMe7^ihK8qoMb@r#<)qZX)IoqlkDShIo@ud4otM-dBV2 zHc^vEC!Qw;D6!8`8AOaE77-hX973g(c*R$9Kec+tt4b3p%ZNV2t3)H>|0R|K{fS~? xyFIy92BhY83GjGQCzQ=KBqViAOfGv6J;1LpKkSdPv59@c{{e>$Y$^Z% delta 7408 zcmb`J36K=k8OPgj>3X1|f(ZCnIYilA4h4h-1eQg@A)qY=BI$rb4}6l)iW2Fw2UpRWye3;t|N+*2}OL z+z#2-+5y?u+6mdidK(@E{|k?Rb^R^taCib#e;_;>4u|^g?DYL~sP*T-S?~gQH2Yg? zXpCUsQ8*fIhiAcVrz9FrfXCCH3;V*QP@by7?(i-sMb|)W_#o^JpMbLbXK)OB4NiqU znY;$J!tv~H?W7?K`VCAL91P{sVNjM_0Of(DP#a|6AXtJ5#cGJo*0WHG{0pJ5UH8_Iwj?$Ep<)b|0@_;rJ$WZ-576w`a47T64B!HZDI_*+Q6S#Llo^mnN5 zKZLTRPkpk^02sR#YW~=ilc5Zo1&6>TP@%X!qM@X^8{&2A38-W83Y1HC!VdTWlm&&; zk`2mG{gqHIzb+lWCFMO(>#T!A;btg{cR+b+C*(w1(JmTw3!pZ> zIOP(kq{%`}Tn@*=4k!yZ!5;8AsQ15sGUQiKitd8i_;3b^mem*PyK`V~o&PB`w9tI0 z*tbE&FiiW`KuuT;d&0XQnp<@3`1VyIB$p$gTt>HD?g@V{KMiGii?C+Ua%##6WHkAhQS4z|DzurGWUo&>v1 zNL<<*X3JY)={0M5H$)vO_Z-LX{ zJg8XT29=cSp^np&P%eKCl5f^~P_gbaDVaY8DkmmE&A$q2{_=>%sWetWZS(-#03U@) zviZ%)#LJ)x#ww`s+o2S^4=O|tL0P;Jz6>|RQ(!bXNxrpE^EX1perw9;RT^^9+fWL; z54G^ekQ}qRO-XX&NT`iX$}^!nHV*1^ya*MVUqfy57L=<$fO7p&#PMD@2x^}#2_x%y z8YeUGQu^X8D23jI2L18vg4k$~SNepo&R0!rnCH19H8|I*5U4~jG zf~1#qFRWvKYda0acGuVvA-Yc!$(p+4)xt;sPDEyS+*Uv!Z)EjH*{vA z=rq`k{wzpMv*tj(51>N2dM4+8D2??DXyIQ%#pYG0@jpSi>JvXL}XcY~)3Qxx_Uz)xUu1nhk;a$jC z$X5}C;2K08${`9xPejj?$Z2saiTS;>{j{%BqlBEA_CXZ?7eAAh;@ca!6uA%4b8!Oe zdnvyIgS0&gb|7<+Md_HZB5ms^o}0GkL7fymvlCePnE2nLp%~Ymqm&52N4~ii;(q*p4AEB-*542_ULD6k3(E!Mmqj5Q~|0W5eXb;v?|uOkxpbWas%=- zqGxb|xTvJf_|iI-&gsaUbnqeg0J0KkLq;PF$Ttx^*CP{;4`T!^$F zUqNi-8^~Iui0F9?xgu^Q=l;aBEet?zM&=_9ax${#^Fum~$U@|PWCNo8t$W0}=KPo` zbNzPXdd*EV>zYeb>I~0ie#K;L&otX6Q?P?za(!Fq8KfboWHL?=)SG|W@BA*6PDGiJ3sO=j&>Ygb_}YwJ6W_J8IQ0`;5a7CJEpztbQ&k?hJM+! z*@#o{tMQxAFU9J3c8A+;hpvxr?6M3AP1R)yyP!{H6YdDAZkWk4J*+yI#fniVFUT<+ zu2WT;{o)lfm2w%UV0pV^>;S{k1xqCd6>84yaB;6+4r+#F-HuwI%6_#z31ageqZz-@ zP|P-*mAr}+W24~D@V%VdUMV|C{Nr)GO7o!BZ*|etuY@Kq5BQ2*GEFv?%^W`r-Qj@= z-LQb#d|R}=PUB-0iQw?kYY$8%h^@xXWdC^az%7=T6FOIiCg;*3wDFp8C+G}9r`Q;~ zMxV!FtSPmxlKUsC>*f6VuF2^<5;r1q^I=$;*wnO4syCJ$JBzYeKNB?i<@P4WYbw~G z6NF7!r-QJU(7dT;bR6);d{```j;<*LYyM1<0xwkT!d?nAH`!C_y6o?=r@4t>eOB1w zeH686*=&RF1qYAWG~-o@Z3Mv289SL!%gZQ9OnmMTE9H8w;$@V_cEJq^TznqWxW*^> z{~LlRDIRGEN}VOQvD6vneJ^&t`csFFoo2@xO%6!>On#VMEKLfXa&dATvHd-tBw@|c z{e4~wl3X6euRov9Tl`F=$kB`=dvMQR;(J;AUd`K~Df-kH6#|vhtj}o<^CpT9%v578 z(I*8r<9Gp?&+)PYMi-KeT@|6Gcalr0gvpx9?Q${6#V%{R@=3D}d_JODH9yb+IrQ_f ze<1d9RF`J4LdtzL`X{}#$fR*daObAeKHmIV@96lt(G6opn=zv&o;_~!;`)V$Map}x z=oGTfK9|*nu49&M+v0f?*TALHdX+=Jx$Lebxw|T=T!cZe=9XVFw&*X4OI7*Ih3*m$qhzr+qShFPxVZFe1X-Q%PZ~N zhbBjvX?F@vyTae*a1RyiO6Z`4>jX|<^4qp#@!Tj(Di0loITA93XV&;#S5IXc=CC6r4*)@T%=nraPl&?#pgD!xm+f%sJc?k zV>?BF8+lLnRUHg)3Ro8SRlcsucN~k>inmNfhk14-z(aV}JJ6Mup4kK5X-cX1N}H43 zO^0w$b*-f?A!6t-lJn`rF4Fi#s98wkJv;LF6I0+;kROyNT-^o2!)+&9icTJ%Rj5>o z+g|3&+hxOVj-Y5+>TZ_X#bvJk;V_i?@?cfA>&}pw`~5BZ@&|d3i>}Qki2Gi5(U-XI z;@C)U{R>m)^OfG2WilorWk}=N;qO{tp&{AIhfOiFNERO9not3tDPU!(_% j?{52JAAErTf7V~kPrFKY>p$8%^e86BYxAblX7~LcCKQ)z diff --git a/kotti/locale/de/LC_MESSAGES/Kotti.po b/kotti/locale/de/LC_MESSAGES/Kotti.po index cb99a3992..a8d467ad9 100644 --- a/kotti/locale/de/LC_MESSAGES/Kotti.po +++ b/kotti/locale/de/LC_MESSAGES/Kotti.po @@ -7,7 +7,7 @@ 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" +"POT-Creation-Date: 2014-12-19 14:27+0100\n" "PO-Revision-Date: 2014-11-21 22:44+0100\n" "Last-Translator: Andreas Kaiser \n" "Language-Team: de \n" @@ -28,7 +28,7 @@ msgid "Congratulations! You have successfully installed Kotti." msgstr "Herzlichen Glückwunsch! Sie haben Kotti erfolgreich installiert." #: kotti/populate.py:66 -#, c-format +#, fuzzy, c-format msgid "" "\n" "

Log in

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

\n" " \n" +"basic/configuration.html\">\n" " Configuration manual\n" " \n" "

\n" @@ -224,77 +224,77 @@ msgstr "" " article.\n" "

\n" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 +#: kotti/resources.py:606 kotti/views/edit/content.py:81 msgid "Document" msgstr "Dokument" -#: kotti/resources.py:649 kotti/views/edit/content.py:113 +#: kotti/resources.py:645 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 +#: kotti/resources.py:697 kotti/views/edit/content.py:144 msgid "Image" msgstr "Bild" -#: kotti/resources.py:500 +#: kotti/resources.py:496 msgid "Folder view" msgstr "Ordneransicht" -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "Inhalt" -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "Bearbeiten" -#: kotti/resources.py:489 +#: kotti/resources.py:485 msgid "Share" msgstr "Teilen" -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "Aktionen" -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 +#: kotti/resources.py:487 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "Kopieren" -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 +#: kotti/resources.py:488 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "Ausschneiden" -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 +#: kotti/resources.py:489 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "Einfügen" -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:490 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 +#: kotti/views/edit/actions.py:448 msgid "Rename" msgstr "Umbenennen" -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:491 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 +#: kotti/views/edit/actions.py:450 msgid "Delete" msgstr "Löschen" -#: kotti/security.py:165 +#: kotti/security.py:189 msgid "Viewer" msgstr "Betrachter" -#: kotti/security.py:166 +#: kotti/security.py:190 msgid "Editor" msgstr "Bearbeiter" -#: kotti/security.py:167 +#: kotti/security.py:191 msgid "Owner" msgstr "Besitzer" -#: kotti/security.py:168 +#: kotti/security.py:192 msgid "Admin" msgstr "Administrator" @@ -371,7 +371,7 @@ msgstr "Anmelden" msgid "Username or email" msgstr "Benutzername oder E-Mail" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 +#: kotti/templates/login.pt:34 kotti/views/login.py:212 #: kotti/views/users.py:212 msgid "Password" msgstr "Passwort" @@ -689,7 +689,7 @@ 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/edit/actions.py:364 kotti/views/edit/actions.py:408 msgid "Your changes have been saved." msgstr "Ihre Änderungen wurden übernommen." @@ -716,27 +716,27 @@ msgstr "${type} in ${title} erstellen" msgid "Add ${type}." msgstr "${type} erstellen" -#: kotti/views/login.py:238 +#: kotti/views/login.py:237 msgid "You have reset your password." msgstr "Ihr Passwort wurde erfolgreich zurückgesetzt." -#: kotti/views/login.py:200 +#: kotti/views/login.py:199 msgid "You have been logged out." msgstr "Sie wurden abgemeldet." -#: kotti/views/login.py:66 kotti/views/users.py:270 +#: kotti/views/login.py:65 kotti/views/users.py:270 msgid "Full name" msgstr "Vollständiger Name" -#: kotti/views/login.py:69 +#: kotti/views/login.py:68 msgid "Username" msgstr "Benutzername" -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 +#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 msgid "Email" msgstr "E-Mail" -#: kotti/views/login.py:109 +#: kotti/views/login.py:108 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." @@ -744,26 +744,26 @@ 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 +#: kotti/views/login.py:123 #, python-format msgid "Register - ${title}" msgstr "Registrieren - ${title}" -#: kotti/views/login.py:164 +#: kotti/views/login.py:163 msgid "Login failed." msgstr "Anmeldung fehlgeschlagen." -#: kotti/views/login.py:286 +#: kotti/views/login.py:285 #, python-format msgid "Reset your password - ${title}." msgstr "Passwort zurücksetzen - ${title}" -#: kotti/views/login.py:160 +#: kotti/views/login.py:159 #, python-format msgid "Welcome, ${user}!" msgstr "Willkommen, ${user}!" -#: kotti/views/login.py:173 +#: kotti/views/login.py:172 msgid "" "You should be receiving an email with a link to reset your password. Doing " "so will activate your account." @@ -771,23 +771,23 @@ 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 +#: kotti/views/login.py:177 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 +#: kotti/views/login.py:82 msgid "Register" msgstr "Registrieren" -#: kotti/views/login.py:90 kotti/views/login.py:257 +#: kotti/views/login.py:89 kotti/views/login.py:256 msgid "There was an error." msgstr "Ein Fehler ist aufgetreten." -#: kotti/views/login.py:250 +#: kotti/views/login.py:249 msgid "Submit" msgstr "Absenden" -#: kotti/views/login.py:279 +#: kotti/views/login.py:278 msgid "Your password reset token may have expired." msgstr "Der Link zum Zurücksetzen Ihres Passwortes ist abgelaufen!" @@ -856,8 +856,8 @@ 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 +#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 +#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 msgid "No changes were made." msgstr "Keine Änderungen." @@ -902,68 +902,68 @@ msgstr "${title} kopiert." msgid "${title} was cut." msgstr "${title} ausgeschnitten." -#: kotti/views/edit/actions.py:182 +#: kotti/views/edit/actions.py:183 #, python-format msgid "${title} was moved." msgstr "${title} verschoben." -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 #, python-format msgid "${title} was deleted." msgstr "${title} gelöscht." -#: kotti/views/edit/actions.py:158 +#: kotti/views/edit/actions.py:159 #, python-format msgid "${title} was pasted." msgstr "${title} eingefügt." -#: kotti/views/edit/actions.py:161 +#: kotti/views/edit/actions.py:162 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:225 #, 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:228 #, 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:293 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:329 kotti/views/edit/actions.py:356 msgid "Name and title are required." msgstr "Name und Titel werden benötigt." -#: kotti/views/edit/actions.py:332 +#: kotti/views/edit/actions.py:333 msgid "Item was renamed." msgstr "Objekt umbenannt." -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:455 msgid "Move up" msgstr "Nach oben verschieben" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:456 msgid "Move down" msgstr "Nach unten verschieben" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:457 msgid "Show" msgstr "Anzeigen" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:458 msgid "Hide" msgstr "Verstecken" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:497 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 +#: kotti/views/edit/actions.py:454 msgid "Change State" msgstr "Status ändern" diff --git a/kotti/locale/en/LC_MESSAGES/Kotti.mo b/kotti/locale/en/LC_MESSAGES/Kotti.mo index c8f41979e4fc72ee701de039c436098ff74ffc01..f6db2b84dfcd0486a9d22579ec0be61d5f990ff9 100644 GIT binary patch delta 21 ccmX@ee2{s\n" "Language-Team: en \n" @@ -54,7 +54,7 @@ msgid "" "

\n" " \n" +"basic/configuration.html\">\n" " Configuration manual\n" " \n" "

\n" @@ -133,77 +133,77 @@ msgid "" "

\n" msgstr "" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 +#: kotti/resources.py:606 kotti/views/edit/content.py:81 msgid "Document" msgstr "" -#: kotti/resources.py:649 kotti/views/edit/content.py:113 +#: kotti/resources.py:645 kotti/views/edit/content.py:113 #: kotti/views/edit/content.py:62 msgid "File" msgstr "" -#: kotti/resources.py:726 kotti/views/edit/content.py:144 +#: kotti/resources.py:697 kotti/views/edit/content.py:144 msgid "Image" msgstr "" -#: kotti/resources.py:500 +#: kotti/resources.py:496 msgid "Folder view" msgstr "" -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "" -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "" -#: kotti/resources.py:489 +#: kotti/resources.py:485 msgid "Share" msgstr "" -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "" -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 +#: kotti/resources.py:487 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "" -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 +#: kotti/resources.py:488 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "" -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 +#: kotti/resources.py:489 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "" -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:490 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 +#: kotti/views/edit/actions.py:448 msgid "Rename" msgstr "" -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:491 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 +#: kotti/views/edit/actions.py:450 msgid "Delete" msgstr "" -#: kotti/security.py:165 +#: kotti/security.py:189 msgid "Viewer" msgstr "" -#: kotti/security.py:166 +#: kotti/security.py:190 msgid "Editor" msgstr "" -#: kotti/security.py:167 +#: kotti/security.py:191 msgid "Owner" msgstr "" -#: kotti/security.py:168 +#: kotti/security.py:192 msgid "Admin" msgstr "" @@ -276,7 +276,7 @@ msgstr "" msgid "Username or email" msgstr "" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 +#: kotti/templates/login.pt:34 kotti/views/login.py:212 #: kotti/views/users.py:212 msgid "Password" msgstr "" @@ -582,7 +582,7 @@ 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/edit/actions.py:364 kotti/views/edit/actions.py:408 msgid "Your changes have been saved." msgstr "" @@ -609,74 +609,74 @@ msgstr "" msgid "Add ${type}." msgstr "" -#: kotti/views/login.py:238 +#: kotti/views/login.py:237 msgid "You have reset your password." msgstr "" -#: kotti/views/login.py:200 +#: kotti/views/login.py:199 msgid "You have been logged out." msgstr "" -#: kotti/views/login.py:66 kotti/views/users.py:270 +#: kotti/views/login.py:65 kotti/views/users.py:270 msgid "Full name" msgstr "" -#: kotti/views/login.py:69 +#: kotti/views/login.py:68 msgid "Username" msgstr "" -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 +#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 msgid "Email" msgstr "" -#: kotti/views/login.py:109 +#: kotti/views/login.py:108 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 +#: kotti/views/login.py:123 #, python-format msgid "Register - ${title}" msgstr "" -#: kotti/views/login.py:164 +#: kotti/views/login.py:163 msgid "Login failed." msgstr "" -#: kotti/views/login.py:286 +#: kotti/views/login.py:285 #, python-format msgid "Reset your password - ${title}." msgstr "" -#: kotti/views/login.py:160 +#: kotti/views/login.py:159 #, python-format msgid "Welcome, ${user}!" msgstr "" -#: kotti/views/login.py:173 +#: kotti/views/login.py:172 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 +#: kotti/views/login.py:177 msgid "That username or email is not known by this system." msgstr "" -#: kotti/views/login.py:83 +#: kotti/views/login.py:82 msgid "Register" msgstr "" -#: kotti/views/login.py:90 kotti/views/login.py:257 +#: kotti/views/login.py:89 kotti/views/login.py:256 msgid "There was an error." msgstr "" -#: kotti/views/login.py:250 +#: kotti/views/login.py:249 msgid "Submit" msgstr "" -#: kotti/views/login.py:279 +#: kotti/views/login.py:278 msgid "Your password reset token may have expired." msgstr "" @@ -742,8 +742,8 @@ 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 +#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 +#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 msgid "No changes were made." msgstr "" @@ -788,68 +788,68 @@ msgstr "" msgid "${title} was cut." msgstr "" -#: kotti/views/edit/actions.py:182 +#: kotti/views/edit/actions.py:183 #, python-format msgid "${title} was moved." msgstr "" -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 #, python-format msgid "${title} was deleted." msgstr "" -#: kotti/views/edit/actions.py:158 +#: kotti/views/edit/actions.py:159 #, python-format msgid "${title} was pasted." msgstr "" -#: kotti/views/edit/actions.py:161 +#: kotti/views/edit/actions.py:162 msgid "Could not paste node. It no longer exists." msgstr "" -#: kotti/views/edit/actions.py:224 +#: kotti/views/edit/actions.py:225 #, python-format msgid "${title} is now visible in the navigation." msgstr "" -#: kotti/views/edit/actions.py:227 +#: kotti/views/edit/actions.py:228 #, python-format msgid "${title} is no longer visible in the navigation." msgstr "" -#: kotti/views/edit/actions.py:292 +#: kotti/views/edit/actions.py:293 msgid "Nothing was deleted." msgstr "" -#: kotti/views/edit/actions.py:328 kotti/views/edit/actions.py:355 +#: kotti/views/edit/actions.py:329 kotti/views/edit/actions.py:356 msgid "Name and title are required." msgstr "" -#: kotti/views/edit/actions.py:332 +#: kotti/views/edit/actions.py:333 msgid "Item was renamed." msgstr "" -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:455 msgid "Move up" msgstr "" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:456 msgid "Move down" msgstr "" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:457 msgid "Show" msgstr "" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:458 msgid "Hide" msgstr "" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:497 msgid "You have to select items to perform an action." msgstr "" -#: kotti/views/edit/actions.py:453 +#: kotti/views/edit/actions.py:454 msgid "Change State" msgstr "" diff --git a/kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo b/kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo index 7c1e7faa5777244877723c378814ae73d870349b..fc0a2343edfec55505e6fce39d89654f7440d085 100644 GIT binary patch delta 23 ecmX>TbtY=VSt%|fT|-L+LlY|_^Uc?#N`(My2M7uP delta 23 ecmX>TbtY=VSt%|\n" "Language-Team: French\n" @@ -54,7 +54,7 @@ msgid "" "

\n" " \n" +"basic/configuration.html\">\n" " Configuration manual\n" " \n" "

\n" @@ -133,77 +133,77 @@ msgid "" "

\n" msgstr "" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 +#: kotti/resources.py:606 kotti/views/edit/content.py:81 msgid "Document" msgstr "Document" -#: kotti/resources.py:649 kotti/views/edit/content.py:113 +#: kotti/resources.py:645 kotti/views/edit/content.py:113 #: kotti/views/edit/content.py:62 msgid "File" msgstr "Fichier" -#: kotti/resources.py:726 kotti/views/edit/content.py:144 +#: kotti/resources.py:697 kotti/views/edit/content.py:144 msgid "Image" msgstr "Image" -#: kotti/resources.py:500 +#: kotti/resources.py:496 msgid "Folder view" msgstr "Contenus" -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "Contenus" -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "Modifier" -#: kotti/resources.py:489 +#: kotti/resources.py:485 msgid "Share" msgstr "Permissions" -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "Actions" -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 +#: kotti/resources.py:487 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "Copier" -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 +#: kotti/resources.py:488 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "Couper" -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 +#: kotti/resources.py:489 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "Coller" -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:490 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 +#: kotti/views/edit/actions.py:448 msgid "Rename" msgstr "Renommer" -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:491 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 +#: kotti/views/edit/actions.py:450 msgid "Delete" msgstr "Supprimer" -#: kotti/security.py:165 +#: kotti/security.py:189 msgid "Viewer" msgstr "Lecteur" -#: kotti/security.py:166 +#: kotti/security.py:190 msgid "Editor" msgstr "Editeur" -#: kotti/security.py:167 +#: kotti/security.py:191 msgid "Owner" msgstr "Propriétaire" -#: kotti/security.py:168 +#: kotti/security.py:192 msgid "Admin" msgstr "Administrateur" @@ -279,7 +279,7 @@ msgstr "Identifiant" msgid "Username or email" msgstr "Identifiant ou e-mail" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 +#: kotti/templates/login.pt:34 kotti/views/login.py:212 #: kotti/views/users.py:212 msgid "Password" msgstr "Mot de passe" @@ -606,7 +606,7 @@ 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/edit/actions.py:364 kotti/views/edit/actions.py:408 msgid "Your changes have been saved." msgstr "Vos modifications ont été enregistrées." @@ -633,27 +633,27 @@ msgstr "Ajouter un(e) ${type} dans ${title}." msgid "Add ${type}." msgstr "Ajouter ${type}." -#: kotti/views/login.py:238 +#: kotti/views/login.py:237 msgid "You have reset your password." msgstr "Vous avez réinitialisé votre mot de passe." -#: kotti/views/login.py:200 +#: kotti/views/login.py:199 msgid "You have been logged out." msgstr "Vous avez été déconnecté." -#: kotti/views/login.py:66 kotti/views/users.py:270 +#: kotti/views/login.py:65 kotti/views/users.py:270 msgid "Full name" msgstr "Nom complet" -#: kotti/views/login.py:69 +#: kotti/views/login.py:68 msgid "Username" msgstr "Identifiant" -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 +#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 msgid "Email" msgstr "E-mail" -#: kotti/views/login.py:109 +#: kotti/views/login.py:108 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." @@ -662,26 +662,26 @@ msgstr "" "recevoir un e-mail contenant le lien permettant d'initialiser votre mot de " "passe, afin d'activer votre compte." -#: kotti/views/login.py:124 +#: kotti/views/login.py:123 #, python-format msgid "Register - ${title}" msgstr "S'inscrire - ${title}" -#: kotti/views/login.py:164 +#: kotti/views/login.py:163 msgid "Login failed." msgstr "Connexion échouée." -#: kotti/views/login.py:286 +#: kotti/views/login.py:285 #, python-format msgid "Reset your password - ${title}." msgstr "Réinitialiser votre mot de passe - ${title}" -#: kotti/views/login.py:160 +#: kotti/views/login.py:159 #, python-format msgid "Welcome, ${user}!" msgstr "Bienvenue, ${user} !" -#: kotti/views/login.py:173 +#: kotti/views/login.py:172 msgid "" "You should be receiving an email with a link to reset your password. Doing " "so will activate your account." @@ -689,23 +689,23 @@ 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 +#: kotti/views/login.py:177 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 +#: kotti/views/login.py:82 msgid "Register" msgstr "S'inscrire" -#: kotti/views/login.py:90 kotti/views/login.py:257 +#: kotti/views/login.py:89 kotti/views/login.py:256 msgid "There was an error." msgstr "Il y a une erreur." -#: kotti/views/login.py:250 +#: kotti/views/login.py:249 msgid "Submit" msgstr "Soumettre" -#: kotti/views/login.py:279 +#: kotti/views/login.py:278 msgid "Your password reset token may have expired." msgstr "Votre jeton de réinitialisation de votre mot de passe est expiré." @@ -773,8 +773,8 @@ 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 +#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 +#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 msgid "No changes were made." msgstr "Aucune modification a été effectuée." @@ -819,68 +819,68 @@ msgstr "${title} a été copié." msgid "${title} was cut." msgstr "${title} a été coupé." -#: kotti/views/edit/actions.py:182 +#: kotti/views/edit/actions.py:183 #, python-format msgid "${title} was moved." msgstr "${title} a été déplacé." -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 #, python-format msgid "${title} was deleted." msgstr "${title} a été supprimé." -#: kotti/views/edit/actions.py:158 +#: kotti/views/edit/actions.py:159 #, python-format msgid "${title} was pasted." msgstr "${title} a été collé." -#: kotti/views/edit/actions.py:161 +#: kotti/views/edit/actions.py:162 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:225 #, 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:228 #, 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:293 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:329 kotti/views/edit/actions.py:356 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:333 msgid "Item was renamed." msgstr "L'élément a été renommé." -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:455 msgid "Move up" msgstr "Décaler vers le haut" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:456 msgid "Move down" msgstr "Décaler vers le bas" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:457 msgid "Show" msgstr "Rendre visible" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:458 msgid "Hide" msgstr "Cacher" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:497 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 +#: kotti/views/edit/actions.py:454 msgid "Change State" msgstr "Modifier le statut" diff --git a/kotti/locale/it/LC_MESSAGES/Kotti.mo b/kotti/locale/it/LC_MESSAGES/Kotti.mo index 6ab94fe69a81eff7e2d5a35ab9be4536ead2e7e3..878bf07184b56c0a634bb920ed04af42eaf7a9ca 100644 GIT binary patch delta 3724 zcmYk;3v`Zk0LStF+SmubaPeS-Z>Dj=TILyWN_#(E!GHim!uqj@`Mp%t53<~r8-V*hDGR9&K#^6Mx zDaWj$63B^Ctczvnk2|nA?!tQb6$axe48rsF_$t=txC(XMefxW8!c|%uiljk@q0>H!yU2L6uxX9^jn8kmM!l6k0+FSW;8 zP#xcey8j^Rxrb5h96|mwCtS?GPF&?k4c^2l_&4f?G0lu&Rm=obM`xi%xCEJ1vk@oY z2dL+?V7+u-6zckT)cI6<+!eKyd8qq~!kK@KU?wND3FqU(xD?f)ZKxY|qDFEA_2BRA z@kP}A*RB7cMs^RQu?ahvRW+TEQ>Hg+DMuo!VdgqiG^J~BD!z{zK_tCUgC5ke7d7Rn z_I$QA5A~pd7=uNq5idi{)EeZCH07u@{|IC83~GSRT`Ic4j~%58gHSyUM~y5A(=iR* zI0dyNt5F>*N8Ps*^}vr&pN`M$@ln)uCs6I2wVp@zh-0o$(T%sT16HF()aDW2ja^Z{ zcSrR+7uDco)UJ2z@0(F0E=S$J4>g0|pw{{n>N&rmI&uR;^#0$XqP4$^8d(4ft{Yq! zjd7@{%R{aGc+?seBa>&=`u(-Br{`tE{(COLHGJfMBN2(NuY; zXzDVod8l1G4B3xnl68?izZtcrdvOpRL~Y_mk-m<^ViL!FFd1i|-ike_>%K-e{v65t zYt8R*LOu2}KU%ZSs1HbY)LQpLjkEyMu>`YmKfa20u_Ml4z4V;js16=LU3Unzr2a9! z8F8VGTf{i}M}T$XL@E}bKE>-$d*U$CH**xVH~vJuh7V8;g)-ltq6;;(r;(q`1=L9E zur75m0_$LF48ST?D7qY({)0c{-bQ0>pGcg30qxQfC)C0F8H<$z17Jov$9f7To3L zt?7n3pO3nJIxfI<7>cbDeKV7QfqMT_sD$uC2I_(w)KvFJ?fwy{-CT@8Sb^F+m8gz= ziR?df7U`3@gIentdMOi71M{MmxCiPv!!U#P%_u7RaFn4&auS2F3iTS^K`lXjF4CvD zDXPPXs0LE4U98zykMp^xsUC!#a44z+t5DCYKt~tsp+XyGA8JHb?D4;-3!@mmHcK1S z_aYrNf!_svKG zKF0Z8I0@%qG~PmeV4Cp`$6_w}p$4V$iVw38-z556t4#F4E6@DvL0Y%vWHYHxRQixW zq6P+#cZfY>q!?fpQwx`6UcjH15p`C5^8Gv-?McEwj&i<|0VXMMyPdtmON$8 ziO0z@d#sJ8qUp{j+jU?|4)!NS_ILsICR+0uWDc1|m^9Oas5npa=XtV;bR;8)HjheU z@)CK0XyY{_D#b(}lecw1=}O9pM+g5aG5tX$*OfIe_`p2g= zX-K?8Kp{IkocF=$3vp KKU_vPjq(Ul1s8Cd@5<)mC|B=c+RPy=$-s_%O ztt2ZIQ|a0_-#*^?yWj8ak1jg;ib0>1?E7d*j_==0Cu+2F50&GRfKr}54OF94qpvb10WxCz_} z?f^6JA@GmESAqLq=;nVXsQ%s!ik|m@n%~zzt^Wy7{eK6%6#M}wKKvK>A~57f^VkBe z1$Tm)M+^K7umnY?w}GDr?*%p9x*MsV}32K}KRQ;ZRz8lnd?*&Wn1EA_Z^QG_zJP*`7 zF9o%}QBZQW73_l7gX;GipxPY=RsUQ5{rCOzk3hN(egUfexe!(Je=#WeTMt6oU<}lJ z_JOK@0Mt4XQ2plq`E8)u-|O*%pw{&X@N)1ApycQuK}06_Imln|bcioGTL+4sSA&b- zYe3EaFF=j=gn#}!Q1t%3fB!EYe*vnWUxMqwb3;dmaZq%+8bo!2y&zSB!{7$64{CiM z2Sv9>LDhQ<)I7feYF&Q|PJ%xG`3s(hkcl5xftu%DQ0)$Y>hBGpOvju3^8%=PJy7E; zdOQY7KHdSU-TmMz!4HF4&$mIf`;q_tub}4rQ&8hwxX#J@%RsfC0=3?~pxWOIihj3& z;&TD2-aA3fK+X4I zP<(p~lzjXNcs}?|Q0x3ID0%!DxC>lA5d?1m6YwhV-$3>A5(d@$Hh`)(3W~2iQ1rOV zKi><|B={I8yZl{HcK947CAruL!usGE@Ivqnp!BcSb&d$>i=ShBYX*{{x1Wyj%z^iXB)Ty zJOGML1t@yF!{dFR`1esz?Y;s^uf73_o<9WD@4tg;_bX8Rf7Ugwzl%ZD-vA=&!Cp{$ zQG%N9eW3K^kNo>@gR1{y@NV#&Yuz~S0Y$e5LDBb7P~$!Bzkd-Fo&OAc0r)qd)ZrNuXlkj2iu_d`~i;-f?C(Z;Kkq){0R75@YA5?|8-FH{}R+Z{sEL6{1YgC{tQ&T{{*#;{|43mqHRu| zH-InZc^{~HO;G&mfNK99P<(w5Lc5nmFUj{L&!Fwrhq`Z`} zK>0M~%@kelrrbk$17$B|grc>tr6iPhP;`l3sI&3;EWO~Gq3oe(?Ddqxl{#o!S<<+|3dXa;m1>QvweO^xyU37hx za*N$zM?HQJd@V&ZZu)m`^0*9ML0RXY1(LbydLcItR1e@E`1{`m@1Wf4pCxCTDW9TT zN|~Z`D34Hd{T}5eyK}lf%Dr^yHz_w$qOzk}dC;6X~}pG&Y!IfwFbiu~V&l;5K0TB7WyJVv>j zvWapP1rr!NpQ3A?@{rvT^MKna&!udk{66Jk%Ihe>nyvGjwjapaVUlhg=XTB3-u5*i z*Xy!=*o@L}YZNv+QBiDLH(RD5Wvu8on{iRB3+MBAZri#})=tuO+X3q%+8&l!xSaL# zut>_-`ijz4Sd>v-hRyjXr8-s1B5Y=98K-5@2*dqzY7)*zi*Y)lYFQDdVHEZlc`3_V z;arwmSJrXV>Lz?`HnV9u_kvtBWcsp(}_fdO8FGU=O<$N5r^LROoTS=Ma;cP^SJK2(bDYKrUN`aIm}oTLJ;#Y zZ5Ls`utnQ4tB#AL+oMStA1TARgge-3UD`vjT$FLQQMWJ_R!vWw<7zog(z$G1-PpHt zGCNt>$5D%^wz6i?$ny4hoQ`**GA_#TR=fzyd(3;hTCy>_F<*8&L!|V5 zSJZR@0S)?5XT|ti$0NrYHKWG0Ub}t+6aEPS3EZFzt6|p?)?OMy|n1k%Zte ze3F#U_0y&#IO-%NOfyCFJT(peHPXMg+)En0<#Iktjq-ZlvHq1)kxgh@fyg)KH$>gu zmNL$}+l*bS$?oK@TIW+v@ZDLn-$f^ltEVUXEm_)v=u7ia8Fn*liwuB_Xe&dF%Xv4` z9btHjYIKrjoEFGDdKDGCJ%}LImcYxDR0%PX{c~MPs`IWB!+Nc?ESX?=3tsi`>(GqV zTv$P-dSW)8(@DJ-{^0UEdU=vIlU~%hwOsDS$HL3+Fne+=T*3~v;!a$~?2hZVNfD+Q z#yf38aoNLJXFep-1G9W$gQYX7su59}731k$6I-zQWxpI6<>Zsq+iteFsx9u#RyBon zl;B84O36mtjS_hg*(Q9@k)*&)2CJ&2Q5S=IVy#(BbueW*R2VMC0cJx*R5j{i#Vbd=J6${2M%}C+@hSvxFkJFS&dh!0-t5twG`nkS&S2qdQCgd zq~;1Sd-4`$EHgQFIWjG-pixmI?KEt6vRSk=&pMiFabF!OpC;9PEmSRXV{g1x|4|BNA2*j-F*!J+CBmunPmT5aNSz zummA7k&?W19a70ryQpmvy7lwUG2f(|N4Yu)6Qf-opQL3;14VbYIu#wXM;-I1?BhT2 zD8*WfA|oc?W5&6D2WIAR8+m44t%g-7=CgjMrKl~Bn{l#eaRPCY>451(R8MEsWE$b_ zObrT|SVdmQ{i5W|7VBfgMu2Z#Ju`)yE2oyE5Vg}G9<-pw+F6WB2R)c!6HPM(XsiGW z)QTGl`Ua+`($!t4t_6m>jbmu!?szWhcS?)-Ry<%=w0?C94On4gRf8d`4<8 z0Jt!;TB~dwJGFL!67VD4Br7SviF(7GNg4iDOyk zdXwVJLpV#w38m1`MzDt{+kQ^BbXDTJU$~yKJlG?-3-%I@+K)!pO~)c&lPZ}xn-r$X zTWV(4G=01L4&&0Tm6{!M6LP8~=~je%a@jtKsx=syt~Fp0GMd@lBG{XC6liGozWNvFl`MnS&z2L+PZ#QhN+ zxd;n+5|iiSgl$8Awtn3R_IGgy!Tz+_>9^v#BVH^LwX%zfI(#m;}6CM&g z9EhdxHfzmx*^MVFDce3Wqma1bo3~O}GxT5t(K<3 z9ola=hh*7AvbKEmbhIGNG&_Ebrz1(X-*p?IA~_n%tO}1!?+m6z)ucIOQl^O~Al*{R zH}3G))63{jJcnV$WfbAqK(eow4C}|Q*!F3d3 zW9SC5-Z)?r_Zf+Xq6iLpw^-i{bFlho8vzVRmHZ&{VDc<)+@Pf~%fW#C2hE39l1;5u zUUXRF95tB8|6Ou6CnAV^e3TiVI}@JjM()O(TX3`7jDG0qX6>{Rr3&0F^S>qTpjQ@TTF_Lz zsN4u=$-c_qO&u5#fV%8kV^9ePW=2D^8~Xk_|5}8c463Kwi2kI*D+1DneKs>~fs^kA zn>+L+UoGZCUtxlCP%hFJQ;19yn23Sm4oM3eW+0wV9=@Jz%rgsMSugg>3~yn1QWp{y zC?QtRzE%oe$B7F#+itY%s>erq&eU67XEL34Gn=x4elkk8;he|zx5jQJ^kA)%KFzYR zacyH0iLo^Y<6f4RW79=DX^rjdw~Mhu*<`q9dTRfHS5NKUeQ?jr%$gf-I5buT!ea`A zC&Nt}CpM2wOpI-s2scekUUl^=Hco8ZNX4;(v3BuUI$Uq##Ms2O;l#w`#Aa9T0OoV- zP#&cPTZ}B93||{9CgGthPmezyZau;SZ`1hIZN+d6#B%$Z18ki7gb!nf;;1_bH^chX zWVmK}|MZ^05+@oP*Ld9;lUh#No@~6=!FO#D^>ZxHw!??^j$JdTr`h0i$M&R6oOIfr z46m6@$~8B3`gznDBPi<@lVRGk$70*2Eup*Jc10Sl-4;$swVU6DfR`^mVN1(y#PORBEw~0ok;wNNFX~bji`9 zk?WUy1aC>>Cy)uU5tFet7RjyHp+!!Imu)dNzPlf%_2DF`b9tGx#XuP z%kN2}b=iEMa@tq94yVDVSDZsg!aTWP2}Cg>bs#**P>KSE%t?h%rz+i#@q+%A?mrq~ zNlEn|h}-QX#9C_Wp|8q%u*q#FC_hF?afZz!@mv`k7nvme|EBo=m$H8bIsW61*%rXk z!MGPW_o1Y^91t~n$?>n6jg;3Q2P6wSWXp#dP+5*f_Vi5ZV#KgWqCV%z!vhbdMdp9y zoa#?y)b5<>8_Dg}tEpQSWXrT#fb5DFADOi}4UTp)n)Z;GJ9p@SFJ}4p zwO;@DpDK1>x$fveIvbD6rn*E+&URX74}=-Ap8oZ`cD$k=2lZxR7s>duPwFI0LtRwK z#2rsw?}X!r)A!4mal20tfmL@QQ(}h?ahv!e?`It-Z|ObwSx~ipYumvjP>4*hCzWZ- zNGbMd#YnDWP!bSXH;I-Mz?pwu>}TkN%o4VZtOxtMK7zt1Rg)??-q#6)7j$mAvkJPT zQ7az8sCtC@PT_``rIbiFLAovKszM7!Wx{2DDAKgeu<@-vb(n!NW=!qOLQ}SF7Rk-k z%_Yb9ypx<ap1&oOEPb zOVKnbA0^kj`d#(mglXj3aB5LGdw&?g6#U*xL-{ZtD@7~ZJJFWTCX$G(7hfTtB-@)0 z5n#m23+p&g-&;(NAu^Nd3<#ZZov| zy0r;asRi}s*!t7HXd-$rg z#`<#)x9Tbu$buW%sYQK8>J0BKnpg@GM9Ix%0jPyP%oOrq6CC)Z*YYSNU9ZhCn zAPFWDS=<&DsX7mlE2y*zZ%}R8jp212otY3%l5&SRc5DK5t*_wbh+Vj>iyfY1!H`v` zLzAeEg--UB|J2Cj?^u?18y1yhp>rBT3zR(Q=wyVW8r%Y`sFPF(dzFMTog48naN3d% z;i*QvhR}$;b=?rRZ26E1_9&O?!od+NDuxH!xCdDbNP{y7_8ykU$AaDBw=zsRso;pd zi>31dJbcnpnZSx6!8EZ6ehX{5c5joKp;2(X1w~SRf9ty9ET$-=DD@1@^2-$1O4)yqv#7q#+mVcm zpQS@eL-4;BbWjBn7RMl)&cZP-{75YbEoq0hcn!`9CGckDYo{@ERCAav@QqabQTHaWO5l@$2}Z^iHx%0 zdbz?H>z`X)T$_rHCtx~M zYB9*NMCLa~oOz&`_8H0`ogyBBN%c0vk51nwjvn%lUkeLbgaQdHuEfA{*eC8g@L^?t w$WxFjuboICJ(u;}Ptcj^B*_9bXI)m0Y96B*wcwQ*Y?+$M#yhLL%9aHG15YQ+#sB~S diff --git a/kotti/locale/it/LC_MESSAGES/Kotti.po b/kotti/locale/it/LC_MESSAGES/Kotti.po index b93cd4156..454b65b10 100644 --- a/kotti/locale/it/LC_MESSAGES/Kotti.po +++ b/kotti/locale/it/LC_MESSAGES/Kotti.po @@ -6,7 +6,7 @@ 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" +"POT-Creation-Date: 2014-12-19 14:27+0100\n" "PO-Revision-Date: 2014-01-19 11:14+0100\n" "Last-Translator: Xavi Torné \n" "Language-Team: it \n" @@ -26,7 +26,7 @@ msgid "Congratulations! You have successfully installed Kotti." msgstr "Congratulazioni! Avete installato Kotti con successo." #: kotti/populate.py:66 -#, c-format +#, fuzzy, c-format msgid "" "\n" "

Log in

\n" @@ -53,7 +53,7 @@ msgid "" "

\n" " \n" +"basic/configuration.html\">\n" " Configuration manual\n" " \n" "

\n" @@ -114,8 +114,8 @@ msgstr "" "

\n" "

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

\n" " \n" @@ -191,77 +191,77 @@ msgid "" "

\n" msgstr "" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 +#: kotti/resources.py:606 kotti/views/edit/content.py:81 msgid "Document" msgstr "Documento" -#: kotti/resources.py:649 kotti/views/edit/content.py:113 +#: kotti/resources.py:645 kotti/views/edit/content.py:113 #: kotti/views/edit/content.py:62 msgid "File" msgstr "File" -#: kotti/resources.py:726 kotti/views/edit/content.py:144 +#: kotti/resources.py:697 kotti/views/edit/content.py:144 msgid "Image" msgstr "Immagine" -#: kotti/resources.py:500 +#: kotti/resources.py:496 msgid "Folder view" msgstr "Vista della cartella" -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "Contenuti" -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "Modificare" -#: kotti/resources.py:489 +#: kotti/resources.py:485 msgid "Share" msgstr "Condividi" -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "Azioni" -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 +#: kotti/resources.py:487 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "Copia" -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 +#: kotti/resources.py:488 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "Taglia" -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 +#: kotti/resources.py:489 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "Incolla" -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:490 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 +#: kotti/views/edit/actions.py:448 msgid "Rename" msgstr "Rinomina" -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:491 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 +#: kotti/views/edit/actions.py:450 msgid "Delete" msgstr "Elimina" -#: kotti/security.py:165 +#: kotti/security.py:189 msgid "Viewer" msgstr "Visualizzatore" -#: kotti/security.py:166 +#: kotti/security.py:190 msgid "Editor" msgstr "Editor" -#: kotti/security.py:167 +#: kotti/security.py:191 msgid "Owner" msgstr "Propietario" -#: kotti/security.py:168 +#: kotti/security.py:192 msgid "Admin" msgstr "Admin" @@ -334,7 +334,7 @@ msgstr "Accedi" msgid "Username or email" msgstr "Username o email" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 +#: kotti/templates/login.pt:34 kotti/views/login.py:212 #: kotti/views/users.py:212 msgid "Password" msgstr "Password" @@ -650,7 +650,7 @@ 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/edit/actions.py:364 kotti/views/edit/actions.py:408 msgid "Your changes have been saved." msgstr "Le tue modifiche sono state salvate." @@ -677,27 +677,27 @@ msgstr "Aggiungere ${type} a ${title}." msgid "Add ${type}." msgstr "Aggiungere ${type}." -#: kotti/views/login.py:238 +#: kotti/views/login.py:237 msgid "You have reset your password." msgstr "Hai resettato la tua password." -#: kotti/views/login.py:200 +#: kotti/views/login.py:199 msgid "You have been logged out." msgstr "Sei uscito." -#: kotti/views/login.py:66 kotti/views/users.py:270 +#: kotti/views/login.py:65 kotti/views/users.py:270 msgid "Full name" msgstr "Nome completo" -#: kotti/views/login.py:69 +#: kotti/views/login.py:68 msgid "Username" msgstr "Username" -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 +#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 msgid "Email" msgstr "Email" -#: kotti/views/login.py:109 +#: kotti/views/login.py:108 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." @@ -705,26 +705,26 @@ 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 +#: kotti/views/login.py:123 #, python-format msgid "Register - ${title}" msgstr "Registrazione - ${title}" -#: kotti/views/login.py:164 +#: kotti/views/login.py:163 msgid "Login failed." msgstr "Errore nell'accesso." -#: kotti/views/login.py:286 +#: kotti/views/login.py:285 #, python-format msgid "Reset your password - ${title}." msgstr "Resetta la tua password - ${title}." -#: kotti/views/login.py:160 +#: kotti/views/login.py:159 #, python-format msgid "Welcome, ${user}!" msgstr "Benvenuto, ${user}!" -#: kotti/views/login.py:173 +#: kotti/views/login.py:172 msgid "" "You should be receiving an email with a link to reset your password. Doing " "so will activate your account." @@ -732,23 +732,23 @@ msgstr "" "Dovresti ricevere una email con un link per resettare la tua password. " "Clikkandolo si attiverà il tuo account." -#: kotti/views/login.py:178 +#: kotti/views/login.py:177 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 +#: kotti/views/login.py:82 msgid "Register" msgstr "Registrazione" -#: kotti/views/login.py:90 kotti/views/login.py:257 +#: kotti/views/login.py:89 kotti/views/login.py:256 msgid "There was an error." msgstr "C'è stato un errore." -#: kotti/views/login.py:250 +#: kotti/views/login.py:249 msgid "Submit" msgstr "Invia" -#: kotti/views/login.py:279 +#: kotti/views/login.py:278 msgid "Your password reset token may have expired." msgstr "Il token per resettare la tua password è scaduto." @@ -816,8 +816,8 @@ 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 +#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 +#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 msgid "No changes were made." msgstr "Non sono state fatte modifiche." @@ -862,68 +862,68 @@ msgstr "${title} è stato copiato." msgid "${title} was cut." msgstr "${title} è stato tagliato." -#: kotti/views/edit/actions.py:182 +#: kotti/views/edit/actions.py:183 #, python-format msgid "${title} was moved." msgstr "${title} è stato spostato." -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 #, python-format msgid "${title} was deleted." msgstr "${title} è stato cancellato." -#: kotti/views/edit/actions.py:158 +#: kotti/views/edit/actions.py:159 #, python-format msgid "${title} was pasted." msgstr "${title} è stato incollato." -#: kotti/views/edit/actions.py:161 +#: kotti/views/edit/actions.py:162 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:225 #, 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:228 #, 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:293 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:329 kotti/views/edit/actions.py:356 msgid "Name and title are required." msgstr "Nome e titolo sono obbligatori." -#: kotti/views/edit/actions.py:332 +#: kotti/views/edit/actions.py:333 msgid "Item was renamed." msgstr "L'elemento è stato rinominato" -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:455 msgid "Move up" msgstr "Muovere su" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:456 msgid "Move down" msgstr "Muovi giù" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:457 msgid "Show" msgstr "Mostra" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:458 msgid "Hide" msgstr "Nascondi" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:497 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 +#: kotti/views/edit/actions.py:454 msgid "Change State" msgstr "Cambia stato" diff --git a/kotti/locale/ja/LC_MESSAGES/Kotti.mo b/kotti/locale/ja/LC_MESSAGES/Kotti.mo index ddfb265496ee362933fcfd0a9ff9d4b2e40f06e2..a15d8fe503e1b788aeef54512bf5c1325b18c3fc 100644 GIT binary patch delta 23 ecmX?;a3*2HWhpKrT|-L+LlY|_^UZgq@\n" "Language-Team: ja \n" @@ -55,7 +55,7 @@ msgid "" "

\n" " \n" +"basic/configuration.html\">\n" " Configuration manual\n" " \n" "

\n" @@ -134,77 +134,77 @@ msgid "" "

\n" msgstr "" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 +#: kotti/resources.py:606 kotti/views/edit/content.py:81 msgid "Document" msgstr "ドキュメント" -#: kotti/resources.py:649 kotti/views/edit/content.py:113 +#: kotti/resources.py:645 kotti/views/edit/content.py:113 #: kotti/views/edit/content.py:62 msgid "File" msgstr "ファイル" -#: kotti/resources.py:726 kotti/views/edit/content.py:144 +#: kotti/resources.py:697 kotti/views/edit/content.py:144 msgid "Image" msgstr "画像" -#: kotti/resources.py:500 +#: kotti/resources.py:496 msgid "Folder view" msgstr "フォルダー表示" -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "コンテンツ" -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "編集" -#: kotti/resources.py:489 +#: kotti/resources.py:485 msgid "Share" msgstr "共有" -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "アクション" -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 +#: kotti/resources.py:487 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "コピー" -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 +#: kotti/resources.py:488 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "切り取り" -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 +#: kotti/resources.py:489 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "貼り付け" -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:490 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 +#: kotti/views/edit/actions.py:448 msgid "Rename" msgstr "名称変更" -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:491 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 +#: kotti/views/edit/actions.py:450 msgid "Delete" msgstr "削除" -#: kotti/security.py:165 +#: kotti/security.py:189 msgid "Viewer" msgstr "閲覧者" -#: kotti/security.py:166 +#: kotti/security.py:190 msgid "Editor" msgstr "編集者" -#: kotti/security.py:167 +#: kotti/security.py:191 msgid "Owner" msgstr "所有者" -#: kotti/security.py:168 +#: kotti/security.py:192 msgid "Admin" msgstr "管理者" @@ -281,7 +281,7 @@ msgstr "ログイン" msgid "Username or email" msgstr "ユーザー名またはメールアドレス" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 +#: kotti/templates/login.pt:34 kotti/views/login.py:212 #: kotti/views/users.py:212 msgid "Password" msgstr "パスワード" @@ -606,7 +606,7 @@ 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/edit/actions.py:364 kotti/views/edit/actions.py:408 msgid "Your changes have been saved." msgstr "変更内容を保存しました。" @@ -633,27 +633,27 @@ msgstr "${title} に ${type} を追加する。" msgid "Add ${type}." msgstr "${type} を追加する。" -#: kotti/views/login.py:238 +#: kotti/views/login.py:237 msgid "You have reset your password." msgstr "パスワードのリセットに成功しました。" -#: kotti/views/login.py:200 +#: kotti/views/login.py:199 msgid "You have been logged out." msgstr "ログアウトしました。" -#: kotti/views/login.py:66 kotti/views/users.py:270 +#: kotti/views/login.py:65 kotti/views/users.py:270 msgid "Full name" msgstr "フルネーム" -#: kotti/views/login.py:69 +#: kotti/views/login.py:68 msgid "Username" msgstr "ユーザー名" -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 +#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 msgid "Email" msgstr "メールアドレス" -#: kotti/views/login.py:109 +#: kotti/views/login.py:108 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." @@ -661,26 +661,26 @@ msgstr "" "おめでとうございます! 登録が完了しました。Eメールのリンクから、すぐにパスワー" "ドを設定して下さい。" -#: kotti/views/login.py:124 +#: kotti/views/login.py:123 #, python-format msgid "Register - ${title}" msgstr "登録 - ${title}" -#: kotti/views/login.py:164 +#: kotti/views/login.py:163 msgid "Login failed." msgstr "ログインに失敗しました。" -#: kotti/views/login.py:286 +#: kotti/views/login.py:285 #, python-format msgid "Reset your password - ${title}." msgstr "パスワードのリセット - ${title}" -#: kotti/views/login.py:160 +#: kotti/views/login.py:159 #, python-format msgid "Welcome, ${user}!" msgstr "ようこそ ${user}!" -#: kotti/views/login.py:173 +#: kotti/views/login.py:172 msgid "" "You should be receiving an email with a link to reset your password. Doing " "so will activate your account." @@ -688,23 +688,23 @@ msgstr "" "まもなくパスワードをリセットするためのリンクが記載されたメールが送信されま" "す。" -#: kotti/views/login.py:178 +#: kotti/views/login.py:177 msgid "That username or email is not known by this system." msgstr "ユーザー名もメールアドレスも見つかりませんでした。" -#: kotti/views/login.py:83 +#: kotti/views/login.py:82 msgid "Register" msgstr "登録" -#: kotti/views/login.py:90 kotti/views/login.py:257 +#: kotti/views/login.py:89 kotti/views/login.py:256 msgid "There was an error." msgstr "エラーが発生しました。" -#: kotti/views/login.py:250 +#: kotti/views/login.py:249 msgid "Submit" msgstr "送信" -#: kotti/views/login.py:279 +#: kotti/views/login.py:278 msgid "Your password reset token may have expired." msgstr "パスワードをリセットするためのトークンが期限切れです。" @@ -772,8 +772,8 @@ 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 +#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 +#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 msgid "No changes were made." msgstr "変更箇所がありませんでした。" @@ -818,68 +818,68 @@ msgstr "${title} をコピーしました。" msgid "${title} was cut." msgstr "${title} を切り取りました。" -#: kotti/views/edit/actions.py:182 +#: kotti/views/edit/actions.py:183 #, python-format msgid "${title} was moved." msgstr "${title} を移動しました。" -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 #, python-format msgid "${title} was deleted." msgstr "${title} を削除しました。" -#: kotti/views/edit/actions.py:158 +#: kotti/views/edit/actions.py:159 #, python-format msgid "${title} was pasted." msgstr "${title} を貼り付けました。" -#: kotti/views/edit/actions.py:161 +#: kotti/views/edit/actions.py:162 msgid "Could not paste node. It no longer exists." msgstr "ノードを張り付けできません。何も存在していません。" -#: kotti/views/edit/actions.py:224 +#: kotti/views/edit/actions.py:225 #, python-format msgid "${title} is now visible in the navigation." msgstr "${title} がナビゲーションに表示されるようになりました。" -#: kotti/views/edit/actions.py:227 +#: kotti/views/edit/actions.py:228 #, python-format msgid "${title} is no longer visible in the navigation." msgstr "${title} がナビゲーションでは非表示になりました。" -#: kotti/views/edit/actions.py:292 +#: kotti/views/edit/actions.py:293 msgid "Nothing was deleted." msgstr "削除対象がありません。" -#: kotti/views/edit/actions.py:328 kotti/views/edit/actions.py:355 +#: kotti/views/edit/actions.py:329 kotti/views/edit/actions.py:356 msgid "Name and title are required." msgstr "名称とタイトルは必須です。" -#: kotti/views/edit/actions.py:332 +#: kotti/views/edit/actions.py:333 msgid "Item was renamed." msgstr "名称変更しました" -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:455 msgid "Move up" msgstr "上に移動する" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:456 msgid "Move down" msgstr "下に移動する" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:457 msgid "Show" msgstr "表示中にする" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:458 msgid "Hide" msgstr "非表示にする" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:497 msgid "You have to select items to perform an action." msgstr "アクションを適用するアイテムを選択してください。" -#: kotti/views/edit/actions.py:453 +#: kotti/views/edit/actions.py:454 msgid "Change State" msgstr "状態を変更" diff --git a/kotti/locale/ko/LC_MESSAGES/Kotti.mo b/kotti/locale/ko/LC_MESSAGES/Kotti.mo index 149baa2196d34bd8c2f800983555b7c3c1ef7535..309ce7b2bc43db585e3b395fecd025fcaab72207 100644 GIT binary patch delta 3747 zcmX}td2o(L0LSr-+=<+$DB?&6AxA@qXrx4Yf_SxNMpU3vCZcU|*+o2xS zhT{Zzj+A;D^DAEU;}6HQKw|>kjhT+!sm~8FCK>l&Pi(+2e2C%b6KV`a(*Ywe3H5sp z>i0<)kHr{gjKfq@;GX6jdgDd(!Up8NrU|358QbC?=!;L#2R-Or^#BaODAajzsNYjj z{SU-k%toD8g$dl>?52=H!$nNP7UZAl!Em{U$;2qkN3BpHdf-~rK&wzU+=f9|gPQqC zOvNuS3m@TL?8lGk*n~dZ-}pqjIt)QAX(Vcd*{BuBN8Mm7cEo9@N8!L2Jc=5~CG3n1 zsDb>B{`eHN65h<>6b7Rva1kAyD11plH~bCN(L>adJw=`9#`>!RFIISu_ zM^J~F`59YpL=C(d)!&b({_mj9yC2Q^tHGn4>qh=qN?cN6oAbwaGrm zd3Y7oPX^1O>#|Yj=c3vRY<&vq5f-7YuZ&^+HS)bQX!9LL`Z6a_Grxkmpb@nKcTgSw zW$XW;5G6XfV_Sg+mPJgTs_SiuO1uf+nT!Ia#84TvGy5TTXJs-8y z1-5;NBuR%V+(46j+hRv3p$}rj7N>MFKT8ZF#{)H zEbc%(l6us@E~Bn%M0NNb>eF%C*6*RtyN?>!L+evyZ#YaF)HUwPpb>XQ?SVwp4AW2@ zQ) zC}_m@FczbGxMny2wGz3uJ`uGdvr(J2%>KR^wO49!J|45}?HF}$>OC{qF7+3Q zqp<_}VM(IH^@Una17Ar~iTY;OqDFoV>C-$zbd0eA_|ViRg*Dw13?IE1?J6Vzrpk2?Q*>s?eokF5V7+sSya z?rQIjy1ozQVSn51D5t06fobUfs3cB$u)Cd=%PFQ8zH(&_$-N;ih$58{hfm*qH zw(i;2HLyrz(k2VF^b4$IsQWljk9HIKvj5Cc3WM=9>b3YA^(aDkdDTGzs-BD*SRQHs zW9;uUQ3Efs^)jmiy=kwo^(~k~y&BcuO?1BhE%t;bsMp3V-Sv#JP)j%#b>kJN8?Qsn zV3+l@ZNG|ol($d=Z^PG2*ZEq*Q0*Ns9=rAD`D@dRpn-dvIjE&OfPr`o`3RaHQ0I9K zaBaG-sAoD9=V1w|!&|6+JThD#sDbE4eG{2N^yU{b#D6g1kzG5zV+a(T8Xb zSwnP$6ZVdCFQ~3#4AGKm)*9$I(t6CIP}5q$EhK>~C2L7E8LQ{NjA$0SiH`SNIA^$( z@{6`E4w1nsaOe@}m`TcsKbb_d0y<8R@y?R#KOz-(kO@Q^cLve@&1?F@QAYG?by3A0 zdVZ}(I|}c%R`5exejAsQ^|r2E&P!oFAllH`B$K>Bbi6}$66eft2+^Vaq@$8}5q;ct>8xoKj+4zKp9~@| z6F<^=9H8(D$s^NkV=V5oWp@l9M{IpH_9C^mUV@8Bl&w#-{y)kEwqXS(6Cc8+cOJRc zw{V0lcd~k5x-AdImq-xVVB4~=4_QSPk_@6_n+xM;Lt!*2whfDLHt{6;iC#y&^OK2= zC1f}`M%Iy3!uQJX<~cvxU<%1zvWLtkFOV=YkQ^ovWFj$snLDp1J+5zw2n_X0?VU0p VHYF`1HND=yM@P5%>q!@){sZeERr3G< delta 7136 zcmb`J3vg7`8OJY5QL$3rh*CHN1*MPxidGFlQM6TBAGM|Us+ZlH?3T^muzQm2uoO|K5{K1f<&W%zXPh z@AJLRx%=bb!Ox8eUp{qgyF=+kCL^og4vAkC*c|JpdEk7j{gzPaGcN?H_36%W5OKB+0IAcad0{0 zT*rl+>trBnoE&^NTnA5p55ePMmu>HYC((WaYTnoE`1hdp{~5d%z6ej^d}rF(j&lJW z6>uJ04ljbOP!>H0PlZ2+6X9!6p85|Q3n!nGC^`-5!1LjI-~uSim%#b(Hn<4h4?E!t z@Dk2upCD)IpEJ0Pcob@LuCMC&TGb3SI84W{BRX+94s`7Vd#hjSH_LU%wdSOMjM5NhKMw*4^F z`W=>gpbXmw&xPNG%Ar3%<SA2hPA;yy`4~8eaxw@$FFSb5J?b4CTp(Z2R+YtnUApsYs)}P#W)t%7Je} zS@;8}4PJtr>AVi*!qd?rPDaQ@a27-5j0?G0&Szjb?1WkP9os*jJNrJ`H^S+h@2sY> zevIRE!`>$QjOwyk6pbsQ%@! z60V0A!*4*n7yp1E=Q(FANIWpvaypdev!H>?p)CD0lt(t$b|;i)df@4Bza4)8Dra7S zb@2DL|5i?)LwhBh3p+2y|8m9m=#UHl3Z>Z@m*5&W7b>>5Kq=Z_`B^xTb_5(+y32_PryIY|I~2*<;nafpyvN9q#{?n3U%=7P#gaX%Cch#+>`JW zcs4u;PlmsRTK8wDWcxSN{Btl~5%y9)$`;A5@4ATK*JjqnF_M@DG*~uSmX%S6F5siS9gL+uwo;;SgM{`+wGz zi6!k&3haP7_%SF~?z7`xxBcITirufEZpk2&B9j&;o|^^Lz6MIMWpEyBhT4Cx<$m~X z&Uc=pqS*cr%B8=9SHM5PDeyv4TA{cZYJ*SO_A03P8=(%|Y{z#(DgGtf-f#IFJdXZ@ zw*4a*E}`QWRCK^(UKt&5k>%x(use&PLeva-qns^J2Ooes_}fqh{L=DIw*MqHKbQU~ zQ2Sm5weC90TbAH|4ctk`4Cp~6O&jDXaP~mC?l(|x{i)X^U$<#c^R9&krlDfG4%Wgx zI0H_(HrZzh)C08|j)C_fcW7MkUrA**au=eq3Aq=!4Pky9L&doa$s#R?${C0fOW*Zl z5S5Q0oE?9HrNBp#;ZjXy`)~t(4wWX>$sCJL7 z-wi*5Y_x49;{xO{WGb>4$su3R{a1MuxhbwCuh|^xbCHLUk0Hk+^@xfR@k_`K44i3 zm)g3qd>7NGGxiS%jR9RLcK5sho){N1XAMnfX;q{Th?aS5{CR zUs+c*-cXkN^(N)!O{Hs6Ik#9`Shga_8)QzgK9%x{#WIsAc-0Hba(+!VUseU;J=`i2 z_@>FPFPLIB@Z!DPeA*NPw-A_ACUo=6XKqk5DL)^0`Jh;C%m=Hr#$?wX^KQteJe@eR;2Bh}@zQ8NJMLkdqUV_)=@$|l@VY|I9!49kPY(8DfmDJRG=4Oy>I zbTFGY!As(=Zk6FYNLt2RlSNm*J}?=1z*p>&WwNnguJ?l=JF{ql zY>-23=GMCTCgWojiQqAquPK`PVm6f7v7y5|7qhi>tO>kN1*SSnjnKw>#*JcAG4N{3 zM^?mP9A0{;l7}a2HecA5NU#O|@@)bEZ z@QOi2+G`-}br?`FY;+v(@=Q>h8|6%^)UZF3q`(XHZf=wUoL^btCgIVF5ql~t2-cg1 z?a-)QnNH8~^Tngb>`IfbuU$a^{A%MS3rzTi#Kh+wu~M#A*XL8pV>g!#2wZ$0ZCvA< z{0@a6_Wz+FsB5arme(}}89yI8U+qQbU3jG%YqYRP;-@k*+}gU!1FulKFpk*ao==i+ z*wRrxAMJ9T|9|@YYClz9%hik{dvwn)^Ydx^-k5O%Q|t4_=poQ^nht%gbC5}NU1ZEM zP03|bUcN}?bG_Umy*HDMBQJu|L?y{3y@bi0%I!jJl8YnOj@YIZN4Ov1A+Pyi7ewpc z*8Mm<5N~kQh-R@u6=g@seR~D=-8JEa)5g!6Gk>0$Kku>y7w;=hn=&TksVaS;Yw+oT zXxqRWKUIg{`Xhg$MlE-%hT1!#M|w^4%tP1MyAno`To(*Bmgy=~F9{%9Tg?QG$|meTeP zGqklWTD#HeuK}i*(!Ty^`#P(?!Eu9o*G63jIKvEev_#u`nctzkdYVTY-%;AM#V~bn zpDf!`>Kz#D+ZOE&J4!p~*w=4|O>|E$i?uT9ZW-$AIHKFEd80lOiDQa(%?{RUIF_C5(e_}~ebjSZvl)D9&EUWW zJ)Esy=2;os-^Q0p&%o9_gHLg>nn$+ij~?yT*ca(yuaV1A>ekD+`Rwv3qpq|5mnY#m t;s@((#OSU4pNqd&*A1T-bF{ZcbkWG0-v_RPjmYM^Cj4{X#_0#o{4d{WBQgL0 diff --git a/kotti/locale/ko/LC_MESSAGES/Kotti.po b/kotti/locale/ko/LC_MESSAGES/Kotti.po index f713edaad..bb4c27e7e 100644 --- a/kotti/locale/ko/LC_MESSAGES/Kotti.po +++ b/kotti/locale/ko/LC_MESSAGES/Kotti.po @@ -7,7 +7,7 @@ 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" +"POT-Creation-Date: 2014-12-19 14:27+0100\n" "PO-Revision-Date: 2014-10-19 08:22+0900\n" "Last-Translator: mete0r \n" "Language-Team: Korean\n" @@ -27,7 +27,7 @@ msgid "Congratulations! You have successfully installed Kotti." msgstr "축하합니다! 성공적으로 Kotti를 설치했습니다." #: kotti/populate.py:66 -#, c-format +#, fuzzy, c-format msgid "" "\n" "

Log in

\n" @@ -54,7 +54,7 @@ msgid "" "

\n" " \n" +"basic/configuration.html\">\n" " Configuration manual\n" " \n" "

\n" @@ -218,77 +218,77 @@ msgstr "" " article.\n" "

\n" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 +#: kotti/resources.py:606 kotti/views/edit/content.py:81 msgid "Document" msgstr "문서" -#: kotti/resources.py:649 kotti/views/edit/content.py:113 +#: kotti/resources.py:645 kotti/views/edit/content.py:113 #: kotti/views/edit/content.py:62 msgid "File" msgstr "파일" -#: kotti/resources.py:726 kotti/views/edit/content.py:144 +#: kotti/resources.py:697 kotti/views/edit/content.py:144 msgid "Image" msgstr "화상" -#: kotti/resources.py:500 +#: kotti/resources.py:496 msgid "Folder view" msgstr "폴더로 보임" -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "컨텐츠 항목" -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "편집" -#: kotti/resources.py:489 +#: kotti/resources.py:485 msgid "Share" msgstr "공유" -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "동작" -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 +#: kotti/resources.py:487 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "복사" -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 +#: kotti/resources.py:488 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "잘라내기" -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 +#: kotti/resources.py:489 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "붙여넣기" -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:490 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 +#: kotti/views/edit/actions.py:448 msgid "Rename" msgstr "이름 바꾸기" -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:491 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 +#: kotti/views/edit/actions.py:450 msgid "Delete" msgstr "삭제" -#: kotti/security.py:165 +#: kotti/security.py:189 msgid "Viewer" msgstr "열람자" -#: kotti/security.py:166 +#: kotti/security.py:190 msgid "Editor" msgstr "편집자" -#: kotti/security.py:167 +#: kotti/security.py:191 msgid "Owner" msgstr "소유자" -#: kotti/security.py:168 +#: kotti/security.py:192 msgid "Admin" msgstr "관리자" @@ -361,7 +361,7 @@ msgstr "로그인" msgid "Username or email" msgstr "사용자 이름 혹은 전자메일" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 +#: kotti/templates/login.pt:34 kotti/views/login.py:212 #: kotti/views/users.py:212 msgid "Password" msgstr "암호" @@ -675,7 +675,7 @@ 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/edit/actions.py:364 kotti/views/edit/actions.py:408 msgid "Your changes have been saved." msgstr "수정사항이 저장되었습니다." @@ -702,27 +702,27 @@ msgstr "${type}을(를) \"${title}\"에 추가합니다." msgid "Add ${type}." msgstr "${type}을(를) 추가합니다." -#: kotti/views/login.py:238 +#: kotti/views/login.py:237 msgid "You have reset your password." msgstr "암호를 재설정했습니다." -#: kotti/views/login.py:200 +#: kotti/views/login.py:199 msgid "You have been logged out." msgstr "로그아웃되었습니다." -#: kotti/views/login.py:66 kotti/views/users.py:270 +#: kotti/views/login.py:65 kotti/views/users.py:270 msgid "Full name" msgstr "전체 이름" -#: kotti/views/login.py:69 +#: kotti/views/login.py:68 msgid "Username" msgstr "사용자 이름" -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 +#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 msgid "Email" msgstr "전자메일" -#: kotti/views/login.py:109 +#: kotti/views/login.py:108 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." @@ -730,49 +730,49 @@ msgstr "" "축하합니다! 성공적으로 등록되었습니다. 전자메일로 암호를 설정하는 전자메일을 " "받게 됩니다. 암호 설정 후 계정이 활성화됩니다." -#: kotti/views/login.py:124 +#: kotti/views/login.py:123 #, python-format msgid "Register - ${title}" msgstr "등록 - ${title}" -#: kotti/views/login.py:164 +#: kotti/views/login.py:163 msgid "Login failed." msgstr "로그인이 실패했습니다." -#: kotti/views/login.py:286 +#: kotti/views/login.py:285 #, python-format msgid "Reset your password - ${title}." msgstr "암호를 재설정하세요 - ${title}." -#: kotti/views/login.py:160 +#: kotti/views/login.py:159 #, python-format msgid "Welcome, ${user}!" msgstr "${user}님, 환영합니다!" -#: kotti/views/login.py:173 +#: kotti/views/login.py:172 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 +#: kotti/views/login.py:177 msgid "That username or email is not known by this system." msgstr "이 시스템에 없는 사용자 이름이나 전자메일입니다." -#: kotti/views/login.py:83 +#: kotti/views/login.py:82 msgid "Register" msgstr "등록하기" -#: kotti/views/login.py:90 kotti/views/login.py:257 +#: kotti/views/login.py:89 kotti/views/login.py:256 msgid "There was an error." msgstr "오류가 있습니다." -#: kotti/views/login.py:250 +#: kotti/views/login.py:249 msgid "Submit" msgstr "제출" -#: kotti/views/login.py:279 +#: kotti/views/login.py:278 msgid "Your password reset token may have expired." msgstr "암호 재설정 토큰이 만료된 것 같습니다." @@ -840,8 +840,8 @@ 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 +#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 +#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 msgid "No changes were made." msgstr "아무 것도 바뀌지 않았습니다." @@ -886,68 +886,68 @@ msgstr "\"${title}\"이(가) 복사되었습니다." msgid "${title} was cut." msgstr "\"${title}\"이(가) 잘라내어졌습니다." -#: kotti/views/edit/actions.py:182 +#: kotti/views/edit/actions.py:183 #, python-format msgid "${title} was moved." msgstr "\"${title}\"이(가) 옮겨졌습니다." -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 #, python-format msgid "${title} was deleted." msgstr "\"${title}\"이(가) 지워졌습니다." -#: kotti/views/edit/actions.py:158 +#: kotti/views/edit/actions.py:159 #, python-format msgid "${title} was pasted." msgstr "\"${title}\"이(가) 붙여넣어졌습니다." -#: kotti/views/edit/actions.py:161 +#: kotti/views/edit/actions.py:162 msgid "Could not paste node. It no longer exists." msgstr "노드를 붙여넣을 수 없습니다. 그 노드가 존재하지 않습니다." -#: kotti/views/edit/actions.py:224 +#: kotti/views/edit/actions.py:225 #, python-format msgid "${title} is now visible in the navigation." msgstr "\"${title}\"이(가) 이제 둘러보기에 나타납니다." -#: kotti/views/edit/actions.py:227 +#: kotti/views/edit/actions.py:228 #, python-format msgid "${title} is no longer visible in the navigation." msgstr "\"${title}\"이(가) 이젠 둘러보기에 나타나지 않습니다." -#: kotti/views/edit/actions.py:292 +#: kotti/views/edit/actions.py:293 msgid "Nothing was deleted." msgstr "아무것도 지워지지 않았습니다." -#: kotti/views/edit/actions.py:328 kotti/views/edit/actions.py:355 +#: kotti/views/edit/actions.py:329 kotti/views/edit/actions.py:356 msgid "Name and title are required." msgstr "이름과 제목이 필요합니다." -#: kotti/views/edit/actions.py:332 +#: kotti/views/edit/actions.py:333 msgid "Item was renamed." msgstr "항목이 재명명되었습니다." -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:455 msgid "Move up" msgstr "위로 옮김" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:456 msgid "Move down" msgstr "아래로 옮김" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:457 msgid "Show" msgstr "보이기" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:458 msgid "Hide" msgstr "숨기기" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:497 msgid "You have to select items to perform an action." msgstr "적용할 항목을 선택해야합니다." -#: kotti/views/edit/actions.py:453 +#: kotti/views/edit/actions.py:454 msgid "Change State" msgstr "작업 상태 바꾸기" diff --git a/kotti/locale/nl/LC_MESSAGES/Kotti.mo b/kotti/locale/nl/LC_MESSAGES/Kotti.mo index cffeb201e6f1d5a9b42055da499c9e605b8f9a54..97bf2fdcb4a11740a2bbb0218fbe310694f75dd2 100644 GIT binary patch delta 23 ecmeww{xy8VWhpKrT|-L+LlY|_^UZgqBm@C+UI+vL delta 23 ecmeww{xy8VWhpL0T_ZyUBSR}wv(0y\n" "Language-Team: nl \n" @@ -54,7 +54,7 @@ msgid "" "

\n" " \n" +"basic/configuration.html\">\n" " Configuration manual\n" " \n" "

\n" @@ -133,77 +133,77 @@ msgid "" "

\n" msgstr "" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 +#: kotti/resources.py:606 kotti/views/edit/content.py:81 msgid "Document" msgstr "Document" -#: kotti/resources.py:649 kotti/views/edit/content.py:113 +#: kotti/resources.py:645 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 +#: kotti/resources.py:697 kotti/views/edit/content.py:144 msgid "Image" msgstr "Afbeelding" -#: kotti/resources.py:500 +#: kotti/resources.py:496 msgid "Folder view" msgstr "Map weergave" -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "Inhoud" -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "Bewerken" -#: kotti/resources.py:489 +#: kotti/resources.py:485 msgid "Share" msgstr "Delen" -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "Acties" -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 +#: kotti/resources.py:487 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "Kopiëren" -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 +#: kotti/resources.py:488 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "Knippen" -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 +#: kotti/resources.py:489 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "Plakken" -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:490 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 +#: kotti/views/edit/actions.py:448 msgid "Rename" msgstr "Hernoemen" -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:491 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 +#: kotti/views/edit/actions.py:450 msgid "Delete" msgstr "Verwijderen" -#: kotti/security.py:165 +#: kotti/security.py:189 msgid "Viewer" msgstr "Lezer" -#: kotti/security.py:166 +#: kotti/security.py:190 msgid "Editor" msgstr "Editor" -#: kotti/security.py:167 +#: kotti/security.py:191 msgid "Owner" msgstr "Eigenaar" -#: kotti/security.py:168 +#: kotti/security.py:192 msgid "Admin" msgstr "Beheerder" @@ -279,7 +279,7 @@ msgstr "Aanmelden" msgid "Username or email" msgstr "Gebuikersnaam of e-mail" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 +#: kotti/templates/login.pt:34 kotti/views/login.py:212 #: kotti/views/users.py:212 msgid "Password" msgstr "Wachtwoord" @@ -607,7 +607,7 @@ 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/edit/actions.py:364 kotti/views/edit/actions.py:408 msgid "Your changes have been saved." msgstr "Je wijzigingen werden opgeslagen." @@ -634,27 +634,27 @@ msgstr "Voeg ${type} aan ${title} toe." msgid "Add ${type}." msgstr "Voeg ${type} toe." -#: kotti/views/login.py:238 +#: kotti/views/login.py:237 msgid "You have reset your password." msgstr "Je wachtwoord werd hersteld." -#: kotti/views/login.py:200 +#: kotti/views/login.py:199 msgid "You have been logged out." msgstr "Je bent afgemeld." -#: kotti/views/login.py:66 kotti/views/users.py:270 +#: kotti/views/login.py:65 kotti/views/users.py:270 msgid "Full name" msgstr "Volledige naam" -#: kotti/views/login.py:69 +#: kotti/views/login.py:68 msgid "Username" msgstr "Gebruikersnaam" -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 +#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 msgid "Email" msgstr "E-mailadres" -#: kotti/views/login.py:109 +#: kotti/views/login.py:108 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." @@ -662,26 +662,26 @@ 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 +#: kotti/views/login.py:123 #, python-format msgid "Register - ${title}" msgstr "Refistreer - ${title}" -#: kotti/views/login.py:164 +#: kotti/views/login.py:163 msgid "Login failed." msgstr "Aanmelden mislukt." -#: kotti/views/login.py:286 +#: kotti/views/login.py:285 #, python-format msgid "Reset your password - ${title}." msgstr "Herstel je wachtwoord - ${title}." -#: kotti/views/login.py:160 +#: kotti/views/login.py:159 #, python-format msgid "Welcome, ${user}!" msgstr "Welkom, ${user}!" -#: kotti/views/login.py:173 +#: kotti/views/login.py:172 msgid "" "You should be receiving an email with a link to reset your password. Doing " "so will activate your account." @@ -689,23 +689,23 @@ 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 +#: kotti/views/login.py:177 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 +#: kotti/views/login.py:82 msgid "Register" msgstr "Registreer" -#: kotti/views/login.py:90 kotti/views/login.py:257 +#: kotti/views/login.py:89 kotti/views/login.py:256 msgid "There was an error." msgstr "Er trad een fout op." -#: kotti/views/login.py:250 +#: kotti/views/login.py:249 msgid "Submit" msgstr "Verzenden" -#: kotti/views/login.py:279 +#: kotti/views/login.py:278 msgid "Your password reset token may have expired." msgstr "Je wachtwoord herstellen link is mogelijks vervallen." @@ -773,8 +773,8 @@ 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 +#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 +#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 msgid "No changes were made." msgstr "Geen wijzigingen aangebracht." @@ -819,68 +819,68 @@ msgstr "${title} werd gekopieerd." msgid "${title} was cut." msgstr "${title} werd geknipt." -#: kotti/views/edit/actions.py:182 +#: kotti/views/edit/actions.py:183 #, python-format msgid "${title} was moved." msgstr "${title} werd verplaatst." -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 #, python-format msgid "${title} was deleted." msgstr "${title} werd verwijderd." -#: kotti/views/edit/actions.py:158 +#: kotti/views/edit/actions.py:159 #, python-format msgid "${title} was pasted." msgstr "${title} werd geplakt." -#: kotti/views/edit/actions.py:161 +#: kotti/views/edit/actions.py:162 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:225 #, 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:228 #, 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:293 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:329 kotti/views/edit/actions.py:356 msgid "Name and title are required." msgstr "Naam en titel zijn vereist." -#: kotti/views/edit/actions.py:332 +#: kotti/views/edit/actions.py:333 msgid "Item was renamed." msgstr "Item werd hernoemd." -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:455 msgid "Move up" msgstr "Naar boven" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:456 msgid "Move down" msgstr "Naar beneden" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:457 msgid "Show" msgstr "Weergeven" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:458 msgid "Hide" msgstr "Verbergen" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:497 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 +#: kotti/views/edit/actions.py:454 msgid "Change State" msgstr "Wijzig Status." diff --git a/kotti/locale/pt/LC_MESSAGES/Kotti.mo b/kotti/locale/pt/LC_MESSAGES/Kotti.mo index eaf4bd6fef6d225aaf516cf76de2b409fb2af071..c3bc8089aecefd92cbea2014517a0c3e07be6687 100644 GIT binary patch delta 23 ecmbPlGv8*zVIeLfT|-L+LlY|_^UY_4g17-)mIo#P delta 23 ecmbPlGv8*zVIeL\n" "Language-Team: pt \n" @@ -54,7 +54,7 @@ msgid "" "

\n" " \n" +"basic/configuration.html\">\n" " Configuration manual\n" " \n" "

\n" @@ -133,77 +133,77 @@ msgid "" "

\n" msgstr "" -#: kotti/resources.py:610 kotti/views/edit/content.py:81 +#: kotti/resources.py:606 kotti/views/edit/content.py:81 msgid "Document" msgstr "Documento" -#: kotti/resources.py:649 kotti/views/edit/content.py:113 +#: kotti/resources.py:645 kotti/views/edit/content.py:113 #: kotti/views/edit/content.py:62 msgid "File" msgstr "Ficheiro" -#: kotti/resources.py:726 kotti/views/edit/content.py:144 +#: kotti/resources.py:697 kotti/views/edit/content.py:144 msgid "Image" msgstr "Imagem" -#: kotti/resources.py:500 +#: kotti/resources.py:496 msgid "Folder view" msgstr "Vista de pasta" -#: kotti/resources.py:487 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "Conteúdo" -#: kotti/resources.py:488 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "Editar" -#: kotti/resources.py:489 +#: kotti/resources.py:485 msgid "Share" msgstr "Partilha" -#: kotti/resources.py:490 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "Ações" -#: kotti/resources.py:491 kotti/views/edit/actions.py:445 +#: kotti/resources.py:487 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "Copiar" -#: kotti/resources.py:492 kotti/views/edit/actions.py:446 +#: kotti/resources.py:488 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "Cortar" -#: kotti/resources.py:493 kotti/views/edit/actions.py:442 +#: kotti/resources.py:489 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "Colar" -#: kotti/resources.py:494 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:490 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 +#: kotti/views/edit/actions.py:448 msgid "Rename" msgstr "Renomear" -#: kotti/resources.py:495 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:491 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 +#: kotti/views/edit/actions.py:450 msgid "Delete" msgstr "Apagar" -#: kotti/security.py:165 +#: kotti/security.py:189 msgid "Viewer" msgstr "Leitor" -#: kotti/security.py:166 +#: kotti/security.py:190 msgid "Editor" msgstr "Editor" -#: kotti/security.py:167 +#: kotti/security.py:191 msgid "Owner" msgstr "Dono" -#: kotti/security.py:168 +#: kotti/security.py:192 msgid "Admin" msgstr "Admin" @@ -280,7 +280,7 @@ msgstr "Entrar" msgid "Username or email" msgstr "Utilizador ou e-mail" -#: kotti/templates/login.pt:34 kotti/views/login.py:213 +#: kotti/templates/login.pt:34 kotti/views/login.py:212 #: kotti/views/users.py:212 msgid "Password" msgstr "Palavra passe" @@ -610,7 +610,7 @@ 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/edit/actions.py:364 kotti/views/edit/actions.py:408 msgid "Your changes have been saved." msgstr "As suas alterações foram guardadas." @@ -638,29 +638,29 @@ msgstr "Adicionar ${type} em ${title}" msgid "Add ${type}." msgstr "Adicionar ${type}" -#: kotti/views/login.py:238 +#: kotti/views/login.py:237 #, fuzzy msgid "You have reset your password." msgstr "Você reinicializou a palavra passe com sucesso" -#: kotti/views/login.py:200 +#: kotti/views/login.py:199 msgid "You have been logged out." msgstr "Você desligou a sessão" -#: kotti/views/login.py:66 kotti/views/users.py:270 +#: kotti/views/login.py:65 kotti/views/users.py:270 #, fuzzy msgid "Full name" msgstr "Nome completo" -#: kotti/views/login.py:69 +#: kotti/views/login.py:68 msgid "Username" msgstr "Nome utilizador" -#: kotti/views/login.py:74 kotti/views/login.py:225 kotti/views/users.py:199 +#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 msgid "Email" msgstr "E-mail" -#: kotti/views/login.py:109 +#: kotti/views/login.py:108 #, fuzzy msgid "" "Congratulations! You are successfully registered. You should be receiving an " @@ -669,26 +669,26 @@ 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 +#: kotti/views/login.py:123 #, fuzzy, python-format msgid "Register - ${title}" msgstr "Registar ${title}" -#: kotti/views/login.py:164 +#: kotti/views/login.py:163 msgid "Login failed." msgstr "Erro de autenticação" -#: kotti/views/login.py:286 +#: kotti/views/login.py:285 #, fuzzy, python-format msgid "Reset your password - ${title}." msgstr "Reiniciar a sua palavra passe - ${title}" -#: kotti/views/login.py:160 +#: kotti/views/login.py:159 #, python-format msgid "Welcome, ${user}!" msgstr "Bem-vindo, ${user}!" -#: kotti/views/login.py:173 +#: kotti/views/login.py:172 #, fuzzy msgid "" "You should be receiving an email with a link to reset your password. Doing " @@ -697,24 +697,24 @@ msgstr "" "Você deverá receber um e-mail com uma ligação temporária para reiniciar a " "sua palavra passe." -#: kotti/views/login.py:178 +#: kotti/views/login.py:177 #, 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 +#: kotti/views/login.py:82 msgid "Register" msgstr "Registar-se" -#: kotti/views/login.py:90 kotti/views/login.py:257 +#: kotti/views/login.py:89 kotti/views/login.py:256 msgid "There was an error." msgstr "Ocorreu um erro." -#: kotti/views/login.py:250 +#: kotti/views/login.py:249 msgid "Submit" msgstr "Enviar" -#: kotti/views/login.py:279 +#: kotti/views/login.py:278 msgid "Your password reset token may have expired." msgstr "A seu pedido para alterar a palavra passe expirou." @@ -782,8 +782,8 @@ 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 +#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 +#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 #, fuzzy msgid "No changes were made." msgstr "Não foram efectuadas nenhumas alterações" @@ -833,72 +833,72 @@ msgstr "${title} copiado." msgid "${title} was cut." msgstr "${title} cortado." -#: kotti/views/edit/actions.py:182 +#: kotti/views/edit/actions.py:183 #, fuzzy, python-format msgid "${title} was moved." msgstr "${title} movido." -#: kotti/views/edit/actions.py:270 kotti/views/edit/actions.py:295 +#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 #, fuzzy, python-format msgid "${title} was deleted." msgstr "${title} apagado." -#: kotti/views/edit/actions.py:158 +#: kotti/views/edit/actions.py:159 #, fuzzy, python-format msgid "${title} was pasted." msgstr "${title} colado." -#: kotti/views/edit/actions.py:161 +#: kotti/views/edit/actions.py:162 #, 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:225 #, 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:228 #, 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:293 #, 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:329 kotti/views/edit/actions.py:356 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:333 #, fuzzy msgid "Item was renamed." msgstr "Item renomeado" -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:455 msgid "Move up" msgstr "Mover para cima" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:456 msgid "Move down" msgstr "Mover para baixo" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:457 msgid "Show" msgstr "Mostrar" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:458 msgid "Hide" msgstr "Esconder" -#: kotti/views/edit/actions.py:496 +#: kotti/views/edit/actions.py:497 #, 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 +#: kotti/views/edit/actions.py:454 msgid "Change State" msgstr "Alterar Estado" diff --git a/kotti/populate.py b/kotti/populate.py index 94a19ec35..a8807698e 100644 --- a/kotti/populate.py +++ b/kotti/populate.py @@ -84,7 +84,7 @@ def populate():

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

From 691907007cbf75c96074f8090a049e341721d26b Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Sat, 20 Dec 2014 00:56:53 +0100 Subject: [PATCH 008/600] Update docstrings to refer to :class:`kotti.request.Request` instead of its base class from Pyramid. [ci skip] --- kotti/events.py | 2 +- kotti/message.py | 2 +- kotti/resources.py | 10 +++++----- .../scaffolds/package/+package+/views/__init__.py_tmpl | 2 +- kotti/security.py | 2 +- kotti/views/edit/upload.py | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/kotti/events.py b/kotti/events.py index 877c3b0f3..380df4468 100644 --- a/kotti/events.py +++ b/kotti/events.py @@ -54,7 +54,7 @@ def __init__(self, object, request=None): :type object: arbitrary :param request: current request - :type request: :class:`pyramid.request.Request` + :type request: :class:`kotti.request.Request` """ self.object = object diff --git a/kotti/message.py b/kotti/message.py index a4f2dcc2f..d9015497a 100644 --- a/kotti/message.py +++ b/kotti/message.py @@ -79,7 +79,7 @@ def send_email(request, recipients, template_name, template_vars={}): """ 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" '. diff --git a/kotti/resources.py b/kotti/resources.py index 25786b06e..4a2db5cc6 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -141,7 +141,7 @@ def children_with_permission(self, request, permission='view'): 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 @@ -353,7 +353,7 @@ 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. @@ -665,7 +665,7 @@ def from_field_storage(cls, fs): 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. @@ -707,7 +707,7 @@ def get_root(request=None): 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; @@ -719,7 +719,7 @@ def default_get_root(request=None): """Default implementation for :func:`~kotti.resources.get_root`. :param request: Current request (optional) - :type request: :class:`pyramid.request.Request` + :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 diff --git a/kotti/scaffolds/package/+package+/views/__init__.py_tmpl b/kotti/scaffolds/package/+package+/views/__init__.py_tmpl index a95087b4b..4e050c322 100644 --- a/kotti/scaffolds/package/+package+/views/__init__.py_tmpl +++ b/kotti/scaffolds/package/+package+/views/__init__.py_tmpl @@ -16,7 +16,7 @@ class BaseView(object): :type context: :class:`kotti.resources.Content` :param request: Current request - :type request: :class:`pyramid.request.Request` + :type request: :class:`kotti.request.Request` """ super(BaseView, self).__init__() diff --git a/kotti/security.py b/kotti/security.py index 27f3041d0..3b39f4fd8 100644 --- a/kotti/security.py +++ b/kotti/security.py @@ -48,7 +48,7 @@ def has_permission(permission, context, request): :type context: :class:``kotti.resources.Node`` :param request: current request - :type request: :class:`pyramid.request.Request` + :type request: :class:`kotti.request.Request` :result: ``True`` if request has the permission, ``False`` else :rtype: bool diff --git a/kotti/views/edit/upload.py b/kotti/views/edit/upload.py index 0c62983ad..06d313ffe 100644 --- a/kotti/views/edit/upload.py +++ b/kotti/views/edit/upload.py @@ -32,7 +32,7 @@ def __init__(self, context, request): :type context: :class:`kotti.resources.Content` or descendants. :param request: Current request. - :type request: :class:`pyramid.request.Request` + :type request: :class:`kotti.request.Request` """ self.context = context From 757dece7b5c79ff81bb6c4771f075de74348abb2 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Sat, 20 Dec 2014 00:57:53 +0100 Subject: [PATCH 009/600] Reorder imports. [ci skip] --- kotti/views/file.py | 4 +++- kotti/views/form.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kotti/views/file.py b/kotti/views/file.py index 5ac25f258..1c81e627d 100644 --- a/kotti/views/file.py +++ b/kotti/views/file.py @@ -1,8 +1,10 @@ -from kotti.resources import File +# -*- coding: utf-8 -*- from pyramid.response import Response from pyramid.view import view_config +from kotti.resources import File + @view_config(name='view', context=File, permission='view', renderer='kotti:templates/view/file.pt') diff --git a/kotti/views/form.py b/kotti/views/form.py index f67a04159..0e1984405 100644 --- a/kotti/views/form.py +++ b/kotti/views/form.py @@ -21,8 +21,8 @@ from kotti.fanstatic import tagit from kotti.resources import Tag from kotti.util import _ -from kotti.util import translate from kotti.util import title_to_name +from kotti.util import translate def get_appstruct(context, schema): From d1f142d46762041c3f2bb3aff405ed1f6f08733e Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Sat, 20 Dec 2014 00:58:14 +0100 Subject: [PATCH 010/600] Add a BaseView class for convenience. --- kotti/views/__init__.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/kotti/views/__init__.py b/kotti/views/__init__.py index dcf6617f9..ec5b692c6 100644 --- a/kotti/views/__init__.py +++ b/kotti/views/__init__.py @@ -1,5 +1,27 @@ -from .util import RootOnlyPredicate -from .util import SettingHasValuePredicate +# -*- coding: utf-8 -*- + +from kotti.views.util import RootOnlyPredicate +from kotti.views.util import SettingHasValuePredicate + + +class BaseView(object): + """ Very basic view class that can be subclassed. Does nothing more than + assignment of ``context`` and ``request`` to instance attributes on + initialization. """ + + def __init__(self, context, request): + """ Constructor + + :param context: Context of the view + :type context: :class:`kotti.resources.Node` or descendant for views on + content. + + :param request: Current request object + :type request: :class:`kotti.request.Request` + """ + + self.context = context + self.request = request def includeme(config): From c21ebbf5ccf5f7711a99b731539459dedff836d3 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Sat, 20 Dec 2014 04:09:53 +0100 Subject: [PATCH 011/600] Update some docs, make sure "framework" is the primary description everywhere. [ci skip] --- README.rst | 10 +- docs/developing/basic/configuration.rst | 152 +++++++++--------------- docs/developing/basic/deployment.rst | 24 ++-- docs/first_steps/installation.rst | 52 ++++---- docs/first_steps/overview.rst | 37 +++--- docs/index.rst | 6 +- setup.py | 4 +- 7 files changed, 119 insertions(+), 166 deletions(-) diff --git a/README.rst b/README.rst index 84a7490c4..fa22f2326 100644 --- a/README.rst +++ b/README.rst @@ -2,8 +2,9 @@ Kotti ===== -A user-friendly, light-weight and extensible web content management -system, based on Pyramid and SQLAlchemy. |build status|_ +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). +|build status|_ Kotti is most useful when you are developing applications that @@ -24,10 +25,11 @@ developer. 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: +Plone_. +Its **main features** are: - **User-friendliness**: editors can edit content where it appears; thus the edit interface is contextual and intuitive diff --git a/docs/developing/basic/configuration.rst b/docs/developing/basic/configuration.rst index f56a73314..246854b61 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,9 +34,8 @@ 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. +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 @@ -80,23 +78,21 @@ 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. +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. +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. 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 +108,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 +124,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 +137,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 +171,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 +180,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 +207,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 +236,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 +262,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 behavour 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: @@ -345,8 +312,7 @@ 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..46f0c6bcf 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: @@ -113,20 +113,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 +134,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/first_steps/installation.rst b/docs/first_steps/installation.rst index 0e58643fe..9a7816769 100644 --- a/docs/first_steps/installation.rst +++ b/docs/first_steps/installation.rst @@ -6,10 +6,11 @@ Installation Requirements ------------ -- Python 2.6 or 2.7 +- Python 2.6 or 2.7 (Python 3 support will land in future versions) - virtualenv_ -- ``build_essential`` and ``python-dev`` (on Debian or Ubuntu) -- or ``Xcode`` (on OSX) +- ``build_essential`` and ``python-dev`` (on Debian or Ubuntu) or +- ``Xcode`` (on OS X) or +- equivalent build toolchain for your OS. Installation using ``virtualenv`` --------------------------------- @@ -20,24 +21,21 @@ It is recommended to install Kotti inside a virtualenv: virtualenv 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 -r https://raw.github.com/Kotti/Kotti/stable/requirements.txt + bin/pip install [--pre] 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,23 +46,19 @@ 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. - -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. +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. +`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 diff --git a/docs/first_steps/overview.rst b/docs/first_steps/overview.rst index 17ecee00f..011b984f6 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/ @@ -22,8 +19,8 @@ Features 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 @@ -53,46 +50,40 @@ 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|_ Support ------- -- Python 2.6 or 2.7 -- Support for PostgreSQL, MySQL and SQLite (tested regularly), and a - list of `other SQL databases`_ +- Python 2.6 or 2.7 (Python 3 coming soon) +- 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/ .. _Colander: http://docs.pylonsproject.org/projects/colander/en/latest/ diff --git a/docs/index.rst b/docs/index.rst index a3bb51af5..23530f631 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 ----------- diff --git a/setup.py b/setup.py index afa8a9eea..e230331c3 100644 --- a/setup.py +++ b/setup.py @@ -86,8 +86,8 @@ install_requires.append('ordereddict') setup(name='Kotti', - version='0.10b2dev', - description="A user-friendly, light-weight and extensible web content management system. Based on Pyramid and SQLAlchemy.", + version='1.0.0-alpha', + description="A high-level, Pythonic web application framework based on Pyramid and SQLAlchemy. It includes an extensible Content Management System called the Kotti CMS.", # noqa long_description='\n\n'.join([README, AUTHORS, CHANGES]), classifiers=[ "Programming Language :: Python", From a0da629bec27ab21a740491cbf838465017ecef2 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Sat, 20 Dec 2014 04:19:49 +0100 Subject: [PATCH 012/600] Update deprecation warnings to refer to 1.0.0 instead of 0.10. --- kotti/resources.py | 2 +- kotti/scaffolds/package/+dot+travis.yml_tmpl | 4 ++-- kotti/util.py | 4 ++-- kotti/views/util.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/kotti/resources.py b/kotti/resources.py index 4a2db5cc6..7f789e271 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -318,7 +318,7 @@ class TypeInfo(object): def __init__(self, **kwargs): if 'action_links' in kwargs: - msg = ("'action_links' is deprecated as of Kotti 0.10. " + msg = ("'action_links' is deprecated as of Kotti 1.0.0. " "'edit_links' includes 'action_links' and should " "be used instead.") diff --git a/kotti/scaffolds/package/+dot+travis.yml_tmpl b/kotti/scaffolds/package/+dot+travis.yml_tmpl index 6710de218..d8c49f2d6 100644 --- a/kotti/scaffolds/package/+dot+travis.yml_tmpl +++ b/kotti/scaffolds/package/+dot+travis.yml_tmpl @@ -9,8 +9,8 @@ env: 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 -e . -r https://raw.github.com/Kotti/Kotti/stable/requirements.txt + - pip install --pre Kotti - pip install "wsgi_intercept==0.5.1" - python setup.py devbefore_script: - psql -c 'create database {{package}}_testing;' -U postgres diff --git a/kotti/util.py b/kotti/util.py index e4084c614..93b775f3f 100644 --- a/kotti/util.py +++ b/kotti/util.py @@ -132,7 +132,7 @@ def path(self): # BBB return self.name path = deprecated( path, - "The 'path' attribute has been deprecated as of Kotti 0.10. Please " + "The 'path' attribute has been deprecated as of Kotti 1.0.0. Please " "use 'name' instead.", ) @@ -334,5 +334,5 @@ def command(func, doc): ViewLink = Link deprecated( 'ViewLink', - "kotti.util.ViewLink has been renamed to Link as of Kotti 0.10." + "kotti.util.ViewLink has been renamed to Link as of Kotti 1.0.0." ) diff --git a/kotti/views/util.py b/kotti/views/util.py index b3dc5777b..f7f454cba 100644 --- a/kotti/views/util.py +++ b/kotti/views/util.py @@ -78,7 +78,7 @@ def add_renderer_globals(event): event['api'] = api -@deprecate("'is_root' is deprecated as of Kotti 0.10. " +@deprecate("'is_root' is deprecated as of Kotti 1.0.0. " "Use the 'root_only=True' if you were using this as a " "'custom_predicates' predicate.") def is_root(context, request): @@ -484,4 +484,4 @@ def search_content_for_tags(tags, request=None): deprecated( name, "kotti.views.util.{0} has been moved to the kotti.util module " - "as of Kotti 0.10. Use kotti.util.{0} instead".format(name)) + "as of Kotti 1.0.0. Use kotti.util.{0} instead".format(name)) From 7f7a314ae70c714cc5ad103e563d56aef2effecf Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Sat, 20 Dec 2014 04:20:56 +0100 Subject: [PATCH 013/600] Bump version in changelog. [ci skip] --- CHANGES.txt | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index a700bbea0..505310302 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,8 +1,8 @@ Change History ============== -0.10 - Unreleased ------------------ +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 diff --git a/setup.py b/setup.py index e230331c3..1b1454779 100644 --- a/setup.py +++ b/setup.py @@ -101,7 +101,7 @@ ], author='Kotti developers', author_email='kotti@googlegroups.com', - url='http://kotti.pylonsproject.org', + url='http://kotti.pylonsproject.org/', keywords='kotti web cms wcms pylons pyramid sqlalchemy bootstrap', license="BSD-derived (http://www.repoze.org/LICENSE.txt)", packages=find_packages(), From cc5347dee90269266cdb13f0039acd4f3b885b6c Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Sat, 20 Dec 2014 04:57:50 +0100 Subject: [PATCH 014/600] Display the build status for the stable branch in the README / on PyPI. [ci skip] --- README.rst | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/README.rst b/README.rst index fa22f2326..d0e480e3e 100644 --- a/README.rst +++ b/README.rst @@ -12,12 +12,9 @@ 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| image:: https://secure.travis-ci.org/Kotti/Kotti.png?branch=stable .. _build status: http://travis-ci.org/Kotti/Kotti .. _Pyramid: http://docs.pylonsproject.org/projects/pyramid/dev/ .. _SQLAlchemy: http://www.sqlalchemy.org/ @@ -27,8 +24,7 @@ Kotti CMS 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_. +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; @@ -59,11 +55,9 @@ Its **main features** are: Support and Documentation ========================= -`Click here to access Kotti's full documentation -`_ +`Click here to access Kotti's full documentation `_ License ======= -Kotti is offered under the BSD-derived `Repoze Public License -`_. +Kotti is offered under the BSD-derived `Repoze Public License `_. From a079cfe2abd8e19043e5fe90947b1f8719653c93 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Sat, 20 Dec 2014 05:40:35 +0100 Subject: [PATCH 015/600] Bump version. --- CHANGES.txt | 5 +++++ setup.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 505310302..5cc722709 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,11 @@ Change History ============== +1.1-dev - unreleased +-------------------- + +- No changes yet. + 1.0.0-alpha - 2014-12-20 ------------------------ diff --git a/setup.py b/setup.py index 1b1454779..edff161d4 100644 --- a/setup.py +++ b/setup.py @@ -86,7 +86,7 @@ install_requires.append('ordereddict') setup(name='Kotti', - version='1.0.0-alpha', + version='1.1-dev', description="A high-level, Pythonic web application framework based on Pyramid and SQLAlchemy. It includes an extensible Content Management System called the Kotti CMS.", # noqa long_description='\n\n'.join([README, AUTHORS, CHANGES]), classifiers=[ From df7277f8f552fc90ec354e0833e6e6a8078e7590 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Sat, 20 Dec 2014 06:01:27 +0100 Subject: [PATCH 016/600] Order API docs by source. [ci skip] --- docs/api/kotti.events.rst | 2 +- docs/api/kotti.fanstatic.rst | 2 +- docs/api/kotti.message.rst | 1 + docs/api/kotti.migrate.rst | 1 + docs/api/kotti.populate.rst | 1 + docs/api/kotti.request.rst | 1 + docs/api/kotti.resources.rst | 1 + docs/api/kotti.security.rst | 1 + docs/api/kotti.sqla.rst | 1 + docs/api/kotti.testing.rst | 1 + docs/api/kotti.tests.rst | 1 + docs/api/kotti.util.rst | 1 + docs/api/kotti.views/index.rst | 1 + docs/api/kotti.views/kotti.views.cache.rst | 1 + docs/api/kotti.views/kotti.views.edit/index.rst | 2 +- .../kotti.views/kotti.views.edit/kotti.views.edit.actions.rst | 1 + .../kotti.views/kotti.views.edit/kotti.views.edit.content.rst | 1 + .../kotti.views.edit/kotti.views.edit.default_views.rst | 1 + docs/api/kotti.views/kotti.views.file.rst | 1 + docs/api/kotti.views/kotti.views.form.rst | 1 + docs/api/kotti.views/kotti.views.image.rst | 1 + docs/api/kotti.views/kotti.views.login.rst | 1 + docs/api/kotti.views/kotti.views.site_setup.rst | 2 +- docs/api/kotti.views/kotti.views.slots.rst | 1 + docs/api/kotti.views/kotti.views.users.rst | 1 + docs/api/kotti.views/kotti.views.util.rst | 1 + docs/api/kotti.views/kotti.views.view.rst | 1 + docs/api/kotti.workflow.rst | 1 + 28 files changed, 28 insertions(+), 4 deletions(-) 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.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.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.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..a5f29aa6f 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 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 index 1869764af..290e22877 100644 --- a/docs/api/kotti.views/kotti.views.image.rst +++ b/docs/api/kotti.views/kotti.views.image.rst @@ -5,3 +5,4 @@ kotti.views.image .. automodule:: kotti.views.image :members: + :member-order: bysource 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 From 7c51daf6c9bef7cf04be0f3e41180a1e4c057c21 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Sat, 20 Dec 2014 06:01:54 +0100 Subject: [PATCH 017/600] Add docstrings. [ci skip] --- kotti/resources.py | 16 ++++++++++------ kotti/tests/__init__.py | 4 ++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/kotti/resources.py b/kotti/resources.py index 7f789e271..5d9cf22ab 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -67,7 +67,7 @@ class ContainerMixin(object, DictMixin): - """Containers form the API of a Node that's used for subitem + """ Containers form the API of a Node that's used for subitem access and in traversal. """ @@ -82,7 +82,7 @@ def __delitem__(self, key): def keys(self): """ - :result: A list of children names. + :result: children names :rtype: list """ @@ -131,20 +131,24 @@ def __getitem__(self, path): @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. + """ Return only those children for which the user initiating the + request has the asked permission. :param request: current 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 """ diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index 2bf8ff658..c9366a26a 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -1,6 +1,10 @@ # -*- coding: utf-8 -*- """ + +Fixture dependencies +-------------------- + .. graphviz:: digraph kotti_fixtures { From babeb68f7ff4597b9b4e89a09d88c9b7834834d5 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Sat, 20 Dec 2014 06:10:37 +0100 Subject: [PATCH 018/600] Add docstrings to ``resources.LocalGroup``. [ci skip] --- kotti/resources.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/kotti/resources.py b/kotti/resources.py index 5d9cf22ab..6572830cf 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -160,15 +160,27 @@ def children_with_permission(self, request, permission='view'): class LocalGroup(Base): + """ Local groups allow the assignment of groups or roles to pricipals + (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'), ) + #: Primary key for the node in the DB + #: (:class:`sqlalchemy.types.Integer`) id = Column(Integer(), primary_key=True) + #: ID of the node for this assignment + #: (:class:`sqlalchemy.types.Integer`) node_id = Column(ForeignKey('nodes.id')) + #: Name of the principal (user or group) + #: (:class:`sqlalchemy.types.Unicode`) principal_name = Column(Unicode(100)) + #: Name of the assigned group or role + #: (:class:`sqlalchemy.types.Unicode`) group_name = Column(Unicode(100)) def __init__(self, node, principal_name, group_name): From 174829a31845142e866ffd3b9db093f9d764baf8 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Tue, 23 Dec 2014 20:35:55 +0100 Subject: [PATCH 019/600] Update tut-1.rst --- docs/first_steps/tut-1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/first_steps/tut-1.rst b/docs/first_steps/tut-1.rst index 0471fd0cd..203b6c802 100644 --- a/docs/first_steps/tut-1.rst +++ b/docs/first_steps/tut-1.rst @@ -33,7 +33,7 @@ To create our add-on, we use the standard Pyramid tool ``pcreate``, with .. code-block:: bash - bin/pcreate -s kotti_addon kotti_mysite + bin/pcreate -s kotti kotti_mysite The script will ask a number of questions. It is safe to accept the defaults. From 7f71560176bda2eb48cff1fcb42a87aa080e031c Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Wed, 31 Dec 2014 23:02:18 +0100 Subject: [PATCH 020/600] Require ``kotti_tinymce==0.5.1``. This fixes #365. --- CHANGES.txt | 5 +++++ requirements.txt | 2 +- setup.py | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 505310302..cba6bd650 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,11 @@ Change History ============== +1.0.0-alpha.2 - 2014-12-31 +-------------------------- + +- Require ``kotti_tinymce==0.5.1``. This fixes #365. + 1.0.0-alpha - 2014-12-20 ------------------------ diff --git a/requirements.txt b/requirements.txt index 9cef309d3..a00e0b1ca 100644 --- a/requirements.txt +++ b/requirements.txt @@ -33,7 +33,7 @@ js.jquery_timepicker_addon==1.3-1 js.jqueryui==1.10.3 js.jqueryui_tagit==2.0.24-2 js.modernizr==2.5.3.1 -kotti_tinymce==0.5.0 +kotti_tinymce==0.5.1 lingua==3.6.1 peppercorn==0.5 plone.scale==1.3.4 diff --git a/setup.py b/setup.py index 1b1454779..1f0d3c980 100644 --- a/setup.py +++ b/setup.py @@ -86,7 +86,7 @@ install_requires.append('ordereddict') setup(name='Kotti', - version='1.0.0-alpha', + version='1.0.0-alpha.2', description="A high-level, Pythonic web application framework based on Pyramid and SQLAlchemy. It includes an extensible Content Management System called the Kotti CMS.", # noqa long_description='\n\n'.join([README, AUTHORS, CHANGES]), classifiers=[ From 8092fb3ce1344cb84347ddcecd5271322e4cc135 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Thu, 1 Jan 2015 07:25:16 +0100 Subject: [PATCH 021/600] Change release date. [ci skip] --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index cba6bd650..c2a9cfb0b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,7 +1,7 @@ Change History ============== -1.0.0-alpha.2 - 2014-12-31 +1.0.0-alpha.2 - 2015-01-01 -------------------------- - Require ``kotti_tinymce==0.5.1``. This fixes #365. From a0f2f04c202ec1563ca4c0470c95e5e9d56e7328 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Mon, 5 Jan 2015 14:26:20 +0100 Subject: [PATCH 022/600] Remove kotti.includes default Handling of that option has been removed before, so usage should throw an exception. --- kotti/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/kotti/__init__.py b/kotti/__init__.py index 44d010988..2a47b0ac9 100644 --- a/kotti/__init__.py +++ b/kotti/__init__.py @@ -57,7 +57,6 @@ def none_factory(**kwargs): # pragma: no cover 'kotti.templates.api': 'kotti.views.util.TemplateAPI', 'kotti.configurators': '', 'pyramid.includes': '', - 'kotti.includes': '', # BBB 'kotti.base_includes': ' '.join([ 'kotti', 'kotti.events', From c353fbe1b4d710dfb1460e5a04e933ec92662658 Mon Sep 17 00:00:00 2001 From: "I can not only fetch JSON, but parse it too." Date: Mon, 5 Jan 2015 14:04:24 -0800 Subject: [PATCH 023/600] Fixed view names setup_users and setup_user are actually named setup-users and setup-user in the code. --- docs/developing/basic/developer-manual.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developing/basic/developer-manual.rst b/docs/developing/basic/developer-manual.rst index f7d5d44e5..7816e8c31 100644 --- a/docs/developing/basic/developer-manual.rst +++ b/docs/developing/basic/developer-manual.rst @@ -187,7 +187,7 @@ 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 +``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. From b06231d7195733217d5a2213c62a83c8d30d9c6f Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 12 Jan 2015 02:12:40 -0800 Subject: [PATCH 024/600] added test for Request --- kotti/tests/test_request.py | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 kotti/tests/test_request.py diff --git a/kotti/tests/test_request.py b/kotti/tests/test_request.py new file mode 100644 index 000000000..a752474f5 --- /dev/null +++ b/kotti/tests/test_request.py @@ -0,0 +1,56 @@ +from pyramid.config import Configurator +from pyramid.threadlocal import manager, get_current_registry +from pyramid.view import render_view_to_response +from pyramid.response import Response + +from kotti.request import Request +from kotti.resources import Document + + +class TestRequest: + + def _make_request(self, name): + environ = { + 'PATH_INFO': '/', + 'SERVER_NAME': 'example.com', + 'SERVER_PORT': '5432', + 'QUERY_STRING': '', + 'wsgi.url_scheme': 'http', + } + + request = Request(environ) + request.view_name = name + registry = get_current_registry() + request.registry = registry + manager.clear() + manager.push({'request':request, 'registry':registry}) + + return request + + def test_can_reify(self, root, setup_app): + + registry = setup_app.registry + config = Configurator(registry, + request_factory='kotti.request.Request') + manager.push({'registry':config.registry}) + + config.add_view(lambda context, request:Response('first'), + name='view', + context=Document) + config.commit() + + response = render_view_to_response( + Document(), self._make_request('view1'), name='', secure=False) + assert response.body == 'first' + + def view2(context, request): + assert request.req_method == 'has it' + return Response("second") + + config.add_request_method(lambda req:'has it', 'req_method', reify=True) + config.add_view(view2, name='view', context=Document) + config.commit() + + response = render_view_to_response( + Document(), self._make_request('view2'), name='', secure=False) + assert response.body == 'second' From 1c60dc35076619db6963fb2d74a07fd2f59b0b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionic=C4=83=20Biz=C4=83u?= Date: Mon, 12 Jan 2015 16:33:12 +0200 Subject: [PATCH 025/600] Updated the year. --- COPYRIGHT.txt | 2 +- kotti/templates/view/footer.pt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index e834e369f..8dacaefd1 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -1,2 +1,2 @@ -Copyright (c) 2010-2014 Daniel Nouri and Contributors. +Copyright (c) 2010-2015 Daniel Nouri and Contributors. All Rights Reserved diff --git a/kotti/templates/view/footer.pt b/kotti/templates/view/footer.pt index a3c6d7d51..c39d4c393 100644 --- a/kotti/templates/view/footer.pt +++ b/kotti/templates/view/footer.pt @@ -1,4 +1,4 @@
-

© Aliens 2014

+

© Aliens 2015

From 03e0e2b7f3f19b84a25edc9c8a5d48525709e9cd Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 12 Jan 2015 17:31:45 +0200 Subject: [PATCH 026/600] A request cannot be subclassed and then properties added; Added a test and a workaround for this --- kotti/request.py | 8 ++++ kotti/tests/test_request.py | 87 +++++++++++++------------------------ 2 files changed, 39 insertions(+), 56 deletions(-) diff --git a/kotti/request.py b/kotti/request.py index d1e274287..aa61a544c 100644 --- a/kotti/request.py +++ b/kotti/request.py @@ -47,3 +47,11 @@ def has_permission(self, permission, context=None): with authz_context(context, self): return BaseRequest.has_permission(self, permission, context) + + +# workaround for https://github.com/Pylons/pyramid/issues/1529 +# this allows addon packages to call config.add_request_method and not lose +# the interfaces provided by the request (for example to call a subview, +# like kotti.view.view_content_default) +from zope.interface import providedBy +providedBy(Request({})) diff --git a/kotti/tests/test_request.py b/kotti/tests/test_request.py index a752474f5..73ab79220 100644 --- a/kotti/tests/test_request.py +++ b/kotti/tests/test_request.py @@ -1,56 +1,31 @@ -from pyramid.config import Configurator -from pyramid.threadlocal import manager, get_current_registry -from pyramid.view import render_view_to_response -from pyramid.response import Response - -from kotti.request import Request -from kotti.resources import Document - - -class TestRequest: - - def _make_request(self, name): - environ = { - 'PATH_INFO': '/', - 'SERVER_NAME': 'example.com', - 'SERVER_PORT': '5432', - 'QUERY_STRING': '', - 'wsgi.url_scheme': 'http', - } - - request = Request(environ) - request.view_name = name - registry = get_current_registry() - request.registry = registry - manager.clear() - manager.push({'request':request, 'registry':registry}) - - return request - - def test_can_reify(self, root, setup_app): - - registry = setup_app.registry - config = Configurator(registry, - request_factory='kotti.request.Request') - manager.push({'registry':config.registry}) - - config.add_view(lambda context, request:Response('first'), - name='view', - context=Document) - config.commit() - - response = render_view_to_response( - Document(), self._make_request('view1'), name='', secure=False) - assert response.body == 'first' - - def view2(context, request): - assert request.req_method == 'has it' - return Response("second") - - config.add_request_method(lambda req:'has it', 'req_method', reify=True) - config.add_view(view2, name='view', context=Document) - config.commit() - - response = render_view_to_response( - Document(), self._make_request('view2'), name='', secure=False) - assert response.body == 'second' +class TestPyramidRequestProperties: + + def test_it(self): + from pyramid.request import Request + from zope.interface import providedBy, implementedBy + + req = Request({}) + assert providedBy(req) == implementedBy(Request) + + req._set_properties({'b':'b'}) + + assert providedBy(req) == implementedBy(Request) + + def test_subclassing(self): + from pyramid.request import Request + from zope.interface import providedBy, implementedBy + + class Subclass(Request): + pass + + req = Subclass({}) + + req._set_properties({'b':'b'}) + assert providedBy(req) != implementedBy(Subclass) + + #calling providedBy(req) before _set_properties makes the test pass + req = Subclass({}) + req._set_properties({'b':'b'}) + providedBy(req) + assert providedBy(req) == implementedBy(Subclass) + From 3b9a6e15f7e1a0c599fddf7470b9b31c26c0912d Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 13 Jan 2015 15:00:35 +0200 Subject: [PATCH 027/600] Simplify workaround. Change test to make it Kotti specific --- kotti/request.py | 12 ++++-------- kotti/tests/test_request.py | 28 ++++------------------------ 2 files changed, 8 insertions(+), 32 deletions(-) diff --git a/kotti/request.py b/kotti/request.py index aa61a544c..a391fa264 100644 --- a/kotti/request.py +++ b/kotti/request.py @@ -1,9 +1,13 @@ # -*- coding: utf-8 -*- +from zope.interface import implementer + from pyramid.decorator import reify +from pyramid.interfaces import IRequest from pyramid.request import Request as BaseRequest +@implementer(IRequest) class Request(BaseRequest): """ Kotti subclasses :class:`pyramid.request.Request` to make additional attributes / methods available on request objects and override Pyramid's @@ -47,11 +51,3 @@ def has_permission(self, permission, context=None): with authz_context(context, self): return BaseRequest.has_permission(self, permission, context) - - -# workaround for https://github.com/Pylons/pyramid/issues/1529 -# this allows addon packages to call config.add_request_method and not lose -# the interfaces provided by the request (for example to call a subview, -# like kotti.view.view_content_default) -from zope.interface import providedBy -providedBy(Request({})) diff --git a/kotti/tests/test_request.py b/kotti/tests/test_request.py index 73ab79220..f241eedcd 100644 --- a/kotti/tests/test_request.py +++ b/kotti/tests/test_request.py @@ -1,31 +1,11 @@ -class TestPyramidRequestProperties: +class TestExtendingRequest: def test_it(self): - from pyramid.request import Request + from kotti.request import Request from zope.interface import providedBy, implementedBy req = Request({}) - assert providedBy(req) == implementedBy(Request) - - req._set_properties({'b':'b'}) + req._set_properties({'marker':'exists'}) assert providedBy(req) == implementedBy(Request) - - def test_subclassing(self): - from pyramid.request import Request - from zope.interface import providedBy, implementedBy - - class Subclass(Request): - pass - - req = Subclass({}) - - req._set_properties({'b':'b'}) - assert providedBy(req) != implementedBy(Subclass) - - #calling providedBy(req) before _set_properties makes the test pass - req = Subclass({}) - req._set_properties({'b':'b'}) - providedBy(req) - assert providedBy(req) == implementedBy(Subclass) - + assert req.marker == 'exists' From 2563be3656a809916d5774c86be0745a8ce6668a Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 13 Jan 2015 15:15:16 +0200 Subject: [PATCH 028/600] Fix pep8 problems --- kotti/tests/test_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotti/tests/test_request.py b/kotti/tests/test_request.py index f241eedcd..a80879dfb 100644 --- a/kotti/tests/test_request.py +++ b/kotti/tests/test_request.py @@ -5,7 +5,7 @@ def test_it(self): from zope.interface import providedBy, implementedBy req = Request({}) - req._set_properties({'marker':'exists'}) + req._set_properties({'marker': 'exists'}) assert providedBy(req) == implementedBy(Request) assert req.marker == 'exists' From 314e24a8fc3b0705933a925f2867bb7de2190000 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 13 Jan 2015 15:24:53 +0200 Subject: [PATCH 029/600] Added changelog entry, added myself to contributors --- CHANGES.txt | 9 +++++++++ CONTRIBUTORS.txt | 1 + 2 files changed, 10 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index fc09bc2e3..aa5807758 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,6 +6,15 @@ Change History - No changes yet. +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 -------------------------- diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 14058d45e..5a1601c05 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -100,3 +100,4 @@ Contributors - Fabien Castarède, 2013/08/08 - Natan Žabkar, 2013/08/28 - Ionică Bizău, 2014/04/09 +- Ichim Tiberiu, 2015/01/13 From 4a51f59bac409bd048dfdedfb94b41cc3e38a7d8 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Tue, 13 Jan 2015 16:48:30 +0100 Subject: [PATCH 030/600] Bump version. --- CHANGES.txt | 5 ----- setup.py | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index aa5807758..be40d010f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,11 +1,6 @@ Change History ============== -1.1-dev - unreleased --------------------- - -- No changes yet. - 1.0.0-alpha.3 - 2015-01-13 -------------------------- diff --git a/setup.py b/setup.py index edff161d4..7804e2875 100644 --- a/setup.py +++ b/setup.py @@ -86,7 +86,7 @@ install_requires.append('ordereddict') setup(name='Kotti', - version='1.1-dev', + version='1.0.0-alpha.3', description="A high-level, Pythonic web application framework based on Pyramid and SQLAlchemy. It includes an extensible Content Management System called the Kotti CMS.", # noqa long_description='\n\n'.join([README, AUTHORS, CHANGES]), classifiers=[ From 6f0f0e952d0bdae4f5486ec004c11758124790f2 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Tue, 13 Jan 2015 16:49:36 +0100 Subject: [PATCH 031/600] Bump version. --- CHANGES.txt | 5 +++++ setup.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index be40d010f..aa5807758 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,11 @@ Change History ============== +1.1-dev - unreleased +-------------------- + +- No changes yet. + 1.0.0-alpha.3 - 2015-01-13 -------------------------- diff --git a/setup.py b/setup.py index 7804e2875..edff161d4 100644 --- a/setup.py +++ b/setup.py @@ -86,7 +86,7 @@ install_requires.append('ordereddict') setup(name='Kotti', - version='1.0.0-alpha.3', + version='1.1-dev', description="A high-level, Pythonic web application framework based on Pyramid and SQLAlchemy. It includes an extensible Content Management System called the Kotti CMS.", # noqa long_description='\n\n'.join([README, AUTHORS, CHANGES]), classifiers=[ From 104e20d371595731923c5db4ae7a103393a44ffd Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Fri, 16 Jan 2015 17:12:56 +0200 Subject: [PATCH 032/600] Allow restricting addview to specific contexts. This still requires that the addable_to information is properly filled in (and will duplicate the context discriminator for the view), but it will no longer fail with TypeError in view_permitted --- kotti/resources.py | 4 ++-- kotti/tests/test_node_views.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/kotti/resources.py b/kotti/resources.py index 6572830cf..f0c9adc6b 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -376,8 +376,8 @@ def addable(self, context, request): :rtype: Boolean """ - if view_permitted(context, request, self.add_view): - return context.type_info.name in self.addable_to + if context.type_info.name in self.addable_to: + return view_permitted(context, request, self.add_view) else: return False diff --git a/kotti/tests/test_node_views.py b/kotti/tests/test_node_views.py index e09a56f1d..cea46c76d 100644 --- a/kotti/tests/test_node_views.py +++ b/kotti/tests/test_node_views.py @@ -22,6 +22,28 @@ def test_view_permitted_no(self, config, root): request = DummyRequest() assert Document.type_info.addable(root, request) is False + def test_addable_views_registered_to_some_context(self, config, root): + + from kotti.resources import Document, File + + _saved = Document.type_info.addable_to + Document.type_info.addable_to = ['File'] + + config.testing_securitypolicy(permissive=True) + config.add_view( + lambda context, request: {}, + name=Document.type_info.add_view, + permission='add', + renderer='kotti:templates/edit/node.pt', + context=File, + ) + request = DummyRequest() + + assert Document.type_info.addable(Document(), request) is False + assert Document.type_info.addable(File(), request) is True + + Document.type_info.addable_to = _saved + class TestNodePaste: def test_get_non_existing_paste_item(self, root): From b6037e30b5685537db7380fafbc5aae4590e9663 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Fri, 16 Jan 2015 17:33:18 +0200 Subject: [PATCH 033/600] Allow restricting addview to specific contexts. This still requires that the addable_to information is properly filled in (and will duplicate the context discriminator for the view), but it will no longer fail with TypeError in kotti.security.view_permitted --- kotti/resources.py | 4 ++-- kotti/tests/test_node_views.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/kotti/resources.py b/kotti/resources.py index 6572830cf..f0c9adc6b 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -376,8 +376,8 @@ def addable(self, context, request): :rtype: Boolean """ - if view_permitted(context, request, self.add_view): - return context.type_info.name in self.addable_to + if context.type_info.name in self.addable_to: + return view_permitted(context, request, self.add_view) else: return False diff --git a/kotti/tests/test_node_views.py b/kotti/tests/test_node_views.py index e09a56f1d..cea46c76d 100644 --- a/kotti/tests/test_node_views.py +++ b/kotti/tests/test_node_views.py @@ -22,6 +22,28 @@ def test_view_permitted_no(self, config, root): request = DummyRequest() assert Document.type_info.addable(root, request) is False + def test_addable_views_registered_to_some_context(self, config, root): + + from kotti.resources import Document, File + + _saved = Document.type_info.addable_to + Document.type_info.addable_to = ['File'] + + config.testing_securitypolicy(permissive=True) + config.add_view( + lambda context, request: {}, + name=Document.type_info.add_view, + permission='add', + renderer='kotti:templates/edit/node.pt', + context=File, + ) + request = DummyRequest() + + assert Document.type_info.addable(Document(), request) is False + assert Document.type_info.addable(File(), request) is True + + Document.type_info.addable_to = _saved + class TestNodePaste: def test_get_non_existing_paste_item(self, root): From b3eb256641465c5dcca014ec9f84034c3539acf4 Mon Sep 17 00:00:00 2001 From: tiberiuichim Date: Fri, 16 Jan 2015 17:55:02 +0200 Subject: [PATCH 034/600] Added changelog entry for restricting add views --- CHANGES.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index aa5807758..e64e345b1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,6 +6,15 @@ Change History - No changes yet. +1.0.0-alpha.4 - unreleased +-------------------------- + +- 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. + 1.0.0-alpha.3 - 2015-01-13 -------------------------- From c7d4681fb4f53224fed82fd419fd0702df47b9b5 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 18 Jan 2015 07:22:31 -0800 Subject: [PATCH 035/600] Improve title_to_name handling of numbers in titles For documents with duplicate titles that end in a number, append a counter instead of incrementing their number. Fixes #245 --- CHANGES.txt | 5 +++++ kotti/tests/test_util.py | 11 +++++++++++ kotti/util.py | 3 +++ 3 files changed, 19 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index aa5807758..8e85fe5c9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,6 +6,11 @@ Change History - No changes yet. +1.0.0-alpha.4 - unreleased +-------------------------- +- For documents with duplicate titles that end in a number, append a counter + instead of incrementing their number. Fixes #245 + 1.0.0-alpha.3 - 2015-01-13 -------------------------- diff --git a/kotti/tests/test_util.py b/kotti/tests/test_util.py index e3a5f0a9b..ccff439dc 100644 --- a/kotti/tests/test_util.py +++ b/kotti/tests/test_util.py @@ -71,6 +71,17 @@ def test_normal(self): from kotti.util import title_to_name assert title_to_name(u'Foo Bar') == u'foo-bar' + def test_numbering(self): + self.setUp() + from kotti.util import title_to_name + assert title_to_name(u'Report Part 1', blacklist=[]) == u'report-part-1' + assert title_to_name(u'Report Part 1', + blacklist=['report-part-1']) == u'report-part-1-1' + assert title_to_name(u'Report Part 3', + blacklist=['report-part-2']) == u'report-part-3' + assert title_to_name(u'Report Part 3', + blacklist=['report-part-3']) == u'report-part-3-1' + def test_disambiguate_name(self): from kotti.util import disambiguate_name assert disambiguate_name(u'foo') == u'foo-1' diff --git a/kotti/util.py b/kotti/util.py index 93b775f3f..d2a427397 100644 --- a/kotti/util.py +++ b/kotti/util.py @@ -299,6 +299,9 @@ def title_to_name(title, blacklist=()): from kotti import get_settings urlnormalizer = get_settings()['kotti.url_normalizer'][0] name = unicode(urlnormalizer(title, locale_name, max_length=40)) + if name not in blacklist: + return name + name += u'-1' while name in blacklist: name = disambiguate_name(name) return name From e697369e615b2bf06369df42583d6dc1448057a8 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 18 Jan 2015 10:27:50 -0800 Subject: [PATCH 036/600] Also test counter increase when blacklist has names with numbers --- kotti/tests/test_util.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kotti/tests/test_util.py b/kotti/tests/test_util.py index ccff439dc..6a12792af 100644 --- a/kotti/tests/test_util.py +++ b/kotti/tests/test_util.py @@ -74,13 +74,15 @@ def test_normal(self): def test_numbering(self): self.setUp() from kotti.util import title_to_name - assert title_to_name(u'Report Part 1', blacklist=[]) == u'report-part-1' + assert title_to_name(u'Report Part 1', + blacklist=[]) == u'report-part-1' assert title_to_name(u'Report Part 1', blacklist=['report-part-1']) == u'report-part-1-1' - assert title_to_name(u'Report Part 3', - blacklist=['report-part-2']) == u'report-part-3' assert title_to_name(u'Report Part 3', blacklist=['report-part-3']) == u'report-part-3-1' + assert title_to_name( + u'Report Part 3', blacklist=['report-part-3', 'report-part-3-1'] + ) == u'report-part-3-2' def test_disambiguate_name(self): from kotti.util import disambiguate_name From cd3737b7bc83c18bd6e99d303a0e007a0e373dc7 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Tue, 20 Jan 2015 15:17:14 -0800 Subject: [PATCH 037/600] .travis.yml: Set sudo false for new containers This makes Travis use a new Docker-based infrastructure that is supposed to be faster and awesomer. Maybe it will solve the networking-related test failures I keep seeing? --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 3baa3b755..ad816d324 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: python +sudo: false python: - "2.6" - "2.7" From 72596b038cf3354c3907e342fbb22b1d54b65b2e Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Tue, 20 Jan 2015 16:38:23 -0800 Subject: [PATCH 038/600] CustomContentEditForm: calendar => CustomContent --- kotti/scaffolds/package/+package+/views/edit.py_tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotti/scaffolds/package/+package+/views/edit.py_tmpl b/kotti/scaffolds/package/+package+/views/edit.py_tmpl index df58fd09d..44c93dfca 100644 --- a/kotti/scaffolds/package/+package+/views/edit.py_tmpl +++ b/kotti/scaffolds/package/+package+/views/edit.py_tmpl @@ -36,6 +36,6 @@ class CustomContentAddForm(AddFormView): @view_config(name='edit', context=CustomContent, permission='edit', renderer='kotti:templates/edit/node.pt') class CustomContentEditForm(EditFormView): - """ Form to edit existing calendars. """ + """ Form to edit existing CustomContent objects. """ schema_factory = CustomContentSchema From b97c4c2af347d2eeeeedf81112b17f5ee60d9b48 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Tue, 20 Jan 2015 17:48:28 -0800 Subject: [PATCH 039/600] Fix typos --- kotti/resources.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kotti/resources.py b/kotti/resources.py index 6572830cf..6e23ddfa3 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -160,7 +160,7 @@ def children_with_permission(self, request, permission='view'): class LocalGroup(Base): - """ Local groups allow the assignment of groups or roles to pricipals + """ 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). """ @@ -227,10 +227,10 @@ class Node(Base, ContainerMixin, PersistentACLMixin): #: 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 + #: 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 + #: 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 From 9398890bdf5c181c7c9d1f70c2599676ef824b6b Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Tue, 20 Jan 2015 14:39:11 -0800 Subject: [PATCH 040/600] Small tweaks to the tutorial --- docs/first_steps/tut-2.rst | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/first_steps/tut-2.rst b/docs/first_steps/tut-2.rst index 5deefdd17..2c2038278 100644 --- a/docs/first_steps/tut-2.rst +++ b/docs/first_steps/tut-2.rst @@ -40,14 +40,15 @@ 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. +- 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 ``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. @@ -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 @@ -169,11 +170,11 @@ Add this to ``views/edit.py``: 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 ------------------------------------- @@ -197,9 +198,9 @@ Open ``__init__.py`` and modify the ``kotti_configure`` method so that the ... -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. From 16e80c202cf053c014ea5a66ddd8d6eb9c869d38 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Tue, 20 Jan 2015 14:00:39 -0800 Subject: [PATCH 041/600] .travis.yml: Add travis_retry to hopefully deal with intermittent network errors --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ad816d324..ea87becf6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,10 +8,10 @@ env: - 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 + - travis_retry pip install "pip==1.3.1" # fix issue with fanstatic==1.0a + - travis_retry pip install -e . -r requirements.txt - python setup.py dev - - pip install psycopg2 oursql + - travis_retry pip install psycopg2 oursql before_script: - psql -c 'create database kotti_testing;' -U postgres - mysql -e 'create database kotti_testing;' From 3ff14f6b4a9a7282e584dda8949e829799fdfd16 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Tue, 20 Jan 2015 13:07:47 -0800 Subject: [PATCH 042/600] Add Dockerfile --- Dockerfile | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..3f1145c6d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +# -------------------------------------------------------------------------- +# This is a Dockerfile to build an Ubuntu 14.04 Docker image with +# Kotti +# +# Use a command like: +# docker build -t /kotti . +# -------------------------------------------------------------------------- + +FROM orchardup/python:2.7 +MAINTAINER Marc Abramowitz (@msabramo) + +RUN pip install -r https://raw.github.com/Kotti/Kotti/stable/requirements.txt +ADD app.ini . + +EXPOSE 5000 + +CMD ["pserve", "app.ini", "host=0.0.0.0"] From ca134ea0e94fa101cace1ba6f363a7726e8b2f30 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 21 Jan 2015 23:29:22 -0800 Subject: [PATCH 043/600] events.py: Fix misspelling of "resources" in docs --- kotti/events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotti/events.py b/kotti/events.py index 380df4468..3332fb0e6 100644 --- a/kotti/events.py +++ b/kotti/events.py @@ -416,7 +416,7 @@ class subscribe(object): 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): From cf7bfa0a1497bd7707b45c96821c615463dafc67 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Fri, 23 Jan 2015 09:51:51 -0800 Subject: [PATCH 044/600] Spelling fix: bject => object --- kotti/views/edit/actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotti/views/edit/actions.py b/kotti/views/edit/actions.py index 39e99fb04..26fc48cc9 100644 --- a/kotti/views/edit/actions.py +++ b/kotti/views/edit/actions.py @@ -520,7 +520,7 @@ def move_child_position(context, request): 0-based old (i.e. the current index of the child to be moved) and new position (its new index) values. :type request: - :result: JSON serializable bject with a single attribute ("result") that is + :result: JSON serializable object with a single attribute ("result") that is either "success" or "error". :rtype: dict """ From 415d16105daf0f7af0fc0096d18114ec9a73cdb0 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 26 Jan 2015 13:38:52 -0800 Subject: [PATCH 045/600] Added a paragraph in the developer manual about registering views for context --- docs/developing/basic/developer-manual.rst | 6 ++++++ docs/first_steps/tut-2.rst | 2 ++ 2 files changed, 8 insertions(+) diff --git a/docs/developing/basic/developer-manual.rst b/docs/developing/basic/developer-manual.rst index 7816e8c31..2b8fd30b3 100644 --- a/docs/developing/basic/developer-manual.rst +++ b/docs/developing/basic/developer-manual.rst @@ -47,6 +47,12 @@ Document content type serves as an example here: self.body = body self.mime_type = mime_type +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. + You can configure the list of active content types in Kotti by modifying the :ref:`kotti.available_types` setting. diff --git a/docs/first_steps/tut-2.rst b/docs/first_steps/tut-2.rst index 5deefdd17..9b6339167 100644 --- a/docs/first_steps/tut-2.rst +++ b/docs/first_steps/tut-2.rst @@ -79,6 +79,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 ----------------------- From 60837e871f188bb12e244a6ebe9cf1cdded6291a Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Wed, 28 Jan 2015 16:46:23 +0100 Subject: [PATCH 046/600] Add Kotti itself to requirements. This resolves #373. --- docs/first_steps/installation.rst | 1 - requirements.txt | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/first_steps/installation.rst b/docs/first_steps/installation.rst index 9a7816769..49d903a32 100644 --- a/docs/first_steps/installation.rst +++ b/docs/first_steps/installation.rst @@ -22,7 +22,6 @@ It is recommended to install Kotti inside a virtualenv: virtualenv mysite cd mysite bin/pip install -r https://raw.github.com/Kotti/Kotti/stable/requirements.txt - bin/pip install [--pre] Kotti This will install the latest released version of Kotti and all its requirements into your virtualenv. diff --git a/requirements.txt b/requirements.txt index a00e0b1ca..f14537ca5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +Kotti==1.0.0-alpha.3 Babel==1.3 Beaker==1.6.4 Chameleon==2.18 From c67f36c6537901d23457064b959c3ff2ac752b2c Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 28 Jan 2015 07:35:11 -0800 Subject: [PATCH 047/600] Add instructions for Docker --- docs/first_steps/installation.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/first_steps/installation.rst b/docs/first_steps/installation.rst index 9a7816769..0cb913f1d 100644 --- a/docs/first_steps/installation.rst +++ b/docs/first_steps/installation.rst @@ -12,6 +12,18 @@ Requirements - ``Xcode`` (on OS X) or - equivalent build toolchain for your OS. +Installation using Docker (experimental) +---------------------------------------- + +This assumes that you already have Docker_ installed: + +.. parsed-literal:: + + docker pull kotti/kotti + docker run -i -t -p 5000:5000 kotti/kotti + +This should get you a running Kotti instance on port 5000. + Installation using ``virtualenv`` --------------------------------- @@ -63,3 +75,4 @@ Although SQLite may prove to be adequate for some deployments, Kotti is flexible .. _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/ From 9e19f953d5e00f121122a923151357447aaff5c1 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 28 Jan 2015 07:56:08 -0800 Subject: [PATCH 048/600] README.rst: Add link to installation instructions Because a lot of folks will be curious about how it is installed and/or might be eager to try it out. --- README.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.rst b/README.rst index d0e480e3e..df5e4a32f 100644 --- a/README.rst +++ b/README.rst @@ -52,6 +52,13 @@ Its **main features** are: .. _Plone: http://plone.org/ .. _Twitter Bootstrap: http://twitter.github.com/bootstrap/ +Install +======= + +See `installation instructions`_. + +.. _installation instructions: http://kotti.readthedocs.org/en/latest/first_steps/installation.html + Support and Documentation ========================= From f05e1be905fbd6b14629ca3359eecda00bc02804 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Wed, 28 Jan 2015 16:57:05 +0100 Subject: [PATCH 049/600] Move Docker section to the bottom, as it's labelled "experimental". [ci skip] --- docs/first_steps/installation.rst | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/first_steps/installation.rst b/docs/first_steps/installation.rst index fc833d88e..9cedcda27 100644 --- a/docs/first_steps/installation.rst +++ b/docs/first_steps/installation.rst @@ -12,18 +12,6 @@ Requirements - ``Xcode`` (on OS X) or - equivalent build toolchain for your OS. -Installation using Docker (experimental) ----------------------------------------- - -This assumes that you already have Docker_ installed: - -.. parsed-literal:: - - docker pull kotti/kotti - docker run -i -t -p 5000:5000 kotti/kotti - -This should get you a running Kotti instance on port 5000. - Installation using ``virtualenv`` --------------------------------- @@ -71,6 +59,18 @@ Kotti includes support for PostgreSQL, MySQL and SQLite (tested regularly), and 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. +Installation using Docker (experimental) +---------------------------------------- + +This assumes that you already have Docker_ installed: + +.. parsed-literal:: + + docker pull kotti/kotti + docker run -i -t -p 5000:5000 kotti/kotti + +This should get you a running Kotti instance on port 5000. + .. _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 From ebcfebd23024f778e78d63edca8a1a5f808c5062 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 28 Jan 2015 08:04:50 -0800 Subject: [PATCH 050/600] README.rst: Wrap long line Rather than wrap whererever my editor (vim) chose, I decided to experiment with [Semantic linefeeds](http://rhodesmill.org/brandon/2012/one-sentence-per-line/) --- README.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index d0e480e3e..b6c902ea3 100644 --- a/README.rst +++ b/README.rst @@ -12,7 +12,10 @@ 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=stable .. _build status: http://travis-ci.org/Kotti/Kotti From 1a7526aaf66c08bd6f977b36b691ce44ae659dfe Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Wed, 28 Jan 2015 17:06:51 +0100 Subject: [PATCH 051/600] Bootstrap isn't called "Twitter Bootstrap" for a while now. [ci skip] --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index d0e480e3e..75a2e3995 100644 --- a/README.rst +++ b/README.rst @@ -32,7 +32,7 @@ Its **main features** are: - **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 @@ -50,7 +50,7 @@ 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/ Support and Documentation ========================= From b3d578c1d63e7d00dc5da889b7b80abc045c8569 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 28 Jan 2015 08:11:30 -0800 Subject: [PATCH 052/600] README.rst: Add PyPI badge; moved badges to top --- README.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index b6c902ea3..c832baaec 100644 --- a/README.rst +++ b/README.rst @@ -2,9 +2,11 @@ Kotti ===== +|pypi|_ +|build status|_ + 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). -|build status|_ Kotti is most useful when you are developing applications that @@ -19,6 +21,8 @@ thus hopefully keeping the learning curve flat for the developer. .. |build status| image:: https://secure.travis-ci.org/Kotti/Kotti.png?branch=stable .. _build status: http://travis-ci.org/Kotti/Kotti +.. |pypi| image:: https://pypip.in/version/Kotti/badge.svg?style=flat +.. _pypi: https://pypi.python.org/pypi/Kotti/ .. _Pyramid: http://docs.pylonsproject.org/projects/pyramid/dev/ .. _SQLAlchemy: http://www.sqlalchemy.org/ From e3340f3bf1b5601f574d918b9cc015a4688a0474 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 28 Jan 2015 08:18:33 -0800 Subject: [PATCH 053/600] README.rst: Link to Pyramid and SQLAlchemy on first mention --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index c832baaec..5b7604125 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ Kotti |pypi|_ |build status|_ -Kotti is a high-level, Pythonic web application framework 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 (see below). Kotti is most useful when you are developing applications that From 70e0446b6912ad7b852e09be452dd900bf73dd21 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Wed, 28 Jan 2015 08:26:23 -0800 Subject: [PATCH 054/600] CHANGES.txt: Add note about Docker support (#245) --- CHANGES.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 8e85fe5c9..4f571bf17 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,11 +4,12 @@ Change History 1.1-dev - unreleased -------------------- -- No changes yet. +- Added experimental Docker support. See #374. 1.0.0-alpha.4 - unreleased -------------------------- -- For documents with duplicate titles that end in a number, append a counter + +- For documents with duplicate titles that end in a number, append a counter instead of incrementing their number. Fixes #245 1.0.0-alpha.3 - 2015-01-13 From ac6baf31821bbf0df4fab78bf3df13bdcd62f7ae Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Wed, 28 Jan 2015 17:46:13 +0100 Subject: [PATCH 055/600] Try to add coveralls to Travis config. --- .travis.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ea87becf6..8611c7af9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,13 +9,16 @@ env: - KOTTI_TEST_DB_STRING=sqlite:// install: - travis_retry pip install "pip==1.3.1" # fix issue with fanstatic==1.0a - - travis_retry pip install -e . -r requirements.txt + - travis_retry pip install -e . -r requirements.txt --use-mirrors - python setup.py dev - - travis_retry pip install psycopg2 oursql + - travis_retry pip install psycopg2 oursql python-coveralls 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 +script: + - py.test --runslow --tb=native --cov=kotti --cov-report=term-missing +after_success: + - coveralls notifications: irc: "irc.freenode.org#kotti" email: false From fdda63a9ae76e07a278e16e463ccb118298d4d28 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Thu, 29 Jan 2015 11:25:15 +0100 Subject: [PATCH 056/600] Everybody loves badges, let's have them all! [ci skip] --- README.rst | 75 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 093a7df76..458f17dac 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,21 @@ Kotti ===== |pypi|_ -|build status|_ +|downloads_month|_ +|license|_ +|build status stable|_ + +.. |pypi| image:: https://img.shields.io/pypi/v/Kotti.svg?style=flat-square +.. _pypi: https://pypi.python.org/pypi/Kotti/ + +.. |downloads_month| image:: https://img.shields.io/pypi/dm/Kotti.svg?style=flat-square +.. _downloads_month: 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| image:: https://img.shields.io/travis/Kotti/Kotti/stable.svg?style=flat-square +.. _build status stable: http://travis-ci.org/Kotti/Kotti 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). @@ -19,10 +33,7 @@ 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=stable -.. _build status: http://travis-ci.org/Kotti/Kotti -.. |pypi| image:: https://pypip.in/version/Kotti/badge.svg?style=flat -.. _pypi: https://pypi.python.org/pypi/Kotti/ + .. _Pyramid: http://docs.pylonsproject.org/projects/pyramid/dev/ .. _SQLAlchemy: http://www.sqlalchemy.org/ @@ -59,6 +70,11 @@ Its **main features** are: .. _Plone: http://plone.org/ .. _Bootstrap: http://getbootstrap.com/ +License +======= + +Kotti is offered under the BSD-derived `Repoze Public License `_. + Install ======= @@ -69,9 +85,50 @@ See `installation instructions`_. 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 `_. -Kotti is offered under the BSD-derived `Repoze Public License `_. +If you think you found a bug, open an issue on or `Github bugtracker `_. + +Development +=========== + +|build status master|_ +|coveralls|_ +|codacy|_ + +.. requirements need to be upgraded before we shoff off + |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`_. + +.. |build status master| image:: https://img.shields.io/travis/Kotti/Kotti/master.svg?style=flat-square +.. _build status master: http://travis-ci.org/Kotti/Kotti +.. _continuous integration: http://travis-ci.org/Kotti/Kotti + +.. |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://img.shields.io/codacy/ad44331fcd904d338c074f2ca3e6a810.svg?style=flat-square +.. _codacy: https://www.codacy.com/public/disko/Kotti +.. _high quality coding standards: https://www.codacy.com/public/disko/Kotti + +.. _contribution guidelines: http://kotti.readthedocs.org/en/latest/contributing.html +.. _Github repository: https://github.com/Kotti/Kotti From a6c477be73e9079c1f1bc91017368506e37a7770 Mon Sep 17 00:00:00 2001 From: tiberiuichim Date: Fri, 16 Jan 2015 17:55:02 +0200 Subject: [PATCH 057/600] Added changelog entry for restricting add views --- CHANGES.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index aa5807758..e64e345b1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,6 +6,15 @@ Change History - No changes yet. +1.0.0-alpha.4 - unreleased +-------------------------- + +- 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. + 1.0.0-alpha.3 - 2015-01-13 -------------------------- From 1f847e0d01e12727648a5b92e66f6c4906e92a99 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 18 Jan 2015 07:22:31 -0800 Subject: [PATCH 058/600] Improve title_to_name handling of numbers in titles For documents with duplicate titles that end in a number, append a counter instead of incrementing their number. Fixes #245 --- CHANGES.txt | 3 +++ kotti/tests/test_util.py | 11 +++++++++++ kotti/util.py | 3 +++ 3 files changed, 17 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index e64e345b1..ccebc08ba 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -15,6 +15,9 @@ Change History *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 + 1.0.0-alpha.3 - 2015-01-13 -------------------------- diff --git a/kotti/tests/test_util.py b/kotti/tests/test_util.py index e3a5f0a9b..ccff439dc 100644 --- a/kotti/tests/test_util.py +++ b/kotti/tests/test_util.py @@ -71,6 +71,17 @@ def test_normal(self): from kotti.util import title_to_name assert title_to_name(u'Foo Bar') == u'foo-bar' + def test_numbering(self): + self.setUp() + from kotti.util import title_to_name + assert title_to_name(u'Report Part 1', blacklist=[]) == u'report-part-1' + assert title_to_name(u'Report Part 1', + blacklist=['report-part-1']) == u'report-part-1-1' + assert title_to_name(u'Report Part 3', + blacklist=['report-part-2']) == u'report-part-3' + assert title_to_name(u'Report Part 3', + blacklist=['report-part-3']) == u'report-part-3-1' + def test_disambiguate_name(self): from kotti.util import disambiguate_name assert disambiguate_name(u'foo') == u'foo-1' diff --git a/kotti/util.py b/kotti/util.py index 93b775f3f..d2a427397 100644 --- a/kotti/util.py +++ b/kotti/util.py @@ -299,6 +299,9 @@ def title_to_name(title, blacklist=()): from kotti import get_settings urlnormalizer = get_settings()['kotti.url_normalizer'][0] name = unicode(urlnormalizer(title, locale_name, max_length=40)) + if name not in blacklist: + return name + name += u'-1' while name in blacklist: name = disambiguate_name(name) return name From 967164b74b51207ce253c90108b99b8b730619a1 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 18 Jan 2015 10:27:50 -0800 Subject: [PATCH 059/600] Also test counter increase when blacklist has names with numbers --- kotti/tests/test_util.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kotti/tests/test_util.py b/kotti/tests/test_util.py index ccff439dc..6a12792af 100644 --- a/kotti/tests/test_util.py +++ b/kotti/tests/test_util.py @@ -74,13 +74,15 @@ def test_normal(self): def test_numbering(self): self.setUp() from kotti.util import title_to_name - assert title_to_name(u'Report Part 1', blacklist=[]) == u'report-part-1' + assert title_to_name(u'Report Part 1', + blacklist=[]) == u'report-part-1' assert title_to_name(u'Report Part 1', blacklist=['report-part-1']) == u'report-part-1-1' - assert title_to_name(u'Report Part 3', - blacklist=['report-part-2']) == u'report-part-3' assert title_to_name(u'Report Part 3', blacklist=['report-part-3']) == u'report-part-3-1' + assert title_to_name( + u'Report Part 3', blacklist=['report-part-3', 'report-part-3-1'] + ) == u'report-part-3-2' def test_disambiguate_name(self): from kotti.util import disambiguate_name From cb742289a71af8ca7767ca4067ade25a1941632e Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 26 Jan 2015 13:38:52 -0800 Subject: [PATCH 060/600] Added a paragraph in the developer manual about registering views for context --- docs/developing/basic/developer-manual.rst | 6 ++++++ docs/first_steps/tut-2.rst | 2 ++ 2 files changed, 8 insertions(+) diff --git a/docs/developing/basic/developer-manual.rst b/docs/developing/basic/developer-manual.rst index 7816e8c31..2b8fd30b3 100644 --- a/docs/developing/basic/developer-manual.rst +++ b/docs/developing/basic/developer-manual.rst @@ -47,6 +47,12 @@ Document content type serves as an example here: self.body = body self.mime_type = mime_type +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. + You can configure the list of active content types in Kotti by modifying the :ref:`kotti.available_types` setting. diff --git a/docs/first_steps/tut-2.rst b/docs/first_steps/tut-2.rst index 5deefdd17..9b6339167 100644 --- a/docs/first_steps/tut-2.rst +++ b/docs/first_steps/tut-2.rst @@ -79,6 +79,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 ----------------------- From a849085200fd41d98c06c8d9bc7523fcbc86cbe7 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Thu, 29 Jan 2015 14:59:53 +0100 Subject: [PATCH 061/600] Update all requirements except alembic to their latest versions. --- requirements.txt | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/requirements.txt b/requirements.txt index f14537ca5..f0423d5c0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,25 +1,25 @@ Kotti==1.0.0-alpha.3 Babel==1.3 Beaker==1.6.4 -Chameleon==2.18 +Chameleon==2.20 FormEncode==1.2.6 -Mako==1.0.0 +Mako==1.0.1 MarkupSafe==0.23 PasteDeploy==1.5.2 -Pillow==2.6.1 -Pygments==2.0.1 +Pillow==2.7.0 +Pygments==2.0.2 SQLAlchemy==0.9.8 -Unidecode==0.04.16 +Unidecode==0.04.17 WebOb==1.4 alembic==0.6.7 -argparse==1.2.2 -colander==1.0b1 +argparse==1.3.0 +colander==1.0 deform==2.0a2 docopt==0.6.2 fanstatic==1.0a5 -html2text==2014.9.25 +html2text==2014.12.29 js.angular==1.1.4 -js.bootstrap==3.1.1 +js.bootstrap==3.3.1 js.chosen==0.9.14 js.deform==2.0a2-2 js.fineuploader==3.3.0 @@ -35,21 +35,21 @@ js.jqueryui==1.10.3 js.jqueryui_tagit==2.0.24-2 js.modernizr==2.5.3.1 kotti_tinymce==0.5.1 -lingua==3.6.1 +lingua==3.8 peppercorn==0.5 plone.scale==1.3.4 -polib==1.0.5 +polib==1.0.6 py-bcrypt==0.4 pyramid==1.5.2 pyramid_beaker==0.8 pyramid_chameleon==0.3 -pyramid_debugtoolbar==2.2.2 +pyramid_debugtoolbar==2.3 pyramid_deform==0.2 -pyramid_mailer==0.13 +pyramid_mailer==0.14 pyramid_mako==1.0.2 -pyramid_tm==0.8 +pyramid_tm==0.10 pyramid_zcml==1.0.0 -pytz==2014.9 +pytz==2014.10 repoze.lru==0.6 repoze.sendmail==4.2 repoze.workflow==0.6.1 @@ -65,9 +65,9 @@ xlrd==0.9.3 xlwt==0.7.5 zope.component==4.2.1 zope.configuration==4.0.3 -zope.deprecation==4.1.1 +zope.deprecation==4.1.2 zope.event==4.0.3 zope.i18nmessageid==4.0.3 -zope.interface==4.1.1 +zope.interface==4.1.2 zope.schema==4.4.2 zope.sqlalchemy==0.7.5 From b308204cc82bd15e62a26be107e589d3825aaa8d Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Thu, 29 Jan 2015 17:38:30 +0100 Subject: [PATCH 062/600] Add a changenote. [ci skip] --- CHANGES.txt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4d31157b3..82587d5d5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,16 +1,13 @@ Change History ============== -1.1-dev - unreleased --------------------- - -- Added experimental Docker support. See #374. - 1.0.0-alpha.4 - unreleased -------------------------- +- 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 + 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. @@ -18,6 +15,8 @@ Change History - 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 -------------------------- From 0ca6fe86b3439fb431201f804fb8faed1793bb3c Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Thu, 29 Jan 2015 18:01:32 +0100 Subject: [PATCH 063/600] Also use new requirements in the scaffold. --- kotti/scaffolds/package/requirements.txt | 74 +++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) mode change 120000 => 100644 kotti/scaffolds/package/requirements.txt 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/requirements.txt b/kotti/scaffolds/package/requirements.txt new file mode 100644 index 000000000..f0423d5c0 --- /dev/null +++ b/kotti/scaffolds/package/requirements.txt @@ -0,0 +1,73 @@ +Kotti==1.0.0-alpha.3 +Babel==1.3 +Beaker==1.6.4 +Chameleon==2.20 +FormEncode==1.2.6 +Mako==1.0.1 +MarkupSafe==0.23 +PasteDeploy==1.5.2 +Pillow==2.7.0 +Pygments==2.0.2 +SQLAlchemy==0.9.8 +Unidecode==0.04.17 +WebOb==1.4 +alembic==0.6.7 +argparse==1.3.0 +colander==1.0 +deform==2.0a2 +docopt==0.6.2 +fanstatic==1.0a5 +html2text==2014.12.29 +js.angular==1.1.4 +js.bootstrap==3.3.1 +js.chosen==0.9.14 +js.deform==2.0a2-2 +js.fineuploader==3.3.0 +js.html5shiv==3.6.2-1 +js.jquery==1.9.1 +js.jquery_form==3.09 +js.jquery_maskedinput==1.3.1 +js.jquery_maskmoney==1.4.1 +js.jquery_sortable==0.9.12 +js.jquery_tablednd==0.4 +js.jquery_timepicker_addon==1.3-1 +js.jqueryui==1.10.3 +js.jqueryui_tagit==2.0.24-2 +js.modernizr==2.5.3.1 +kotti_tinymce==0.5.1 +lingua==3.8 +peppercorn==0.5 +plone.scale==1.3.4 +polib==1.0.6 +py-bcrypt==0.4 +pyramid==1.5.2 +pyramid_beaker==0.8 +pyramid_chameleon==0.3 +pyramid_debugtoolbar==2.3 +pyramid_deform==0.2 +pyramid_mailer==0.14 +pyramid_mako==1.0.2 +pyramid_tm==0.10 +pyramid_zcml==1.0.0 +pytz==2014.10 +repoze.lru==0.6 +repoze.sendmail==4.2 +repoze.workflow==0.6.1 +repoze.zcml==0.4 +shutilwhich==1.0.1 +transaction==1.4.3 +translationstring==1.3 +usersettings==1.0.7 +venusian==1.0 +waitress==0.8.9 +wsgiref==0.1.2 +xlrd==0.9.3 +xlwt==0.7.5 +zope.component==4.2.1 +zope.configuration==4.0.3 +zope.deprecation==4.1.2 +zope.event==4.0.3 +zope.i18nmessageid==4.0.3 +zope.interface==4.1.2 +zope.schema==4.4.2 +zope.sqlalchemy==0.7.5 From 0679302be3f404c95b2ef9163ef94ee4554f77ce Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Thu, 29 Jan 2015 18:24:40 +0100 Subject: [PATCH 064/600] Bump version. --- CHANGES.txt | 2 +- kotti/scaffolds/package/requirements.txt | 2 +- requirements.txt | 2 +- setup.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 82587d5d5..b42321cd7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,7 +1,7 @@ Change History ============== -1.0.0-alpha.4 - unreleased +1.0.0-alpha.4 - 2015-01-29 -------------------------- - Added experimental Docker support. See #374. diff --git a/kotti/scaffolds/package/requirements.txt b/kotti/scaffolds/package/requirements.txt index f0423d5c0..0831752a7 100644 --- a/kotti/scaffolds/package/requirements.txt +++ b/kotti/scaffolds/package/requirements.txt @@ -1,4 +1,4 @@ -Kotti==1.0.0-alpha.3 +Kotti==1.0.0-alpha.4 Babel==1.3 Beaker==1.6.4 Chameleon==2.20 diff --git a/requirements.txt b/requirements.txt index f0423d5c0..0831752a7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Kotti==1.0.0-alpha.3 +Kotti==1.0.0-alpha.4 Babel==1.3 Beaker==1.6.4 Chameleon==2.20 diff --git a/setup.py b/setup.py index edff161d4..36641edc7 100644 --- a/setup.py +++ b/setup.py @@ -86,7 +86,7 @@ install_requires.append('ordereddict') setup(name='Kotti', - version='1.1-dev', + version='1.0.0-alpha.4', description="A high-level, Pythonic web application framework based on Pyramid and SQLAlchemy. It includes an extensible Content Management System called the Kotti CMS.", # noqa long_description='\n\n'.join([README, AUTHORS, CHANGES]), classifiers=[ From 976f5e890c0c94307c488a0e7fb04d9c49672361 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Thu, 29 Jan 2015 18:26:40 +0100 Subject: [PATCH 065/600] Bump version. --- CHANGES.txt | 5 +++++ setup.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index b42321cd7..263e845c3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,11 @@ Change History ============== +1.1-dev - unreleased +-------------------- + +- No changes yet. + 1.0.0-alpha.4 - 2015-01-29 -------------------------- diff --git a/setup.py b/setup.py index 36641edc7..edff161d4 100644 --- a/setup.py +++ b/setup.py @@ -86,7 +86,7 @@ install_requires.append('ordereddict') setup(name='Kotti', - version='1.0.0-alpha.4', + version='1.1-dev', description="A high-level, Pythonic web application framework based on Pyramid and SQLAlchemy. It includes an extensible Content Management System called the Kotti CMS.", # noqa long_description='\n\n'.join([README, AUTHORS, CHANGES]), classifiers=[ From 6f507d7626a2de6407c59d5fc6a67f942ca64423 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 30 Nov 2014 14:36:46 -0800 Subject: [PATCH 066/600] Work in progress on using filedepot to store blobs --- development.ini | 2 +- kotti/__init__.py | 9 +++++++++ kotti/resources.py | 16 +++++++++++++++- kotti/views/edit/content.py | 28 ++++++++++++++++++++++------ kotti/views/file.py | 2 +- kotti/views/image.py | 4 ++-- requirements.txt | 2 ++ 7 files changed, 52 insertions(+), 11 deletions(-) diff --git a/development.ini b/development.ini index 76ed43710..e7b34d938 100644 --- a/development.ini +++ b/development.ini @@ -28,7 +28,7 @@ pipeline = [server:main] use = egg:waitress#main -host = 127.0.0.1 +host = 0.0.0.0 port = 5000 [alembic] diff --git a/kotti/__init__.py b/kotti/__init__.py index 2a47b0ac9..971c9baa7 100644 --- a/kotti/__init__.py +++ b/kotti/__init__.py @@ -25,6 +25,15 @@ TRUE_VALUES = ('1', 'y', 'yes', 't', 'true') FALSE_VALUES = ('0', 'n', 'no', 'f', 'false', 'none') +#tibi temp +from depot.manager import DepotManager +# Configure a *default* depot to store files on MongoDB GridFS +DepotManager.configure('default', { + 'depot.backend': 'depot.io.local.LocalFileStorage', + 'depot.storage_path': 'var/files' +}) +#end tibi temp + def authtkt_factory(**settings): from kotti.security import list_groups_callback diff --git a/kotti/resources.py b/kotti/resources.py index f9216d711..b1be85a28 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -13,7 +13,12 @@ from fnmatch import fnmatch from UserDict import DictMixin +from depot.fields.sqlalchemy import UploadedFileField +from depot.fields.sqlalchemy import _SQLAMutationTracker + +from pyramid.threadlocal import get_current_registry from pyramid.traversal import resource_path +from sqlalchemy import event from sqlalchemy import Boolean from sqlalchemy import Column from sqlalchemy import DateTime @@ -644,7 +649,8 @@ class File(Content): id = Column(Integer(), ForeignKey('contents.id'), primary_key=True) #: The binary data itself #: (:class:`sqlalchemy.types.LargeBinary`) - data = deferred(Column("data", LargeBinary())) + #data = deferred(Column("data", LargeBinary())) + data = Column(UploadedFileField) #: The filename is used in the attachment view to give downloads #: the original filename it had when it was uploaded. #: (:class:`sqlalchemy.types.Unicode`) @@ -698,6 +704,14 @@ def from_field_storage(cls, fs): return cls(data=data, filename=filename, mimetype=mimetype, size=size) + @classmethod + def __declare_last__(cls): + # For the ``data`` column, use the field value setter from filedepot. + # filedepot already registers this event listener, but it does so in a + # way that won't work properly for subclasses of File + event.listen(cls.data, 'set', + _SQLAMutationTracker._field_set, retval=True) + class Image(File): """Image doesn't add anything to :class:`~kotti.resources.File`, but images diff --git a/kotti/views/edit/content.py b/kotti/views/edit/content.py index 6e5bcbfbc..7531f62c7 100644 --- a/kotti/views/edit/content.py +++ b/kotti/views/edit/content.py @@ -86,7 +86,7 @@ def before(self, form): form.appstruct = get_appstruct(self.context, self.schema) if self.context.data is not None: form.appstruct.update({'file': { - 'fp': StringIO(self.context.data), + 'fp': StringIO(self.context.data.file.read()), 'filename': self.context.name, 'mimetype': self.context.mimetype, 'uid': str(random.randint(1000000000, 9999999999)), @@ -102,11 +102,18 @@ def edit(self, **appstruct): self.context.description = appstruct['description'] self.context.tags = appstruct['tags'] if appstruct['file']: - buf = appstruct['file']['fp'].read() + buf = appstruct['file']['fp'] + size = 0 + while True: + chunk = buf.read(1024) + if not chunk: + break + size += len(chunk) + buf.seek(0) self.context.data = buf self.context.filename = appstruct['file']['filename'] self.context.mimetype = appstruct['file']['mimetype'] - self.context.size = len(buf) + self.context.size = size class FileAddForm(AddFormView): @@ -123,17 +130,26 @@ def save_success(self, appstruct): return super(FileAddForm, self).save_success(appstruct) def add(self, **appstruct): - buf = appstruct['file']['fp'].read() + buf = appstruct['file']['fp'] #.read() + size = 0 + while True: + chunk = buf.read(1024) + if not chunk: + break + size += len(chunk) + buf.seek(0) filename = appstruct['file']['filename'] - return self.item_class( + item = self.item_class( title=appstruct['title'] or filename, description=appstruct['description'], tags=appstruct['tags'], data=buf, filename=filename, mimetype=appstruct['file']['mimetype'], - size=len(buf), + size=size, ) + import pdb; pdb.set_trace() + return item class ImageEditForm(FileEditForm): diff --git a/kotti/views/file.py b/kotti/views/file.py index 1c81e627d..36dd0aa7d 100644 --- a/kotti/views/file.py +++ b/kotti/views/file.py @@ -22,7 +22,7 @@ def inline_view(context, request, disposition='inline'): ('Content-Type', str(context.mimetype)), ] ) - res.body = context.data + res.body = context.data.file.read() return res diff --git a/kotti/views/image.py b/kotti/views/image.py index ec49edb7b..5c98e9d2b 100644 --- a/kotti/views/image.py +++ b/kotti/views/image.py @@ -88,12 +88,12 @@ def image(self, subpath=None): width, height = image_scales[scale] if width and height: - image, format, size = scaleImage(self.context.data, + image, format, size = scaleImage(self.context.data.file.read(), width=width, height=height, direction="thumb") else: - image = self.context.data + image = self.context.data.file.read() res = Response( headerlist=[('Content-Disposition', '%s;filename="%s"' % ( diff --git a/requirements.txt b/requirements.txt index 0831752a7..7db5873aa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,6 @@ Kotti==1.0.0-alpha.4 +-e git+ssh://git@github.com/amol-/depot.git#egg=filedepot + Babel==1.3 Beaker==1.6.4 Chameleon==2.20 From d08a5dda607123af00c2c0b9f73cdc4392c97d74 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 2 Dec 2014 17:51:49 +0200 Subject: [PATCH 067/600] Cleanup code; added mock for the data column for test_file; make tests pass --- kotti/__init__.py | 5 ++--- kotti/resources.py | 1 - kotti/tests/test_file.py | 24 ++++++++++++++++++++---- kotti/views/edit/content.py | 3 +-- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/kotti/__init__.py b/kotti/__init__.py index 971c9baa7..bc77dd84d 100644 --- a/kotti/__init__.py +++ b/kotti/__init__.py @@ -25,14 +25,13 @@ TRUE_VALUES = ('1', 'y', 'yes', 't', 'true') FALSE_VALUES = ('0', 'n', 'no', 'f', 'false', 'none') -#tibi temp +# tibi temp from depot.manager import DepotManager -# Configure a *default* depot to store files on MongoDB GridFS DepotManager.configure('default', { 'depot.backend': 'depot.io.local.LocalFileStorage', 'depot.storage_path': 'var/files' }) -#end tibi temp +# end tibi temp def authtkt_factory(**settings): diff --git a/kotti/resources.py b/kotti/resources.py index b1be85a28..088080977 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -649,7 +649,6 @@ class File(Content): id = Column(Integer(), ForeignKey('contents.id'), primary_key=True) #: The binary data itself #: (:class:`sqlalchemy.types.LargeBinary`) - #data = deferred(Column("data", LargeBinary())) data = Column(UploadedFileField) #: The filename is used in the attachment view to give downloads #: the original filename it had when it was uploaded. diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index 22399b3f3..41ddb56ba 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -42,7 +42,23 @@ def test_attachment_view(self, config): class TestFileEditForm: def make_one(self): from kotti.views.edit.content import FileEditForm - return FileEditForm(MagicMock(), DummyRequest()) + + class MockFileColumn(object): + def __init__(self): + self.file = MagicMock() + + def __set__(self, instance, value): + if isinstance(value, StringIO): + value.seek(0) + rv = value.read() + else: + rv = value + self.file.read.return_value = rv + + class MockDepotFile(object): + data = MockFileColumn() + + return FileEditForm(MockDepotFile(), DummyRequest()) def test_edit_with_file(self): view = self.make_one() @@ -57,7 +73,7 @@ def test_edit_with_file(self): ) assert view.context.title == u'A title' assert view.context.description == u'A description' - assert view.context.data == 'filecontents' + assert view.context.data.file.read() == 'filecontents' assert view.context.filename == u'myfile.png' assert view.context.mimetype == u'image/png' assert view.context.size == len('filecontents') @@ -75,7 +91,7 @@ def test_edit_without_file(self): file=null) assert view.context.title == u'A title' assert view.context.description == u'A description' - assert view.context.data == 'filecontents' + assert view.context.data.file.read() == 'filecontents' assert view.context.filename == u'myfile.png' assert view.context.mimetype == u'image/png' assert view.context.size == 777 @@ -102,7 +118,7 @@ def test_add(self, config): assert file.title == u'A title' assert file.description == u'A description' assert file.tags == [] - assert file.data == 'filecontents' + assert file.data.file.read() == 'filecontents' assert file.filename == u'myfile.png' assert file.mimetype == u'image/png' assert file.size == len('filecontents') diff --git a/kotti/views/edit/content.py b/kotti/views/edit/content.py index 7531f62c7..1891c1150 100644 --- a/kotti/views/edit/content.py +++ b/kotti/views/edit/content.py @@ -130,7 +130,7 @@ def save_success(self, appstruct): return super(FileAddForm, self).save_success(appstruct) def add(self, **appstruct): - buf = appstruct['file']['fp'] #.read() + buf = appstruct['file']['fp'] size = 0 while True: chunk = buf.read(1024) @@ -148,7 +148,6 @@ def add(self, **appstruct): mimetype=appstruct['file']['mimetype'], size=size, ) - import pdb; pdb.set_trace() return item From 86467a5e9ee28670b0cea3e7e69d8279625587c4 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 2 Dec 2014 13:21:26 -0800 Subject: [PATCH 068/600] Added test for File and Image that proves usage of depot store --- kotti/tests/test_file.py | 59 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index 41ddb56ba..e32933df0 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -140,3 +140,62 @@ def test_delitem(self): tmpstore.session['important'] = 3 del tmpstore['important'] assert 'important' not in tmpstore.session + + +class TestFileUsesDepotStorage: + + @classmethod + def setup_class(cls): + from depot.manager import DepotManager + from mock import Mock + + class TestStorage: + def __init__(self): + self._storage = {} + self._storage.setdefault(0) + + def get(self, id): + f = Mock() + f.read.return_value = self._storage[id] + return f + + def create(self, content, filename=None, content_type=None): + id = max(self._storage) + 1 + self._storage[id] = content + return id + + DepotManager._depots = { + 'default': TestStorage() + } + + @classmethod + def teardown_class(cls): + from depot.manager import DepotManager + DepotManager._depots = {} + + def test_create_file(self): + from kotti.resources import File + f = File('file content') + assert len(f.data['files']) == 1 + assert f.data.file.read() == 'file content' + + def test_edit_file_content(self): + from kotti.resources import File + f = File('file content') + assert f.data.file.read() == 'file content' + f.data = 'edited' + assert f.data.file.read() == 'edited' + + def test_create_image(self): + from kotti.resources import Image + f = Image('file content') + assert len(f.data['files']) == 1 + assert f.data.file.read() == 'file content' + + def test_edit_image_content(self): + from kotti.resources import Image + f = Image('file content') + assert f.data.file.read() == 'file content' + f.data = 'edited' + assert f.data.file.read() == 'edited' + From 16c0e521f1073f1f1eab647fdb752420151ae693 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 2 Dec 2014 14:35:02 -0800 Subject: [PATCH 069/600] Test session rollback integration for filedepot --- kotti/tests/test_file.py | 56 ++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index e32933df0..b68666c0f 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -1,7 +1,8 @@ from StringIO import StringIO - from colander import null + from mock import MagicMock +import pytest from kotti.testing import DummyRequest @@ -142,12 +143,12 @@ def test_delitem(self): assert 'important' not in tmpstore.session -class TestFileUsesDepotStorage: +class TestDepotStore: @classmethod def setup_class(cls): from depot.manager import DepotManager - from mock import Mock + from datetime import datetime class TestStorage: def __init__(self): @@ -155,8 +156,12 @@ def __init__(self): self._storage.setdefault(0) def get(self, id): - f = Mock() + f = MagicMock() f.read.return_value = self._storage[id] + f.last_modified = datetime.now() + f.filename = str(id) + f.public_url = '' + f.content_type = 'image/png' return f def create(self, content, filename=None, content_type=None): @@ -164,8 +169,11 @@ def create(self, content, filename=None, content_type=None): self._storage[id] = content return id + def delete(self, id): + del self._storage[int(id)] + DepotManager._depots = { - 'default': TestStorage() + 'default': MagicMock(wraps=TestStorage()) } @classmethod @@ -173,29 +181,33 @@ def teardown_class(cls): from depot.manager import DepotManager DepotManager._depots = {} - def test_create_file(self): - from kotti.resources import File - f = File('file content') + from kotti.resources import File, Image + + @pytest.mark.parametrize("factory", [File, Image]) + def test_create(self, factory): + f = factory('file content') assert len(f.data['files']) == 1 assert f.data.file.read() == 'file content' - def test_edit_file_content(self): - from kotti.resources import File - f = File('file content') + @pytest.mark.parametrize("factory", [File, Image]) + def test_edit_content(self, factory): + f = factory('file content') assert f.data.file.read() == 'file content' f.data = 'edited' assert f.data.file.read() == 'edited' - def test_create_image(self): - from kotti.resources import Image - f = Image('file content') - assert len(f.data['files']) == 1 - assert f.data.file.read() == 'file content' + @pytest.mark.parametrize("factory", [File, Image]) + def test_session_rollback(self, factory, db_session): + from depot.manager import DepotManager - def test_edit_image_content(self): - from kotti.resources import Image - f = Image('file content') - assert f.data.file.read() == 'file content' - f.data = 'edited' - assert f.data.file.read() == 'edited' + f = factory(data='file content', name=u'content', title=u'content') + id = f.data['file_id'] + + assert id in DepotManager.get()._storage.keys() + + db_session.add(f) + db_session.flush() + db_session.rollback() + assert DepotManager.get().delete.called + assert id not in DepotManager.get()._storage.keys() From 2ac03829f14ed56aceff9045b10895a475807d01 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 2 Dec 2014 14:38:06 -0800 Subject: [PATCH 070/600] Better test in depot rollback test --- kotti/tests/test_file.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index b68666c0f..b34397b33 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -207,7 +207,7 @@ def test_session_rollback(self, factory, db_session): db_session.add(f) db_session.flush() + assert id in DepotManager.get()._storage.keys() db_session.rollback() - - assert DepotManager.get().delete.called assert id not in DepotManager.get()._storage.keys() + assert DepotManager.get().delete.called From 23fd86c8ccc00818b52c225aa1b67a462c022a7c Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 2 Dec 2014 15:13:13 -0800 Subject: [PATCH 071/600] Added test for depot filestorage deletion --- kotti/tests/test_file.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index b34397b33..01c4da28c 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -158,10 +158,13 @@ def __init__(self): def get(self, id): f = MagicMock() f.read.return_value = self._storage[id] + + # needed to make JSON serializable, Mock objects are not f.last_modified = datetime.now() f.filename = str(id) f.public_url = '' f.content_type = 'image/png' + return f def create(self, content, filename=None, content_type=None): @@ -211,3 +214,20 @@ def test_session_rollback(self, factory, db_session): db_session.rollback() assert id not in DepotManager.get()._storage.keys() assert DepotManager.get().delete.called + + @pytest.mark.parametrize("factory", [File, Image]) + def test_delete(self, factory, db_session, root): + from depot.manager import DepotManager + + f = factory(data='file content', name=u'content', title=u'content') + id = f.data['file_id'] + root[str(id)] = f + db_session.flush() + + assert id in DepotManager.get()._storage.keys() + + del root[str(id)] + import transaction; transaction.commit() + + assert DepotManager.get().delete.called + assert id not in DepotManager.get()._storage.keys() From c6747aec4003884da8cb24db41c78affa3f2ddac Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 2 Dec 2014 16:56:19 -0800 Subject: [PATCH 072/600] Added stuf implementation of filestorage --- kotti/resources.py | 176 ++++++++++++++++++++++++++++++++++++++- kotti/tests/test_file.py | 3 +- 2 files changed, 175 insertions(+), 4 deletions(-) diff --git a/kotti/resources.py b/kotti/resources.py index 088080977..0687318ba 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -9,12 +9,16 @@ """ import os +import uuid import warnings + +from datetime import datetime from fnmatch import fnmatch from UserDict import DictMixin from depot.fields.sqlalchemy import UploadedFileField from depot.fields.sqlalchemy import _SQLAMutationTracker +from depot.io.interfaces import StoredFile, FileStorage from pyramid.threadlocal import get_current_registry from pyramid.traversal import resource_path @@ -647,8 +651,8 @@ class File(Content): #: 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`) + #: Filedepot mapped blob + #: (:class:`depot.fileds.sqlalchemy.UploadedFileField`) data = Column(UploadedFileField) #: The filename is used in the attachment view to give downloads #: the original filename it had when it was uploaded. @@ -731,6 +735,174 @@ class Image(File): ) +class CooperativeMeta(type): + def __new__(cls, name, bases, members): + #collect up the metaclasses + metas = [type(base) for base in bases] + + # prune repeated or conflicting entries + metas = [meta for index, meta in enumerate(metas) + if not [later for later in metas[index+1:] + if issubclass(later, meta)]] + + # whip up the actual combined meta class derive off all of these + meta = type(name, tuple(metas), dict(combined_metas = metas)) + + # make the actual object + return meta(name, bases, members) + + def __init__(self, name, bases, members): + for meta in self.combined_metas: + meta.__init__(self, name, bases, members) + + +class DBStoredFile(Base, StoredFile): + """depotfile StoredFile implementation that stores data in the db. + + Can be used together with DBFileStorage to implement blobs (large files) + storage in the database. + """ + + __tablename__ = "blobs" + __metaclass__ = CooperativeMeta + + #: Primary key column in the DB + #: (:class:`sqlalchemy.types.Integer`) + id = Column(Integer(), primary_key=True) + #: Unique 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(30)) + #: Size of the blob in bytes + #: (:class:`sqlalchemy.types.Integer`) + content_length = Column(Integer()) + #: Date / time the blob was created + #: (:class:`sqlalchemy.types.DateTime`) + last_modified = Column(DateTime()) + #: The binary data itself + #: (:class:`sqlalchemy.types.LargeBinary`) + data = deferred(Column('data', LargeBinary())) + + # def __init__(self, **kw): + # for k, v in kw.items(): + # setattr(self, k, v) + + def read(self, n=-1): # pragma: no cover + """Reads ``n`` bytes from the file. + + If ``n`` is not specified or is ``-1`` the whole + file content is read in memory and returned + """ + return + + def close(self, *args, **kwargs): # pragma: no cover + """Closes the file. + + After closing the file it won't be possible to read + from it anymore. Some implementation might not do anything + when closing the file, but they still are required to prevent + further reads from a closed file. + """ + return + + def closed(self): # pragma: no cover + """Returns if the file has been closed. + + When ``closed`` return ``True`` it won't be possible + to read anoymore from this file. + """ + return + + +class DBFileStorage(FileStorage): + """ depotfile FileStorage implementation, uses DBStoredFile to store data + """ + + def get(self, file_id): + """Returns the file given by the file_id + """ + + f = DBSession.query(DBStoredFile).filter_by(file_id=file_id).first() + if f is None: + raise IOError + return f + + def create(self, content, filename=None, content_type=None): + """Saves a new file and returns the file id + + ``content`` parameter 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. + """ + 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, + content_length=len(content), + last_modified=datetime.now()) + DBSession.add(fstore) + return new_file_id + + def replace(self, file_or_id, content, filename=None, content_type=None): # pragma: no cover + """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. + """ + + if isinstance(file_or_id, StoredFile): + file_id = file_or_id.file_id + else: + 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): # pragma: no cover + """Deletes a file. If the file didn't exist it will just do nothing.""" + + if isinstance(file_or_id, StoredFile): + file_id = file_or_id.file_id + else: + file_id = file_or_id + fstore = self.get(file_id) + DBSession.delete(fstore) + + def exists(self, file_or_id): # pragma: no cover + """Returns if a file or its ID still exist.""" + if isinstance(file_or_id, StoredFile): + file_id = file_or_id.file_id + else: + file_id = file_or_id + return bool( + DBSession.query(StoredFile).filter_by(file_id=file_id).count()) + + def get_root(request=None): """Call the function defined by the ``kotti.root_factory`` setting and return its result. diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index 01c4da28c..9b748e48d 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -206,11 +206,10 @@ def test_session_rollback(self, factory, db_session): f = factory(data='file content', name=u'content', title=u'content') id = f.data['file_id'] - assert id in DepotManager.get()._storage.keys() - db_session.add(f) db_session.flush() assert id in DepotManager.get()._storage.keys() + db_session.rollback() assert id not in DepotManager.get()._storage.keys() assert DepotManager.get().delete.called From 51c90b8b7599fd26a36edb6a708457931f34b883 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Thu, 4 Dec 2014 14:51:24 +0200 Subject: [PATCH 073/600] Moved filedepot code to filedepot module; make a test fixture for filedepot; properly implement filedepot protocol in fixture --- kotti/filedepot.py | 178 +++++++++++++++++++++++++++++++++ kotti/resources.py | 174 -------------------------------- kotti/tests/__init__.py | 43 ++++++++ kotti/tests/test_file.py | 50 ++------- kotti/tests/test_functional.py | 2 +- kotti/views/edit/content.py | 2 +- 6 files changed, 229 insertions(+), 220 deletions(-) create mode 100644 kotti/filedepot.py diff --git a/kotti/filedepot.py b/kotti/filedepot.py new file mode 100644 index 000000000..efa9211cb --- /dev/null +++ b/kotti/filedepot.py @@ -0,0 +1,178 @@ +from datetime import datetime +from depot.io.interfaces import StoredFile, FileStorage + +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.orm import deferred + +from kotti import Base +from kotti import DBSession + +import uuid + +class CooperativeMeta(type): + def __new__(cls, name, bases, members): + # collect up the metaclasses + metas = [type(base) for base in bases] + + # prune repeated or conflicting entries + metas = [meta for index, meta in enumerate(metas) + if not [later for later in metas[index + 1:] + if issubclass(later, meta)]] + + # whip up the actual combined meta class derive off all of these + meta = type(name, tuple(metas), dict(combined_metas=metas)) + + # make the actual object + return meta(name, bases, members) + + def __init__(self, name, bases, members): + for meta in self.combined_metas: + meta.__init__(self, name, bases, members) + + +class DBStoredFile(Base, StoredFile): + """depotfile StoredFile implementation that stores data in the db. + + Can be used together with DBFileStorage to implement blobs (large files) + storage in the database. + """ + + __tablename__ = "blobs" + __metaclass__ = CooperativeMeta + + #: Primary key column in the DB + #: (:class:`sqlalchemy.types.Integer`) + id = Column(Integer(), primary_key=True) + #: Unique 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(30)) + #: Size of the blob in bytes + #: (:class:`sqlalchemy.types.Integer`) + content_length = Column(Integer()) + #: Date / time the blob was created + #: (:class:`sqlalchemy.types.DateTime`) + last_modified = Column(DateTime()) + #: The binary data itself + #: (:class:`sqlalchemy.types.LargeBinary`) + data = deferred(Column('data', LargeBinary())) + + def read(self, n=-1): # pragma: no cover + """Reads ``n`` bytes from the file. + + If ``n`` is not specified or is ``-1`` the whole + file content is read in memory and returned + """ + return + + def close(self, *args, **kwargs): # pragma: no cover + """Closes the file. + + After closing the file it won't be possible to read + from it anymore. Some implementation might not do anything + when closing the file, but they still are required to prevent + further reads from a closed file. + """ + return + + def closed(self): # pragma: no cover + """Returns if the file has been closed. + + When ``closed`` return ``True`` it won't be possible + to read anoymore from this file. + """ + return + + +class DBFileStorage(FileStorage): + """ depotfile FileStorage implementation, uses DBStoredFile to store data + """ + + def get(self, file_id): + """Returns the file given by the file_id + """ + + f = DBSession.query(DBStoredFile).filter_by(file_id=file_id).first() + if f is None: + raise IOError + return f + + def create(self, content, filename=None, content_type=None): + """Saves a new file and returns the file id + + ``content`` parameter 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. + """ + 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, + content_length=len(content), + last_modified=datetime.now()) + DBSession.add(fstore) + return new_file_id + + def replace(self, file_or_id, content, filename=None, content_type=None): # pragma: no cover + """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. + """ + + if isinstance(file_or_id, StoredFile): + file_id = file_or_id.file_id + else: + 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): # pragma: no cover + """Deletes a file. If the file didn't exist it will just do nothing.""" + + if isinstance(file_or_id, StoredFile): + file_id = file_or_id.file_id + else: + file_id = file_or_id + fstore = self.get(file_id) + DBSession.delete(fstore) + + def exists(self, file_or_id): # pragma: no cover + """Returns if a file or its ID still exist.""" + if isinstance(file_or_id, StoredFile): + file_id = file_or_id.file_id + else: + file_id = file_or_id + return bool( + DBSession.query(StoredFile).filter_by(file_id=file_id).count()) diff --git a/kotti/resources.py b/kotti/resources.py index 0687318ba..f7a15d3ea 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -9,18 +9,14 @@ """ import os -import uuid import warnings -from datetime import datetime from fnmatch import fnmatch from UserDict import DictMixin from depot.fields.sqlalchemy import UploadedFileField from depot.fields.sqlalchemy import _SQLAMutationTracker -from depot.io.interfaces import StoredFile, FileStorage -from pyramid.threadlocal import get_current_registry from pyramid.traversal import resource_path from sqlalchemy import event from sqlalchemy import Boolean @@ -28,7 +24,6 @@ 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 @@ -37,7 +32,6 @@ from sqlalchemy.ext.hybrid import hybrid_property 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.exc import NoResultFound @@ -735,174 +729,6 @@ class Image(File): ) -class CooperativeMeta(type): - def __new__(cls, name, bases, members): - #collect up the metaclasses - metas = [type(base) for base in bases] - - # prune repeated or conflicting entries - metas = [meta for index, meta in enumerate(metas) - if not [later for later in metas[index+1:] - if issubclass(later, meta)]] - - # whip up the actual combined meta class derive off all of these - meta = type(name, tuple(metas), dict(combined_metas = metas)) - - # make the actual object - return meta(name, bases, members) - - def __init__(self, name, bases, members): - for meta in self.combined_metas: - meta.__init__(self, name, bases, members) - - -class DBStoredFile(Base, StoredFile): - """depotfile StoredFile implementation that stores data in the db. - - Can be used together with DBFileStorage to implement blobs (large files) - storage in the database. - """ - - __tablename__ = "blobs" - __metaclass__ = CooperativeMeta - - #: Primary key column in the DB - #: (:class:`sqlalchemy.types.Integer`) - id = Column(Integer(), primary_key=True) - #: Unique 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(30)) - #: Size of the blob in bytes - #: (:class:`sqlalchemy.types.Integer`) - content_length = Column(Integer()) - #: Date / time the blob was created - #: (:class:`sqlalchemy.types.DateTime`) - last_modified = Column(DateTime()) - #: The binary data itself - #: (:class:`sqlalchemy.types.LargeBinary`) - data = deferred(Column('data', LargeBinary())) - - # def __init__(self, **kw): - # for k, v in kw.items(): - # setattr(self, k, v) - - def read(self, n=-1): # pragma: no cover - """Reads ``n`` bytes from the file. - - If ``n`` is not specified or is ``-1`` the whole - file content is read in memory and returned - """ - return - - def close(self, *args, **kwargs): # pragma: no cover - """Closes the file. - - After closing the file it won't be possible to read - from it anymore. Some implementation might not do anything - when closing the file, but they still are required to prevent - further reads from a closed file. - """ - return - - def closed(self): # pragma: no cover - """Returns if the file has been closed. - - When ``closed`` return ``True`` it won't be possible - to read anoymore from this file. - """ - return - - -class DBFileStorage(FileStorage): - """ depotfile FileStorage implementation, uses DBStoredFile to store data - """ - - def get(self, file_id): - """Returns the file given by the file_id - """ - - f = DBSession.query(DBStoredFile).filter_by(file_id=file_id).first() - if f is None: - raise IOError - return f - - def create(self, content, filename=None, content_type=None): - """Saves a new file and returns the file id - - ``content`` parameter 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. - """ - 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, - content_length=len(content), - last_modified=datetime.now()) - DBSession.add(fstore) - return new_file_id - - def replace(self, file_or_id, content, filename=None, content_type=None): # pragma: no cover - """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. - """ - - if isinstance(file_or_id, StoredFile): - file_id = file_or_id.file_id - else: - 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): # pragma: no cover - """Deletes a file. If the file didn't exist it will just do nothing.""" - - if isinstance(file_or_id, StoredFile): - file_id = file_or_id.file_id - else: - file_id = file_or_id - fstore = self.get(file_id) - DBSession.delete(fstore) - - def exists(self, file_or_id): # pragma: no cover - """Returns if a file or its ID still exist.""" - if isinstance(file_or_id, StoredFile): - file_id = file_or_id.file_id - else: - file_id = file_or_id - return bool( - DBSession.query(StoredFile).filter_by(file_id=file_id).count()) - - def get_root(request=None): """Call the function defined by the ``kotti.root_factory`` setting and return its result. diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index c9366a26a..0910e0b2e 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -9,6 +9,7 @@ digraph kotti_fixtures { "allwarnings"; + "filedepot"; "app" -> "webtest"; "config" -> "db_session"; "config" -> "dummy_request"; @@ -41,6 +42,7 @@ import warnings from pytest import fixture +from mock import MagicMock @fixture @@ -276,3 +278,44 @@ def workflow(config): from zope.configuration import xmlconfig import kotti xmlconfig.file('workflow.zcml', kotti, execute=True) + + +@fixture +def filedepot(request): + from depot.manager import DepotManager + from datetime import datetime + + class TestStorage: + def __init__(self): + self._storage = {} + self._storage.setdefault(0) + + def get(self, id): + f = MagicMock() + f.read.return_value = self._storage[id] + + # needed to make JSON serializable, Mock objects are not + f.last_modified = datetime.now() + f.filename = str(id) + f.public_url = '' + f.content_type = 'image/png' + + return f + + def create(self, content, filename=None, content_type=None): + id = max(self._storage) + 1 + if hasattr(content, 'read'): + content = content.read() + self._storage[id] = content + return id + + def delete(self, id): + del self._storage[int(id)] + + DepotManager._depots = { + 'default': MagicMock(wraps=TestStorage()) + } + + def restore(): + DepotManager._depots = {} + request.addfinalizer(restore) diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index 9b748e48d..77003a77d 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -145,62 +145,23 @@ def test_delitem(self): class TestDepotStore: - @classmethod - def setup_class(cls): - from depot.manager import DepotManager - from datetime import datetime - - class TestStorage: - def __init__(self): - self._storage = {} - self._storage.setdefault(0) - - def get(self, id): - f = MagicMock() - f.read.return_value = self._storage[id] - - # needed to make JSON serializable, Mock objects are not - f.last_modified = datetime.now() - f.filename = str(id) - f.public_url = '' - f.content_type = 'image/png' - - return f - - def create(self, content, filename=None, content_type=None): - id = max(self._storage) + 1 - self._storage[id] = content - return id - - def delete(self, id): - del self._storage[int(id)] - - DepotManager._depots = { - 'default': MagicMock(wraps=TestStorage()) - } - - @classmethod - def teardown_class(cls): - from depot.manager import DepotManager - DepotManager._depots = {} - from kotti.resources import File, Image @pytest.mark.parametrize("factory", [File, Image]) - def test_create(self, factory): + def test_create(self, factory, filedepot): f = factory('file content') assert len(f.data['files']) == 1 assert f.data.file.read() == 'file content' @pytest.mark.parametrize("factory", [File, Image]) - def test_edit_content(self, factory): + def test_edit_content(self, factory, filedepot): f = factory('file content') assert f.data.file.read() == 'file content' f.data = 'edited' assert f.data.file.read() == 'edited' @pytest.mark.parametrize("factory", [File, Image]) - def test_session_rollback(self, factory, db_session): + def test_session_rollback(self, factory, db_session, filedepot): from depot.manager import DepotManager f = factory(data='file content', name=u'content', title=u'content') @@ -215,7 +176,7 @@ def test_session_rollback(self, factory, db_session): assert DepotManager.get().delete.called @pytest.mark.parametrize("factory", [File, Image]) - def test_delete(self, factory, db_session, root): + def test_delete(self, factory, db_session, root, filedepot): from depot.manager import DepotManager f = factory(data='file content', name=u'content', title=u'content') @@ -226,7 +187,8 @@ def test_delete(self, factory, db_session, root): assert id in DepotManager.get()._storage.keys() del root[str(id)] - import transaction; transaction.commit() + import transaction + transaction.commit() assert DepotManager.get().delete.called assert id not in DepotManager.get()._storage.keys() diff --git a/kotti/tests/test_functional.py b/kotti/tests/test_functional.py index e6a238bbe..9bdd605e1 100644 --- a/kotti/tests/test_functional.py +++ b/kotti/tests/test_functional.py @@ -64,7 +64,7 @@ def test_tempstorage(self, browser): assert browser.contents == 'DEF' @user('admin') - def test_edit_uploaded_file(self, browser): + def test_edit_uploaded_file(self, browser, filedepot): browser.open(BASE_URL + '/@@add_file') self.add_file(browser, contents='GHI') browser.getLink("Edit").click() diff --git a/kotti/views/edit/content.py b/kotti/views/edit/content.py index 1891c1150..23f095db9 100644 --- a/kotti/views/edit/content.py +++ b/kotti/views/edit/content.py @@ -147,7 +147,7 @@ def add(self, **appstruct): filename=filename, mimetype=appstruct['file']['mimetype'], size=size, - ) + ) return item From 90a5fe5cbdd8192649190bb415d8ff3c76186b8d Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Thu, 4 Dec 2014 16:10:44 -0800 Subject: [PATCH 074/600] Configure and tests a default file depot --- kotti/__init__.py | 11 +++-------- kotti/filedepot.py | 15 +++++++++++++++ kotti/tests/test_app.py | 39 +++++++++++++++++++++++++++++++++++++++ kotti/util.py | 26 ++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 8 deletions(-) diff --git a/kotti/__init__.py b/kotti/__init__.py index bc77dd84d..c23723ae0 100644 --- a/kotti/__init__.py +++ b/kotti/__init__.py @@ -25,14 +25,6 @@ TRUE_VALUES = ('1', 'y', 'yes', 't', 'true') FALSE_VALUES = ('0', 'n', 'no', 'f', 'false', 'none') -# tibi temp -from depot.manager import DepotManager -DepotManager.configure('default', { - 'depot.backend': 'depot.io.local.LocalFileStorage', - 'depot.storage_path': 'var/files' -}) -# end tibi temp - def authtkt_factory(**settings): from kotti.security import list_groups_callback @@ -67,6 +59,7 @@ def none_factory(**kwargs): # pragma: no cover 'pyramid.includes': '', 'kotti.base_includes': ' '.join([ 'kotti', + 'kotti.filedepot', 'kotti.events', 'kotti.views', 'kotti.views.cache', @@ -108,6 +101,8 @@ def none_factory(**kwargs): # pragma: no cover 'kotti.datetime_format': 'medium', 'kotti.time_format': 'medium', 'kotti.max_file_size': '10', + 'kotti.depot.default.backend': 'depot.io.local.LocalFileStorage', + 'kotti.depot.default.storage_path': 'var/files', 'kotti.fanstatic.edit_needed': 'kotti.fanstatic.edit_needed', 'kotti.fanstatic.view_needed': 'kotti.fanstatic.view_needed', 'kotti.static.edit_needed': '', # BBB diff --git a/kotti/filedepot.py b/kotti/filedepot.py index efa9211cb..a80633444 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -14,6 +14,7 @@ import uuid + class CooperativeMeta(type): def __new__(cls, name, bases, members): # collect up the metaclasses @@ -176,3 +177,17 @@ def exists(self, file_or_id): # pragma: no cover file_id = file_or_id return bool( DBSession.query(StoredFile).filter_by(file_id=file_id).count()) + + +def configure_filedepot(settings): + from kotti.util import flatdotted_to_dict + from depot.manager import DepotManager + + config = flatdotted_to_dict('kotti.depot.', settings) + for name, conf in config.items(): + if DepotManager.get(name) is None: + DepotManager.configure(name, conf, prefix='') + + +def includeme(config): + configure_filedepot(config.get_settings()) diff --git a/kotti/tests/test_app.py b/kotti/tests/test_app.py index 00cbd41cb..33384c737 100644 --- a/kotti/tests/test_app.py +++ b/kotti/tests/test_app.py @@ -148,6 +148,45 @@ def test_setting_values_as_unicode(self, db_session): assert get_settings()['kotti_foo.site_title'] == u'K\xf6tti' assert get_settings()['foo.site_title'] == 'K\xc3\xb6tti' + def test_default_filedepot(self, db_session): + from kotti import main + from depot.manager import DepotManager + + settings = self.required_settings() + + with patch('kotti.resources.initialize_sql'): + main({}, **settings) + assert DepotManager.get().__class__.__name__ == 'LocalFileStorage' + + def test_configure_filedepot(self): + from depot.manager import DepotManager + from kotti.filedepot import configure_filedepot + from kotti import tests + + tests.TFS1 = Mock(return_value=Mock(marker="TFS1")) + tests.TFS2 = Mock(return_value=Mock(marker="TFS2")) + + settings = { + 'kotti.depot.default.backend': 'kotti.tests.TFS1', + 'kotti.depot.default.location': '/tmp', + 'kotti.depot.mongo.backend': 'kotti.tests.TFS2', + 'kotti.depot.mongo.uri': 'mongo://', + } + depots = DepotManager._depots + DepotManager._depots = {} + configure_filedepot(settings) + + assert DepotManager.get().marker == 'TFS1' + assert DepotManager.get('default').marker == 'TFS1' + assert DepotManager.get('mongo').marker == 'TFS2' + + tests.TFS1.assert_called_with(location='/tmp') + tests.TFS2.assert_called_with(uri='mongo://') + + DepotManager._depots = depots + del tests.TFS1 + del tests.TFS2 + def test_search_content(self, db_session): from kotti import main from kotti.views.util import search_content diff --git a/kotti/util.py b/kotti/util.py index d2a427397..4cd225cfd 100644 --- a/kotti/util.py +++ b/kotti/util.py @@ -276,6 +276,32 @@ def extract_from_settings(prefix, settings=None): return extracted +def flatdotted_to_dict(prefix, settings=None): + """ + >>> settings = { + ... 'kotti.depot.default.backend': 'local', + ... 'kotti.depot.default.file_storage': 'var/files', + ... 'kotti.depot.mongo.backend': 'mongodb', + ... 'kotti.depot.mongo.uri': 'localhost://', + ... } + >>> res = flatdotted_to_dict('kotti.depot.', settings) + >>> print sorted(res.keys()) + ['default', 'mongo'] + >>> print res['default'] + {'file_storage': 'var/files', 'backend': 'local'} + >>> print res['mongo'] + {'uri': 'localhost://', 'backend': 'mongodb'} + """ + + extracted = {} + for k, v in extract_from_settings(prefix, settings).items(): + name, conf = k.split('.', 1) + extracted.setdefault(name, {}) + extracted[name][conf] = v + + return extracted + + def disambiguate_name(name): parts = name.split(u'-') if len(parts) > 1: From a4aca27fb830cbbb238ca8c65be0bc42c3c926a9 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sat, 6 Dec 2014 12:09:21 -0800 Subject: [PATCH 075/600] DBStoredFile changes * Automatically set last_modified and content_length with an event * Stop subclassing StoredFile, remove CooperativeMeta * Added a test, test_filedepot.py --- kotti/filedepot.py | 109 ++++++++++++++++++++++------------ kotti/tests/test_filedepot.py | 63 ++++++++++++++++++++ 2 files changed, 135 insertions(+), 37 deletions(-) create mode 100644 kotti/tests/test_filedepot.py diff --git a/kotti/filedepot.py b/kotti/filedepot.py index a80633444..433438a7b 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -15,28 +15,7 @@ import uuid -class CooperativeMeta(type): - def __new__(cls, name, bases, members): - # collect up the metaclasses - metas = [type(base) for base in bases] - - # prune repeated or conflicting entries - metas = [meta for index, meta in enumerate(metas) - if not [later for later in metas[index + 1:] - if issubclass(later, meta)]] - - # whip up the actual combined meta class derive off all of these - meta = type(name, tuple(metas), dict(combined_metas=metas)) - - # make the actual object - return meta(name, bases, members) - - def __init__(self, name, bases, members): - for meta in self.combined_metas: - meta.__init__(self, name, bases, members) - - -class DBStoredFile(Base, StoredFile): +class DBStoredFile(Base): #, StoredFile): """depotfile StoredFile implementation that stores data in the db. Can be used together with DBFileStorage to implement blobs (large files) @@ -44,7 +23,6 @@ class DBStoredFile(Base, StoredFile): """ __tablename__ = "blobs" - __metaclass__ = CooperativeMeta #: Primary key column in the DB #: (:class:`sqlalchemy.types.Integer`) @@ -68,35 +46,77 @@ class DBStoredFile(Base, StoredFile): #: (:class:`sqlalchemy.types.LargeBinary`) data = deferred(Column('data', LargeBinary())) - def read(self, n=-1): # pragma: no cover + 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 + self.content_length = content_length + + for k, v in kwds.items(): + setattr(self, k, v) + + def read(self, n=-1): """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 n == -1: + return self.data + return self.data[:n] + + def close(self, *args, **kwargs): + """Implement :meth:`StoredFile.close`. + :class:`DBStoredFile` never closes. + """ return - def close(self, *args, **kwargs): # pragma: no cover - """Closes the file. + def closed(self): + """Implement :meth:`StoredFile.closed`. + """ + return False - After closing the file it won't be possible to read - from it anymore. Some implementation might not do anything - when closing the file, but they still are required to prevent - further reads from a closed file. + def writable(self): + """Implement :meth:`StoredFile.writable`. """ - return + return False - def closed(self): # pragma: no cover - """Returns if the file has been closed. + def seekable(self): + """Implement :meth:`StoredFile.seekable`. + """ + return False + + @property + def name(self): + """Implement :meth:`StoredFile.name`. - When ``closed`` return ``True`` it won't be possible - to read anoymore from this file. + This is the filename of the saved file """ - return + return self.filename + + @property + def public_url(self): + """The public HTTP url from which file can be accessed. + + When supported by the storage this will provide the + public url to which the file content can be accessed. + In case this returns ``None`` it means that the file can + only be served by the :class:`DepotMiddleware` itself. + """ + return None + + +def set_metadata(event): + obj = event.object + obj.content_length = len(obj.data) + obj.last_modified = datetime.now() class DBFileStorage(FileStorage): - """ depotfile FileStorage implementation, uses DBStoredFile to store data + """ Implementation of :class:`depot.io.interfaces.FileStorage`, uses + `kotti.filedepot.DBStoredFile` to store blob data in an SQL database. """ def get(self, file_id): @@ -190,4 +210,19 @@ def configure_filedepot(settings): def includeme(config): + """ Pyramid includeme hook. + + :param config: app config + :type config: :class:`pyramid.config.Configurator` + """ + from kotti.events import objectevent_listeners + from kotti.events import ObjectInsert + from kotti.events import ObjectUpdate + 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) diff --git a/kotti/tests/test_filedepot.py b/kotti/tests/test_filedepot.py new file mode 100644 index 000000000..6095a7e70 --- /dev/null +++ b/kotti/tests/test_filedepot.py @@ -0,0 +1,63 @@ +import datetime + +class TestDBStoredFile: + + def test_content_length(self, db_session, events, setup_app): + from kotti.filedepot import DBStoredFile + + f = DBStoredFile('fileid', data="content") + db_session.add(f) + db_session.flush() + assert f.content_length == 7 + f.data = 'content changed' + db_session.flush() + assert f.content_length == len('content changed') + + def test_last_modified(self, monkeypatch, db_session, events, setup_app): + from kotti.filedepot import DBStoredFile + from kotti import filedepot + + now = datetime.datetime.now() + + class mockdatetime: + @staticmethod + def now(): + return now + + monkeypatch.setattr(filedepot, 'datetime', mockdatetime) + + f = DBStoredFile('fileid', data="content") + db_session.add(f) + db_session.flush() + + assert f.last_modified == now + + f.last_modified = None + f.data = 'content changed' + db_session.flush() + + assert f.last_modified == now + + def test_storedfile_interface(self): + from kotti.filedepot import DBStoredFile + + f = DBStoredFile('fileid', filename='f.jpg', content_type='image/jpeg', + content_length=1000, data='content') + + assert f.close() == None + assert f.closed() == False + assert f.seekable() == False + assert f.writable() == False + + assert f.read() == 'content' + assert f.read(-1) == 'content' + assert f.read(0) == '' + assert f.read(2) == 'co' + assert f.read(4) == 'cont' + + assert f.content_length == 1000 + assert f.content_type == 'image/jpeg' + assert f.file_id == 'fileid' + assert f.filename == 'f.jpg' + assert f.name == "f.jpg" + assert f.public_url == None From f3aa80ef608a230c694e5242e3bf126ef2593213 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sat, 6 Dec 2014 14:23:10 -0800 Subject: [PATCH 076/600] Added test for DBFileStorage; Refined DBFileStorage implementation --- kotti/filedepot.py | 67 ++++++++++++---------- kotti/tests/test_filedepot.py | 103 ++++++++++++++++++++++++++-------- 2 files changed, 117 insertions(+), 53 deletions(-) diff --git a/kotti/filedepot.py b/kotti/filedepot.py index 433438a7b..4a43f0d06 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -1,5 +1,5 @@ +import uuid from datetime import datetime -from depot.io.interfaces import StoredFile, FileStorage from sqlalchemy import Column from sqlalchemy import DateTime @@ -9,11 +9,11 @@ from sqlalchemy import Unicode from sqlalchemy.orm import deferred +from depot.io.interfaces import FileStorage + from kotti import Base from kotti import DBSession -import uuid - class DBStoredFile(Base): #, StoredFile): """depotfile StoredFile implementation that stores data in the db. @@ -109,14 +109,20 @@ def public_url(self): def set_metadata(event): + """Set DBStoredFile metadata based on data + + :param event: event that trigerred this handler. + :type event: :class:`ObjectInsert` and :class:`ObjectUpdate` + """ obj = event.object - obj.content_length = len(obj.data) + 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. + """Implementation of :class:`depot.io.interfaces.FileStorage`, + + Uses `kotti.filedepot.DBStoredFile` to store blob data in an SQL database. """ def get(self, file_id): @@ -145,24 +151,22 @@ def create(self, content, filename=None, content_type=None): file_id=new_file_id, filename=filename, content_type=content_type, - content_length=len(content), - last_modified=datetime.now()) + ) DBSession.add(fstore) return new_file_id - def replace(self, file_or_id, content, filename=None, content_type=None): # pragma: no cover - """Replaces an existing file, an ``IOError`` is raised if the file didn't already exist. + def replace(self, file_or_id, content, filename=None, content_type=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. + 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. """ - if isinstance(file_or_id, StoredFile): - file_id = file_or_id.file_id - else: - file_id = file_or_id + file_id = self._get_file_id(file_or_id) content, filename, content_type = self.fileinfo( content, filename, content_type) @@ -179,24 +183,25 @@ def replace(self, file_or_id, content, filename=None, content_type=None): # pra fstore.data = content - def delete(self, file_or_id): # pragma: no cover + def delete(self, file_or_id): """Deletes a file. If the file didn't exist it will just do nothing.""" - if isinstance(file_or_id, StoredFile): - file_id = file_or_id.file_id - else: - file_id = file_or_id - fstore = self.get(file_id) - DBSession.delete(fstore) + 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): # pragma: no cover + def exists(self, file_or_id): """Returns if a file or its ID still exist.""" - if isinstance(file_or_id, StoredFile): - file_id = file_or_id.file_id - else: - file_id = file_or_id + + file_id = self._get_file_id(file_or_id) + return bool( - DBSession.query(StoredFile).filter_by(file_id=file_id).count()) + DBSession.query(DBStoredFile).filter_by(file_id=file_id).count()) + + def _get_file_id(self, file_or_id): + if hasattr(file_or_id, 'file_id'): + return file_or_id.file_id + return file_or_id def configure_filedepot(settings): diff --git a/kotti/tests/test_filedepot.py b/kotti/tests/test_filedepot.py index 6095a7e70..3b8eb6b32 100644 --- a/kotti/tests/test_filedepot.py +++ b/kotti/tests/test_filedepot.py @@ -1,20 +1,51 @@ import datetime +import pytest + +from kotti.filedepot import DBFileStorage, DBStoredFile + class TestDBStoredFile: - def test_content_length(self, db_session, events, setup_app): - from kotti.filedepot import DBStoredFile + def test_storedfile_interface(self, db_session, events, setup_app): + f = DBStoredFile('fileid', filename=u'f.jpg', content_type='image/jpeg', + content_length=1000, data='content') + + assert f.close() is None + assert f.closed() is False + assert f.seekable() is False + assert f.writable() is False + + assert f.read() == 'content' + assert f.read(-1) == 'content' + assert f.read(0) == '' + assert f.read(2) == 'co' + assert f.read(4) == 'cont' + assert f.content_length == 1000 + assert f.content_type == 'image/jpeg' + assert f.file_id == 'fileid' + assert f.filename == u'f.jpg' + assert f.name == u"f.jpg" + assert f.public_url is None + + f.data = None + db_session.add(f) + db_session.flush() + assert f.content_length == 0 + + def test_content_length(self, db_session, events, setup_app): f = DBStoredFile('fileid', data="content") db_session.add(f) db_session.flush() + assert f.content_length == 7 + f.data = 'content changed' db_session.flush() + assert f.content_length == len('content changed') def test_last_modified(self, monkeypatch, db_session, events, setup_app): - from kotti.filedepot import DBStoredFile from kotti import filedepot now = datetime.datetime.now() @@ -38,26 +69,54 @@ def now(): assert f.last_modified == now - def test_storedfile_interface(self): - from kotti.filedepot import DBStoredFile - f = DBStoredFile('fileid', filename='f.jpg', content_type='image/jpeg', - content_length=1000, data='content') +class TestDBFileStorage: - assert f.close() == None - assert f.closed() == False - assert f.seekable() == False - assert f.writable() == False + def make_one(self, + content='content here', + filename=u'f.jpg', + content_type='image/jpg'): - assert f.read() == 'content' - assert f.read(-1) == 'content' - assert f.read(0) == '' - assert f.read(2) == 'co' - assert f.read(4) == 'cont' + file_id = DBFileStorage().create( + content=content, filename=filename, content_type=content_type) + return file_id - assert f.content_length == 1000 - assert f.content_type == 'image/jpeg' - assert f.file_id == 'fileid' - assert f.filename == 'f.jpg' - assert f.name == "f.jpg" - assert f.public_url == None + def test_create(self, db_session): + file_id = self.make_one() + assert len(file_id) == 36 + + fs = db_session.query(DBStoredFile).filter_by(file_id=file_id).one() + assert fs.data == "content here" + + def test_get(self, db_session): + with pytest.raises(IOError): + DBFileStorage().get(1) + + file_id = self.make_one() + assert DBFileStorage().get(file_id).data == "content here" + + def test_delete(self, db_session): + file_id = DBFileStorage().create('content here', u'f.jpg', 'image/jpg') + fs = DBFileStorage().get(file_id) + + db_session.add(fs) + db_session.flush() + + assert db_session.query(DBStoredFile.file_id).one()[0] == file_id + + DBFileStorage().delete(file_id) + assert db_session.query(DBStoredFile).count() == 0 + + def test_replace(self, db_session): + file_id = self.make_one() + + DBFileStorage().replace(file_id, 'second content', u'f2.jpg', 'doc') + fs = DBFileStorage().get(file_id) + assert fs.filename == u'f2.jpg' + assert fs.content_type == 'doc' + assert fs.read() == 'second content' + + DBFileStorage().replace(fs, 'third content', u'f3.jpg', 'xls') + assert fs.filename == u'f3.jpg' + assert fs.content_type == 'xls' + assert fs.read() == 'third content' From 8aa9a14ea5e744d6d736a6ec02bf8fdd537492ba Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 7 Dec 2014 14:51:57 -0800 Subject: [PATCH 077/600] Added a migration, still under tests, for filedepot --- .../versions/413fa5fcc581_add_filedepot.py | 50 +++++++++++++++++++ kotti/filedepot.py | 2 +- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 kotti/alembic/versions/413fa5fcc581_add_filedepot.py diff --git a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py new file mode 100644 index 000000000..637a52c5d --- /dev/null +++ b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py @@ -0,0 +1,50 @@ +"""Migrate binary file storage to filedepot + +Revision ID: 413fa5fcc581 +Revises: 1063d7178fa +Create Date: 2014-12-07 05:10:04.294222 + +""" + +# revision identifiers, used by Alembic. +revision = '413fa5fcc581' +down_revision = '1063d7178fa' + +#from alembic import op +import sqlalchemy as sa + + +def upgrade(): + sa.orm.events.MapperEvents._clear() # avoids filedepot magic + + from depot.manager import DepotManager + from depot.fields.upload import UploadedFile + + from kotti import DBSession, metadata + from kotti.resources import File + + t = sa.Table('files', metadata) + t.c.data.type = sa.LargeBinary() + + class UF(UploadedFile): + _frozen = False + + def __init__(self): + self.depot_name = DepotManager.get_default() + self.files = [] + + for o in DBSession.query(File): + print o.id, len(o.data), o.filename, o.mimetype, o.modification_date + + s = UF() + s.process_content(o.data, filename=o.filename, content_type=o.mimetype) + f = DepotManager.get().get(s['file_id']) + f.last_modified = o.modification_date # not ok for LocalStore + o.data = s.encode() + + DBSession.flush() + raise ValueError + + +def downgrade(): + pass diff --git a/kotti/filedepot.py b/kotti/filedepot.py index 4a43f0d06..0415d6f7f 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -35,7 +35,7 @@ class DBStoredFile(Base): #, StoredFile): filename = Column(Unicode(100)) #: MIME type of the blob #: (:class:`sqlalchemy.types.String`) - content_type = Column(String(30)) + content_type = Column(String(100)) #: Size of the blob in bytes #: (:class:`sqlalchemy.types.Integer`) content_length = Column(Integer()) From 9d85afcd196ac6b47597a459329bbcf4f9a37f79 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 8 Dec 2014 10:10:47 -0800 Subject: [PATCH 078/600] Cleanup code in filedepot migration; Renamed default storage to fs0 --- kotti/__init__.py | 4 +- .../versions/413fa5fcc581_add_filedepot.py | 39 ++++++++----------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/kotti/__init__.py b/kotti/__init__.py index c23723ae0..97883f2ad 100644 --- a/kotti/__init__.py +++ b/kotti/__init__.py @@ -101,8 +101,8 @@ def none_factory(**kwargs): # pragma: no cover 'kotti.datetime_format': 'medium', 'kotti.time_format': 'medium', 'kotti.max_file_size': '10', - 'kotti.depot.default.backend': 'depot.io.local.LocalFileStorage', - 'kotti.depot.default.storage_path': 'var/files', + 'kotti.depot.fs0.backend': 'depot.io.local.LocalFileStorage', + 'kotti.depot.fs0.storage_path': 'var/files', 'kotti.fanstatic.edit_needed': 'kotti.fanstatic.edit_needed', 'kotti.fanstatic.view_needed': 'kotti.fanstatic.view_needed', 'kotti.static.edit_needed': '', # BBB diff --git a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py index 637a52c5d..92b4fe31d 100644 --- a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py +++ b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py @@ -10,12 +10,14 @@ revision = '413fa5fcc581' down_revision = '1063d7178fa' -#from alembic import op +import logging import sqlalchemy as sa +log = logging.getLogger('kotti') + def upgrade(): - sa.orm.events.MapperEvents._clear() # avoids filedepot magic + sa.orm.events.MapperEvents._clear() # avoids filedepot magic from depot.manager import DepotManager from depot.fields.upload import UploadedFile @@ -25,26 +27,19 @@ def upgrade(): t = sa.Table('files', metadata) t.c.data.type = sa.LargeBinary() - - class UF(UploadedFile): - _frozen = False - - def __init__(self): - self.depot_name = DepotManager.get_default() - self.files = [] - - for o in DBSession.query(File): - print o.id, len(o.data), o.filename, o.mimetype, o.modification_date - - s = UF() - s.process_content(o.data, filename=o.filename, content_type=o.mimetype) - f = DepotManager.get().get(s['file_id']) - f.last_modified = o.modification_date # not ok for LocalStore - o.data = s.encode() - - DBSession.flush() - raise ValueError - + dn = DepotManager.get_default() + + for obj in DBSession.query(File): + uploaded_file = UploadedFile({'depot_name': dn, 'files': []}) + uploaded_file._thaw() + uploaded_file.process_content( + obj.data, filename=obj.filename, content_type=obj.mimetype) + stored_file = DepotManager.get().get(uploaded_file['file_id']) + stored_file.last_modified = obj.modification_date + obj.data = uploaded_file.encode() + + log.info("Migrated {} bytes for File with pk {} to {}/{}". + format(len(obj.data), obj.id, dn, uploaded_file['file_id'])) def downgrade(): pass From 39d163d64f044aeeb658b47027ead7fe79501c28 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 8 Dec 2014 10:55:18 -0800 Subject: [PATCH 079/600] Migrate column type from blob to varchar() --- kotti/alembic/versions/413fa5fcc581_add_filedepot.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py index 92b4fe31d..e31d4390b 100644 --- a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py +++ b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py @@ -12,6 +12,7 @@ import logging import sqlalchemy as sa +from alembic import op log = logging.getLogger('kotti') @@ -21,6 +22,7 @@ def upgrade(): from depot.manager import DepotManager from depot.fields.upload import UploadedFile + from depot.fields.sqlalchemy import UploadedFileField from kotti import DBSession, metadata from kotti.resources import File @@ -41,5 +43,10 @@ def upgrade(): log.info("Migrated {} bytes for File with pk {} to {}/{}". format(len(obj.data), obj.id, dn, uploaded_file['file_id'])) + DBSession.flush() + if DBSession.get_bind().name != 'sqlite': # not supported by sqlite + op.alter_column('files', 'data', type_=UploadedFileField()) + + def downgrade(): pass From 435dfc355e71ab0bbab739263b900877702c3372 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 8 Dec 2014 10:59:51 -0800 Subject: [PATCH 080/600] Fix test_functional; the mock depot fixture was not properly retrieved --- kotti/tests/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index 0910e0b2e..d9142d440 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -313,9 +313,11 @@ def delete(self, id): del self._storage[int(id)] DepotManager._depots = { - 'default': MagicMock(wraps=TestStorage()) + 'mockdepot': MagicMock(wraps=TestStorage()) } + DepotManager._default_depot = 'mockdepot' def restore(): DepotManager._depots = {} + DepotManager._default_depot = None request.addfinalizer(restore) From 12c4ddc0e7de7f8667914c1b89afb919cf49f554 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 8 Dec 2014 11:27:41 -0800 Subject: [PATCH 081/600] Fixes to tests --- kotti/alembic/versions/413fa5fcc581_add_filedepot.py | 4 ++-- kotti/filedepot.py | 2 +- kotti/tests/__init__.py | 7 +++++-- kotti/tests/test_app.py | 1 + kotti/tests/test_file.py | 6 +++--- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py index e31d4390b..4e8c10fcc 100644 --- a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py +++ b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py @@ -40,8 +40,8 @@ def upgrade(): stored_file.last_modified = obj.modification_date obj.data = uploaded_file.encode() - log.info("Migrated {} bytes for File with pk {} to {}/{}". - format(len(obj.data), obj.id, dn, uploaded_file['file_id'])) + log.info("Migrated {} bytes for File with pk {} to {}/{}".format( + len(obj.data), obj.id, dn, uploaded_file['file_id'])) DBSession.flush() if DBSession.get_bind().name != 'sqlite': # not supported by sqlite diff --git a/kotti/filedepot.py b/kotti/filedepot.py index 0415d6f7f..34948b7c6 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -15,7 +15,7 @@ from kotti import DBSession -class DBStoredFile(Base): #, StoredFile): +class DBStoredFile(Base): """depotfile StoredFile implementation that stores data in the db. Can be used together with DBFileStorage to implement blobs (large files) diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index d9142d440..eecfe9252 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -312,12 +312,15 @@ def create(self, content, filename=None, content_type=None): def delete(self, id): del self._storage[int(id)] + _old_depots = DepotManager._depots + _old_default_depot = DepotManager._default_depot DepotManager._depots = { 'mockdepot': MagicMock(wraps=TestStorage()) } DepotManager._default_depot = 'mockdepot' def restore(): - DepotManager._depots = {} - DepotManager._default_depot = None + DepotManager._depots = _old_depots + DepotManager._default_depot = _old_default_depot + request.addfinalizer(restore) diff --git a/kotti/tests/test_app.py b/kotti/tests/test_app.py index 33384c737..a21910746 100644 --- a/kotti/tests/test_app.py +++ b/kotti/tests/test_app.py @@ -174,6 +174,7 @@ def test_configure_filedepot(self): } depots = DepotManager._depots DepotManager._depots = {} + DepotManager._default_depot = None configure_filedepot(settings) assert DepotManager.get().marker == 'TFS1' diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index 77003a77d..a399ec628 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -18,7 +18,7 @@ def _test_common_headers(self, headers): assert headers["Content-Length"] == "13" assert headers["Content-Type"] == "image/png" - def test_inline_view(self, config): + def test_inline_view(self, config, filedepot): self._create_file(config) from kotti.views.file import inline_view res = inline_view(self.file, None) @@ -28,7 +28,7 @@ def test_inline_view(self, config): assert headers["Content-Disposition"] == 'inline;filename="myfle.png"' assert res.body == 'file contents' - def test_attachment_view(self, config): + def test_attachment_view(self, config, filedepot): self._create_file(config) from kotti.views.file import attachment_view res = attachment_view(self.file, None) @@ -103,7 +103,7 @@ def make_one(self): from kotti.views.edit.content import FileAddForm return FileAddForm(MagicMock(), DummyRequest()) - def test_add(self, config): + def test_add(self, config, filedepot): view = self.make_one() file = view.add( title=u'A title', From 850455117a1972d714c4071ccd60968f308ae292 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 8 Dec 2014 11:32:44 -0800 Subject: [PATCH 082/600] Don't asume name 'default' is magic for configuration of depots, in test --- kotti/tests/test_app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kotti/tests/test_app.py b/kotti/tests/test_app.py index a21910746..b268d56f9 100644 --- a/kotti/tests/test_app.py +++ b/kotti/tests/test_app.py @@ -167,8 +167,8 @@ def test_configure_filedepot(self): tests.TFS2 = Mock(return_value=Mock(marker="TFS2")) settings = { - 'kotti.depot.default.backend': 'kotti.tests.TFS1', - 'kotti.depot.default.location': '/tmp', + 'kotti.depot.localfs.backend': 'kotti.tests.TFS1', + 'kotti.depot.localfs.location': '/tmp', 'kotti.depot.mongo.backend': 'kotti.tests.TFS2', 'kotti.depot.mongo.uri': 'mongo://', } @@ -178,7 +178,7 @@ def test_configure_filedepot(self): configure_filedepot(settings) assert DepotManager.get().marker == 'TFS1' - assert DepotManager.get('default').marker == 'TFS1' + assert DepotManager.get('localfs').marker == 'TFS1' assert DepotManager.get('mongo').marker == 'TFS2' tests.TFS1.assert_called_with(location='/tmp') From 1c82b4abe5f1e8369bae534580f0782ff0553d01 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 9 Dec 2014 10:54:55 -0800 Subject: [PATCH 083/600] Fix test failures: added the filedepot fixture where needed; fixed the filedepot fixture --- kotti/tests/__init__.py | 3 ++- kotti/tests/test_app.py | 10 ++++++++-- kotti/tests/test_functional.py | 6 +++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index eecfe9252..0eeace4d8 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -281,7 +281,7 @@ def workflow(config): @fixture -def filedepot(request): +def filedepot(request, db_session): from depot.manager import DepotManager from datetime import datetime @@ -320,6 +320,7 @@ def delete(self, id): DepotManager._default_depot = 'mockdepot' def restore(): + db_session.rollback() DepotManager._depots = _old_depots DepotManager._default_depot = _old_default_depot diff --git a/kotti/tests/test_app.py b/kotti/tests/test_app.py index b268d56f9..ddb76b907 100644 --- a/kotti/tests/test_app.py +++ b/kotti/tests/test_app.py @@ -172,9 +172,13 @@ def test_configure_filedepot(self): 'kotti.depot.mongo.backend': 'kotti.tests.TFS2', 'kotti.depot.mongo.uri': 'mongo://', } - depots = DepotManager._depots + + # depot.manager.DepotManager acts as singleton, save its settings + _depots = DepotManager._depots + _default_depot = DepotManager._default_depot DepotManager._depots = {} DepotManager._default_depot = None + configure_filedepot(settings) assert DepotManager.get().marker == 'TFS1' @@ -184,7 +188,9 @@ def test_configure_filedepot(self): tests.TFS1.assert_called_with(location='/tmp') tests.TFS2.assert_called_with(uri='mongo://') - DepotManager._depots = depots + DepotManager._depots = _depots + DepotManager._default_depot = _default_depot + del tests.TFS1 del tests.TFS2 diff --git a/kotti/tests/test_functional.py b/kotti/tests/test_functional.py index 9bdd605e1..a87d059ee 100644 --- a/kotti/tests/test_functional.py +++ b/kotti/tests/test_functional.py @@ -36,13 +36,13 @@ def add_file(self, browser, contents='ABC'): browser.getControl('save').click() @user('admin') - def test_it(self, browser): + def test_it(self, browser, filedepot): browser.open(BASE_URL + '/@@add_file') self.add_file(browser) assert "Item was added" in browser.contents @user('admin') - def test_view_uploaded_file(self, browser): + def test_view_uploaded_file(self, browser, filedepot): browser.open(BASE_URL + '/@@add_file') self.add_file(browser) browser.getLink("View").click() @@ -50,7 +50,7 @@ def test_view_uploaded_file(self, browser): assert browser.contents == 'ABC' @user('admin') - def test_tempstorage(self, browser): + def test_tempstorage(self, browser, filedepot): browser.open(BASE_URL + '/@@add_file') self.add_file(browser, contents='DEF') browser.getLink("Edit").click() From a5a7751565411f919da051cea3e03c9f04a596c7 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 9 Dec 2014 14:23:10 -0800 Subject: [PATCH 084/600] Explore setting size and mimetype for File from the processing loop of the data field --- kotti/filedepot.py | 2 +- kotti/resources.py | 28 ++++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/kotti/filedepot.py b/kotti/filedepot.py index 34948b7c6..708a3b0d8 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -112,7 +112,7 @@ def set_metadata(event): """Set DBStoredFile metadata based on data :param event: event that trigerred this handler. - :type event: :class:`ObjectInsert` and :class:`ObjectUpdate` + :type event: :class:`ObjectInsert` or :class:`ObjectUpdate` """ obj = event.object obj.content_length = obj.data and len(obj.data) or 0 diff --git a/kotti/resources.py b/kotti/resources.py index f7a15d3ea..241fa07e2 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -35,6 +35,7 @@ from sqlalchemy.orm import object_mapper from sqlalchemy.orm import relation from sqlalchemy.orm.exc import NoResultFound +from sqlalchemy.orm import ColumnProperty from sqlalchemy.sql import and_ from sqlalchemy.sql import select from sqlalchemy.util import classproperty @@ -706,8 +707,31 @@ def __declare_last__(cls): # For the ``data`` column, use the field value setter from filedepot. # filedepot already registers this event listener, but it does so in a # way that won't work properly for subclasses of File - event.listen(cls.data, 'set', - _SQLAMutationTracker._field_set, retval=True) + + # mapper = cls._sa_class_manager.mapper + # for mapper_property in mapper.iterate_properties: + # if isinstance(mapper_property, ColumnProperty): + # for idx, col in enumerate(mapper_property.columns): + # if isinstance(col.type, UploadedFileField): + # event.listen( + # getattr(cls, col.name), + # 'set', + # _SQLAMutationTracker._field_set, + # retval=True + # ) + event.listen(cls.data, 'set', cls.set_metadata, retval=True) + + @classmethod + def set_metadata(cls, target, value, oldvalue, initiator): + newvalue = _SQLAMutationTracker._field_set( + target, value, oldvalue, initiator) + + if newvalue is None: + return + + target.mimetype = newvalue.file.content_type + target.size = newvalue.file.content_length + return newvalue class Image(File): From 718308ae26a0aaa5ddc70e81fd93c5c4f5eabb99 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Thu, 11 Dec 2014 10:10:58 -0800 Subject: [PATCH 085/600] Cleanup after the merge; fix the digraph for the fixtures; switch the position of the request parameter for filedepot fixture to fix tests --- kotti/resources.py | 1 - kotti/tests/__init__.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/kotti/resources.py b/kotti/resources.py index 241fa07e2..067c8f6eb 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -35,7 +35,6 @@ from sqlalchemy.orm import object_mapper from sqlalchemy.orm import relation from sqlalchemy.orm.exc import NoResultFound -from sqlalchemy.orm import ColumnProperty from sqlalchemy.sql import and_ from sqlalchemy.sql import select from sqlalchemy.util import classproperty diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index 0eeace4d8..2a8879ca1 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -9,7 +9,6 @@ digraph kotti_fixtures { "allwarnings"; - "filedepot"; "app" -> "webtest"; "config" -> "db_session"; "config" -> "dummy_request"; @@ -23,6 +22,7 @@ "db_session" -> "app"; "db_session" -> "browser"; "db_session" -> "root"; + "db_session" -> "filedepot"; "dummy_mailer" -> "app"; "dummy_mailer"; "events" -> "app"; @@ -281,7 +281,7 @@ def workflow(config): @fixture -def filedepot(request, db_session): +def filedepot(db_session, request): from depot.manager import DepotManager from datetime import datetime From 7e048a5f68c8fad3cf9a508dac656d19be182e1e Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Thu, 11 Dec 2014 16:11:41 -0800 Subject: [PATCH 086/600] Refactor data saving on files; we always pass a cgi.FieldStorage, because that's what filedepot accepts best --- kotti/filedepot.py | 4 ++-- kotti/resources.py | 10 ++++++++-- kotti/tests/__init__.py | 19 ++++++++++++++----- kotti/tests/test_file.py | 34 ++++++++++++++++------------------ kotti/util.py | 15 +++++++++++++++ kotti/views/edit/content.py | 27 +++------------------------ 6 files changed, 58 insertions(+), 51 deletions(-) diff --git a/kotti/filedepot.py b/kotti/filedepot.py index 708a3b0d8..b221c2b20 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -46,8 +46,8 @@ class DBStoredFile(Base): #: (:class:`sqlalchemy.types.LargeBinary`) data = deferred(Column('data', LargeBinary())) - def __init__(self, file_id, filename=None, content_type=None, last_modified=None, - content_length=None, **kwds): + 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 diff --git a/kotti/resources.py b/kotti/resources.py index 067c8f6eb..8ce299001 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -12,6 +12,7 @@ import warnings from fnmatch import fnmatch +from cStringIO import StringIO from UserDict import DictMixin from depot.fields.sqlalchemy import UploadedFileField @@ -62,6 +63,7 @@ from kotti.sqla import MutationList from kotti.sqla import NestedMutationDict 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 from kotti.util import Link @@ -673,10 +675,13 @@ def __init__(self, data=None, filename=None, mimetype=None, size=None, super(File, self).__init__(**kwargs) - self.data = data self.filename = filename self.mimetype = mimetype self.size = size + if isinstance(data, basestring): + data = _to_fieldstorage(fp=StringIO(data), filename=filename, + mimetype=mimetype, size=size) + self.data = data @classmethod def from_field_storage(cls, fs): @@ -728,8 +733,9 @@ def set_metadata(cls, target, value, oldvalue, initiator): if newvalue is None: return - target.mimetype = newvalue.file.content_type + target.filename = newvalue.filename target.size = newvalue.file.content_length + target.mimetype = newvalue.content_type return newvalue diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index 2a8879ca1..4528974c2 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -291,22 +291,31 @@ def __init__(self): self._storage.setdefault(0) def get(self, id): + content = self._storage[id]['content'] f = MagicMock() - f.read.return_value = self._storage[id] + f.read.return_value = content - # needed to make JSON serializable, Mock objects are not - f.last_modified = datetime.now() - f.filename = str(id) + f.filename = self._storage[id]['filename'] f.public_url = '' f.content_type = 'image/png' + f.content_length = len(content) + + # needed to make JSON serializable, Mock objects are not + f.last_modified = datetime.now() return f def create(self, content, filename=None, content_type=None): id = max(self._storage) + 1 + if hasattr(content, 'filename'): + filename = filename or content.filename + if hasattr(content, 'type'): + content_type = content_type or content.type if hasattr(content, 'read'): content = content.read() - self._storage[id] = content + elif hasattr(content, 'file'): + content = content.file.read() + self._storage[id] = {'content': content, 'filename': filename} return id def delete(self, id): diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index a399ec628..e54f02f5e 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -43,25 +43,11 @@ def test_attachment_view(self, config, filedepot): class TestFileEditForm: def make_one(self): from kotti.views.edit.content import FileEditForm + from kotti.resources import File - class MockFileColumn(object): - def __init__(self): - self.file = MagicMock() - - def __set__(self, instance, value): - if isinstance(value, StringIO): - value.seek(0) - rv = value.read() - else: - rv = value - self.file.read.return_value = rv - - class MockDepotFile(object): - data = MockFileColumn() - - return FileEditForm(MockDepotFile(), DummyRequest()) + return FileEditForm(File(), DummyRequest()) - def test_edit_with_file(self): + def test_edit_with_file(self, db_session, filedepot): view = self.make_one() view.edit( title=u'A title', description=u'A description', @@ -70,6 +56,8 @@ def test_edit_with_file(self): fp=StringIO('filecontents'), filename=u'myfile.png', mimetype=u'image/png', + size=10, + uid="randomabc", ), ) assert view.context.title == u'A title' @@ -80,7 +68,7 @@ def test_edit_with_file(self): assert view.context.size == len('filecontents') assert view.context.tags == [u"A tag"] - def test_edit_without_file(self): + def test_edit_without_file(self, filedepot): view = self.make_one() view.context.data = 'filecontents' view.context.filename = u'myfile.png' @@ -113,6 +101,8 @@ def test_add(self, config, filedepot): fp=StringIO('filecontents'), filename=u'myfile.png', mimetype=u'image/png', + size=None, + uid="randomabc", ), ) @@ -192,3 +182,11 @@ def test_delete(self, factory, db_session, root, filedepot): assert DepotManager.get().delete.called assert id not in DepotManager.get()._storage.keys() + + +class TestFileSetMetadata: + + def test_it(self, db_session, filedepot): + from kotti.resources import File + f = File("file contents", u"myfile.png", u"image/png") + assert f.filename == u"myfile.png" diff --git a/kotti/util.py b/kotti/util.py index 4cd225cfd..042af195f 100644 --- a/kotti/util.py +++ b/kotti/util.py @@ -7,6 +7,7 @@ .. inheritance-diagram:: kotti.util """ +import cgi import re import urllib from urlparse import urlparse, urlunparse @@ -365,3 +366,17 @@ def command(func, doc): 'ViewLink', "kotti.util.ViewLink has been renamed to Link as of Kotti 1.0.0." ) + + +def _to_fieldstorage(fp, filename, mimetype, size, **_kwds): + """ Build a cgi.FieldStorage instance. + + Deform's FileUploadWidget returns a dict, but filedepot's + UploadedFieldFile likes cgi.FieldStorage objects + """ + f = cgi.FieldStorage() + f.file = fp + f.filename = filename + f.type = mimetype + f.length = size + return f diff --git a/kotti/views/edit/content.py b/kotti/views/edit/content.py index 23f095db9..cecf417c9 100644 --- a/kotti/views/edit/content.py +++ b/kotti/views/edit/content.py @@ -16,6 +16,7 @@ from kotti.resources import File from kotti.resources import Image from kotti.util import _ +from kotti.util import _to_fieldstorage from kotti.views.form import get_appstruct from kotti.views.form import AddFormView from kotti.views.form import EditFormView @@ -102,18 +103,7 @@ def edit(self, **appstruct): self.context.description = appstruct['description'] self.context.tags = appstruct['tags'] if appstruct['file']: - buf = appstruct['file']['fp'] - size = 0 - while True: - chunk = buf.read(1024) - if not chunk: - break - size += len(chunk) - buf.seek(0) - self.context.data = buf - self.context.filename = appstruct['file']['filename'] - self.context.mimetype = appstruct['file']['mimetype'] - self.context.size = size + self.context.data = _to_fieldstorage(**appstruct['file']) class FileAddForm(AddFormView): @@ -130,23 +120,12 @@ def save_success(self, appstruct): return super(FileAddForm, self).save_success(appstruct) def add(self, **appstruct): - buf = appstruct['file']['fp'] - size = 0 - while True: - chunk = buf.read(1024) - if not chunk: - break - size += len(chunk) - buf.seek(0) filename = appstruct['file']['filename'] item = self.item_class( title=appstruct['title'] or filename, description=appstruct['description'], tags=appstruct['tags'], - data=buf, - filename=filename, - mimetype=appstruct['file']['mimetype'], - size=size, + data=_to_fieldstorage(**appstruct['file']), ) return item From 7b58c4b03e891b9812cec13bad919fcf21ab8b02 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Thu, 11 Dec 2014 16:20:04 -0800 Subject: [PATCH 087/600] Simplify code in mock filedepot --- kotti/tests/__init__.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index 4528974c2..a2c040c0c 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -291,15 +291,14 @@ def __init__(self): self._storage.setdefault(0) def get(self, id): - content = self._storage[id]['content'] - f = MagicMock() - f.read.return_value = content + info = self._storage[id] - f.filename = self._storage[id]['filename'] + f = MagicMock() f.public_url = '' - f.content_type = 'image/png' - f.content_length = len(content) - + f.read.return_value = info['content'] + f.filename = info['filename'] + f.content_type = info['content_type'] + f.content_length = len(info['content']) # needed to make JSON serializable, Mock objects are not f.last_modified = datetime.now() @@ -307,15 +306,13 @@ def get(self, id): def create(self, content, filename=None, content_type=None): id = max(self._storage) + 1 - if hasattr(content, 'filename'): - filename = filename or content.filename - if hasattr(content, 'type'): - content_type = content_type or content.type - if hasattr(content, 'read'): - content = content.read() - elif hasattr(content, 'file'): + filename = filename or getattr(content, 'filename', None) + content_type = content_type or getattr(content, 'type', None) + if not isinstance(content, str): content = content.file.read() - self._storage[id] = {'content': content, 'filename': filename} + self._storage[id] = {'content': content, + 'filename': filename, + 'content_type': content_type} return id def delete(self, id): From cdf0558e817a9e8bbf3c78fd0a5e8d2aa18c4390 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Thu, 11 Dec 2014 16:22:59 -0800 Subject: [PATCH 088/600] Make it clear about the destination of last_modified in mock filedepot --- kotti/tests/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index a2c040c0c..e4dcd6577 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -300,7 +300,7 @@ def get(self, id): f.content_type = info['content_type'] f.content_length = len(info['content']) # needed to make JSON serializable, Mock objects are not - f.last_modified = datetime.now() + f.last_modified = datetime(2012, 12, 30) return f From 6a7da18f18e1417437d1b0b7e2ee9f11caae0f08 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Fri, 12 Dec 2014 15:42:15 -0800 Subject: [PATCH 089/600] Use a file iterator for file downloading --- kotti/resources.py | 11 --- kotti/tests/test_file.py | 30 ++++++-- kotti/views/file.py | 148 +++++++++++++++++++++++++++++++++++---- 3 files changed, 158 insertions(+), 31 deletions(-) diff --git a/kotti/resources.py b/kotti/resources.py index 8ce299001..fbe6f0809 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -712,17 +712,6 @@ def __declare_last__(cls): # filedepot already registers this event listener, but it does so in a # way that won't work properly for subclasses of File - # mapper = cls._sa_class_manager.mapper - # for mapper_property in mapper.iterate_properties: - # if isinstance(mapper_property, ColumnProperty): - # for idx, col in enumerate(mapper_property.columns): - # if isinstance(col.type, UploadedFileField): - # event.listen( - # getattr(cls, col.name), - # 'set', - # _SQLAMutationTracker._field_set, - # retval=True - # ) event.listen(cls.data, 'set', cls.set_metadata, retval=True) @classmethod diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index e54f02f5e..5110b79c2 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -19,25 +19,43 @@ def _test_common_headers(self, headers): assert headers["Content-Type"] == "image/png" def test_inline_view(self, config, filedepot): - self._create_file(config) from kotti.views.file import inline_view + from kotti.views.file import as_inline + from pyramid.response import IResponse + + config.include('kotti.views.file') + + self._create_file(config) + res = inline_view(self.file, None) - headers = res.headers + assert isinstance(res, as_inline) + + response = IResponse(res) + headers = response.headers self._test_common_headers(headers) + assert headers["Content-Disposition"] == 'inline;filename="myfle.png"' - assert res.body == 'file contents' + assert response.app_iter.file.read() == 'file contents' def test_attachment_view(self, config, filedepot): - self._create_file(config) from kotti.views.file import attachment_view + from kotti.views.file import as_download + from pyramid.response import IResponse + + config.include('kotti.views.file') + + self._create_file(config) res = attachment_view(self.file, None) - headers = res.headers + assert isinstance(res, as_download) + + response = IResponse(res) + headers = response.headers self._test_common_headers(headers) assert headers["Content-Disposition"] == ( 'attachment;filename="myfle.png"') - assert res.body == 'file contents' + assert response.app_iter.file.read() == 'file contents' class TestFileEditForm: diff --git a/kotti/views/file.py b/kotti/views/file.py index 36dd0aa7d..93584bdb6 100644 --- a/kotti/views/file.py +++ b/kotti/views/file.py @@ -1,10 +1,140 @@ # -*- coding: utf-8 -*- +from pyramid.response import _BLOCK_SIZE +from pyramid.response import FileIter +from pyramid.response import response_adapter from pyramid.response import Response from pyramid.view import view_config from kotti.resources import File +import mimetypes + + +class UploadedFileResponse(Response): + """ + A Response object that can be used to serve a uploaded files. + + ``data`` is the ``UploadedFile`` file field value. + + ``request`` must be a Pyramid :term:`request` object. Note + that a request *must* be passed if the response is meant to attempt to + use the ``wsgi.file_wrapper`` feature of the web server that you're using + to serve your Pyramid application. + + ``cache_max_age`` is the number of seconds that should be used + to HTTP cache this response. + + ``content_type`` is the content_type of the response. + + ``content_encoding`` is 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``. + + Code adapted from pyramid.response.FileResponse + """ + def __init__(self, data, request=None, disposition='attachment', + cache_max_age=None, content_type=None, content_encoding=None): + + filename = data.filename + if content_type is None: + content_type, content_encoding = mimetypes.guess_type( + 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) + super(UploadedFileResponse, self).__init__( + conditional_response=True, + content_type=content_type, + content_encoding=content_encoding + ) + self.last_modified = data.file.last_modified + content_length = data.file.content_length + f = data.file + app_iter = None + if request is not None: + 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, _BLOCK_SIZE) + self.app_iter = app_iter + # assignment of content_length must come after assignment of app_iter + self.content_length = content_length + if cache_max_age is not None: + self.cache_expires = cache_max_age + + disp = '%s;filename="%s"' % (disposition, + data.filename.encode('ascii', 'ignore')) + self.headerlist.append(('Content-Disposition', disp)) + + +class as_inline(object): + """ ``UploadedFile`` adapter for an inline content-disposition Response + + Writing a view to inline view a file (such as an image) can be as easy as:: + + @view_config(name='image', context=Image, permission='View') + def view_image(context, request): + return as_inline(context.imagefield) + """ + + def __init__(self, data, request): + """ + :param data: :A file field obtained by reading an + :class:`~depot.fields.sqlalchemy.UploadedFileField` + :type data: :class:`depot.fields.upload.UploadedField`, + + :param request: current request + :type request: :class:`pyramid.request.Request` + """ + self.data = data + self.request = request + + +class as_download(object): + """ ``UploadedFile`` adapter for an attachment content-disposition Response + + Writing a view to download a file can be as easy as:: + + @view_config(name='image', context=Image, permission='View') + def download(context, request): + return as_download(context.filefield) + """ + + def __init__(self, data, request): + """ + :param data: :A file field obtained by reading an + :class:`~depot.fields.sqlalchemy.UploadedFileField` + :type data: :class:`depot.fields.upload.UploadedField`, + + :param request: current request + :type request: :class:`pyramid.request.Request` + """ + self.data = data + self.request = request + + +@response_adapter(as_download) +def field_to_download_response(adapter): + return UploadedFileResponse(adapter.data, + request=adapter.request, + disposition='attachment') + + +@response_adapter(as_inline) +def field_to_inline_response(adapter): + return UploadedFileResponse(adapter.data, + request=adapter.request, + disposition='inline') +>>>>>>> Use a file iterator for file downloading + @view_config(name='view', context=File, permission='view', renderer='kotti:templates/view/file.pt') @@ -12,24 +142,14 @@ def view(context, request): return {} -@view_config(name='inline-view', context=File, - permission='view') +@view_config(name='inline-view', context=File, permission='view') def inline_view(context, request, disposition='inline'): - res = Response( - headerlist=[ - ('Content-Disposition', '%s;filename="%s"' % ( - disposition, context.filename.encode('ascii', 'ignore'))), - ('Content-Type', str(context.mimetype)), - ] - ) - res.body = context.data.file.read() - return res + return as_inline(context.data, request) -@view_config(name='attachment-view', context=File, - permission='view') +@view_config(name='attachment-view', context=File, permission='view') def attachment_view(context, request): - return inline_view(context, request, 'attachment') + return as_download(context.data, request) def includeme(config): From a5ffdfb1b5a7ee2238c9fd3a0c1c462ea71800ba Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Fri, 12 Dec 2014 15:47:56 -0800 Subject: [PATCH 090/600] Don't complicate things with adapters, simply return the UploadedFileResponse in file views --- kotti/tests/test_file.py | 20 ++++---------------- kotti/views/file.py | 8 +++----- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index 5110b79c2..ece2bbd28 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -20,42 +20,30 @@ def _test_common_headers(self, headers): def test_inline_view(self, config, filedepot): from kotti.views.file import inline_view - from kotti.views.file import as_inline - from pyramid.response import IResponse config.include('kotti.views.file') self._create_file(config) res = inline_view(self.file, None) - assert isinstance(res, as_inline) - - response = IResponse(res) - headers = response.headers + headers = res.headers self._test_common_headers(headers) assert headers["Content-Disposition"] == 'inline;filename="myfle.png"' - assert response.app_iter.file.read() == 'file contents' + assert res.app_iter.file.read() == 'file contents' def test_attachment_view(self, config, filedepot): from kotti.views.file import attachment_view - from kotti.views.file import as_download - from pyramid.response import IResponse - - config.include('kotti.views.file') self._create_file(config) res = attachment_view(self.file, None) - assert isinstance(res, as_download) - - response = IResponse(res) - headers = response.headers + headers = res.headers self._test_common_headers(headers) assert headers["Content-Disposition"] == ( 'attachment;filename="myfle.png"') - assert response.app_iter.file.read() == 'file contents' + assert res.app_iter.file.read() == 'file contents' class TestFileEditForm: diff --git a/kotti/views/file.py b/kotti/views/file.py index 93584bdb6..1e97f5082 100644 --- a/kotti/views/file.py +++ b/kotti/views/file.py @@ -2,7 +2,6 @@ from pyramid.response import _BLOCK_SIZE from pyramid.response import FileIter -from pyramid.response import response_adapter from pyramid.response import Response from pyramid.view import view_config @@ -133,7 +132,6 @@ def field_to_inline_response(adapter): return UploadedFileResponse(adapter.data, request=adapter.request, disposition='inline') ->>>>>>> Use a file iterator for file downloading @view_config(name='view', context=File, permission='view', @@ -143,13 +141,13 @@ def view(context, request): @view_config(name='inline-view', context=File, permission='view') -def inline_view(context, request, disposition='inline'): - return as_inline(context.data, request) +def inline_view(context, request): + return UploadedFileResponse(context.data, request, disposition='inline') @view_config(name='attachment-view', context=File, permission='view') def attachment_view(context, request): - return as_download(context.data, request) + return UploadedFileResponse(context.data, request, disposition='attachment') def includeme(config): From 96449e26c3133a8406a41a76aa2f9c37ed519fb9 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sat, 13 Dec 2014 17:35:48 -0800 Subject: [PATCH 091/600] Avoid problem with wsgi serving of files; Use the UploadedFileResponse in image view, where possible --- kotti/views/file.py | 21 ++++++++------------- kotti/views/image.py | 17 +++++++++-------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/kotti/views/file.py b/kotti/views/file.py index 1e97f5082..f4b35cb53 100644 --- a/kotti/views/file.py +++ b/kotti/views/file.py @@ -34,9 +34,11 @@ class UploadedFileResponse(Response): Code adapted from pyramid.response.FileResponse """ def __init__(self, data, request=None, disposition='attachment', - cache_max_age=None, content_type=None, content_encoding=None): + cache_max_age=None, content_type=None, + content_encoding=None): filename = data.filename + if content_type is None: content_type, content_encoding = mimetypes.guess_type( filename, @@ -48,24 +50,17 @@ def __init__(self, data, request=None, disposition='attachment', # on Windows where mimetypes.guess_type returns unicode for the # content_type. content_type = str(content_type) + super(UploadedFileResponse, self).__init__( conditional_response=True, content_type=content_type, content_encoding=content_encoding ) - self.last_modified = data.file.last_modified - content_length = data.file.content_length - f = data.file - app_iter = None - if request is not None: - 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, _BLOCK_SIZE) - self.app_iter = app_iter + + self.app_iter = FileIter(data.file, _BLOCK_SIZE) # assignment of content_length must come after assignment of app_iter - self.content_length = content_length + self.content_length = data.file.content_length + self.last_modified = data.file.last_modified if cache_max_age is not None: self.cache_expires = cache_max_age diff --git a/kotti/views/image.py b/kotti/views/image.py index 5c98e9d2b..6a5dd75fa 100644 --- a/kotti/views/image.py +++ b/kotti/views/image.py @@ -10,6 +10,7 @@ from kotti.interfaces import IImage from kotti.util import extract_from_settings +from kotti.views.file import UploadedFileResponse PIL.ImageFile.MAXBLOCK = 33554432 @@ -87,14 +88,14 @@ def image(self, subpath=None): # /path/to/image/scale/thumb width, height = image_scales[scale] - if width and height: - image, format, size = scaleImage(self.context.data.file.read(), - width=width, - height=height, - direction="thumb") - else: - image = self.context.data.file.read() + if not (width and height): + return UploadedFileResponse( + self.context.data, self.request, disposition) + image, format, size = scaleImage(self.context.data.file.read(), + width=width, + height=height, + direction="thumb") res = Response( headerlist=[('Content-Disposition', '%s;filename="%s"' % ( disposition, @@ -103,7 +104,7 @@ def image(self, subpath=None): ('Content-Type', str(self.context.mimetype)), ], body=image, - ) + ) return res From 0c1e082e7bece03ea57cac7b9b642116a0df4bb9 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sat, 13 Dec 2014 18:14:40 -0800 Subject: [PATCH 092/600] Fix freeze in tests caused by improper fixture --- kotti/tests/__init__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index e4dcd6577..c4eea8b58 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -293,9 +293,16 @@ def __init__(self): def get(self, id): info = self._storage[id] + finished = [] + def read(block_size=-1): + if not finished: + finished.append(True) + return info['content'] + return None + f = MagicMock() f.public_url = '' - f.read.return_value = info['content'] + f.read = read f.filename = info['filename'] f.content_type = info['content_type'] f.content_length = len(info['content']) From bc2ff3f84dd252be3b189866a8eeb295afd38ad9 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sat, 13 Dec 2014 18:19:42 -0800 Subject: [PATCH 093/600] Revert changes to development.ini and requirements.txt to allow travis to run --- development.ini | 2 +- requirements.txt | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/development.ini b/development.ini index e7b34d938..76ed43710 100644 --- a/development.ini +++ b/development.ini @@ -28,7 +28,7 @@ pipeline = [server:main] use = egg:waitress#main -host = 0.0.0.0 +host = 127.0.0.1 port = 5000 [alembic] diff --git a/requirements.txt b/requirements.txt index 7db5873aa..40bdee355 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ Kotti==1.0.0-alpha.4 --e git+ssh://git@github.com/amol-/depot.git#egg=filedepot - +filedepot==0.0.2 Babel==1.3 Beaker==1.6.4 Chameleon==2.20 From 9c80e5fc2299ff330feeca1c9fdb41c207c809ee Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sat, 13 Dec 2014 18:34:58 -0800 Subject: [PATCH 094/600] Code cleanup --- kotti/tests/__init__.py | 1 + kotti/tests/test_file.py | 7 ------- kotti/views/image.py | 6 +++--- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index c4eea8b58..06e94f159 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -294,6 +294,7 @@ def get(self, id): info = self._storage[id] finished = [] + def read(block_size=-1): if not finished: finished.append(True) diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index ece2bbd28..e2fe1eba6 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -189,10 +189,3 @@ def test_delete(self, factory, db_session, root, filedepot): assert DepotManager.get().delete.called assert id not in DepotManager.get()._storage.keys() - -class TestFileSetMetadata: - - def test_it(self, db_session, filedepot): - from kotti.resources import File - f = File("file contents", u"myfile.png", u"image/png") - assert f.filename == u"myfile.png" diff --git a/kotti/views/image.py b/kotti/views/image.py index 6a5dd75fa..3a8070f07 100644 --- a/kotti/views/image.py +++ b/kotti/views/image.py @@ -93,9 +93,9 @@ def image(self, subpath=None): self.context.data, self.request, disposition) image, format, size = scaleImage(self.context.data.file.read(), - width=width, - height=height, - direction="thumb") + width=width, + height=height, + direction="thumb") res = Response( headerlist=[('Content-Disposition', '%s;filename="%s"' % ( disposition, From 369d193ef4ae96d18c17aac31b0a2443926d401a Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 14 Dec 2014 02:31:52 -0800 Subject: [PATCH 095/600] Properly implement read() from DBStoredFile --- kotti/__init__.py | 3 +-- kotti/filedepot.py | 43 ++++++++++++++++++++++++++++++++--- kotti/tests/test_file.py | 1 - kotti/tests/test_filedepot.py | 11 ++++++--- 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/kotti/__init__.py b/kotti/__init__.py index 97883f2ad..e05af887d 100644 --- a/kotti/__init__.py +++ b/kotti/__init__.py @@ -101,8 +101,7 @@ def none_factory(**kwargs): # pragma: no cover 'kotti.datetime_format': 'medium', 'kotti.time_format': 'medium', 'kotti.max_file_size': '10', - 'kotti.depot.fs0.backend': 'depot.io.local.LocalFileStorage', - 'kotti.depot.fs0.storage_path': 'var/files', + 'kotti.depot.fs0.backend': 'kotti.filedepot.DBFileStorage', 'kotti.fanstatic.edit_needed': 'kotti.fanstatic.edit_needed', 'kotti.fanstatic.view_needed': 'kotti.fanstatic.view_needed', 'kotti.static.edit_needed': '', # BBB diff --git a/kotti/filedepot.py b/kotti/filedepot.py index b221c2b20..70a600aca 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -7,6 +7,7 @@ from sqlalchemy import LargeBinary from sqlalchemy import String from sqlalchemy import Unicode +from sqlalchemy import event from sqlalchemy.orm import deferred from depot.io.interfaces import FileStorage @@ -14,6 +15,7 @@ from kotti import Base from kotti import DBSession +_marker = object() class DBStoredFile(Base): """depotfile StoredFile implementation that stores data in the db. @@ -46,6 +48,9 @@ class DBStoredFile(Base): #: (:class:`sqlalchemy.types.LargeBinary`) data = deferred(Column('data', LargeBinary())) + _cursor = 0 + _data = _marker + def __init__(self, file_id, filename=None, content_type=None, last_modified=None, content_length=None, **kwds): self.file_id = file_id @@ -63,9 +68,19 @@ def read(self, n=-1): 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: - return self.data - return self.data[:n] + result = self._data[self._cursor:] + else: + result = self._data[self._cursor:self._cursor + n] + + self._cursor += len(result) + + return result def close(self, *args, **kwargs): """Implement :meth:`StoredFile.close`. @@ -86,7 +101,13 @@ def writable(self): def seekable(self): """Implement :meth:`StoredFile.seekable`. """ - return False + return True + + def seek(self, n): + self._cursor = n + + def tell(self): + return self._cursor @property def name(self): @@ -107,6 +128,19 @@ def public_url(self): """ return None + @classmethod + def refresh_data(cls, target, value, oldvalue, initiator): + target._cursor = 0 + target._data = _marker + + @classmethod + def __declare_last__(cls): + # For the ``data`` column, use the field value setter from filedepot. + # filedepot already registers this event listener, but it does so in a + # way that won't work properly for subclasses of File + + event.listen(DBStoredFile.data, 'set', DBStoredFile.refresh_data) + def set_metadata(event): """Set DBStoredFile metadata based on data @@ -223,6 +257,7 @@ def includeme(config): from kotti.events import objectevent_listeners from kotti.events import ObjectInsert from kotti.events import ObjectUpdate + #from sqlalchemy import event configure_filedepot(config.get_settings()) @@ -231,3 +266,5 @@ def includeme(config): (ObjectInsert, DBStoredFile)].append(set_metadata) objectevent_listeners[ (ObjectUpdate, DBStoredFile)].append(set_metadata) + + #event.listen(DBStoredFile, 'load', DBStoredFile.set_cursor) diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index e2fe1eba6..ab5dfb356 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -188,4 +188,3 @@ def test_delete(self, factory, db_session, root, filedepot): assert DepotManager.get().delete.called assert id not in DepotManager.get()._storage.keys() - diff --git a/kotti/tests/test_filedepot.py b/kotti/tests/test_filedepot.py index 3b8eb6b32..6ec17021d 100644 --- a/kotti/tests/test_filedepot.py +++ b/kotti/tests/test_filedepot.py @@ -12,14 +12,19 @@ def test_storedfile_interface(self, db_session, events, setup_app): assert f.close() is None assert f.closed() is False - assert f.seekable() is False + assert f.seekable() is True assert f.writable() is False assert f.read() == 'content' + assert f.read() == '' + f.seek(0) + assert f.read() == 'content' + f.seek(0) assert f.read(-1) == 'content' - assert f.read(0) == '' + f.seek(0) assert f.read(2) == 'co' - assert f.read(4) == 'cont' + assert f.read(4) == 'nten' + assert f.tell() == 6 assert f.content_length == 1000 assert f.content_type == 'image/jpeg' From 0cadfe4289414e33cbaef391cd019337b0a18d90 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 14 Dec 2014 06:09:36 -0800 Subject: [PATCH 096/600] Fix test for default depot storage configuration --- kotti/tests/test_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotti/tests/test_app.py b/kotti/tests/test_app.py index ddb76b907..3785d4251 100644 --- a/kotti/tests/test_app.py +++ b/kotti/tests/test_app.py @@ -156,7 +156,7 @@ def test_default_filedepot(self, db_session): with patch('kotti.resources.initialize_sql'): main({}, **settings) - assert DepotManager.get().__class__.__name__ == 'LocalFileStorage' + assert DepotManager.get().__class__.__name__ == 'DBFileStorage' def test_configure_filedepot(self): from depot.manager import DepotManager From b0659ade512748ad45b8d39fb65f0592c5f3d041 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 14 Dec 2014 06:12:17 -0800 Subject: [PATCH 097/600] DBFileStorage.delete happens too late with default depot configuration --- kotti/filedepot.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/kotti/filedepot.py b/kotti/filedepot.py index 70a600aca..cb58acafa 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -257,7 +257,7 @@ def includeme(config): from kotti.events import objectevent_listeners from kotti.events import ObjectInsert from kotti.events import ObjectUpdate - #from sqlalchemy import event + from depot.fields.sqlalchemy import _SQLAMutationTracker configure_filedepot(config.get_settings()) @@ -267,4 +267,9 @@ def includeme(config): objectevent_listeners[ (ObjectUpdate, DBStoredFile)].append(set_metadata) - #event.listen(DBStoredFile, 'load', DBStoredFile.set_cursor) + # 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) From 525362154c10cecc3ec31993e681a71326b9f3f3 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 14 Dec 2014 09:13:39 -0800 Subject: [PATCH 098/600] Better test of DBStoredFile.read() --- kotti/tests/test_filedepot.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kotti/tests/test_filedepot.py b/kotti/tests/test_filedepot.py index 6ec17021d..7a9315cc5 100644 --- a/kotti/tests/test_filedepot.py +++ b/kotti/tests/test_filedepot.py @@ -25,6 +25,10 @@ def test_storedfile_interface(self, db_session, events, setup_app): assert f.read(2) == 'co' assert f.read(4) == 'nten' assert f.tell() == 6 + f.seek(0) + f.seek(100) + assert f.tell() == 100 + assert f.read() == '' assert f.content_length == 1000 assert f.content_type == 'image/jpeg' From 069b879ba7c003b4297968a16b35e2c7b062df22 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 14 Dec 2014 10:17:52 -0800 Subject: [PATCH 099/600] Use a string as fileid in depot test --- kotti/filedepot.py | 1 + kotti/tests/test_filedepot.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/kotti/filedepot.py b/kotti/filedepot.py index cb58acafa..031ae400c 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -17,6 +17,7 @@ _marker = object() + class DBStoredFile(Base): """depotfile StoredFile implementation that stores data in the db. diff --git a/kotti/tests/test_filedepot.py b/kotti/tests/test_filedepot.py index 7a9315cc5..c78a6b1c5 100644 --- a/kotti/tests/test_filedepot.py +++ b/kotti/tests/test_filedepot.py @@ -99,7 +99,7 @@ def test_create(self, db_session): def test_get(self, db_session): with pytest.raises(IOError): - DBFileStorage().get(1) + DBFileStorage().get("1") file_id = self.make_one() assert DBFileStorage().get(file_id).data == "content here" From d492e23970ad5b7ff78f1b79c38d99617141c299 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 14 Dec 2014 12:20:36 -0800 Subject: [PATCH 100/600] Added test for UploadedFileResponse; Parametrized the TestFileViews --- kotti/tests/__init__.py | 9 ++++- kotti/tests/test_file.py | 80 +++++++++++++++++++++++++++++----------- kotti/views/file.py | 1 + 3 files changed, 67 insertions(+), 23 deletions(-) diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index 06e94f159..c4e6bd86b 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -301,9 +301,14 @@ def read(block_size=-1): return info['content'] return None - f = MagicMock() + def seek(n=0): + finished[:] + + from StringIO import StringIO + + f = MagicMock(wraps=StringIO(info['content'])) + f.seek(0) f.public_url = '' - f.read = read f.filename = info['filename'] f.content_type = info['content_type'] f.content_length = len(info['content']) diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index ab5dfb356..606f27236 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -5,6 +5,9 @@ import pytest from kotti.testing import DummyRequest +from kotti.views.file import inline_view +from kotti.views.file import attachment_view +from kotti.views.file import UploadedFileResponse class TestFileViews: @@ -18,32 +21,22 @@ def _test_common_headers(self, headers): assert headers["Content-Length"] == "13" assert headers["Content-Type"] == "image/png" - def test_inline_view(self, config, filedepot): - from kotti.views.file import inline_view - - config.include('kotti.views.file') - + @pytest.mark.parametrize("params", + [(inline_view, 'inline'), + (attachment_view, 'attachment')]) + def test_file_views(self, params, config, filedepot): + view, disposition = params self._create_file(config) + res = view(self.file, None) - res = inline_view(self.file, None) - headers = res.headers + self._test_common_headers(res.headers) - self._test_common_headers(headers) + assert res.headers["Content-Disposition"] == disposition + \ + ';filename="myfle.png"' - assert headers["Content-Disposition"] == 'inline;filename="myfle.png"' - assert res.app_iter.file.read() == 'file contents' - - def test_attachment_view(self, config, filedepot): - from kotti.views.file import attachment_view - - self._create_file(config) - res = attachment_view(self.file, None) - headers = res.headers - - self._test_common_headers(headers) - assert headers["Content-Disposition"] == ( - 'attachment;filename="myfle.png"') assert res.app_iter.file.read() == 'file contents' + res.app_iter.file.seek(0) + assert res.body == 'file contents' class TestFileEditForm: @@ -188,3 +181,48 @@ def test_delete(self, factory, db_session, root, filedepot): assert DepotManager.get().delete.called assert id not in DepotManager.get()._storage.keys() + + +class TestUploadedFileResponse: + def _create_file(self): + from kotti.resources import File + return File("file contents", u"myf\xfcle.png", u"image/png") + + def test_as_body(self, filedepot): + f = self._create_file() + resp = UploadedFileResponse(f.data, DummyRequest()) + assert resp.body == 'file contents' + + def test_as_app_iter(self, filedepot): + from pyramid.response import FileIter + + f = self._create_file() + resp = UploadedFileResponse(f.data, DummyRequest()) + assert isinstance(resp.app_iter, FileIter) + assert ''.join(resp.app_iter) == 'file contents' + + def test_unknown_filename(self, filedepot): + from kotti.resources import File + f = File("file contents", u"file", None) + resp = UploadedFileResponse(f.data, DummyRequest()) + assert resp.headers['Content-Type'] == 'application/octet-stream' + + def test_caching(self, filedepot, monkeypatch): + import datetime + import webob.response + + f = self._create_file() + d = datetime.datetime(2012, 12, 31, 13, 0, 0) + + class mockdatetime: + @staticmethod + def utcnow(): + return d + + monkeypatch.setattr(webob.response, 'datetime', mockdatetime) + + resp = UploadedFileResponse(f.data, DummyRequest(), cache_max_age=10) + + # this is set by filedepot fixture + assert resp.headers['Last-Modified'] == 'Sun, 30 Dec 2012 00:00:00 GMT' + assert resp.headers['Expires'] == 'Mon, 31 Dec 2012 13:00:10 GMT' diff --git a/kotti/views/file.py b/kotti/views/file.py index f4b35cb53..5b75e48e6 100644 --- a/kotti/views/file.py +++ b/kotti/views/file.py @@ -38,6 +38,7 @@ def __init__(self, data, request=None, disposition='attachment', content_encoding=None): filename = data.filename + content_type = content_type or getattr(data, 'content_type', None) if content_type is None: content_type, content_encoding = mimetypes.guess_type( From a0be53f2645c631aa9a7378c7969386a90547f53 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 14 Dec 2014 12:36:40 -0800 Subject: [PATCH 101/600] Set the Content-Type as string in UploadedFileResponse --- kotti/views/file.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kotti/views/file.py b/kotti/views/file.py index 5b75e48e6..e100c8ac8 100644 --- a/kotti/views/file.py +++ b/kotti/views/file.py @@ -38,8 +38,8 @@ def __init__(self, data, request=None, disposition='attachment', content_encoding=None): filename = data.filename - content_type = content_type or getattr(data, 'content_type', None) + content_type = content_type or getattr(data, 'content_type', None) if content_type is None: content_type, content_encoding = mimetypes.guess_type( filename, @@ -47,10 +47,10 @@ def __init__(self, data, request=None, disposition='attachment', ) 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) + # 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) super(UploadedFileResponse, self).__init__( conditional_response=True, From 16c662e5973bf3d2531efcacc179230e89293da9 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 14 Dec 2014 12:36:51 -0800 Subject: [PATCH 102/600] Cleanup code in filedepot fixture --- kotti/tests/__init__.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index c4e6bd86b..146bb19b1 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -293,17 +293,6 @@ def __init__(self): def get(self, id): info = self._storage[id] - finished = [] - - def read(block_size=-1): - if not finished: - finished.append(True) - return info['content'] - return None - - def seek(n=0): - finished[:] - from StringIO import StringIO f = MagicMock(wraps=StringIO(info['content'])) From d44714167f430d32e1af48260851c7b890e081f7 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 14 Dec 2014 12:46:57 -0800 Subject: [PATCH 103/600] Added another test for UploadedFileResponse --- kotti/tests/test_file.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index 606f27236..904701ff5 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -207,6 +207,12 @@ def test_unknown_filename(self, filedepot): resp = UploadedFileResponse(f.data, DummyRequest()) assert resp.headers['Content-Type'] == 'application/octet-stream' + def test_guess_content_type(self, filedepot): + from kotti.resources import File + f = File("file contents", u"file.jpg", None) + resp = UploadedFileResponse(f.data, DummyRequest()) + assert resp.headers['Content-Type'] == 'image/jpeg' + def test_caching(self, filedepot, monkeypatch): import datetime import webob.response From 84d0ac0059e1eef100b31e04a7a46cca69173aef Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 14 Dec 2014 13:13:41 -0800 Subject: [PATCH 104/600] Added test for DBFileStorage session delete integration --- kotti/tests/test_filedepot.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/kotti/tests/test_filedepot.py b/kotti/tests/test_filedepot.py index c78a6b1c5..3b5f407b4 100644 --- a/kotti/tests/test_filedepot.py +++ b/kotti/tests/test_filedepot.py @@ -129,3 +129,23 @@ def test_replace(self, db_session): assert fs.filename == u'f3.jpg' assert fs.content_type == 'xls' assert fs.read() == 'third content' + + def test_session_integration(self, db_session): + from depot.manager import DepotManager + + DepotManager._default_depot = 'default' + DepotManager._depots = {'default': DBFileStorage()} + + file_id = DepotManager.get().create('content here', u'f.jpg', 'image/jpg') + fs = DepotManager.get().get(file_id) + + db_session.add(fs) + import transaction + transaction.commit() + + transaction.begin() + db_session.delete(fs) + transaction.commit() + + with pytest.raises(IOError): + DepotManager.get().get(file_id) From e79f8f1e2c246123f3e2b411f313ea6421cd92f4 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 15 Dec 2014 13:08:55 +0200 Subject: [PATCH 105/600] Remove wrong comment of __declare_last__ of DBStoredFile --- kotti/filedepot.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/kotti/filedepot.py b/kotti/filedepot.py index 031ae400c..720c24ac3 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -136,10 +136,6 @@ def refresh_data(cls, target, value, oldvalue, initiator): @classmethod def __declare_last__(cls): - # For the ``data`` column, use the field value setter from filedepot. - # filedepot already registers this event listener, but it does so in a - # way that won't work properly for subclasses of File - event.listen(DBStoredFile.data, 'set', DBStoredFile.refresh_data) From 8c80ce3fa0e63178b3c14fef4df6d23819949c48 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 15 Dec 2014 13:14:16 -0800 Subject: [PATCH 106/600] UploadedFileResponse can redirect to the permanent public_url of an uploaded field, if set by the depot storage --- kotti/tests/test_file.py | 26 ++++++++++++++++++++------ kotti/views/file.py | 17 ++++++++++------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/kotti/tests/test_file.py b/kotti/tests/test_file.py index 904701ff5..d4218377b 100644 --- a/kotti/tests/test_file.py +++ b/kotti/tests/test_file.py @@ -1,5 +1,6 @@ from StringIO import StringIO from colander import null +from pyramid.httpexceptions import HTTPMovedPermanently from mock import MagicMock import pytest @@ -184,9 +185,12 @@ def test_delete(self, factory, db_session, root, filedepot): class TestUploadedFileResponse: - def _create_file(self): + def _create_file(self, + data="file contents", + filename=u"myf\xfcle.png", + mimetype=u"image/png"): from kotti.resources import File - return File("file contents", u"myf\xfcle.png", u"image/png") + return File(data, filename, mimetype) def test_as_body(self, filedepot): f = self._create_file() @@ -202,14 +206,12 @@ def test_as_app_iter(self, filedepot): assert ''.join(resp.app_iter) == 'file contents' def test_unknown_filename(self, filedepot): - from kotti.resources import File - f = File("file contents", u"file", None) + f = self._create_file("file contents", u"file", None) resp = UploadedFileResponse(f.data, DummyRequest()) assert resp.headers['Content-Type'] == 'application/octet-stream' def test_guess_content_type(self, filedepot): - from kotti.resources import File - f = File("file contents", u"file.jpg", None) + f = self._create_file("file contents", u"file.jpg", None) resp = UploadedFileResponse(f.data, DummyRequest()) assert resp.headers['Content-Type'] == 'image/jpeg' @@ -232,3 +234,15 @@ def utcnow(): # this is set by filedepot fixture assert resp.headers['Last-Modified'] == 'Sun, 30 Dec 2012 00:00:00 GMT' assert resp.headers['Expires'] == 'Mon, 31 Dec 2012 13:00:10 GMT' + + def test_redirect(self, filedepot): + f = self._create_file() + f.data._thaw() + f.data['_public_url'] = 'http://example.com' + f.data._freeze() + + with pytest.raises(HTTPMovedPermanently) as e: + UploadedFileResponse(f.data, DummyRequest()) + + response = e.value + assert response.headers['Location'] == 'http://example.com' diff --git a/kotti/views/file.py b/kotti/views/file.py index e100c8ac8..e1be1dda7 100644 --- a/kotti/views/file.py +++ b/kotti/views/file.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from pyramid.httpexceptions import HTTPMovedPermanently from pyramid.response import _BLOCK_SIZE from pyramid.response import FileIter from pyramid.response import Response @@ -12,7 +13,7 @@ class UploadedFileResponse(Response): """ - A Response object that can be used to serve a uploaded files. + A Response object that can be used to serve an UploadedFile instance. ``data`` is the ``UploadedFile`` file field value. @@ -37,16 +38,18 @@ def __init__(self, data, request=None, disposition='attachment', cache_max_age=None, content_type=None, content_encoding=None): + if data._public_url: + raise HTTPMovedPermanently(data._public_url) + filename = data.filename content_type = content_type or getattr(data, 'content_type', None) if content_type is None: - content_type, content_encoding = mimetypes.guess_type( - filename, - strict=False - ) - if content_type is None: - content_type = 'application/octet-stream' + content_type, content_encoding = \ + mimetypes.guess_type(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. From a9954d5d2538341ef971259de27b97141cbe9a90 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 16 Dec 2014 13:36:35 -0800 Subject: [PATCH 107/600] Move last_modified modification after setting the data, as that automatically sets the last_modified value --- kotti/alembic/versions/413fa5fcc581_add_filedepot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py index 4e8c10fcc..c29865a78 100644 --- a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py +++ b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py @@ -37,8 +37,8 @@ def upgrade(): uploaded_file.process_content( obj.data, filename=obj.filename, content_type=obj.mimetype) stored_file = DepotManager.get().get(uploaded_file['file_id']) - stored_file.last_modified = obj.modification_date obj.data = uploaded_file.encode() + stored_file.last_modified = obj.modification_date log.info("Migrated {} bytes for File with pk {} to {}/{}".format( len(obj.data), obj.id, dn, uploaded_file['file_id'])) From 9f1fdc79fcae77908686b3a4aa82a55d5ba7f49c Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 16 Dec 2014 13:37:21 -0800 Subject: [PATCH 108/600] Improve inline documentation for filedepot.py --- kotti/filedepot.py | 85 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 17 deletions(-) diff --git a/kotti/filedepot.py b/kotti/filedepot.py index 720c24ac3..bebd77208 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -19,10 +19,11 @@ class DBStoredFile(Base): - """depotfile StoredFile implementation that stores data in the db. + """ :class:`depot.io.interfaces.StoredFile` implementation that stores + file data in SQL database. - Can be used together with DBFileStorage to implement blobs (large files) - storage in the database. + Can be used together with :class:`kotti.filedepot.DBFileStorage` to + implement blobs storage in the database. """ __tablename__ = "blobs" @@ -30,7 +31,7 @@ class DBStoredFile(Base): #: Primary key column in the DB #: (:class:`sqlalchemy.types.Integer`) id = Column(Integer(), primary_key=True) - #: Unique id given to this blob + #: 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. @@ -42,7 +43,7 @@ class DBStoredFile(Base): #: Size of the blob in bytes #: (:class:`sqlalchemy.types.Integer`) content_length = Column(Integer()) - #: Date / time the blob was created + #: Date / time the blob was created or last modified #: (:class:`sqlalchemy.types.DateTime`) last_modified = Column(DateTime()) #: The binary data itself @@ -105,38 +106,54 @@ def seekable(self): return True def seek(self, n): + """ Move the file cursor to position `n` + + :param n: Position for the cursor + :type n: int + """ self._cursor = n def tell(self): + """ Returns current position of file cursor + + :result: Current file cursor position. + :rtype: int + """ return self._cursor @property def name(self): """Implement :meth:`StoredFile.name`. - This is the filename of the saved file + :result: the filename of the saved file + :rtype: string """ return self.filename @property def public_url(self): - """The public HTTP url from which file can be accessed. + """ Integration with :class:`depot.middleware.DepotMiddleware` When supported by the storage this will provide the public url to which the file content can be accessed. In case this returns ``None`` it means that the file can - only be served by the :class:`DepotMiddleware` itself. + only be served by the :class:`depot.middleware.DepotMiddleware` itself. """ return None - @classmethod - def refresh_data(cls, target, value, oldvalue, initiator): - target._cursor = 0 - target._data = _marker - @classmethod def __declare_last__(cls): - event.listen(DBStoredFile.data, 'set', DBStoredFile.refresh_data) + """ 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) + + +def handle_change_data(target, value, oldvalue, initiator): + target._cursor = 0 + target._data = _marker def set_metadata(event): @@ -158,6 +175,11 @@ class DBFileStorage(FileStorage): def get(self, file_id): """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() @@ -168,9 +190,18 @@ def get(self, file_id): def create(self, content, filename=None, content_type=None): """Saves a new file and returns the file id - ``content`` parameter can either be ``bytes``, another ``file object`` + :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( @@ -195,6 +226,18 @@ def replace(self, file_or_id, content, filename=None, content_type=None): ``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) @@ -215,14 +258,21 @@ def replace(self, file_or_id, content, filename=None, content_type=None): fstore.data = content def delete(self, file_or_id): - """Deletes a file. If the file didn't exist it will just do nothing.""" + """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): - """Returns if a file or its ID still exist.""" + """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) @@ -251,6 +301,7 @@ def includeme(config): :param config: app config :type config: :class:`pyramid.config.Configurator` """ + from kotti.events import objectevent_listeners from kotti.events import ObjectInsert from kotti.events import ObjectUpdate From ea11c9fc9d566b0ec712594d65acae86f97e6f0c Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 16 Dec 2014 14:08:53 -0800 Subject: [PATCH 109/600] Improvements to inline documentation --- kotti/resources.py | 7 +++++++ kotti/tests/__init__.py | 2 ++ kotti/util.py | 16 ++++++++++++---- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/kotti/resources.py b/kotti/resources.py index fbe6f0809..d35afe78d 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -716,6 +716,13 @@ def __declare_last__(cls): @classmethod def set_metadata(cls, target, value, oldvalue, initiator): + """ 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 + """ newvalue = _SQLAMutationTracker._field_set( target, value, oldvalue, initiator) diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index 146bb19b1..33cb36889 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -282,6 +282,8 @@ def workflow(config): @fixture def filedepot(db_session, request): + """ Configures a mock depot store for :class:`depot.manager.DepotManager` + """ from depot.manager import DepotManager from datetime import datetime diff --git a/kotti/util.py b/kotti/util.py index 042af195f..3c3884b6e 100644 --- a/kotti/util.py +++ b/kotti/util.py @@ -278,7 +278,14 @@ def extract_from_settings(prefix, settings=None): def flatdotted_to_dict(prefix, settings=None): - """ + """ Merges items from a dictionary that have keys that start with `prefix` + to a new dictionary result. + + :param prefix: A dotted string representing the prefix for the common values + :type prefix: string + :value settings: A dictionary with settings. Result is extracted from this + :type settings: dict + >>> settings = { ... 'kotti.depot.default.backend': 'local', ... 'kotti.depot.default.file_storage': 'var/files', @@ -369,10 +376,11 @@ def command(func, doc): def _to_fieldstorage(fp, filename, mimetype, size, **_kwds): - """ Build a cgi.FieldStorage instance. + """ Build a :class:`cgi.FieldStorage` instance. - Deform's FileUploadWidget returns a dict, but filedepot's - UploadedFieldFile likes cgi.FieldStorage objects + Deform's :class:`FileUploadWidget` returns a dict, but + :class:`depot.fields.sqlalchemy.UploadedFileField` likes + :class:`cgi.FieldStorage` objects """ f = cgi.FieldStorage() f.file = fp From 0f80ac571fcc57e034f87dc63e66597410850d01 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Thu, 18 Dec 2014 14:35:08 -0800 Subject: [PATCH 110/600] Change the way we configure the depots --- kotti/__init__.py | 3 ++- kotti/filedepot.py | 9 +++++---- kotti/tests/test_app.py | 10 ++++++---- kotti/util.py | 39 ++++++++++++++++++++++----------------- 4 files changed, 35 insertions(+), 26 deletions(-) diff --git a/kotti/__init__.py b/kotti/__init__.py index e05af887d..47641e7be 100644 --- a/kotti/__init__.py +++ b/kotti/__init__.py @@ -101,7 +101,8 @@ def none_factory(**kwargs): # pragma: no cover 'kotti.datetime_format': 'medium', 'kotti.time_format': 'medium', 'kotti.max_file_size': '10', - 'kotti.depot.fs0.backend': 'kotti.filedepot.DBFileStorage', + 'kotti.depot.0.name': 'dbfiles', + 'kotti.depot.0.backend': 'kotti.filedepot.DBFileStorage', 'kotti.fanstatic.edit_needed': 'kotti.fanstatic.edit_needed', 'kotti.fanstatic.view_needed': 'kotti.fanstatic.view_needed', 'kotti.static.edit_needed': '', # BBB diff --git a/kotti/filedepot.py b/kotti/filedepot.py index bebd77208..fff69bdfb 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -286,12 +286,13 @@ def _get_file_id(self, file_or_id): def configure_filedepot(settings): - from kotti.util import flatdotted_to_dict + from kotti.util import extract_depot_settings from depot.manager import DepotManager - config = flatdotted_to_dict('kotti.depot.', settings) - for name, conf in config.items(): - if DepotManager.get(name) is 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='') diff --git a/kotti/tests/test_app.py b/kotti/tests/test_app.py index 3785d4251..cbb0c30a3 100644 --- a/kotti/tests/test_app.py +++ b/kotti/tests/test_app.py @@ -167,10 +167,12 @@ def test_configure_filedepot(self): tests.TFS2 = Mock(return_value=Mock(marker="TFS2")) settings = { - 'kotti.depot.localfs.backend': 'kotti.tests.TFS1', - 'kotti.depot.localfs.location': '/tmp', - 'kotti.depot.mongo.backend': 'kotti.tests.TFS2', - 'kotti.depot.mongo.uri': 'mongo://', + 'kotti.depot.0.backend': 'kotti.tests.TFS1', + 'kotti.depot.0.name': 'localfs', + 'kotti.depot.0.location': '/tmp', + 'kotti.depot.1.backend': 'kotti.tests.TFS2', + 'kotti.depot.1.uri': 'mongo://', + 'kotti.depot.1.name': 'mongo', } # depot.manager.DepotManager acts as singleton, save its settings diff --git a/kotti/util.py b/kotti/util.py index 3c3884b6e..9f9614381 100644 --- a/kotti/util.py +++ b/kotti/util.py @@ -277,9 +277,9 @@ def extract_from_settings(prefix, settings=None): return extracted -def flatdotted_to_dict(prefix, settings=None): +def extract_depot_settings(prefix="kotti.depot.", settings=None): """ Merges items from a dictionary that have keys that start with `prefix` - to a new dictionary result. + to a list of dictionaries. :param prefix: A dotted string representing the prefix for the common values :type prefix: string @@ -287,27 +287,32 @@ def flatdotted_to_dict(prefix, settings=None): :type settings: dict >>> settings = { - ... 'kotti.depot.default.backend': 'local', - ... 'kotti.depot.default.file_storage': 'var/files', - ... 'kotti.depot.mongo.backend': 'mongodb', - ... 'kotti.depot.mongo.uri': 'localhost://', + ... '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 = flatdotted_to_dict('kotti.depot.', settings) - >>> print sorted(res.keys()) - ['default', 'mongo'] - >>> print res['default'] - {'file_storage': 'var/files', 'backend': 'local'} - >>> print res['mongo'] - {'uri': 'localhost://', 'backend': 'mongodb'} + >>> 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(): - name, conf = k.split('.', 1) - extracted.setdefault(name, {}) - extracted[name][conf] = v + index, conf = k.split('.', 1) + index = int(index) + extracted.setdefault(index, {}) + extracted[index][conf] = v - return extracted + result = [] + for k in sorted(extracted.keys()): + result.append(extracted[k]) + + return result def disambiguate_name(name): From 6ba346353c9f00cfc733f18583fd83c1a6e77bb6 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Thu, 18 Dec 2014 16:12:18 -0800 Subject: [PATCH 111/600] Provide automatic conversion to cgi.FieldStorage using an event listener on set of File.data --- kotti/resources.py | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/kotti/resources.py b/kotti/resources.py index d35afe78d..290e25027 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -19,7 +19,6 @@ from depot.fields.sqlalchemy import _SQLAMutationTracker from pyramid.traversal import resource_path -from sqlalchemy import event from sqlalchemy import Boolean from sqlalchemy import Column from sqlalchemy import DateTime @@ -29,9 +28,11 @@ from sqlalchemy import Unicode from sqlalchemy import UnicodeText from sqlalchemy import UniqueConstraint +from sqlalchemy import event from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.ext.orderinglist import ordering_list +from sqlalchemy.orm import ColumnProperty from sqlalchemy.orm import backref from sqlalchemy.orm import object_mapper from sqlalchemy.orm import relation @@ -678,9 +679,6 @@ def __init__(self, data=None, filename=None, mimetype=None, size=None, self.filename = filename self.mimetype = mimetype self.size = size - if isinstance(data, basestring): - data = _to_fieldstorage(fp=StringIO(data), filename=filename, - mimetype=mimetype, size=size) self.data = data @classmethod @@ -712,10 +710,22 @@ def __declare_last__(cls): # filedepot already registers this event listener, but it does so in a # way that won't work properly for subclasses of File - event.listen(cls.data, 'set', cls.set_metadata, retval=True) + mapper = cls._sa_class_manager.mapper + + for mapper_property in mapper.iterate_properties: + if isinstance(mapper_property, ColumnProperty): + for idx, col in enumerate(mapper_property.columns): + if isinstance(col.type, UploadedFileField): + args = (mapper_property, + 'set', + _SQLAMutationTracker._field_set) + if event.contains(*args): + event.remove(*args) + + event.listen(cls.data, 'set', cls._save_data, retval=True) @classmethod - def set_metadata(cls, target, value, oldvalue, initiator): + def _save_data(cls, target, value, oldvalue, initiator): """ Refresh metadata and save the binary data to the data field. :param target: The File instance @@ -723,15 +733,25 @@ def set_metadata(cls, target, value, oldvalue, initiator): :param value: The container for binary data :type value: A :class:`cgi.FieldStorage` instance """ + + if isinstance(value, bytes): + value = _to_fieldstorage(fp=StringIO(value), + 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 + if newvalue.filename: + target.filename = newvalue.filename + if newvalue.content_type: + target.mimetype = newvalue.content_type + target.size = newvalue.file.content_length - target.mimetype = newvalue.content_type return newvalue From 3baf09a7256d46b719d9c64feb4336672d72c5f2 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Thu, 18 Dec 2014 16:25:05 -0800 Subject: [PATCH 112/600] Cleanup File.from_field_storage method --- kotti/resources.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/kotti/resources.py b/kotti/resources.py index 290e25027..4a85cb3a1 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -694,15 +694,10 @@ def from_field_storage(cls, fs): :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("Unsupported MIME type: %s" % fs.type) - if not cls.type_info.is_uploadable_mimetype(mimetype): - raise ValueError("Unsupported MIME type: %s" % mimetype) - - return cls(data=data, filename=filename, mimetype=mimetype, size=size) + return cls(data=fs) @classmethod def __declare_last__(cls): @@ -750,8 +745,8 @@ def _save_data(cls, target, value, oldvalue, initiator): target.filename = newvalue.filename if newvalue.content_type: target.mimetype = newvalue.content_type - target.size = newvalue.file.content_length + return newvalue From 7bcbfc8711fa0ee042d05c6ef5458149627e7a31 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Thu, 18 Dec 2014 22:31:06 -0800 Subject: [PATCH 113/600] Simplify __declare_last__; Remove not needed code in _save_data --- kotti/resources.py | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/kotti/resources.py b/kotti/resources.py index 4a85cb3a1..34aaf49ce 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -32,7 +32,6 @@ from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.ext.orderinglist import ordering_list -from sqlalchemy.orm import ColumnProperty from sqlalchemy.orm import backref from sqlalchemy.orm import object_mapper from sqlalchemy.orm import relation @@ -701,22 +700,15 @@ def from_field_storage(cls, fs): @classmethod def __declare_last__(cls): - # For the ``data`` column, use the field value setter from filedepot. - # filedepot already registers this event listener, but it does so in a - # way that won't work properly for subclasses of File - + # Unconfigure the event set in _SQLAMutationTracker, we have _save_data mapper = cls._sa_class_manager.mapper + prop = mapper.attrs['data'] + args = (prop, 'set', _SQLAMutationTracker._field_set) + if event.contains(*args): + event.remove(*args) - for mapper_property in mapper.iterate_properties: - if isinstance(mapper_property, ColumnProperty): - for idx, col in enumerate(mapper_property.columns): - if isinstance(col.type, UploadedFileField): - args = (mapper_property, - 'set', - _SQLAMutationTracker._field_set) - if event.contains(*args): - event.remove(*args) - + # Declaring the event on the class attribute instead of mapper property + # enables its registration on subclasses event.listen(cls.data, 'set', cls._save_data, retval=True) @classmethod @@ -741,10 +733,8 @@ def _save_data(cls, target, value, oldvalue, initiator): if newvalue is None: return - if newvalue.filename: - target.filename = newvalue.filename - if newvalue.content_type: - target.mimetype = newvalue.content_type + target.filename = newvalue.filename + target.mimetype = newvalue.content_type target.size = newvalue.file.content_length return newvalue From c4abf8a25fde7a1d691abaaeb5b5c4f69f8424bf Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Thu, 18 Dec 2014 22:55:17 -0800 Subject: [PATCH 114/600] Don't treat File.data for mysql engine, do it for DBStoredFile.data --- kotti/filedepot.py | 6 ++++++ kotti/resources.py | 7 ++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/kotti/filedepot.py b/kotti/filedepot.py index fff69bdfb..f4becc612 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -322,3 +322,9 @@ def includeme(config): event.listen(DBSession, 'before_commit', _SQLAMutationTracker._session_committed) + + # adjust for engine type + engine = DBSession.connection().engine + if engine.dialect.name == 'mysql': # pragma: no cover + from sqlalchemy.dialects.mysql.base import LONGBLOB + DBStoredFile.__table__.c.data.type = LONGBLOB() diff --git a/kotti/resources.py b/kotti/resources.py index 34aaf49ce..9271a6b04 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -702,13 +702,12 @@ def from_field_storage(cls, fs): def __declare_last__(cls): # Unconfigure the event set in _SQLAMutationTracker, we have _save_data mapper = cls._sa_class_manager.mapper - prop = mapper.attrs['data'] - args = (prop, 'set', _SQLAMutationTracker._field_set) + 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 its registration on subclasses + # enables proper registration on its subclasses event.listen(cls.data, 'set', cls._save_data, retval=True) @classmethod @@ -788,8 +787,6 @@ def default_get_root(request=None): 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() # 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 From 87c59c5ad4b3bb79a7303a0c383b012602965e09 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sat, 20 Dec 2014 07:51:03 -0800 Subject: [PATCH 115/600] Added some documentation for filedepot --- docs/developing/advanced/blobs.rst | 124 +++++++++++++++++++++++++++++ docs/developing/advanced/index.rst | 1 + 2 files changed, 125 insertions(+) create mode 100644 docs/developing/advanced/blobs.rst diff --git a/docs/developing/advanced/blobs.rst b/docs/developing/advanced/blobs.rst new file mode 100644 index 000000000..af64259c7 --- /dev/null +++ b/docs/developing/advanced/blobs.rst @@ -0,0 +1,124 @@ +.. _blobs + +Working with blob data in Kotti +=============================== + +Kotti provides a flexible mechanism of storing blob data by with the help of +:app:`filedepot`_ storages. Both ``File`` and ``Image`` store their data in +:class:`depot.fields.sqlalchemy.UploadedFileField` and they will offload their +blob data to the configured depot storage. Working together with +:app:`filedepot` configured storages means it is possible to store blob data in +a variety of ways: filesystem, GridFS, Amazon storage, etc. By default +:app:`Kotti` will store its blob data in the configured SQL database, using +``~kotti.filedepot.DBFileStorage`` storage, but you can configure your own +preferred way of storing your blob data. + +Configuring a depot store +------------------------- + +While :app:`filedepot` allows storing data in any of the configured +filestorages, at this time there's no mechanism in Kotti to select, at runtime, +the depot where new data will be saved. Instead, :app:`Kotti` will store new +files only in the configured *default* store. If, for example, you add a new +depot and make that the default, you should leave the old depot configured so +that :app:`Kotti` will continue serving files uploaded there. + +By default, `Kotti` comes configured with a db-based filestorage.:: + + kotti.depot.0.name = dbfiles + kotti.depot.0.backend = kotti.filedepot.DBFileStorage + +The depot configured at position 0 is the default file depot. The minimum +information required to configure a depot are the `name` and `backend`. The +`name` can be any string and it is used by :app:`filedepot` to identify the +depot store for a particular saved file. The `name` should never be changed, as +it will make the saved files unaccessible. + +Any further parameters for a particular backend will be passed as keyword +arguments to the backend class. See this example, in which we store, by +default, files in `/var/local/files/` using the +:class:`depot.io.local.LocalFileStorage`:: + + kotti.depot.0.name = localfs + kotti.depot.0.backend = depot.io.local.LocalFileStorage + kotti.depot.0.storage_path = /var/local/files + kotti.depot.1.name = dbfiles + kotti.depot.1.backend = kotti.filedepot.DBFileStorage + +Notice that we kept the `dbfiles` storage, but we moved it to position 1. No +blob data will be saved anymore, but existing files in that storage will +continue to be served from there. + +Add a blob field to your model +------------------------------ +Adding a blob data attribute to your 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_fielstorage(**data) + +Note that the ``data`` dictionary described here has the same format as the +deserialized value of a ``deform.widget.FileUploadWidget``. See the +: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. + +Downloading blob data +--------------------- + +Serving blob data is facilitated by the +:class:``~kotti.views.file.UploadedFileResponse``. You should return an +instance of this class as the response of your view, and it will stream the +blob from the storage to the client browser. As parameters it takes the blob +column and the type of disposition: ``inline`` or ``attachment`` (to trigger a +download in the browser). This, for example is the ``inline-view`` view for a +:class:``~kotti.resources.File``:: + + @view_config(name='inline-view', context=File, permission='view') + def inline_view(context, request): + return UploadedFileResponse(context.data, request, disposition='inline') + +If the used depot storage offers a ``public_url`` value for the blob, then +``UploadedFileResponse``, instead of streaming the data, will redirect instead +to that location. + +Inheritance issues with UploadedFileField columns +------------------------------------------------- + +You should be aware that, presently, inheriting the ``UploadedFileField`` column doesn't work properly. For a solution to this problem, look how we solve the problem using :meth:`~kotti.resources.File.__declare_last__`, which will solve the problem for the :class:`kotti.resources.Image` subclass. + +.. _filedepot: https://pypi.python.org/pypi/filedepot/ diff --git a/docs/developing/advanced/index.rst b/docs/developing/advanced/index.rst index cec0acc14..b86d8746c 100644 --- a/docs/developing/advanced/index.rst +++ b/docs/developing/advanced/index.rst @@ -12,5 +12,6 @@ Advanced Topics events frontpage-different-template images + blobs static-resource-management understanding-kotti-startup From 3a98d1fef96a16118e61961f5756a6572276946e Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sat, 20 Dec 2014 22:22:42 -0800 Subject: [PATCH 116/600] Fixes to blobs.rst documentation --- docs/developing/advanced/blobs.rst | 34 +++++++++++++++++------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/docs/developing/advanced/blobs.rst b/docs/developing/advanced/blobs.rst index af64259c7..c15cf9ad6 100644 --- a/docs/developing/advanced/blobs.rst +++ b/docs/developing/advanced/blobs.rst @@ -10,7 +10,7 @@ blob data to the configured depot storage. Working together with :app:`filedepot` configured storages means it is possible to store blob data in a variety of ways: filesystem, GridFS, Amazon storage, etc. By default :app:`Kotti` will store its blob data in the configured SQL database, using -``~kotti.filedepot.DBFileStorage`` storage, but you can configure your own +:class:``~kotti.filedepot.DBFileStorage`` storage, but you can configure your own preferred way of storing your blob data. Configuring a depot store @@ -29,14 +29,14 @@ By default, `Kotti` comes configured with a db-based filestorage.:: kotti.depot.0.backend = kotti.filedepot.DBFileStorage The depot configured at position 0 is the default file depot. The minimum -information required to configure a depot are the `name` and `backend`. The -`name` can be any string and it is used by :app:`filedepot` to identify the -depot store for a particular saved file. The `name` should never be changed, as -it will make the saved files unaccessible. +information required to configure a depot are the ``name`` and ``backend``. The +``name`` can be any string and it is used by :app:`filedepot` to identify the +depot store for a particular saved file. The ``name`` should never be changed, as +it will make the saved files unaccessible. Any further parameters for a particular backend will be passed as keyword arguments to the backend class. See this example, in which we store, by -default, files in `/var/local/files/` using the +default, files in ``/var/local/files/`` using the :class:`depot.io.local.LocalFileStorage`:: kotti.depot.0.name = localfs @@ -45,13 +45,13 @@ default, files in `/var/local/files/` using the kotti.depot.1.name = dbfiles kotti.depot.1.backend = kotti.filedepot.DBFileStorage -Notice that we kept the `dbfiles` storage, but we moved it to position 1. No -blob data will be saved anymore, but existing files in that storage will -continue to be served from there. +Notice that we kept the ``dbfiles`` storage, but we moved it to position 1. No +blob data will be saved there anymore, but existing files in that storage will +continue to be available from there. Add a blob field to your model ------------------------------ -Adding a blob data attribute to your can be as simple as:: +Adding a blob data attribute to your models can be as simple as:: from depot.fields.sqlalchemy import UploadedFileField from kotti.resources import Content @@ -59,7 +59,7 @@ Adding a blob data attribute to your can be as simple as:: class Person(Content): avatar = UploadedFileField() -While you can directly assign a `bytes` value to the `avatar` column, the +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.:: @@ -85,7 +85,7 @@ 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 +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:: @@ -113,12 +113,16 @@ download in the browser). This, for example is the ``inline-view`` view for a return UploadedFileResponse(context.data, request, disposition='inline') If the used depot storage offers a ``public_url`` value for the blob, then -``UploadedFileResponse``, instead of streaming the data, will redirect instead -to that location. +``UploadedFileResponse``, instead of streaming the data, will redirect to that +location. Inheritance issues with UploadedFileField columns ------------------------------------------------- -You should be aware that, presently, inheriting the ``UploadedFileField`` column doesn't work properly. For a solution to this problem, look how we solve the problem using :meth:`~kotti.resources.File.__declare_last__`, which will solve the problem for the :class:`kotti.resources.Image` subclass. +You should be aware that, presently, subclassing a model with an +``UploadedFileField`` column doesn't work properly. As a workaround, look how +we solve the problem using a ``__declare_last__`` classmethod in +:meth:`~kotti.resources.File.__declare_last__`, which will solve the problem +for the :class:`kotti.resources.Image` subclass. .. _filedepot: https://pypi.python.org/pypi/filedepot/ From d3102599271eae69f465429ece29634f0e624c28 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sat, 20 Dec 2014 23:21:31 -0800 Subject: [PATCH 117/600] Better explanation of how to subclass model with UploadedFileField --- docs/developing/advanced/blobs.rst | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/developing/advanced/blobs.rst b/docs/developing/advanced/blobs.rst index c15cf9ad6..060aec2ab 100644 --- a/docs/developing/advanced/blobs.rst +++ b/docs/developing/advanced/blobs.rst @@ -120,9 +120,19 @@ 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, look how -we solve the problem using a ``__declare_last__`` classmethod in -:meth:`~kotti.resources.File.__declare_last__`, which will solve the problem -for the :class:`kotti.resources.Image` subclass. +``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) + .. _filedepot: https://pypi.python.org/pypi/filedepot/ From 6e336dc0c7df8aec11e15af5f4fbde903b708f54 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 21 Dec 2014 09:54:18 -0800 Subject: [PATCH 118/600] Added a kotti-migrate-storage command --- kotti/filedepot.py | 75 +++++++++++++++++++++++++++++++---- kotti/tests/__init__.py | 21 ++++++++++ kotti/tests/test_app.py | 11 +---- kotti/tests/test_filedepot.py | 63 +++++++++++++++++++++++++++++ setup.py | 1 + 5 files changed, 154 insertions(+), 17 deletions(-) diff --git a/kotti/filedepot.py b/kotti/filedepot.py index f4becc612..fb963f87b 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -14,6 +14,7 @@ from kotti import Base from kotti import DBSession +from kotti.util import command _marker = object() @@ -58,7 +59,7 @@ def __init__(self, file_id, filename=None, content_type=None, self.file_id = file_id self.filename = filename self.content_type = content_type - self.last_modified = last_modified + self.last_modified = last_modified or datetime.now() self.content_length = content_length for k, v in kwds.items(): @@ -296,6 +297,67 @@ def configure_filedepot(settings): DepotManager.configure(name, conf, prefix='') +def migrate_storage(from_storage, to_storage): + from depot.fields.sqlalchemy import _SQLAMutationTracker + from depot.manager import DepotManager + from kotti.util import _to_fieldstorage + import logging + + 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 + for instance in DBSession.query(klass): + 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__, + ) + + +def adjust_for_engine(conn, branch): + # 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() + + def includeme(config): """ Pyramid includeme hook. @@ -308,6 +370,11 @@ def includeme(config): from kotti.events import ObjectUpdate from depot.fields.sqlalchemy import _SQLAMutationTracker + 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 @@ -322,9 +389,3 @@ def includeme(config): event.listen(DBSession, 'before_commit', _SQLAMutationTracker._session_committed) - - # adjust for engine type - engine = DBSession.connection().engine - if engine.dialect.name == 'mysql': # pragma: no cover - from sqlalchemy.dialects.mysql.base import LONGBLOB - DBStoredFile.__table__.c.data.type = LONGBLOB() diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index 33cb36889..c1b0d84b2 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -335,3 +335,24 @@ def restore(): DepotManager._default_depot = _old_default_depot request.addfinalizer(restore) + + +@fixture +def no_filedepots(db_session, request): + """ A filedepot fixture to empty and then restore DepotManager configuration + """ + from depot.manager import DepotManager + + _old_depots = DepotManager._depots + _old_default_depot = DepotManager._default_depot + + DepotManager._depots = {} + DepotManager._default_depot = None + + + def restore(): + db_session.rollback() + DepotManager._depots = _old_depots + DepotManager._default_depot = _old_default_depot + + request.addfinalizer(restore) diff --git a/kotti/tests/test_app.py b/kotti/tests/test_app.py index cbb0c30a3..12dc91f88 100644 --- a/kotti/tests/test_app.py +++ b/kotti/tests/test_app.py @@ -158,7 +158,7 @@ def test_default_filedepot(self, db_session): main({}, **settings) assert DepotManager.get().__class__.__name__ == 'DBFileStorage' - def test_configure_filedepot(self): + def test_configure_filedepot(self, no_filedepots): from depot.manager import DepotManager from kotti.filedepot import configure_filedepot from kotti import tests @@ -175,12 +175,6 @@ def test_configure_filedepot(self): 'kotti.depot.1.name': 'mongo', } - # depot.manager.DepotManager acts as singleton, save its settings - _depots = DepotManager._depots - _default_depot = DepotManager._default_depot - DepotManager._depots = {} - DepotManager._default_depot = None - configure_filedepot(settings) assert DepotManager.get().marker == 'TFS1' @@ -190,9 +184,6 @@ def test_configure_filedepot(self): tests.TFS1.assert_called_with(location='/tmp') tests.TFS2.assert_called_with(uri='mongo://') - DepotManager._depots = _depots - DepotManager._default_depot = _default_depot - del tests.TFS1 del tests.TFS2 diff --git a/kotti/tests/test_filedepot.py b/kotti/tests/test_filedepot.py index 3b5f407b4..130aa77c8 100644 --- a/kotti/tests/test_filedepot.py +++ b/kotti/tests/test_filedepot.py @@ -2,6 +2,7 @@ import pytest from kotti.filedepot import DBFileStorage, DBStoredFile +from kotti.resources import File class TestDBStoredFile: @@ -149,3 +150,65 @@ def test_session_integration(self, db_session): with pytest.raises(IOError): DepotManager.get().get(file_id) + + +class TestMigrateBetweenStorage: + + def _create_content(self, db_session, root): + data = [ + ('f1', u'file1.jpg', 'image/jpeg'), + ('f2', u'file2.png', 'image/png'), + ] + for row in data: + f = File(data=row[0], filename=row[1], mimetype=row[2]) + root[row[1]] = f + + db_session.flush() + + def test_migrate_between_storages(self, db_session, root, no_filedepots): + from kotti.filedepot import configure_filedepot + from kotti.filedepot import migrate_storage + from depot.fields.sqlalchemy import _SQLAMutationTracker + from sqlalchemy import event + import os + import tempfile + import shutil + + event.listen(db_session, + 'before_commit', + _SQLAMutationTracker._session_committed) + + # save depot configuration: use a fixture "save_depot_config" + # configure two depots: + + tmp_location = tempfile.mkdtemp() + + settings = { + 'kotti.depot.0.backend': 'kotti.filedepot.DBFileStorage', + 'kotti.depot.0.name': 'dbfiles', + + 'kotti.depot.1.backend': 'depot.io.local.LocalFileStorage', + 'kotti.depot.1.name': 'localfs', + 'kotti.depot.1.storage_path': tmp_location, + } + + configure_filedepot(settings) + self._create_content(db_session, root) + + assert db_session.query(DBStoredFile).count() == 2 + + migrate_storage('dbfiles', 'localfs') + + folders = os.listdir(tmp_location) + assert len(folders) == 2 + + db_session.flush() + + + for f in db_session.query(File): + uf = f.data + assert uf.file_id and uf.file_id in folders + + # assert db_session.query(DBStoredFile).count() == 0 + + shutil.rmtree(tmp_location) diff --git a/setup.py b/setup.py index edff161d4..05ff26730 100644 --- a/setup.py +++ b/setup.py @@ -120,6 +120,7 @@ [console_scripts] kotti-migrate = kotti.migrate:kotti_migrate_command kotti-reset-workflow = kotti.workflow:reset_workflow_command + kotti-migrate-storage = kotti.filedepot:migrate_storages_command [pytest11] kotti = kotti.tests From 851c4db23572b48702c986f21e765fdf9a6db80d Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 21 Dec 2014 11:46:49 -0800 Subject: [PATCH 119/600] Improve kotti-migrate-storage test --- kotti/tests/test_filedepot.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/kotti/tests/test_filedepot.py b/kotti/tests/test_filedepot.py index 130aa77c8..f6b085b13 100644 --- a/kotti/tests/test_filedepot.py +++ b/kotti/tests/test_filedepot.py @@ -156,8 +156,8 @@ class TestMigrateBetweenStorage: def _create_content(self, db_session, root): data = [ - ('f1', u'file1.jpg', 'image/jpeg'), - ('f2', u'file2.png', 'image/png'), + ('f1...', u'file1.jpg', 'image/jpeg'), + ('f2...', u'file2.png', 'image/png'), ] for row in data: f = File(data=row[0], filename=row[1], mimetype=row[2]) @@ -168,6 +168,7 @@ def _create_content(self, db_session, root): def test_migrate_between_storages(self, db_session, root, no_filedepots): from kotti.filedepot import configure_filedepot from kotti.filedepot import migrate_storage + from kotti.resources import Node from depot.fields.sqlalchemy import _SQLAMutationTracker from sqlalchemy import event import os @@ -178,9 +179,6 @@ def test_migrate_between_storages(self, db_session, root, no_filedepots): 'before_commit', _SQLAMutationTracker._session_committed) - # save depot configuration: use a fixture "save_depot_config" - # configure two depots: - tmp_location = tempfile.mkdtemp() settings = { @@ -204,11 +202,19 @@ def test_migrate_between_storages(self, db_session, root, no_filedepots): db_session.flush() + # here we need a transaction.commit(), but that would mess with the + # rest of the tests; we'll just trigger the event handler manually + _SQLAMutationTracker._session_committed(db_session) + + root = db_session.query(Node).filter_by(parent=None).one() + f1 = root['file1.jpg'] + assert f1.data.file_id in folders + assert f1.data.file.read() == 'f1...' - for f in db_session.query(File): - uf = f.data - assert uf.file_id and uf.file_id in folders + f2 = root['file2.png'] + assert f2.data.file_id in folders + assert f2.data.file.read() == 'f2...' - # assert db_session.query(DBStoredFile).count() == 0 + assert db_session.query(DBStoredFile).count() == 0 shutil.rmtree(tmp_location) From 26a276ca2e6f7594a01714b436c508d3aef1ba89 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 22 Dec 2014 15:39:44 -0800 Subject: [PATCH 120/600] Also test for Image for the filedepot migration --- kotti/tests/test_filedepot.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/kotti/tests/test_filedepot.py b/kotti/tests/test_filedepot.py index f6b085b13..c2079b69c 100644 --- a/kotti/tests/test_filedepot.py +++ b/kotti/tests/test_filedepot.py @@ -3,6 +3,7 @@ from kotti.filedepot import DBFileStorage, DBStoredFile from kotti.resources import File +from kotti.resources import Image class TestDBStoredFile: @@ -163,6 +164,14 @@ def _create_content(self, db_session, root): f = File(data=row[0], filename=row[1], mimetype=row[2]) root[row[1]] = f + data = [ + ('i1...', u'image1.jpg', 'image/jpeg'), + ('i2...', u'image2.png', 'image/png'), + ] + for row in data: + f = Image(data=row[0], filename=row[1], mimetype=row[2]) + root[row[1]] = f + db_session.flush() def test_migrate_between_storages(self, db_session, root, no_filedepots): @@ -193,12 +202,12 @@ def test_migrate_between_storages(self, db_session, root, no_filedepots): configure_filedepot(settings) self._create_content(db_session, root) - assert db_session.query(DBStoredFile).count() == 2 + assert db_session.query(DBStoredFile).count() == 4 migrate_storage('dbfiles', 'localfs') folders = os.listdir(tmp_location) - assert len(folders) == 2 + assert len(folders) == 4 db_session.flush() @@ -215,6 +224,14 @@ def test_migrate_between_storages(self, db_session, root, no_filedepots): assert f2.data.file_id in folders assert f2.data.file.read() == 'f2...' + i1 = root['image1.jpg'] + assert i1.data.file_id in folders + assert i1.data.file.read() == 'i1...' + + i2 = root['image2.png'] + assert i2.data.file_id in folders + assert i2.data.file.read() == 'i2...' + assert db_session.query(DBStoredFile).count() == 0 shutil.rmtree(tmp_location) From 33536a21c999db9e87c6a548ce4d9ee147754a1a Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 22 Dec 2014 15:40:28 -0800 Subject: [PATCH 121/600] In the filedepot migration, filter by type when discovering migratable items, to avoid polymorphic issues --- kotti/filedepot.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/kotti/filedepot.py b/kotti/filedepot.py index fb963f87b..bd68f651e 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -14,6 +14,7 @@ from kotti import Base from kotti import DBSession +from kotti.util import camel_case_to_name from kotti.util import command _marker = object() @@ -310,8 +311,13 @@ def migrate_storage(from_storage, to_storage): for klass, props in _SQLAMutationTracker.mapped_entities.items(): log.info("Migrating %r", klass) + mapper = klass._sa_class_manager.mapper - for instance in DBSession.query(klass): + + # 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: From e4dc80f6b6cd6ef0ee36f49730af8362f4e8892e Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 22 Dec 2014 15:53:41 -0800 Subject: [PATCH 122/600] Added section to blobs.rst about the fixtures for testing UploadedFileField columns --- docs/developing/advanced/blobs.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/developing/advanced/blobs.rst b/docs/developing/advanced/blobs.rst index 060aec2ab..7a6d7ca43 100644 --- a/docs/developing/advanced/blobs.rst +++ b/docs/developing/advanced/blobs.rst @@ -116,6 +116,15 @@ If the used depot storage offers a ``public_url`` value for the blob, then ``UploadedFileResponse``, instead of streaming the data, will redirect to that location. +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 always 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 file depot 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 ------------------------------------------------- From 044a5af5f78065a6e988c6a6ec581ef881e0db73 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 22 Dec 2014 15:54:19 -0800 Subject: [PATCH 123/600] Line formatting in blobs.rst --- docs/developing/advanced/blobs.rst | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/developing/advanced/blobs.rst b/docs/developing/advanced/blobs.rst index 7a6d7ca43..d587ee11b 100644 --- a/docs/developing/advanced/blobs.rst +++ b/docs/developing/advanced/blobs.rst @@ -119,11 +119,17 @@ location. 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. +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 always 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. +``UploadedFileField`` columns always 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 file depot 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. +If you're developing new file depot 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 ------------------------------------------------- From f289779b5fe33bc37637681ec8d5e3b33ab4845d Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 23 Dec 2014 04:38:48 -0800 Subject: [PATCH 124/600] Small fixes to blobs.rst --- docs/developing/advanced/blobs.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/developing/advanced/blobs.rst b/docs/developing/advanced/blobs.rst index d587ee11b..c20f428d9 100644 --- a/docs/developing/advanced/blobs.rst +++ b/docs/developing/advanced/blobs.rst @@ -74,7 +74,7 @@ While you can directly assign a ``bytes`` value to the ``avatar`` column, the 'size': len(content), } person = Person() - person.avatar = _to_fielstorage(**data) + 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 the @@ -123,11 +123,11 @@ 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 always 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. +``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 file depot storages you should use the +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. From 5ff6f34dfbf5cd11d61fcf6afec43f5192695113 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 23 Dec 2014 10:28:06 -0800 Subject: [PATCH 125/600] Filedepot documentation: added apidoc rst, added section on migration in blobs.rst --- docs/api/index.rst | 1 + docs/api/kotti.filedepot.rst | 7 +++++ docs/developing/advanced/blobs.rst | 45 +++++++++++++++++++++++++++--- 3 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 docs/api/kotti.filedepot.rst diff --git a/docs/api/index.rst b/docs/api/index.rst index 99045ac76..4b2284af7 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -17,6 +17,7 @@ API Documentation kotti.populate kotti.request kotti.resources + kotti.filedepot kotti.security kotti.sqla kotti.testing 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/developing/advanced/blobs.rst b/docs/developing/advanced/blobs.rst index c20f428d9..4354b9d0f 100644 --- a/docs/developing/advanced/blobs.rst +++ b/docs/developing/advanced/blobs.rst @@ -8,10 +8,17 @@ Kotti provides a flexible mechanism of storing blob data by with the help of :class:`depot.fields.sqlalchemy.UploadedFileField` and they will offload their blob data to the configured depot storage. Working together with :app:`filedepot` configured storages means it is possible to store blob data in -a variety of ways: filesystem, GridFS, Amazon storage, etc. By default -:app:`Kotti` will store its blob data in the configured SQL database, using -:class:``~kotti.filedepot.DBFileStorage`` storage, but you can configure your own -preferred way of storing your blob data. +a variety of ways: filesystem, GridFS, Amazon storage, etc. + +By default :app:`Kotti` will store its blob data in the configured SQL +database, using :class:``~kotti.filedepot.DBFileStorage`` storage, but you can +configure your own preferred way of storing your blob data. The benefit of +storing files in ``DBFileStorage`` is having *all* content in a single place +(the DB) which makes backups, exporting and importing of your site's data easy, +as long as you don't have too many or too large files. The downsides of this +approach appear when your database server resides on a different host (network +performance becomes a greater issue) or your DB dumps become too large to be +handled efficiently. Configuring a depot store ------------------------- @@ -150,4 +157,34 @@ below, where we're fixing the ``data`` column of the ``File`` class. :: 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! + .. _filedepot: https://pypi.python.org/pypi/filedepot/ From 3a6a47467738e65bfd2761aeb0392dc6cda59d9e Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 23 Dec 2014 10:38:52 -0800 Subject: [PATCH 126/600] Fix kotti-migrate-storage inline help --- kotti/filedepot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kotti/filedepot.py b/kotti/filedepot.py index bd68f651e..e33462f3c 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -344,8 +344,8 @@ def migrate_storages_command(): # pragma: no cover 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 + --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 0e59bb2cb92a8e3f57eb31b09bf62aec420002e8 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Wed, 24 Dec 2014 04:32:28 -0800 Subject: [PATCH 127/600] Added filedepot section in configuration.rst --- docs/developing/advanced/blobs.rst | 2 +- docs/developing/basic/configuration.rst | 50 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/docs/developing/advanced/blobs.rst b/docs/developing/advanced/blobs.rst index 4354b9d0f..9ab2a4a5a 100644 --- a/docs/developing/advanced/blobs.rst +++ b/docs/developing/advanced/blobs.rst @@ -1,4 +1,4 @@ -.. _blobs +.. _blobs: Working with blob data in Kotti =============================== diff --git a/docs/developing/basic/configuration.rst b/docs/developing/basic/configuration.rst index 246854b61..1e925cc2c 100644 --- a/docs/developing/basic/configuration.rst +++ b/docs/developing/basic/configuration.rst @@ -75,6 +75,8 @@ 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) +kotti.depot.*.* Configure the blob storage. More details below + pyramid.default_locale_name Set the user interface language, default ``en`` ============================ ================================================== @@ -307,6 +309,54 @@ The default configuration here is: kotti.url_normalizer.map_non_ascii_characters = True +Blob storage configuration +-------------------------- + +By default, Kotti will store blob data (files uploaded in File and Image +instances) in the database. Internally, Kotti integrates with :app:`filedepot`, +so it is possible to use any :app:``filedepot`` compatible storage, including those +provided by :app:``filedepot`` itself: + +- :class:``depot.io.local.LocalFileStorage`` +- :class:``depot.io.awss3.S3Storage`` +- :class:``depot.io.gridfs.GridFSStorage`` + +The default storage for :app:``Kotti`` is +:class:``~kotti.filedepot.DBFileStorage``. The benefit of storing files in +``DBFileStorage`` is having *all* content in a single place (the DB) which +makes backups, exporting and importing of your site's data easy, as long as you +don't have too many or too large files. The downsides of this approach appear +when your database server resides on a different host (network performance +becomes a greater issue) or your DB dumps become too large to be handled +efficiently. + +To configure a depot, several ``kotti.depot.*.*`` lines need to be added. The +number in the first position is used to group backend configuration and to +order the file storages in the configuration of :app:``filedepot``. The depot +configured with number 0 will be the default depot, where all new blob data +will be saved. There are 2 options that are required for every storage +configuration: ``name`` and ``backend``. The ``name`` is a unique string that +will be used to identify the path of saved files (it is recorded with each blob +info), so once configured for a particular storage, it should never change. The +``backend`` should point to a dotted path for the storage class. Then, any +number of keyword arguments can be added, and they will be passed to the +backend class on initialization. + +Example of a possible configurationi that stores blob data on the disk, in +``/var/local/files`` using the :app:``filedepot`` +:class:``depot.io.local.LocalFileStorage`` provided backend. Kotti's default +backend, ``DBFileStorage`` has been moved to position **1** and all data stored +there will continue to be available. See :ref:`blobs` to see how to migrate +blob data between storages. + +.. code-block:: ini + + kotti.depot.0.name = localfs + kotti.depot.0.backend = depot.io.local.LocalFileStorage + kotti.depot.0.storage_path = /var/local/files + kotti.depot.1.name = dbfiles + kotti.depot.1.backend = kotti.filedepot.DBFileStorage + Local navigation ---------------- From 2d918858de6fb3eaf3c7edc9e3576edf54b69582 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Wed, 24 Dec 2014 04:59:56 -0800 Subject: [PATCH 128/600] Fix stamp ids for filedepot migration --- kotti/alembic/versions/413fa5fcc581_add_filedepot.py | 2 +- kotti/tests/__init__.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py index c29865a78..b5dce2843 100644 --- a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py +++ b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py @@ -8,7 +8,7 @@ # revision identifiers, used by Alembic. revision = '413fa5fcc581' -down_revision = '1063d7178fa' +down_revision = '559ce6eb0949' import logging import sqlalchemy as sa diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index c1b0d84b2..713cce3c5 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -349,7 +349,6 @@ def no_filedepots(db_session, request): DepotManager._depots = {} DepotManager._default_depot = None - def restore(): db_session.rollback() DepotManager._depots = _old_depots From 0a61a59ba127d973a6d940b548e5ee9c1b9aa68d Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 26 Jan 2015 14:52:43 -0800 Subject: [PATCH 129/600] Fix rst formatting in documentation --- docs/developing/advanced/blobs.rst | 36 +++++++++++++++--------------- kotti/filedepot.py | 10 +++++---- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/docs/developing/advanced/blobs.rst b/docs/developing/advanced/blobs.rst index 9ab2a4a5a..11c42930a 100644 --- a/docs/developing/advanced/blobs.rst +++ b/docs/developing/advanced/blobs.rst @@ -4,14 +4,14 @@ Working with blob data in Kotti =============================== Kotti provides a flexible mechanism of storing blob data by with the help of -:app:`filedepot`_ storages. Both ``File`` and ``Image`` store their data in +`filedepot`_ storages. Both ``File`` and ``Image`` store their data in :class:`depot.fields.sqlalchemy.UploadedFileField` and they will offload their blob data to the configured depot storage. Working together with -:app:`filedepot` configured storages means it is possible to store blob data in +`filedepot`_ configured storages means it is possible to store blob data in a variety of ways: filesystem, GridFS, Amazon storage, etc. -By default :app:`Kotti` will store its blob data in the configured SQL -database, using :class:``~kotti.filedepot.DBFileStorage`` storage, but you can +By default Kotti will store its blob data in the configured SQL +database, using :class:`kotti.filedepot.DBFileStorage` storage, but you can configure your own preferred way of storing your blob data. The benefit of storing files in ``DBFileStorage`` is having *all* content in a single place (the DB) which makes backups, exporting and importing of your site's data easy, @@ -23,21 +23,21 @@ handled efficiently. Configuring a depot store ------------------------- -While :app:`filedepot` allows storing data in any of the configured +While `filedepot`_ allows storing data in any of the configured filestorages, at this time there's no mechanism in Kotti to select, at runtime, -the depot where new data will be saved. Instead, :app:`Kotti` will store new -files only in the configured *default* store. If, for example, you add a new +the depot where new data will be saved. Instead, Kotti will store new +files only in the configured ``default`` store. If, for example, you add a new depot and make that the default, you should leave the old depot configured so -that :app:`Kotti` will continue serving files uploaded there. +that Kotti will continue serving files uploaded there. -By default, `Kotti` comes configured with a db-based filestorage.:: +By default, Kotti comes configured with a db-based filestorage.:: kotti.depot.0.name = dbfiles kotti.depot.0.backend = kotti.filedepot.DBFileStorage The depot configured at position 0 is the default file depot. The minimum information required to configure a depot are the ``name`` and ``backend``. The -``name`` can be any string and it is used by :app:`filedepot` to identify the +``name`` can be any string and it is used by `filedepot`_ to identify the depot store for a particular saved file. The ``name`` should never be changed, as it will make the saved files unaccessible. @@ -68,7 +68,7 @@ Adding a blob data attribute to your models can be as simple as:: 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.:: +:class:`cgi.FieldStorage` instance as value:: from StringIO import StringIO from kotti.util import _to_fieldstorage @@ -84,9 +84,9 @@ While you can directly assign a ``bytes`` value to the ``avatar`` column, the 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 the -:class:`~kotti.views.edit.content.FileAddForm` and -:class:`~kotti.views.edit.content.FileEditForm` for a full example +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 @@ -108,12 +108,12 @@ Downloading blob data --------------------- Serving blob data is facilitated by the -:class:``~kotti.views.file.UploadedFileResponse``. You should return an +:class:`kotti.views.file.UploadedFileResponse`. You should return an instance of this class as the response of your view, and it will stream the blob from the storage to the client browser. As parameters it takes the blob column and the type of disposition: ``inline`` or ``attachment`` (to trigger a download in the browser). This, for example is the ``inline-view`` view for a -:class:``~kotti.resources.File``:: +:class:`kotti.resources.File`:: @view_config(name='inline-view', context=File, permission='view') def inline_view(context, request): @@ -126,7 +126,7 @@ location. Testing UploadedFileField columns --------------------------------- -Because :class:``depot.manager.DepotManager`` acts as a singleton, special care +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. @@ -171,7 +171,7 @@ only. You can invoke the script with:: 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`` +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*::: diff --git a/kotti/filedepot.py b/kotti/filedepot.py index e33462f3c..feb968aed 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -193,8 +193,9 @@ def create(self, content, filename=None, content_type=None): """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. + 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 @@ -232,8 +233,9 @@ def replace(self, file_or_id, content, filename=None, content_type=None): :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. + 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 From 2a5bbb0e2f54f16028faa8af6ec4f6fe4d369292 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Thu, 29 Jan 2015 13:05:10 -0800 Subject: [PATCH 130/600] Fix file broken by rebasing --- kotti/views/file.py | 60 --------------------------------------------- 1 file changed, 60 deletions(-) diff --git a/kotti/views/file.py b/kotti/views/file.py index e1be1dda7..c0b66366d 100644 --- a/kotti/views/file.py +++ b/kotti/views/file.py @@ -73,66 +73,6 @@ def __init__(self, data, request=None, disposition='attachment', self.headerlist.append(('Content-Disposition', disp)) -class as_inline(object): - """ ``UploadedFile`` adapter for an inline content-disposition Response - - Writing a view to inline view a file (such as an image) can be as easy as:: - - @view_config(name='image', context=Image, permission='View') - def view_image(context, request): - return as_inline(context.imagefield) - """ - - def __init__(self, data, request): - """ - :param data: :A file field obtained by reading an - :class:`~depot.fields.sqlalchemy.UploadedFileField` - :type data: :class:`depot.fields.upload.UploadedField`, - - :param request: current request - :type request: :class:`pyramid.request.Request` - """ - self.data = data - self.request = request - - -class as_download(object): - """ ``UploadedFile`` adapter for an attachment content-disposition Response - - Writing a view to download a file can be as easy as:: - - @view_config(name='image', context=Image, permission='View') - def download(context, request): - return as_download(context.filefield) - """ - - def __init__(self, data, request): - """ - :param data: :A file field obtained by reading an - :class:`~depot.fields.sqlalchemy.UploadedFileField` - :type data: :class:`depot.fields.upload.UploadedField`, - - :param request: current request - :type request: :class:`pyramid.request.Request` - """ - self.data = data - self.request = request - - -@response_adapter(as_download) -def field_to_download_response(adapter): - return UploadedFileResponse(adapter.data, - request=adapter.request, - disposition='attachment') - - -@response_adapter(as_inline) -def field_to_inline_response(adapter): - return UploadedFileResponse(adapter.data, - request=adapter.request, - disposition='inline') - - @view_config(name='view', context=File, permission='view', renderer='kotti:templates/view/file.pt') def view(context, request): From 7c0a2e174cfeea56f67cd960d8c5424cfefc626d Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sat, 31 Jan 2015 10:10:50 -0800 Subject: [PATCH 131/600] Set Kotti=1.1-dev in requirements.txt. Projects that want to try kotti master need to be able to load requirements.txt and have the same Kotti version as master --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 40bdee355..532874c2e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Kotti==1.0.0-alpha.4 +Kotti==1.1-dev filedepot==0.0.2 Babel==1.3 Beaker==1.6.4 From d1ea59a63ebca137320b34d4cacce8873584272c Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Sun, 1 Feb 2015 00:16:00 -0800 Subject: [PATCH 132/600] Include patch for depot to allow compatibility with sqlite --- kotti/filedepot.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/kotti/filedepot.py b/kotti/filedepot.py index feb968aed..1f96d10bb 100644 --- a/kotti/filedepot.py +++ b/kotti/filedepot.py @@ -365,6 +365,20 @@ def adjust_for_engine(conn, branch): 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 corectly by + # :class:`depot.fields.sqlalchemy.upload.UploadedFile` + + def patched_processed_result_value(self, value, dialect): + if not value: + return None + return self._upload_type.decode(unicode(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 includeme(config): """ Pyramid includeme hook. From ec94243ecd62e8044a74b3105eee9be308a4c469 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Sun, 1 Feb 2015 13:25:56 +0100 Subject: [PATCH 133/600] Revert "Set Kotti=1.1-dev in requirements.txt." This reverts commit 7c0a2e174cfeea56f67cd960d8c5424cfefc626d. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 532874c2e..40bdee355 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Kotti==1.1-dev +Kotti==1.0.0-alpha.4 filedepot==0.0.2 Babel==1.3 Beaker==1.6.4 From 3f3825c1ab91624993c8c84572f28c6968cba3fb Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Sun, 1 Feb 2015 13:41:30 +0100 Subject: [PATCH 134/600] Fix Travis build errors caused by havin Kotti itself in requirements.txt. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8611c7af9..99dbc1b83 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,8 @@ env: install: - travis_retry pip install "pip==1.3.1" # fix issue with fanstatic==1.0a - travis_retry pip install -e . -r requirements.txt --use-mirrors + - pip -y uninstall Kotti + - python setup.py develop - python setup.py dev - travis_retry pip install psycopg2 oursql python-coveralls before_script: From bf8213f6f85936da8ad4f4c0dab6a7cf01e00b14 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Sun, 1 Feb 2015 13:47:45 +0100 Subject: [PATCH 135/600] Fix wrong option position. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 99dbc1b83..e007eb4e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ env: install: - travis_retry pip install "pip==1.3.1" # fix issue with fanstatic==1.0a - travis_retry pip install -e . -r requirements.txt --use-mirrors - - pip -y uninstall Kotti + - pip uninstall -y Kotti - python setup.py develop - python setup.py dev - travis_retry pip install psycopg2 oursql python-coveralls From f4175498207076cbcfff42ba16a777d432a15042 Mon Sep 17 00:00:00 2001 From: Nuno Teixeira Date: Tue, 3 Feb 2015 22:10:21 +0000 Subject: [PATCH 136/600] Reindent code by following JSLint rules --- kotti/static/upload.js | 267 +++++++++++++++++++++-------------------- 1 file changed, 136 insertions(+), 131 deletions(-) diff --git a/kotti/static/upload.js b/kotti/static/upload.js index f12b01ba0..993d28ced 100644 --- a/kotti/static/upload.js +++ b/kotti/static/upload.js @@ -1,143 +1,148 @@ -/*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.num_files_waiting = 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) { + $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, + 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; + } + + 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 += 1; + }); + }); + }, + 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 -= 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) { + $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); + }); - 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."); } From c7ade7bab05e53d73f8089871afe1337071c0458 Mon Sep 17 00:00:00 2001 From: Nuno Teixeira Date: Tue, 3 Feb 2015 22:22:38 +0000 Subject: [PATCH 137/600] Use camel case in variable names --- kotti/static/kotti.js | 14 +++++++------- kotti/static/kotti.min.js | 2 +- kotti/static/upload.js | 18 +++++++++--------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/kotti/static/kotti.js b/kotti/static/kotti.js index 8a9290a00..f87f77c18 100644 --- a/kotti/static/kotti.js +++ b/kotti/static/kotti.js @@ -4,7 +4,7 @@ "use strict"; var kotti = { - dom_changed_handlers: [] + domChangedHandlers: [] }; var jq = jQuery; @@ -15,7 +15,7 @@ var jq = jQuery; return this.filter(selector).add(this.find(selector)); }; - kotti.dirty_forms = function (node) { + kotti.dirtyForms = function (node) { var forms = $("form").not("[class~=dirty-ignore]"), initial = forms.serialize(); @@ -35,8 +35,8 @@ var jq = jQuery; }); }; - kotti.dom_changed = function (node) { - $.each(kotti.dom_changed_handlers, function (index, func) { + kotti.domChanged = function (node) { + $.each(kotti.domChangedHandlers, function (index, func) { func(node); }); }; @@ -48,14 +48,14 @@ var jq = jQuery; ]; } $.each(handlers, function (index, func) { - kotti.dom_changed_handlers.push(func); + kotti.domChangedHandlers.push(func); }); - kotti.dom_changed(node); + kotti.domChanged(node); }; // deform might be undefined, e.g. in kotti_tinymce's kottibrowser if (window.deform) { - deform.load(); + deform.load(); } kotti.main(); diff --git a/kotti/static/kotti.min.js b/kotti/static/kotti.min.js index 1cbfa0e1f..c9ea8b3b1 100644 --- a/kotti/static/kotti.min.js +++ b/kotti/static/kotti.min.js @@ -1 +1 @@ -"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 +"use strict";var kotti={domChangedHandlers:[]};var jq=jQuery;(function(a){a.fn.find2=function(b){return this.filter(b).add(this.find(b))};kotti.dirtyForms=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.domChanged=function(b){a.each(kotti.domChangedHandlers,function(c,d){d(b)})};kotti.main=function(b){var c=a("html");if(!b){b=[]}a.each(b,function(d,e){kotti.domChangedHandlers.push(e)});kotti.domChanged(c)};if(window.deform){deform.load()}kotti.main()}(jQuery)); \ No newline at end of file diff --git a/kotti/static/upload.js b/kotti/static/upload.js index 993d28ced..0d4385485 100644 --- a/kotti/static/upload.js +++ b/kotti/static/upload.js @@ -31,7 +31,7 @@ function UploadController($scope, $http, $log) { $scope.files = []; $scope.errors = []; - $scope.num_files_waiting = 0; + $scope.numFilesWaiting = 0; $scope.uploadAll = function () { $scope.uploader.uploadStoredFiles(); @@ -69,10 +69,10 @@ function UploadController($scope, $http, $log) { $scope.apply(function () { var file = $scope.uploader.getFile(id); $http.get( - $scope.endpoints.content_types, + $scope.endpoints.contentTypes, {params: {mimetype: file.type}} ).success(function (data, status, headers, config) { - var content_types = data.content_types, + var contentTypes = data.contentTypes, file = { id: id, name: name, @@ -80,7 +80,7 @@ function UploadController($scope, $http, $log) { file: $scope.uploader.getFile(id) }; - if (content_types.length === 0) { + 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.'; @@ -90,11 +90,11 @@ function UploadController($scope, $http, $log) { file.status = 'ready for upload'; file.transfered = {bytes: 0, percent: 0}; - file.allowed_types = content_types; - file.desired_type = content_types[0]; + file.allowedTypes = contentTypes; + file.desiredType = contentTypes[0]; $scope.files.splice(id, 0, file); - $scope.num_files_waiting += 1; + $scope.numFilesWaiting += 1; }); }); }, @@ -103,9 +103,9 @@ function UploadController($scope, $http, $log) { $scope.apply(function () { $scope.files[id].status = 'uploading'; $scope.uploader.setParams({ - content_type: $scope.files[id].desired_type.name + contentType: $scope.files[id].desiredType.name }, id); - $scope.num_files_waiting -= 1; + $scope.numFilesWaiting -= 1; }); }, onProgress: function (id, name, uploadedBytes, totalBytes) { From 82c4e8176f06fb8a4d997ed9c945c4b24d377023 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Wed, 4 Feb 2015 15:26:12 +0200 Subject: [PATCH 138/600] For depot migration, don't try to remove sqlalchemy events, use a low level column update instead --- kotti/alembic/versions/413fa5fcc581_add_filedepot.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py index b5dce2843..261439406 100644 --- a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py +++ b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py @@ -18,8 +18,6 @@ def upgrade(): - sa.orm.events.MapperEvents._clear() # avoids filedepot magic - from depot.manager import DepotManager from depot.fields.upload import UploadedFile from depot.fields.sqlalchemy import UploadedFileField @@ -31,13 +29,19 @@ def upgrade(): t.c.data.type = sa.LargeBinary() dn = DepotManager.get_default() + update = t.update() + conn = DBSession.connection() + for obj in DBSession.query(File): uploaded_file = UploadedFile({'depot_name': dn, 'files': []}) uploaded_file._thaw() uploaded_file.process_content( obj.data, filename=obj.filename, content_type=obj.mimetype) stored_file = DepotManager.get().get(uploaded_file['file_id']) - obj.data = uploaded_file.encode() + stmt = update.where( + t.c.id == obj.id).values(data=uploaded_file.encode()) + res = conn.execute(stmt) + assert res.rowcount == 1 stored_file.last_modified = obj.modification_date log.info("Migrated {} bytes for File with pk {} to {}/{}".format( From c1fae973584701eb4b0caf150a0e5549798e970a Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Fri, 6 Feb 2015 13:42:47 +0200 Subject: [PATCH 139/600] Optimized fix path migration --- .../559ce6eb0949_update_node_path_column.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py b/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py index e39b7134c..1cc3039ea 100644 --- a/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py +++ b/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py @@ -13,13 +13,21 @@ def upgrade(): - from kotti import DBSession - from kotti.resources import Node + from kotti.resources import metadata, DBSession + from sqlalchemy import Table, select, bindparam + from sqlalchemy.sql.expression import not_ - for node in DBSession.query(Node).with_polymorphic([Node]): - # append '/' to all nodes but root - if node.path != u'/': - node.path += u'/' + nodes = Table('nodes', metadata) + + to_change = [dict(nodepath=r[0], nodeid=r[1]) for r in + select([nodes.c.path + '/', nodes.c.id]). + where(not_(nodes.c.path == u'/')).execute()] + + updater = nodes.update().\ + where(nodes.c.id == bindparam('nodeid')).\ + values({nodes.c.path:bindparam('nodepath')}) + + DBSession.execute(updater, to_change) def downgrade(): From cefbf8dd7d79575407a00857d5b6916cf016b502 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Fri, 6 Feb 2015 15:22:16 +0200 Subject: [PATCH 140/600] Use low-level SQL to update path --- .../559ce6eb0949_update_node_path_column.py | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py b/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py index 1cc3039ea..2ad05a522 100644 --- a/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py +++ b/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py @@ -13,21 +13,10 @@ def upgrade(): - from kotti.resources import metadata, DBSession - from sqlalchemy import Table, select, bindparam - from sqlalchemy.sql.expression import not_ - - nodes = Table('nodes', metadata) - - to_change = [dict(nodepath=r[0], nodeid=r[1]) for r in - select([nodes.c.path + '/', nodes.c.id]). - where(not_(nodes.c.path == u'/')).execute()] - - updater = nodes.update().\ - where(nodes.c.id == bindparam('nodeid')).\ - values({nodes.c.path:bindparam('nodepath')}) - - DBSession.execute(updater, to_change) + from kotti.resources import DBSession + DBSession.execute( + "update nodes set path = path || '/' where path not like '/'" + ) def downgrade(): From b1a84a0be424965c7e63e4190b9d3980d156a014 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Fri, 6 Feb 2015 19:07:38 +0200 Subject: [PATCH 141/600] Optimized memory usage for filedepot migration --- .../versions/413fa5fcc581_add_filedepot.py | 63 ++++++++++++------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py index 261439406..004db1147 100644 --- a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py +++ b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py @@ -12,44 +12,63 @@ import logging import sqlalchemy as sa -from alembic import op +import sys 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 depot.fields.sqlalchemy import UploadedFileField + from sqlalchemy import bindparam from kotti import DBSession, metadata - from kotti.resources import File - t = sa.Table('files', metadata) - t.c.data.type = sa.LargeBinary() + files = sa.Table('files', metadata) + files.c.data.type = sa.LargeBinary() dn = DepotManager.get_default() - update = t.update() - conn = DBSession.connection() + _saved = [] - for obj in DBSession.query(File): + def process(thing): + id, data, filename, mimetype = thing uploaded_file = UploadedFile({'depot_name': dn, 'files': []}) uploaded_file._thaw() uploaded_file.process_content( - obj.data, filename=obj.filename, content_type=obj.mimetype) - stored_file = DepotManager.get().get(uploaded_file['file_id']) - stmt = update.where( - t.c.id == obj.id).values(data=uploaded_file.encode()) - res = conn.execute(stmt) - assert res.rowcount == 1 - stored_file.last_modified = obj.modification_date - - log.info("Migrated {} bytes for File with pk {} to {}/{}".format( - len(obj.data), obj.id, dn, uploaded_file['file_id'])) - - DBSession.flush() - if DBSession.get_bind().name != 'sqlite': # not supported by sqlite - op.alter_column('files', 'data', type_=UploadedFileField()) + data, filename=filename, content_type=mimetype) + _saved.append({'nodeid': id, 'data': uploaded_file.encode()}) + log.info("Saved data for node id {}".format(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 # or whatever limit you like + window_idx = 0 + + log.info("Starting migration of blob data") + + 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") + + update = files.update().where(files.c.id == bindparam('nodeid')).\ + values({files.c.data: bindparam('data')}) + + DBSession.execute(update, _saved) + + log.info("Blob migration completed") def downgrade(): From 6008acebde99fbbc771b6c320cbf0c5c0da8075e Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 9 Feb 2015 11:08:57 +0200 Subject: [PATCH 142/600] Improve migration of file data --- .../versions/413fa5fcc581_add_filedepot.py | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py index 004db1147..4fc1e7423 100644 --- a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py +++ b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py @@ -10,9 +10,11 @@ revision = '413fa5fcc581' down_revision = '559ce6eb0949' +from alembic import op import logging import sqlalchemy as sa import sys +import time log = logging.getLogger('kotti') log.addHandler(logging.StreamHandler(sys.stdout)) @@ -22,12 +24,12 @@ def upgrade(): from depot.manager import DepotManager from depot.fields.upload import UploadedFile - from sqlalchemy import bindparam + from sqlalchemy import bindparam, Unicode from kotti import DBSession, metadata files = sa.Table('files', metadata) - files.c.data.type = sa.LargeBinary() + files.c.data.type = sa.LargeBinary() # this restores to old column type dn = DepotManager.get_default() _saved = [] @@ -45,11 +47,12 @@ def process(thing): files.c.id, files.c.data, files.c.filename, files.c.mimetype ).order_by(files.c.id).yield_per(10) - window_size = 10 # or whatever limit you like + 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() @@ -66,9 +69,17 @@ def process(thing): update = files.update().where(files.c.id == bindparam('nodeid')).\ values({files.c.data: bindparam('data')}) - DBSession.execute(update, _saved) + def chunks(l, n): + for i in xrange(0, len(l), n): + yield l[i:i+n] - log.info("Blob migration completed") + for cdata in chunks(_saved, 10): + DBSession.execute(update, cdata) + + log.info("Blob migration completed in {} seconds".format( + int(time.time() - now))) + + op.alter_column('files', 'data', Unicode()) def downgrade(): From f7aa502ef85134fce38e67593056e9608149d323 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 9 Feb 2015 18:51:47 +0200 Subject: [PATCH 143/600] Drop and recreate data column before updating with saved data --- kotti/alembic/versions/413fa5fcc581_add_filedepot.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py index 4fc1e7423..6187cdfa1 100644 --- a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py +++ b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py @@ -24,7 +24,7 @@ def upgrade(): from depot.manager import DepotManager from depot.fields.upload import UploadedFile - from sqlalchemy import bindparam, Unicode + from sqlalchemy import bindparam, Unicode, Column from kotti import DBSession, metadata @@ -66,6 +66,9 @@ def process(thing): log.info("Files written on disk, saving information to DB") + op.drop_column('files', 'data') + op.add_column('files', Column('data', Unicode(4096))) + update = files.update().where(files.c.id == bindparam('nodeid')).\ values({files.c.data: bindparam('data')}) @@ -79,8 +82,6 @@ def chunks(l, n): log.info("Blob migration completed in {} seconds".format( int(time.time() - now))) - op.alter_column('files', 'data', Unicode()) - def downgrade(): pass From 191ef0ec7f33561013a790a6d81ee97b5b3b5abc Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 10 Feb 2015 19:13:47 +0200 Subject: [PATCH 144/600] Added mock_filedepot, a mock filedepot to be used when dbsession fixture is not needed --- kotti/tests/__init__.py | 99 ++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 37 deletions(-) diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index 713cce3c5..eba795644 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -44,6 +44,7 @@ from pytest import fixture from mock import MagicMock +from datetime import datetime @fixture def allwarnings(request): @@ -280,47 +281,71 @@ def workflow(config): xmlconfig.file('workflow.zcml', kotti, execute=True) +class TestStorage: + def __init__(self): + self._storage = {} + self._storage.setdefault(0) + + def get(self, id): + info = self._storage[id] + + from StringIO import StringIO + + f = MagicMock(wraps=StringIO(info['content'])) + f.seek(0) + f.public_url = '' + f.filename = info['filename'] + f.content_type = info['content_type'] + f.content_length = len(info['content']) + # needed to make JSON serializable, Mock objects are not + f.last_modified = datetime(2012, 12, 30) + + return f + + def create(self, content, filename=None, content_type=None): + id = max(self._storage) + 1 + filename = filename or getattr(content, 'filename', None) + content_type = content_type or getattr(content, 'type', None) + if not isinstance(content, str): + content = content.file.read() + self._storage[id] = {'content': content, + 'filename': filename, + 'content_type': content_type} + return id + + def delete(self, id): + del self._storage[int(id)] + + @fixture -def filedepot(db_session, request): +def mock_filedepot(request): """ Configures a mock depot store for :class:`depot.manager.DepotManager` + + This filedepot is not integrated with dbsession. + Can be used in simple, standalone unit tests. + """ + from depot.manager import DepotManager + + _old_depots = DepotManager._depots + _old_default_depot = DepotManager._default_depot + DepotManager._depots = { + 'mockdepot': MagicMock(wraps=TestStorage()) + } + DepotManager._default_depot = 'mockdepot' + + def restore(): + DepotManager._depots = _old_depots + DepotManager._default_depot = _old_default_depot + + request.addfinalizer(restore) + + +@fixture +def filedepot(db_session, request): + """ Configures a dbsession integrated mock depot store for + :class:`depot.manager.DepotManager` """ from depot.manager import DepotManager - from datetime import datetime - - class TestStorage: - def __init__(self): - self._storage = {} - self._storage.setdefault(0) - - def get(self, id): - info = self._storage[id] - - from StringIO import StringIO - - f = MagicMock(wraps=StringIO(info['content'])) - f.seek(0) - f.public_url = '' - f.filename = info['filename'] - f.content_type = info['content_type'] - f.content_length = len(info['content']) - # needed to make JSON serializable, Mock objects are not - f.last_modified = datetime(2012, 12, 30) - - return f - - def create(self, content, filename=None, content_type=None): - id = max(self._storage) + 1 - filename = filename or getattr(content, 'filename', None) - content_type = content_type or getattr(content, 'type', None) - if not isinstance(content, str): - content = content.file.read() - self._storage[id] = {'content': content, - 'filename': filename, - 'content_type': content_type} - return id - - def delete(self, id): - del self._storage[int(id)] _old_depots = DepotManager._depots _old_default_depot = DepotManager._default_depot From 5a7dbc0afa1d0dc280622c83b9f0163519e1ec00 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 10 Feb 2015 19:14:30 +0200 Subject: [PATCH 145/600] pylint fix in test --- kotti/tests/test_workflow.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kotti/tests/test_workflow.py b/kotti/tests/test_workflow.py index e3f3d2d3f..f0de99212 100644 --- a/kotti/tests/test_workflow.py +++ b/kotti/tests/test_workflow.py @@ -59,7 +59,9 @@ def test_workflow_callback_event(self): from kotti.workflow import WorkflowTransition events = [] - my_listener = lambda event: events.append(event) + + def my_listener(event): + events.append(event) listeners[WorkflowTransition].append(my_listener) context = Dummy() From b865dbee61ab1e4751273e9a50e42a71a00ba109 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 10 Feb 2015 19:43:49 +0200 Subject: [PATCH 146/600] pep8 fixes --- kotti/alembic/versions/413fa5fcc581_add_filedepot.py | 4 ++-- kotti/tests/__init__.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py index 6187cdfa1..85a6056e6 100644 --- a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py +++ b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py @@ -43,7 +43,7 @@ def process(thing): _saved.append({'nodeid': id, 'data': uploaded_file.encode()}) log.info("Saved data for node id {}".format(id)) - query = DBSession.query( + query = DBSession.query( files.c.id, files.c.data, files.c.filename, files.c.mimetype ).order_by(files.c.id).yield_per(10) @@ -74,7 +74,7 @@ def process(thing): def chunks(l, n): for i in xrange(0, len(l), n): - yield l[i:i+n] + yield l[i:i + n] for cdata in chunks(_saved, 10): DBSession.execute(update, cdata) diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index eba795644..19f074cc8 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -9,6 +9,7 @@ digraph kotti_fixtures { "allwarnings"; + "mock_filedepot"; "app" -> "webtest"; "config" -> "db_session"; "config" -> "dummy_request"; @@ -46,6 +47,7 @@ from datetime import datetime + @fixture def allwarnings(request): save_filters = warnings.filters[:] @@ -309,8 +311,8 @@ def create(self, content, filename=None, content_type=None): if not isinstance(content, str): content = content.file.read() self._storage[id] = {'content': content, - 'filename': filename, - 'content_type': content_type} + 'filename': filename, + 'content_type': content_type} return id def delete(self, id): From 09f1e91b7af2583a310e995bc6ad9c47b6e5c645 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Wed, 11 Feb 2015 11:45:22 +0200 Subject: [PATCH 147/600] Added E402 to list of ignored PEP8 errors --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 6fd6f5532..6de0e9b9f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,7 +11,7 @@ addopts = --cov-report=term-missing kotti/ python_files = test_*.py -pep8ignore = E501 E122 E123 E125 E128 E711 E713 E714 +pep8ignore = E501 E122 E123 E125 E128 E711 E713 E714 E402 markers = user: mark test to be run as the given user slow: mark test to be run only with --runslow option From 0efef98104939dac68f0177bdfaa72304a28d3cf Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Wed, 11 Feb 2015 13:51:14 +0200 Subject: [PATCH 148/600] Another pep8 fix --- kotti/tests/test_workflow.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kotti/tests/test_workflow.py b/kotti/tests/test_workflow.py index e3f3d2d3f..3a53aafc6 100644 --- a/kotti/tests/test_workflow.py +++ b/kotti/tests/test_workflow.py @@ -59,8 +59,7 @@ def test_workflow_callback_event(self): from kotti.workflow import WorkflowTransition events = [] - my_listener = lambda event: events.append(event) - listeners[WorkflowTransition].append(my_listener) + listeners[WorkflowTransition].append(lambda event: events.append(event)) context = Dummy() info = Dummy() From 8f45d9f6c10ab8a2c6e7a8d39fc551a933f6d6fe Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Wed, 11 Feb 2015 14:12:24 +0200 Subject: [PATCH 149/600] In the update_node_path_column migration, only append slash if path doesn't end with slash --- kotti/alembic/versions/559ce6eb0949_update_node_path_column.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py b/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py index 2ad05a522..ae1cd05e9 100644 --- a/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py +++ b/kotti/alembic/versions/559ce6eb0949_update_node_path_column.py @@ -15,7 +15,7 @@ def upgrade(): from kotti.resources import DBSession DBSession.execute( - "update nodes set path = path || '/' where path not like '/'" + "update nodes set path = path || '/' where path not like '%/'" ) From 2a076c2589419d426538d2bca11ed5b52a7cff19 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 17 Feb 2015 17:54:43 +0200 Subject: [PATCH 150/600] Fix migration of filedepot: values where saved b64 encoded to database --- kotti/alembic/versions/413fa5fcc581_add_filedepot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py index 85a6056e6..00bd7e9ba 100644 --- a/kotti/alembic/versions/413fa5fcc581_add_filedepot.py +++ b/kotti/alembic/versions/413fa5fcc581_add_filedepot.py @@ -68,6 +68,7 @@ def process(thing): 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')}) From cc898601132c85c70e5384b165bbbbf5903fb032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20=C5=BDabkar?= Date: Mon, 16 Feb 2015 19:44:19 +0100 Subject: [PATCH 151/600] Some minor fixes (there were some small 'errors') and changing things around a bit so they are nicer for beginners --- docs/first_steps/tut-1.rst | 5 +--- docs/first_steps/tut-2.rst | 58 ++++++++++++++++++++++---------------- docs/first_steps/tut-3.rst | 43 ++++++++++++++++------------ 3 files changed, 59 insertions(+), 47 deletions(-) diff --git a/docs/first_steps/tut-1.rst b/docs/first_steps/tut-1.rst index 203b6c802..c141b3225 100644 --- a/docs/first_steps/tut-1.rst +++ b/docs/first_steps/tut-1.rst @@ -83,11 +83,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 +169,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 4727e97b9..f057f25b6 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 @@ -43,8 +42,7 @@ Things to note here: - ``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. + 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. @@ -106,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``: @@ -170,7 +170,6 @@ 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. @@ -199,7 +198,6 @@ 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 registry. We also removed the ``CustomContent`` content type included with the scaffolding. @@ -215,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 ------------------------------------- @@ -230,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. @@ -246,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) @@ -256,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. @@ -279,27 +277,37 @@ 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. 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. - From ddb395a05b22d879614894abc373bc6da304c6da Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Wed, 11 Feb 2015 11:45:22 +0200 Subject: [PATCH 152/600] Added E402 to list of ignored PEP8 errors --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 6fd6f5532..6de0e9b9f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,7 +11,7 @@ addopts = --cov-report=term-missing kotti/ python_files = test_*.py -pep8ignore = E501 E122 E123 E125 E128 E711 E713 E714 +pep8ignore = E501 E122 E123 E125 E128 E711 E713 E714 E402 markers = user: mark test to be run as the given user slow: mark test to be run only with --runslow option From b9f3f95fc38c0c8c1ae46f092e2800e03771cd0c Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Fri, 20 Feb 2015 17:29:33 +0100 Subject: [PATCH 153/600] Bump version. --- CHANGES.txt | 5 +++++ kotti/scaffolds/package/requirements.txt | 2 +- kotti/scaffolds/package/setup.cfg_tmpl | 2 +- requirements.txt | 2 +- setup.py | 4 +--- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b42321cd7..39f3e9403 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,11 @@ Change History ============== +1.0.0 - 2015-01-20 +------------------ + +- No changes. + 1.0.0-alpha.4 - 2015-01-29 -------------------------- diff --git a/kotti/scaffolds/package/requirements.txt b/kotti/scaffolds/package/requirements.txt index 0831752a7..4072ee29f 100644 --- a/kotti/scaffolds/package/requirements.txt +++ b/kotti/scaffolds/package/requirements.txt @@ -1,4 +1,4 @@ -Kotti==1.0.0-alpha.4 +Kotti==1.0.0 Babel==1.3 Beaker==1.6.4 Chameleon==2.20 diff --git a/kotti/scaffolds/package/setup.cfg_tmpl b/kotti/scaffolds/package/setup.cfg_tmpl index 13b1fe203..93b39155e 100644 --- a/kotti/scaffolds/package/setup.cfg_tmpl +++ b/kotti/scaffolds/package/setup.cfg_tmpl @@ -21,4 +21,4 @@ addopts = python_files = test*py markers = user: mark test to be run as the given user -pep8ignore = E501 E122 E123 E125 E128 E711 +pep8ignore = E501 E122 E123 E125 E128 E711 E402 diff --git a/requirements.txt b/requirements.txt index 0831752a7..4072ee29f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Kotti==1.0.0-alpha.4 +Kotti==1.0.0 Babel==1.3 Beaker==1.6.4 Chameleon==2.20 diff --git a/setup.py b/setup.py index 36641edc7..702c9a8ac 100644 --- a/setup.py +++ b/setup.py @@ -19,14 +19,12 @@ 'alembic', 'colander>=0.9.3', 'deform>=2.0a1', # >=2.0a1 to support Bootstrap 2 - # 'deform_bootstrap>=0.1', # checked_input widget 'docopt', 'formencode', 'html2text', 'js.angular', 'js.bootstrap>=2.1.5', 'js.deform>=2.0a2-2', - # 'js.deform_bootstrap>=0.2.4-1', 'js.fineuploader', 'js.html5shiv', 'js.jquery', @@ -86,7 +84,7 @@ install_requires.append('ordereddict') setup(name='Kotti', - version='1.0.0-alpha.4', + version='1.0.0', description="A high-level, Pythonic web application framework based on Pyramid and SQLAlchemy. It includes an extensible Content Management System called the Kotti CMS.", # noqa long_description='\n\n'.join([README, AUTHORS, CHANGES]), classifiers=[ From c65dfbd3b49ba04628c01cd580c8c72e40f900b5 Mon Sep 17 00:00:00 2001 From: Florian Cech Date: Sat, 21 Feb 2015 09:05:10 +0100 Subject: [PATCH 154/600] * kotti/Master Initialize pyramid.paster.logging for custom commands defined via kotti.util.command, to allow log message output for kotti sessions started via custom commands. The current practice would not initialize logging at all, producing a lot of "No handlers could be found for logger "myapp.foobar"" errors when the system encountered loggers. --- kotti/util.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kotti/util.py b/kotti/util.py index 9f9614381..b271995aa 100644 --- a/kotti/util.py +++ b/kotti/util.py @@ -20,6 +20,7 @@ from pyramid.interfaces import ITranslationDirectories from pyramid.location import inside from pyramid.paster import bootstrap +from pyramid.paster import setup_logging from pyramid.renderers import render from pyramid.threadlocal import get_current_registry from pyramid.threadlocal import get_current_request @@ -365,7 +366,11 @@ def camel_case_to_name(text): def command(func, doc): args = docopt(doc) - pyramid_env = bootstrap(args['']) + # establish config file uri + config_uri = args[''] + pyramid_env = bootstrap(config_uri) + # Setup logging to allow log output from command methods + setup_logging(config_uri) try: func(args) finally: From b39c3679273e66970b089e5588f21015afa8858d Mon Sep 17 00:00:00 2001 From: Martin Peeters Date: Sat, 21 Feb 2015 15:56:29 +0100 Subject: [PATCH 155/600] Update contributors list --- CONTRIBUTORS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 5a1601c05..f2e26615c 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -101,3 +101,4 @@ Contributors - Natan Žabkar, 2013/08/28 - Ionică Bizău, 2014/04/09 - Ichim Tiberiu, 2015/01/13 +- Martin Peeters, 2015/02/21 From b4b906505f87107dee560f69fc4e76de2d10f296 Mon Sep 17 00:00:00 2001 From: Martin Peeters Date: Sat, 21 Feb 2015 17:30:49 +0100 Subject: [PATCH 156/600] Update french translations --- kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo | Bin 11596 -> 17653 bytes kotti/locale/fr_FR/LC_MESSAGES/Kotti.po | 114 +++++++++++++++++++++--- 2 files changed, 103 insertions(+), 11 deletions(-) diff --git a/kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo b/kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo index fc0a2343edfec55505e6fce39d89654f7440d085..f6845c5164404e5eb9d70e55513e849bb8033d71 100644 GIT binary patch literal 17653 zcmdU#dyE}dea8p#AUKdf2#`Q1lg(ot;(OQ6B;Ks;tk-sM;ul`qX=qA1d++S-#P`nJ z%*@^OCOAM!p(&)i8=$mF%PXc(Xeg};60NkTLQs&ZQd(67N+}Xjk$AM_(W<1M@9&(s z_pW#C_>ZK5D<6Mn&YU@O&hPy@zy10}XTK}p_hH&)v_t0v!8gD+ui`&`=Uos451tzY z&jc?xD+nICI0%O6|IiDAU@iD%@MYlR;Pb$jT@nNg3ATdI1t-DhgE6S*_xk5=0k7u% z1K?HQ7ePK9`~Z9g_+yaI29JY$HaPdvAmB1s1wISB0(>U87Ca9e_4gD0elMu`j(|6V zcYx0ZKLB0>J_fD>{~6o}UhtwIxCop8*-CH(JR3|wt)l?--Fv_b!8d~1*F)fX@MGXM z@L$09fZ>aS;Ck@Wpw{<8Q1kr{D0+$5TFXicUWPHSTAi<~xf; zh`tws+UKSIegxDyH-q}lE>Q392Q}YeP~-3L_c^F}7r+v{57hYo0G|W?6x4cFu{iDL z5>Rrp8f=53pyquP)O#NXHU3k+f6?E60c7jJKZ1Jyzd)`38KIM>=Yp_0xD?cSHh>y` z1E_r*1~u=i{Cx}5`(2N(0ky9Oz$?LrLDBnjpycHrK(-Qm54;$>04B|WSAbggdqI8Y z5r2OI6rDfr`#|LJVvTJA%Tr3ZV!cRno$I^YZV@Hwl|{ih>qBSBE(`Gpt@&9em*eOjQt z^9U&W_GyoQ2Z~?c^>`kG_54auc4Z5A4cG-mhlfG!^HJ~|@Du+2&p_?-v!M3>El~Ua zPw)lcxf|X6OF-R^fa1e0@DA`WsC7IBYTaK3uLJ)V+yZXgzyC3)_aFCo9!mBC?wC0bT;$?)!6(3*de9zZMi< z9|vW(&biU;dkED1dQft@AACL72KBukg8KgB;KktiWA44s<63Yv{WpPno_M?u6u;g9 zUIv~3&jSA%6raBUimu-WMVJ2u^_^!S6w;$hz~_NigTvq`D7sI9F9y5d3&FR6YrqrW zrQlb=Iq>_S=9$^*_R$4JulqsuKLF}`4}sG2_k&vRAA_Rr=RmFNOW-eo-vITUpMi*8 zaP2ntegx|MHuxg&82B>q9iYDRSy1o&4Y&jRDJVJIdXw8n25KE|0=52!K18fehWMs%)#~GJQ#u>0rlN4g4coH z2DP5cwmW%R?Qt9weO?LbdB@+s8q~aR2PKCO`R5P&`#%CD7oYU_c~JEE8hAB$-h{I& zBj6R>-vqt{Y=WZO{UD+jd>j;C{~gr(QxKEO;6d;T@NdDVf%?6acGRwd;FrNWXb<{( z!OQ*ievhJ?=G5=$4uZm?;4Ip6XoqM=Xc6uGv^#0~wH#Os{G0w-GIJ|!lGdQzLKFSo zM3X$fmL_>wN7L`U4vgohXwalR;`_wM-}Bd!jg7RM{XOh5d3-a7iwNH0`vs!kKAPnC zcWJ*$dpm6>O}~d|@1TkQ`jxactxJ;*=oiv{gEm2XAFcYK2IgmVKTErbCb@nk?LJzK zCjG>$ntiwjlpHS5^g|8ohuWJTIt-pod#m*ZLFVxf!D-qM?Onc4AlZ4nzyAX;p~W=G z_Fc5$+WoYvXa{KeeULUqOKHbxSJ3p64ZW5o8T>VxWN)S4-E>MHt6#@oJOIi* zJ)8D6+ON~zKs%qdn^yhyaPc~tbnex(8))yLNyaav>9>J4LwgnNGTN`u>a=&$ZlmcZ z-TDwswqP%9GflrO4$L;ZhU+)dj`_Za!Fk%VXa{M(Px}DvUYdT}X}8mI-SB$_Z5Qo# zXunOnlJ-v84qETGYHW7>);-xwn51JPT&^1HY+V)dyFKfMjVKMrqOj45iek&qRGEgf zTG4GZ;-VM|XY+V^%TOzuNz$RMfXxwY4a+QC$hvu0BxP)KMQJlE$|x_x#%z=_oS|hA zHnOyg)3T_C;qGa@63#|*aXPGFSrMmU6n6OXe3m!E=`6RYY~rZdPI%gAWZkq37vgda z^9{FJVJkTrw-&;wI2?=HTkoEa^KxNqgvu@N>_ZEk=a~BoI~Rl6YR%e9k6pbn_gNuIe*zsQ7`c-6dcVW0IVX0ZS3}?0f zOpJ4{X@_}uYgU%Ya1oYC*<#geZ9ixLi(?bUGXAz3ks}GCNzAozanzqgr@9kE13_ZDx(4 zp5-$maXQk9%D5;;n(-Vg@38KXsi;UABh|)@<@MRJ-5MaM|3XDmCl*km8?~0KVr(RG z4AKj|J&ldPt}|xbc(Yl{(&F@N8xPZNdkP+8(_!RZSSpy1J%(G7_33WfkQ_&?q=a>* zj-Ioo$4?;_It!ho-dQMTv()IX`)yZk7>{g0TM7ifF?)5??%Y_$d3%d7Z8_PU6ju9u z(h0sJYjoRarg8Q3WWOy-n-G0|HY&q*hJ}$CkST3uD04aMR=O<=Z_|iY(umUn$w#}Q zg5HCOW6vIVnUX>wMzVr#O38NKc4FAuYfqL;u)H0wW_T*}V>uU=kg2yYThHmF-V1-= z4fI&j#yqylEko1ET#UJCW%+cs74AG%=25tFe8bwcVU*;ZR+Pp?INe&1Wx;gxXkuz# zyVAb_12}mj1_HsAKreU6~jI}e62|CiAO{0k8=|lIp+hjnLJ4@ zZ#Zf`IvlF#89JJ?O)uj^;nYI7tJR&kHO^b>Ws4B!C8peSa0L#J?832(R#UIYPFvJO z)>+8KAtY%!labbU_S2PM6^SZ3-Kkbm;3y<&$i@n*h*MjD2~}ObL?Sj~VteP#@fvbF zw6!P2y7j&QPA3fr3N?*RTTM2CEAQ>(N!my{QR~ifp%Whuue{fs?D24(;Gh||;xca5 z8RvVGB1|(Pnsf#Oq#$9+`BYi>eynox3-f0*R9}SEOTMSR1`cw0XSW>q%F2sXZ#$b? z_AW7>?P{EmL3UIovy`cf+fky>N)DUA>{wC|I|j>!rBRzGV2!%d6tw50A7fo!! zt(+0qtod1FAD2L=xyAxQg-s)}?oq1HFM)jHoeolG5p&Fvy(1}z*36^KN5qM&*hxWe z;oHt+)1@DykfEEmj{8^2 zSrnp`q_!w*=khm8UQn8Hr&FN*`>0SJm0bcsf}-MbONSyB;FBM`5xmLc8Dx-kHS0E@ zn9aJarqYi*Zp6u)1-u0OCbuTXQExfR7E=#*WO|`cl%Ny>S)G&ESU~KPoq9lcxqM~H zo0cvuNg-;hg|V{e+}^WfiwQa~!xoxm$~4#kR-+l$mA&-MN2OmAsGDUtJB(v!(T;dJ z>b6QtUY6Wo3$%Yt3k}#|ed%KY93GGYIbN9X+Ek|+=ckaFV(7(qhC3CzrAi7iqJ zWNnQlJdsO&>b>aA*)AWpMKV5NC(k9L#!5lBB~J-GaV#U-OTe802&YJ= zARanc4|d`+?Z4AeLxXtkX`#C;4|Ylv19`+?SJqOPstlCtd@8}Vr@ESfeI|32f^7?S zA?~)+;O15~)zkNz^9Tuz^1zu!`@aZY9%Cui@M+mZuUMA{FL(0;aX_};n2j4pP8?7!8Ry7eFbL2c^;!&&1W0qjP)qA?$J>Ntc@U?ql>6)!tt6gy4 zlYx>z3{NVdstD%|1cn3=9EOF*90w&urm`kOGM2Ii+_30uKJC5F*X&e9fMc=$!)Z8Z z3(02W()LD2r4(jDk8^h{X?NQ$nRbDf4BS2O@x9xFy`pN;7|`auxL-&&pYn_=+}mE_ z9FBFcX;|wbto0>mANvpVpWm_N`dGv~zPp>q1oF^MLo~`=gp-)oU|&4%WJeb!j=?^( z$oxS63L%hzZ$}s+>mATXn93t*XoUA{nk%qDrag)yX2=s#>%u_Ka&m*KmNB=B+R(TS;j2fai(n~_sI92HuWuBl<*oJ1ep>SsZd`cv7z@K~R0umnRvxMu6gQ=nj z4tnp?yBOrwL6cV=8Hsvk;Gj9dN9lGBYls zsBRtgwQ1u>)nBjkvZ=61?CRngsfJJ1`p-oB6yb4kf=hB_$o(-POKBHj-}#!?IJF|0 z3)Xi_Vy6L5HaQ#RY(xL|rGAnluRvlPG9LYyM3;g|3tsepmz{K{+L+@*ksTw-%a7`& z2o5P`)y9nlP|d{w$dVl7(NvmFT4ScfA9eALlt6MwaAbCNH(|OkR;hc`&U`BA5)!ToA(VRMM ziQ0zOAobP45yxHa*WOxkA>A_y1V`j@^dFNSQIeAA(8*o0)7#<}dS|ho#V%Dxo$k$Hjb{p{#u<}FtBzoRsx)*{c#&=9B!;%&nu(r*ST?fFt4>kd6X7J`dL02 z?uw=ob~!LgLIWsjo)vXgo*xJH$80ox6;gL)~+zq=3Bw7ud zB&__8RNJiXYx3WiZmSoOsfiBVd`(Cm+D&SWg!kt1h%kBaBU;<(Yesv>8Sd`Jp_(X# zI_E5=+UeqnHLiOOvrT_1Qb~weXYz!FHHQ~Z5P~C*A>!4^auD_ zSp8<(6VnlU5aR!RCWAOw>FLSaxrH&e?ZuiWTH{Ya< zumbzpaLe5m?uj^Ocg3e_58Dnx4SrKi3KHIbxuLpiC0FFws>l%Dtt2JDR@IOEVSUd?G=#&I0X#{8dok?A0|mzqSD zA`ffA?U_=epVJc41d)Ue!H{k|Vn+q1UxZRqtTS_0ZCX5`&tS7?fGoTaU! z!@+BN@x(w~he`$ul{KhpO--Uz5qltt397?~P@|jJerXH|>>0gNeLlJiySC)x)f3g9 zPVu1IS@A*9UA~oSHr7Wx-dOQH>d-AcINXae6filP+8{Dhph-kRv=`Ux$ls382~`$P z+_m_~soovCAOFuOAb3phgO_e zZ}qs;n{yZBWNDnWng6{!Jfg&j(_WU$N^L1|>W?3o*diQN=@uX54-0LuvOMLtI!=+C zDI4$_$|fMHDyfK?t=DH+>Qabvx)oFUF?tQ#DO_(;F6pcXOIPj}ZV9X}&S6z8a$LIR ziqYaYmGzwSbEhSi_F{tg5X5yWCpq3tWoFo08rfoMX(~@a8)ec)HkJF>S8xR1O}b?4 zC3^~p0Oyu=am?EPn9GX2EO08n!tly$4sw_D{j_8I6HZpz$suZAPfuCj8yw`zS|lO8 z65sBbl)tvpA4y^)Q7)QNaZ=iTsc&L7QefgaHfSM5mFd!wzF2Z_hcX68&_{@0d`LIgrBl%B71S>t{Fpy&{drGup zHctg1JMNMvRQ3VoOn z7bC~h(+HrWppEMp8ax;wjHa{IRxeNi#Q91_+%p$$83pa~xbGiiHerz;R?SlC7)_HL zl*8qaba}{5g%kD_Sp&6JG%r(E5FIznAH(vnirnFOBeuVpkP9KSepGc}RX8itD5X=F zFVgU*DEzUjRXJ^9^E=32lRrHa*(Hhkqz)gJ*cil4o{SL#yzSLCk;Y)sYk_KVy~}~> zBw>>jj5(@{zS%ptG}wUCgUWysu%6-ha{{IedlkOwNxw9f9I+JV!ab{3ow>u-0mEf6(?FnP@rL%cgy1N=iznIfNfAeIKz|d* z-;ycK8Mt;1xaY{c0rEMcw77TZi+43WK9D8N!fLbF4J*z98Gp6SnYjsHQENa8N~;)C zDMAsBN+x}GFo9{VPV*K1LG%@~I-)J5y(&ULbBr+yQ2loCQOce~WBf^o>4pnFtbWV) zt$r)RhueelZsyD>nh6m0wx|M0_)Q^8Y^|WOn`{i5lDVe7IR>zWfJMI8&YxC z53@AFJyDu8%4(H_=s)VkGs;CE=}9KQ##p@XY%UD;4pm?7Kg|cM5vF;0$~8E6NswdO zKA@mguBGsix(-KkB+mAsGfIJQPN!7o<*Ryv?B@t^@1Wzvh@n&7`&7me$AbhPG3Rs_ hS+FIagY@W`ljYwxA7({pM@~7ofw;+Pj!QTa{2!$0JkMa(EszW|F@0JlktW~;T+5Vk z!O^mL&8S0*T53(^WQ(ceSd^txGo?9Jjv{rwm0L;Wp` z#oir^@i)e8Mw9WQA{)JNGIqmUq)$_X8q0EQgQd1yVar?4m-<>9i*-mJ<_7l1dzgs* zgN#YRsn`iCFqHmHEg2?l>QMtZit6|j^3QzFAI<0rCSen%D(p*r$o_y;iv)!|VL#}lXy ze?zr%6SXo;sOPj_YR4C~q8-t#nT6SkMAQg}qB_b#HIRd9Fc%65UT!!EuTgm&Wor9ZlPunz-Tx` zrZaZOFw}qsqn=MgO(Yl9?hIQlM7_Txg!NZu6&3t5Yxxs{JCIc~CovQ+p!Vzr(zglj z+A`o+R0jiWIR&-EX|{e0vIu4(YC^M6E3p7Mm!`x`MtfI=eXtHSgKtnT{D6A!5^5lS zq6X|uAr0GO1ZJU@d_HO=OHl8vM739rZL!M!z8%%Rdlwn)`F_-cM=%*bL5=($RD*up zN&Oy(8fZsU2dTDxENW)isQ2CZG}J6 zV|z_^)Y2wk9;TxPunjfS!}uUx#o^eSWnxk$4`Xo&Y9Mc0ccBjR0p$FeqsXh~9J8O#H+xi`-J>QGX14AwORn&|-N3|T*R8%<=HITWe zTT&9m`m3WgR2;-|R5^nUYu+>dsKWx(jNGUJmD=)p)blmg?Wh&pi|XK0)ZzZh{(c_y zegmq#YtinOy}oXL2w<3cAQ)F*C~7ZjkZm{HP%}Pa%U>e9XMV@k=)?M}!!pzWYEa*c zov8N?Sx;d<%0Ig81G-asFa)(nai|V6&>N>AkD8gN`(KJ$nMzbgwWwS2KI-~?jD7Je zYRT`Q&QKtg+-TDe2ckQJj81(4s)16}464x&ciQ?o)Cc4cYJkU4OMf0UkOtJDx`}GI z6Vn@v>8JrNM!mlZby(M7obLY~GV1tS9F0Gt4qGgDN+W&})xaXuKv$q{$0pQF_n-z^ zj{$fbbp}qOI{X#Y?q8^tNTyd`9D_c(|Jh`csK`Neuo~6jJE-r#KI><;{t{{huAvY9 zhyLh2pyj=GsB$Q3OT&>zObW6KCLi@3Sc5u$W+NFLqI%TQUBIE}N8u&RL^W^(N8uIZ zyqbP|#$5OWF`FnN^gVchXib%5v@QBxJWA-AwkA#5J-@Yt`UbS7Y@3j3aH`1}wLE{a>-37)xlol@@6Km12oRVm#qZ+@I!9c%4{7 zlo7fuxr9#t1VTv%QAyv7EJ7!~HRX|sBC3g5wz9(N)>lqx7@>Q-n9!+L(xD3{bSpL! zImBzk^F%a}Nt6;g?J-0XDCvOD)&93_F1Fmw0-Jvv-yl}m zvRG&H6RjM6^DHr)7-;K)FwN#8@GatHq6@K!XkG18@-;+DuP}yqj7T7y4oN*d$3hcb zp4;6{csY$>|9X6Th5I;LBO{#8BhNVRMXmEGo>@HG86UmXf7HUl;@O48PM?@~XHLv; z&xbKN-kzYON|&cDx!&8eZAhxi^LMJt*O@ya!BaaT&gC?vUvwHW&N=5tws(4Fj&O=H Lvpr`sW4!(cAh1iq diff --git a/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po b/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po index ba62174ed..500a568af 100644 --- a/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po +++ b/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po @@ -20,11 +20,11 @@ msgstr "" #: kotti/populate.py:64 msgid "Welcome to Kotti" -msgstr "" +msgstr "Bienvenue dans Kotti" #: kotti/populate.py:65 msgid "Congratulations! You have successfully installed Kotti." -msgstr "" +msgstr "Félicitations! Vous avez installé Kotti avec succès." #: kotti/populate.py:66 #, c-format @@ -89,16 +89,79 @@ msgid "" " \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" +"
    \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 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 "" +"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 msgid "" @@ -132,6 +195,35 @@ msgid "" " 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" #: kotti/resources.py:606 kotti/views/edit/content.py:81 msgid "Document" @@ -497,7 +589,7 @@ msgstr "Gravatar" #: kotti/templates/edit/upload.pt:9 msgid "Upload content from local file(s)" -msgstr "" +msgstr "Téléverser des contenus à partir de fichiers locaux" #: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:164 #, fuzzy @@ -506,27 +598,27 @@ msgstr "Renommer" #: kotti/templates/edit/upload.pt:26 kotti/templates/edit/upload.pt:170 msgid "Size" -msgstr "" +msgstr "Poids" #: kotti/templates/edit/upload.pt:29 msgid "Status" -msgstr "" +msgstr "Statut" #: kotti/templates/edit/upload.pt:32 msgid "Progress" -msgstr "" +msgstr "Progression" #: kotti/templates/edit/upload.pt:124 msgid "Select file(s) to upload..." -msgstr "" +msgstr "Selectionner des fichiers à téléverser..." #: kotti/templates/edit/upload.pt:134 msgid "Upload ${number} files." -msgstr "" +msgstr "Téléverser ${number} fichiers." #: kotti/templates/edit/upload.pt:144 msgid "Dismiss all errors" -msgstr "" +msgstr "Ignorer toutes les erreurs" #: kotti/templates/site-setup/delete-user.pt:20 msgid "Are you sure you want to delete ${type} ${title}?" @@ -561,7 +653,7 @@ msgstr "Rechercher des utilisateurs ou des groupes" #: kotti/templates/site-setup/users.pt:63 msgid "User- / groupname" -msgstr "" +msgstr "Utilisateur / groupe" #: kotti/templates/site-setup/users.pt:70 #, fuzzy From 07233f772ac4aeb5a6be2b0858dcbbf3c9c8a0a0 Mon Sep 17 00:00:00 2001 From: Martin Peeters Date: Sun, 22 Feb 2015 11:33:56 +0100 Subject: [PATCH 157/600] Update translations files --- kotti/locale/Kotti.pot | 43 ++++++++++++------------ kotti/locale/de/LC_MESSAGES/Kotti.mo | Bin 15091 -> 15091 bytes kotti/locale/de/LC_MESSAGES/Kotti.po | 38 ++++++++++----------- kotti/locale/en/LC_MESSAGES/Kotti.mo | Bin 449 -> 449 bytes kotti/locale/en/LC_MESSAGES/Kotti.po | 38 ++++++++++----------- kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo | Bin 17653 -> 17652 bytes kotti/locale/fr_FR/LC_MESSAGES/Kotti.po | 38 ++++++++++----------- kotti/locale/it/LC_MESSAGES/Kotti.mo | Bin 12656 -> 12656 bytes kotti/locale/it/LC_MESSAGES/Kotti.po | 38 ++++++++++----------- kotti/locale/ja/LC_MESSAGES/Kotti.mo | Bin 12364 -> 12364 bytes kotti/locale/ja/LC_MESSAGES/Kotti.po | 38 ++++++++++----------- kotti/locale/ko/LC_MESSAGES/Kotti.mo | Bin 14928 -> 14928 bytes kotti/locale/ko/LC_MESSAGES/Kotti.po | 38 ++++++++++----------- kotti/locale/nl/LC_MESSAGES/Kotti.mo | Bin 11253 -> 11253 bytes kotti/locale/nl/LC_MESSAGES/Kotti.po | 38 ++++++++++----------- kotti/locale/pt/LC_MESSAGES/Kotti.mo | Bin 7711 -> 7711 bytes kotti/locale/pt/LC_MESSAGES/Kotti.po | 38 ++++++++++----------- 17 files changed, 174 insertions(+), 173 deletions(-) diff --git a/kotti/locale/Kotti.pot b/kotti/locale/Kotti.pot index e9361f4dc..587ac6352 100644 --- a/kotti/locale/Kotti.pot +++ b/kotti/locale/Kotti.pot @@ -1,19 +1,20 @@ # # SOME DESCRIPTIVE TITLE # This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , 2014. +# FIRST AUTHOR , 2015. #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE 1.0\n" -"POT-Creation-Date: 2014-12-19 14:27+0100\n" +"POT-Creation-Date: 2015-02-22 11:33+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 3.8\n" #: ./kotti/populate.py:64 msgid "Welcome to Kotti" @@ -121,58 +122,58 @@ msgid "" "

    \n" msgstr "" -#: ./kotti/resources.py:606 ./kotti/views/edit/content.py:81 +#: ./kotti/resources.py:627 ./kotti/views/edit/content.py:82 msgid "Document" msgstr "" -#: ./kotti/resources.py:645 ./kotti/views/edit/content.py:113 -#: ./kotti/views/edit/content.py:62 +#: ./kotti/resources.py:666 ./kotti/views/edit/content.py:110 +#: ./kotti/views/edit/content.py:63 msgid "File" msgstr "" -#: ./kotti/resources.py:697 ./kotti/views/edit/content.py:144 +#: ./kotti/resources.py:753 ./kotti/views/edit/content.py:138 msgid "Image" msgstr "" -#: ./kotti/resources.py:496 +#: ./kotti/resources.py:517 msgid "Folder view" msgstr "" -#: ./kotti/resources.py:483 ./kotti/templates/view/folder.pt:17 +#: ./kotti/resources.py:504 ./kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "" -#: ./kotti/resources.py:484 ./kotti/templates/edit/nav-tree.pt:10 +#: ./kotti/resources.py:505 ./kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "" -#: ./kotti/resources.py:485 +#: ./kotti/resources.py:506 msgid "Share" msgstr "" -#: ./kotti/resources.py:486 ./kotti/templates/actions-dropdown.pt:5 +#: ./kotti/resources.py:507 ./kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "" -#: ./kotti/resources.py:487 ./kotti/views/edit/actions.py:446 +#: ./kotti/resources.py:508 ./kotti/views/edit/actions.py:446 msgid "Copy" msgstr "" -#: ./kotti/resources.py:488 ./kotti/views/edit/actions.py:447 +#: ./kotti/resources.py:509 ./kotti/views/edit/actions.py:447 msgid "Cut" msgstr "" -#: ./kotti/resources.py:489 ./kotti/views/edit/actions.py:443 +#: ./kotti/resources.py:510 ./kotti/views/edit/actions.py:443 msgid "Paste" msgstr "" -#: ./kotti/resources.py:490 ./kotti/templates/edit/rename-nodes.pt:7 +#: ./kotti/resources.py:511 ./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:448 msgid "Rename" msgstr "" -#: ./kotti/resources.py:491 ./kotti/templates/edit/delete-nodes.pt:8 +#: ./kotti/resources.py:512 ./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 @@ -321,7 +322,7 @@ msgstr "" #: ./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/views/edit/content.py:32 msgid "Title" msgstr "" @@ -859,15 +860,15 @@ msgstr "" msgid "Change State" msgstr "" -#: ./kotti/views/edit/content.py:35 +#: ./kotti/views/edit/content.py:36 msgid "Description" msgstr "" -#: ./kotti/views/edit/content.py:41 +#: ./kotti/views/edit/content.py:42 msgid "Tags" msgstr "" -#: ./kotti/views/edit/content.py:50 +#: ./kotti/views/edit/content.py:51 msgid "Body" msgstr "" diff --git a/kotti/locale/de/LC_MESSAGES/Kotti.mo b/kotti/locale/de/LC_MESSAGES/Kotti.mo index 2ab463a51f33ac5ce5fee7973dccf11e5954a620..803b241d845b019ff910880b80f593efa15e6db2 100644 GIT binary patch delta 26 hcmexd`nhz2zY?#hu7Q!Rk&%L-p_Q@m<`|{v5&(j{2vz_9 delta 26 hcmexd`nhz2zY?#BuAz~xp{0VMiItK0<`|{v5&(lO2x0&L diff --git a/kotti/locale/de/LC_MESSAGES/Kotti.po b/kotti/locale/de/LC_MESSAGES/Kotti.po index a8d467ad9..242c630f6 100644 --- a/kotti/locale/de/LC_MESSAGES/Kotti.po +++ b/kotti/locale/de/LC_MESSAGES/Kotti.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Kotti 0.7dev4\n" "Report-Msgid-Bugs-To: kotti@googlegroups.com\n" -"POT-Creation-Date: 2014-12-19 14:27+0100\n" +"POT-Creation-Date: 2015-02-22 11:33+0100\n" "PO-Revision-Date: 2014-11-21 22:44+0100\n" "Last-Translator: Andreas Kaiser \n" "Language-Team: de \n" @@ -224,58 +224,58 @@ msgstr "" " article.\n" "

    \n" -#: kotti/resources.py:606 kotti/views/edit/content.py:81 +#: kotti/resources.py:627 kotti/views/edit/content.py:82 msgid "Document" msgstr "Dokument" -#: kotti/resources.py:645 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 +#: kotti/resources.py:666 kotti/views/edit/content.py:110 +#: kotti/views/edit/content.py:63 msgid "File" msgstr "Datei" -#: kotti/resources.py:697 kotti/views/edit/content.py:144 +#: kotti/resources.py:753 kotti/views/edit/content.py:138 msgid "Image" msgstr "Bild" -#: kotti/resources.py:496 +#: kotti/resources.py:517 msgid "Folder view" msgstr "Ordneransicht" -#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "Inhalt" -#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "Bearbeiten" -#: kotti/resources.py:485 +#: kotti/resources.py:506 msgid "Share" msgstr "Teilen" -#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "Aktionen" -#: kotti/resources.py:487 kotti/views/edit/actions.py:446 +#: kotti/resources.py:508 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "Kopieren" -#: kotti/resources.py:488 kotti/views/edit/actions.py:447 +#: kotti/resources.py:509 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "Ausschneiden" -#: kotti/resources.py:489 kotti/views/edit/actions.py:443 +#: kotti/resources.py:510 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "Einfügen" -#: kotti/resources.py:490 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:511 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:448 msgid "Rename" msgstr "Umbenennen" -#: kotti/resources.py:491 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:512 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:450 @@ -430,7 +430,7 @@ 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/views/users.py:196 kotti/views/edit/content.py:32 msgid "Title" msgstr "Titel" @@ -967,15 +967,15 @@ msgstr "Sie müssen Dokumente auswählen, um eine Aktion auszuführen." msgid "Change State" msgstr "Status ändern" -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:36 msgid "Description" msgstr "Beschreibung" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:42 msgid "Tags" msgstr "Schlagworte:" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:51 msgid "Body" msgstr "Inhalt" diff --git a/kotti/locale/en/LC_MESSAGES/Kotti.mo b/kotti/locale/en/LC_MESSAGES/Kotti.mo index f6db2b84dfcd0486a9d22579ec0be61d5f990ff9..0a090a05ce65dabda58eccf9cd234e38be246e8a 100644 GIT binary patch delta 24 fcmX@ee2{s7O9ev{D\n" "Language-Team: en \n" @@ -133,58 +133,58 @@ msgid "" "

    \n" msgstr "" -#: kotti/resources.py:606 kotti/views/edit/content.py:81 +#: kotti/resources.py:627 kotti/views/edit/content.py:82 msgid "Document" msgstr "" -#: kotti/resources.py:645 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 +#: kotti/resources.py:666 kotti/views/edit/content.py:110 +#: kotti/views/edit/content.py:63 msgid "File" msgstr "" -#: kotti/resources.py:697 kotti/views/edit/content.py:144 +#: kotti/resources.py:753 kotti/views/edit/content.py:138 msgid "Image" msgstr "" -#: kotti/resources.py:496 +#: kotti/resources.py:517 msgid "Folder view" msgstr "" -#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "" -#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "" -#: kotti/resources.py:485 +#: kotti/resources.py:506 msgid "Share" msgstr "" -#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "" -#: kotti/resources.py:487 kotti/views/edit/actions.py:446 +#: kotti/resources.py:508 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "" -#: kotti/resources.py:488 kotti/views/edit/actions.py:447 +#: kotti/resources.py:509 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "" -#: kotti/resources.py:489 kotti/views/edit/actions.py:443 +#: kotti/resources.py:510 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "" -#: kotti/resources.py:490 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:511 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:448 msgid "Rename" msgstr "" -#: kotti/resources.py:491 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:512 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:450 @@ -330,7 +330,7 @@ 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/views/users.py:196 kotti/views/edit/content.py:32 msgid "Title" msgstr "" @@ -853,15 +853,15 @@ msgstr "" msgid "Change State" msgstr "" -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:36 msgid "Description" msgstr "" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:42 msgid "Tags" msgstr "" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:51 msgid "Body" msgstr "" diff --git a/kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo b/kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo index f6845c5164404e5eb9d70e55513e849bb8033d71..b3cf7a02460a054e43644f929b6853ab01a18345 100644 GIT binary patch delta 197 zcmey`$@ry{al;$VdJzT&hA1Zn25umo52QtbbTyD>0n!tJG&_)<29=)&<*x$LAbp#l z^l>1q1?1lW(rQ4O-I;+w07&ZrX=Na752R&*bTW|E0n&{?+8Ri&b7qiZC<8KH0R=z? vB)Tv#0GSLuKw23{U)bEq z^a&uX1?1la(rQ4O!`dC<8KH0|h_^ xB)Kp!0GSNEKw23{U)f=2WMCCIIheASeI; diff --git a/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po b/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po index 500a568af..7ed43b372 100644 --- a/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po +++ b/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Kotti 0.9a3dev\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2014-12-19 14:27+0100\n" +"POT-Creation-Date: 2015-02-22 11:33+0100\n" "PO-Revision-Date: 2013-09-22 20:21+0100\n" "Last-Translator: Fabien Castarède \n" "Language-Team: French\n" @@ -225,58 +225,58 @@ msgstr "" " article.\n" "

    \n" -#: kotti/resources.py:606 kotti/views/edit/content.py:81 +#: kotti/resources.py:627 kotti/views/edit/content.py:82 msgid "Document" msgstr "Document" -#: kotti/resources.py:645 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 +#: kotti/resources.py:666 kotti/views/edit/content.py:110 +#: kotti/views/edit/content.py:63 msgid "File" msgstr "Fichier" -#: kotti/resources.py:697 kotti/views/edit/content.py:144 +#: kotti/resources.py:753 kotti/views/edit/content.py:138 msgid "Image" msgstr "Image" -#: kotti/resources.py:496 +#: kotti/resources.py:517 msgid "Folder view" msgstr "Contenus" -#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "Contenus" -#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "Modifier" -#: kotti/resources.py:485 +#: kotti/resources.py:506 msgid "Share" msgstr "Permissions" -#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "Actions" -#: kotti/resources.py:487 kotti/views/edit/actions.py:446 +#: kotti/resources.py:508 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "Copier" -#: kotti/resources.py:488 kotti/views/edit/actions.py:447 +#: kotti/resources.py:509 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "Couper" -#: kotti/resources.py:489 kotti/views/edit/actions.py:443 +#: kotti/resources.py:510 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "Coller" -#: kotti/resources.py:490 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:511 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:448 msgid "Rename" msgstr "Renommer" -#: kotti/resources.py:491 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:512 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:450 @@ -432,7 +432,7 @@ 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/views/users.py:196 kotti/views/edit/content.py:32 msgid "Title" msgstr "Titre" @@ -976,15 +976,15 @@ msgstr "Vous avez sélectionné des éléments afin de les traiter." msgid "Change State" msgstr "Modifier le statut" -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:36 msgid "Description" msgstr "Description" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:42 msgid "Tags" msgstr "Mots-clés" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:51 msgid "Body" msgstr "Contenu" diff --git a/kotti/locale/it/LC_MESSAGES/Kotti.mo b/kotti/locale/it/LC_MESSAGES/Kotti.mo index 878bf07184b56c0a634bb920ed04af42eaf7a9ca..bcf8597ed2f0ba47dd35edcabb64b608f51684e9 100644 GIT binary patch delta 26 hcmey6^dV`3sT{AVu7Q!Rk&%L-p_Q@mW+yp4VE}sB2Y>(o delta 26 hcmey6^dV`3sT{9~uAz~xp{0VMiItK0W+yp4VE}td2aEs! diff --git a/kotti/locale/it/LC_MESSAGES/Kotti.po b/kotti/locale/it/LC_MESSAGES/Kotti.po index 454b65b10..8d96ff5f0 100644 --- a/kotti/locale/it/LC_MESSAGES/Kotti.po +++ b/kotti/locale/it/LC_MESSAGES/Kotti.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Kotti 0.9.2dev\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2014-12-19 14:27+0100\n" +"POT-Creation-Date: 2015-02-22 11:33+0100\n" "PO-Revision-Date: 2014-01-19 11:14+0100\n" "Last-Translator: Xavi Torné \n" "Language-Team: it \n" @@ -191,58 +191,58 @@ msgid "" "

    \n" msgstr "" -#: kotti/resources.py:606 kotti/views/edit/content.py:81 +#: kotti/resources.py:627 kotti/views/edit/content.py:82 msgid "Document" msgstr "Documento" -#: kotti/resources.py:645 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 +#: kotti/resources.py:666 kotti/views/edit/content.py:110 +#: kotti/views/edit/content.py:63 msgid "File" msgstr "File" -#: kotti/resources.py:697 kotti/views/edit/content.py:144 +#: kotti/resources.py:753 kotti/views/edit/content.py:138 msgid "Image" msgstr "Immagine" -#: kotti/resources.py:496 +#: kotti/resources.py:517 msgid "Folder view" msgstr "Vista della cartella" -#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "Contenuti" -#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "Modificare" -#: kotti/resources.py:485 +#: kotti/resources.py:506 msgid "Share" msgstr "Condividi" -#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "Azioni" -#: kotti/resources.py:487 kotti/views/edit/actions.py:446 +#: kotti/resources.py:508 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "Copia" -#: kotti/resources.py:488 kotti/views/edit/actions.py:447 +#: kotti/resources.py:509 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "Taglia" -#: kotti/resources.py:489 kotti/views/edit/actions.py:443 +#: kotti/resources.py:510 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "Incolla" -#: kotti/resources.py:490 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:511 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:448 msgid "Rename" msgstr "Rinomina" -#: kotti/resources.py:491 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:512 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:450 @@ -392,7 +392,7 @@ 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/views/users.py:196 kotti/views/edit/content.py:32 msgid "Title" msgstr "Titolo" @@ -927,15 +927,15 @@ msgstr "Devi selezionare degli elementi per fare quest'azione." msgid "Change State" msgstr "Cambia stato" -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:36 msgid "Description" msgstr "Descrizione" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:42 msgid "Tags" msgstr "Tags" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:51 msgid "Body" msgstr "Contenuto" diff --git a/kotti/locale/ja/LC_MESSAGES/Kotti.mo b/kotti/locale/ja/LC_MESSAGES/Kotti.mo index a15d8fe503e1b788aeef54512bf5c1325b18c3fc..8afab5d0692126151c0a527e48ff9c0c0a3bce65 100644 GIT binary patch delta 26 hcmX?;a3*2H1u0%rT>~RsBO?VvLn~wB&3C2pMFE1m2&Di3 delta 26 hcmX?;a3*2H1u0$=T|*;XLrVoi6DuS0&3C2pMFE2?2(bVF diff --git a/kotti/locale/ja/LC_MESSAGES/Kotti.po b/kotti/locale/ja/LC_MESSAGES/Kotti.po index 4d4d74ccf..6ef1c8f9d 100644 --- a/kotti/locale/ja/LC_MESSAGES/Kotti.po +++ b/kotti/locale/ja/LC_MESSAGES/Kotti.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Kotti 0.7.0\n" "Report-Msgid-Bugs-To: kotti@googlegroups.com\n" -"POT-Creation-Date: 2014-12-19 14:27+0100\n" +"POT-Creation-Date: 2015-02-22 11:33+0100\n" "PO-Revision-Date: 2013-09-19 02:45+0900\n" "Last-Translator: OCHIAI, Gouji \n" "Language-Team: ja \n" @@ -134,58 +134,58 @@ msgid "" "

    \n" msgstr "" -#: kotti/resources.py:606 kotti/views/edit/content.py:81 +#: kotti/resources.py:627 kotti/views/edit/content.py:82 msgid "Document" msgstr "ドキュメント" -#: kotti/resources.py:645 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 +#: kotti/resources.py:666 kotti/views/edit/content.py:110 +#: kotti/views/edit/content.py:63 msgid "File" msgstr "ファイル" -#: kotti/resources.py:697 kotti/views/edit/content.py:144 +#: kotti/resources.py:753 kotti/views/edit/content.py:138 msgid "Image" msgstr "画像" -#: kotti/resources.py:496 +#: kotti/resources.py:517 msgid "Folder view" msgstr "フォルダー表示" -#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "コンテンツ" -#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "編集" -#: kotti/resources.py:485 +#: kotti/resources.py:506 msgid "Share" msgstr "共有" -#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "アクション" -#: kotti/resources.py:487 kotti/views/edit/actions.py:446 +#: kotti/resources.py:508 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "コピー" -#: kotti/resources.py:488 kotti/views/edit/actions.py:447 +#: kotti/resources.py:509 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "切り取り" -#: kotti/resources.py:489 kotti/views/edit/actions.py:443 +#: kotti/resources.py:510 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "貼り付け" -#: kotti/resources.py:490 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:511 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:448 msgid "Rename" msgstr "名称変更" -#: kotti/resources.py:491 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:512 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:450 @@ -343,7 +343,7 @@ 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/views/users.py:196 kotti/views/edit/content.py:32 msgid "Title" msgstr "タイトル" @@ -883,15 +883,15 @@ msgstr "アクションを適用するアイテムを選択してください。 msgid "Change State" msgstr "状態を変更" -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:36 msgid "Description" msgstr "説明" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:42 msgid "Tags" msgstr "タグ" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:51 msgid "Body" msgstr "本文" diff --git a/kotti/locale/ko/LC_MESSAGES/Kotti.mo b/kotti/locale/ko/LC_MESSAGES/Kotti.mo index 309ce7b2bc43db585e3b395fecd025fcaab72207..b401efe29124298404c7e020c704630d06da7d7d 100644 GIT binary patch delta 26 hcmcama-n2Hnj){Mu7Q!Rk&%L-p_Q@m=3>RW5&(W`2!H?p delta 26 hcmcama-n2Hnj)`>uAz~xp{0VMiItK0=3>RW5&(YN2#f## diff --git a/kotti/locale/ko/LC_MESSAGES/Kotti.po b/kotti/locale/ko/LC_MESSAGES/Kotti.po index bb4c27e7e..2f0bdb434 100644 --- a/kotti/locale/ko/LC_MESSAGES/Kotti.po +++ b/kotti/locale/ko/LC_MESSAGES/Kotti.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Kotti 0.10b2dev\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2014-12-19 14:27+0100\n" +"POT-Creation-Date: 2015-02-22 11:33+0100\n" "PO-Revision-Date: 2014-10-19 08:22+0900\n" "Last-Translator: mete0r \n" "Language-Team: Korean\n" @@ -218,58 +218,58 @@ msgstr "" " article.\n" "

    \n" -#: kotti/resources.py:606 kotti/views/edit/content.py:81 +#: kotti/resources.py:627 kotti/views/edit/content.py:82 msgid "Document" msgstr "문서" -#: kotti/resources.py:645 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 +#: kotti/resources.py:666 kotti/views/edit/content.py:110 +#: kotti/views/edit/content.py:63 msgid "File" msgstr "파일" -#: kotti/resources.py:697 kotti/views/edit/content.py:144 +#: kotti/resources.py:753 kotti/views/edit/content.py:138 msgid "Image" msgstr "화상" -#: kotti/resources.py:496 +#: kotti/resources.py:517 msgid "Folder view" msgstr "폴더로 보임" -#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "컨텐츠 항목" -#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "편집" -#: kotti/resources.py:485 +#: kotti/resources.py:506 msgid "Share" msgstr "공유" -#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "동작" -#: kotti/resources.py:487 kotti/views/edit/actions.py:446 +#: kotti/resources.py:508 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "복사" -#: kotti/resources.py:488 kotti/views/edit/actions.py:447 +#: kotti/resources.py:509 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "잘라내기" -#: kotti/resources.py:489 kotti/views/edit/actions.py:443 +#: kotti/resources.py:510 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "붙여넣기" -#: kotti/resources.py:490 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:511 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:448 msgid "Rename" msgstr "이름 바꾸기" -#: kotti/resources.py:491 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:512 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:450 @@ -419,7 +419,7 @@ 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/views/users.py:196 kotti/views/edit/content.py:32 msgid "Title" msgstr "제목" @@ -951,15 +951,15 @@ msgstr "적용할 항목을 선택해야합니다." msgid "Change State" msgstr "작업 상태 바꾸기" -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:36 msgid "Description" msgstr "설명" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:42 msgid "Tags" msgstr "태그" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:51 msgid "Body" msgstr "본문" diff --git a/kotti/locale/nl/LC_MESSAGES/Kotti.mo b/kotti/locale/nl/LC_MESSAGES/Kotti.mo index 97bf2fdcb4a11740a2bbb0218fbe310694f75dd2..3cd4fc509f15f9af655bc12f9c0cb8810f43bf5f 100644 GIT binary patch delta 26 hcmeww{xy8V1u0%rT>~RsBO?VvLn~wB&3C0F1ObR#2#Npz delta 26 hcmeww{xy8V1u0$=T|*;XLrVoi6DuS0&3C0F1ObT62$lc< diff --git a/kotti/locale/nl/LC_MESSAGES/Kotti.po b/kotti/locale/nl/LC_MESSAGES/Kotti.po index 4aca31860..62d3f1596 100644 --- a/kotti/locale/nl/LC_MESSAGES/Kotti.po +++ b/kotti/locale/nl/LC_MESSAGES/Kotti.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Kotti 0.4.4\n" "Report-Msgid-Bugs-To: kotti@googlegroups.com\n" -"POT-Creation-Date: 2014-12-19 14:27+0100\n" +"POT-Creation-Date: 2015-02-22 11:33+0100\n" "PO-Revision-Date: 2014-02-13 21:51+0100\n" "Last-Translator: Wim Boucquaert \n" "Language-Team: nl \n" @@ -133,58 +133,58 @@ msgid "" "

    \n" msgstr "" -#: kotti/resources.py:606 kotti/views/edit/content.py:81 +#: kotti/resources.py:627 kotti/views/edit/content.py:82 msgid "Document" msgstr "Document" -#: kotti/resources.py:645 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 +#: kotti/resources.py:666 kotti/views/edit/content.py:110 +#: kotti/views/edit/content.py:63 msgid "File" msgstr "Bestand" -#: kotti/resources.py:697 kotti/views/edit/content.py:144 +#: kotti/resources.py:753 kotti/views/edit/content.py:138 msgid "Image" msgstr "Afbeelding" -#: kotti/resources.py:496 +#: kotti/resources.py:517 msgid "Folder view" msgstr "Map weergave" -#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "Inhoud" -#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "Bewerken" -#: kotti/resources.py:485 +#: kotti/resources.py:506 msgid "Share" msgstr "Delen" -#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "Acties" -#: kotti/resources.py:487 kotti/views/edit/actions.py:446 +#: kotti/resources.py:508 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "Kopiëren" -#: kotti/resources.py:488 kotti/views/edit/actions.py:447 +#: kotti/resources.py:509 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "Knippen" -#: kotti/resources.py:489 kotti/views/edit/actions.py:443 +#: kotti/resources.py:510 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "Plakken" -#: kotti/resources.py:490 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:511 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:448 msgid "Rename" msgstr "Hernoemen" -#: kotti/resources.py:491 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:512 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:450 @@ -339,7 +339,7 @@ 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/views/users.py:196 kotti/views/edit/content.py:32 msgid "Title" msgstr "Titel" @@ -884,15 +884,15 @@ msgstr "Je dient items te selecteren om acties uit te voeren." msgid "Change State" msgstr "Wijzig Status." -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:36 msgid "Description" msgstr "Beschrijving" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:42 msgid "Tags" msgstr "Tags" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:51 msgid "Body" msgstr "Inhoud" diff --git a/kotti/locale/pt/LC_MESSAGES/Kotti.mo b/kotti/locale/pt/LC_MESSAGES/Kotti.mo index c3bc8089aecefd92cbea2014517a0c3e07be6687..c3a7a5dcafa7882e06b053c1ffbc8183817b53e0 100644 GIT binary patch delta 26 hcmbPlGv8*z0U=&fT>~RsBO?VvLn~wB&1Z#zxB+mc2dn@9 delta 26 hcmbPlGv8*z0U=%!T|*;XLrVoi6DuS0&1Z#zxB+n&2e<$L diff --git a/kotti/locale/pt/LC_MESSAGES/Kotti.po b/kotti/locale/pt/LC_MESSAGES/Kotti.po index ef403c067..3ccc8e218 100644 --- a/kotti/locale/pt/LC_MESSAGES/Kotti.po +++ b/kotti/locale/pt/LC_MESSAGES/Kotti.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Kotti 0.8\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2014-12-19 14:27+0100\n" +"POT-Creation-Date: 2015-02-22 11:33+0100\n" "PO-Revision-Date: 2012-01-16 12:02+0000\n" "Last-Translator: Nuno Teixeira \n" "Language-Team: pt \n" @@ -133,58 +133,58 @@ msgid "" "

    \n" msgstr "" -#: kotti/resources.py:606 kotti/views/edit/content.py:81 +#: kotti/resources.py:627 kotti/views/edit/content.py:82 msgid "Document" msgstr "Documento" -#: kotti/resources.py:645 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:62 +#: kotti/resources.py:666 kotti/views/edit/content.py:110 +#: kotti/views/edit/content.py:63 msgid "File" msgstr "Ficheiro" -#: kotti/resources.py:697 kotti/views/edit/content.py:144 +#: kotti/resources.py:753 kotti/views/edit/content.py:138 msgid "Image" msgstr "Imagem" -#: kotti/resources.py:496 +#: kotti/resources.py:517 msgid "Folder view" msgstr "Vista de pasta" -#: kotti/resources.py:483 kotti/templates/view/folder.pt:17 +#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 msgid "Contents" msgstr "Conteúdo" -#: kotti/resources.py:484 kotti/templates/edit/nav-tree.pt:10 +#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 msgid "Edit" msgstr "Editar" -#: kotti/resources.py:485 +#: kotti/resources.py:506 msgid "Share" msgstr "Partilha" -#: kotti/resources.py:486 kotti/templates/actions-dropdown.pt:5 +#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 msgid "Actions" msgstr "Ações" -#: kotti/resources.py:487 kotti/views/edit/actions.py:446 +#: kotti/resources.py:508 kotti/views/edit/actions.py:446 msgid "Copy" msgstr "Copiar" -#: kotti/resources.py:488 kotti/views/edit/actions.py:447 +#: kotti/resources.py:509 kotti/views/edit/actions.py:447 msgid "Cut" msgstr "Cortar" -#: kotti/resources.py:489 kotti/views/edit/actions.py:443 +#: kotti/resources.py:510 kotti/views/edit/actions.py:443 msgid "Paste" msgstr "Colar" -#: kotti/resources.py:490 kotti/templates/edit/rename-nodes.pt:7 +#: kotti/resources.py:511 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:448 msgid "Rename" msgstr "Renomear" -#: kotti/resources.py:491 kotti/templates/edit/delete-nodes.pt:8 +#: kotti/resources.py:512 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:450 @@ -341,7 +341,7 @@ 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/views/users.py:196 kotti/views/edit/content.py:32 msgid "Title" msgstr "Título" @@ -902,15 +902,15 @@ msgstr "Terá de escolher items para executar a ação." msgid "Change State" msgstr "Alterar Estado" -#: kotti/views/edit/content.py:35 +#: kotti/views/edit/content.py:36 msgid "Description" msgstr "Descrição" -#: kotti/views/edit/content.py:41 +#: kotti/views/edit/content.py:42 msgid "Tags" msgstr "Etiquetas" -#: kotti/views/edit/content.py:50 +#: kotti/views/edit/content.py:51 msgid "Body" msgstr "Corpo" From a1113350f6f0b27cc4de3ae33383b6654bb17cf9 Mon Sep 17 00:00:00 2001 From: Martin Peeters Date: Sun, 22 Feb 2015 11:35:49 +0100 Subject: [PATCH 158/600] Fix fuzzy translations and some typos --- kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo | Bin 17652 -> 18638 bytes kotti/locale/fr_FR/LC_MESSAGES/Kotti.po | 19 +++++-------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo b/kotti/locale/fr_FR/LC_MESSAGES/Kotti.mo index b3cf7a02460a054e43644f929b6853ab01a18345..b0c1df66d24b792df1d4a385ace1cf9ec79397e5 100644 GIT binary patch delta 4423 zcmZYC3vg7`9mnyLCm;}zM)aV!rt8w5L{_d z5m3+teDDxNv0y>!cItGP)(T~4r&y+Tik5L&89QyKwsx$JRmRqSe|rxz&h(7`e9pOh z?|J;sxmW$<(Slz;QIPpVpW=Ona)ua094s;BpQ?A~59OUQV;(Ct=J!}uWK3&+V@7km zV}LQ^@GyQ2FJoW4j{UH#!kC+J1oDV!K>fY|_4{fZWlYATsEp*o0c33R26n-V$hhV* zGOoFT^lh$U5BwOrp{X>cE8K)?_r;z#7t@f^%( z-)pF7C0B7M{t-3sm#7YVa#5-4hq|v4)xl8Qh}Eb)eI7N?YpAU_kIKNCw*5Y80slZf ze|;$VSI0$8Fi zXHgkChrG$=Eo2u=<{FhzR0^1uR#uIApcZvwJ!<8%P!nB+T3H%7$R>kR@c=q_1+_K* zMoqLUvrs=}Sd4@5CLA8LGp3eG2^S`y2A*tfL>-p7s0SD0L|l$q;Vvw}BdFhxqZaZq zYNBtU2L2neOXhRbeFM2knQ*YL-v4n_wCA%?dv~vGuSPwv9!s$UHS;Z~GqD}D(mkl> zk75<(P^obZEY#b#4DEi^d_Hjo?kzATg5_OU zNsTf8DrE5SY!km1@wKYM@u&=~K@GGQ^~F4Gy@=Yf_pD!FMmG#-;7f$#k-m+G%0w0! z&m6=Oe8IL)qE`AUDg$q$GVoU{$LqFz^Q7SV2-NikYcpzV!js7VN-9Y%Xl9pDEBOHV z$5b+_>9`2>h1+dCiEO92fXna$)I?`Z4qSkGy%uACY(Z_oX1o&*;9~sqWb*%GDpRKf z8TbUX^06H0Qf$C+I1RPJn7y7s9ioR(6W)*7vJ0rpe1v*S%5MwW4r)SkP_ONBti(u$ ziUyQtaJy|!nHKDQ6KdcV)CxOM6MN9MA3>#ZH~tI{qyF8onzX6k9o8(C(>{orKo0eG zWnQ;8TtapHJJdk$;sE@Uz5X9-@$JSu!1eB^GqD2);T}{*PTBTPa4_wwxD7u;4Y;K- zn7|Gs0~xcQiXQxd^$o11{Tu9u*R8!~1bbP98gMEW;WE@&Sb<9UR#e7zqE>nc^%|Z) zeK%gi(fBLuqxb(~Dg(JtIy3li49Bswr(y-RVh3);{QHbrNoiA%vA(G5m8b!$QQwJr z)ZuGHWnw96OMKJ>H{%e-H;+@%fTyqlucA8YJuB#F5bCguM(u5rHHI4aY1H$_aW?)I zb!Z3B(8A`U`g2hWT8}z2J29h_JWEA;c^Y*FerP?5O6fV&;kkrb+55;p=0pDIR8O1} zJUa~6yHK9-EkbfPr!nwg=8;IKTdejZ`Q7K%C>UbUMaJJju zJ8gR#>bYIkqo_>%2uI;H)Q76;yx^^>KyAtNd5Iu(E4aX$XSSpE`sb()Zo7kz3bx<~ zJc$LUPS+7CFA&PkDuVA&zTD5>ZxY`plxp>LEAeplT93XN<)(_zDNxZzTjgou8^n4- zd95Kf5Yvgf2_|d4O%xG2D7y)zX$Vn5C`C8QL$e@zPY&fO>v+KO_KCF+Sg z3GK;df_K)m5IVM{gvu#GsZbeB>?hJ{P`*d#d)%moEo3tPY1Avzi&(Dl57-~KTA#A+ zz@)9~2%jXHiKX_M*l6oInKOvh#9f455taD?rp+41$824+YyICMo*|-yPwXM45^D$* z-P1}eQiF1on3=B`^Q3hw>aeKn4ak2Q^-7(v?N{(PaUZdZ2ovf%|DPyN;L8NxY;&4W zd5+LayM#DGs5~B!|GS;P&R5zI>XQhMm}RdY!zo0HxP#Ei)`#LzqEq9qq_T}TL#PZ3 zFvG24DAAj!CKeDX&l87;O~hJaJW)%0m$*?TQ#ouKJFyGVM9d`?6SMVyU-wc85#J%6 zC1QliOT>zNE%?&t<-bw%(SL+kNVr6W-lhI^)b1e;5-$=e`*I~^&E3o6?gpndnMk*r znURR2R@!%y+2)G>_GpbJ)`p@^G7)wCY*S_Jz-TDG&hgz)GTi2*+|HEK;>9Dr6N*Ob za691s{7}kGGmYxzkmvjECMW7T;kHn$ z-RbZmuG6j+lQ1XL(&FWNNhQ*LO*!|WuI&uoBMJ~kHTKfAt#)O$K6hk+@3#@>o@I8 j;ov#(q^k``x|?b{G$&0Y>;`Gioxi=LAa|&7S;_wZbihU_ delta 3711 zcmZYBd2mfv0LSr@5l6zz1Bs+ut!GnT5Qol3vIcTfK6nf~%Q=f3yOcF(S1`0F;!8C;r8CUO|t zq7OBZ%NUBcPz$(^8qn3snBy3R8u&D}!853xxQSkMc%O>4>Iv$L&rlr(F$-;R1Zt(x zw%rXi(R9>6IjG+cL-jWX_5BI9U4rU&AUH(F)|%cD;kRW{wUP<#-k>dk6Or5kncnqc->9d$tMKo?X;eNgv!IBH9$*!C=R(S8#(v1O=qBCtbGIui=Hv||n z8=Eq6a02guK+an?W4iKr1TT$5#y1&Mv_%D|fp((abSJGppzhfnYhAvq&*M-pm<;TQ zrKp|Qfm-PS494TO{V{5xr%^j_6TRBPhg6zkNQ$$itx)Z5s9TYP6L1V_BGsrJxQMCv zFJ@p$Pv=Edf^3Rei<*efdI}rSK7&p0N>BD*kINk{+-BMr5%YHCjs@8qY-b_GsEMpbEuaeZ6n%u+fiF?F?h=N0 zsr;-8-au{PZPa0TfSO3XzRo~VsKYuA)j)WGL46>p&?)Uuy*rV^}u z(W`qro{GLujM}orsE#X8hjEkre7kKQLjA7V`VDG_u3|ja@i;G-M2w}Kg{^QZYUh?B z=g%DVu>ZQZe{ew^Vi9 z?6lW(D?iZvSJ44UAp_K)aLfFMat-qDg;_^xi?(nm(P@64yh7e1&l8m@Qb}|MROXUG zQcCnBsJO}7#OvYDHd0%782tb2sy&lr6P@nyq>OYWdQ5r4`ri|aP=|3LQOR;JJpTTF zl*XWothd*Tt-J75J^$^fY_u1J4$yMj{t)xYbfOdf8mTQhn5)P#(w+<_DtpNkQbgV) zu|!30)6PT(aWc^X)cF4^uX9C@V{MsjE9IycRT!xtZ;=%wlnf-bWiXYcM33GZB#mq# zI_MEZC7H}16E%KI@-j&zo5_nrMUU7%qIbd&l1@}I9Q^MH{eC4`V6W}K`J@3ENw$+c zWHC|cN5+v7H7IRI@F$0CBJYwovVrs`{^GkFJ~*_pPm8CO17q6zR>y1%@_pLodQj!( zu@%0VanoG^$-avTlfx>rQ^S1IQgZ?-zv>n5^P~-S`Br5#2(0HRE-IS7AiucCw>Rr} Ppl?n;SAcJqr%UjEAC_\n" "Language-Team: French\n" @@ -394,12 +394,11 @@ msgstr "Réinitialiser le mot de passe" #. 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 " +"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." @@ -408,9 +407,8 @@ msgid "Not registered yet?" msgstr "" #: kotti/templates/login.pt:127 -#, fuzzy msgid "Register for an account on this site." -msgstr "Pas encore inscrit ? ${register} sur ce site." +msgstr "Enregistrez-vous sur ce site." #: kotti/templates/workflow-dropdown.pt:19 msgid "Make ${state}" @@ -592,9 +590,8 @@ msgid "Upload content from local file(s)" msgstr "Téléverser des contenus à partir de fichiers locaux" #: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:164 -#, fuzzy msgid "Filename" -msgstr "Renommer" +msgstr "Nom du fichier" #: kotti/templates/edit/upload.pt:26 kotti/templates/edit/upload.pt:170 msgid "Size" @@ -634,7 +631,6 @@ msgid "Edit ${title}" msgstr "Modifier ${title}" #: kotti/templates/site-setup/users.pt:15 -#, fuzzy msgid "Search user(s) / group(s)" msgstr "Rechercher des utilisateurs ou des groupes" @@ -647,7 +643,6 @@ msgid "Add group" msgstr "Ajouter un groupe" #: kotti/templates/site-setup/users.pt:52 -#, fuzzy msgid "Find users or groups" msgstr "Rechercher des utilisateurs ou des groupes" @@ -656,24 +651,20 @@ msgid "User- / groupname" msgstr "Utilisateur / groupe" #: kotti/templates/site-setup/users.pt:70 -#, fuzzy msgid "Blank search text finds all." msgstr "" "Rechercher et modifier des utilisateurs (Laissez le champ vide pour les " "afficher tous)." #: kotti/templates/site-setup/users.pt:96 -#, fuzzy msgid "Assign global roles" msgstr "Assigner des rôles locaux" #: kotti/templates/site-setup/users.pt:180 -#, fuzzy msgid "Add new user" msgstr "Ajouter un utilisateur" #: kotti/templates/site-setup/users.pt:190 -#, fuzzy msgid "Add new group" msgstr "Ajouter un groupe" @@ -761,7 +752,7 @@ msgstr "S'inscrire - ${title}" #: kotti/views/login.py:163 msgid "Login failed." -msgstr "Connexion échouée." +msgstr "La connexion à échouée." #: kotti/views/login.py:285 #, python-format From 0b00b370d06dbf74f17be22870448311ada551ce Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Mon, 23 Feb 2015 09:57:20 +0100 Subject: [PATCH 159/600] Update CHANGES.txt --- CHANGES.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 263e845c3..595a7b433 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,7 +4,9 @@ Change History 1.1-dev - unreleased -------------------- -- No changes yet. +- Initialize pyramid.paster.logging for custom commands defined via + ``kotti.util.command``, to allow log message output for kotti sessions + started via custom commands. 1.0.0-alpha.4 - 2015-01-29 -------------------------- From 12950a9ab6d62f3eeb56718a67e913ffc95082da Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Mon, 23 Feb 2015 10:16:52 +0100 Subject: [PATCH 160/600] Add E402 to the list of ignores for the scaffold. --- kotti/scaffolds/package/setup.cfg_tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotti/scaffolds/package/setup.cfg_tmpl b/kotti/scaffolds/package/setup.cfg_tmpl index 13b1fe203..93b39155e 100644 --- a/kotti/scaffolds/package/setup.cfg_tmpl +++ b/kotti/scaffolds/package/setup.cfg_tmpl @@ -21,4 +21,4 @@ addopts = python_files = test*py markers = user: mark test to be run as the given user -pep8ignore = E501 E122 E123 E125 E128 E711 +pep8ignore = E501 E122 E123 E125 E128 E711 E402 From bd5dbc7a54840899aa7e560d8866b0841c5c45cd Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Mon, 23 Feb 2015 11:16:42 +0100 Subject: [PATCH 161/600] Remove unused ``kotti.js``. --- kotti/fanstatic.py | 9 +----- kotti/static/kotti.js | 63 --------------------------------------- kotti/static/kotti.min.js | 1 - 3 files changed, 1 insertion(+), 72 deletions(-) delete mode 100644 kotti/static/kotti.js delete mode 100644 kotti/static/kotti.min.js diff --git a/kotti/fanstatic.py b/kotti/fanstatic.py index e94c37f0c..dc51b66ee 100644 --- a/kotti/fanstatic.py +++ b/kotti/fanstatic.py @@ -24,15 +24,10 @@ # 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) base_css = Resource( @@ -124,9 +119,7 @@ def need(self): # pragma: no cover jquery, bootstrap_js, html5shiv, - kotti_js, jquery_form, - # deform_bootstrap_js, ]) edit_needed = NeededGroup([ edit_needed_css, diff --git a/kotti/static/kotti.js b/kotti/static/kotti.js deleted file mode 100644 index f87f77c18..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 = { - domChangedHandlers: [] -}; -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.dirtyForms = 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.domChanged = function (node) { - $.each(kotti.domChangedHandlers, function (index, func) { - func(node); - }); - }; - - kotti.main = function (handlers) { - var node = $('html'); - if (!handlers) { - handlers = [ - ]; - } - $.each(handlers, function (index, func) { - kotti.domChangedHandlers.push(func); - }); - kotti.domChanged(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 c9ea8b3b1..000000000 --- a/kotti/static/kotti.min.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";var kotti={domChangedHandlers:[]};var jq=jQuery;(function(a){a.fn.find2=function(b){return this.filter(b).add(this.find(b))};kotti.dirtyForms=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.domChanged=function(b){a.each(kotti.domChangedHandlers,function(c,d){d(b)})};kotti.main=function(b){var c=a("html");if(!b){b=[]}a.each(b,function(d,e){kotti.domChangedHandlers.push(e)});kotti.domChanged(c)};if(window.deform){deform.load()}kotti.main()}(jQuery)); \ No newline at end of file From 3875ed8ed80f02b709fb51eda501b0b10402d5c2 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Mon, 23 Feb 2015 11:25:38 +0100 Subject: [PATCH 162/600] Adjust tests. --- CHANGES.txt | 2 ++ kotti/tests/test_static.py | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 66463c417..b4c49842e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -8,6 +8,8 @@ Change History ``kotti.util.command``, to allow log message output for kotti sessions started via custom commands. +- Remove unused ``kotti.js``. + 1.0.0 - 2015-01-20 ------------------ diff --git a/kotti/tests/test_static.py b/kotti/tests/test_static.py index 11402c200..286a29ebd 100644 --- a/kotti/tests/test_static.py +++ b/kotti/tests/test_static.py @@ -6,7 +6,7 @@ class TestStatic: def test_NeededGroup(self): from js.deform import deform_js - from kotti.fanstatic import kotti_js + from kotti.fanstatic import contents_view_js from kotti.fanstatic import NeededGroup def NeededGroupFactory(resources): @@ -21,9 +21,9 @@ def NeededGroupFactory(resources): assert needed.resources == [deform_js, ] - needed.add(kotti_js) + needed.add(contents_view_js) - assert needed.resources == [deform_js, kotti_js] + assert needed.resources == [deform_js, contents_view_js] def needed_group_adder(resource): needed.add(resource) From b264e838b6c57a188ca27ee79cf8498a5ff38b57 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 24 Feb 2015 16:34:16 +0200 Subject: [PATCH 163/600] Added changelog entries about filedepot feature --- CHANGES.txt | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index b4c49842e..0425df5d3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,41 @@ Change History 1.1-dev - unreleased -------------------- +- 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 between blob data between depot storages. See + docs/developing/advanced/blobs.rst for details on how to use it. + +- Simplify serving blob data by using the + :class:`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. From da26fb7211313cd3bf2df66ea2d65bdb01b7bb4e Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Tue, 24 Feb 2015 16:34:50 +0200 Subject: [PATCH 164/600] Use a different depot name for the filedepot fixture --- kotti/tests/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kotti/tests/__init__.py b/kotti/tests/__init__.py index 19f074cc8..f7a1d17ad 100644 --- a/kotti/tests/__init__.py +++ b/kotti/tests/__init__.py @@ -352,9 +352,9 @@ def filedepot(db_session, request): _old_depots = DepotManager._depots _old_default_depot = DepotManager._default_depot DepotManager._depots = { - 'mockdepot': MagicMock(wraps=TestStorage()) + 'filedepot': MagicMock(wraps=TestStorage()) } - DepotManager._default_depot = 'mockdepot' + DepotManager._default_depot = 'filedepot' def restore(): db_session.rollback() From 52bd28d915ecc774bd6b29ddc162594d28aeeb4c Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Wed, 25 Feb 2015 17:53:52 +0100 Subject: [PATCH 165/600] Upgrade most requirements to their recent versions. --- kotti/scaffolds/package/requirements.txt | 17 +++++++++-------- requirements.txt | 16 ++++++++-------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/kotti/scaffolds/package/requirements.txt b/kotti/scaffolds/package/requirements.txt index 4072ee29f..f60a2410f 100644 --- a/kotti/scaffolds/package/requirements.txt +++ b/kotti/scaffolds/package/requirements.txt @@ -1,8 +1,9 @@ Kotti==1.0.0 +filedepot==0.0.3 Babel==1.3 -Beaker==1.6.4 -Chameleon==2.20 -FormEncode==1.2.6 +Beaker==1.6.5.post1 +Chameleon==2.22 +FormEncode==1.3.0 Mako==1.0.1 MarkupSafe==0.23 PasteDeploy==1.5.2 @@ -17,11 +18,11 @@ colander==1.0 deform==2.0a2 docopt==0.6.2 fanstatic==1.0a5 -html2text==2014.12.29 +html2text==2015.2.18 js.angular==1.1.4 js.bootstrap==3.3.1 js.chosen==0.9.14 -js.deform==2.0a2-2 +js.deform==2.0a2-3 js.fineuploader==3.3.0 js.html5shiv==3.6.2-1 js.jquery==1.9.1 @@ -35,19 +36,19 @@ js.jqueryui==1.10.3 js.jqueryui_tagit==2.0.24-2 js.modernizr==2.5.3.1 kotti_tinymce==0.5.1 -lingua==3.8 +lingua==3.9 peppercorn==0.5 plone.scale==1.3.4 polib==1.0.6 py-bcrypt==0.4 -pyramid==1.5.2 +pyramid==1.5.4 pyramid_beaker==0.8 pyramid_chameleon==0.3 pyramid_debugtoolbar==2.3 pyramid_deform==0.2 pyramid_mailer==0.14 pyramid_mako==1.0.2 -pyramid_tm==0.10 +pyramid_tm==0.11 pyramid_zcml==1.0.0 pytz==2014.10 repoze.lru==0.6 diff --git a/requirements.txt b/requirements.txt index 67bfa2eee..f60a2410f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,9 @@ Kotti==1.0.0 filedepot==0.0.3 Babel==1.3 -Beaker==1.6.4 -Chameleon==2.20 -FormEncode==1.2.6 +Beaker==1.6.5.post1 +Chameleon==2.22 +FormEncode==1.3.0 Mako==1.0.1 MarkupSafe==0.23 PasteDeploy==1.5.2 @@ -18,11 +18,11 @@ colander==1.0 deform==2.0a2 docopt==0.6.2 fanstatic==1.0a5 -html2text==2014.12.29 +html2text==2015.2.18 js.angular==1.1.4 js.bootstrap==3.3.1 js.chosen==0.9.14 -js.deform==2.0a2-2 +js.deform==2.0a2-3 js.fineuploader==3.3.0 js.html5shiv==3.6.2-1 js.jquery==1.9.1 @@ -36,19 +36,19 @@ js.jqueryui==1.10.3 js.jqueryui_tagit==2.0.24-2 js.modernizr==2.5.3.1 kotti_tinymce==0.5.1 -lingua==3.8 +lingua==3.9 peppercorn==0.5 plone.scale==1.3.4 polib==1.0.6 py-bcrypt==0.4 -pyramid==1.5.2 +pyramid==1.5.4 pyramid_beaker==0.8 pyramid_chameleon==0.3 pyramid_debugtoolbar==2.3 pyramid_deform==0.2 pyramid_mailer==0.14 pyramid_mako==1.0.2 -pyramid_tm==0.10 +pyramid_tm==0.11 pyramid_zcml==1.0.0 pytz==2014.10 repoze.lru==0.6 From af2032fe2b30db01c25d47a43f28e8f0b9383c37 Mon Sep 17 00:00:00 2001 From: tiberiuichim Date: Mon, 2 Mar 2015 18:04:26 +0200 Subject: [PATCH 166/600] Update CHANGES.txt remove duplicate word --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0425df5d3..18e7ca464 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -22,7 +22,7 @@ Change History ``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 between blob data between depot storages. See +- 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 the From 4d36cf4735258326fdec4d8fc05d19db21368367 Mon Sep 17 00:00:00 2001 From: Tiberiu Ichim Date: Mon, 16 Mar 2015 18:39:48 +0200 Subject: [PATCH 167/600] Separate default actions, so it can be modified --- kotti/resources.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/kotti/resources.py b/kotti/resources.py index 9271a6b04..d6f993ff7 100644 --- a/kotti/resources.py +++ b/kotti/resources.py @@ -495,6 +495,16 @@ def _not_root(context, request): return not context is get_root() +default_actions = 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'), +]) + + default_type_info = TypeInfo( name=u'Content', title=u'type_info title missing', # BBB @@ -504,14 +514,7 @@ def _not_root(context, request): 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'), - ]), + default_actions, ], selectable_default_views=[ ("folder_view", _(u"Folder view")), From ee94ba89b51be08206de7f20cd87098587b46fb5 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Thu, 19 Mar 2015 13:22:04 +0100 Subject: [PATCH 168/600] Remove deprecated ``kotti.views.slots.local_navigation`` and ``kotti.views.slots.includeme_local_navigation``. --- kotti/tests/test_deprecated.py | 23 ----------------------- kotti/views/slots.py | 25 ------------------------- 2 files changed, 48 deletions(-) diff --git a/kotti/tests/test_deprecated.py b/kotti/tests/test_deprecated.py index 7a8bd762c..25d3727cc 100644 --- a/kotti/tests/test_deprecated.py +++ b/kotti/tests/test_deprecated.py @@ -12,29 +12,6 @@ def assert_deprecations(w, *msgs): assert msgs[i] in str(w[i].message) -class TestDeprecated09: - - def test_render_tree_navigation_moved(self, allwarnings): - with warnings.catch_warnings(record=True) as w: - - from kotti.views.edit.actions import render_tree_navigation - render_tree_navigation # pyflakes - - assert_deprecations( - w, "has been moved to kotti.views.navigation as of Kotti 0.9") - - def test_local_navigation_moved(self, allwarnings): - with warnings.catch_warnings(record=True) as w: - - from kotti.views.slots import includeme_local_navigation - from kotti.views.slots import local_navigation - includeme_local_navigation # pyflakes - local_navigation # pyflakes - - assert_deprecations( - w, "deprecated as of Kotti 0.9", "deprecated as of Kotti 0.9") - - class TestDeprecated10: def test_security_has_permission(self, allwarnings): diff --git a/kotti/views/slots.py b/kotti/views/slots.py index 543f94702..a36f1242d 100644 --- a/kotti/views/slots.py +++ b/kotti/views/slots.py @@ -135,28 +135,3 @@ class RenderEditInHead(ObjectEvent): slot_events = [ RenderLeftSlot, RenderRightSlot, RenderAboveContent, RenderBelowContent, RenderInHead, RenderBeforeBodyEnd, RenderEditInHead, ] - - -# BBB starts here --- --- --- --- --- --- - -from zope.deprecation import deprecated - -# The remainder of this file will be removed in Kotti 0.11 or 1.1, whichever -# will be the version number we chose. - -from kotti.views.navigation import local_navigation -from kotti.views.navigation import includeme_local_navigation - -local_navigation = local_navigation -includeme_local_navigation = includeme_local_navigation - -deprecated( - 'local_navigation', - 'deprecated as of Kotti 0.9. Use ' - 'kotti.views.navigation.local_navigation instead.' -) -deprecated( - 'includeme_local_navigation', - 'deprecated as of Kotti 0.9. Use ' - 'kotti.views.navigation.includeme_local_navigation instead.' -) From 6c3597e9e3d2a90e4473a1479a7b31f6ab696349 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Thu, 19 Mar 2015 13:23:08 +0100 Subject: [PATCH 169/600] Upgrade ``plone.scale`` and ``SQLAlchemy`` to their latest stable versions. --- kotti/scaffolds/package/requirements.txt | 4 ++-- requirements.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/kotti/scaffolds/package/requirements.txt b/kotti/scaffolds/package/requirements.txt index f60a2410f..ba5b99417 100644 --- a/kotti/scaffolds/package/requirements.txt +++ b/kotti/scaffolds/package/requirements.txt @@ -9,7 +9,7 @@ MarkupSafe==0.23 PasteDeploy==1.5.2 Pillow==2.7.0 Pygments==2.0.2 -SQLAlchemy==0.9.8 +SQLAlchemy==0.9.9 Unidecode==0.04.17 WebOb==1.4 alembic==0.6.7 @@ -38,7 +38,7 @@ js.modernizr==2.5.3.1 kotti_tinymce==0.5.1 lingua==3.9 peppercorn==0.5 -plone.scale==1.3.4 +plone.scale==1.3.5 polib==1.0.6 py-bcrypt==0.4 pyramid==1.5.4 diff --git a/requirements.txt b/requirements.txt index f60a2410f..ba5b99417 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ MarkupSafe==0.23 PasteDeploy==1.5.2 Pillow==2.7.0 Pygments==2.0.2 -SQLAlchemy==0.9.8 +SQLAlchemy==0.9.9 Unidecode==0.04.17 WebOb==1.4 alembic==0.6.7 @@ -38,7 +38,7 @@ js.modernizr==2.5.3.1 kotti_tinymce==0.5.1 lingua==3.9 peppercorn==0.5 -plone.scale==1.3.4 +plone.scale==1.3.5 polib==1.0.6 py-bcrypt==0.4 pyramid==1.5.4 From c034e32f52064eb92d95b94456cc0799bd773f34 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Thu, 19 Mar 2015 13:23:43 +0100 Subject: [PATCH 170/600] Add ``filedepot`` to package requirements. --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index a6f976e1a..3a5af056d 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,7 @@ 'colander>=0.9.3', 'deform>=2.0a1', # >=2.0a1 to support Bootstrap 2 'docopt', + 'filedepot', 'formencode', 'html2text', 'js.angular', From b17a22a410b56d42521015ea4d909c94272dd7ea Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Thu, 19 Mar 2015 13:24:23 +0100 Subject: [PATCH 171/600] Add changenotes. --- CHANGES.txt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 18e7ca464..a273cf8ef 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -25,11 +25,10 @@ Change History - 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 the - :class:`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. +- 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 @@ -45,6 +44,13 @@ Change History - 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. + 1.0.0 - 2015-01-20 ------------------ From 847e8737cee9bad975e369ad491b87f711763a3a Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Thu, 19 Mar 2015 13:29:17 +0100 Subject: [PATCH 172/600] Bump version. --- CHANGES.txt | 4 ++-- kotti/scaffolds/package/requirements.txt | 2 +- requirements.txt | 2 +- setup.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index a273cf8ef..be88a1560 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,8 +1,8 @@ Change History ============== -1.1-dev - unreleased --------------------- +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 diff --git a/kotti/scaffolds/package/requirements.txt b/kotti/scaffolds/package/requirements.txt index ba5b99417..12bebae57 100644 --- a/kotti/scaffolds/package/requirements.txt +++ b/kotti/scaffolds/package/requirements.txt @@ -1,4 +1,4 @@ -Kotti==1.0.0 +Kotti==1.1.0-alpha.1 filedepot==0.0.3 Babel==1.3 Beaker==1.6.5.post1 diff --git a/requirements.txt b/requirements.txt index ba5b99417..12bebae57 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Kotti==1.0.0 +Kotti==1.1.0-alpha.1 filedepot==0.0.3 Babel==1.3 Beaker==1.6.5.post1 diff --git a/setup.py b/setup.py index 3a5af056d..b2a657f2e 100644 --- a/setup.py +++ b/setup.py @@ -85,7 +85,7 @@ install_requires.append('ordereddict') setup(name='Kotti', - version='1.1-dev', + version='1.1.0-alpha.1', description="A high-level, Pythonic web application framework based on Pyramid and SQLAlchemy. It includes an extensible Content Management System called the Kotti CMS.", # noqa long_description='\n\n'.join([README, AUTHORS, CHANGES]), classifiers=[ From b5aa1c2df274ff256fbd8024d0465e6049cc8ebd Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Thu, 19 Mar 2015 15:02:35 +0100 Subject: [PATCH 173/600] Add __pycache__ to gitignores. [ci skip] --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index e28e50f5d..5e7c94c1b 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ junit*.xml *.sublime-* bower_components node_modules +__pycache__ + From 6b7c9e48b5cad643ea42fd9f6f18aeaec1e190ad Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Thu, 2 Apr 2015 12:45:20 +0200 Subject: [PATCH 174/600] Bump version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b2a657f2e..6c53e9a1c 100644 --- a/setup.py +++ b/setup.py @@ -85,7 +85,7 @@ install_requires.append('ordereddict') setup(name='Kotti', - version='1.1.0-alpha.1', + version='1.1.0-alpha.2.dev', description="A high-level, Pythonic web application framework based on Pyramid and SQLAlchemy. It includes an extensible Content Management System called the Kotti CMS.", # noqa long_description='\n\n'.join([README, AUTHORS, CHANGES]), classifiers=[ From e2cd4b2c80ca8d55d513d245b63a99aca5705318 Mon Sep 17 00:00:00 2001 From: davidemoro Date: Thu, 2 Apr 2015 14:36:25 +0200 Subject: [PATCH 175/600] added height=500 to the DocumentSchema's RichTextWidget body --- kotti/views/edit/content.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kotti/views/edit/content.py b/kotti/views/edit/content.py index cecf417c9..57bbab7f2 100644 --- a/kotti/views/edit/content.py +++ b/kotti/views/edit/content.py @@ -51,6 +51,7 @@ class DocumentSchema(ContentSchema): title=_(u'Body'), widget=RichTextWidget( # theme='advanced', width=790, height=500 + height=500, ), missing=u"", ) From 0dfbbf05e3aaafd68f9f05876d54910f6c16f50d Mon Sep 17 00:00:00 2001 From: davidemoro Date: Thu, 2 Apr 2015 14:36:53 +0200 Subject: [PATCH 176/600] signed Kotti's contributor agreement --- CONTRIBUTORS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index f2e26615c..54b960800 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -102,3 +102,4 @@ Contributors - Ionică Bizău, 2014/04/09 - Ichim Tiberiu, 2015/01/13 - Martin Peeters, 2015/02/21 +- Davide Moro, 2015/04/02 From 313c21c4a04e6a9f8f3bf343a5f18c5217035442 Mon Sep 17 00:00:00 2001 From: davidemoro Date: Thu, 2 Apr 2015 14:55:29 +0200 Subject: [PATCH 177/600] updated change history --- CHANGES.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index be88a1560..774d4c775 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -51,6 +51,9 @@ Change History - 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 ------------------ From c119faaab34e2cca05f79c60686b41ca6dca8d60 Mon Sep 17 00:00:00 2001 From: davidemoro Date: Thu, 2 Apr 2015 15:33:18 +0200 Subject: [PATCH 178/600] added target attribute on kotti.util.Link. See #405 --- CHANGES.txt | 2 ++ kotti/templates/edit/el-link.pt | 2 +- kotti/tests/test_util.py | 5 +++++ kotti/util.py | 3 ++- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 774d4c775..ec367a7fc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -54,6 +54,8 @@ Change History - Change ``height`` property on ``body``'s widget (``RichTextField``) for improved usability. See #403. +- Add ``target`` option to ``kotti.util.Link``. See #405. + 1.0.0 - 2015-01-20 ------------------ diff --git a/kotti/templates/edit/el-link.pt b/kotti/templates/edit/el-link.pt index 47decd193..90b114656 100644 --- a/kotti/templates/edit/el-link.pt +++ b/kotti/templates/edit/el-link.pt @@ -1,6 +1,6 @@
  • - + ${link.title}
  • diff --git a/kotti/tests/test_util.py b/kotti/tests/test_util.py index 6a12792af..b2f9b653e 100644 --- a/kotti/tests/test_util.py +++ b/kotti/tests/test_util.py @@ -160,3 +160,8 @@ def test_link_selected_no_view_markers(self): req.url = "http://example.com" assert link.selected(root, req) + + def test_link_target(self): + from kotti.util import Link + assert Link('').target is None + assert Link('', target='_blank').target == '_blank' diff --git a/kotti/util.py b/kotti/util.py index b271995aa..c5f295026 100644 --- a/kotti/util.py +++ b/kotti/util.py @@ -175,12 +175,13 @@ def get_visible_children(self, context, request): class Link(LinkBase): template = 'kotti:templates/edit/el-link.pt' - def __init__(self, name, title=None, predicate=None): + def __init__(self, name, title=None, predicate=None, target=None): self.name = name if title is None: title = name.replace('-', ' ').replace('_', ' ').title() self.title = title self.predicate = predicate + self.target = target def url(self, context, request): return resource_url(context, request) + '@@' + self.name From ac3207abfedf8fd3759be5123c09eba243da79d8 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Thu, 2 Apr 2015 12:45:20 +0200 Subject: [PATCH 179/600] Bump version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b2a657f2e..6c53e9a1c 100644 --- a/setup.py +++ b/setup.py @@ -85,7 +85,7 @@ install_requires.append('ordereddict') setup(name='Kotti', - version='1.1.0-alpha.1', + version='1.1.0-alpha.2.dev', description="A high-level, Pythonic web application framework based on Pyramid and SQLAlchemy. It includes an extensible Content Management System called the Kotti CMS.", # noqa long_description='\n\n'.join([README, AUTHORS, CHANGES]), classifiers=[ From d24e751529dd6ce4490275461d3bf108527fb96b Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Tue, 7 Apr 2015 14:20:07 +0200 Subject: [PATCH 180/600] Add sanitizer docs. --- docs/api/index.rst | 1 + docs/api/kotti.sanitizers.rst | 8 +++ docs/conf.py | 1 + docs/developing/advanced/index.rst | 1 + docs/developing/advanced/sanitizers.rst | 73 +++++++++++++++++++++++++ docs/developing/basic/configuration.rst | 59 +++++++++----------- 6 files changed, 109 insertions(+), 34 deletions(-) create mode 100644 docs/api/kotti.sanitizers.rst create mode 100644 docs/developing/advanced/sanitizers.rst diff --git a/docs/api/index.rst b/docs/api/index.rst index 4b2284af7..af5e64564 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -18,6 +18,7 @@ API Documentation kotti.request kotti.resources kotti.filedepot + kotti.sanitizers kotti.security kotti.sqla kotti.testing 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/conf.py b/docs/conf.py index bcf920dc1..84e5c2985 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -120,6 +120,7 @@ # -- Options for Intersphinx --------------------------------------------------- intersphinx_mapping = { + 'bleach': ('http://bleach.readthedocs.org/en/latest/', None), 'colander': ('http://colander.readthedocs.org/en/latest/', None), 'deform': ('http://deform.readthedocs.org/en/latest/', None), 'fanstatic': ('http://www.fanstatic.org/en/latest/', None), diff --git a/docs/developing/advanced/index.rst b/docs/developing/advanced/index.rst index b86d8746c..9d1f5686b 100644 --- a/docs/developing/advanced/index.rst +++ b/docs/developing/advanced/index.rst @@ -15,3 +15,4 @@ Advanced Topics 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..c549bb8d0 --- /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 *what* is filtered *how* when values are assigned to object attributes. + +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/basic/configuration.rst b/docs/developing/basic/configuration.rst index 1e925cc2c..c402bf8fd 100644 --- a/docs/developing/basic/configuration.rst +++ b/docs/developing/basic/configuration.rst @@ -77,6 +77,10 @@ kotti.max_file_size Max size for file uploads, default: ```10`` (MB) kotti.depot.*.* Configure the blob storage. More details below +kotti.sanitizers Configure available :ref:`sanitizers`. +kotti.sanitize_on_write Configure :ref:`sanitizers` to be used on write + access to resource objects. + pyramid.default_locale_name Set the user interface language, default ``en`` ============================ ================================================== @@ -312,42 +316,29 @@ The default configuration here is: Blob storage configuration -------------------------- -By default, Kotti will store blob data (files uploaded in File and Image -instances) in the database. Internally, Kotti integrates with :app:`filedepot`, -so it is possible to use any :app:``filedepot`` compatible storage, including those -provided by :app:``filedepot`` itself: - -- :class:``depot.io.local.LocalFileStorage`` -- :class:``depot.io.awss3.S3Storage`` -- :class:``depot.io.gridfs.GridFSStorage`` - -The default storage for :app:``Kotti`` is -:class:``~kotti.filedepot.DBFileStorage``. The benefit of storing files in -``DBFileStorage`` is having *all* content in a single place (the DB) which -makes backups, exporting and importing of your site's data easy, as long as you -don't have too many or too large files. The downsides of this approach appear -when your database server resides on a different host (network performance -becomes a greater issue) or your DB dumps become too large to be handled -efficiently. - -To configure a depot, several ``kotti.depot.*.*`` lines need to be added. The -number in the first position is used to group backend configuration and to -order the file storages in the configuration of :app:``filedepot``. The depot -configured with number 0 will be the default depot, where all new blob data -will be saved. There are 2 options that are required for every storage -configuration: ``name`` and ``backend``. The ``name`` is a unique string that -will be used to identify the path of saved files (it is recorded with each blob -info), so once configured for a particular storage, it should never change. The -``backend`` should point to a dotted path for the storage class. Then, any -number of keyword arguments can be added, and they will be passed to the -backend class on initialization. +By default, Kotti will store blob data (files uploaded in File and Image instances) in the database. +Internally, Kotti integrates with :app:`filedepot`, so it is possible to use any :app:`filedepot` compatible storage, including those provided by :app:`filedepot` itself: + +- :class:`depot.io.local.LocalFileStorage` +- :class:`depot.io.awss3.S3Storage` +- :class:`depot.io.gridfs.GridFSStorage` + +The default storage for :app:`Kotti` is :class:`~kotti.filedepot.DBFileStorage`. +The benefit of storing files in ``DBFileStorage`` is having *all* content in a single place (the DB) which makes backups, exporting and importing of your site's data easy, as long as you don't have too many or too large files. +The downsides of this approach appear when your database server resides on a different host (network performance becomes a greater issue) or your DB dumps become too large to be handled efficiently. + +To configure a depot, several ``kotti.depot.*.*`` lines need to be added. +The number in the first position is used to group backend configuration and to order the file storages in the configuration of :app:`filedepot`. +The depot configured with number 0 will be the default depot, where all new blob data will be saved. +There are 2 options that are required for every storage configuration: ``name`` and ``backend``. +The ``name`` is a unique string that will be used to identify the path of saved files (it is recorded with each blob info), so once configured for a particular storage, it should never change. +The ``backend`` should point to a dotted path for the storage class. +Then, any number of keyword arguments can be added, and they will be passed to the backend class on initialization. Example of a possible configurationi that stores blob data on the disk, in -``/var/local/files`` using the :app:``filedepot`` -:class:``depot.io.local.LocalFileStorage`` provided backend. Kotti's default -backend, ``DBFileStorage`` has been moved to position **1** and all data stored -there will continue to be available. See :ref:`blobs` to see how to migrate -blob data between storages. +``/var/local/files`` using the :app:`filedepot` :class:`depot.io.local.LocalFileStorage` provided backend. +Kotti's default backend, ``DBFileStorage`` has been moved to position **1** and all data stored there will continue to be available. +See :ref:`blobs` to see how to migrate blob data between storages. .. code-block:: ini From bba79a745b2a4f2a72fc35491cd3ab99886f1d89 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Tue, 7 Apr 2015 14:21:15 +0200 Subject: [PATCH 181/600] Add requirements. --- kotti/scaffolds/package/requirements.txt | 2 ++ kotti/scaffolds/package/setup.py_tmpl | 2 +- requirements.txt | 2 ++ setup.py | 2 ++ 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/kotti/scaffolds/package/requirements.txt b/kotti/scaffolds/package/requirements.txt index 12bebae57..b7dc53970 100644 --- a/kotti/scaffolds/package/requirements.txt +++ b/kotti/scaffolds/package/requirements.txt @@ -14,6 +14,8 @@ Unidecode==0.04.17 WebOb==1.4 alembic==0.6.7 argparse==1.3.0 +bleach==1.4.1 +bleach-whitelist==0.0.7 colander==1.0 deform==2.0a2 docopt==0.6.2 diff --git a/kotti/scaffolds/package/setup.py_tmpl b/kotti/scaffolds/package/setup.py_tmpl index bf1e71483..1728b4d2e 100644 --- a/kotti/scaffolds/package/setup.py_tmpl +++ b/kotti/scaffolds/package/setup.py_tmpl @@ -16,7 +16,7 @@ except IOError: version = '0.1dev' install_requires = [ - 'Kotti>=0.10b1', + 'Kotti>=1.0.0', ] diff --git a/requirements.txt b/requirements.txt index 12bebae57..b7dc53970 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,6 +14,8 @@ Unidecode==0.04.17 WebOb==1.4 alembic==0.6.7 argparse==1.3.0 +bleach==1.4.1 +bleach-whitelist==0.0.7 colander==1.0 deform==2.0a2 docopt==0.6.2 diff --git a/setup.py b/setup.py index 6c53e9a1c..6369ce9c8 100644 --- a/setup.py +++ b/setup.py @@ -17,6 +17,8 @@ 'Chameleon>=2.7.4', # Fixes error when raising HTTPFound 'Pillow', # dependency of plone.scale 'alembic', + 'bleach', + 'bleach-whitelist', 'colander>=0.9.3', 'deform>=2.0a1', # >=2.0a1 to support Bootstrap 2 'docopt', From 0a28bc653443001309e2295bbf59b484f6866049 Mon Sep 17 00:00:00 2001 From: Andreas Kaiser Date: Tue, 7 Apr 2015 17:04:59 +0200 Subject: [PATCH 182/600] Add sanitizers. See :ref:`sanitizers` and :mod:`kotti.sanitizers` for details. --- CHANGES.txt | 6 ++ kotti/__init__.py | 11 +++ kotti/sanitizers.py | 158 +++++++++++++++++++++++++++++++++ kotti/tests/test_sanitizers.py | 127 ++++++++++++++++++++++++++ kotti/views/util.py | 25 ++++-- 5 files changed, 322 insertions(+), 5 deletions(-) create mode 100644 kotti/sanitizers.py create mode 100644 kotti/tests/test_sanitizers.py diff --git a/CHANGES.txt b/CHANGES.txt index ec367a7fc..50cce29e8 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,12 @@ Change History ============== +1.1.0-alpha.2 - unreleased +-------------------------- + +- Add sanitizers. See :ref:`sanitizers` and :mod:`kotti.sanitizers` for + details. + 1.1.0-alpha.1 - 2015-03-19 -------------------------- diff --git a/kotti/__init__.py b/kotti/__init__.py index 47641e7be..ba28ad319 100644 --- a/kotti/__init__.py +++ b/kotti/__init__.py @@ -61,6 +61,7 @@ def none_factory(**kwargs): # pragma: no cover 'kotti', 'kotti.filedepot', 'kotti.events', + 'kotti.sanitizers', 'kotti.views', 'kotti.views.cache', 'kotti.views.view', @@ -112,6 +113,16 @@ def none_factory(**kwargs): # pragma: no cover 'kotti.register.group': '', 'kotti.register.role': '', 'pyramid_deform.template_search_path': 'kotti:templates/deform', + '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', + ]), } conf_dotted = set([ diff --git a/kotti/sanitizers.py b/kotti/sanitizers.py new file mode 100644 index 000000000..896219b0c --- /dev/null +++ b/kotti/sanitizers.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- + +""" +For a high level introduction and available configuration options +see :ref:`sanitizers`. +""" + +from bleach import clean +from bleach_whitelist import all_styles +from bleach_whitelist import generally_xss_safe +from bleach_whitelist import markdown_attrs +from bleach_whitelist import markdown_tags +from bleach_whitelist import print_attrs +from bleach_whitelist import print_tags +from pyramid.util import DottedNameResolver + +from kotti import get_settings +from kotti.events import objectevent_listeners +from kotti.events import ObjectInsert +from kotti.events import ObjectUpdate + + +def sanitize(html, sanitizer): + """ 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: unicode + """ + + sanitized = get_settings()['kotti.sanitizers'][sanitizer](html) + + return sanitized + + +def xss_protection(html): + """ 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: unicode + """ + + sanitized = clean( + html, + tags=generally_xss_safe, + attributes=lambda self, key, value: True, + styles=all_styles, + strip=True, + strip_comments=True) + + return sanitized + + +def minimal_html(html): + """ 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: unicode + """ + + attributes = dict(zip( + markdown_attrs.keys() + print_attrs.keys(), + markdown_attrs.values() + print_attrs.values())) + + sanitized = clean( + html, + tags=markdown_tags + print_tags, + attributes=attributes, + styles=[], + strip=True, + strip_comments=True) + + return sanitized + + +def no_html(html): + """ Sanitizer that removes **all** tags. + + :param html: HTML to be sanitized + :type html: basestring + + :result: plain text + :rtype: unicode + """ + + sanitized = clean( + html, + tags=[], + attributes={}, + styles=[], + strip=True, + strip_comments=True) + + return sanitized + + +def _setup_sanitizers(settings): + + # step 1: resolve sanitizer functions and make ``kotti.sanitizers`` a + # dictionary containing resolved functions + + if not isinstance(settings['kotti.sanitizers'], basestring): + return + + sanitizers = {} + + for s in settings['kotti.sanitizers'].split(): + name, dottedname = s.split(':') + sanitizers[name.strip()] = DottedNameResolver(None).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(None).resolve(classname) + + def _create_handler(attributename, sanitizers): + def handler(event): + value = getattr(event.object, attributename) + 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): + + _setup_sanitizers(config.registry.settings) + _setup_listeners(config.registry.settings) diff --git a/kotti/tests/test_sanitizers.py b/kotti/tests/test_sanitizers.py new file mode 100644 index 000000000..fb0e5f414 --- /dev/null +++ b/kotti/tests/test_sanitizers.py @@ -0,0 +1,127 @@ +# -*- coding: utf-8 -*- + +unsanitized = u''' +

    Title

    +
    Descrüptiön
    +

    + Paragraph with + internal and + external links. +

    +Fancy marquee! +IMPORTANT! + +

    Unclosed paragraph +''' + + +def _verify_no_html(sanitized): + assert u'<' not in sanitized + assert u'external links' in sanitized + + +def test_no_html(): + + from kotti.sanitizers import no_html + + _verify_no_html(no_html(unsanitized)) + + +def _verify_minimal_html(sanitized): + + from bleach_whitelist import all_tags + from bleach_whitelist import markdown_tags + from bleach_whitelist import print_tags + + for tag in set(all_tags) - set(markdown_tags) - set(print_tags): + assert u'<{}'.format(tag) not in sanitized + + assert u'style=""' in sanitized + assert u'' in sanitized + assert u'size' not in sanitized.lower() + + +def test_minmal_html(): + + from kotti.sanitizers import minimal_html + + _verify_minimal_html(minimal_html(unsanitized)) + + +def _verify_xss_protection(sanitized): + + assert u' @@ -29,21 +30,23 @@ view-${request.view_name or 'default'} ${api.body_css_class}">

    \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 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" +" 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=\"http://kotti.readthedocs.org/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -122,13 +184,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,9 +196,7 @@ 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" "

    \n" -#: kotti/populate.py:121 +#: kotti/populate.py:123 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:124 +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:125 +#, fuzzy msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -185,20 +238,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,619 +267,180 @@ 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:627 kotti/views/edit/content.py:82 -msgid "Document" -msgstr "Dokument" +#: kotti/views/form.py:79 kotti/views/users.py:69 +#: kotti/views/edit/actions.py:374 kotti/views/edit/actions.py:418 +msgid "Your changes have been saved." +msgstr "Ihre Änderungen wurden übernommen." -#: kotti/resources.py:666 kotti/views/edit/content.py:110 -#: kotti/views/edit/content.py:63 -msgid "File" -msgstr "Datei" +#: kotti/views/form.py:174 +msgid "Item was added." +msgstr "Dokument hinzugefügt." -#: kotti/resources.py:753 kotti/views/edit/content.py:138 -msgid "Image" -msgstr "Bild" +#: kotti/views/form.py:156 kotti/templates/site-setup/user.pt:21 +#, python-format +msgid "Edit ${title}" +msgstr "Bearbeite ${title}" -#: kotti/resources.py:517 -msgid "Folder view" -msgstr "Ordneransicht" +#: kotti/views/form.py:264 +#, python-format +msgid "Maximum file size: ${size}MB" +msgstr "Maximale Dateigröße: ${size}MB" -#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 -msgid "Contents" -msgstr "Inhalt" +#: kotti/views/form.py:77 kotti/views/users.py:441 +msgid "Save" +msgstr "Übernehmen" -#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" -msgstr "Bearbeiten" +#: kotti/views/form.py:78 kotti/views/users.py:316 kotti/views/users.py:350 +#: kotti/views/users.py:442 kotti/templates/edit/delete-nodes.pt:45 +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:66 +#: kotti/templates/edit/rename-nodes.pt:42 +msgid "Cancel" +msgstr "Abbrechen" -#: kotti/resources.py:506 -msgid "Share" -msgstr "Teilen" +#: kotti/views/form.py:198 +#, python-format +msgid "Add ${type} to ${title}." +msgstr "${type} in ${title} erstellen" -#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" -msgstr "Aktionen" +#: kotti/views/form.py:202 +#, python-format +msgid "Add ${type}." +msgstr "${type} erstellen" -#: kotti/resources.py:508 kotti/views/edit/actions.py:446 -msgid "Copy" -msgstr "Kopieren" +#: kotti/views/site_setup.py:6 kotti/views/users.py:378 +msgid "User Management" +msgstr "Benutzerverwaltung" -#: kotti/resources.py:509 kotti/views/edit/actions.py:447 -msgid "Cut" -msgstr "Ausschneiden" +#: kotti/views/login.py:237 +msgid "You have reset your password." +msgstr "Ihr Passwort wurde erfolgreich zurückgesetzt." -#: kotti/resources.py:510 kotti/views/edit/actions.py:443 -msgid "Paste" -msgstr "Einfügen" +#: kotti/views/login.py:199 +msgid "You have been logged out." +msgstr "Sie wurden abgemeldet." -#: kotti/resources.py:511 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:448 -msgid "Rename" -msgstr "Umbenennen" +#: kotti/views/login.py:65 kotti/views/users.py:270 +msgid "Full name" +msgstr "Vollständiger Name" -#: kotti/resources.py:512 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:450 -msgid "Delete" -msgstr "Löschen" +#: kotti/views/login.py:68 +msgid "Username" +msgstr "Benutzername" -#: kotti/security.py:189 -msgid "Viewer" -msgstr "Betrachter" +#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 +msgid "Email" +msgstr "E-Mail" -#: kotti/security.py:190 -msgid "Editor" -msgstr "Bearbeiter" +#: kotti/views/login.py:108 +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/security.py:191 -msgid "Owner" -msgstr "Besitzer" +#: kotti/views/login.py:123 +#, python-format +msgid "Register - ${title}" +msgstr "Registrieren - ${title}" -#: kotti/security.py:192 -msgid "Admin" -msgstr "Administrator" +#: kotti/views/login.py:163 +msgid "Login failed." +msgstr "Anmeldung fehlgeschlagen." -#: kotti/templates/add-dropdown.pt:5 -msgid "Add" -msgstr "Hinzufügen" +#: kotti/views/login.py:212 kotti/views/users.py:212 +#: kotti/templates/login.pt:25 +msgid "Password" +msgstr "Passwort" -#: kotti/templates/add-dropdown.pt:27 -msgid "Upload Content" -msgstr "Dateien hochladen" +#: kotti/views/login.py:285 +#, python-format +msgid "Reset your password - ${title}." +msgstr "Passwort zurücksetzen - ${title}" -#: kotti/templates/default-view-selector.pt:5 -msgid "Set default view" -msgstr "Standardansicht setzen" +#: kotti/views/login.py:159 +#, python-format +msgid "Welcome, ${user}!" +msgstr "Willkommen, ${user}!" -#: kotti/templates/editor-bar.pt:35 -msgid "View" -msgstr "Anzeigen" +#: kotti/views/login.py:172 +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/templates/editor-bar.pt:76 -msgid "Navigate" -msgstr "Navigieren" +#: kotti/views/login.py:177 +msgid "That username or email is not known by this system." +msgstr "Der angegebene Benutzername oder die E-Mail-Adresse ist nicht bekannt." -#: kotti/templates/editor-bar.pt:98 -msgid "Preferences" -msgstr "Einstellungen" +#: kotti/views/login.py:82 +msgid "Register" +msgstr "Registrieren" -#: kotti/templates/editor-bar.pt:109 -msgid "Site Setup" -msgstr "Seiten-Einstellungen" +#: kotti/views/login.py:89 kotti/views/login.py:256 +msgid "There was an error." +msgstr "Ein Fehler ist aufgetreten." -#: kotti/templates/editor-bar.pt:131 -msgid "Logout" -msgstr "Abmelden" +#: kotti/views/login.py:249 +msgid "Submit" +msgstr "Absenden" -#: kotti/templates/email-reset-password.pt:3 -msgid "Reset your password for ${site_title}." -msgstr "Passwort zurücksetzen bei ${site_title}" +#: kotti/views/login.py:278 +msgid "Your password reset token may have expired." +msgstr "Der Link zum Zurücksetzen Ihres Passwortes ist abgelaufen!" -#: kotti/templates/email-reset-password.pt:7 -#: kotti/templates/email-set-password.pt:6 -msgid "Hello, ${user_title}!" -msgstr "Hallo ${user_title}!" +#: kotti/views/users.py:313 kotti/views/users.py:501 kotti/views/users.py:536 +#: kotti/templates/site-setup/users.pt:79 kotti/templates/edit/share.pt:59 +msgid "User" +msgstr "Benutzer" -#: 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/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 +#: kotti/views/users.py:536 kotti/templates/site-setup/users.pt:80 +#: kotti/templates/edit/share.pt:60 +msgid "Group" +msgstr "Gruppe" -#: kotti/templates/email-set-password.pt:3 -msgid "Your registration for ${site_title}" -msgstr "Ihre Anmeldung bei ${site_title}" +#: 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/templates/email-set-password.pt:9 -msgid "You are joining ${site_title}." -msgstr "Ihre Anmeldung bei ${site_title}" +#: kotti/views/users.py:587 +#, python-format +msgid "My preferences - ${title}" +msgstr "Meine Einstellungen - ${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/views/users.py:145 +msgid "Invalid value" +msgstr "Ungültige Eingabe" -#: kotti/templates/forbidden.pt:7 -msgid "Forbidden" -msgstr "Zugriff verweigert" +#: kotti/views/users.py:151 +msgid "A user with that name already exists." +msgstr "Ein Benutzer mit diesem Namen existiert bereits." -#: kotti/templates/login.pt:16 -msgid "Login" -msgstr "Anmelden" +#: kotti/views/users.py:158 +msgid "A user with that email already exists." +msgstr "Ein Benutzer mit dieser E-Mail Adresse existiert bereits." -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 -msgid "Username or email" -msgstr "Benutzername oder E-Mail" +#: kotti/views/users.py:181 +#, python-format +msgid "No such group: ${group}" +msgstr "Unbekannte Gruppe: ${group}" -#: kotti/templates/login.pt:34 kotti/views/login.py:212 -#: kotti/views/users.py:212 -msgid "Password" -msgstr "Passwort" +#: kotti/views/users.py:196 kotti/views/edit/content.py:33 +#: kotti/templates/view/folder.pt:20 kotti/templates/edit/delete-nodes.pt:22 +#: kotti/templates/edit/contents.pt:39 kotti/templates/edit/change-state.pt:18 +msgid "Title" +msgstr "Titel" -#: kotti/templates/login.pt:47 -msgid "Log in" -msgstr "Anmelden" +#: kotti/views/users.py:207 kotti/templates/site-setup/users.pt:68 +#: kotti/templates/edit/share.pt:48 +msgid "Name" +msgstr "Name" -#: kotti/templates/login.pt:65 -msgid "Forgot your password?" -msgstr "Passwort vergessen?" - -#. 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 "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:118 -msgid "Not registered yet?" -msgstr "Noch nicht registriert?" - -#: kotti/templates/login.pt:127 -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 -msgid "Make ${state}" -msgstr "Setze ${state}" - -#: kotti/templates/edit/breadcrumbs.pt:5 -msgid "You are here:" -msgstr "Sie sind hier:" - -#: kotti/templates/edit/change-state.pt:7 -msgid "Change workflow state" -msgstr "Workflowstatus ändern" - -#: kotti/templates/edit/change-state.pt:14 -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." - -#: 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:32 -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 -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 -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 -msgid "Modification Date" -msgstr "Bearbeitungsdatum" - -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 -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:100 -msgid "Change state to" -msgstr "Ändere Status in" - -#: kotti/templates/edit/change-state.pt:103 -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." - -#: kotti/templates/edit/change-state.pt:111 -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 -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-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-nodes.pt:35 kotti/templates/edit/rename.pt:26 -msgid "New name" -msgstr "Neuer Name" - -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 -msgid "New title" -msgstr "Neuer Titel" - -#: kotti/templates/edit/rename.pt:7 -msgid "Rename ${title}" -msgstr "${title} umbenennen" - -#: kotti/templates/edit/share.pt:7 -msgid "Share ${title}" -msgstr "Teile ${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 "Suche Benutzer und Gruppen" - -#: 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 -msgid "Status" -msgstr "Status" - -#: kotti/templates/edit/upload.pt:32 -msgid "Progress" -msgstr "Fortschritt" - -#: kotti/templates/edit/upload.pt:124 -msgid "Select file(s) to upload..." -msgstr "Hochzuladene Dateien auswählen..." - -#: kotti/templates/edit/upload.pt:134 -msgid "Upload ${number} files." -msgstr "${number} Dateien hochladen" - -#: kotti/templates/edit/upload.pt:144 -msgid "Dismiss all errors" -msgstr "Alle Fehlermeldungen ausblenden" - -#: kotti/templates/site-setup/delete-user.pt:20 -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 -msgid "Back to User Management" -msgstr "Zurück zur Benutzerverwaltung" - -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 -#, python-format -msgid "Edit ${title}" -msgstr "Bearbeite ${title}" - -#: kotti/templates/site-setup/users.pt:15 -msgid "Search user(s) / group(s)" -msgstr "Suche Benutzer / Gruppe(n)" - -#: kotti/templates/site-setup/users.pt:24 -msgid "Add user" -msgstr "Neuer Benutzer" - -#: kotti/templates/site-setup/users.pt:33 -msgid "Add group" -msgstr "Neue Gruppe" - -#: kotti/templates/site-setup/users.pt:52 -msgid "Find users or groups" -msgstr "Suche Benutzer oder Gruppen" - -#: kotti/templates/site-setup/users.pt:63 -msgid "User- / groupname" -msgstr "Benutzer- / Gruppenname" - -#: kotti/templates/site-setup/users.pt:70 -msgid "Blank search text finds all." -msgstr "Suche mit leerem Eingabefeld liefert alle Benutzer." - -#: kotti/templates/site-setup/users.pt:96 -msgid "Assign global roles" -msgstr "Globale Rollen zuweisen" - -#: kotti/templates/site-setup/users.pt:180 -msgid "Add new user" -msgstr "Neuen Benutzer hinzufügen" - -#: kotti/templates/site-setup/users.pt:190 -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:364 kotti/views/edit/actions.py:408 -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:237 -msgid "You have reset your password." -msgstr "Ihr Passwort wurde erfolgreich zurückgesetzt." - -#: kotti/views/login.py:199 -msgid "You have been logged out." -msgstr "Sie wurden abgemeldet." - -#: kotti/views/login.py:65 kotti/views/users.py:270 -msgid "Full name" -msgstr "Vollständiger Name" - -#: kotti/views/login.py:68 -msgid "Username" -msgstr "Benutzername" - -#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 -msgid "Email" -msgstr "E-Mail" - -#: kotti/views/login.py:108 -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:123 -#, python-format -msgid "Register - ${title}" -msgstr "Registrieren - ${title}" - -#: kotti/views/login.py:163 -msgid "Login failed." -msgstr "Anmeldung fehlgeschlagen." - -#: kotti/views/login.py:285 -#, python-format -msgid "Reset your password - ${title}." -msgstr "Passwort zurücksetzen - ${title}" - -#: kotti/views/login.py:159 -#, python-format -msgid "Welcome, ${user}!" -msgstr "Willkommen, ${user}!" - -#: kotti/views/login.py:172 -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:177 -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:82 -msgid "Register" -msgstr "Registrieren" - -#: kotti/views/login.py:89 kotti/views/login.py:256 -msgid "There was an error." -msgstr "Ein Fehler ist aufgetreten." - -#: kotti/views/login.py:249 -msgid "Submit" -msgstr "Absenden" - -#: kotti/views/login.py:278 -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:587 -#, python-format -msgid "My preferences - ${title}" -msgstr "Meine Einstellungen - ${title}" - -#: kotti/views/users.py:145 -msgid "Invalid value" -msgstr "Ungültige Eingabe" - -#: kotti/views/users.py:151 -msgid "A user with that name already exists." -msgstr "Ein Benutzer mit diesem Namen existiert bereits." - -#: kotti/views/users.py:158 -msgid "A user with that email already exists." -msgstr "Ein Benutzer mit dieser E-Mail Adresse existiert bereits." - -#: kotti/views/users.py:181 -#, python-format -msgid "No such group: ${group}" -msgstr "Unbekannte Gruppe: ${group}" - -#: kotti/views/users.py:219 -msgid "Active" -msgstr "Aktiv" +#: kotti/views/users.py:219 +msgid "Active" +msgstr "Aktiv" #: kotti/views/users.py:220 msgid "Untick this to deactivate the account." @@ -856,8 +468,8 @@ msgid "Add Group" msgstr "Neue Gruppe" #: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 -#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 +#: kotti/views/edit/actions.py:312 kotti/views/edit/actions.py:378 +#: kotti/views/edit/actions.py:424 kotti/views/edit/actions.py:420 msgid "No changes were made." msgstr "Keine Änderungen." @@ -907,11 +519,6 @@ msgstr "${title} ausgeschnitten." msgid "${title} was moved." msgstr "${title} verschoben." -#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 -#, python-format -msgid "${title} was deleted." -msgstr "${title} gelöscht." - #: kotti/views/edit/actions.py:159 #, python-format msgid "${title} was pasted." @@ -931,70 +538,411 @@ msgstr "${title} wird nun in der Navigation angezeigt." msgid "${title} is no longer visible in the navigation." msgstr "${title} wird nun nicht mehr in der Navigation angezeigt." -#: kotti/views/edit/actions.py:293 +#: kotti/views/edit/actions.py:277 kotti/views/edit/actions.py:306 +#, python-format +msgid "${title} was deleted." +msgstr "${title} gelöscht." + +#: kotti/views/edit/actions.py:303 msgid "Nothing was deleted." msgstr "Nichts wurde gelöscht." -#: kotti/views/edit/actions.py:329 kotti/views/edit/actions.py:356 +#: kotti/views/edit/actions.py:339 kotti/views/edit/actions.py:366 msgid "Name and title are required." msgstr "Name und Titel werden benötigt." -#: kotti/views/edit/actions.py:333 +#: kotti/views/edit/actions.py:343 msgid "Item was renamed." msgstr "Objekt umbenannt." -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:465 msgid "Move up" msgstr "Nach oben verschieben" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:466 msgid "Move down" msgstr "Nach unten verschieben" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:467 msgid "Show" msgstr "Anzeigen" -#: kotti/views/edit/actions.py:458 +#: kotti/views/edit/actions.py:468 msgid "Hide" msgstr "Verstecken" -#: kotti/views/edit/actions.py:497 +#: kotti/views/edit/actions.py:507 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:454 +#: kotti/views/edit/actions.py:464 msgid "Change State" msgstr "Status ändern" -#: kotti/views/edit/content.py:36 +#: kotti/views/edit/content.py:38 msgid "Description" msgstr "Beschreibung" -#: kotti/views/edit/content.py:42 +#: kotti/views/edit/content.py:44 msgid "Tags" msgstr "Schlagworte:" -#: kotti/views/edit/content.py:51 +#: kotti/views/edit/content.py:53 msgid "Body" msgstr "Inhalt" -#: kotti/views/edit/default_views.py:99 +#: kotti/views/edit/default_views.py:100 msgid "Default view has been reset to default." msgstr "Standardansicht wurde zurückgesetzt." -#: kotti/views/edit/default_views.py:78 +#: kotti/views/edit/default_views.py:79 msgid "Default view" msgstr "Standardansicht" -#: kotti/views/edit/default_views.py:106 +#: kotti/views/edit/default_views.py:107 msgid "Default view has been set." msgstr "Standardansicht wurde gesetzt." -#: kotti/views/edit/default_views.py:111 +#: kotti/views/edit/default_views.py:112 msgid "Default view could not be set." msgstr "Standardansicht konnte nicht geändert werden." +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "Zugriff verweigert" + +#: 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/add-dropdown.pt:3 +msgid "Add" +msgstr "Hinzufügen" + +#: kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" +msgstr "Dateien hochladen" + +#: 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/editor-bar.pt:21 +msgid "View" +msgstr "Anzeigen" + +#: kotti/templates/editor-bar.pt:44 +msgid "Navigate" +msgstr "Navigieren" + +#: kotti/templates/editor-bar.pt:55 +msgid "Preferences" +msgstr "Einstellungen" + +#: kotti/templates/editor-bar.pt:60 +msgid "Site Setup" +msgstr "Seiten-Einstellungen" + +#: kotti/templates/editor-bar.pt:71 +msgid "Logout" +msgstr "Abmelden" + +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "Setze ${state}" + +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "Standardansicht setzen" + +#: kotti/templates/login.pt:15 +msgid "Login" +msgstr "Anmelden" + +#: kotti/templates/login.pt:20 kotti/templates/login.pt:57 +msgid "Username or email" +msgstr "Benutzername oder E-Mail" + +#: kotti/templates/login.pt:33 +msgid "Log in" +msgstr "Anmelden" + +#: kotti/templates/login.pt:43 +msgid "Forgot your password?" +msgstr "Passwort vergessen?" + +#. 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:50 kotti/templates/login.pt:65 +msgid "Reset password" +msgstr "Passwort zurücksetzen" + +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:48 +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:76 +msgid "Not registered yet?" +msgstr "Noch nicht registriert?" + +#: kotti/templates/login.pt:82 +msgid "Register for an account on this site." +msgstr "Einen Benutzer für den Zugang zu dieser Seite erstellen." + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "Verschlagwortet mit:" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "Suche..." + +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:67 +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:112 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:40 +#: kotti/templates/edit/share.pt:47 kotti/templates/edit/change-state.pt:19 +msgid "Type" +msgstr "Typ" + +#: kotti/templates/view/folder.pt:22 kotti/templates/edit/delete-nodes.pt:25 +#: kotti/templates/edit/contents.pt:43 kotti/templates/edit/change-state.pt:21 +msgid "Creation Date" +msgstr "Erstellungsdatum" + +#: kotti/templates/view/folder.pt:23 kotti/templates/edit/delete-nodes.pt:26 +#: kotti/templates/edit/contents.pt:44 kotti/templates/edit/change-state.pt:22 +msgid "Modification Date" +msgstr "Bearbeitungsdatum" + +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "Suchergebnisse" + +#: kotti/templates/http-errors/notfound.pt:6 +msgid "Not Found" +msgstr "" + +#: kotti/templates/http-errors/notfound.pt:7 +msgid "The resource could not be found" +msgstr "" + +#: kotti/templates/site-setup/delete-user.pt:9 +#: kotti/templates/edit/delete.pt:9 +msgid "Delete ${title}" +msgstr "Lösche ${title}" + +#: 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:12 +msgid "Back to User Management" +msgstr "Zurück zur Benutzerverwaltung" + +#: kotti/templates/site-setup/users.pt:12 +msgid "Search user(s) / group(s)" +msgstr "Suche Benutzer / Gruppe(n)" + +#: kotti/templates/site-setup/users.pt:17 +msgid "Add user" +msgstr "Neuer Benutzer" + +#: kotti/templates/site-setup/users.pt:22 +msgid "Add group" +msgstr "Neue Gruppe" + +#: kotti/templates/site-setup/users.pt:34 +msgid "Find users or groups" +msgstr "Suche Benutzer oder Gruppen" + +#: kotti/templates/site-setup/users.pt:39 +msgid "User- / groupname" +msgstr "Benutzer- / Gruppenname" + +#: kotti/templates/site-setup/users.pt:47 kotti/templates/edit/share.pt:14 +#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:26 +msgid "Search users and groups" +msgstr "Suche Benutzer und Gruppen" + +#: kotti/templates/site-setup/users.pt:49 +msgid "Blank search text finds all." +msgstr "Suche mit leerem Eingabefeld liefert alle Benutzer." + +#: kotti/templates/site-setup/users.pt:55 kotti/templates/edit/share.pt:33 +msgid "Search" +msgstr "Suche" + +#: kotti/templates/site-setup/users.pt:63 +msgid "Assign global roles" +msgstr "Globale Rollen zuweisen" + +#: kotti/templates/site-setup/users.pt:83 kotti/templates/edit/share.pt:63 +msgid "Gravatar" +msgstr "Gravatar" + +#: kotti/templates/site-setup/users.pt:105 kotti/templates/edit/share.pt:83 +#: kotti/templates/edit/change-state.pt:64 +msgid "Apply changes" +msgstr "Änderungen übernehmen" + +#: kotti/templates/site-setup/users.pt:115 +msgid "Add new user" +msgstr "Neuen Benutzer hinzufügen" + +#: kotti/templates/site-setup/users.pt:121 +msgid "Add new group" +msgstr "Neue Gruppe hinzufügen" + +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "Navigieren" + +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" +msgstr "Inhalte aus lokalen Dateien hochladen" + +#: kotti/templates/edit/upload.pt:18 kotti/templates/edit/upload.pt:111 +msgid "Filename" +msgstr "Dateiname" + +#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:113 +msgid "Size" +msgstr "Größe" + +#: kotti/templates/edit/upload.pt:21 +msgid "Status" +msgstr "Status" + +#: kotti/templates/edit/upload.pt:22 +msgid "Progress" +msgstr "Fortschritt" + +#: kotti/templates/edit/upload.pt:88 +msgid "Select file(s) to upload..." +msgstr "Hochzuladene Dateien auswählen..." + +#: kotti/templates/edit/upload.pt:96 +msgid "Upload ${number} files." +msgstr "${number} Dateien hochladen" + +#: kotti/templates/edit/upload.pt:101 +msgid "Dismiss all errors" +msgstr "Alle Fehlermeldungen ausblenden" + +#: kotti/templates/edit/delete-nodes.pt:14 +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-nodes.pt:24 kotti/templates/edit/contents.pt:41 +#: kotti/templates/edit/change-state.pt:20 +msgid "State" +msgstr "Status" + +#: kotti/templates/edit/breadcrumbs.pt:8 +msgid "You are here:" +msgstr "Sie sind hier:" + +#: kotti/templates/edit/contents.pt:30 +msgid "No content items are contained here." +msgstr "Kein sichtbarer Inhalt." + +#: kotti/templates/edit/contents.pt:42 +msgid "Visibility" +msgstr "Sichtbarkeit" + +#: kotti/templates/edit/contents.pt:75 +msgid "Visible" +msgstr "Sichtbar" + +#: kotti/templates/edit/contents.pt:79 +msgid "Hidden" +msgstr "Unsichtbar" + +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "${title} umbenennen" + +#: 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.pt:19 kotti/templates/edit/rename-nodes.pt:26 +msgid "New name" +msgstr "Neuer Name" + +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:33 +msgid "New title" +msgstr "Neuer Titel" + +#: 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/share.pt:8 +msgid "Share ${title}" +msgstr "Teile ${title}" + +#: kotti/templates/edit/share.pt:42 +msgid "Assign local roles" +msgstr "Lokale Rollen zuweisen" + +#: kotti/templates/edit/change-state.pt:8 +msgid "Change workflow state" +msgstr "Workflowstatus ändern" + +#: 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." + +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:45 +msgid "Include children" +msgstr "Unterordner einbeziehen" + +#: 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:50 +msgid "Change state to" +msgstr "Ändere Status in" + +#: kotti/templates/edit/change-state.pt:51 +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." + +#: kotti/templates/edit/change-state.pt:55 +msgid "No change" +msgstr "Keine Änderungen." + +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "Willkommen, ${user}! Sie sind angemeldet." + +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "Willkommen! Sie sind nicht angemeldet." + #~ msgid "Private" #~ msgstr "Privat" diff --git a/kotti/locale/en/LC_MESSAGES/Kotti.po b/kotti/locale/en/LC_MESSAGES/Kotti.po index c7186f8ca..f8db8bd51 100644 --- a/kotti/locale/en/LC_MESSAGES/Kotti.po +++ b/kotti/locale/en/LC_MESSAGES/Kotti.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Kotti 0.4.4\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2015-02-22 11:33+0100\n" +"POT-Creation-Date: 2015-09-29 14:24+0100\n" "PO-Revision-Date: 2012-01-16 12:02+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: en \n" @@ -18,6 +18,81 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "Generated-By: Babel 0.9.6\n" +#: kotti/security.py:190 +msgid "Viewer" +msgstr "" + +#: kotti/security.py:191 +msgid "Editor" +msgstr "" + +#: kotti/security.py:192 +msgid "Owner" +msgstr "" + +#: kotti/security.py:193 +msgid "Admin" +msgstr "" + +#: kotti/resources.py:508 kotti/views/edit/actions.py:456 +msgid "Copy" +msgstr "" + +#: kotti/resources.py:509 kotti/views/edit/actions.py:457 +msgid "Cut" +msgstr "" + +#: kotti/resources.py:510 kotti/views/edit/actions.py:453 +msgid "Paste" +msgstr "" + +#: kotti/resources.py:511 kotti/views/edit/actions.py:458 +#: kotti/templates/edit/rename.pt:31 kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:40 +msgid "Rename" +msgstr "" + +#: kotti/resources.py:512 kotti/views/users.py:443 +#: kotti/views/edit/actions.py:460 +#: kotti/templates/site-setup/delete-user.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:50 kotti/templates/edit/delete.pt:24 +msgid "Delete" +msgstr "" + +#: kotti/resources.py:637 kotti/views/edit/content.py:85 +msgid "Document" +msgstr "" + +#: kotti/resources.py:726 kotti/views/edit/content.py:113 +#: kotti/views/edit/content.py:66 +msgid "File" +msgstr "" + +#: kotti/resources.py:772 kotti/views/edit/content.py:141 +msgid "Image" +msgstr "" + +#: kotti/resources.py:529 +msgid "Folder view" +msgstr "" + +#: kotti/resources.py:523 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "" + +#: kotti/resources.py:524 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "" + +#: kotti/resources.py:525 +msgid "Share" +msgstr "" + +#: kotti/resources.py:526 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "" + #: kotti/populate.py:64 msgid "Welcome to Kotti" msgstr "" @@ -32,29 +107,25 @@ 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=\"http://kotti.readthedocs.org/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -62,13 +133,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,9 +146,9 @@ 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" msgstr "" -#: kotti/populate.py:121 +#: kotti/populate.py:123 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:124 +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:125 msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -123,760 +190,680 @@ 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:627 kotti/views/edit/content.py:82 -msgid "Document" +#: kotti/views/form.py:79 kotti/views/users.py:69 +#: kotti/views/edit/actions.py:374 kotti/views/edit/actions.py:418 +msgid "Your changes have been saved." msgstr "" -#: kotti/resources.py:666 kotti/views/edit/content.py:110 -#: kotti/views/edit/content.py:63 -msgid "File" +#: kotti/views/form.py:174 +msgid "Item was added." msgstr "" -#: kotti/resources.py:753 kotti/views/edit/content.py:138 -msgid "Image" +#: kotti/views/form.py:156 kotti/templates/site-setup/user.pt:21 +#, python-format +msgid "Edit ${title}" msgstr "" -#: kotti/resources.py:517 -msgid "Folder view" +#: kotti/views/form.py:264 +#, python-format +msgid "Maximum file size: ${size}MB" msgstr "" -#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 -msgid "Contents" +#: kotti/views/form.py:77 kotti/views/users.py:441 +msgid "Save" msgstr "" -#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" +#: kotti/views/form.py:78 kotti/views/users.py:316 kotti/views/users.py:350 +#: kotti/views/users.py:442 kotti/templates/edit/delete-nodes.pt:45 +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:66 +#: kotti/templates/edit/rename-nodes.pt:42 +msgid "Cancel" msgstr "" -#: kotti/resources.py:506 -msgid "Share" +#: kotti/views/form.py:198 +#, python-format +msgid "Add ${type} to ${title}." msgstr "" -#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" +#: kotti/views/form.py:202 +#, python-format +msgid "Add ${type}." msgstr "" -#: kotti/resources.py:508 kotti/views/edit/actions.py:446 -msgid "Copy" +#: kotti/views/site_setup.py:6 kotti/views/users.py:378 +msgid "User Management" msgstr "" -#: kotti/resources.py:509 kotti/views/edit/actions.py:447 -msgid "Cut" +#: kotti/views/login.py:237 +msgid "You have reset your password." msgstr "" -#: kotti/resources.py:510 kotti/views/edit/actions.py:443 -msgid "Paste" +#: kotti/views/login.py:199 +msgid "You have been logged out." msgstr "" -#: kotti/resources.py:511 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:448 -msgid "Rename" +#: kotti/views/login.py:65 kotti/views/users.py:270 +msgid "Full name" msgstr "" -#: kotti/resources.py:512 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:450 -msgid "Delete" +#: kotti/views/login.py:68 +msgid "Username" msgstr "" -#: kotti/security.py:189 -msgid "Viewer" +#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 +msgid "Email" msgstr "" -#: kotti/security.py:190 -msgid "Editor" +#: kotti/views/login.py:108 +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/security.py:191 -msgid "Owner" +#: kotti/views/login.py:123 +#, python-format +msgid "Register - ${title}" msgstr "" -#: kotti/security.py:192 -msgid "Admin" +#: kotti/views/login.py:163 +msgid "Login failed." msgstr "" -#: kotti/templates/add-dropdown.pt:5 -msgid "Add" +#: kotti/views/login.py:212 kotti/views/users.py:212 +#: kotti/templates/login.pt:25 +msgid "Password" msgstr "" -#: kotti/templates/add-dropdown.pt:27 -msgid "Upload Content" +#: kotti/views/login.py:285 +#, python-format +msgid "Reset your password - ${title}." msgstr "" -#: kotti/templates/default-view-selector.pt:5 -msgid "Set default view" +#: kotti/views/login.py:159 +#, python-format +msgid "Welcome, ${user}!" msgstr "" -#: kotti/templates/editor-bar.pt:35 -msgid "View" +#: kotti/views/login.py:172 +msgid "You should be receiving an email with a link to reset your password. Doing so will activate your account." msgstr "" -#: kotti/templates/editor-bar.pt:76 -msgid "Navigate" +#: kotti/views/login.py:177 +msgid "That username or email is not known by this system." msgstr "" -#: kotti/templates/editor-bar.pt:98 -msgid "Preferences" +#: kotti/views/login.py:82 +msgid "Register" msgstr "" -#: kotti/templates/editor-bar.pt:109 -msgid "Site Setup" +#: kotti/views/login.py:89 kotti/views/login.py:256 +msgid "There was an error." msgstr "" -#: kotti/templates/editor-bar.pt:131 -msgid "Logout" +#: kotti/views/login.py:249 +msgid "Submit" msgstr "" -#: kotti/templates/email-reset-password.pt:3 -msgid "Reset your password for ${site_title}." +#: kotti/views/login.py:278 +msgid "Your password reset token may have expired." msgstr "" -#: kotti/templates/email-reset-password.pt:7 -#: kotti/templates/email-set-password.pt:6 -msgid "Hello, ${user_title}!" +#: kotti/views/users.py:313 kotti/views/users.py:501 kotti/views/users.py:536 +#: kotti/templates/site-setup/users.pt:79 kotti/templates/edit/share.pt:59 +msgid "User" msgstr "" -#: kotti/templates/email-reset-password.pt:10 -msgid "Click this link to reset your password at ${site_title}: ${url}" +#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 +#: kotti/views/users.py:536 kotti/templates/site-setup/users.pt:80 +#: kotti/templates/edit/share.pt:60 +msgid "Group" msgstr "" -#: kotti/templates/email-set-password.pt:3 -msgid "Your registration for ${site_title}" +#: 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/templates/email-set-password.pt:9 -msgid "You are joining ${site_title}." +#: kotti/views/users.py:587 +#, python-format +msgid "My preferences - ${title}" msgstr "" -#: kotti/templates/email-set-password.pt:12 -msgid "Click here to set your password and log in: ${url}" +#: kotti/views/users.py:145 +msgid "Invalid value" msgstr "" -#: kotti/templates/forbidden.pt:7 -msgid "Forbidden" +#: kotti/views/users.py:151 +msgid "A user with that name already exists." msgstr "" -#: kotti/templates/login.pt:16 -msgid "Login" +#: kotti/views/users.py:158 +msgid "A user with that email already exists." msgstr "" -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 -msgid "Username or email" +#: kotti/views/users.py:181 +#, python-format +msgid "No such group: ${group}" msgstr "" -#: kotti/templates/login.pt:34 kotti/views/login.py:212 -#: kotti/views/users.py:212 -msgid "Password" +#: kotti/views/users.py:196 kotti/views/edit/content.py:33 +#: kotti/templates/view/folder.pt:20 kotti/templates/edit/delete-nodes.pt:22 +#: kotti/templates/edit/contents.pt:39 kotti/templates/edit/change-state.pt:18 +msgid "Title" msgstr "" -#: kotti/templates/login.pt:47 -msgid "Log in" +#: kotti/views/users.py:207 kotti/templates/site-setup/users.pt:68 +#: kotti/templates/edit/share.pt:48 +msgid "Name" msgstr "" -#: kotti/templates/login.pt:65 -msgid "Forgot your password?" +#: kotti/views/users.py:219 +msgid "Active" 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/views/users.py:220 +msgid "Untick this to deactivate the account." 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/views/users.py:226 +msgid "Global roles" msgstr "" -#: kotti/templates/login.pt:118 -msgid "Not registered yet?" +#: kotti/views/users.py:230 +msgid "Groups" msgstr "" -#: kotti/templates/login.pt:127 -msgid "Register for an account on this site." +#: kotti/views/users.py:315 +msgid "Add User" msgstr "" -#: kotti/templates/workflow-dropdown.pt:19 -msgid "Make ${state}" +#: kotti/views/users.py:339 +#, python-format +msgid "${title} was added." msgstr "" -#: kotti/templates/edit/breadcrumbs.pt:5 -msgid "You are here:" +#: kotti/views/users.py:349 +msgid "Add Group" msgstr "" -#: kotti/templates/edit/change-state.pt:7 -msgid "Change workflow state" +#: kotti/views/users.py:465 kotti/views/users.py:71 +#: kotti/views/edit/actions.py:312 kotti/views/edit/actions.py:378 +#: kotti/views/edit/actions.py:424 kotti/views/edit/actions.py:420 +msgid "No changes were made." msgstr "" -#: kotti/templates/edit/change-state.pt:14 -msgid "An item's workflow state determines who can see, edit and/or manage it." +#: kotti/views/users.py:558 +msgid "No name was given." 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:32 -msgid "Title" +#: kotti/views/users.py:98 +msgid "No users or groups were found." 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" +#: kotti/views/users.py:505 +#, python-format +msgid "Edit ${principal_type} ${title}" msgstr "" -#: kotti/templates/edit/change-state.pt:32 kotti/templates/edit/contents.pt:63 -#: kotti/templates/edit/delete-nodes.pt:39 -msgid "State" +#: kotti/views/users.py:533 +msgid "User was not found." 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 -msgid "Creation Date" +#: kotti/views/users.py:324 +msgid "Send password registration link." 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 -msgid "Modification Date" +#: kotti/views/users.py:543 +#, python-format +msgid "${principal_type} ${title} was deleted." msgstr "" -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 -msgid "Include children" +#: kotti/views/users.py:551 +#, python-format +msgid "Delete ${principal_type} ${title}" 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/views/edit/actions.py:106 +#, python-format +msgid "${title} was copied." msgstr "" -#: kotti/templates/edit/change-state.pt:100 -msgid "Change state to" +#: kotti/views/edit/actions.py:124 +#, python-format +msgid "${title} was cut." msgstr "" -#: kotti/templates/edit/change-state.pt:103 -msgid "Select the new state where all chosen items should be set." +#: kotti/views/edit/actions.py:183 +#, python-format +msgid "${title} was moved." msgstr "" -#: kotti/templates/edit/change-state.pt:111 -msgid "No change" +#: kotti/views/edit/actions.py:159 +#, python-format +msgid "${title} was pasted." msgstr "" -#: kotti/templates/edit/change-state.pt:127 kotti/templates/edit/share.pt:121 -#: kotti/templates/site-setup/users.pt:165 -msgid "Apply changes" +#: kotti/views/edit/actions.py:162 +msgid "Could not paste node. It no longer exists." 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" +#: kotti/views/edit/actions.py:225 +#, python-format +msgid "${title} is now visible in the navigation." msgstr "" -#: kotti/templates/edit/contents.pt:43 -msgid "No content items are contained here." +#: kotti/views/edit/actions.py:228 +#, python-format +msgid "${title} is no longer visible in the navigation." msgstr "" -#: kotti/templates/edit/contents.pt:66 -msgid "Visibility" +#: kotti/views/edit/actions.py:277 kotti/views/edit/actions.py:306 +#, python-format +msgid "${title} was deleted." msgstr "" -#: kotti/templates/edit/contents.pt:125 -msgid "Visible" +#: kotti/views/edit/actions.py:303 +msgid "Nothing was deleted." msgstr "" -#: kotti/templates/edit/contents.pt:134 -msgid "Hidden" +#: kotti/views/edit/actions.py:339 kotti/views/edit/actions.py:366 +msgid "Name and title are required." msgstr "" -#: kotti/templates/edit/delete-nodes.pt:17 -msgid "Are you sure you want to delete the following items?" +#: kotti/views/edit/actions.py:343 +msgid "Item was renamed." msgstr "" -#: kotti/templates/edit/delete.pt:8 -#: kotti/templates/site-setup/delete-user.pt:8 -msgid "Delete ${title}" +#: kotti/views/edit/actions.py:465 +msgid "Move up" msgstr "" -#: kotti/templates/edit/delete.pt:20 -msgid "Are you sure you want to delete ${title}?" +#: kotti/views/edit/actions.py:466 +msgid "Move down" msgstr "" -#: kotti/templates/edit/nav-tree-view.pt:7 -msgid "Navigate Site" +#: kotti/views/edit/actions.py:467 +msgid "Show" 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/views/edit/actions.py:468 +msgid "Hide" msgstr "" -#: kotti/templates/edit/rename-nodes.pt:35 kotti/templates/edit/rename.pt:26 -msgid "New name" +#: kotti/views/edit/actions.py:507 +msgid "You have to select items to perform an action." msgstr "" -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 -msgid "New title" +#: kotti/views/edit/actions.py:464 +msgid "Change State" msgstr "" -#: kotti/templates/edit/rename.pt:7 -msgid "Rename ${title}" +#: kotti/views/edit/content.py:38 +msgid "Description" msgstr "" -#: kotti/templates/edit/share.pt:7 -msgid "Share ${title}" +#: kotti/views/edit/content.py:44 +msgid "Tags" 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/views/edit/content.py:53 +msgid "Body" msgstr "" -#: kotti/templates/edit/share.pt:43 kotti/templates/site-setup/users.pt:81 -msgid "Search" +#: kotti/views/edit/default_views.py:100 +msgid "Default view has been reset to default." msgstr "" -#: kotti/templates/edit/share.pt:60 -msgid "Assign local roles" +#: kotti/views/edit/default_views.py:79 +msgid "Default view" msgstr "" -#: kotti/templates/edit/share.pt:73 kotti/templates/site-setup/users.pt:108 -#: kotti/views/users.py:207 -msgid "Name" +#: kotti/views/edit/default_views.py:107 +msgid "Default view has been set." 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/views/edit/default_views.py:112 +msgid "Default view could not be set." 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/forbidden.pt:10 +msgid "Forbidden" msgstr "" -#: kotti/templates/edit/share.pt:101 kotti/templates/site-setup/users.pt:136 -msgid "Gravatar" +#: kotti/templates/email-reset-password.pt:2 +msgid "Reset your password for ${site_title}." msgstr "" -#: kotti/templates/edit/upload.pt:9 -msgid "Upload content from local file(s)" +#: kotti/templates/email-reset-password.pt:4 +#: kotti/templates/email-set-password.pt:4 +msgid "Hello, ${user_title}!" msgstr "" -#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:164 -msgid "Filename" +#: kotti/templates/email-reset-password.pt:5 +msgid "Click this link to reset your password at ${site_title}: ${url}" msgstr "" -#: kotti/templates/edit/upload.pt:26 kotti/templates/edit/upload.pt:170 -msgid "Size" +#: kotti/templates/add-dropdown.pt:3 +msgid "Add" msgstr "" -#: kotti/templates/edit/upload.pt:29 -msgid "Status" +#: kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" msgstr "" -#: kotti/templates/edit/upload.pt:32 -msgid "Progress" +#: kotti/templates/email-set-password.pt:2 +msgid "Your registration for ${site_title}" msgstr "" -#: kotti/templates/edit/upload.pt:124 -msgid "Select file(s) to upload..." +#: kotti/templates/email-set-password.pt:5 +msgid "You are joining ${site_title}." msgstr "" -#: kotti/templates/edit/upload.pt:134 -msgid "Upload ${number} files." +#: kotti/templates/email-set-password.pt:6 +msgid "Click here to set your password and log in: ${url}" msgstr "" -#: kotti/templates/edit/upload.pt:144 -msgid "Dismiss all errors" +#: kotti/templates/editor-bar.pt:21 +msgid "View" msgstr "" -#: kotti/templates/site-setup/delete-user.pt:20 -msgid "Are you sure you want to delete ${type} ${title}?" +#: kotti/templates/editor-bar.pt:44 +msgid "Navigate" msgstr "" -#: kotti/templates/site-setup/user.pt:13 -msgid "Back to User Management" +#: kotti/templates/editor-bar.pt:55 +msgid "Preferences" msgstr "" -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 -#, python-format -msgid "Edit ${title}" +#: kotti/templates/editor-bar.pt:60 +msgid "Site Setup" msgstr "" -#: kotti/templates/site-setup/users.pt:15 -msgid "Search user(s) / group(s)" +#: kotti/templates/editor-bar.pt:71 +msgid "Logout" msgstr "" -#: kotti/templates/site-setup/users.pt:24 -msgid "Add user" +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" msgstr "" -#: kotti/templates/site-setup/users.pt:33 -msgid "Add group" +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" msgstr "" -#: kotti/templates/site-setup/users.pt:52 -msgid "Find users or groups" +#: kotti/templates/login.pt:15 +msgid "Login" msgstr "" -#: kotti/templates/site-setup/users.pt:63 -msgid "User- / groupname" +#: kotti/templates/login.pt:20 kotti/templates/login.pt:57 +msgid "Username or email" msgstr "" -#: kotti/templates/site-setup/users.pt:70 -msgid "Blank search text finds all." +#: kotti/templates/login.pt:33 +msgid "Log in" msgstr "" -#: kotti/templates/site-setup/users.pt:96 -msgid "Assign global roles" +#: kotti/templates/login.pt:43 +msgid "Forgot your password?" msgstr "" -#: kotti/templates/site-setup/users.pt:180 -msgid "Add new user" +#. 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:50 kotti/templates/login.pt:65 +msgid "Reset password" msgstr "" -#: kotti/templates/site-setup/users.pt:190 -msgid "Add new group" +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:48 +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/templates/view/search-results.pt:8 -msgid "Search Results" +#: kotti/templates/login.pt:76 +msgid "Not registered yet?" msgstr "" -#: kotti/templates/view/search.pt:5 -msgid "Search..." +#: kotti/templates/login.pt:82 +msgid "Register for an account on this site." msgstr "" -#: kotti/templates/view/tags.pt:5 +#: kotti/templates/view/tags.pt:7 msgid "Tagged with:" msgstr "" -#: kotti/tests/testing_view.pt:7 -msgid "Welcome, ${user}! You are logged in." -msgstr "" - -#: 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:364 kotti/views/edit/actions.py:408 -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 "" - -#: 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 "" - -#: kotti/views/form.py:202 -#, python-format -msgid "Add ${type}." -msgstr "" - -#: kotti/views/login.py:237 -msgid "You have reset your password." -msgstr "" - -#: kotti/views/login.py:199 -msgid "You have been logged out." -msgstr "" - -#: kotti/views/login.py:65 kotti/views/users.py:270 -msgid "Full name" -msgstr "" - -#: kotti/views/login.py:68 -msgid "Username" -msgstr "" - -#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 -msgid "Email" -msgstr "" - -#: kotti/views/login.py:108 -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:123 -#, python-format -msgid "Register - ${title}" -msgstr "" - -#: kotti/views/login.py:163 -msgid "Login failed." +#: kotti/templates/view/search.pt:12 +msgid "Search..." msgstr "" -#: kotti/views/login.py:285 -#, python-format -msgid "Reset your password - ${title}." +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:67 +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:112 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:40 +#: kotti/templates/edit/share.pt:47 kotti/templates/edit/change-state.pt:19 +msgid "Type" msgstr "" -#: kotti/views/login.py:159 -#, python-format -msgid "Welcome, ${user}!" +#: kotti/templates/view/folder.pt:22 kotti/templates/edit/delete-nodes.pt:25 +#: kotti/templates/edit/contents.pt:43 kotti/templates/edit/change-state.pt:21 +msgid "Creation Date" msgstr "" -#: kotti/views/login.py:172 -msgid "" -"You should be receiving an email with a link to reset your password. Doing " -"so will activate your account." +#: kotti/templates/view/folder.pt:23 kotti/templates/edit/delete-nodes.pt:26 +#: kotti/templates/edit/contents.pt:44 kotti/templates/edit/change-state.pt:22 +msgid "Modification Date" msgstr "" -#: kotti/views/login.py:177 -msgid "That username or email is not known by this system." +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" msgstr "" -#: kotti/views/login.py:82 -msgid "Register" +#: kotti/templates/http-errors/notfound.pt:6 +msgid "Not Found" msgstr "" -#: kotti/views/login.py:89 kotti/views/login.py:256 -msgid "There was an error." +#: kotti/templates/http-errors/notfound.pt:7 +msgid "The resource could not be found" msgstr "" -#: kotti/views/login.py:249 -msgid "Submit" +#: kotti/templates/site-setup/delete-user.pt:9 +#: kotti/templates/edit/delete.pt:9 +msgid "Delete ${title}" msgstr "" -#: kotti/views/login.py:278 -msgid "Your password reset token may have expired." +#: kotti/templates/site-setup/delete-user.pt:15 +msgid "Are you sure you want to delete ${type} ${title}?" msgstr "" -#: kotti/views/site_setup.py:6 kotti/views/users.py:378 -msgid "User Management" +#: kotti/templates/site-setup/user.pt:12 +msgid "Back to 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." +#: kotti/templates/site-setup/users.pt:12 +msgid "Search user(s) / group(s)" msgstr "" -#: kotti/views/users.py:587 -#, python-format -msgid "My preferences - ${title}" +#: kotti/templates/site-setup/users.pt:17 +msgid "Add user" msgstr "" -#: kotti/views/users.py:145 -msgid "Invalid value" +#: kotti/templates/site-setup/users.pt:22 +msgid "Add group" msgstr "" -#: kotti/views/users.py:151 -msgid "A user with that name already exists." +#: kotti/templates/site-setup/users.pt:34 +msgid "Find users or groups" msgstr "" -#: kotti/views/users.py:158 -msgid "A user with that email already exists." +#: kotti/templates/site-setup/users.pt:39 +msgid "User- / groupname" msgstr "" -#: kotti/views/users.py:181 -#, python-format -msgid "No such group: ${group}" +#: kotti/templates/site-setup/users.pt:47 kotti/templates/edit/share.pt:14 +#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:26 +msgid "Search users and groups" msgstr "" -#: kotti/views/users.py:219 -msgid "Active" +#: kotti/templates/site-setup/users.pt:49 +msgid "Blank search text finds all." msgstr "" -#: kotti/views/users.py:220 -msgid "Untick this to deactivate the account." +#: kotti/templates/site-setup/users.pt:55 kotti/templates/edit/share.pt:33 +msgid "Search" msgstr "" -#: kotti/views/users.py:226 -msgid "Global roles" +#: kotti/templates/site-setup/users.pt:63 +msgid "Assign global roles" msgstr "" -#: kotti/views/users.py:230 -msgid "Groups" +#: kotti/templates/site-setup/users.pt:83 kotti/templates/edit/share.pt:63 +msgid "Gravatar" msgstr "" -#: kotti/views/users.py:315 -msgid "Add User" +#: kotti/templates/site-setup/users.pt:105 kotti/templates/edit/share.pt:83 +#: kotti/templates/edit/change-state.pt:64 +msgid "Apply changes" msgstr "" -#: kotti/views/users.py:339 -#, python-format -msgid "${title} was added." +#: kotti/templates/site-setup/users.pt:115 +msgid "Add new user" msgstr "" -#: kotti/views/users.py:349 -msgid "Add Group" +#: kotti/templates/site-setup/users.pt:121 +msgid "Add new group" msgstr "" -#: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 -#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 -msgid "No changes were made." +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" msgstr "" -#: kotti/views/users.py:558 -msgid "No name was given." +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" msgstr "" -#: kotti/views/users.py:98 -msgid "No users or groups were found." +#: kotti/templates/edit/upload.pt:18 kotti/templates/edit/upload.pt:111 +msgid "Filename" msgstr "" -#: kotti/views/users.py:505 -#, python-format -msgid "Edit ${principal_type} ${title}" +#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:113 +msgid "Size" msgstr "" -#: kotti/views/users.py:533 -msgid "User was not found." +#: kotti/templates/edit/upload.pt:21 +msgid "Status" msgstr "" -#: kotti/views/users.py:324 -msgid "Send password registration link." +#: kotti/templates/edit/upload.pt:22 +msgid "Progress" msgstr "" -#: kotti/views/users.py:543 -#, python-format -msgid "${principal_type} ${title} was deleted." +#: kotti/templates/edit/upload.pt:88 +msgid "Select file(s) to upload..." msgstr "" -#: kotti/views/users.py:551 -#, python-format -msgid "Delete ${principal_type} ${title}" +#: kotti/templates/edit/upload.pt:96 +msgid "Upload ${number} files." msgstr "" -#: kotti/views/edit/actions.py:106 -#, python-format -msgid "${title} was copied." +#: kotti/templates/edit/upload.pt:101 +msgid "Dismiss all errors" msgstr "" -#: kotti/views/edit/actions.py:124 -#, python-format -msgid "${title} was cut." +#: kotti/templates/edit/delete-nodes.pt:14 +msgid "Are you sure you want to delete the following items?" msgstr "" -#: kotti/views/edit/actions.py:183 -#, python-format -msgid "${title} was moved." +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:41 +#: kotti/templates/edit/change-state.pt:20 +msgid "State" msgstr "" -#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 -#, python-format -msgid "${title} was deleted." +#: kotti/templates/edit/breadcrumbs.pt:8 +msgid "You are here:" msgstr "" -#: kotti/views/edit/actions.py:159 -#, python-format -msgid "${title} was pasted." +#: kotti/templates/edit/contents.pt:30 +msgid "No content items are contained here." msgstr "" -#: kotti/views/edit/actions.py:162 -msgid "Could not paste node. It no longer exists." +#: kotti/templates/edit/contents.pt:42 +msgid "Visibility" msgstr "" -#: kotti/views/edit/actions.py:225 -#, python-format -msgid "${title} is now visible in the navigation." +#: kotti/templates/edit/contents.pt:75 +msgid "Visible" msgstr "" -#: kotti/views/edit/actions.py:228 -#, python-format -msgid "${title} is no longer visible in the navigation." +#: kotti/templates/edit/contents.pt:79 +msgid "Hidden" msgstr "" -#: kotti/views/edit/actions.py:293 -msgid "Nothing was deleted." +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" msgstr "" -#: kotti/views/edit/actions.py:329 kotti/views/edit/actions.py:356 -msgid "Name and title are required." +#: 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/views/edit/actions.py:333 -msgid "Item was renamed." +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +msgid "New name" msgstr "" -#: kotti/views/edit/actions.py:455 -msgid "Move up" +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:33 +msgid "New title" msgstr "" -#: kotti/views/edit/actions.py:456 -msgid "Move down" +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" msgstr "" -#: kotti/views/edit/actions.py:457 -msgid "Show" +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" msgstr "" -#: kotti/views/edit/actions.py:458 -msgid "Hide" +#: kotti/templates/edit/share.pt:42 +msgid "Assign local roles" msgstr "" -#: kotti/views/edit/actions.py:497 -msgid "You have to select items to perform an action." +#: kotti/templates/edit/change-state.pt:8 +msgid "Change workflow state" msgstr "" -#: kotti/views/edit/actions.py:454 -msgid "Change State" +#: kotti/templates/edit/change-state.pt:12 +msgid "An item's workflow state determines who can see, edit and/or manage it." msgstr "" -#: kotti/views/edit/content.py:36 -msgid "Description" +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:45 +msgid "Include children" msgstr "" -#: kotti/views/edit/content.py:42 -msgid "Tags" +#: 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/views/edit/content.py:51 -msgid "Body" +#: kotti/templates/edit/change-state.pt:50 +msgid "Change state to" msgstr "" -#: kotti/views/edit/default_views.py:99 -msgid "Default view has been reset to default." +#: kotti/templates/edit/change-state.pt:51 +msgid "Select the new state where all chosen items should be set." msgstr "" -#: kotti/views/edit/default_views.py:78 -msgid "Default view" +#: kotti/templates/edit/change-state.pt:55 +msgid "No change" msgstr "" -#: kotti/views/edit/default_views.py:106 -msgid "Default view has been set." +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." msgstr "" -#: kotti/views/edit/default_views.py:111 -msgid "Default view could not be set." +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." msgstr "" diff --git a/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po b/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po index 369507a06..75c59cc6b 100644 --- a/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po +++ b/kotti/locale/fr_FR/LC_MESSAGES/Kotti.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Kotti 0.9a3dev\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2015-02-22 11:34+0100\n" +"POT-Creation-Date: 2015-09-29 14:24+0100\n" "PO-Revision-Date: 2013-09-22 20:21+0100\n" "Last-Translator: Fabien Castarède \n" "Language-Team: French\n" @@ -18,6 +18,81 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n > 1)\n" "Generated-By: Babel 0.9.6\n" +#: kotti/security.py:190 +msgid "Viewer" +msgstr "Lecteur" + +#: kotti/security.py:191 +msgid "Editor" +msgstr "Editeur" + +#: kotti/security.py:192 +msgid "Owner" +msgstr "Propriétaire" + +#: kotti/security.py:193 +msgid "Admin" +msgstr "Administrateur" + +#: kotti/resources.py:508 kotti/views/edit/actions.py:456 +msgid "Copy" +msgstr "Copier" + +#: kotti/resources.py:509 kotti/views/edit/actions.py:457 +msgid "Cut" +msgstr "Couper" + +#: kotti/resources.py:510 kotti/views/edit/actions.py:453 +msgid "Paste" +msgstr "Coller" + +#: kotti/resources.py:511 kotti/views/edit/actions.py:458 +#: kotti/templates/edit/rename.pt:31 kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:40 +msgid "Rename" +msgstr "Renommer" + +#: kotti/resources.py:512 kotti/views/users.py:443 +#: kotti/views/edit/actions.py:460 +#: kotti/templates/site-setup/delete-user.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:50 kotti/templates/edit/delete.pt:24 +msgid "Delete" +msgstr "Supprimer" + +#: kotti/resources.py:637 kotti/views/edit/content.py:85 +msgid "Document" +msgstr "Document" + +#: kotti/resources.py:726 kotti/views/edit/content.py:113 +#: kotti/views/edit/content.py:66 +msgid "File" +msgstr "Fichier" + +#: kotti/resources.py:772 kotti/views/edit/content.py:141 +msgid "Image" +msgstr "Image" + +#: kotti/resources.py:529 +msgid "Folder view" +msgstr "Contenus" + +#: kotti/resources.py:523 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "Contenus" + +#: kotti/resources.py:524 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "Modifier" + +#: kotti/resources.py:525 +msgid "Share" +msgstr "Permissions" + +#: kotti/resources.py:526 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "Actions" + #: kotti/populate.py:64 msgid "Welcome to Kotti" msgstr "Bienvenue dans Kotti" @@ -27,34 +102,30 @@ msgid "Congratulations! You have successfully installed Kotti." msgstr "Félicitations! Vous avez installé Kotti avec succès." #: kotti/populate.py:66 -#, c-format +#, 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=\"http://kotti.readthedocs.org/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -62,13 +133,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,9 +146,9 @@ 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" " 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" +" 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" +" 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" +" 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" +" href=\"http://kotti.readthedocs.org/en/latest/developing/basic/configuration.html\">\n" " Configuration manuelle\n" " \n" "

    \n" @@ -124,13 +188,11 @@ msgstr "" "
    \n" "

    Extensions

    \n" "

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

    \n" "

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

    \n" @@ -138,8 +200,7 @@ msgstr "" "
    \n" "

    Documentation

    \n" "

    \n" -" Découvrez tout ce que vous pouvez faire avec Kotti? La License " -"de Kotti?Parcourez le\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" @@ -151,25 +212,21 @@ msgstr "" "

    \n" "
    \n" -#: kotti/populate.py:121 +#: kotti/populate.py:123 msgid "About" 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 "" -"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:124 +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:125 +#, fuzzy msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -186,20 +243,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" " \"Cinq\n" "

    \n" "\n" @@ -217,618 +272,180 @@ msgstr "" "\n" "

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

    \n" -#: kotti/resources.py:627 kotti/views/edit/content.py:82 -msgid "Document" -msgstr "Document" - -#: kotti/resources.py:666 kotti/views/edit/content.py:110 -#: kotti/views/edit/content.py:63 -msgid "File" -msgstr "Fichier" +#: kotti/views/form.py:79 kotti/views/users.py:69 +#: kotti/views/edit/actions.py:374 kotti/views/edit/actions.py:418 +msgid "Your changes have been saved." +msgstr "Vos modifications ont été enregistrées." -#: kotti/resources.py:753 kotti/views/edit/content.py:138 -msgid "Image" -msgstr "Image" +#: kotti/views/form.py:174 +msgid "Item was added." +msgstr "L'élément a été ajouté." -#: kotti/resources.py:517 -msgid "Folder view" -msgstr "Contenus" +#: kotti/views/form.py:156 kotti/templates/site-setup/user.pt:21 +#, python-format +msgid "Edit ${title}" +msgstr "Modifier ${title}" -#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 -msgid "Contents" -msgstr "Contenus" +#: kotti/views/form.py:264 +#, python-format +msgid "Maximum file size: ${size}MB" +msgstr "Taille maximum d'un fichier : ${size} MB" -#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" -msgstr "Modifier" +#: kotti/views/form.py:77 kotti/views/users.py:441 +msgid "Save" +msgstr "Enregistrer" -#: kotti/resources.py:506 -msgid "Share" -msgstr "Permissions" +#: kotti/views/form.py:78 kotti/views/users.py:316 kotti/views/users.py:350 +#: kotti/views/users.py:442 kotti/templates/edit/delete-nodes.pt:45 +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:66 +#: kotti/templates/edit/rename-nodes.pt:42 +msgid "Cancel" +msgstr "Annuler" -#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" -msgstr "Actions" +#: kotti/views/form.py:198 +#, python-format +msgid "Add ${type} to ${title}." +msgstr "Ajouter un(e) ${type} dans ${title}." -#: kotti/resources.py:508 kotti/views/edit/actions.py:446 -msgid "Copy" -msgstr "Copier" +#: kotti/views/form.py:202 +#, python-format +msgid "Add ${type}." +msgstr "Ajouter ${type}." -#: kotti/resources.py:509 kotti/views/edit/actions.py:447 -msgid "Cut" -msgstr "Couper" +#: kotti/views/site_setup.py:6 kotti/views/users.py:378 +msgid "User Management" +msgstr "Gestion des utilisateurs" -#: kotti/resources.py:510 kotti/views/edit/actions.py:443 -msgid "Paste" -msgstr "Coller" +#: kotti/views/login.py:237 +msgid "You have reset your password." +msgstr "Vous avez réinitialisé votre mot de passe." -#: kotti/resources.py:511 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:448 -msgid "Rename" -msgstr "Renommer" +#: kotti/views/login.py:199 +msgid "You have been logged out." +msgstr "Vous avez été déconnecté." -#: kotti/resources.py:512 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:450 -msgid "Delete" -msgstr "Supprimer" +#: kotti/views/login.py:65 kotti/views/users.py:270 +msgid "Full name" +msgstr "Nom complet" -#: kotti/security.py:189 -msgid "Viewer" -msgstr "Lecteur" +#: kotti/views/login.py:68 +msgid "Username" +msgstr "Identifiant" -#: kotti/security.py:190 -msgid "Editor" -msgstr "Editeur" +#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 +msgid "Email" +msgstr "E-mail" -#: kotti/security.py:191 -msgid "Owner" -msgstr "Propriétaire" +#: kotti/views/login.py:108 +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/security.py:192 -msgid "Admin" -msgstr "Administrateur" +#: kotti/views/login.py:123 +#, python-format +msgid "Register - ${title}" +msgstr "S'inscrire - ${title}" -#: kotti/templates/add-dropdown.pt:5 -msgid "Add" -msgstr "Ajouter" +#: kotti/views/login.py:163 +msgid "Login failed." +msgstr "La connexion à échouée." -#: kotti/templates/add-dropdown.pt:27 -msgid "Upload Content" -msgstr "Téléverser des contenus" +#: kotti/views/login.py:212 kotti/views/users.py:212 +#: kotti/templates/login.pt:25 +msgid "Password" +msgstr "Mot de passe" -#: kotti/templates/default-view-selector.pt:5 -msgid "Set default view" -msgstr "Définir la vue par défaut" +#: kotti/views/login.py:285 +#, python-format +msgid "Reset your password - ${title}." +msgstr "Réinitialiser votre mot de passe - ${title}" -#: kotti/templates/editor-bar.pt:35 -msgid "View" -msgstr "Voir" +#: kotti/views/login.py:159 +#, python-format +msgid "Welcome, ${user}!" +msgstr "Bienvenue, ${user} !" -#: kotti/templates/editor-bar.pt:76 -msgid "Navigate" -msgstr "Parcourir" +#: kotti/views/login.py:172 +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/templates/editor-bar.pt:98 -msgid "Preferences" -msgstr "Préférences" +#: kotti/views/login.py:177 +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/templates/editor-bar.pt:109 -msgid "Site Setup" -msgstr "Paramètres du Site" +#: kotti/views/login.py:82 +msgid "Register" +msgstr "S'inscrire" -#: kotti/templates/editor-bar.pt:131 -msgid "Logout" -msgstr "Se déconnecter" +#: kotti/views/login.py:89 kotti/views/login.py:256 +msgid "There was an error." +msgstr "Il y a une erreur." -#: kotti/templates/email-reset-password.pt:3 -msgid "Reset your password for ${site_title}." -msgstr "Réinitialiser votre mot de passe pour ${site_title}." +#: kotti/views/login.py:249 +msgid "Submit" +msgstr "Soumettre" -#: kotti/templates/email-reset-password.pt:7 -#: kotti/templates/email-set-password.pt:6 -msgid "Hello, ${user_title}!" -msgstr "Bonjour, ${user_title} !" +#: kotti/views/login.py:278 +msgid "Your password reset token may have expired." +msgstr "Votre jeton de réinitialisation de votre mot de passe est expiré." -#: kotti/templates/email-reset-password.pt:10 -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}" +#: kotti/views/users.py:313 kotti/views/users.py:501 kotti/views/users.py:536 +#: kotti/templates/site-setup/users.pt:79 kotti/templates/edit/share.pt:59 +msgid "User" +msgstr "Utilisateur" -#: kotti/templates/email-set-password.pt:3 -msgid "Your registration for ${site_title}" -msgstr "Votre inscription sur ${site_title}" +#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 +#: kotti/views/users.py:536 kotti/templates/site-setup/users.pt:80 +#: kotti/templates/edit/share.pt:60 +msgid "Group" +msgstr "Groupe" -#: kotti/templates/email-set-password.pt:9 -msgid "You are joining ${site_title}." -msgstr "Vous vous joignez à ${site_title}." +#: 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/templates/email-set-password.pt:12 -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}" +#: kotti/views/users.py:587 +#, python-format +msgid "My preferences - ${title}" +msgstr "Mes préférences - ${title}" -#: kotti/templates/forbidden.pt:7 -msgid "Forbidden" -msgstr "Interdit" +#: kotti/views/users.py:145 +msgid "Invalid value" +msgstr "Valeur invalide" -#: kotti/templates/login.pt:16 -msgid "Login" -msgstr "Identifiant" +#: kotti/views/users.py:151 +msgid "A user with that name already exists." +msgstr "Un utilisateur avec ce nom existe déjà." -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 -msgid "Username or email" -msgstr "Identifiant ou e-mail" +#: kotti/views/users.py:158 +msgid "A user with that email already exists." +msgstr "Un utilisateur avec cette adresse e-mail existe déjà." -#: kotti/templates/login.pt:34 kotti/views/login.py:212 -#: kotti/views/users.py:212 -msgid "Password" -msgstr "Mot de passe" +#: kotti/views/users.py:181 +#, python-format +msgid "No such group: ${group}" +msgstr "Aucun groupe : ${group}" -#: kotti/templates/login.pt:47 -msgid "Log in" -msgstr "Se connecter" +#: kotti/views/users.py:196 kotti/views/edit/content.py:33 +#: kotti/templates/view/folder.pt:20 kotti/templates/edit/delete-nodes.pt:22 +#: kotti/templates/edit/contents.pt:39 kotti/templates/edit/change-state.pt:18 +msgid "Title" +msgstr "Titre" -#: kotti/templates/login.pt:65 -msgid "Forgot your password?" -msgstr "Mot de passe oublié ?" +#: kotti/views/users.py:207 kotti/templates/site-setup/users.pt:68 +#: kotti/templates/edit/share.pt:48 +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" - -#. 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 "" -"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/templates/login.pt:118 -msgid "Not registered yet?" -msgstr "" - -#: kotti/templates/login.pt:127 -msgid "Register for an account on this site." -msgstr "Enregistrez-vous sur ce site." - -#: kotti/templates/workflow-dropdown.pt:19 -msgid "Make ${state}" -msgstr "Rendre ${state}" - -#: kotti/templates/edit/breadcrumbs.pt:5 -msgid "You are here:" -msgstr "Vous êtes ici :" - -#: kotti/templates/edit/change-state.pt:7 -msgid "Change workflow state" -msgstr "Modifier le statut de workflow" - -#: kotti/templates/edit/change-state.pt:14 -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." - -#: 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:32 -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 -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 -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 -msgid "Modification Date" -msgstr "Date de modification" - -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 -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:100 -msgid "Change state to" -msgstr "Modifier le statut à" - -#: kotti/templates/edit/change-state.pt:103 -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 -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 -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-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-nodes.pt:35 kotti/templates/edit/rename.pt:26 -msgid "New name" -msgstr "Nouveau nom" - -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 -msgid "New title" -msgstr "Nouveau titre" - -#: kotti/templates/edit/rename.pt:7 -msgid "Rename ${title}" -msgstr "Renommer ${title}" - -#: kotti/templates/edit/share.pt:7 -msgid "Share ${title}" -msgstr "Permissions pour ${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 "Rechercher des utilisateurs ou des groupes" - -#: kotti/templates/edit/share.pt:43 kotti/templates/site-setup/users.pt:81 -msgid "Search" -msgstr "Rechercher" - -#: kotti/templates/edit/share.pt:60 -msgid "Assign local roles" -msgstr "Assigner des rôles locaux" - -#: 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/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/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/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 "Téléverser des contenus à partir de fichiers locaux" - -#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:164 -msgid "Filename" -msgstr "Nom du fichier" - -#: kotti/templates/edit/upload.pt:26 kotti/templates/edit/upload.pt:170 -msgid "Size" -msgstr "Poids" - -#: kotti/templates/edit/upload.pt:29 -msgid "Status" -msgstr "Statut" - -#: kotti/templates/edit/upload.pt:32 -msgid "Progress" -msgstr "Progression" - -#: kotti/templates/edit/upload.pt:124 -msgid "Select file(s) to upload..." -msgstr "Selectionner des fichiers à téléverser..." - -#: kotti/templates/edit/upload.pt:134 -msgid "Upload ${number} files." -msgstr "Téléverser ${number} fichiers." - -#: kotti/templates/edit/upload.pt:144 -msgid "Dismiss all errors" -msgstr "Ignorer toutes les erreurs" - -#: kotti/templates/site-setup/delete-user.pt:20 -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 -msgid "Back to User Management" -msgstr "Retour à la gestion des Utilisateurs" - -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 -#, python-format -msgid "Edit ${title}" -msgstr "Modifier ${title}" - -#: kotti/templates/site-setup/users.pt:15 -msgid "Search user(s) / group(s)" -msgstr "Rechercher des utilisateurs ou des groupes" - -#: kotti/templates/site-setup/users.pt:24 -msgid "Add user" -msgstr "Ajouter un utilisateur" - -#: kotti/templates/site-setup/users.pt:33 -msgid "Add group" -msgstr "Ajouter un groupe" - -#: kotti/templates/site-setup/users.pt:52 -msgid "Find users or groups" -msgstr "Rechercher des utilisateurs ou des groupes" - -#: kotti/templates/site-setup/users.pt:63 -msgid "User- / groupname" -msgstr "Utilisateur / groupe" - -#: kotti/templates/site-setup/users.pt:70 -msgid "Blank search text finds all." -msgstr "" -"Rechercher et modifier des utilisateurs (Laissez le champ vide pour les " -"afficher tous)." - -#: kotti/templates/site-setup/users.pt:96 -msgid "Assign global roles" -msgstr "Assigner des rôles locaux" - -#: kotti/templates/site-setup/users.pt:180 -msgid "Add new user" -msgstr "Ajouter un utilisateur" - -#: kotti/templates/site-setup/users.pt:190 -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:364 kotti/views/edit/actions.py:408 -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:237 -msgid "You have reset your password." -msgstr "Vous avez réinitialisé votre mot de passe." - -#: kotti/views/login.py:199 -msgid "You have been logged out." -msgstr "Vous avez été déconnecté." - -#: kotti/views/login.py:65 kotti/views/users.py:270 -msgid "Full name" -msgstr "Nom complet" - -#: kotti/views/login.py:68 -msgid "Username" -msgstr "Identifiant" - -#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 -msgid "Email" -msgstr "E-mail" - -#: kotti/views/login.py:108 -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:123 -#, python-format -msgid "Register - ${title}" -msgstr "S'inscrire - ${title}" - -#: kotti/views/login.py:163 -msgid "Login failed." -msgstr "La connexion à échouée." - -#: kotti/views/login.py:285 -#, python-format -msgid "Reset your password - ${title}." -msgstr "Réinitialiser votre mot de passe - ${title}" - -#: kotti/views/login.py:159 -#, python-format -msgid "Welcome, ${user}!" -msgstr "Bienvenue, ${user} !" - -#: kotti/views/login.py:172 -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:177 -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:82 -msgid "Register" -msgstr "S'inscrire" - -#: kotti/views/login.py:89 kotti/views/login.py:256 -msgid "There was an error." -msgstr "Il y a une erreur." - -#: kotti/views/login.py:249 -msgid "Submit" -msgstr "Soumettre" - -#: kotti/views/login.py:278 -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:587 -#, python-format -msgid "My preferences - ${title}" -msgstr "Mes préférences - ${title}" - -#: kotti/views/users.py:145 -msgid "Invalid value" -msgstr "Valeur invalide" - -#: kotti/views/users.py:151 -msgid "A user with that name already exists." -msgstr "Un utilisateur avec ce nom existe déjà." - -#: kotti/views/users.py:158 -msgid "A user with that email already exists." -msgstr "Un utilisateur avec cette adresse e-mail existe déjà." - -#: kotti/views/users.py:181 -#, python-format -msgid "No such group: ${group}" -msgstr "Aucun groupe : ${group}" - -#: kotti/views/users.py:219 -msgid "Active" -msgstr "Actif" +#: kotti/views/users.py:219 +msgid "Active" +msgstr "Actif" #: kotti/views/users.py:220 msgid "Untick this to deactivate the account." @@ -856,8 +473,8 @@ msgid "Add Group" msgstr "Ajouter un Groupe" #: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 -#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 +#: kotti/views/edit/actions.py:312 kotti/views/edit/actions.py:378 +#: kotti/views/edit/actions.py:424 kotti/views/edit/actions.py:420 msgid "No changes were made." msgstr "Aucune modification a été effectuée." @@ -907,11 +524,6 @@ msgstr "${title} a été coupé." msgid "${title} was moved." msgstr "${title} a été déplacé." -#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 -#, python-format -msgid "${title} was deleted." -msgstr "${title} a été supprimé." - #: kotti/views/edit/actions.py:159 #, python-format msgid "${title} was pasted." @@ -931,70 +543,411 @@ msgstr "${title} est désormais visible dans la navigation." msgid "${title} is no longer visible in the navigation." msgstr "${title} nest plus visible dans la navigation." -#: kotti/views/edit/actions.py:293 +#: kotti/views/edit/actions.py:277 kotti/views/edit/actions.py:306 +#, python-format +msgid "${title} was deleted." +msgstr "${title} a été supprimé." + +#: kotti/views/edit/actions.py:303 msgid "Nothing was deleted." msgstr "Rien n'a été supprimé." -#: kotti/views/edit/actions.py:329 kotti/views/edit/actions.py:356 +#: kotti/views/edit/actions.py:339 kotti/views/edit/actions.py:366 msgid "Name and title are required." msgstr "Le nom et le titre sont obligatoires." -#: kotti/views/edit/actions.py:333 +#: kotti/views/edit/actions.py:343 msgid "Item was renamed." msgstr "L'élément a été renommé." -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:465 msgid "Move up" msgstr "Décaler vers le haut" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:466 msgid "Move down" msgstr "Décaler vers le bas" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:467 msgid "Show" msgstr "Rendre visible" -#: kotti/views/edit/actions.py:458 +#: kotti/views/edit/actions.py:468 msgid "Hide" msgstr "Cacher" -#: kotti/views/edit/actions.py:497 +#: kotti/views/edit/actions.py:507 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:454 +#: kotti/views/edit/actions.py:464 msgid "Change State" msgstr "Modifier le statut" -#: kotti/views/edit/content.py:36 +#: kotti/views/edit/content.py:38 msgid "Description" msgstr "Description" -#: kotti/views/edit/content.py:42 +#: kotti/views/edit/content.py:44 msgid "Tags" msgstr "Mots-clés" -#: kotti/views/edit/content.py:51 +#: kotti/views/edit/content.py:53 msgid "Body" msgstr "Contenu" -#: kotti/views/edit/default_views.py:99 +#: kotti/views/edit/default_views.py:100 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 +#: kotti/views/edit/default_views.py:79 msgid "Default view" msgstr "Vue par défaut" -#: kotti/views/edit/default_views.py:106 +#: kotti/views/edit/default_views.py:107 msgid "Default view has been set." msgstr "La vue par défaut a été définie." -#: kotti/views/edit/default_views.py:111 +#: kotti/views/edit/default_views.py:112 msgid "Default view could not be set." msgstr "La vue par défaut ne peut pas être définie." +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "Interdit" + +#: 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:4 +#: kotti/templates/email-set-password.pt:4 +msgid "Hello, ${user_title}!" +msgstr "Bonjour, ${user_title} !" + +#: 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}" + +#: kotti/templates/add-dropdown.pt:3 +msgid "Add" +msgstr "Ajouter" + +#: kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" +msgstr "Téléverser des contenus" + +#: 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:5 +msgid "You are joining ${site_title}." +msgstr "Vous vous joignez à ${site_title}." + +#: 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}" + +#: kotti/templates/editor-bar.pt:21 +msgid "View" +msgstr "Voir" + +#: kotti/templates/editor-bar.pt:44 +msgid "Navigate" +msgstr "Parcourir" + +#: kotti/templates/editor-bar.pt:55 +msgid "Preferences" +msgstr "Préférences" + +#: kotti/templates/editor-bar.pt:60 +msgid "Site Setup" +msgstr "Paramètres du Site" + +#: kotti/templates/editor-bar.pt:71 +msgid "Logout" +msgstr "Se déconnecter" + +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "Rendre ${state}" + +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "Définir la vue par défaut" + +#: kotti/templates/login.pt:15 +msgid "Login" +msgstr "Identifiant" + +#: kotti/templates/login.pt:20 kotti/templates/login.pt:57 +msgid "Username or email" +msgstr "Identifiant ou e-mail" + +#: kotti/templates/login.pt:33 +msgid "Log in" +msgstr "Se connecter" + +#: kotti/templates/login.pt:43 +msgid "Forgot your password?" +msgstr "Mot de passe oublié ?" + +#. 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:50 kotti/templates/login.pt:65 +msgid "Reset password" +msgstr "Réinitialiser le mot de passe" + +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:48 +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/templates/login.pt:76 +msgid "Not registered yet?" +msgstr "" + +#: kotti/templates/login.pt:82 +msgid "Register for an account on this site." +msgstr "Enregistrez-vous sur ce site." + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "Mots-clés associés :" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "Rechercher..." + +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:67 +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:112 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:40 +#: kotti/templates/edit/share.pt:47 kotti/templates/edit/change-state.pt:19 +msgid "Type" +msgstr "Type" + +#: kotti/templates/view/folder.pt:22 kotti/templates/edit/delete-nodes.pt:25 +#: kotti/templates/edit/contents.pt:43 kotti/templates/edit/change-state.pt:21 +msgid "Creation Date" +msgstr "Date de création" + +#: kotti/templates/view/folder.pt:23 kotti/templates/edit/delete-nodes.pt:26 +#: kotti/templates/edit/contents.pt:44 kotti/templates/edit/change-state.pt:22 +msgid "Modification Date" +msgstr "Date de modification" + +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "Résultats de la recherche" + +#: kotti/templates/http-errors/notfound.pt:6 +msgid "Not Found" +msgstr "" + +#: kotti/templates/http-errors/notfound.pt:7 +msgid "The resource could not be found" +msgstr "" + +#: kotti/templates/site-setup/delete-user.pt:9 +#: kotti/templates/edit/delete.pt:9 +msgid "Delete ${title}" +msgstr "Supprimer ${title}" + +#: 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:12 +msgid "Back to User Management" +msgstr "Retour à la gestion des Utilisateurs" + +#: 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:17 +msgid "Add user" +msgstr "Ajouter un utilisateur" + +#: kotti/templates/site-setup/users.pt:22 +msgid "Add group" +msgstr "Ajouter un groupe" + +#: 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:39 +msgid "User- / groupname" +msgstr "Utilisateur / groupe" + +#: kotti/templates/site-setup/users.pt:47 kotti/templates/edit/share.pt:14 +#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:26 +msgid "Search users and groups" +msgstr "Rechercher des utilisateurs ou des groupes" + +#: kotti/templates/site-setup/users.pt:49 +msgid "Blank search text finds all." +msgstr "Rechercher et modifier des utilisateurs (Laissez le champ vide pour les afficher tous)." + +#: kotti/templates/site-setup/users.pt:55 kotti/templates/edit/share.pt:33 +msgid "Search" +msgstr "Rechercher" + +#: kotti/templates/site-setup/users.pt:63 +msgid "Assign global roles" +msgstr "Assigner des rôles locaux" + +#: kotti/templates/site-setup/users.pt:83 kotti/templates/edit/share.pt:63 +msgid "Gravatar" +msgstr "Gravatar" + +#: kotti/templates/site-setup/users.pt:105 kotti/templates/edit/share.pt:83 +#: kotti/templates/edit/change-state.pt:64 +msgid "Apply changes" +msgstr "Appliquer les modifications" + +#: kotti/templates/site-setup/users.pt:115 +msgid "Add new user" +msgstr "Ajouter un utilisateur" + +#: kotti/templates/site-setup/users.pt:121 +msgid "Add new group" +msgstr "Ajouter un groupe" + +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "Parcourir le Site" + +#: 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/upload.pt:18 kotti/templates/edit/upload.pt:111 +msgid "Filename" +msgstr "Nom du fichier" + +#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:113 +msgid "Size" +msgstr "Poids" + +#: kotti/templates/edit/upload.pt:21 +msgid "Status" +msgstr "Statut" + +#: kotti/templates/edit/upload.pt:22 +msgid "Progress" +msgstr "Progression" + +#: kotti/templates/edit/upload.pt:88 +msgid "Select file(s) to upload..." +msgstr "Selectionner des fichiers à téléverser..." + +#: kotti/templates/edit/upload.pt:96 +msgid "Upload ${number} files." +msgstr "Téléverser ${number} fichiers." + +#: kotti/templates/edit/upload.pt:101 +msgid "Dismiss all errors" +msgstr "Ignorer toutes les erreurs" + +#: kotti/templates/edit/delete-nodes.pt:14 +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-nodes.pt:24 kotti/templates/edit/contents.pt:41 +#: kotti/templates/edit/change-state.pt:20 +msgid "State" +msgstr "Statut" + +#: kotti/templates/edit/breadcrumbs.pt:8 +msgid "You are here:" +msgstr "Vous êtes ici :" + +#: kotti/templates/edit/contents.pt:30 +msgid "No content items are contained here." +msgstr "Aucun contenu ici." + +#: kotti/templates/edit/contents.pt:42 +msgid "Visibility" +msgstr "Visibilité" + +#: kotti/templates/edit/contents.pt:75 +msgid "Visible" +msgstr "Visible" + +#: kotti/templates/edit/contents.pt:79 +msgid "Hidden" +msgstr "Caché" + +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "Renommer ${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 "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:19 kotti/templates/edit/rename-nodes.pt:26 +msgid "New name" +msgstr "Nouveau nom" + +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:33 +msgid "New title" +msgstr "Nouveau titre" + +#: 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/share.pt:8 +msgid "Share ${title}" +msgstr "Permissions pour ${title}" + +#: kotti/templates/edit/share.pt:42 +msgid "Assign local roles" +msgstr "Assigner des rôles locaux" + +#: kotti/templates/edit/change-state.pt:8 +msgid "Change workflow state" +msgstr "Modifier le statut de workflow" + +#: 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." + +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:45 +msgid "Include children" +msgstr "Inclure les contenus 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:50 +msgid "Change state to" +msgstr "Modifier le statut à" + +#: kotti/templates/edit/change-state.pt:51 +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:55 +msgid "No change" +msgstr "Aucune modification" + +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "Bienvenue, ${user} ! Vous êtes connecté." + +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "Bienvenue, vous n'êtes pas connecté." + #~ msgid "Private" #~ msgstr "Privé" diff --git a/kotti/locale/it/LC_MESSAGES/Kotti.po b/kotti/locale/it/LC_MESSAGES/Kotti.po index a70a09c98..07c69f20b 100644 --- a/kotti/locale/it/LC_MESSAGES/Kotti.po +++ b/kotti/locale/it/LC_MESSAGES/Kotti.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Kotti 0.9.2dev\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2015-02-22 11:33+0100\n" +"POT-Creation-Date: 2015-09-29 14:24+0100\n" "PO-Revision-Date: 2014-01-19 11:14+0100\n" "Last-Translator: Xavi Torné \n" "Language-Team: it \n" @@ -17,6 +17,81 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "Generated-By: Babel 0.9.6\n" +#: kotti/security.py:190 +msgid "Viewer" +msgstr "Visualizzatore" + +#: kotti/security.py:191 +msgid "Editor" +msgstr "Editor" + +#: kotti/security.py:192 +msgid "Owner" +msgstr "Proprietario" + +#: kotti/security.py:193 +msgid "Admin" +msgstr "Admin" + +#: kotti/resources.py:508 kotti/views/edit/actions.py:456 +msgid "Copy" +msgstr "Copia" + +#: kotti/resources.py:509 kotti/views/edit/actions.py:457 +msgid "Cut" +msgstr "Taglia" + +#: kotti/resources.py:510 kotti/views/edit/actions.py:453 +msgid "Paste" +msgstr "Incolla" + +#: kotti/resources.py:511 kotti/views/edit/actions.py:458 +#: kotti/templates/edit/rename.pt:31 kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:40 +msgid "Rename" +msgstr "Rinomina" + +#: kotti/resources.py:512 kotti/views/users.py:443 +#: kotti/views/edit/actions.py:460 +#: kotti/templates/site-setup/delete-user.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:50 kotti/templates/edit/delete.pt:24 +msgid "Delete" +msgstr "Elimina" + +#: kotti/resources.py:637 kotti/views/edit/content.py:85 +msgid "Document" +msgstr "Documento" + +#: kotti/resources.py:726 kotti/views/edit/content.py:113 +#: kotti/views/edit/content.py:66 +msgid "File" +msgstr "File" + +#: kotti/resources.py:772 kotti/views/edit/content.py:141 +msgid "Image" +msgstr "Immagine" + +#: kotti/resources.py:529 +msgid "Folder view" +msgstr "Vista della cartella" + +#: kotti/resources.py:523 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "Contenuti" + +#: kotti/resources.py:524 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "Modificare" + +#: kotti/resources.py:525 +msgid "Share" +msgstr "Condividi" + +#: kotti/resources.py:526 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "Azioni" + #: kotti/populate.py:64 msgid "Welcome to Kotti" msgstr "Benvenuto in Kotti" @@ -31,29 +106,25 @@ 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=\"http://kotti.readthedocs.org/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -61,13 +132,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,9 +145,9 @@ 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" " 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=\"http://kotti.readthedocs.org/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,8 +195,7 @@ 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" @@ -146,25 +205,20 @@ msgstr "" "

    \n" "
    \n" -#: kotti/populate.py:121 +#: kotti/populate.py:123 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:124 +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:125 msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -181,476 +235,17 @@ 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:627 kotti/views/edit/content.py:82 -msgid "Document" -msgstr "Documento" - -#: kotti/resources.py:666 kotti/views/edit/content.py:110 -#: kotti/views/edit/content.py:63 -msgid "File" -msgstr "File" - -#: kotti/resources.py:753 kotti/views/edit/content.py:138 -msgid "Image" -msgstr "Immagine" - -#: kotti/resources.py:517 -msgid "Folder view" -msgstr "Vista della cartella" - -#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 -msgid "Contents" -msgstr "Contenuti" - -#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" -msgstr "Modificare" - -#: kotti/resources.py:506 -msgid "Share" -msgstr "Condividi" - -#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" -msgstr "Azioni" - -#: kotti/resources.py:508 kotti/views/edit/actions.py:446 -msgid "Copy" -msgstr "Copia" - -#: kotti/resources.py:509 kotti/views/edit/actions.py:447 -msgid "Cut" -msgstr "Taglia" - -#: kotti/resources.py:510 kotti/views/edit/actions.py:443 -msgid "Paste" -msgstr "Incolla" - -#: kotti/resources.py:511 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:448 -msgid "Rename" -msgstr "Rinomina" - -#: kotti/resources.py:512 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:450 -msgid "Delete" -msgstr "Elimina" - -#: kotti/security.py:189 -msgid "Viewer" -msgstr "Visualizzatore" - -#: kotti/security.py:190 -msgid "Editor" -msgstr "Editor" - -#: kotti/security.py:191 -msgid "Owner" -msgstr "Proprietario" - -#: kotti/security.py:192 -msgid "Admin" -msgstr "Admin" - -#: kotti/templates/add-dropdown.pt:5 -msgid "Add" -msgstr "Aggiungere" - -#: kotti/templates/add-dropdown.pt:27 -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/email-reset-password.pt:3 -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 -msgid "Hello, ${user_title}!" -msgstr "Ciao, ${user_title}!" - -#: kotti/templates/email-reset-password.pt:10 -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 -msgid "Your registration for ${site_title}" -msgstr "La tua registrazione per ${site_title}" - -#: kotti/templates/email-set-password.pt:9 -msgid "You are joining ${site_title}." -msgstr "Stai entrando in ${site_title}." - -#: kotti/templates/email-set-password.pt:12 -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/login.pt:16 -msgid "Login" -msgstr "Accedi" - -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 -msgid "Username or email" -msgstr "Username o email" - -#: kotti/templates/login.pt:34 kotti/views/login.py:212 -#: kotti/views/users.py:212 -msgid "Password" -msgstr "Password" - -#: kotti/templates/login.pt:47 -msgid "Log in" -msgstr "Accedi" - -#: kotti/templates/login.pt:65 -msgid "Forgot your password?" -msgstr "Hai dimenticato la tua password?" - -#. 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" - -#. 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/login.pt:118 -msgid "Not registered yet?" -msgstr "Non sei ancora registrato?" - -#: kotti/templates/login.pt:127 -msgid "Register for an account on this site." -msgstr "Registrati per avere un account su questo sito." - -#: kotti/templates/workflow-dropdown.pt:19 -msgid "Make ${state}" -msgstr "Rendi ${state}" - -#: kotti/templates/edit/breadcrumbs.pt:5 -msgid "You are here:" -msgstr "Sei qua:" - -#: kotti/templates/edit/change-state.pt:7 -msgid "Change workflow state" -msgstr "Cambia lo stato del workflow" - -#: kotti/templates/edit/change-state.pt:14 -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." - -#: 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:32 -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 -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 -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 -msgid "Modification Date" -msgstr "Data di modifica" - -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 -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:100 -msgid "Change state to" -msgstr "Cambia stato a" - -#: kotti/templates/edit/change-state.pt:103 -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 -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 -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-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-nodes.pt:35 kotti/templates/edit/rename.pt:26 -msgid "New name" -msgstr "Nuovo nome" - -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 -msgid "New title" -msgstr "Nuovo titolo" - -#: kotti/templates/edit/rename.pt:7 -msgid "Rename ${title}" -msgstr "Rinomina ${title}" - -#: kotti/templates/edit/share.pt:7 -msgid "Share ${title}" -msgstr "Condividi ${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 "Cerca utenti e gruppi" - -#: 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 -msgid "Status" -msgstr "Stato" - -#: kotti/templates/edit/upload.pt:32 -msgid "Progress" -msgstr "Avanzamento" - -#: kotti/templates/edit/upload.pt:124 -msgid "Select file(s) to upload..." -msgstr "" - -#: kotti/templates/edit/upload.pt:134 -msgid "Upload ${number} files." -msgstr "" - -#: kotti/templates/edit/upload.pt:144 -msgid "Dismiss all errors" -msgstr "" - -#: kotti/templates/site-setup/delete-user.pt:20 -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 -msgid "Back to User Management" -msgstr "Torna alla gestione degli utenti" - -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 -#, python-format -msgid "Edit ${title}" -msgstr " Modificare ${title}" - -#: kotti/templates/site-setup/users.pt:15 -#, fuzzy -msgid "Search user(s) / group(s)" -msgstr "Cerca utente(i) / gruppo(i)" - -#: kotti/templates/site-setup/users.pt:24 -msgid "Add user" -msgstr "Aggiungi un utente" - -#: kotti/templates/site-setup/users.pt:33 -msgid "Add group" -msgstr "Aggiungi un gruppo" - -#: kotti/templates/site-setup/users.pt:52 -msgid "Find users or groups" -msgstr "Cerca utenti o gruppi" - -#: kotti/templates/site-setup/users.pt:63 -msgid "User- / groupname" -msgstr "" - -#: kotti/templates/site-setup/users.pt:70 -msgid "Blank search text finds all." -msgstr "Lascia il testo vuoto per trovare tutti gli utenti." - -#: kotti/templates/site-setup/users.pt:96 -msgid "Assign global roles" -msgstr "Assegna ruoli globali" - -#: kotti/templates/site-setup/users.pt:180 -msgid "Add new user" -msgstr "Aggiungi un nuovo utente" - -#: kotti/templates/site-setup/users.pt:190 -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:364 kotti/views/edit/actions.py:408 +#: kotti/views/edit/actions.py:374 kotti/views/edit/actions.py:418 msgid "Your changes have been saved." msgstr "Le tue modifiche sono state salvate." @@ -658,6 +253,11 @@ msgstr "Le tue modifiche sono state salvate." msgid "Item was added." msgstr "L'elemento è stato aggiunto." +#: kotti/views/form.py:156 kotti/templates/site-setup/user.pt:21 +#, python-format +msgid "Edit ${title}" +msgstr " Modificare ${title}" + #: kotti/views/form.py:264 #, python-format msgid "Maximum file size: ${size}MB" @@ -667,6 +267,13 @@ msgstr "Dimensione massima del file: ${size}MB" msgid "Save" msgstr "Salva" +#: kotti/views/form.py:78 kotti/views/users.py:316 kotti/views/users.py:350 +#: kotti/views/users.py:442 kotti/templates/edit/delete-nodes.pt:45 +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:66 +#: kotti/templates/edit/rename-nodes.pt:42 +msgid "Cancel" +msgstr "Cancella" + #: kotti/views/form.py:198 #, python-format msgid "Add ${type} to ${title}." @@ -677,6 +284,10 @@ msgstr "Aggiungere ${type} a ${title}." msgid "Add ${type}." msgstr "Aggiungere ${type}." +#: kotti/views/site_setup.py:6 kotti/views/users.py:378 +msgid "User Management" +msgstr "Gestione utenti" + #: kotti/views/login.py:237 msgid "You have reset your password." msgstr "Hai resettato la tua password." @@ -698,12 +309,8 @@ msgid "Email" msgstr "Email" #: kotti/views/login.py:108 -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." +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:123 #, python-format @@ -714,6 +321,11 @@ msgstr "Registrazione - ${title}" msgid "Login failed." msgstr "Errore nell'accesso." +#: kotti/views/login.py:212 kotti/views/users.py:212 +#: kotti/templates/login.pt:25 +msgid "Password" +msgstr "Password" + #: kotti/views/login.py:285 #, python-format msgid "Reset your password - ${title}." @@ -725,12 +337,8 @@ msgid "Welcome, ${user}!" msgstr "Benvenuto, ${user}!" #: kotti/views/login.py:172 -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." +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:177 msgid "That username or email is not known by this system." @@ -752,17 +360,20 @@ msgstr "Invia" 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:313 kotti/views/users.py:501 kotti/views/users.py:536 +#: kotti/templates/site-setup/users.pt:79 kotti/templates/edit/share.pt:59 +msgid "User" +msgstr "Utente" + +#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 +#: kotti/views/users.py:536 kotti/templates/site-setup/users.pt:80 +#: kotti/templates/edit/share.pt:60 +msgid "Group" +msgstr "Gruppo" #: 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." +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:587 #, python-format @@ -786,6 +397,17 @@ msgstr "Un utente con questa email già esiste." msgid "No such group: ${group}" msgstr "Gruppo non esistente: ${group}" +#: kotti/views/users.py:196 kotti/views/edit/content.py:33 +#: kotti/templates/view/folder.pt:20 kotti/templates/edit/delete-nodes.pt:22 +#: kotti/templates/edit/contents.pt:39 kotti/templates/edit/change-state.pt:18 +msgid "Title" +msgstr "Titolo" + +#: kotti/views/users.py:207 kotti/templates/site-setup/users.pt:68 +#: kotti/templates/edit/share.pt:48 +msgid "Name" +msgstr "Nome" + #: kotti/views/users.py:219 msgid "Active" msgstr "Attivo" @@ -816,8 +438,8 @@ msgid "Add Group" msgstr "Aggiungi gruppo" #: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 -#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 +#: kotti/views/edit/actions.py:312 kotti/views/edit/actions.py:378 +#: kotti/views/edit/actions.py:424 kotti/views/edit/actions.py:420 msgid "No changes were made." msgstr "Non sono state fatte modifiche." @@ -867,11 +489,6 @@ msgstr "${title} è stato tagliato." msgid "${title} was moved." msgstr "${title} è stato spostato." -#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 -#, python-format -msgid "${title} was deleted." -msgstr "${title} è stato cancellato." - #: kotti/views/edit/actions.py:159 #, python-format msgid "${title} was pasted." @@ -891,70 +508,412 @@ msgstr "${title} è ora visibile nella navigazione." msgid "${title} is no longer visible in the navigation." msgstr "${title} non è più visibile nella navigazione." -#: kotti/views/edit/actions.py:293 +#: kotti/views/edit/actions.py:277 kotti/views/edit/actions.py:306 +#, python-format +msgid "${title} was deleted." +msgstr "${title} è stato cancellato." + +#: kotti/views/edit/actions.py:303 msgid "Nothing was deleted." msgstr "Non è stato cancellato niente." -#: kotti/views/edit/actions.py:329 kotti/views/edit/actions.py:356 +#: kotti/views/edit/actions.py:339 kotti/views/edit/actions.py:366 msgid "Name and title are required." msgstr "Nome e titolo sono obbligatori." -#: kotti/views/edit/actions.py:333 +#: kotti/views/edit/actions.py:343 msgid "Item was renamed." msgstr "L'elemento è stato rinominato" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:465 msgid "Move up" msgstr "Muovere su" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:466 msgid "Move down" msgstr "Muovi giù" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:467 msgid "Show" msgstr "Mostra" -#: kotti/views/edit/actions.py:458 +#: kotti/views/edit/actions.py:468 msgid "Hide" msgstr "Nascondi" -#: kotti/views/edit/actions.py:497 +#: kotti/views/edit/actions.py:507 msgid "You have to select items to perform an action." msgstr "Devi selezionare degli elementi per fare quest'azione." -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:464 msgid "Change State" msgstr "Cambia stato" -#: kotti/views/edit/content.py:36 +#: kotti/views/edit/content.py:38 msgid "Description" msgstr "Descrizione" -#: kotti/views/edit/content.py:42 +#: kotti/views/edit/content.py:44 msgid "Tags" msgstr "Tags" -#: kotti/views/edit/content.py:51 +#: kotti/views/edit/content.py:53 msgid "Body" msgstr "Contenuto" -#: kotti/views/edit/default_views.py:99 +#: kotti/views/edit/default_views.py:100 msgid "Default view has been reset to default." msgstr "La vista predefinita è stata modificata a predefinita." -#: kotti/views/edit/default_views.py:78 +#: kotti/views/edit/default_views.py:79 msgid "Default view" msgstr "Vista predefinita" -#: kotti/views/edit/default_views.py:106 +#: kotti/views/edit/default_views.py:107 msgid "Default view has been set." msgstr "E' stata selezionata la vista predefinita." -#: kotti/views/edit/default_views.py:111 +#: kotti/views/edit/default_views.py:112 msgid "Default view could not be set." msgstr "La vista predefinita non può essere selezionata." +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "Vietato" + +#: 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:4 +#: kotti/templates/email-set-password.pt:4 +msgid "Hello, ${user_title}!" +msgstr "Ciao, ${user_title}!" + +#: 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/add-dropdown.pt:3 +msgid "Add" +msgstr "Aggiungere" + +#: kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" +msgstr "Carica contenuto" + +#: 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:5 +msgid "You are joining ${site_title}." +msgstr "Stai entrando in ${site_title}." + +#: 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/editor-bar.pt:21 +msgid "View" +msgstr "Vista" + +#: kotti/templates/editor-bar.pt:44 +msgid "Navigate" +msgstr "Navigazione" + +#: kotti/templates/editor-bar.pt:55 +msgid "Preferences" +msgstr "Preferenze" + +#: kotti/templates/editor-bar.pt:60 +msgid "Site Setup" +msgstr "Configurazione del sito" + +#: kotti/templates/editor-bar.pt:71 +msgid "Logout" +msgstr "Esci" + +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "Rendi ${state}" + +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "Seleziona la vista predefinita" + +#: kotti/templates/login.pt:15 +msgid "Login" +msgstr "Accedi" + +#: kotti/templates/login.pt:20 kotti/templates/login.pt:57 +msgid "Username or email" +msgstr "Username o email" + +#: kotti/templates/login.pt:33 +msgid "Log in" +msgstr "Accedi" + +#: kotti/templates/login.pt:43 +msgid "Forgot your password?" +msgstr "Hai dimenticato la tua password?" + +#. 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:50 kotti/templates/login.pt:65 +msgid "Reset password" +msgstr "Resetta password" + +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:48 +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/login.pt:76 +msgid "Not registered yet?" +msgstr "Non sei ancora registrato?" + +#: kotti/templates/login.pt:82 +msgid "Register for an account on this site." +msgstr "Registrati per avere un account su questo sito." + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "Taggati con:" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "Cerca..." + +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:67 +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:112 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:40 +#: kotti/templates/edit/share.pt:47 kotti/templates/edit/change-state.pt:19 +msgid "Type" +msgstr "Tipo" + +#: kotti/templates/view/folder.pt:22 kotti/templates/edit/delete-nodes.pt:25 +#: kotti/templates/edit/contents.pt:43 kotti/templates/edit/change-state.pt:21 +msgid "Creation Date" +msgstr "Data di creazione" + +#: kotti/templates/view/folder.pt:23 kotti/templates/edit/delete-nodes.pt:26 +#: kotti/templates/edit/contents.pt:44 kotti/templates/edit/change-state.pt:22 +msgid "Modification Date" +msgstr "Data di modifica" + +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "Risultati di ricerca" + +#: kotti/templates/http-errors/notfound.pt:6 +msgid "Not Found" +msgstr "" + +#: kotti/templates/http-errors/notfound.pt:7 +msgid "The resource could not be found" +msgstr "" + +#: kotti/templates/site-setup/delete-user.pt:9 +#: kotti/templates/edit/delete.pt:9 +msgid "Delete ${title}" +msgstr "Elimina ${title}" + +#: 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:12 +msgid "Back to User Management" +msgstr "Torna alla gestione degli utenti" + +#: 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:17 +msgid "Add user" +msgstr "Aggiungi un utente" + +#: kotti/templates/site-setup/users.pt:22 +msgid "Add group" +msgstr "Aggiungi un gruppo" + +#: kotti/templates/site-setup/users.pt:34 +msgid "Find users or groups" +msgstr "Cerca utenti o gruppi" + +#: kotti/templates/site-setup/users.pt:39 +msgid "User- / groupname" +msgstr "" + +#: kotti/templates/site-setup/users.pt:47 kotti/templates/edit/share.pt:14 +#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:26 +msgid "Search users and groups" +msgstr "Cerca utenti e gruppi" + +#: kotti/templates/site-setup/users.pt:49 +msgid "Blank search text finds all." +msgstr "Lascia il testo vuoto per trovare tutti gli utenti." + +#: kotti/templates/site-setup/users.pt:55 kotti/templates/edit/share.pt:33 +msgid "Search" +msgstr "Cerca" + +#: kotti/templates/site-setup/users.pt:63 +msgid "Assign global roles" +msgstr "Assegna ruoli globali" + +#: kotti/templates/site-setup/users.pt:83 kotti/templates/edit/share.pt:63 +msgid "Gravatar" +msgstr "Gravatar" + +#: kotti/templates/site-setup/users.pt:105 kotti/templates/edit/share.pt:83 +#: kotti/templates/edit/change-state.pt:64 +msgid "Apply changes" +msgstr "Applica le modifiche" + +#: kotti/templates/site-setup/users.pt:115 +msgid "Add new user" +msgstr "Aggiungi un nuovo utente" + +#: kotti/templates/site-setup/users.pt:121 +msgid "Add new group" +msgstr "Aggiungi un nuovo gruppo" + +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "Navigazione del Sito" + +#: 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/upload.pt:18 kotti/templates/edit/upload.pt:111 +msgid "Filename" +msgstr "Nome del file" + +#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:113 +msgid "Size" +msgstr "Dimensione" + +#: kotti/templates/edit/upload.pt:21 +msgid "Status" +msgstr "Stato" + +#: kotti/templates/edit/upload.pt:22 +msgid "Progress" +msgstr "Avanzamento" + +#: kotti/templates/edit/upload.pt:88 +msgid "Select file(s) to upload..." +msgstr "" + +#: kotti/templates/edit/upload.pt:96 +msgid "Upload ${number} files." +msgstr "" + +#: kotti/templates/edit/upload.pt:101 +msgid "Dismiss all errors" +msgstr "" + +#: kotti/templates/edit/delete-nodes.pt:14 +msgid "Are you sure you want to delete the following items?" +msgstr "Sei sicuro di voler eliminare i seguenti elementi?" + +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:41 +#: kotti/templates/edit/change-state.pt:20 +msgid "State" +msgstr "Stato" + +#: kotti/templates/edit/breadcrumbs.pt:8 +msgid "You are here:" +msgstr "Sei qua:" + +#: kotti/templates/edit/contents.pt:30 +msgid "No content items are contained here." +msgstr "Non è presente nessun elemento di contenuto." + +#: kotti/templates/edit/contents.pt:42 +msgid "Visibility" +msgstr "Visibilità" + +#: kotti/templates/edit/contents.pt:75 +msgid "Visible" +msgstr "Visibile" + +#: kotti/templates/edit/contents.pt:79 +msgid "Hidden" +msgstr "Nascosto" + +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "Rinomina ${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 "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:19 kotti/templates/edit/rename-nodes.pt:26 +msgid "New name" +msgstr "Nuovo nome" + +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:33 +msgid "New title" +msgstr "Nuovo titolo" + +#: 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/share.pt:8 +msgid "Share ${title}" +msgstr "Condividi ${title}" + +#: kotti/templates/edit/share.pt:42 +msgid "Assign local roles" +msgstr "Assegna ruoli locali" + +#: kotti/templates/edit/change-state.pt:8 +msgid "Change workflow state" +msgstr "Cambia lo stato del workflow" + +#: 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." + +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:45 +msgid "Include children" +msgstr "Includere figli" + +#: 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:50 +msgid "Change state to" +msgstr "Cambia stato a" + +#: kotti/templates/edit/change-state.pt:51 +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:55 +msgid "No change" +msgstr "Nessuna modifica" + +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "Benvenuto, ${user}! Sei loggato." + +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "Benvenuto, non sei loggato." + #~ msgid "Private" #~ msgstr "Privato" diff --git a/kotti/locale/ja/LC_MESSAGES/Kotti.po b/kotti/locale/ja/LC_MESSAGES/Kotti.po index 6ef1c8f9d..2ae412eb3 100644 --- a/kotti/locale/ja/LC_MESSAGES/Kotti.po +++ b/kotti/locale/ja/LC_MESSAGES/Kotti.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Kotti 0.7.0\n" "Report-Msgid-Bugs-To: kotti@googlegroups.com\n" -"POT-Creation-Date: 2015-02-22 11:33+0100\n" +"POT-Creation-Date: 2015-09-29 14:24+0100\n" "PO-Revision-Date: 2013-09-19 02:45+0900\n" "Last-Translator: OCHIAI, Gouji \n" "Language-Team: ja \n" @@ -19,6 +19,81 @@ msgstr "" "Plural-Forms: nplurals=1; plural=0\n" "Generated-By: Babel 0.9.6\n" +#: kotti/security.py:190 +msgid "Viewer" +msgstr "閲覧者" + +#: kotti/security.py:191 +msgid "Editor" +msgstr "編集者" + +#: kotti/security.py:192 +msgid "Owner" +msgstr "所有者" + +#: kotti/security.py:193 +msgid "Admin" +msgstr "管理者" + +#: kotti/resources.py:508 kotti/views/edit/actions.py:456 +msgid "Copy" +msgstr "コピー" + +#: kotti/resources.py:509 kotti/views/edit/actions.py:457 +msgid "Cut" +msgstr "切り取り" + +#: kotti/resources.py:510 kotti/views/edit/actions.py:453 +msgid "Paste" +msgstr "貼り付け" + +#: kotti/resources.py:511 kotti/views/edit/actions.py:458 +#: kotti/templates/edit/rename.pt:31 kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:40 +msgid "Rename" +msgstr "名称変更" + +#: kotti/resources.py:512 kotti/views/users.py:443 +#: kotti/views/edit/actions.py:460 +#: kotti/templates/site-setup/delete-user.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:50 kotti/templates/edit/delete.pt:24 +msgid "Delete" +msgstr "削除" + +#: kotti/resources.py:637 kotti/views/edit/content.py:85 +msgid "Document" +msgstr "ドキュメント" + +#: kotti/resources.py:726 kotti/views/edit/content.py:113 +#: kotti/views/edit/content.py:66 +msgid "File" +msgstr "ファイル" + +#: kotti/resources.py:772 kotti/views/edit/content.py:141 +msgid "Image" +msgstr "画像" + +#: kotti/resources.py:529 +msgid "Folder view" +msgstr "フォルダー表示" + +#: kotti/resources.py:523 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "コンテンツ" + +#: kotti/resources.py:524 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "編集" + +#: kotti/resources.py:525 +msgid "Share" +msgstr "共有" + +#: kotti/resources.py:526 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "アクション" + #: kotti/populate.py:64 msgid "Welcome to Kotti" msgstr "" @@ -33,29 +108,25 @@ 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=\"http://kotti.readthedocs.org/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -63,13 +134,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,9 +147,9 @@ 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" msgstr "" -#: kotti/populate.py:121 +#: kotti/populate.py:123 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:124 +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:125 msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -124,489 +191,17 @@ 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:627 kotti/views/edit/content.py:82 -msgid "Document" -msgstr "ドキュメント" - -#: kotti/resources.py:666 kotti/views/edit/content.py:110 -#: kotti/views/edit/content.py:63 -msgid "File" -msgstr "ファイル" - -#: kotti/resources.py:753 kotti/views/edit/content.py:138 -msgid "Image" -msgstr "画像" - -#: kotti/resources.py:517 -msgid "Folder view" -msgstr "フォルダー表示" - -#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 -msgid "Contents" -msgstr "コンテンツ" - -#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" -msgstr "編集" - -#: kotti/resources.py:506 -msgid "Share" -msgstr "共有" - -#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" -msgstr "アクション" - -#: kotti/resources.py:508 kotti/views/edit/actions.py:446 -msgid "Copy" -msgstr "コピー" - -#: kotti/resources.py:509 kotti/views/edit/actions.py:447 -msgid "Cut" -msgstr "切り取り" - -#: kotti/resources.py:510 kotti/views/edit/actions.py:443 -msgid "Paste" -msgstr "貼り付け" - -#: kotti/resources.py:511 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:448 -msgid "Rename" -msgstr "名称変更" - -#: kotti/resources.py:512 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:450 -msgid "Delete" -msgstr "削除" - -#: kotti/security.py:189 -msgid "Viewer" -msgstr "閲覧者" - -#: kotti/security.py:190 -msgid "Editor" -msgstr "編集者" - -#: kotti/security.py:191 -msgid "Owner" -msgstr "所有者" - -#: kotti/security.py:192 -msgid "Admin" -msgstr "管理者" - -#: kotti/templates/add-dropdown.pt:5 -msgid "Add" -msgstr "追加" - -#: kotti/templates/add-dropdown.pt:27 -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/email-reset-password.pt:3 -msgid "Reset your password for ${site_title}." -msgstr "${site_title} のパスワードをリセット" - -#: kotti/templates/email-reset-password.pt:7 -#: kotti/templates/email-set-password.pt:6 -msgid "Hello, ${user_title}!" -msgstr "こんにちは ${user_title}!" - -#: kotti/templates/email-reset-password.pt:10 -msgid "Click this link to reset your password at ${site_title}: ${url}" -msgstr "" -"${site_title} のパスワードをリセットするにはこのリンクをクリックしてくださ" -"い: ${url}" - -#: kotti/templates/email-set-password.pt:3 -msgid "Your registration for ${site_title}" -msgstr "${site_title} へのアカウント登録" - -#: kotti/templates/email-set-password.pt:9 -msgid "You are joining ${site_title}." -msgstr "あなたは ${site_title} に参加しようとしています。" - -#: kotti/templates/email-set-password.pt:12 -msgid "Click here to set your password and log in: ${url}" -msgstr "" -"パスワードをセットしてログインするにはこのリンクをクリックしてください: " -"${url}" - -#: kotti/templates/forbidden.pt:7 -msgid "Forbidden" -msgstr "表示権限がありません" - -#: kotti/templates/login.pt:16 -msgid "Login" -msgstr "ログイン" - -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 -msgid "Username or email" -msgstr "ユーザー名またはメールアドレス" - -#: kotti/templates/login.pt:34 kotti/views/login.py:212 -#: kotti/views/users.py:212 -msgid "Password" -msgstr "パスワード" - -#: kotti/templates/login.pt:47 -msgid "Log in" -msgstr "ログイン" - -#: kotti/templates/login.pt:65 -msgid "Forgot your password?" -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 "パスワードをリセット" - -#. 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/login.pt:118 -msgid "Not registered yet?" -msgstr "" - -#: kotti/templates/login.pt:127 -#, fuzzy -msgid "Register for an account on this site." -msgstr "" -"まだ登録していませんか? このサイトにアカウントを ${register} しましょう。" - -#: kotti/templates/workflow-dropdown.pt:19 -msgid "Make ${state}" -msgstr "${state} にする" - -#: kotti/templates/edit/breadcrumbs.pt:5 -msgid "You are here:" -msgstr "今の場所:" - -#: kotti/templates/edit/change-state.pt:7 -msgid "Change workflow state" -msgstr "ワークフローの状態を変更" - -#: kotti/templates/edit/change-state.pt:14 -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:32 -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 -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 -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 -msgid "Modification Date" -msgstr "変更日" - -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 -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:100 -msgid "Change state to" -msgstr "変更後の状態" - -#: kotti/templates/edit/change-state.pt:103 -msgid "Select the new state where all chosen items should be set." -msgstr "新しく設定する状態を選択して下さい。" - -#: kotti/templates/edit/change-state.pt:111 -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 -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-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-nodes.pt:35 kotti/templates/edit/rename.pt:26 -msgid "New name" -msgstr "新しい名称" - -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 -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 -msgid "Upload content from local file(s)" -msgstr "" - -#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:164 -#, fuzzy -msgid "Filename" -msgstr "名称変更" - -#: kotti/templates/edit/upload.pt:26 kotti/templates/edit/upload.pt:170 -msgid "Size" -msgstr "" - -#: kotti/templates/edit/upload.pt:29 -msgid "Status" -msgstr "" - -#: kotti/templates/edit/upload.pt:32 -msgid "Progress" -msgstr "" - -#: kotti/templates/edit/upload.pt:124 -msgid "Select file(s) to upload..." -msgstr "" - -#: kotti/templates/edit/upload.pt:134 -msgid "Upload ${number} files." -msgstr "" - -#: kotti/templates/edit/upload.pt:144 -msgid "Dismiss all errors" -msgstr "" - -#: kotti/templates/site-setup/delete-user.pt:20 -msgid "Are you sure you want to delete ${type} ${title}?" -msgstr "本当に ${type} ${title} を削除しますか?" - -#: kotti/templates/site-setup/user.pt:13 -msgid "Back to User Management" -msgstr "ユーザー管理に戻る" - -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 -#, python-format -msgid "Edit ${title}" -msgstr "${title} を編集" - -#: kotti/templates/site-setup/users.pt:15 -#, fuzzy -msgid "Search user(s) / group(s)" -msgstr "ユーザーとグループを検索" - -#: kotti/templates/site-setup/users.pt:24 -msgid "Add user" -msgstr "ユーザーを追加" - -#: kotti/templates/site-setup/users.pt:33 -msgid "Add group" -msgstr "グループを追加" - -#: kotti/templates/site-setup/users.pt:52 -#, fuzzy -msgid "Find users or groups" -msgstr "ユーザーとグループを検索" - -#: kotti/templates/site-setup/users.pt:63 -msgid "User- / groupname" -msgstr "" - -#: kotti/templates/site-setup/users.pt:70 -#, fuzzy -msgid "Blank search text finds all." -msgstr "ユーザーを検索して変更する (空文字列での検索で全部表示) 。" - -#: kotti/templates/site-setup/users.pt:96 -#, fuzzy -msgid "Assign global roles" -msgstr "ローカルロールに割り当て" - -#: kotti/templates/site-setup/users.pt:180 -#, fuzzy -msgid "Add new user" -msgstr "ユーザーを追加" - -#: kotti/templates/site-setup/users.pt:190 -#, fuzzy -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:364 kotti/views/edit/actions.py:408 +#: kotti/views/edit/actions.py:374 kotti/views/edit/actions.py:418 msgid "Your changes have been saved." msgstr "変更内容を保存しました。" @@ -614,6 +209,11 @@ msgstr "変更内容を保存しました。" msgid "Item was added." msgstr "${title} を追加しました。" +#: kotti/views/form.py:156 kotti/templates/site-setup/user.pt:21 +#, python-format +msgid "Edit ${title}" +msgstr "${title} を編集" + #: kotti/views/form.py:264 #, python-format msgid "Maximum file size: ${size}MB" @@ -623,6 +223,13 @@ msgstr "最大ファイルサイズ: ${size}MB" msgid "Save" msgstr "保存" +#: kotti/views/form.py:78 kotti/views/users.py:316 kotti/views/users.py:350 +#: kotti/views/users.py:442 kotti/templates/edit/delete-nodes.pt:45 +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:66 +#: kotti/templates/edit/rename-nodes.pt:42 +msgid "Cancel" +msgstr "キャンセル" + #: kotti/views/form.py:198 #, python-format msgid "Add ${type} to ${title}." @@ -633,6 +240,10 @@ msgstr "${title} に ${type} を追加する。" msgid "Add ${type}." msgstr "${type} を追加する。" +#: kotti/views/site_setup.py:6 kotti/views/users.py:378 +msgid "User Management" +msgstr "ユーザー管理" + #: kotti/views/login.py:237 msgid "You have reset your password." msgstr "パスワードのリセットに成功しました。" @@ -654,12 +265,8 @@ msgid "Email" msgstr "メールアドレス" #: kotti/views/login.py:108 -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メールのリンクから、すぐにパスワー" -"ドを設定して下さい。" +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:123 #, python-format @@ -670,6 +277,11 @@ msgstr "登録 - ${title}" msgid "Login failed." msgstr "ログインに失敗しました。" +#: kotti/views/login.py:212 kotti/views/users.py:212 +#: kotti/templates/login.pt:25 +msgid "Password" +msgstr "パスワード" + #: kotti/views/login.py:285 #, python-format msgid "Reset your password - ${title}." @@ -681,12 +293,8 @@ msgid "Welcome, ${user}!" msgstr "ようこそ ${user}!" #: kotti/views/login.py:172 -msgid "" -"You should be receiving an email with a link to reset your password. Doing " -"so will activate your account." -msgstr "" -"まもなくパスワードをリセットするためのリンクが記載されたメールが送信されま" -"す。" +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:177 msgid "That username or email is not known by this system." @@ -708,17 +316,20 @@ msgstr "送信" 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:313 kotti/views/users.py:501 kotti/views/users.py:536 +#: kotti/templates/site-setup/users.pt:79 kotti/templates/edit/share.pt:59 +msgid "User" +msgstr "ユーザー" + +#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 +#: kotti/views/users.py:536 kotti/templates/site-setup/users.pt:80 +#: kotti/templates/edit/share.pt:60 +msgid "Group" +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 "" -"この項目を空にして下の 'パスワード登録を送信する' をチェックすると、ユーザー" -"自身にパスワードを設定させられます。" +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:587 #, python-format @@ -742,6 +353,17 @@ msgstr "そのメールアドレスをもつユーザーがすでに存在しま msgid "No such group: ${group}" msgstr "グループが見つかりません: ${group}" +#: kotti/views/users.py:196 kotti/views/edit/content.py:33 +#: kotti/templates/view/folder.pt:20 kotti/templates/edit/delete-nodes.pt:22 +#: kotti/templates/edit/contents.pt:39 kotti/templates/edit/change-state.pt:18 +msgid "Title" +msgstr "タイトル" + +#: kotti/views/users.py:207 kotti/templates/site-setup/users.pt:68 +#: kotti/templates/edit/share.pt:48 +msgid "Name" +msgstr "名称" + #: kotti/views/users.py:219 msgid "Active" msgstr "有効" @@ -772,8 +394,8 @@ msgid "Add Group" msgstr "グループ追加" #: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 -#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 +#: kotti/views/edit/actions.py:312 kotti/views/edit/actions.py:378 +#: kotti/views/edit/actions.py:424 kotti/views/edit/actions.py:420 msgid "No changes were made." msgstr "変更箇所がありませんでした。" @@ -823,11 +445,6 @@ msgstr "${title} を切り取りました。" msgid "${title} was moved." msgstr "${title} を移動しました。" -#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 -#, python-format -msgid "${title} was deleted." -msgstr "${title} を削除しました。" - #: kotti/views/edit/actions.py:159 #, python-format msgid "${title} was pasted." @@ -847,70 +464,420 @@ msgstr "${title} がナビゲーションに表示されるようになりまし msgid "${title} is no longer visible in the navigation." msgstr "${title} がナビゲーションでは非表示になりました。" -#: kotti/views/edit/actions.py:293 +#: kotti/views/edit/actions.py:277 kotti/views/edit/actions.py:306 +#, python-format +msgid "${title} was deleted." +msgstr "${title} を削除しました。" + +#: kotti/views/edit/actions.py:303 msgid "Nothing was deleted." msgstr "削除対象がありません。" -#: kotti/views/edit/actions.py:329 kotti/views/edit/actions.py:356 +#: kotti/views/edit/actions.py:339 kotti/views/edit/actions.py:366 msgid "Name and title are required." msgstr "名称とタイトルは必須です。" -#: kotti/views/edit/actions.py:333 +#: kotti/views/edit/actions.py:343 msgid "Item was renamed." msgstr "名称変更しました" -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:465 msgid "Move up" msgstr "上に移動する" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:466 msgid "Move down" msgstr "下に移動する" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:467 msgid "Show" msgstr "表示中にする" -#: kotti/views/edit/actions.py:458 +#: kotti/views/edit/actions.py:468 msgid "Hide" msgstr "非表示にする" -#: kotti/views/edit/actions.py:497 +#: kotti/views/edit/actions.py:507 msgid "You have to select items to perform an action." msgstr "アクションを適用するアイテムを選択してください。" -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:464 msgid "Change State" msgstr "状態を変更" -#: kotti/views/edit/content.py:36 +#: kotti/views/edit/content.py:38 msgid "Description" msgstr "説明" -#: kotti/views/edit/content.py:42 +#: kotti/views/edit/content.py:44 msgid "Tags" msgstr "タグ" -#: kotti/views/edit/content.py:51 +#: kotti/views/edit/content.py:53 msgid "Body" msgstr "本文" -#: kotti/views/edit/default_views.py:99 +#: kotti/views/edit/default_views.py:100 msgid "Default view has been reset to default." msgstr "デフォルト表示は既定値にリセットされました。" -#: kotti/views/edit/default_views.py:78 +#: kotti/views/edit/default_views.py:79 msgid "Default view" msgstr "デフォルト表示" -#: kotti/views/edit/default_views.py:106 +#: kotti/views/edit/default_views.py:107 msgid "Default view has been set." msgstr "デフォルト表示が設定されました。" -#: kotti/views/edit/default_views.py:111 +#: kotti/views/edit/default_views.py:112 msgid "Default view could not be set." msgstr "デフォルト表示は設定できませんでした。" +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "表示権限がありません" + +#: kotti/templates/email-reset-password.pt:2 +msgid "Reset your password for ${site_title}." +msgstr "${site_title} のパスワードをリセット" + +#: 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:5 +msgid "Click this link to reset your password at ${site_title}: ${url}" +msgstr "${site_title} のパスワードをリセットするにはこのリンクをクリックしてください: ${url}" + +#: kotti/templates/add-dropdown.pt:3 +msgid "Add" +msgstr "追加" + +#: kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" +msgstr "アップロード" + +#: kotti/templates/email-set-password.pt:2 +msgid "Your registration for ${site_title}" +msgstr "${site_title} へのアカウント登録" + +#: kotti/templates/email-set-password.pt:5 +msgid "You are joining ${site_title}." +msgstr "あなたは ${site_title} に参加しようとしています。" + +#: kotti/templates/email-set-password.pt:6 +msgid "Click here to set your password and log in: ${url}" +msgstr "パスワードをセットしてログインするにはこのリンクをクリックしてください: ${url}" + +#: kotti/templates/editor-bar.pt:21 +msgid "View" +msgstr "表示" + +#: kotti/templates/editor-bar.pt:44 +msgid "Navigate" +msgstr "ナビゲート" + +#: kotti/templates/editor-bar.pt:55 +msgid "Preferences" +msgstr "設定" + +#: kotti/templates/editor-bar.pt:60 +msgid "Site Setup" +msgstr "サイト設定" + +#: kotti/templates/editor-bar.pt:71 +msgid "Logout" +msgstr "ログアウト" + +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "${state} にする" + +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "表示方法の設定" + +#: kotti/templates/login.pt:15 +msgid "Login" +msgstr "ログイン" + +#: kotti/templates/login.pt:20 kotti/templates/login.pt:57 +msgid "Username or email" +msgstr "ユーザー名またはメールアドレス" + +#: kotti/templates/login.pt:33 +msgid "Log in" +msgstr "ログイン" + +#: kotti/templates/login.pt:43 +msgid "Forgot your password?" +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:50 kotti/templates/login.pt:65 +msgid "Reset password" +msgstr "パスワードをリセット" + +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:48 +#, 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/login.pt:76 +msgid "Not registered yet?" +msgstr "" + +#: kotti/templates/login.pt:82 +#, fuzzy +msgid "Register for an account on this site." +msgstr "まだ登録していませんか? このサイトにアカウントを ${register} しましょう。" + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "付与されたタグ:" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "検索..." + +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:67 +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:112 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:40 +#: kotti/templates/edit/share.pt:47 kotti/templates/edit/change-state.pt:19 +msgid "Type" +msgstr "種類" + +#: kotti/templates/view/folder.pt:22 kotti/templates/edit/delete-nodes.pt:25 +#: kotti/templates/edit/contents.pt:43 kotti/templates/edit/change-state.pt:21 +msgid "Creation Date" +msgstr "作成日" + +#: kotti/templates/view/folder.pt:23 kotti/templates/edit/delete-nodes.pt:26 +#: kotti/templates/edit/contents.pt:44 kotti/templates/edit/change-state.pt:22 +msgid "Modification Date" +msgstr "変更日" + +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "検索結果" + +#: kotti/templates/http-errors/notfound.pt:6 +msgid "Not Found" +msgstr "" + +#: kotti/templates/http-errors/notfound.pt:7 +msgid "The resource could not be found" +msgstr "" + +#: kotti/templates/site-setup/delete-user.pt:9 +#: kotti/templates/edit/delete.pt:9 +msgid "Delete ${title}" +msgstr "${title} を削除する" + +#: 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:12 +msgid "Back to User Management" +msgstr "ユーザー管理に戻る" + +#: kotti/templates/site-setup/users.pt:12 +#, fuzzy +msgid "Search user(s) / group(s)" +msgstr "ユーザーとグループを検索" + +#: kotti/templates/site-setup/users.pt:17 +msgid "Add user" +msgstr "ユーザーを追加" + +#: kotti/templates/site-setup/users.pt:22 +msgid "Add group" +msgstr "グループを追加" + +#: kotti/templates/site-setup/users.pt:34 +#, fuzzy +msgid "Find users or groups" +msgstr "ユーザーとグループを検索" + +#: kotti/templates/site-setup/users.pt:39 +msgid "User- / groupname" +msgstr "" + +#: kotti/templates/site-setup/users.pt:47 kotti/templates/edit/share.pt:14 +#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:26 +msgid "Search users and groups" +msgstr "ユーザーとグループを検索" + +#: kotti/templates/site-setup/users.pt:49 +#, fuzzy +msgid "Blank search text finds all." +msgstr "ユーザーを検索して変更する (空文字列での検索で全部表示) 。" + +#: kotti/templates/site-setup/users.pt:55 kotti/templates/edit/share.pt:33 +msgid "Search" +msgstr "検索" + +#: kotti/templates/site-setup/users.pt:63 +#, fuzzy +msgid "Assign global roles" +msgstr "ローカルロールに割り当て" + +#: kotti/templates/site-setup/users.pt:83 kotti/templates/edit/share.pt:63 +msgid "Gravatar" +msgstr "Gravatar" + +#: kotti/templates/site-setup/users.pt:105 kotti/templates/edit/share.pt:83 +#: kotti/templates/edit/change-state.pt:64 +msgid "Apply changes" +msgstr "変更を適用" + +#: kotti/templates/site-setup/users.pt:115 +#, fuzzy +msgid "Add new user" +msgstr "ユーザーを追加" + +#: kotti/templates/site-setup/users.pt:121 +#, fuzzy +msgid "Add new group" +msgstr "グループを追加" + +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "ナビゲート" + +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" +msgstr "" + +#: kotti/templates/edit/upload.pt:18 kotti/templates/edit/upload.pt:111 +#, fuzzy +msgid "Filename" +msgstr "名称変更" + +#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:113 +msgid "Size" +msgstr "" + +#: kotti/templates/edit/upload.pt:21 +msgid "Status" +msgstr "" + +#: kotti/templates/edit/upload.pt:22 +msgid "Progress" +msgstr "" + +#: kotti/templates/edit/upload.pt:88 +msgid "Select file(s) to upload..." +msgstr "" + +#: kotti/templates/edit/upload.pt:96 +msgid "Upload ${number} files." +msgstr "" + +#: kotti/templates/edit/upload.pt:101 +msgid "Dismiss all errors" +msgstr "" + +#: kotti/templates/edit/delete-nodes.pt:14 +msgid "Are you sure you want to delete the following items?" +msgstr "本当に以下のアイテムを削除しますか?" + +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:41 +#: kotti/templates/edit/change-state.pt:20 +msgid "State" +msgstr "状態" + +#: kotti/templates/edit/breadcrumbs.pt:8 +msgid "You are here:" +msgstr "今の場所:" + +#: kotti/templates/edit/contents.pt:30 +msgid "No content items are contained here." +msgstr "ここにはコンテンツアイテムが含まれていません。" + +#: kotti/templates/edit/contents.pt:42 +msgid "Visibility" +msgstr "表示状態" + +#: kotti/templates/edit/contents.pt:75 +msgid "Visible" +msgstr "表示中" + +#: kotti/templates/edit/contents.pt:79 +msgid "Hidden" +msgstr "非表示" + +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "${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 "それぞれのコンテンツには名称とタイトルがあり、名称は URL を生成するのに使用します。下のフォームでは、その両方を変更することができます。" + +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +msgid "New name" +msgstr "新しい名称" + +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:33 +msgid "New title" +msgstr "新しいタイトル" + +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" +msgstr "本当に ${title} を削除しますか?" + +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" +msgstr "${title} を共有" + +#: kotti/templates/edit/share.pt:42 +msgid "Assign local roles" +msgstr "ローカルロールに割り当て" + +#: kotti/templates/edit/change-state.pt:8 +msgid "Change workflow state" +msgstr "ワークフローの状態を変更" + +#: 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:40 +#: kotti/templates/edit/change-state.pt:45 +msgid "Include children" +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:50 +msgid "Change state to" +msgstr "変更後の状態" + +#: kotti/templates/edit/change-state.pt:51 +msgid "Select the new state where all chosen items should be set." +msgstr "新しく設定する状態を選択して下さい。" + +#: kotti/templates/edit/change-state.pt:55 +msgid "No change" +msgstr "変更しない" + +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "ようこそ ${user}! ログインしています。" + +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "ようこそ。ログインしていません。" + #~ msgid "Private" #~ msgstr "プライベート" diff --git a/kotti/locale/ko/LC_MESSAGES/Kotti.po b/kotti/locale/ko/LC_MESSAGES/Kotti.po index 2f0bdb434..065186b6b 100644 --- a/kotti/locale/ko/LC_MESSAGES/Kotti.po +++ b/kotti/locale/ko/LC_MESSAGES/Kotti.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Kotti 0.10b2dev\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2015-02-22 11:33+0100\n" +"POT-Creation-Date: 2015-09-29 14:24+0100\n" "PO-Revision-Date: 2014-10-19 08:22+0900\n" "Last-Translator: mete0r \n" "Language-Team: Korean\n" @@ -18,6 +18,81 @@ msgstr "" "Generated-By: Babel 0.9.6\n" "Plural-Forms: nplurals=1; plural=0;\n" +#: kotti/security.py:190 +msgid "Viewer" +msgstr "열람자" + +#: kotti/security.py:191 +msgid "Editor" +msgstr "편집자" + +#: kotti/security.py:192 +msgid "Owner" +msgstr "소유자" + +#: kotti/security.py:193 +msgid "Admin" +msgstr "관리자" + +#: kotti/resources.py:508 kotti/views/edit/actions.py:456 +msgid "Copy" +msgstr "복사" + +#: kotti/resources.py:509 kotti/views/edit/actions.py:457 +msgid "Cut" +msgstr "잘라내기" + +#: kotti/resources.py:510 kotti/views/edit/actions.py:453 +msgid "Paste" +msgstr "붙여넣기" + +#: kotti/resources.py:511 kotti/views/edit/actions.py:458 +#: kotti/templates/edit/rename.pt:31 kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:40 +msgid "Rename" +msgstr "이름 바꾸기" + +#: kotti/resources.py:512 kotti/views/users.py:443 +#: kotti/views/edit/actions.py:460 +#: kotti/templates/site-setup/delete-user.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:50 kotti/templates/edit/delete.pt:24 +msgid "Delete" +msgstr "삭제" + +#: kotti/resources.py:637 kotti/views/edit/content.py:85 +msgid "Document" +msgstr "문서" + +#: kotti/resources.py:726 kotti/views/edit/content.py:113 +#: kotti/views/edit/content.py:66 +msgid "File" +msgstr "파일" + +#: kotti/resources.py:772 kotti/views/edit/content.py:141 +msgid "Image" +msgstr "화상" + +#: kotti/resources.py:529 +msgid "Folder view" +msgstr "폴더로 보임" + +#: kotti/resources.py:523 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "컨텐츠 항목" + +#: kotti/resources.py:524 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "편집" + +#: kotti/resources.py:525 +msgid "Share" +msgstr "공유" + +#: kotti/resources.py:526 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "동작" + #: kotti/populate.py:64 msgid "Welcome to Kotti" msgstr "Kotti에 오신 것을 환영합니다" @@ -32,29 +107,25 @@ 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=\"http://kotti.readthedocs.org/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -62,13 +133,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,9 +146,9 @@ 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" "

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

    \n" "

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

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

    설정

    \n" "

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

    \n" "

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

    \n" @@ -118,13 +183,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,8 +195,7 @@ msgstr "" "
    \n" "

    기술문헌

    \n" "

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

    \n" "

    \n" " \n" "

    \n" -#: kotti/populate.py:121 +#: kotti/populate.py:123 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:124 +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:125 +#, fuzzy msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -179,20 +237,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,609 +266,180 @@ 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:627 kotti/views/edit/content.py:82 -msgid "Document" -msgstr "문서" +#: kotti/views/form.py:79 kotti/views/users.py:69 +#: kotti/views/edit/actions.py:374 kotti/views/edit/actions.py:418 +msgid "Your changes have been saved." +msgstr "수정사항이 저장되었습니다." -#: kotti/resources.py:666 kotti/views/edit/content.py:110 -#: kotti/views/edit/content.py:63 -msgid "File" -msgstr "파일" +#: kotti/views/form.py:174 +msgid "Item was added." +msgstr "항목이 추가되었습니다." -#: kotti/resources.py:753 kotti/views/edit/content.py:138 -msgid "Image" -msgstr "화상" +#: kotti/views/form.py:156 kotti/templates/site-setup/user.pt:21 +#, python-format +msgid "Edit ${title}" +msgstr "\"${title}\" 편집" -#: kotti/resources.py:517 -msgid "Folder view" -msgstr "폴더로 보임" +#: kotti/views/form.py:264 +#, python-format +msgid "Maximum file size: ${size}MB" +msgstr "최대 파일 크기: ${size}MB" -#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 -msgid "Contents" -msgstr "컨텐츠 항목" +#: kotti/views/form.py:77 kotti/views/users.py:441 +msgid "Save" +msgstr "저장" -#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" -msgstr "편집" +#: kotti/views/form.py:78 kotti/views/users.py:316 kotti/views/users.py:350 +#: kotti/views/users.py:442 kotti/templates/edit/delete-nodes.pt:45 +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:66 +#: kotti/templates/edit/rename-nodes.pt:42 +msgid "Cancel" +msgstr "취소" -#: kotti/resources.py:506 -msgid "Share" -msgstr "공유" +#: kotti/views/form.py:198 +#, python-format +msgid "Add ${type} to ${title}." +msgstr "${type}을(를) \"${title}\"에 추가합니다." -#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" -msgstr "동작" +#: kotti/views/form.py:202 +#, python-format +msgid "Add ${type}." +msgstr "${type}을(를) 추가합니다." -#: kotti/resources.py:508 kotti/views/edit/actions.py:446 -msgid "Copy" -msgstr "복사" +#: kotti/views/site_setup.py:6 kotti/views/users.py:378 +msgid "User Management" +msgstr "사용자 관리" -#: kotti/resources.py:509 kotti/views/edit/actions.py:447 -msgid "Cut" -msgstr "잘라내기" +#: kotti/views/login.py:237 +msgid "You have reset your password." +msgstr "암호를 재설정했습니다." -#: kotti/resources.py:510 kotti/views/edit/actions.py:443 -msgid "Paste" -msgstr "붙여넣기" +#: kotti/views/login.py:199 +msgid "You have been logged out." +msgstr "로그아웃되었습니다." -#: kotti/resources.py:511 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:448 -msgid "Rename" -msgstr "이름 바꾸기" +#: kotti/views/login.py:65 kotti/views/users.py:270 +msgid "Full name" +msgstr "전체 이름" -#: kotti/resources.py:512 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:450 -msgid "Delete" -msgstr "삭제" +#: kotti/views/login.py:68 +msgid "Username" +msgstr "사용자 이름" -#: kotti/security.py:189 -msgid "Viewer" -msgstr "열람자" +#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 +msgid "Email" +msgstr "전자메일" -#: kotti/security.py:190 -msgid "Editor" -msgstr "편집자" +#: kotti/views/login.py:108 +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/security.py:191 -msgid "Owner" -msgstr "소유자" +#: kotti/views/login.py:123 +#, python-format +msgid "Register - ${title}" +msgstr "등록 - ${title}" -#: kotti/security.py:192 -msgid "Admin" -msgstr "관리자" +#: kotti/views/login.py:163 +msgid "Login failed." +msgstr "로그인이 실패했습니다." -#: kotti/templates/add-dropdown.pt:5 -msgid "Add" -msgstr "추가" +#: kotti/views/login.py:212 kotti/views/users.py:212 +#: kotti/templates/login.pt:25 +msgid "Password" +msgstr "암호" -#: kotti/templates/add-dropdown.pt:27 -msgid "Upload Content" -msgstr "컨텐트 올리기" +#: kotti/views/login.py:285 +#, python-format +msgid "Reset your password - ${title}." +msgstr "암호를 재설정하세요 - ${title}." -#: kotti/templates/default-view-selector.pt:5 -msgid "Set default view" -msgstr "보이는 방식 설정" +#: kotti/views/login.py:159 +#, python-format +msgid "Welcome, ${user}!" +msgstr "${user}님, 환영합니다!" -#: kotti/templates/editor-bar.pt:35 -msgid "View" -msgstr "보기" +#: kotti/views/login.py:172 +msgid "You should be receiving an email with a link to reset your password. Doing so will activate your account." +msgstr "암호를 재설정할 링크를 전자메일로 받게 됩니다. 설정하면 계정이 활성화됩니다." -#: kotti/templates/editor-bar.pt:76 -msgid "Navigate" -msgstr "둘러보기" +#: kotti/views/login.py:177 +msgid "That username or email is not known by this system." +msgstr "이 시스템에 없는 사용자 이름이나 전자메일입니다." -#: kotti/templates/editor-bar.pt:98 -msgid "Preferences" -msgstr "기본 설정" +#: kotti/views/login.py:82 +msgid "Register" +msgstr "등록하기" -#: kotti/templates/editor-bar.pt:109 -msgid "Site Setup" -msgstr "사이트 설정" +#: kotti/views/login.py:89 kotti/views/login.py:256 +msgid "There was an error." +msgstr "오류가 있습니다." -#: kotti/templates/editor-bar.pt:131 -msgid "Logout" -msgstr "로그아웃" +#: kotti/views/login.py:249 +msgid "Submit" +msgstr "제출" -#: kotti/templates/email-reset-password.pt:3 -msgid "Reset your password for ${site_title}." -msgstr "${site_title}의 암호를 재설정하십시오." +#: kotti/views/login.py:278 +msgid "Your password reset token may have expired." +msgstr "암호 재설정 토큰이 만료된 것 같습니다." -#: kotti/templates/email-reset-password.pt:7 -#: kotti/templates/email-set-password.pt:6 -msgid "Hello, ${user_title}!" -msgstr "${user_title}님, 안녕하세요!" +#: kotti/views/users.py:313 kotti/views/users.py:501 kotti/views/users.py:536 +#: kotti/templates/site-setup/users.pt:79 kotti/templates/edit/share.pt:59 +msgid "User" +msgstr "사용자" -#: kotti/templates/email-reset-password.pt:10 -msgid "Click this link to reset your password at ${site_title}: ${url}" -msgstr "이 링크를 눌러 ${site_title}에서의 암호를 설정하십시오: ${url}" +#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 +#: kotti/views/users.py:536 kotti/templates/site-setup/users.pt:80 +#: kotti/templates/edit/share.pt:60 +msgid "Group" +msgstr "그룹" -#: kotti/templates/email-set-password.pt:3 -msgid "Your registration for ${site_title}" -msgstr "${site_title} 가입" +#: 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/templates/email-set-password.pt:9 -msgid "You are joining ${site_title}." -msgstr "${site_title}에 가입 중입니다." +#: kotti/views/users.py:587 +#, python-format +msgid "My preferences - ${title}" +msgstr "나의 선호사항" -#: kotti/templates/email-set-password.pt:12 -msgid "Click here to set your password and log in: ${url}" -msgstr "여기를 눌러 암호를 설정 후 로그인하십시오: ${url}" +#: kotti/views/users.py:145 +msgid "Invalid value" +msgstr "잘못된 값입니다." -#: kotti/templates/forbidden.pt:7 -msgid "Forbidden" -msgstr "금지됨" +#: kotti/views/users.py:151 +msgid "A user with that name already exists." +msgstr "그 이름을 쓰는 사용자가 이미 있습니다." -#: kotti/templates/login.pt:16 -msgid "Login" -msgstr "로그인" +#: kotti/views/users.py:158 +msgid "A user with that email already exists." +msgstr "그 전자메일을 쓰는 사용자가 이미 있습니다." -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 -msgid "Username or email" -msgstr "사용자 이름 혹은 전자메일" +#: kotti/views/users.py:181 +#, python-format +msgid "No such group: ${group}" +msgstr "그런 그룹이 없습니다: ${group}" -#: kotti/templates/login.pt:34 kotti/views/login.py:212 -#: kotti/views/users.py:212 -msgid "Password" -msgstr "암호" +#: kotti/views/users.py:196 kotti/views/edit/content.py:33 +#: kotti/templates/view/folder.pt:20 kotti/templates/edit/delete-nodes.pt:22 +#: kotti/templates/edit/contents.pt:39 kotti/templates/edit/change-state.pt:18 +msgid "Title" +msgstr "제목" -#: kotti/templates/login.pt:47 -msgid "Log in" -msgstr "로그인" +#: kotti/views/users.py:207 kotti/templates/site-setup/users.pt:68 +#: kotti/templates/edit/share.pt:48 +msgid "Name" +msgstr "이름" -#: kotti/templates/login.pt:65 -msgid "Forgot your password?" -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 "암호 재설정" - -#. 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 "" -"사용자 이름이나 전자메일을 입력한 뒤 아래의 ${reset_password}를 누르면 암호" -"를 재설정할 수 있는 링크를 전자메일로 받으실 수 있습니다." - -#: kotti/templates/login.pt:118 -msgid "Not registered yet?" -msgstr "아직 등록하지 않으셨습니까?" - -#: kotti/templates/login.pt:127 -msgid "Register for an account on this site." -msgstr "이 사이트에 계정을 등록하십시오." - -#: kotti/templates/workflow-dropdown.pt:19 -msgid "Make ${state}" -msgstr "\"${state}\"로(으로) 바꿈" - -#: kotti/templates/edit/breadcrumbs.pt:5 -msgid "You are here:" -msgstr "현재 위치:" - -#: kotti/templates/edit/change-state.pt:7 -msgid "Change workflow state" -msgstr "작업 상태 바꾸기" - -#: kotti/templates/edit/change-state.pt:14 -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:32 -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 -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 -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 -msgid "Modification Date" -msgstr "바꾼 날" - -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 -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:100 -msgid "Change state to" -msgstr "바꿀 상태" - -#: kotti/templates/edit/change-state.pt:103 -msgid "Select the new state where all chosen items should be set." -msgstr "고른 항목 모두가 갖게 될 상태를 선택하십시오." - -#: kotti/templates/edit/change-state.pt:111 -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 -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-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-nodes.pt:35 kotti/templates/edit/rename.pt:26 -msgid "New name" -msgstr "새 이름" - -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 -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 "" - -#: 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 -msgid "Size" -msgstr "크기" - -#: kotti/templates/edit/upload.pt:29 -msgid "Status" -msgstr "상태" - -#: kotti/templates/edit/upload.pt:32 -msgid "Progress" -msgstr "진행률" - -#: kotti/templates/edit/upload.pt:124 -msgid "Select file(s) to upload..." -msgstr "" - -#: kotti/templates/edit/upload.pt:134 -msgid "Upload ${number} files." -msgstr "" - -#: kotti/templates/edit/upload.pt:144 -msgid "Dismiss all errors" -msgstr "" - -#: kotti/templates/site-setup/delete-user.pt:20 -msgid "Are you sure you want to delete ${type} ${title}?" -msgstr "${type} \"${title}\"을 정말 지우겠습니까?" - -#: kotti/templates/site-setup/user.pt:13 -msgid "Back to User Management" -msgstr "사용자 관리로 되돌아가기" - -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 -#, python-format -msgid "Edit ${title}" -msgstr "\"${title}\" 편집" - -#: kotti/templates/site-setup/users.pt:15 -#, fuzzy -msgid "Search user(s) / group(s)" -msgstr "사용자/그룹 검색" - -#: kotti/templates/site-setup/users.pt:24 -msgid "Add user" -msgstr "사용자 추가" - -#: kotti/templates/site-setup/users.pt:33 -msgid "Add group" -msgstr "그룹 추가" - -#: kotti/templates/site-setup/users.pt:52 -msgid "Find users or groups" -msgstr "사용자나 그룹 찾기" - -#: kotti/templates/site-setup/users.pt:63 -msgid "User- / groupname" -msgstr "사용자나 그룹 이름" - -#: kotti/templates/site-setup/users.pt:70 -msgid "Blank search text finds all." -msgstr "비워두면 모두 찾습니다." - -#: kotti/templates/site-setup/users.pt:96 -msgid "Assign global roles" -msgstr "사이트 전체 역할 부여" - -#: kotti/templates/site-setup/users.pt:180 -msgid "Add new user" -msgstr "새 사용자 추가" - -#: kotti/templates/site-setup/users.pt:190 -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:364 kotti/views/edit/actions.py:408 -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:237 -msgid "You have reset your password." -msgstr "암호를 재설정했습니다." - -#: kotti/views/login.py:199 -msgid "You have been logged out." -msgstr "로그아웃되었습니다." - -#: kotti/views/login.py:65 kotti/views/users.py:270 -msgid "Full name" -msgstr "전체 이름" - -#: kotti/views/login.py:68 -msgid "Username" -msgstr "사용자 이름" - -#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 -msgid "Email" -msgstr "전자메일" - -#: kotti/views/login.py:108 -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:123 -#, python-format -msgid "Register - ${title}" -msgstr "등록 - ${title}" - -#: kotti/views/login.py:163 -msgid "Login failed." -msgstr "로그인이 실패했습니다." - -#: kotti/views/login.py:285 -#, python-format -msgid "Reset your password - ${title}." -msgstr "암호를 재설정하세요 - ${title}." - -#: kotti/views/login.py:159 -#, python-format -msgid "Welcome, ${user}!" -msgstr "${user}님, 환영합니다!" - -#: kotti/views/login.py:172 -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:177 -msgid "That username or email is not known by this system." -msgstr "이 시스템에 없는 사용자 이름이나 전자메일입니다." - -#: kotti/views/login.py:82 -msgid "Register" -msgstr "등록하기" - -#: kotti/views/login.py:89 kotti/views/login.py:256 -msgid "There was an error." -msgstr "오류가 있습니다." - -#: kotti/views/login.py:249 -msgid "Submit" -msgstr "제출" - -#: kotti/views/login.py:278 -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:587 -#, python-format -msgid "My preferences - ${title}" -msgstr "나의 선호사항" - -#: kotti/views/users.py:145 -msgid "Invalid value" -msgstr "잘못된 값입니다." - -#: kotti/views/users.py:151 -msgid "A user with that name already exists." -msgstr "그 이름을 쓰는 사용자가 이미 있습니다." - -#: kotti/views/users.py:158 -msgid "A user with that email already exists." -msgstr "그 전자메일을 쓰는 사용자가 이미 있습니다." - -#: kotti/views/users.py:181 -#, python-format -msgid "No such group: ${group}" -msgstr "그런 그룹이 없습니다: ${group}" - -#: kotti/views/users.py:219 -msgid "Active" -msgstr "활성" +#: kotti/views/users.py:219 +msgid "Active" +msgstr "활성" #: kotti/views/users.py:220 msgid "Untick this to deactivate the account." @@ -840,8 +467,8 @@ msgid "Add Group" msgstr "그룹 추가" #: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 -#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 +#: kotti/views/edit/actions.py:312 kotti/views/edit/actions.py:378 +#: kotti/views/edit/actions.py:424 kotti/views/edit/actions.py:420 msgid "No changes were made." msgstr "아무 것도 바뀌지 않았습니다." @@ -891,11 +518,6 @@ msgstr "\"${title}\"이(가) 잘라내어졌습니다." msgid "${title} was moved." msgstr "\"${title}\"이(가) 옮겨졌습니다." -#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 -#, python-format -msgid "${title} was deleted." -msgstr "\"${title}\"이(가) 지워졌습니다." - #: kotti/views/edit/actions.py:159 #, python-format msgid "${title} was pasted." @@ -915,70 +537,412 @@ msgstr "\"${title}\"이(가) 이제 둘러보기에 나타납니다." msgid "${title} is no longer visible in the navigation." msgstr "\"${title}\"이(가) 이젠 둘러보기에 나타나지 않습니다." -#: kotti/views/edit/actions.py:293 +#: kotti/views/edit/actions.py:277 kotti/views/edit/actions.py:306 +#, python-format +msgid "${title} was deleted." +msgstr "\"${title}\"이(가) 지워졌습니다." + +#: kotti/views/edit/actions.py:303 msgid "Nothing was deleted." msgstr "아무것도 지워지지 않았습니다." -#: kotti/views/edit/actions.py:329 kotti/views/edit/actions.py:356 +#: kotti/views/edit/actions.py:339 kotti/views/edit/actions.py:366 msgid "Name and title are required." msgstr "이름과 제목이 필요합니다." -#: kotti/views/edit/actions.py:333 +#: kotti/views/edit/actions.py:343 msgid "Item was renamed." msgstr "항목이 재명명되었습니다." -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:465 msgid "Move up" msgstr "위로 옮김" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:466 msgid "Move down" msgstr "아래로 옮김" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:467 msgid "Show" msgstr "보이기" -#: kotti/views/edit/actions.py:458 +#: kotti/views/edit/actions.py:468 msgid "Hide" msgstr "숨기기" -#: kotti/views/edit/actions.py:497 +#: kotti/views/edit/actions.py:507 msgid "You have to select items to perform an action." msgstr "적용할 항목을 선택해야합니다." -#: kotti/views/edit/actions.py:454 +#: kotti/views/edit/actions.py:464 msgid "Change State" msgstr "작업 상태 바꾸기" -#: kotti/views/edit/content.py:36 +#: kotti/views/edit/content.py:38 msgid "Description" msgstr "설명" -#: kotti/views/edit/content.py:42 +#: kotti/views/edit/content.py:44 msgid "Tags" msgstr "태그" -#: kotti/views/edit/content.py:51 +#: kotti/views/edit/content.py:53 msgid "Body" msgstr "본문" -#: kotti/views/edit/default_views.py:99 +#: kotti/views/edit/default_views.py:100 msgid "Default view has been reset to default." msgstr "기본형 보기로 재설정되었습니다." -#: kotti/views/edit/default_views.py:78 +#: kotti/views/edit/default_views.py:79 msgid "Default view" msgstr "기본형으로 보임" -#: kotti/views/edit/default_views.py:106 +#: kotti/views/edit/default_views.py:107 msgid "Default view has been set." msgstr "보이는 방식이 설정되었습니다." -#: kotti/views/edit/default_views.py:111 +#: kotti/views/edit/default_views.py:112 msgid "Default view could not be set." msgstr "보이는 방식을 설정할 수 없었습니다." +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "금지됨" + +#: kotti/templates/email-reset-password.pt:2 +msgid "Reset your password for ${site_title}." +msgstr "${site_title}의 암호를 재설정하십시오." + +#: 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:5 +msgid "Click this link to reset your password at ${site_title}: ${url}" +msgstr "이 링크를 눌러 ${site_title}에서의 암호를 설정하십시오: ${url}" + +#: kotti/templates/add-dropdown.pt:3 +msgid "Add" +msgstr "추가" + +#: kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" +msgstr "컨텐트 올리기" + +#: kotti/templates/email-set-password.pt:2 +msgid "Your registration for ${site_title}" +msgstr "${site_title} 가입" + +#: kotti/templates/email-set-password.pt:5 +msgid "You are joining ${site_title}." +msgstr "${site_title}에 가입 중입니다." + +#: kotti/templates/email-set-password.pt:6 +msgid "Click here to set your password and log in: ${url}" +msgstr "여기를 눌러 암호를 설정 후 로그인하십시오: ${url}" + +#: kotti/templates/editor-bar.pt:21 +msgid "View" +msgstr "보기" + +#: kotti/templates/editor-bar.pt:44 +msgid "Navigate" +msgstr "둘러보기" + +#: kotti/templates/editor-bar.pt:55 +msgid "Preferences" +msgstr "기본 설정" + +#: kotti/templates/editor-bar.pt:60 +msgid "Site Setup" +msgstr "사이트 설정" + +#: kotti/templates/editor-bar.pt:71 +msgid "Logout" +msgstr "로그아웃" + +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "\"${state}\"로(으로) 바꿈" + +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "보이는 방식 설정" + +#: kotti/templates/login.pt:15 +msgid "Login" +msgstr "로그인" + +#: kotti/templates/login.pt:20 kotti/templates/login.pt:57 +msgid "Username or email" +msgstr "사용자 이름 혹은 전자메일" + +#: kotti/templates/login.pt:33 +msgid "Log in" +msgstr "로그인" + +#: kotti/templates/login.pt:43 +msgid "Forgot your password?" +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:50 kotti/templates/login.pt:65 +msgid "Reset password" +msgstr "암호 재설정" + +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:48 +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/login.pt:76 +msgid "Not registered yet?" +msgstr "아직 등록하지 않으셨습니까?" + +#: kotti/templates/login.pt:82 +msgid "Register for an account on this site." +msgstr "이 사이트에 계정을 등록하십시오." + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "태그:" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "검색..." + +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:67 +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:112 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:40 +#: kotti/templates/edit/share.pt:47 kotti/templates/edit/change-state.pt:19 +msgid "Type" +msgstr "유형" + +#: kotti/templates/view/folder.pt:22 kotti/templates/edit/delete-nodes.pt:25 +#: kotti/templates/edit/contents.pt:43 kotti/templates/edit/change-state.pt:21 +msgid "Creation Date" +msgstr "만든 날" + +#: kotti/templates/view/folder.pt:23 kotti/templates/edit/delete-nodes.pt:26 +#: kotti/templates/edit/contents.pt:44 kotti/templates/edit/change-state.pt:22 +msgid "Modification Date" +msgstr "바꾼 날" + +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "검색결과" + +#: kotti/templates/http-errors/notfound.pt:6 +msgid "Not Found" +msgstr "" + +#: kotti/templates/http-errors/notfound.pt:7 +msgid "The resource could not be found" +msgstr "" + +#: kotti/templates/site-setup/delete-user.pt:9 +#: kotti/templates/edit/delete.pt:9 +msgid "Delete ${title}" +msgstr "\"${title}\" 삭제" + +#: 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:12 +msgid "Back to User Management" +msgstr "사용자 관리로 되돌아가기" + +#: kotti/templates/site-setup/users.pt:12 +#, fuzzy +msgid "Search user(s) / group(s)" +msgstr "사용자/그룹 검색" + +#: kotti/templates/site-setup/users.pt:17 +msgid "Add user" +msgstr "사용자 추가" + +#: kotti/templates/site-setup/users.pt:22 +msgid "Add group" +msgstr "그룹 추가" + +#: kotti/templates/site-setup/users.pt:34 +msgid "Find users or groups" +msgstr "사용자나 그룹 찾기" + +#: kotti/templates/site-setup/users.pt:39 +msgid "User- / groupname" +msgstr "사용자나 그룹 이름" + +#: kotti/templates/site-setup/users.pt:47 kotti/templates/edit/share.pt:14 +#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:26 +msgid "Search users and groups" +msgstr "사용자와 그룹 검색" + +#: kotti/templates/site-setup/users.pt:49 +msgid "Blank search text finds all." +msgstr "비워두면 모두 찾습니다." + +#: kotti/templates/site-setup/users.pt:55 kotti/templates/edit/share.pt:33 +msgid "Search" +msgstr "검색" + +#: kotti/templates/site-setup/users.pt:63 +msgid "Assign global roles" +msgstr "사이트 전체 역할 부여" + +#: kotti/templates/site-setup/users.pt:83 kotti/templates/edit/share.pt:63 +msgid "Gravatar" +msgstr "" + +#: kotti/templates/site-setup/users.pt:105 kotti/templates/edit/share.pt:83 +#: kotti/templates/edit/change-state.pt:64 +msgid "Apply changes" +msgstr "바꾸기 적용" + +#: kotti/templates/site-setup/users.pt:115 +msgid "Add new user" +msgstr "새 사용자 추가" + +#: kotti/templates/site-setup/users.pt:121 +msgid "Add new group" +msgstr "새 그룹 추가" + +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "사이트 둘러보기" + +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" +msgstr "내 컴퓨터에서 컨텐트 올리기" + +#: kotti/templates/edit/upload.pt:18 kotti/templates/edit/upload.pt:111 +msgid "Filename" +msgstr "파일 이름" + +#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:113 +msgid "Size" +msgstr "크기" + +#: kotti/templates/edit/upload.pt:21 +msgid "Status" +msgstr "상태" + +#: kotti/templates/edit/upload.pt:22 +msgid "Progress" +msgstr "진행률" + +#: kotti/templates/edit/upload.pt:88 +msgid "Select file(s) to upload..." +msgstr "" + +#: kotti/templates/edit/upload.pt:96 +msgid "Upload ${number} files." +msgstr "" + +#: kotti/templates/edit/upload.pt:101 +msgid "Dismiss all errors" +msgstr "" + +#: kotti/templates/edit/delete-nodes.pt:14 +msgid "Are you sure you want to delete the following items?" +msgstr "다음 항목을 정말 지우겠습니까?" + +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:41 +#: kotti/templates/edit/change-state.pt:20 +msgid "State" +msgstr "작업 상태" + +#: kotti/templates/edit/breadcrumbs.pt:8 +msgid "You are here:" +msgstr "현재 위치:" + +#: kotti/templates/edit/contents.pt:30 +msgid "No content items are contained here." +msgstr "여기엔 하위 항목이 없습니다." + +#: kotti/templates/edit/contents.pt:42 +msgid "Visibility" +msgstr "가시성" + +#: kotti/templates/edit/contents.pt:75 +msgid "Visible" +msgstr "보임" + +#: kotti/templates/edit/contents.pt:79 +msgid "Hidden" +msgstr "숨김" + +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "\"${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 "각 컨텐트 항목은 이름을 가지며, 이는 URL과 제목을 만드는데 사용됩니다. 이름과 제목의 올바른 형식에 대해 알려면 사용자 매뉴얼을 읽으십시오. 둘 다 아래에서 바꿀 수 있습니다." + +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +msgid "New name" +msgstr "새 이름" + +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:33 +msgid "New title" +msgstr "새 제목" + +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" +msgstr "\"${title}\"을(를) 정말 지우겠습니까?" + +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" +msgstr "\"${title}\" 공유하기" + +#: kotti/templates/edit/share.pt:42 +msgid "Assign local roles" +msgstr "컨텐츠별 역할 부여" + +#: kotti/templates/edit/change-state.pt:8 +msgid "Change workflow state" +msgstr "작업 상태 바꾸기" + +#: 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:40 +#: kotti/templates/edit/change-state.pt:45 +msgid "Include children" +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:50 +msgid "Change state to" +msgstr "바꿀 상태" + +#: kotti/templates/edit/change-state.pt:51 +msgid "Select the new state where all chosen items should be set." +msgstr "고른 항목 모두가 갖게 될 상태를 선택하십시오." + +#: kotti/templates/edit/change-state.pt:55 +msgid "No change" +msgstr "바꾸지 않음" + +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "${user}님, 환영합니다! 로그인되었습니다." + +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "환영합니다, 로그인되지 않았습니다." + #~ msgid "Private" #~ msgstr "내부 전용" diff --git a/kotti/locale/nl/LC_MESSAGES/Kotti.po b/kotti/locale/nl/LC_MESSAGES/Kotti.po index 62d3f1596..3cf5caa8f 100644 --- a/kotti/locale/nl/LC_MESSAGES/Kotti.po +++ b/kotti/locale/nl/LC_MESSAGES/Kotti.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Kotti 0.4.4\n" "Report-Msgid-Bugs-To: kotti@googlegroups.com\n" -"POT-Creation-Date: 2015-02-22 11:33+0100\n" +"POT-Creation-Date: 2015-09-29 14:24+0100\n" "PO-Revision-Date: 2014-02-13 21:51+0100\n" "Last-Translator: Wim Boucquaert \n" "Language-Team: nl \n" @@ -18,6 +18,81 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "Generated-By: Babel 0.9.6\n" +#: kotti/security.py:190 +msgid "Viewer" +msgstr "Lezer" + +#: kotti/security.py:191 +msgid "Editor" +msgstr "Editor" + +#: kotti/security.py:192 +msgid "Owner" +msgstr "Eigenaar" + +#: kotti/security.py:193 +msgid "Admin" +msgstr "Beheerder" + +#: kotti/resources.py:508 kotti/views/edit/actions.py:456 +msgid "Copy" +msgstr "Kopiëren" + +#: kotti/resources.py:509 kotti/views/edit/actions.py:457 +msgid "Cut" +msgstr "Knippen" + +#: kotti/resources.py:510 kotti/views/edit/actions.py:453 +msgid "Paste" +msgstr "Plakken" + +#: kotti/resources.py:511 kotti/views/edit/actions.py:458 +#: kotti/templates/edit/rename.pt:31 kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:40 +msgid "Rename" +msgstr "Hernoemen" + +#: kotti/resources.py:512 kotti/views/users.py:443 +#: kotti/views/edit/actions.py:460 +#: kotti/templates/site-setup/delete-user.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:50 kotti/templates/edit/delete.pt:24 +msgid "Delete" +msgstr "Verwijderen" + +#: kotti/resources.py:637 kotti/views/edit/content.py:85 +msgid "Document" +msgstr "Document" + +#: kotti/resources.py:726 kotti/views/edit/content.py:113 +#: kotti/views/edit/content.py:66 +msgid "File" +msgstr "Bestand" + +#: kotti/resources.py:772 kotti/views/edit/content.py:141 +msgid "Image" +msgstr "Afbeelding" + +#: kotti/resources.py:529 +msgid "Folder view" +msgstr "Map weergave" + +#: kotti/resources.py:523 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "Inhoud" + +#: kotti/resources.py:524 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "Bewerken" + +#: kotti/resources.py:525 +msgid "Share" +msgstr "Delen" + +#: kotti/resources.py:526 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "Acties" + #: kotti/populate.py:64 msgid "Welcome to Kotti" msgstr "" @@ -32,29 +107,25 @@ 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=\"http://kotti.readthedocs.org/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -62,13 +133,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,9 +146,9 @@ 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" msgstr "" -#: kotti/populate.py:121 +#: kotti/populate.py:123 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:124 +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:125 msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -123,491 +190,17 @@ 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:627 kotti/views/edit/content.py:82 -msgid "Document" -msgstr "Document" - -#: kotti/resources.py:666 kotti/views/edit/content.py:110 -#: kotti/views/edit/content.py:63 -msgid "File" -msgstr "Bestand" - -#: kotti/resources.py:753 kotti/views/edit/content.py:138 -msgid "Image" -msgstr "Afbeelding" - -#: kotti/resources.py:517 -msgid "Folder view" -msgstr "Map weergave" - -#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 -msgid "Contents" -msgstr "Inhoud" - -#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" -msgstr "Bewerken" - -#: kotti/resources.py:506 -msgid "Share" -msgstr "Delen" - -#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" -msgstr "Acties" - -#: kotti/resources.py:508 kotti/views/edit/actions.py:446 -msgid "Copy" -msgstr "Kopiëren" - -#: kotti/resources.py:509 kotti/views/edit/actions.py:447 -msgid "Cut" -msgstr "Knippen" - -#: kotti/resources.py:510 kotti/views/edit/actions.py:443 -msgid "Paste" -msgstr "Plakken" - -#: kotti/resources.py:511 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:448 -msgid "Rename" -msgstr "Hernoemen" - -#: kotti/resources.py:512 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:450 -msgid "Delete" -msgstr "Verwijderen" - -#: kotti/security.py:189 -msgid "Viewer" -msgstr "Lezer" - -#: kotti/security.py:190 -msgid "Editor" -msgstr "Editor" - -#: kotti/security.py:191 -msgid "Owner" -msgstr "Eigenaar" - -#: kotti/security.py:192 -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/templates/default-view-selector.pt:5 -msgid "Set default view" -msgstr "Zet standaard weergave" - -#: kotti/templates/editor-bar.pt:35 -msgid "View" -msgstr "Weergeven" - -#: kotti/templates/editor-bar.pt:76 -msgid "Navigate" -msgstr "Navigeren" - -#: kotti/templates/editor-bar.pt:98 -msgid "Preferences" -msgstr "Voorkeuren" - -#: kotti/templates/editor-bar.pt:109 -msgid "Site Setup" -msgstr "Site Instellingen" - -#: kotti/templates/editor-bar.pt:131 -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/login.pt:16 -msgid "Login" -msgstr "Aanmelden" - -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 -msgid "Username or email" -msgstr "Gebuikersnaam of e-mail" - -#: kotti/templates/login.pt:34 kotti/views/login.py:212 -#: kotti/views/users.py:212 -msgid "Password" -msgstr "Wachtwoord" - -#: kotti/templates/login.pt:47 -msgid "Log in" -msgstr "Aanmelden" - -#: kotti/templates/login.pt:65 -msgid "Forgot your password?" -msgstr "Wachtwoord vergeten?" - -#. 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 "Wachtwoord herstellen" - -#. 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 "" -"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 -msgid "Not registered yet?" -msgstr "" - -#: kotti/templates/login.pt:127 -#, 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 -msgid "Make ${state}" -msgstr "Maak ${state}" - -#: kotti/templates/edit/breadcrumbs.pt:5 -msgid "You are here:" -msgstr "U bent hier:" - -#: kotti/templates/edit/change-state.pt:7 -msgid "Change workflow state" -msgstr "Wijzig de workflow status" - -#: kotti/templates/edit/change-state.pt:14 -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." - -#: 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:32 -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 -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 -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 -msgid "Modification Date" -msgstr "Wijzigingsdatum" - -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 -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:100 -msgid "Change state to" -msgstr "Wij status naar" - -#: kotti/templates/edit/change-state.pt:103 -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." - -#: kotti/templates/edit/change-state.pt:111 -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 -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-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-nodes.pt:35 kotti/templates/edit/rename.pt:26 -msgid "New name" -msgstr "Nieuwe naam" - -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 -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 -msgid "Upload content from local file(s)" -msgstr "" - -#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:164 -#, fuzzy -msgid "Filename" -msgstr "Hernoemen" - -#: kotti/templates/edit/upload.pt:26 kotti/templates/edit/upload.pt:170 -msgid "Size" -msgstr "" - -#: kotti/templates/edit/upload.pt:29 -msgid "Status" -msgstr "" - -#: kotti/templates/edit/upload.pt:32 -msgid "Progress" -msgstr "" - -#: kotti/templates/edit/upload.pt:124 -msgid "Select file(s) to upload..." -msgstr "" - -#: kotti/templates/edit/upload.pt:134 -msgid "Upload ${number} files." -msgstr "" - -#: kotti/templates/edit/upload.pt:144 -msgid "Dismiss all errors" -msgstr "" - -#: kotti/templates/site-setup/delete-user.pt:20 -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 -msgid "Back to User Management" -msgstr "Terug naar gebruiksersbeheer" - -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 -#, python-format -msgid "Edit ${title}" -msgstr "Bewerken ${title}" - -#: kotti/templates/site-setup/users.pt:15 -#, fuzzy -msgid "Search user(s) / group(s)" -msgstr "Zoeken naar gebruikers en groepen" - -#: kotti/templates/site-setup/users.pt:24 -msgid "Add user" -msgstr "Gebruiker toevoegen" - -#: kotti/templates/site-setup/users.pt:33 -msgid "Add group" -msgstr "Groep toevoegen" - -#: kotti/templates/site-setup/users.pt:52 -#, fuzzy -msgid "Find users or groups" -msgstr "Zoeken naar gebruikers en groepen" - -#: kotti/templates/site-setup/users.pt:63 -msgid "User- / groupname" -msgstr "" - -#: kotti/templates/site-setup/users.pt:70 -#, fuzzy -msgid "Blank search text finds all." -msgstr "" -"Zoek en wijzig gebruikers (laat tekstveld leeg om alle gebruikers te vinden." - -#: kotti/templates/site-setup/users.pt:96 -#, fuzzy -msgid "Assign global roles" -msgstr "Locale rollen toewijzen" - -#: kotti/templates/site-setup/users.pt:180 -#, fuzzy -msgid "Add new user" -msgstr "Gebruiker toevoegen" - -#: kotti/templates/site-setup/users.pt:190 -#, 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:364 kotti/views/edit/actions.py:408 +#: kotti/views/edit/actions.py:374 kotti/views/edit/actions.py:418 msgid "Your changes have been saved." msgstr "Je wijzigingen werden opgeslagen." @@ -615,6 +208,11 @@ msgstr "Je wijzigingen werden opgeslagen." msgid "Item was added." msgstr "Item werd toegevoegd." +#: kotti/views/form.py:156 kotti/templates/site-setup/user.pt:21 +#, python-format +msgid "Edit ${title}" +msgstr "Bewerken ${title}" + #: kotti/views/form.py:264 #, python-format msgid "Maximum file size: ${size}MB" @@ -624,6 +222,13 @@ msgstr "Maximale bestandsgrootte: ${size}MB" msgid "Save" msgstr "Opslaan" +#: kotti/views/form.py:78 kotti/views/users.py:316 kotti/views/users.py:350 +#: kotti/views/users.py:442 kotti/templates/edit/delete-nodes.pt:45 +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:66 +#: kotti/templates/edit/rename-nodes.pt:42 +msgid "Cancel" +msgstr "Annuleren" + #: kotti/views/form.py:198 #, python-format msgid "Add ${type} to ${title}." @@ -634,6 +239,10 @@ msgstr "Voeg ${type} aan ${title} toe." msgid "Add ${type}." msgstr "Voeg ${type} toe." +#: kotti/views/site_setup.py:6 kotti/views/users.py:378 +msgid "User Management" +msgstr "Gebruikersbeheer" + #: kotti/views/login.py:237 msgid "You have reset your password." msgstr "Je wachtwoord werd hersteld." @@ -655,12 +264,8 @@ msgid "Email" msgstr "E-mailadres" #: kotti/views/login.py:108 -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." +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:123 #, python-format @@ -671,6 +276,11 @@ msgstr "Refistreer - ${title}" msgid "Login failed." msgstr "Aanmelden mislukt." +#: kotti/views/login.py:212 kotti/views/users.py:212 +#: kotti/templates/login.pt:25 +msgid "Password" +msgstr "Wachtwoord" + #: kotti/views/login.py:285 #, python-format msgid "Reset your password - ${title}." @@ -682,12 +292,8 @@ msgid "Welcome, ${user}!" msgstr "Welkom, ${user}!" #: kotti/views/login.py:172 -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." +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:177 msgid "That username or email is not known by this system." @@ -709,17 +315,20 @@ msgstr "Verzenden" 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:313 kotti/views/users.py:501 kotti/views/users.py:536 +#: kotti/templates/site-setup/users.pt:79 kotti/templates/edit/share.pt:59 +msgid "User" +msgstr "gebruiker" + +#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 +#: kotti/views/users.py:536 kotti/templates/site-setup/users.pt:80 +#: kotti/templates/edit/share.pt:60 +msgid "Group" +msgstr "Groep" #: 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." +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:587 #, python-format @@ -743,6 +352,17 @@ msgstr "Een gebruiker met deze naam bestaat reeds." msgid "No such group: ${group}" msgstr "Onbekende groep: ${group}" +#: kotti/views/users.py:196 kotti/views/edit/content.py:33 +#: kotti/templates/view/folder.pt:20 kotti/templates/edit/delete-nodes.pt:22 +#: kotti/templates/edit/contents.pt:39 kotti/templates/edit/change-state.pt:18 +msgid "Title" +msgstr "Titel" + +#: kotti/views/users.py:207 kotti/templates/site-setup/users.pt:68 +#: kotti/templates/edit/share.pt:48 +msgid "Name" +msgstr "Naam" + #: kotti/views/users.py:219 msgid "Active" msgstr "Actief" @@ -773,8 +393,8 @@ msgid "Add Group" msgstr "Groep toevoegen" #: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 -#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 +#: kotti/views/edit/actions.py:312 kotti/views/edit/actions.py:378 +#: kotti/views/edit/actions.py:424 kotti/views/edit/actions.py:420 msgid "No changes were made." msgstr "Geen wijzigingen aangebracht." @@ -824,11 +444,6 @@ msgstr "${title} werd geknipt." msgid "${title} was moved." msgstr "${title} werd verplaatst." -#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 -#, python-format -msgid "${title} was deleted." -msgstr "${title} werd verwijderd." - #: kotti/views/edit/actions.py:159 #, python-format msgid "${title} was pasted." @@ -848,70 +463,420 @@ msgstr "${title} is nu zichtbaar in de navigatie." msgid "${title} is no longer visible in the navigation." msgstr "${title} is niet langer zichtbaar in de navigatie." -#: kotti/views/edit/actions.py:293 +#: kotti/views/edit/actions.py:277 kotti/views/edit/actions.py:306 +#, python-format +msgid "${title} was deleted." +msgstr "${title} werd verwijderd." + +#: kotti/views/edit/actions.py:303 msgid "Nothing was deleted." msgstr "Er werd niks verwijderd." -#: kotti/views/edit/actions.py:329 kotti/views/edit/actions.py:356 +#: kotti/views/edit/actions.py:339 kotti/views/edit/actions.py:366 msgid "Name and title are required." msgstr "Naam en titel zijn vereist." -#: kotti/views/edit/actions.py:333 +#: kotti/views/edit/actions.py:343 msgid "Item was renamed." msgstr "Item werd hernoemd." -#: kotti/views/edit/actions.py:455 +#: kotti/views/edit/actions.py:465 msgid "Move up" msgstr "Naar boven" -#: kotti/views/edit/actions.py:456 +#: kotti/views/edit/actions.py:466 msgid "Move down" msgstr "Naar beneden" -#: kotti/views/edit/actions.py:457 +#: kotti/views/edit/actions.py:467 msgid "Show" msgstr "Weergeven" -#: kotti/views/edit/actions.py:458 +#: kotti/views/edit/actions.py:468 msgid "Hide" msgstr "Verbergen" -#: kotti/views/edit/actions.py:497 +#: kotti/views/edit/actions.py:507 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:454 +#: kotti/views/edit/actions.py:464 msgid "Change State" msgstr "Wijzig Status." -#: kotti/views/edit/content.py:36 +#: kotti/views/edit/content.py:38 msgid "Description" msgstr "Beschrijving" -#: kotti/views/edit/content.py:42 +#: kotti/views/edit/content.py:44 msgid "Tags" msgstr "Tags" -#: kotti/views/edit/content.py:51 +#: kotti/views/edit/content.py:53 msgid "Body" msgstr "Inhoud" -#: kotti/views/edit/default_views.py:99 +#: kotti/views/edit/default_views.py:100 msgid "Default view has been reset to default." msgstr "Standaard weergave werd hersteld naar standaard." -#: kotti/views/edit/default_views.py:78 +#: kotti/views/edit/default_views.py:79 msgid "Default view" msgstr "Standaard weergave" -#: kotti/views/edit/default_views.py:106 +#: kotti/views/edit/default_views.py:107 msgid "Default view has been set." msgstr "Standaard weergave is ingesteld." -#: kotti/views/edit/default_views.py:111 +#: kotti/views/edit/default_views.py:112 msgid "Default view could not be set." msgstr "Standaar weergave kon niet ingesteld worden." +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "Verboden" + +#: 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/add-dropdown.pt:3 +msgid "Add" +msgstr "Toevoegen" + +#: kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" +msgstr "Inhoud Opladen" + +#: 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/editor-bar.pt:21 +msgid "View" +msgstr "Weergeven" + +#: kotti/templates/editor-bar.pt:44 +msgid "Navigate" +msgstr "Navigeren" + +#: kotti/templates/editor-bar.pt:55 +msgid "Preferences" +msgstr "Voorkeuren" + +#: kotti/templates/editor-bar.pt:60 +msgid "Site Setup" +msgstr "Site Instellingen" + +#: kotti/templates/editor-bar.pt:71 +msgid "Logout" +msgstr "Afmelden" + +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "Maak ${state}" + +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "Zet standaard weergave" + +#: kotti/templates/login.pt:15 +msgid "Login" +msgstr "Aanmelden" + +#: kotti/templates/login.pt:20 kotti/templates/login.pt:57 +msgid "Username or email" +msgstr "Gebuikersnaam of e-mail" + +#: kotti/templates/login.pt:33 +msgid "Log in" +msgstr "Aanmelden" + +#: kotti/templates/login.pt:43 +msgid "Forgot your password?" +msgstr "Wachtwoord vergeten?" + +#. 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:50 kotti/templates/login.pt:65 +msgid "Reset password" +msgstr "Wachtwoord herstellen" + +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:48 +#, 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." + +#: kotti/templates/login.pt:76 +msgid "Not registered yet?" +msgstr "" + +#: kotti/templates/login.pt:82 +#, fuzzy +msgid "Register for an account on this site." +msgstr "Nog niet geregistreerd? ${register} voor een account op deze site." + +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "Getagd met:" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "Zoeken..." + +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:67 +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:112 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:40 +#: kotti/templates/edit/share.pt:47 kotti/templates/edit/change-state.pt:19 +msgid "Type" +msgstr "Type" + +#: kotti/templates/view/folder.pt:22 kotti/templates/edit/delete-nodes.pt:25 +#: kotti/templates/edit/contents.pt:43 kotti/templates/edit/change-state.pt:21 +msgid "Creation Date" +msgstr "Aanmaak datum" + +#: kotti/templates/view/folder.pt:23 kotti/templates/edit/delete-nodes.pt:26 +#: kotti/templates/edit/contents.pt:44 kotti/templates/edit/change-state.pt:22 +msgid "Modification Date" +msgstr "Wijzigingsdatum" + +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "Zoek Resultaten" + +#: kotti/templates/http-errors/notfound.pt:6 +msgid "Not Found" +msgstr "" + +#: kotti/templates/http-errors/notfound.pt:7 +msgid "The resource could not be found" +msgstr "" + +#: kotti/templates/site-setup/delete-user.pt:9 +#: kotti/templates/edit/delete.pt:9 +msgid "Delete ${title}" +msgstr "Verwijder ${title}" + +#: 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:12 +msgid "Back to User Management" +msgstr "Terug naar gebruiksersbeheer" + +#: 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:17 +msgid "Add user" +msgstr "Gebruiker toevoegen" + +#: kotti/templates/site-setup/users.pt:22 +msgid "Add group" +msgstr "Groep toevoegen" + +#: 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:39 +msgid "User- / groupname" +msgstr "" + +#: kotti/templates/site-setup/users.pt:47 kotti/templates/edit/share.pt:14 +#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:26 +msgid "Search users and groups" +msgstr "Zoeken naar gebruikers en groepen" + +#: kotti/templates/site-setup/users.pt:49 +#, fuzzy +msgid "Blank search text finds all." +msgstr "Zoek en wijzig gebruikers (laat tekstveld leeg om alle gebruikers te vinden." + +#: kotti/templates/site-setup/users.pt:55 kotti/templates/edit/share.pt:33 +msgid "Search" +msgstr "Zoeken" + +#: kotti/templates/site-setup/users.pt:63 +#, fuzzy +msgid "Assign global roles" +msgstr "Locale rollen toewijzen" + +#: kotti/templates/site-setup/users.pt:83 kotti/templates/edit/share.pt:63 +msgid "Gravatar" +msgstr "Gravatar" + +#: kotti/templates/site-setup/users.pt:105 kotti/templates/edit/share.pt:83 +#: kotti/templates/edit/change-state.pt:64 +msgid "Apply changes" +msgstr "Wijzigingen toepassen" + +#: kotti/templates/site-setup/users.pt:115 +#, fuzzy +msgid "Add new user" +msgstr "Gebruiker toevoegen" + +#: kotti/templates/site-setup/users.pt:121 +#, fuzzy +msgid "Add new group" +msgstr "Groep toevoegen" + +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "Site Navigeren" + +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" +msgstr "" + +#: kotti/templates/edit/upload.pt:18 kotti/templates/edit/upload.pt:111 +#, fuzzy +msgid "Filename" +msgstr "Hernoemen" + +#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:113 +msgid "Size" +msgstr "" + +#: kotti/templates/edit/upload.pt:21 +msgid "Status" +msgstr "" + +#: kotti/templates/edit/upload.pt:22 +msgid "Progress" +msgstr "" + +#: kotti/templates/edit/upload.pt:88 +msgid "Select file(s) to upload..." +msgstr "" + +#: kotti/templates/edit/upload.pt:96 +msgid "Upload ${number} files." +msgstr "" + +#: kotti/templates/edit/upload.pt:101 +msgid "Dismiss all errors" +msgstr "" + +#: kotti/templates/edit/delete-nodes.pt:14 +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-nodes.pt:24 kotti/templates/edit/contents.pt:41 +#: kotti/templates/edit/change-state.pt:20 +msgid "State" +msgstr "Status" + +#: kotti/templates/edit/breadcrumbs.pt:8 +msgid "You are here:" +msgstr "U bent hier:" + +#: kotti/templates/edit/contents.pt:30 +msgid "No content items are contained here." +msgstr "Bevat geen content items." + +#: kotti/templates/edit/contents.pt:42 +msgid "Visibility" +msgstr "Zichtbaarheid" + +#: kotti/templates/edit/contents.pt:75 +msgid "Visible" +msgstr "Zichtbaar" + +#: kotti/templates/edit/contents.pt:79 +msgid "Hidden" +msgstr "Verborgen" + +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "Hernoemen ${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 "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:19 kotti/templates/edit/rename-nodes.pt:26 +msgid "New name" +msgstr "Nieuwe naam" + +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:33 +msgid "New title" +msgstr "Nieuwe titel" + +#: 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/share.pt:8 +msgid "Share ${title}" +msgstr "Delen ${title}" + +#: kotti/templates/edit/share.pt:42 +msgid "Assign local roles" +msgstr "Locale rollen toewijzen" + +#: kotti/templates/edit/change-state.pt:8 +msgid "Change workflow state" +msgstr "Wijzig de workflow status" + +#: 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." + +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:45 +msgid "Include children" +msgstr "Inclusief onderliggende" + +#: 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:50 +msgid "Change state to" +msgstr "Wij status naar" + +#: kotti/templates/edit/change-state.pt:51 +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." + +#: kotti/templates/edit/change-state.pt:55 +msgid "No change" +msgstr "Geen wijzigingen" + +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "Welkom, ${user}! U bent nu aangemeld." + +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "Wilkom, je bent niet aangemeld." + #~ msgid "Private" #~ msgstr "Privaat" diff --git a/kotti/locale/pl/LC_MESSAGES/Kotti.po b/kotti/locale/pl/LC_MESSAGES/Kotti.po index 7e67456a6..756b68c07 100644 --- a/kotti/locale/pl/LC_MESSAGES/Kotti.po +++ b/kotti/locale/pl/LC_MESSAGES/Kotti.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: Kotti 1.1.5-dev\n" -"POT-Creation-Date: 2015-09-13 21:09+0200\n" +"POT-Creation-Date: 2015-09-29 14:24+0100\n" "PO-Revision-Date: 2015-09-09 12:49+0200\n" "Last-Translator: Piotr Dobrogost \n" "Language-Team: Polish\n" @@ -14,8 +14,82 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Lingua 3.8\n" -"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " -"|| n%100>=20) ? 1 : 2);\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: kotti/security.py:190 +msgid "Viewer" +msgstr "Czytelnik" + +#: kotti/security.py:191 +msgid "Editor" +msgstr "Edytor" + +#: kotti/security.py:192 +msgid "Owner" +msgstr "Właściciel" + +#: kotti/security.py:193 +msgid "Admin" +msgstr "Administrator" + +#: kotti/resources.py:508 kotti/views/edit/actions.py:456 +msgid "Copy" +msgstr "Kopiuj" + +#: kotti/resources.py:509 kotti/views/edit/actions.py:457 +msgid "Cut" +msgstr "Wytnij" + +#: kotti/resources.py:510 kotti/views/edit/actions.py:453 +msgid "Paste" +msgstr "Wklej" + +#: kotti/resources.py:511 kotti/views/edit/actions.py:458 +#: kotti/templates/edit/rename.pt:31 kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:40 +msgid "Rename" +msgstr "Zmień nazwę" + +#: kotti/resources.py:512 kotti/views/users.py:443 +#: kotti/views/edit/actions.py:460 +#: kotti/templates/site-setup/delete-user.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:50 kotti/templates/edit/delete.pt:24 +msgid "Delete" +msgstr "Usuń" + +#: kotti/resources.py:637 kotti/views/edit/content.py:85 +msgid "Document" +msgstr "Dokument" + +#: kotti/resources.py:726 kotti/views/edit/content.py:113 +#: kotti/views/edit/content.py:66 +msgid "File" +msgstr "Plik" + +#: kotti/resources.py:772 kotti/views/edit/content.py:141 +msgid "Image" +msgstr "Obraz" + +#: kotti/resources.py:529 +msgid "Folder view" +msgstr "Widok katalogów" + +#: kotti/resources.py:523 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "Zawartość" + +#: kotti/resources.py:524 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "Edytuj" + +#: kotti/resources.py:525 +msgid "Share" +msgstr "Udostępnij" + +#: kotti/resources.py:526 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "Akcje" #: kotti/populate.py:64 msgid "Welcome to Kotti" @@ -24,22 +98,20 @@ msgstr "Witaj w Kotti" #: kotti/populate.py:65 msgid "Congratulations! You have successfully installed Kotti." msgstr "Gratulacje! Właśnie zainstalowałeś Kotti." - + #: kotti/populate.py:66 #, 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" @@ -51,8 +123,7 @@ msgid "" "

    \n" "

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

    \n" @@ -60,14 +131,12 @@ msgid "" "
    \n" "

    Add-ons

    \n" "

    \n" -" A number of add-ons allow you to extend the functionality of " -"your\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" @@ -91,27 +160,23 @@ msgstr "" "\n" "

    Zaloguj się

    \n" "

    \n" -" Możesz zalogować się do " -"twojego serwisu\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" +" 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" +" Dowiedz się jak skonfigurować tytył i inne ustawienia używając prostego pliku tekstowego w systemie plików.\n" "

    \n" "

    \n" " \n" +" href=\"http://kotti.readthedocs.org/en/latest/developing/basic/configuration.html\">\n" " Podręcznik konfiguracji\n" " \n" "

    \n" @@ -119,13 +184,11 @@ msgstr "" "
    \n" "

    Wtyczki

    \n" "

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

    \n" "

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

    \n" @@ -133,9 +196,7 @@ msgstr "" "
    \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" +" 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" " \"five\n" "

    \n" "\n" @@ -182,8 +238,7 @@ msgid "" "\n" "

    \n" "Photo credit: \"Northern Lights Formation\" by FlugKerl2.\n" -"\n" +"\n" "Copyright info.\n" "Originally published in the\n" " Extra EA-300\n" @@ -193,8 +248,7 @@ msgstr "" "\n" "

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

    \n" "\n" @@ -212,772 +266,677 @@ msgstr "" "\n" "

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

    \n" -#: kotti/resources.py:504 kotti/views/edit/actions.py:456 -msgid "Copy" -msgstr "Kopiuj" - -#: kotti/resources.py:505 kotti/views/edit/actions.py:457 -msgid "Cut" -msgstr "Wytnij" +#: kotti/views/form.py:79 kotti/views/users.py:69 +#: kotti/views/edit/actions.py:374 kotti/views/edit/actions.py:418 +msgid "Your changes have been saved." +msgstr "Twoje zmiany zostały zapisane." -#: kotti/resources.py:506 kotti/views/edit/actions.py:453 -msgid "Paste" -msgstr "Wklej" +#: kotti/views/form.py:174 +msgid "Item was added." +msgstr "Element został dodany." -#: kotti/resources.py:507 kotti/templates/edit/rename-nodes.pt:8 -#: kotti/templates/edit/rename-nodes.pt:40 kotti/templates/edit/rename.pt:31 -#: kotti/views/edit/actions.py:458 -msgid "Rename" -msgstr "Zmień nazwę" +#: kotti/views/form.py:156 kotti/templates/site-setup/user.pt:21 +#, python-format +msgid "Edit ${title}" +msgstr "Edycja ${title}" -#: kotti/resources.py:508 kotti/templates/edit/delete-nodes.pt:9 -#: kotti/templates/edit/delete-nodes.pt:50 kotti/templates/edit/delete.pt:24 -#: kotti/templates/site-setup/delete-user.pt:24 kotti/views/users.py:443 -#: kotti/views/edit/actions.py:460 -msgid "Delete" -msgstr "Usuń" +#: kotti/views/form.py:264 +#, python-format +msgid "Maximum file size: ${size}MB" +msgstr "Maksymalny rozmiar pliku: ${size}MB" -#: kotti/resources.py:635 kotti/views/edit/content.py:85 -msgid "Document" -msgstr "Dokument" +#: kotti/views/form.py:77 kotti/views/users.py:441 +msgid "Save" +msgstr "Zapisz" -#: kotti/resources.py:726 kotti/views/edit/content.py:113 -#: kotti/views/edit/content.py:66 -msgid "File" -msgstr "Plik" +#: kotti/views/form.py:78 kotti/views/users.py:316 kotti/views/users.py:350 +#: kotti/views/users.py:442 kotti/templates/edit/delete-nodes.pt:45 +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:66 +#: kotti/templates/edit/rename-nodes.pt:42 +msgid "Cancel" +msgstr "Anuluj" -#: kotti/resources.py:773 kotti/views/edit/content.py:141 -msgid "Image" -msgstr "Obraz" +#: kotti/views/form.py:198 +#, python-format +msgid "Add ${type} to ${title}." +msgstr "Dodaj ${type} do ${title}." -#: kotti/resources.py:525 -msgid "Folder view" -msgstr "Widok katalogów" +#: kotti/views/form.py:202 +#, python-format +msgid "Add ${type}." +msgstr "Dodaj ${type}." -#: kotti/resources.py:519 kotti/templates/view/folder.pt:15 -msgid "Contents" -msgstr "Zawartość" +#: kotti/views/site_setup.py:6 kotti/views/users.py:378 +msgid "User Management" +msgstr "Zarządzanie użytkownikami" -#: kotti/resources.py:520 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" -msgstr "Edytuj" +#: kotti/views/login.py:237 +msgid "You have reset your password." +msgstr "Zresetowałeś swoje hasło." -#: kotti/resources.py:521 -msgid "Share" -msgstr "Udostępnij" +#: kotti/views/login.py:199 +msgid "You have been logged out." +msgstr "Zostałeś wylogowany." -#: kotti/resources.py:522 kotti/templates/actions-dropdown.pt:3 -msgid "Actions" -msgstr "Akcje" +#: kotti/views/login.py:65 kotti/views/users.py:270 +msgid "Full name" +msgstr "Imię i nazwisko" -#: kotti/security.py:189 -msgid "Viewer" -msgstr "Czytelnik" +#: kotti/views/login.py:68 +msgid "Username" +msgstr "Nazwa użytkownika" -#: kotti/security.py:190 -msgid "Editor" -msgstr "Edytor" +#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 +msgid "Email" +msgstr "" -#: kotti/security.py:191 -msgid "Owner" -msgstr "Właściciel" +#: kotti/views/login.py:108 +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/security.py:192 -msgid "Admin" -msgstr "Administrator" +#: kotti/views/login.py:123 +#, python-format +msgid "Register - ${title}" +msgstr "Zarejestruj - ${title}" -#: kotti/templates/add-dropdown.pt:3 -msgid "Add" -msgstr "Dodaj" +#: kotti/views/login.py:163 +msgid "Login failed." +msgstr "Nie udało się zalogować." -#: kotti/templates/add-dropdown.pt:13 -msgid "Upload Content" -msgstr "Dołącz treści" +#: kotti/views/login.py:212 kotti/views/users.py:212 +#: kotti/templates/login.pt:25 +msgid "Password" +msgstr "Hasło" -#: kotti/templates/default-view-selector.pt:3 -msgid "Set default view" -msgstr "Ustaw domyślny widok" +#: kotti/views/login.py:285 +#, python-format +msgid "Reset your password - ${title}." +msgstr "Resetowanie hasła - ${title}." -#: kotti/templates/editor-bar.pt:21 -msgid "View" -msgstr "Widok" +#: kotti/views/login.py:159 +#, python-format +msgid "Welcome, ${user}!" +msgstr "Witaj, ${user}!" -#: kotti/templates/editor-bar.pt:44 -msgid "Navigate" -msgstr "Nawigacja" +#: kotti/views/login.py:172 +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/templates/editor-bar.pt:55 -msgid "Preferences" -msgstr "Ustawienia" +#: kotti/views/login.py:177 +msgid "That username or email is not known by this system." +msgstr "Nieznana nazwa użytkownika lub email." -#: kotti/templates/editor-bar.pt:60 -msgid "Site Setup" -msgstr "Ustawienia strony" +#: kotti/views/login.py:82 +msgid "Register" +msgstr "Zarejestruj" -#: kotti/templates/editor-bar.pt:71 -msgid "Logout" -msgstr "Wyloguj się" +#: kotti/views/login.py:89 kotti/views/login.py:256 +msgid "There was an error." +msgstr "Wystąpił błąd." -#: kotti/templates/email-reset-password.pt:2 -msgid "Reset your password for ${site_title}." -msgstr "Zresetuj swoje hasło do ${site_title}." +#: kotti/views/login.py:249 +msgid "Submit" +msgstr "Zatwierdź" -#: kotti/templates/email-reset-password.pt:4 -#: kotti/templates/email-set-password.pt:4 -msgid "Hello, ${user_title}!" -msgstr "Witaj, ${user_title}!" +#: kotti/views/login.py:278 +msgid "Your password reset token may have expired." +msgstr "Twój token do resetu hasła mógł stracić ważność." -#: 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/views/users.py:313 kotti/views/users.py:501 kotti/views/users.py:536 +#: kotti/templates/site-setup/users.pt:79 kotti/templates/edit/share.pt:59 +msgid "User" +msgstr "Użytkownik" -#: kotti/templates/email-set-password.pt:2 -msgid "Your registration for ${site_title}" -msgstr "Rejestracja na ${site_title}" +#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 +#: kotti/views/users.py:536 kotti/templates/site-setup/users.pt:80 +#: kotti/templates/edit/share.pt:60 +msgid "Group" +msgstr "Grupa" -#: kotti/templates/email-set-password.pt:5 -msgid "You are joining ${site_title}." -msgstr "Rejestrujesz się na ${site_title}." +#: 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 "Zostaw to puste i zaznacz 'Wyślij link do rejestracji' poniżej, aby użytkownik sam ustawił sobie hasło." -#: 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/views/users.py:587 +#, python-format +msgid "My preferences - ${title}" +msgstr "Moje ustawienia - ${title}" -#: kotti/templates/forbidden.pt:10 -msgid "Forbidden" -msgstr "Brak dostępu" +#: kotti/views/users.py:145 +msgid "Invalid value" +msgstr "Niepoprawna wartość" -#: kotti/templates/login.pt:15 -msgid "Login" -msgstr "Logowanie" +#: kotti/views/users.py:151 +msgid "A user with that name already exists." +msgstr "Użytkownik z takim imieniem już istnieje." -#: kotti/templates/login.pt:20 kotti/templates/login.pt:57 -msgid "Username or email" -msgstr "Nazwa użytkownika lub email" +#: kotti/views/users.py:158 +msgid "A user with that email already exists." +msgstr "Użytkownik z takim adresem email już istnieje." -#: kotti/templates/login.pt:25 kotti/views/login.py:212 -#: kotti/views/users.py:212 -msgid "Password" -msgstr "Hasło" - -#: kotti/templates/login.pt:33 -msgid "Log in" -msgstr "Zaloguj się" - -#: kotti/templates/login.pt:43 -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:50 kotti/templates/login.pt:65 -msgid "Reset password" -msgstr "Reset hasła" - -#. Canonical text for ${reset_password} is: "Reset password" -#: kotti/templates/login.pt:48 -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:76 -msgid "Not registered yet?" -msgstr "Nie jesteś jeszcze zarejestrowany?" - -#: kotti/templates/login.pt:82 -msgid "Register for an account on this site." -msgstr "Zarejestruj się na stronie." - -#: kotti/templates/workflow-dropdown.pt:14 -msgid "Make ${state}" -msgstr "Zrób ${state}" - -#: kotti/templates/edit/breadcrumbs.pt:8 -msgid "You are here:" -msgstr "Jesteś tutaj:" - -#: 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/views/users.py:181 +#, python-format +msgid "No such group: ${group}" +msgstr "Nie ma takiej grupy: ${group}" -#: kotti/templates/edit/change-state.pt:18 kotti/templates/edit/contents.pt:39 -#: kotti/templates/edit/delete-nodes.pt:22 kotti/templates/view/folder.pt:20 #: kotti/views/users.py:196 kotti/views/edit/content.py:33 +#: kotti/templates/view/folder.pt:20 kotti/templates/edit/delete-nodes.pt:22 +#: kotti/templates/edit/contents.pt:39 kotti/templates/edit/change-state.pt:18 msgid "Title" msgstr "Tytuł" -#: kotti/templates/edit/change-state.pt:19 kotti/templates/edit/contents.pt:40 -#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/share.pt:47 -#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:112 -#: kotti/templates/site-setup/users.pt:67 kotti/templates/view/folder.pt:21 -msgid "Type" -msgstr "Typ" - -#: kotti/templates/edit/change-state.pt:20 kotti/templates/edit/contents.pt:41 -#: kotti/templates/edit/delete-nodes.pt:24 -msgid "State" -msgstr "Stan" - -#: kotti/templates/edit/change-state.pt:21 kotti/templates/edit/contents.pt:43 -#: kotti/templates/edit/delete-nodes.pt:25 kotti/templates/view/folder.pt:22 -msgid "Creation Date" -msgstr "Data utworzenia" - -#: kotti/templates/edit/change-state.pt:22 kotti/templates/edit/contents.pt:44 -#: kotti/templates/edit/delete-nodes.pt:26 kotti/templates/view/folder.pt:23 -msgid "Modification Date" -msgstr "Data modyfikacji" - -#: kotti/templates/edit/change-state.pt:40 -#: kotti/templates/edit/change-state.pt:45 -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/views/users.py:207 kotti/templates/site-setup/users.pt:68 +#: kotti/templates/edit/share.pt:48 +msgid "Name" +msgstr "Nazwa" -#: kotti/templates/edit/change-state.pt:50 -msgid "Change state to" -msgstr "Zmień stan na" +#: kotti/views/users.py:219 +msgid "Active" +msgstr "Aktywne" -#: kotti/templates/edit/change-state.pt:51 -msgid "Select the new state where all chosen items should be set." -msgstr "Zaznacz nowy stan, gdzie wszystkie wybrane elementy zostaną zmienione." +#: kotti/views/users.py:220 +msgid "Untick this to deactivate the account." +msgstr "Odznacz, aby dezaktywować konto." -#: kotti/templates/edit/change-state.pt:55 -msgid "No change" -msgstr "Bez zmian" +#: kotti/views/users.py:226 +msgid "Global roles" +msgstr "Globalne role" -#: kotti/templates/edit/change-state.pt:64 kotti/templates/edit/share.pt:83 -#: kotti/templates/site-setup/users.pt:105 -msgid "Apply changes" -msgstr "Zapisz zmiany" +#: kotti/views/users.py:230 +msgid "Groups" +msgstr "Grupy" -#: kotti/templates/edit/change-state.pt:66 -#: kotti/templates/edit/delete-nodes.pt:45 kotti/templates/edit/delete.pt:19 -#: kotti/templates/edit/rename-nodes.pt:42 kotti/views/form.py:78 -#: kotti/views/users.py:316 kotti/views/users.py:350 kotti/views/users.py:442 -msgid "Cancel" -msgstr "Anuluj" +#: kotti/views/users.py:315 +msgid "Add User" +msgstr "Dodaj użytkownika" -#: kotti/templates/edit/contents.pt:30 -msgid "No content items are contained here." -msgstr "Brak elementów potomnych." +#: kotti/views/users.py:339 +#, python-format +msgid "${title} was added." +msgstr "${title} został dodany." -#: kotti/templates/edit/contents.pt:42 -msgid "Visibility" -msgstr "Widoczność" +#: kotti/views/users.py:349 +msgid "Add Group" +msgstr "Dodaj grupę" -#: kotti/templates/edit/contents.pt:75 -msgid "Visible" -msgstr "Widoczny" +#: kotti/views/users.py:465 kotti/views/users.py:71 +#: kotti/views/edit/actions.py:312 kotti/views/edit/actions.py:378 +#: kotti/views/edit/actions.py:424 kotti/views/edit/actions.py:420 +msgid "No changes were made." +msgstr "Nie wykonano żadnych zmian." -#: kotti/templates/edit/contents.pt:79 -msgid "Hidden" -msgstr "Ukryty" +#: kotti/views/users.py:558 +msgid "No name was given." +msgstr "Nie przekazano nazwy." -#: kotti/templates/edit/delete-nodes.pt:14 -msgid "Are you sure you want to delete the following items?" -msgstr "Czy jesteś pewien, że chcesz usunąć wybrane elementy?" +#: kotti/views/users.py:98 +msgid "No users or groups were found." +msgstr "Nie znaleziono użytkowników ani grup." -#: kotti/templates/edit/delete.pt:9 -#: kotti/templates/site-setup/delete-user.pt:9 -msgid "Delete ${title}" -msgstr "Usuń ${title}" +#: kotti/views/users.py:505 +#, python-format +msgid "Edit ${principal_type} ${title}" +msgstr "Edycja ${principal_type} ${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/views/users.py:533 +msgid "User was not found." +msgstr "Użytkownik nie został odnaleziony." -#: kotti/templates/edit/nav-tree-view.pt:8 -msgid "Navigate Site" -msgstr "Nawigacja strony" +#: kotti/views/users.py:324 +msgid "Send password registration link." +msgstr "Wyślij link do rejestracji na email." -#: kotti/templates/edit/rename-nodes.pt:13 kotti/templates/edit/rename.pt:11 -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/views/users.py:543 +#, python-format +msgid "${principal_type} ${title} was deleted." +msgstr "${principal_type} ${title} został usunięty." -#: kotti/templates/edit/rename-nodes.pt:26 kotti/templates/edit/rename.pt:19 -msgid "New name" -msgstr "Nowa nazwa" +#: kotti/views/users.py:551 +#, python-format +msgid "Delete ${principal_type} ${title}" +msgstr "Usuń ${principal_type} ${title}" -#: kotti/templates/edit/rename-nodes.pt:33 kotti/templates/edit/rename.pt:25 -msgid "New title" -msgstr "Nowy tytuł" +#: kotti/views/edit/actions.py:106 +#, python-format +msgid "${title} was copied." +msgstr "${title} został skopiowany." -#: kotti/templates/edit/rename.pt:8 -msgid "Rename ${title}" -msgstr "Zmień nazwę ${title}" +#: kotti/views/edit/actions.py:124 +#, python-format +msgid "${title} was cut." +msgstr "${title} został wycięty." -#: kotti/templates/edit/share.pt:8 -msgid "Share ${title}" -msgstr "Udostępnij ${title}" +#: kotti/views/edit/actions.py:183 +#, python-format +msgid "${title} was moved." +msgstr "${title} został przeniesiony." -#: kotti/templates/edit/share.pt:14 kotti/templates/edit/share.pt:19 -#: kotti/templates/edit/share.pt:26 kotti/templates/site-setup/users.pt:47 -msgid "Search users and groups" -msgstr "Szukaj użytkowników i grup" +#: kotti/views/edit/actions.py:159 +#, python-format +msgid "${title} was pasted." +msgstr "${title} został wklejony." -#: kotti/templates/edit/share.pt:33 kotti/templates/site-setup/users.pt:55 -msgid "Search" -msgstr "Szukaj" +#: kotti/views/edit/actions.py:162 +msgid "Could not paste node. It no longer exists." +msgstr "Nie można wkleić. Element już nie istnieje." -#: kotti/templates/edit/share.pt:42 -msgid "Assign local roles" -msgstr "Przypisz lokalne role" +#: kotti/views/edit/actions.py:225 +#, python-format +msgid "${title} is now visible in the navigation." +msgstr "${title} jest teraz widoczny w nawigacji." -#: kotti/templates/edit/share.pt:48 kotti/templates/site-setup/users.pt:68 -#: kotti/views/users.py:207 -msgid "Name" -msgstr "Nazwa" +#: kotti/views/edit/actions.py:228 +#, python-format +msgid "${title} is no longer visible in the navigation." +msgstr "${title} nie jest już widoczny w nawigacji." -#: kotti/templates/edit/share.pt:59 kotti/templates/site-setup/users.pt:79 -#: kotti/views/users.py:313 kotti/views/users.py:501 kotti/views/users.py:536 -msgid "User" -msgstr "Użytkownik" +#: kotti/views/edit/actions.py:277 kotti/views/edit/actions.py:306 +#, python-format +msgid "${title} was deleted." +msgstr "${title} został usunięty." -#: kotti/templates/edit/share.pt:60 kotti/templates/site-setup/users.pt:80 -#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 -#: kotti/views/users.py:536 -msgid "Group" -msgstr "Grupa" +#: kotti/views/edit/actions.py:303 +msgid "Nothing was deleted." +msgstr "Nic nie zostało usunięte" -#: kotti/templates/edit/share.pt:63 kotti/templates/site-setup/users.pt:83 -msgid "Gravatar" -msgstr "" +#: kotti/views/edit/actions.py:339 kotti/views/edit/actions.py:366 +msgid "Name and title are required." +msgstr "Nazwa i tytuł są obowiązkowe." -#: kotti/templates/edit/upload.pt:10 -msgid "Upload content from local file(s)" -msgstr "Załącz zawartość z lokalnego pliku(ów)" +#: kotti/views/edit/actions.py:343 +msgid "Item was renamed." +msgstr "Zmieniono nazwę elementu." -#: kotti/templates/edit/upload.pt:18 kotti/templates/edit/upload.pt:111 -msgid "Filename" -msgstr "Nazwa pliku" +#: kotti/views/edit/actions.py:465 +msgid "Move up" +msgstr "W górę" -#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:113 -msgid "Size" -msgstr "Rozmiar" +#: kotti/views/edit/actions.py:466 +msgid "Move down" +msgstr "W dół" -#: kotti/templates/edit/upload.pt:21 -msgid "Status" -msgstr "Status" +#: kotti/views/edit/actions.py:467 +msgid "Show" +msgstr "Pokaż" -#: kotti/templates/edit/upload.pt:22 -msgid "Progress" -msgstr "Postęp" +#: kotti/views/edit/actions.py:468 +msgid "Hide" +msgstr "Ukryj" -#: kotti/templates/edit/upload.pt:88 -msgid "Select file(s) to upload..." -msgstr "Wybierz plik(i) do przesłania..." +#: kotti/views/edit/actions.py:507 +msgid "You have to select items to perform an action." +msgstr "Musisz zaznaczyć elementy, aby wykonać akcję." -#: kotti/templates/edit/upload.pt:96 -msgid "Upload ${number} files." -msgstr "Prześlij ${number} plików." +#: kotti/views/edit/actions.py:464 +msgid "Change State" +msgstr "Zmień stan" -#: kotti/templates/edit/upload.pt:101 -msgid "Dismiss all errors" -msgstr "Zignoruj wszystkie błędy" +#: kotti/views/edit/content.py:38 +msgid "Description" +msgstr "Opis" -#: 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/views/edit/content.py:44 +msgid "Tags" +msgstr "Tagi" -#: kotti/templates/site-setup/user.pt:12 -msgid "Back to User Management" -msgstr "Powrót do zarządzania użytkownikami" +#: kotti/views/edit/content.py:53 +msgid "Body" +msgstr "Treść" -#: kotti/templates/site-setup/user.pt:21 kotti/views/form.py:156 -#, python-format -msgid "Edit ${title}" -msgstr "Edycja ${title}" +#: kotti/views/edit/default_views.py:100 +msgid "Default view has been reset to default." +msgstr "Zresetowano widok na domyślny." -#: kotti/templates/site-setup/users.pt:12 -msgid "Search user(s) / group(s)" -msgstr "Szukaj użytkownika(ów) / grupę(y)" +#: kotti/views/edit/default_views.py:79 +msgid "Default view" +msgstr "Domyślny widok" -#: kotti/templates/site-setup/users.pt:17 -msgid "Add user" -msgstr "Dodaj użytkownika" +#: kotti/views/edit/default_views.py:107 +msgid "Default view has been set." +msgstr "Ustawiono domyślny widok." -#: kotti/templates/site-setup/users.pt:22 -msgid "Add group" -msgstr "Dodaj grupę" +#: kotti/views/edit/default_views.py:112 +msgid "Default view could not be set." +msgstr "Domyślny widok nie mógł zostać ustawiony." -#: kotti/templates/site-setup/users.pt:34 -msgid "Find users or groups" -msgstr "Znajdź użytkowników lub grupy" +#: kotti/templates/forbidden.pt:10 +msgid "Forbidden" +msgstr "Brak dostępu" -#: kotti/templates/site-setup/users.pt:39 -msgid "User- / groupname" -msgstr "Użytkownik- / nazwa grupy" +#: kotti/templates/email-reset-password.pt:2 +msgid "Reset your password for ${site_title}." +msgstr "Zresetuj swoje hasło do ${site_title}." -#: kotti/templates/site-setup/users.pt:49 -msgid "Blank search text finds all." -msgstr "Zostaw puste, aby znaleźć wszystko." +#: kotti/templates/email-reset-password.pt:4 +#: kotti/templates/email-set-password.pt:4 +msgid "Hello, ${user_title}!" +msgstr "Witaj, ${user_title}!" -#: kotti/templates/site-setup/users.pt:63 -msgid "Assign global roles" -msgstr "Przypisz globalne role" +#: 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/site-setup/users.pt:115 -msgid "Add new user" -msgstr "Dodaj nowego użytkownika" +#: kotti/templates/add-dropdown.pt:3 +msgid "Add" +msgstr "Dodaj" -#: kotti/templates/site-setup/users.pt:121 -msgid "Add new group" -msgstr "Dodaj nową grupę" +#: kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" +msgstr "Dołącz treści" -#: kotti/templates/view/search-results.pt:9 -msgid "Search Results" -msgstr "Wyniki wyszukania" +#: kotti/templates/email-set-password.pt:2 +msgid "Your registration for ${site_title}" +msgstr "Rejestracja na ${site_title}" -#: kotti/templates/view/search.pt:12 -msgid "Search..." -msgstr "Szukaj..." +#: kotti/templates/email-set-password.pt:5 +msgid "You are joining ${site_title}." +msgstr "Rejestrujesz się na ${site_title}." -#: kotti/templates/view/tags.pt:7 -msgid "Tagged with:" -msgstr "Otagowane z:" +#: 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/tests/testing_view.pt:11 -msgid "Welcome, ${user}! You are logged in." -msgstr "Witaj, ${user}! Jesteś zalogowany." +#: kotti/templates/editor-bar.pt:21 +msgid "View" +msgstr "Widok" -#: kotti/tests/testing_view.pt:15 -msgid "Welcome, you are not logged in." -msgstr "Witaj, nie jesteś zalogowany." +#: kotti/templates/editor-bar.pt:44 +msgid "Navigate" +msgstr "Nawigacja" -#: kotti/views/form.py:79 kotti/views/users.py:69 -#: kotti/views/edit/actions.py:374 kotti/views/edit/actions.py:418 -msgid "Your changes have been saved." -msgstr "Twoje zmiany zostały zapisane." +#: kotti/templates/editor-bar.pt:55 +msgid "Preferences" +msgstr "Ustawienia" -#: kotti/views/form.py:174 -msgid "Item was added." -msgstr "Element został dodany." +#: kotti/templates/editor-bar.pt:60 +msgid "Site Setup" +msgstr "Ustawienia strony" -#: kotti/views/form.py:264 -#, python-format -msgid "Maximum file size: ${size}MB" -msgstr "Maksymalny rozmiar pliku: ${size}MB" +#: kotti/templates/editor-bar.pt:71 +msgid "Logout" +msgstr "Wyloguj się" -#: kotti/views/form.py:77 kotti/views/users.py:441 -msgid "Save" -msgstr "Zapisz" +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "Zrób ${state}" -#: kotti/views/form.py:198 -#, python-format -msgid "Add ${type} to ${title}." -msgstr "Dodaj ${type} do ${title}." +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "Ustaw domyślny widok" -#: kotti/views/form.py:202 -#, python-format -msgid "Add ${type}." -msgstr "Dodaj ${type}." +#: kotti/templates/login.pt:15 +msgid "Login" +msgstr "Logowanie" -#: kotti/views/login.py:237 -msgid "You have reset your password." -msgstr "Zresetowałeś swoje hasło." +#: kotti/templates/login.pt:20 kotti/templates/login.pt:57 +msgid "Username or email" +msgstr "Nazwa użytkownika lub email" -#: kotti/views/login.py:199 -msgid "You have been logged out." -msgstr "Zostałeś wylogowany." +#: kotti/templates/login.pt:33 +msgid "Log in" +msgstr "Zaloguj się" -#: kotti/views/login.py:65 kotti/views/users.py:270 -msgid "Full name" -msgstr "Imię i nazwisko" +#: kotti/templates/login.pt:43 +msgid "Forgot your password?" +msgstr "Zapomniałeś hasła?" -#: kotti/views/login.py:68 -msgid "Username" -msgstr "Nazwa użytkownika" +#. 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:50 kotti/templates/login.pt:65 +msgid "Reset password" +msgstr "Reset hasła" -#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 -msgid "Email" -msgstr "" +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:48 +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/views/login.py:108 -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/templates/login.pt:76 +msgid "Not registered yet?" +msgstr "Nie jesteś jeszcze zarejestrowany?" -#: kotti/views/login.py:123 -#, python-format -msgid "Register - ${title}" -msgstr "Zarejestruj - ${title}" +#: kotti/templates/login.pt:82 +msgid "Register for an account on this site." +msgstr "Zarejestruj się na stronie." -#: kotti/views/login.py:163 -msgid "Login failed." -msgstr "Nie udało się zalogować." +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "Otagowane z:" -#: kotti/views/login.py:285 -#, python-format -msgid "Reset your password - ${title}." -msgstr "Resetowanie hasła - ${title}." +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "Szukaj..." -#: kotti/views/login.py:159 -#, python-format -msgid "Welcome, ${user}!" -msgstr "Witaj, ${user}!" +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:67 +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:112 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:40 +#: kotti/templates/edit/share.pt:47 kotti/templates/edit/change-state.pt:19 +msgid "Type" +msgstr "Typ" -#: kotti/views/login.py:172 -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/templates/view/folder.pt:22 kotti/templates/edit/delete-nodes.pt:25 +#: kotti/templates/edit/contents.pt:43 kotti/templates/edit/change-state.pt:21 +msgid "Creation Date" +msgstr "Data utworzenia" -#: kotti/views/login.py:177 -msgid "That username or email is not known by this system." -msgstr "Nieznana nazwa użytkownika lub email." +#: kotti/templates/view/folder.pt:23 kotti/templates/edit/delete-nodes.pt:26 +#: kotti/templates/edit/contents.pt:44 kotti/templates/edit/change-state.pt:22 +msgid "Modification Date" +msgstr "Data modyfikacji" -#: kotti/views/login.py:82 -msgid "Register" -msgstr "Zarejestruj" +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "Wyniki wyszukania" -#: kotti/views/login.py:89 kotti/views/login.py:256 -msgid "There was an error." -msgstr "Wystąpił błąd." +#: kotti/templates/http-errors/notfound.pt:6 +msgid "Not Found" +msgstr "" -#: kotti/views/login.py:249 -msgid "Submit" -msgstr "Zatwierdź" +#: kotti/templates/http-errors/notfound.pt:7 +msgid "The resource could not be found" +msgstr "" -#: kotti/views/login.py:278 -msgid "Your password reset token may have expired." -msgstr "Twój token do resetu hasła mógł stracić ważność." +#: kotti/templates/site-setup/delete-user.pt:9 +#: kotti/templates/edit/delete.pt:9 +msgid "Delete ${title}" +msgstr "Usuń ${title}" -#: kotti/views/site_setup.py:6 kotti/views/users.py:378 -msgid "User Management" -msgstr "Zarządzanie użytkownikami" +#: 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/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 "" -"Zostaw to puste i zaznacz 'Wyślij link do rejestracji' poniżej, aby " -"użytkownik sam ustawił sobie hasło." +#: kotti/templates/site-setup/user.pt:12 +msgid "Back to User Management" +msgstr "Powrót do zarządzania użytkownikami" -#: kotti/views/users.py:587 -#, python-format -msgid "My preferences - ${title}" -msgstr "Moje ustawienia - ${title}" +#: kotti/templates/site-setup/users.pt:12 +msgid "Search user(s) / group(s)" +msgstr "Szukaj użytkownika(ów) / grupę(y)" -#: kotti/views/users.py:145 -msgid "Invalid value" -msgstr "Niepoprawna wartość" +#: 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/views/users.py:151 -msgid "A user with that name already exists." -msgstr "Użytkownik z takim imieniem już istnieje." +#: kotti/templates/site-setup/users.pt:34 +msgid "Find users or groups" +msgstr "Znajdź użytkowników lub grupy" -#: kotti/views/users.py:158 -msgid "A user with that email already exists." -msgstr "Użytkownik z takim adresem email już istnieje." +#: kotti/templates/site-setup/users.pt:39 +msgid "User- / groupname" +msgstr "Użytkownik- / nazwa grupy" -#: kotti/views/users.py:181 -#, python-format -msgid "No such group: ${group}" -msgstr "Nie ma takiej grupy: ${group}" +#: kotti/templates/site-setup/users.pt:47 kotti/templates/edit/share.pt:14 +#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:26 +msgid "Search users and groups" +msgstr "Szukaj użytkowników i grup" -#: kotti/views/users.py:219 -msgid "Active" -msgstr "Aktywne" +#: kotti/templates/site-setup/users.pt:49 +msgid "Blank search text finds all." +msgstr "Zostaw puste, aby znaleźć wszystko." -#: kotti/views/users.py:220 -msgid "Untick this to deactivate the account." -msgstr "Odznacz, aby dezaktywować konto." +#: kotti/templates/site-setup/users.pt:55 kotti/templates/edit/share.pt:33 +msgid "Search" +msgstr "Szukaj" -#: kotti/views/users.py:226 -msgid "Global roles" -msgstr "Globalne role" +#: kotti/templates/site-setup/users.pt:63 +msgid "Assign global roles" +msgstr "Przypisz globalne role" -#: kotti/views/users.py:230 -msgid "Groups" -msgstr "Grupy" +#: kotti/templates/site-setup/users.pt:83 kotti/templates/edit/share.pt:63 +msgid "Gravatar" +msgstr "" -#: kotti/views/users.py:315 -msgid "Add User" -msgstr "Dodaj użytkownika" +#: kotti/templates/site-setup/users.pt:105 kotti/templates/edit/share.pt:83 +#: kotti/templates/edit/change-state.pt:64 +msgid "Apply changes" +msgstr "Zapisz zmiany" -#: kotti/views/users.py:339 -#, python-format -msgid "${title} was added." -msgstr "${title} został dodany." +#: kotti/templates/site-setup/users.pt:115 +msgid "Add new user" +msgstr "Dodaj nowego użytkownika" -#: kotti/views/users.py:349 -msgid "Add Group" -msgstr "Dodaj grupę" +#: kotti/templates/site-setup/users.pt:121 +msgid "Add new group" +msgstr "Dodaj nową grupę" -#: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:312 kotti/views/edit/actions.py:378 -#: kotti/views/edit/actions.py:424 kotti/views/edit/actions.py:420 -msgid "No changes were made." -msgstr "Nie wykonano żadnych zmian." +#: kotti/templates/edit/nav-tree-view.pt:8 +msgid "Navigate Site" +msgstr "Nawigacja strony" -#: kotti/views/users.py:558 -msgid "No name was given." -msgstr "Nie przekazano nazwy." +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" +msgstr "Załącz zawartość z lokalnego pliku(ów)" -#: kotti/views/users.py:98 -msgid "No users or groups were found." -msgstr "Nie znaleziono użytkowników ani grup." +#: kotti/templates/edit/upload.pt:18 kotti/templates/edit/upload.pt:111 +msgid "Filename" +msgstr "Nazwa pliku" -#: kotti/views/users.py:505 -#, python-format -msgid "Edit ${principal_type} ${title}" -msgstr "Edycja ${principal_type} ${title}" +#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:113 +msgid "Size" +msgstr "Rozmiar" -#: kotti/views/users.py:533 -msgid "User was not found." -msgstr "Użytkownik nie został odnaleziony." +#: kotti/templates/edit/upload.pt:21 +msgid "Status" +msgstr "Status" -#: kotti/views/users.py:324 -msgid "Send password registration link." -msgstr "Wyślij link do rejestracji na email." +#: kotti/templates/edit/upload.pt:22 +msgid "Progress" +msgstr "Postęp" -#: kotti/views/users.py:543 -#, python-format -msgid "${principal_type} ${title} was deleted." -msgstr "${principal_type} ${title} został usunięty." +#: kotti/templates/edit/upload.pt:88 +msgid "Select file(s) to upload..." +msgstr "Wybierz plik(i) do przesłania..." -#: kotti/views/users.py:551 -#, python-format -msgid "Delete ${principal_type} ${title}" -msgstr "Usuń ${principal_type} ${title}" +#: kotti/templates/edit/upload.pt:96 +msgid "Upload ${number} files." +msgstr "Prześlij ${number} plików." -#: kotti/views/edit/actions.py:106 -#, python-format -msgid "${title} was copied." -msgstr "${title} został skopiowany." +#: kotti/templates/edit/upload.pt:101 +msgid "Dismiss all errors" +msgstr "Zignoruj wszystkie błędy" -#: kotti/views/edit/actions.py:124 -#, python-format -msgid "${title} was cut." -msgstr "${title} został wycięty." +#: kotti/templates/edit/delete-nodes.pt:14 +msgid "Are you sure you want to delete the following items?" +msgstr "Czy jesteś pewien, że chcesz usunąć wybrane elementy?" -#: kotti/views/edit/actions.py:183 -#, python-format -msgid "${title} was moved." -msgstr "${title} został przeniesiony." +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:41 +#: kotti/templates/edit/change-state.pt:20 +msgid "State" +msgstr "Stan" -#: kotti/views/edit/actions.py:159 -#, python-format -msgid "${title} was pasted." -msgstr "${title} został wklejony." +#: kotti/templates/edit/breadcrumbs.pt:8 +msgid "You are here:" +msgstr "Jesteś tutaj:" -#: kotti/views/edit/actions.py:162 -msgid "Could not paste node. It no longer exists." -msgstr "Nie można wkleić. Element już nie istnieje." +#: kotti/templates/edit/contents.pt:30 +msgid "No content items are contained here." +msgstr "Brak elementów potomnych." -#: kotti/views/edit/actions.py:225 -#, python-format -msgid "${title} is now visible in the navigation." -msgstr "${title} jest teraz widoczny w nawigacji." +#: kotti/templates/edit/contents.pt:42 +msgid "Visibility" +msgstr "Widoczność" -#: kotti/views/edit/actions.py:228 -#, python-format -msgid "${title} is no longer visible in the navigation." -msgstr "${title} nie jest już widoczny w nawigacji." +#: kotti/templates/edit/contents.pt:75 +msgid "Visible" +msgstr "Widoczny" -#: kotti/views/edit/actions.py:277 kotti/views/edit/actions.py:306 -#, python-format -msgid "${title} was deleted." -msgstr "${title} został usunięty." +#: kotti/templates/edit/contents.pt:79 +msgid "Hidden" +msgstr "Ukryty" -#: kotti/views/edit/actions.py:303 -msgid "Nothing was deleted." -msgstr "Nic nie zostało usunięte" +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "Zmień nazwę ${title}" -#: kotti/views/edit/actions.py:339 kotti/views/edit/actions.py:366 -msgid "Name and title are required." -msgstr "Nazwa i tytuł są obowiązkowe." +#: 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/views/edit/actions.py:343 -msgid "Item was renamed." -msgstr "Zmieniono nazwę elementu." +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +msgid "New name" +msgstr "Nowa nazwa" -#: kotti/views/edit/actions.py:465 -msgid "Move up" -msgstr "W górę" +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:33 +msgid "New title" +msgstr "Nowy tytuł" -#: kotti/views/edit/actions.py:466 -msgid "Move down" -msgstr "W dół" +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" +msgstr "Czy jesteś pewien, że chcesz usunąć ${title}?" -#: kotti/views/edit/actions.py:467 -msgid "Show" -msgstr "Pokaż" +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" +msgstr "Udostępnij ${title}" -#: kotti/views/edit/actions.py:468 -msgid "Hide" -msgstr "Ukryj" +#: kotti/templates/edit/share.pt:42 +msgid "Assign local roles" +msgstr "Przypisz lokalne role" -#: kotti/views/edit/actions.py:507 -msgid "You have to select items to perform an action." -msgstr "Musisz zaznaczyć elementy, aby wykonać akcję." +#: kotti/templates/edit/change-state.pt:8 +msgid "Change workflow state" +msgstr "Zmień stan publikacji" -#: kotti/views/edit/actions.py:464 -msgid "Change State" -msgstr "Zmień stan" +#: 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/views/edit/content.py:38 -msgid "Description" -msgstr "Opis" +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:45 +msgid "Include children" +msgstr "Załącz potomka" -#: kotti/views/edit/content.py:44 -msgid "Tags" -msgstr "Tagi" +#: 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/views/edit/content.py:53 -msgid "Body" -msgstr "Treść" +#: kotti/templates/edit/change-state.pt:50 +msgid "Change state to" +msgstr "Zmień stan na" -#: kotti/views/edit/default_views.py:100 -msgid "Default view has been reset to default." -msgstr "Zresetowano widok na domyślny." +#: kotti/templates/edit/change-state.pt:51 +msgid "Select the new state where all chosen items should be set." +msgstr "Zaznacz nowy stan, gdzie wszystkie wybrane elementy zostaną zmienione." -#: kotti/views/edit/default_views.py:79 -msgid "Default view" -msgstr "Domyślny widok" +#: kotti/templates/edit/change-state.pt:55 +msgid "No change" +msgstr "Bez zmian" -#: kotti/views/edit/default_views.py:107 -msgid "Default view has been set." -msgstr "Ustawiono domyślny widok." +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "Witaj, ${user}! Jesteś zalogowany." -#: kotti/views/edit/default_views.py:112 -msgid "Default view could not be set." -msgstr "Domyślny widok nie mógł zostać ustawiony." +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "Witaj, nie jesteś zalogowany." diff --git a/kotti/locale/pt/LC_MESSAGES/Kotti.po b/kotti/locale/pt/LC_MESSAGES/Kotti.po index 3ccc8e218..e0d5f100f 100644 --- a/kotti/locale/pt/LC_MESSAGES/Kotti.po +++ b/kotti/locale/pt/LC_MESSAGES/Kotti.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Kotti 0.8\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2015-02-22 11:33+0100\n" +"POT-Creation-Date: 2015-09-29 14:24+0100\n" "PO-Revision-Date: 2012-01-16 12:02+0000\n" "Last-Translator: Nuno Teixeira \n" "Language-Team: pt \n" @@ -18,6 +18,81 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "Generated-By: Babel 0.9.6\n" +#: kotti/security.py:190 +msgid "Viewer" +msgstr "Leitor" + +#: kotti/security.py:191 +msgid "Editor" +msgstr "Editor" + +#: kotti/security.py:192 +msgid "Owner" +msgstr "Dono" + +#: kotti/security.py:193 +msgid "Admin" +msgstr "Admin" + +#: kotti/resources.py:508 kotti/views/edit/actions.py:456 +msgid "Copy" +msgstr "Copiar" + +#: kotti/resources.py:509 kotti/views/edit/actions.py:457 +msgid "Cut" +msgstr "Cortar" + +#: kotti/resources.py:510 kotti/views/edit/actions.py:453 +msgid "Paste" +msgstr "Colar" + +#: kotti/resources.py:511 kotti/views/edit/actions.py:458 +#: kotti/templates/edit/rename.pt:31 kotti/templates/edit/rename-nodes.pt:8 +#: kotti/templates/edit/rename-nodes.pt:40 +msgid "Rename" +msgstr "Renomear" + +#: kotti/resources.py:512 kotti/views/users.py:443 +#: kotti/views/edit/actions.py:460 +#: kotti/templates/site-setup/delete-user.pt:24 +#: kotti/templates/edit/delete-nodes.pt:9 +#: kotti/templates/edit/delete-nodes.pt:50 kotti/templates/edit/delete.pt:24 +msgid "Delete" +msgstr "Apagar" + +#: kotti/resources.py:637 kotti/views/edit/content.py:85 +msgid "Document" +msgstr "Documento" + +#: kotti/resources.py:726 kotti/views/edit/content.py:113 +#: kotti/views/edit/content.py:66 +msgid "File" +msgstr "Ficheiro" + +#: kotti/resources.py:772 kotti/views/edit/content.py:141 +msgid "Image" +msgstr "Imagem" + +#: kotti/resources.py:529 +msgid "Folder view" +msgstr "Vista de pasta" + +#: kotti/resources.py:523 kotti/templates/view/folder.pt:15 +msgid "Contents" +msgstr "Conteúdo" + +#: kotti/resources.py:524 kotti/templates/edit/nav-tree.pt:10 +msgid "Edit" +msgstr "Editar" + +#: kotti/resources.py:525 +msgid "Share" +msgstr "Partilha" + +#: kotti/resources.py:526 kotti/templates/actions-dropdown.pt:3 +msgid "Actions" +msgstr "Ações" + #: kotti/populate.py:64 msgid "Welcome to Kotti" msgstr "" @@ -32,29 +107,25 @@ 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=\"http://kotti.readthedocs.org/en/latest/developing/basic/configuration.html\">\n" " Configuration manual\n" " \n" "

    \n" @@ -62,13 +133,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,9 +146,9 @@ 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" msgstr "" -#: kotti/populate.py:121 +#: kotti/populate.py:123 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:124 +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:125 msgid "" "\n" "

    \n" " \"five\n" "

    \n" "\n" @@ -123,812 +190,716 @@ 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:627 kotti/views/edit/content.py:82 -msgid "Document" -msgstr "Documento" +#: kotti/views/form.py:79 kotti/views/users.py:69 +#: kotti/views/edit/actions.py:374 kotti/views/edit/actions.py:418 +msgid "Your changes have been saved." +msgstr "As suas alterações foram guardadas." -#: kotti/resources.py:666 kotti/views/edit/content.py:110 -#: kotti/views/edit/content.py:63 -msgid "File" -msgstr "Ficheiro" +#: kotti/views/form.py:174 +#, fuzzy +msgid "Item was added." +msgstr "${title} adicionado." -#: kotti/resources.py:753 kotti/views/edit/content.py:138 -msgid "Image" -msgstr "Imagem" +#: kotti/views/form.py:156 kotti/templates/site-setup/user.pt:21 +#, python-format +msgid "Edit ${title}" +msgstr "Editar ${title}" -#: kotti/resources.py:517 -msgid "Folder view" -msgstr "Vista de pasta" +#: kotti/views/form.py:264 +#, python-format +msgid "Maximum file size: ${size}MB" +msgstr "Tamanho máximo do ficheiro: ${size}MB" -#: kotti/resources.py:504 kotti/templates/view/folder.pt:17 -msgid "Contents" -msgstr "Conteúdo" +#: kotti/views/form.py:77 kotti/views/users.py:441 +msgid "Save" +msgstr "Guardar" -#: kotti/resources.py:505 kotti/templates/edit/nav-tree.pt:10 -msgid "Edit" -msgstr "Editar" +#: kotti/views/form.py:78 kotti/views/users.py:316 kotti/views/users.py:350 +#: kotti/views/users.py:442 kotti/templates/edit/delete-nodes.pt:45 +#: kotti/templates/edit/delete.pt:19 kotti/templates/edit/change-state.pt:66 +#: kotti/templates/edit/rename-nodes.pt:42 +msgid "Cancel" +msgstr "Cancelar" -#: kotti/resources.py:506 -msgid "Share" -msgstr "Partilha" +#: kotti/views/form.py:198 +#, fuzzy, python-format +msgid "Add ${type} to ${title}." +msgstr "Adicionar ${type} em ${title}" -#: kotti/resources.py:507 kotti/templates/actions-dropdown.pt:5 -msgid "Actions" -msgstr "Ações" +#: kotti/views/form.py:202 +#, fuzzy, python-format +msgid "Add ${type}." +msgstr "Adicionar ${type}" -#: kotti/resources.py:508 kotti/views/edit/actions.py:446 -msgid "Copy" -msgstr "Copiar" +#: kotti/views/site_setup.py:6 kotti/views/users.py:378 +msgid "User Management" +msgstr "Gestão de Utilizadores" -#: kotti/resources.py:509 kotti/views/edit/actions.py:447 -msgid "Cut" -msgstr "Cortar" +#: kotti/views/login.py:237 +#, fuzzy +msgid "You have reset your password." +msgstr "Você reinicializou a palavra passe com sucesso" -#: kotti/resources.py:510 kotti/views/edit/actions.py:443 -msgid "Paste" -msgstr "Colar" +#: kotti/views/login.py:199 +msgid "You have been logged out." +msgstr "Você desligou a sessão" -#: kotti/resources.py:511 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:448 -msgid "Rename" -msgstr "Renomear" +#: kotti/views/login.py:65 kotti/views/users.py:270 +#, fuzzy +msgid "Full name" +msgstr "Nome completo" -#: kotti/resources.py:512 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:450 -msgid "Delete" -msgstr "Apagar" +#: kotti/views/login.py:68 +msgid "Username" +msgstr "Nome utilizador" -#: kotti/security.py:189 -msgid "Viewer" -msgstr "Leitor" +#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 +msgid "Email" +msgstr "E-mail" -#: kotti/security.py:190 -msgid "Editor" -msgstr "Editor" +#: kotti/views/login.py:108 +#, 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/security.py:191 -msgid "Owner" -msgstr "Dono" +#: kotti/views/login.py:123 +#, fuzzy, python-format +msgid "Register - ${title}" +msgstr "Registar ${title}" -#: kotti/security.py:192 -msgid "Admin" -msgstr "Admin" +#: kotti/views/login.py:163 +msgid "Login failed." +msgstr "Erro de autenticação" -#: kotti/templates/add-dropdown.pt:5 -msgid "Add" -msgstr "Adicionar" +#: kotti/views/login.py:212 kotti/views/users.py:212 +#: kotti/templates/login.pt:25 +msgid "Password" +msgstr "Palavra passe" -#: kotti/templates/add-dropdown.pt:27 -msgid "Upload Content" -msgstr "Colocar Conteúdo" +#: kotti/views/login.py:285 +#, fuzzy, python-format +msgid "Reset your password - ${title}." +msgstr "Reiniciar a sua palavra passe - ${title}" -#: kotti/templates/default-view-selector.pt:5 -msgid "Set default view" -msgstr "Definir vista por defeito" +#: kotti/views/login.py:159 +#, python-format +msgid "Welcome, ${user}!" +msgstr "Bem-vindo, ${user}!" -#: kotti/templates/editor-bar.pt:35 -msgid "View" -msgstr "Ver" +#: kotti/views/login.py:172 +#, 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/templates/editor-bar.pt:76 -msgid "Navigate" -msgstr "Navegar" +#: kotti/views/login.py:177 +#, fuzzy +msgid "That username or email is not known by this system." +msgstr "Este utilizador ou e-mail não são reconhecidos." -#: kotti/templates/editor-bar.pt:98 -msgid "Preferences" -msgstr "Preferências" +#: kotti/views/login.py:82 +msgid "Register" +msgstr "Registar-se" -#: kotti/templates/editor-bar.pt:109 -msgid "Site Setup" -msgstr "Configurar Sítio" +#: kotti/views/login.py:89 kotti/views/login.py:256 +msgid "There was an error." +msgstr "Ocorreu um erro." -#: kotti/templates/editor-bar.pt:131 -msgid "Logout" -msgstr "Sair" +#: kotti/views/login.py:249 +msgid "Submit" +msgstr "Enviar" -#: kotti/templates/email-reset-password.pt:3 -#, fuzzy -msgid "Reset your password for ${site_title}." -msgstr "Reiniciar a sua palavra passe em ${site_title}" +#: kotti/views/login.py:278 +msgid "Your password reset token may have expired." +msgstr "A seu pedido para alterar a palavra passe expirou." -#: kotti/templates/email-reset-password.pt:7 -#: kotti/templates/email-set-password.pt:6 -msgid "Hello, ${user_title}!" -msgstr "Bem-vindo, ${user_title}!" +#: kotti/views/users.py:313 kotti/views/users.py:501 kotti/views/users.py:536 +#: kotti/templates/site-setup/users.pt:79 kotti/templates/edit/share.pt:59 +msgid "User" +msgstr "Utilizador" -#: kotti/templates/email-reset-password.pt:10 -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}" +#: kotti/views/users.py:347 kotti/views/users.py:188 kotti/views/users.py:501 +#: kotti/views/users.py:536 kotti/templates/site-setup/users.pt:80 +#: kotti/templates/edit/share.pt:60 +msgid "Group" +msgstr "Grupo" -#: kotti/templates/email-set-password.pt:3 -msgid "Your registration for ${site_title}" -msgstr "O seu registo em ${site_title}" +#: 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/templates/email-set-password.pt:9 -#, fuzzy -msgid "You are joining ${site_title}." -msgstr "O seu registo em ${site_title}" +#: kotti/views/users.py:587 +#, python-format +msgid "My preferences - ${title}" +msgstr "Minhas preferências - ${title}" -#: kotti/templates/email-set-password.pt:12 -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/views/users.py:145 +msgid "Invalid value" +msgstr "Valor inválido" -#: kotti/templates/forbidden.pt:7 -#, fuzzy -msgid "Forbidden" -msgstr "Escondido" +#: kotti/views/users.py:151 +msgid "A user with that name already exists." +msgstr "Esse utilizador com esse nome já existe." -#: kotti/templates/login.pt:16 -msgid "Login" -msgstr "Entrar" +#: kotti/views/users.py:158 +msgid "A user with that email already exists." +msgstr "Esse utilizador com esse e-mail já existe." -#: kotti/templates/login.pt:25 kotti/templates/login.pt:87 -msgid "Username or email" -msgstr "Utilizador ou e-mail" +#: kotti/views/users.py:181 +#, python-format +msgid "No such group: ${group}" +msgstr "Grupo não encontrado: ${group}" -#: kotti/templates/login.pt:34 kotti/views/login.py:212 -#: kotti/views/users.py:212 -msgid "Password" -msgstr "Palavra passe" +#: kotti/views/users.py:196 kotti/views/edit/content.py:33 +#: kotti/templates/view/folder.pt:20 kotti/templates/edit/delete-nodes.pt:22 +#: kotti/templates/edit/contents.pt:39 kotti/templates/edit/change-state.pt:18 +msgid "Title" +msgstr "Título" -#: kotti/templates/login.pt:47 -msgid "Log in" -msgstr "Entrar" +#: kotti/views/users.py:207 kotti/templates/site-setup/users.pt:68 +#: kotti/templates/edit/share.pt:48 +msgid "Name" +msgstr "Nome" -#: kotti/templates/login.pt:65 -msgid "Forgot your password?" -msgstr "Esqueceu-se da palavra passe?" +#: kotti/views/users.py:219 +msgid "Active" +msgstr "Activo" -#. 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" - -#. 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/login.pt:118 -msgid "Not registered yet?" -msgstr "" - -#: kotti/templates/login.pt:127 -#, fuzzy -msgid "Register for an account on this site." -msgstr "Não se encontra registado? ${register} para criar uma conta." - -#: kotti/templates/workflow-dropdown.pt:19 -msgid "Make ${state}" -msgstr "Tornar ${state}" - -#: kotti/templates/edit/breadcrumbs.pt:5 -msgid "You are here:" -msgstr "Você está aqui:" - -#: kotti/templates/edit/change-state.pt:7 -msgid "Change workflow state" -msgstr "Modificar estado de workflow" - -#: kotti/templates/edit/change-state.pt:14 -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." - -#: 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:32 -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 -msgid "State" -msgstr "Estado" +#: kotti/views/users.py:220 +msgid "Untick this to deactivate the account." +msgstr "Retirar para desactivar a conta." -#: 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 -msgid "Creation Date" -msgstr "Data de Criação" +#: kotti/views/users.py:226 +msgid "Global roles" +msgstr "Papéis globais" -#: 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 -msgid "Modification Date" -msgstr "Data de Modificação" +#: kotti/views/users.py:230 +msgid "Groups" +msgstr "Grupos" -#: kotti/templates/edit/change-state.pt:79 -#: kotti/templates/edit/change-state.pt:90 -msgid "Include children" -msgstr "Incluir nós interiores" +#: kotti/views/users.py:315 +msgid "Add User" +msgstr "Adicionar Utilizador" -#: 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/views/users.py:339 +#, fuzzy, python-format +msgid "${title} was added." +msgstr "${title} adicionado." -#: kotti/templates/edit/change-state.pt:100 -msgid "Change state to" -msgstr "Mudar estado para" +#: kotti/views/users.py:349 +msgid "Add Group" +msgstr "Adicionar Grupo" -#: kotti/templates/edit/change-state.pt:103 +#: kotti/views/users.py:465 kotti/views/users.py:71 +#: kotti/views/edit/actions.py:312 kotti/views/edit/actions.py:378 +#: kotti/views/edit/actions.py:424 kotti/views/edit/actions.py:420 #, 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 -msgid "No change" +msgid "No changes were made." 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 +#: kotti/views/users.py:558 #, fuzzy -msgid "Hidden" -msgstr "Escondido" - -#: kotti/templates/edit/delete-nodes.pt:17 -msgid "Are you sure you want to delete the following items?" -msgstr "Deseja realmente eliminar os items seguintes?" +msgid "No name was given." +msgstr "Nenhum nome foi indicado." -#: kotti/templates/edit/delete.pt:8 -#: kotti/templates/site-setup/delete-user.pt:8 -msgid "Delete ${title}" -msgstr "Apagar ${title}" +#: kotti/views/users.py:98 +#, fuzzy +msgid "No users or groups were found." +msgstr "Utilizadores e grupos não encontrados." -#: kotti/templates/edit/delete.pt:20 -msgid "Are you sure you want to delete ${title}?" -msgstr "Deseja realmente eliminar ${title}?" +#: kotti/views/users.py:505 +#, fuzzy, python-format +msgid "Edit ${principal_type} ${title}" +msgstr "Editar ${principal_type} - ${title}" -#: kotti/templates/edit/nav-tree-view.pt:7 +#: kotti/views/users.py:533 #, fuzzy -msgid "Navigate Site" -msgstr "Navegar" +msgid "User was not found." +msgstr "Utilizador não encontrado." -#: kotti/templates/edit/rename-nodes.pt:16 kotti/templates/edit/rename.pt:14 +#: kotti/views/users.py:324 #, 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." - -#: kotti/templates/edit/rename-nodes.pt:35 kotti/templates/edit/rename.pt:26 -msgid "New name" -msgstr "Novo nome" - -#: kotti/templates/edit/rename-nodes.pt:45 kotti/templates/edit/rename.pt:35 -msgid "New title" -msgstr "Novo título" +msgid "Send password registration link." +msgstr "Enviar ligação para registo de palavra passe" -#: kotti/templates/edit/rename.pt:7 -msgid "Rename ${title}" -msgstr "Renomear ${title}" +#: kotti/views/users.py:543 +#, fuzzy, python-format +msgid "${principal_type} ${title} was deleted." +msgstr "${principal_type} ${title} apagado." -#: kotti/templates/edit/share.pt:7 -msgid "Share ${title}" -msgstr "Partilhar ${title}" +#: kotti/views/users.py:551 +#, fuzzy, python-format +msgid "Delete ${principal_type} ${title}" +msgstr "Apagar ${principal_type} - ${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/views/edit/actions.py:106 +#, fuzzy, python-format +msgid "${title} was copied." +msgstr "${title} copiado." -#: kotti/templates/edit/share.pt:43 kotti/templates/site-setup/users.pt:81 -msgid "Search" -msgstr "Procurar" +#: kotti/views/edit/actions.py:124 +#, fuzzy, python-format +msgid "${title} was cut." +msgstr "${title} cortado." -#: kotti/templates/edit/share.pt:60 -msgid "Assign local roles" -msgstr "Atribuir papéis locais" +#: kotti/views/edit/actions.py:183 +#, fuzzy, python-format +msgid "${title} was moved." +msgstr "${title} movido." -#: kotti/templates/edit/share.pt:73 kotti/templates/site-setup/users.pt:108 -#: kotti/views/users.py:207 -msgid "Name" -msgstr "Nome" +#: kotti/views/edit/actions.py:159 +#, fuzzy, python-format +msgid "${title} was pasted." +msgstr "${title} colado." -#: 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/views/edit/actions.py:162 +#, fuzzy +msgid "Could not paste node. It no longer exists." +msgstr "Nãp é possivel copiar. A cópia já não está disponivel." -#: 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/views/edit/actions.py:225 +#, python-format +msgid "${title} is now visible in the navigation." +msgstr "${title} encontra-se visivel na navegação." -#: kotti/templates/edit/share.pt:101 kotti/templates/site-setup/users.pt:136 -msgid "Gravatar" -msgstr "Gravatar" +#: kotti/views/edit/actions.py:228 +#, python-format +msgid "${title} is no longer visible in the navigation." +msgstr "${title} já não está visivel na navegação." -#: kotti/templates/edit/upload.pt:9 -msgid "Upload content from local file(s)" -msgstr "" +#: kotti/views/edit/actions.py:277 kotti/views/edit/actions.py:306 +#, fuzzy, python-format +msgid "${title} was deleted." +msgstr "${title} apagado." -#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:164 +#: kotti/views/edit/actions.py:303 #, fuzzy -msgid "Filename" -msgstr "Renomear" - -#: kotti/templates/edit/upload.pt:26 kotti/templates/edit/upload.pt:170 -msgid "Size" -msgstr "" +msgid "Nothing was deleted." +msgstr "Nada foi apagado." -#: kotti/templates/edit/upload.pt:29 -msgid "Status" -msgstr "" +#: kotti/views/edit/actions.py:339 kotti/views/edit/actions.py:366 +msgid "Name and title are required." +msgstr "Nome e título são obrigatórios." -#: kotti/templates/edit/upload.pt:32 -msgid "Progress" -msgstr "" +#: kotti/views/edit/actions.py:343 +#, fuzzy +msgid "Item was renamed." +msgstr "Item renomeado" -#: kotti/templates/edit/upload.pt:124 -msgid "Select file(s) to upload..." -msgstr "" +#: kotti/views/edit/actions.py:465 +msgid "Move up" +msgstr "Mover para cima" -#: kotti/templates/edit/upload.pt:134 -msgid "Upload ${number} files." -msgstr "" +#: kotti/views/edit/actions.py:466 +msgid "Move down" +msgstr "Mover para baixo" -#: kotti/templates/edit/upload.pt:144 -msgid "Dismiss all errors" -msgstr "" +#: kotti/views/edit/actions.py:467 +msgid "Show" +msgstr "Mostrar" -#: kotti/templates/site-setup/delete-user.pt:20 -msgid "Are you sure you want to delete ${type} ${title}?" -msgstr "Deseja realmente eliminar ${type} ${title}?" +#: kotti/views/edit/actions.py:468 +msgid "Hide" +msgstr "Esconder" -#: kotti/templates/site-setup/user.pt:13 -msgid "Back to User Management" -msgstr "Voltar à gestão de utilizadores" +#: kotti/views/edit/actions.py:507 +#, fuzzy +msgid "You have to select items to perform an action." +msgstr "Terá de escolher items para executar a ação." -#: kotti/templates/site-setup/user.pt:25 kotti/views/form.py:156 -#, python-format -msgid "Edit ${title}" -msgstr "Editar ${title}" +#: kotti/views/edit/actions.py:464 +msgid "Change State" +msgstr "Alterar Estado" -#: kotti/templates/site-setup/users.pt:15 -#, fuzzy -msgid "Search user(s) / group(s)" -msgstr "Procurar utilizadores e grupos" +#: kotti/views/edit/content.py:38 +msgid "Description" +msgstr "Descrição" -#: kotti/templates/site-setup/users.pt:24 -msgid "Add user" -msgstr "Adicionar utilizador" +#: kotti/views/edit/content.py:44 +msgid "Tags" +msgstr "Etiquetas" -#: kotti/templates/site-setup/users.pt:33 -msgid "Add group" -msgstr "Adicionar grupo" +#: kotti/views/edit/content.py:53 +msgid "Body" +msgstr "Corpo" -#: kotti/templates/site-setup/users.pt:52 -#, fuzzy -msgid "Find users or groups" -msgstr "Procurar utilizadores e grupos" +#: kotti/views/edit/default_views.py:100 +msgid "Default view has been reset to default." +msgstr "A vista original foi indicada como a vista por defeito." -#: kotti/templates/site-setup/users.pt:63 -msgid "User- / groupname" -msgstr "" +#: kotti/views/edit/default_views.py:79 +msgid "Default view" +msgstr "Vista por defeito" -#: kotti/templates/site-setup/users.pt:70 -#, fuzzy -msgid "Blank search text finds all." -msgstr "" -"Encontre e modifique utilizadores (Uma pesquisa em branco retorna\"\n" -"\" todos os utilizadores)" +#: kotti/views/edit/default_views.py:107 +msgid "Default view has been set." +msgstr "Vista por defeito foi alterada." -#: kotti/templates/site-setup/users.pt:96 -#, fuzzy -msgid "Assign global roles" -msgstr "Atribuir papéis locais" +#: kotti/views/edit/default_views.py:112 +msgid "Default view could not be set." +msgstr "Vista por defeito não pode ser alterada." -#: kotti/templates/site-setup/users.pt:180 +#: kotti/templates/forbidden.pt:10 #, fuzzy -msgid "Add new user" -msgstr "Adicionar utilizador" +msgid "Forbidden" +msgstr "Escondido" -#: kotti/templates/site-setup/users.pt:190 +#: kotti/templates/email-reset-password.pt:2 #, fuzzy -msgid "Add new group" -msgstr "Adicionar grupo" - -#: kotti/templates/view/search-results.pt:8 -msgid "Search Results" -msgstr "Resultados da pesquisa" +msgid "Reset your password for ${site_title}." +msgstr "Reiniciar a sua palavra passe em ${site_title}" -#: kotti/templates/view/search.pt:5 -msgid "Search..." -msgstr "Procurar..." +#: 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/view/tags.pt:5 -msgid "Tagged with:" -msgstr "Marcado como:" +#: 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}" -#: kotti/tests/testing_view.pt:7 -msgid "Welcome, ${user}! You are logged in." -msgstr "Bem-vindo, ${user}! Você está autenticado." +#: kotti/templates/add-dropdown.pt:3 +msgid "Add" +msgstr "Adicionar" -#: kotti/tests/testing_view.pt:14 -msgid "Welcome, you are not logged in." -msgstr "Bem-vindo, você não está autenticado." +#: kotti/templates/add-dropdown.pt:13 +msgid "Upload Content" +msgstr "Colocar Conteúdo" -#: kotti/views/form.py:79 kotti/views/users.py:69 -#: kotti/views/edit/actions.py:364 kotti/views/edit/actions.py:408 -msgid "Your changes have been saved." -msgstr "As suas alterações foram guardadas." +#: kotti/templates/email-set-password.pt:2 +msgid "Your registration for ${site_title}" +msgstr "O seu registo em ${site_title}" -#: kotti/views/form.py:174 +#: kotti/templates/email-set-password.pt:5 #, 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" +msgid "You are joining ${site_title}." +msgstr "O seu registo em ${site_title}" -#: kotti/views/form.py:77 kotti/views/users.py:441 -msgid "Save" -msgstr "Guardar" +#: 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/views/form.py:198 -#, fuzzy, python-format -msgid "Add ${type} to ${title}." -msgstr "Adicionar ${type} em ${title}" +#: kotti/templates/editor-bar.pt:21 +msgid "View" +msgstr "Ver" -#: kotti/views/form.py:202 -#, fuzzy, python-format -msgid "Add ${type}." -msgstr "Adicionar ${type}" +#: kotti/templates/editor-bar.pt:44 +msgid "Navigate" +msgstr "Navegar" -#: kotti/views/login.py:237 -#, fuzzy -msgid "You have reset your password." -msgstr "Você reinicializou a palavra passe com sucesso" +#: kotti/templates/editor-bar.pt:55 +msgid "Preferences" +msgstr "Preferências" -#: kotti/views/login.py:199 -msgid "You have been logged out." -msgstr "Você desligou a sessão" +#: kotti/templates/editor-bar.pt:60 +msgid "Site Setup" +msgstr "Configurar Sítio" -#: kotti/views/login.py:65 kotti/views/users.py:270 -#, fuzzy -msgid "Full name" -msgstr "Nome completo" +#: kotti/templates/editor-bar.pt:71 +msgid "Logout" +msgstr "Sair" -#: kotti/views/login.py:68 -msgid "Username" -msgstr "Nome utilizador" +#: kotti/templates/workflow-dropdown.pt:14 +msgid "Make ${state}" +msgstr "Tornar ${state}" -#: kotti/views/login.py:73 kotti/views/login.py:224 kotti/views/users.py:199 -msgid "Email" -msgstr "E-mail" +#: kotti/templates/default-view-selector.pt:3 +msgid "Set default view" +msgstr "Definir vista por defeito" -#: kotti/views/login.py:108 -#, 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/templates/login.pt:15 +msgid "Login" +msgstr "Entrar" -#: kotti/views/login.py:123 -#, fuzzy, python-format -msgid "Register - ${title}" -msgstr "Registar ${title}" +#: kotti/templates/login.pt:20 kotti/templates/login.pt:57 +msgid "Username or email" +msgstr "Utilizador ou e-mail" -#: kotti/views/login.py:163 -msgid "Login failed." -msgstr "Erro de autenticação" +#: kotti/templates/login.pt:33 +msgid "Log in" +msgstr "Entrar" -#: kotti/views/login.py:285 -#, fuzzy, python-format -msgid "Reset your password - ${title}." -msgstr "Reiniciar a sua palavra passe - ${title}" +#: kotti/templates/login.pt:43 +msgid "Forgot your password?" +msgstr "Esqueceu-se da palavra passe?" -#: kotti/views/login.py:159 -#, python-format -msgid "Welcome, ${user}!" -msgstr "Bem-vindo, ${user}!" +#. 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:50 kotti/templates/login.pt:65 +msgid "Reset password" +msgstr "Repor a palavra passe" -#: kotti/views/login.py:172 +#. Canonical text for ${reset_password} is: "Reset password" +#: kotti/templates/login.pt:48 #, fuzzy -msgid "" -"You should be receiving an email with a link to reset your password. Doing " -"so will activate your account." +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/login.pt:76 +msgid "Not registered yet?" msgstr "" -"Você deverá receber um e-mail com uma ligação temporária para reiniciar a " -"sua palavra passe." -#: kotti/views/login.py:177 +#: kotti/templates/login.pt:82 #, fuzzy -msgid "That username or email is not known by this system." -msgstr "Este utilizador ou e-mail não são reconhecidos." +msgid "Register for an account on this site." +msgstr "Não se encontra registado? ${register} para criar uma conta." -#: kotti/views/login.py:82 -msgid "Register" -msgstr "Registar-se" +#: kotti/templates/view/tags.pt:7 +msgid "Tagged with:" +msgstr "Marcado como:" + +#: kotti/templates/view/search.pt:12 +msgid "Search..." +msgstr "Procurar..." + +#: kotti/templates/view/folder.pt:21 kotti/templates/site-setup/users.pt:67 +#: kotti/templates/edit/upload.pt:19 kotti/templates/edit/upload.pt:112 +#: kotti/templates/edit/delete-nodes.pt:23 kotti/templates/edit/contents.pt:40 +#: kotti/templates/edit/share.pt:47 kotti/templates/edit/change-state.pt:19 +msgid "Type" +msgstr "Tipo" -#: kotti/views/login.py:89 kotti/views/login.py:256 -msgid "There was an error." -msgstr "Ocorreu um erro." +#: kotti/templates/view/folder.pt:22 kotti/templates/edit/delete-nodes.pt:25 +#: kotti/templates/edit/contents.pt:43 kotti/templates/edit/change-state.pt:21 +msgid "Creation Date" +msgstr "Data de Criação" -#: kotti/views/login.py:249 -msgid "Submit" -msgstr "Enviar" +#: kotti/templates/view/folder.pt:23 kotti/templates/edit/delete-nodes.pt:26 +#: kotti/templates/edit/contents.pt:44 kotti/templates/edit/change-state.pt:22 +msgid "Modification Date" +msgstr "Data de Modificação" -#: kotti/views/login.py:278 -msgid "Your password reset token may have expired." -msgstr "A seu pedido para alterar a palavra passe expirou." +#: kotti/templates/view/search-results.pt:9 +msgid "Search Results" +msgstr "Resultados da pesquisa" -#: kotti/views/site_setup.py:6 kotti/views/users.py:378 -msgid "User Management" -msgstr "Gestão de Utilizadores" +#: kotti/templates/http-errors/notfound.pt:6 +msgid "Not Found" +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/templates/http-errors/notfound.pt:7 +msgid "The resource could not be found" 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}" +#: kotti/templates/site-setup/delete-user.pt:9 +#: kotti/templates/edit/delete.pt:9 +msgid "Delete ${title}" +msgstr "Apagar ${title}" -#: kotti/views/users.py:145 -msgid "Invalid value" -msgstr "Valor inválido" +#: 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/views/users.py:151 -msgid "A user with that name already exists." -msgstr "Esse utilizador com esse nome já existe." +#: kotti/templates/site-setup/user.pt:12 +msgid "Back to User Management" +msgstr "Voltar à gestão de utilizadores" -#: kotti/views/users.py:158 -msgid "A user with that email already exists." -msgstr "Esse utilizador com esse e-mail já existe." +#: kotti/templates/site-setup/users.pt:12 +#, fuzzy +msgid "Search user(s) / group(s)" +msgstr "Procurar utilizadores e grupos" -#: kotti/views/users.py:181 -#, python-format -msgid "No such group: ${group}" -msgstr "Grupo não encontrado: ${group}" +#: kotti/templates/site-setup/users.pt:17 +msgid "Add user" +msgstr "Adicionar utilizador" -#: kotti/views/users.py:219 -msgid "Active" -msgstr "Activo" +#: kotti/templates/site-setup/users.pt:22 +msgid "Add group" +msgstr "Adicionar grupo" -#: kotti/views/users.py:220 -msgid "Untick this to deactivate the account." -msgstr "Retirar para desactivar a conta." +#: kotti/templates/site-setup/users.pt:34 +#, fuzzy +msgid "Find users or groups" +msgstr "Procurar utilizadores e grupos" -#: kotti/views/users.py:226 -msgid "Global roles" -msgstr "Papéis globais" +#: kotti/templates/site-setup/users.pt:39 +msgid "User- / groupname" +msgstr "" -#: kotti/views/users.py:230 -msgid "Groups" -msgstr "Grupos" +#: kotti/templates/site-setup/users.pt:47 kotti/templates/edit/share.pt:14 +#: kotti/templates/edit/share.pt:19 kotti/templates/edit/share.pt:26 +msgid "Search users and groups" +msgstr "Procurar utilizadores e grupos" -#: kotti/views/users.py:315 -msgid "Add User" -msgstr "Adicionar Utilizador" +#: kotti/templates/site-setup/users.pt:49 +#, fuzzy +msgid "Blank search text finds all." +msgstr "" +"Encontre e modifique utilizadores (Uma pesquisa em branco retorna\"\n" +"\" todos os utilizadores)" -#: kotti/views/users.py:339 -#, fuzzy, python-format -msgid "${title} was added." -msgstr "${title} adicionado." +#: kotti/templates/site-setup/users.pt:55 kotti/templates/edit/share.pt:33 +msgid "Search" +msgstr "Procurar" -#: kotti/views/users.py:349 -msgid "Add Group" -msgstr "Adicionar Grupo" +#: kotti/templates/site-setup/users.pt:63 +#, fuzzy +msgid "Assign global roles" +msgstr "Atribuir papéis locais" -#: kotti/views/users.py:465 kotti/views/users.py:71 -#: kotti/views/edit/actions.py:302 kotti/views/edit/actions.py:368 -#: kotti/views/edit/actions.py:414 kotti/views/edit/actions.py:410 +#: kotti/templates/site-setup/users.pt:83 kotti/templates/edit/share.pt:63 +msgid "Gravatar" +msgstr "Gravatar" + +#: kotti/templates/site-setup/users.pt:105 kotti/templates/edit/share.pt:83 +#: kotti/templates/edit/change-state.pt:64 +msgid "Apply changes" +msgstr "Aplicar alterações" + +#: kotti/templates/site-setup/users.pt:115 #, fuzzy -msgid "No changes were made." -msgstr "Não foram efectuadas nenhumas alterações" +msgid "Add new user" +msgstr "Adicionar utilizador" -#: kotti/views/users.py:558 +#: kotti/templates/site-setup/users.pt:121 #, fuzzy -msgid "No name was given." -msgstr "Nenhum nome foi indicado." +msgid "Add new group" +msgstr "Adicionar grupo" -#: kotti/views/users.py:98 +#: kotti/templates/edit/nav-tree-view.pt:8 #, fuzzy -msgid "No users or groups were found." -msgstr "Utilizadores e grupos não encontrados." +msgid "Navigate Site" +msgstr "Navegar" -#: kotti/views/users.py:505 -#, fuzzy, python-format -msgid "Edit ${principal_type} ${title}" -msgstr "Editar ${principal_type} - ${title}" +#: kotti/templates/edit/upload.pt:10 +msgid "Upload content from local file(s)" +msgstr "" -#: kotti/views/users.py:533 +#: kotti/templates/edit/upload.pt:18 kotti/templates/edit/upload.pt:111 #, fuzzy -msgid "User was not found." -msgstr "Utilizador não encontrado." +msgid "Filename" +msgstr "Renomear" -#: kotti/views/users.py:324 -#, fuzzy -msgid "Send password registration link." -msgstr "Enviar ligação para registo de palavra passe" +#: kotti/templates/edit/upload.pt:20 kotti/templates/edit/upload.pt:113 +msgid "Size" +msgstr "" -#: kotti/views/users.py:543 -#, fuzzy, python-format -msgid "${principal_type} ${title} was deleted." -msgstr "${principal_type} ${title} apagado." +#: kotti/templates/edit/upload.pt:21 +msgid "Status" +msgstr "" -#: kotti/views/users.py:551 -#, fuzzy, python-format -msgid "Delete ${principal_type} ${title}" -msgstr "Apagar ${principal_type} - ${title}" +#: kotti/templates/edit/upload.pt:22 +msgid "Progress" +msgstr "" -#: kotti/views/edit/actions.py:106 -#, fuzzy, python-format -msgid "${title} was copied." -msgstr "${title} copiado." +#: kotti/templates/edit/upload.pt:88 +msgid "Select file(s) to upload..." +msgstr "" -#: kotti/views/edit/actions.py:124 -#, fuzzy, python-format -msgid "${title} was cut." -msgstr "${title} cortado." +#: kotti/templates/edit/upload.pt:96 +msgid "Upload ${number} files." +msgstr "" -#: kotti/views/edit/actions.py:183 -#, fuzzy, python-format -msgid "${title} was moved." -msgstr "${title} movido." +#: kotti/templates/edit/upload.pt:101 +msgid "Dismiss all errors" +msgstr "" -#: kotti/views/edit/actions.py:271 kotti/views/edit/actions.py:296 -#, fuzzy, python-format -msgid "${title} was deleted." -msgstr "${title} apagado." +#: kotti/templates/edit/delete-nodes.pt:14 +msgid "Are you sure you want to delete the following items?" +msgstr "Deseja realmente eliminar os items seguintes?" -#: kotti/views/edit/actions.py:159 -#, fuzzy, python-format -msgid "${title} was pasted." -msgstr "${title} colado." +#: kotti/templates/edit/delete-nodes.pt:24 kotti/templates/edit/contents.pt:41 +#: kotti/templates/edit/change-state.pt:20 +msgid "State" +msgstr "Estado" -#: kotti/views/edit/actions.py:162 -#, fuzzy -msgid "Could not paste node. It no longer exists." -msgstr "Nãp é possivel copiar. A cópia já não está disponivel." +#: kotti/templates/edit/breadcrumbs.pt:8 +msgid "You are here:" +msgstr "Você está aqui:" -#: kotti/views/edit/actions.py:225 -#, python-format -msgid "${title} is now visible in the navigation." -msgstr "${title} encontra-se visivel na navegação." +#: kotti/templates/edit/contents.pt:30 +msgid "No content items are contained here." +msgstr "Não existem items nesta pasta." -#: kotti/views/edit/actions.py:228 -#, python-format -msgid "${title} is no longer visible in the navigation." -msgstr "${title} já não está visivel na navegação." +#: kotti/templates/edit/contents.pt:42 +msgid "Visibility" +msgstr "Visibilidade" + +#: kotti/templates/edit/contents.pt:75 +msgid "Visible" +msgstr "Visível" -#: kotti/views/edit/actions.py:293 +#: kotti/templates/edit/contents.pt:79 #, fuzzy -msgid "Nothing was deleted." -msgstr "Nada foi apagado." +msgid "Hidden" +msgstr "Escondido" -#: kotti/views/edit/actions.py:329 kotti/views/edit/actions.py:356 -msgid "Name and title are required." -msgstr "Nome e título são obrigatórios." +#: kotti/templates/edit/rename.pt:8 +msgid "Rename ${title}" +msgstr "Renomear ${title}" -#: kotti/views/edit/actions.py:333 +#: kotti/templates/edit/rename.pt:11 kotti/templates/edit/rename-nodes.pt:13 #, fuzzy -msgid "Item was renamed." -msgstr "Item renomeado" +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/views/edit/actions.py:455 -msgid "Move up" -msgstr "Mover para cima" +#: kotti/templates/edit/rename.pt:19 kotti/templates/edit/rename-nodes.pt:26 +msgid "New name" +msgstr "Novo nome" -#: kotti/views/edit/actions.py:456 -msgid "Move down" -msgstr "Mover para baixo" +#: kotti/templates/edit/rename.pt:25 kotti/templates/edit/rename-nodes.pt:33 +msgid "New title" +msgstr "Novo título" -#: kotti/views/edit/actions.py:457 -msgid "Show" -msgstr "Mostrar" +#: kotti/templates/edit/delete.pt:13 +msgid "Are you sure you want to delete ${title}?" +msgstr "Deseja realmente eliminar ${title}?" -#: kotti/views/edit/actions.py:458 -msgid "Hide" -msgstr "Esconder" +#: kotti/templates/edit/share.pt:8 +msgid "Share ${title}" +msgstr "Partilhar ${title}" -#: kotti/views/edit/actions.py:497 -#, fuzzy -msgid "You have to select items to perform an action." -msgstr "Terá de escolher items para executar a ação." +#: kotti/templates/edit/share.pt:42 +msgid "Assign local roles" +msgstr "Atribuir papéis locais" -#: kotti/views/edit/actions.py:454 -msgid "Change State" -msgstr "Alterar Estado" +#: kotti/templates/edit/change-state.pt:8 +msgid "Change workflow state" +msgstr "Modificar estado de workflow" -#: kotti/views/edit/content.py:36 -msgid "Description" -msgstr "Descrição" +#: 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." -#: kotti/views/edit/content.py:42 -msgid "Tags" -msgstr "Etiquetas" +#: kotti/templates/edit/change-state.pt:40 +#: kotti/templates/edit/change-state.pt:45 +msgid "Include children" +msgstr "Incluir nós interiores" -#: kotti/views/edit/content.py:51 -msgid "Body" -msgstr "Corpo" +#: 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/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/templates/edit/change-state.pt:50 +msgid "Change state to" +msgstr "Mudar estado para" -#: kotti/views/edit/default_views.py:78 -msgid "Default view" -msgstr "Vista por defeito" +#: kotti/templates/edit/change-state.pt:51 +#, fuzzy +msgid "Select the new state where all chosen items should be set." +msgstr "Selecione o novo estado para os items selecionados." -#: kotti/views/edit/default_views.py:106 -msgid "Default view has been set." -msgstr "Vista por defeito foi alterada." +#: kotti/templates/edit/change-state.pt:55 +msgid "No change" +msgstr "Não foram efectuadas nenhumas alterações" -#: kotti/views/edit/default_views.py:111 -msgid "Default view could not be set." -msgstr "Vista por defeito não pode ser alterada." +#: kotti/tests/testing_view.pt:11 +msgid "Welcome, ${user}! You are logged in." +msgstr "Bem-vindo, ${user}! Você está autenticado." + +#: kotti/tests/testing_view.pt:15 +msgid "Welcome, you are not logged in." +msgstr "Bem-vindo, você não está autenticado." #~ msgid "Private" #~ msgstr "Privado" From 969173f3a33f212b186e46bebd1e4ee22799a8a9 Mon Sep 17 00:00:00 2001 From: Matt Russell Date: Wed, 30 Sep 2015 01:36:09 +0100 Subject: [PATCH 338/600] Fix malformed namespace prefixes --- kotti/templates/view/search.pt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kotti/templates/view/search.pt b/kotti/templates/view/search.pt index df41a5390..14d75e23c 100644 --- a/kotti/templates/view/search.pt +++ b/kotti/templates/view/search.pt @@ -1,4 +1,5 @@ -