Skip to content

Commit

Permalink
Merge ee5b4bb into 9a41ab0
Browse files Browse the repository at this point in the history
  • Loading branch information
disko committed Aug 28, 2015
2 parents 9a41ab0 + ee5b4bb commit b406000
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 23 deletions.
53 changes: 51 additions & 2 deletions kotti/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from depot.fields.sqlalchemy import _SQLAMutationTracker
from depot.fields.sqlalchemy import UploadedFileField
from kotti import _resolve_dotted
from pyramid.decorator import reify
from pyramid.traversal import resource_path
from sqlalchemy import Boolean
from sqlalchemy import Column
Expand Down Expand Up @@ -111,10 +112,27 @@ def __getitem__(self, path):
else:
return child

# Get a single child by its name
if len(path) == 1:
# Add a cache dict if it doesn't exist yet
if '_child_cache' not in self.__dict__:
self._child_cache = {}
if path[0] in self._child_cache:
child = self._child_cache[path[0]]
# Only return an item from the cache if it's still a child
# (i.e. it hasn't been assigned to a different parent in the
# meantime).
if child.parent == self:
return child
else:
# remove item from the cache if the parent has changed.
self._child_cache.pop(path[0])
try:
return DBSession.query(Node).filter_by(
child = DBSession.query(Node).filter_by(
name=path[0], parent=self).one()
# put the item in the cache for subsequent access
self._child_cache[path[0]] = child
return child
except NoResultFound:
raise KeyError(path)

Expand Down Expand Up @@ -256,6 +274,7 @@ class Node(Base, ContainerMixin, PersistentACLMixin):
LocalGroup,
backref=backref('node'),
cascade='all',
lazy='joined',
)

def __init__(self, name=None, parent=None, title=u"", annotations=None,
Expand Down Expand Up @@ -789,6 +808,30 @@ def get_root(request=None):
return get_settings()['kotti.root_factory'][0](request)


class DefaultRootCache(object):
"""docstring for DefaultGetRoot"""

@reify
def root_id(self):
""" Query for the one node without a parent and return its id.
:result: The root node's id.
:rtype: int
"""

return Node.query.filter(Node.parent_id == None).one().id # noqa

def get_root(self):
""" Query for the root node by its id. This enables SQLAlchemy's
session cache (query is executed only once per session).
:result: The root node.
:rtype: :class:`Node`.
"""

return Node.query.get(self.root_id)


def default_get_root(request=None):
"""Default implementation for :func:`~kotti.resources.get_root`.
Expand All @@ -801,7 +844,13 @@ def default_get_root(request=None):
this will be an instance of :class:`~kotti.resources.Document`.
"""

return DBSession.query(Node).filter(Node.parent_id == None).one() # noqa
# Get the root cache, make one if it doesn't exist yet.
try:
drc = get_settings()['kotti.default_root_cache']
except KeyError:
drc = get_settings()['kotti.default_root_cache'] = DefaultRootCache()

return drc.get_root()


def _adjust_for_engine(engine):
Expand Down
47 changes: 28 additions & 19 deletions kotti/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,14 +269,14 @@ def list_groups_raw(name, context):
Only groups defined in context will be considered, therefore no
global or inherited groups are returned.
"""
from kotti.resources import LocalGroup

from kotti.resources import Node

if isinstance(context, Node):
return set(
r[0] for r in DBSession.query(LocalGroup.group_name).filter(
LocalGroup.node_id == context.id).filter(
LocalGroup.principal_name == name).all())
r.group_name for r in context.local_groups
if r.principal_name == name
)
return set()


Expand All @@ -294,7 +294,7 @@ def _cachekey_list_groups_ext(name, context=None, _seen=None, _inherited=None):
raise DontCache
else:
context_id = getattr(context, 'id', id(context))
return (name, context_id)
return (unicode(name), context_id)


@request_cache(_cachekey_list_groups_ext)
Expand Down Expand Up @@ -339,14 +339,19 @@ def set_groups(name, context, groups_to_set=()):
"""Set the list of groups for principal with given ``name`` and in
given ``context``.
"""
name = unicode(name)

from kotti.resources import LocalGroup
DBSession.query(LocalGroup).filter(
LocalGroup.node_id == context.id).filter(
LocalGroup.principal_name == name).delete()

for group_name in groups_to_set:
DBSession.add(LocalGroup(context, name, unicode(group_name)))
name = unicode(name)
context.local_groups = [
# keep groups for "other" principals
lg for lg in context.local_groups
if lg.principal_name != name
] + [
# reset groups for given principal
LocalGroup(context, name, unicode(group_name))
for group_name in groups_to_set
]


def list_groups_callback(name, request):
Expand Down Expand Up @@ -396,19 +401,19 @@ def principals_with_local_roles(context, inherit=True):
"""Return a list of principal names that have local roles in the
context.
"""
from resources import LocalGroup

principals = set()
items = [context]

if inherit:
items = lineage(context)

for item in items:
principals.update(
r[0] for r in
DBSession.query(LocalGroup.principal_name).filter(
LocalGroup.node_id == item.id).group_by(
LocalGroup.principal_name).all()
if not r[0].startswith('role:')
)
r.principal_name for r in item.local_groups
if not r.principal_name.startswith('role:')
)

return list(principals)


Expand Down Expand Up @@ -442,9 +447,13 @@ class Principals(DictMixin):
"""
factory = Principal

@request_cache(lambda self, name: name)
@request_cache(lambda self, name: unicode(name))
def __getitem__(self, name):
name = unicode(name)
# avoid calls to the DB for roles
# (they're not stored in the ``principals`` table)
if name.startswith('role:'):
raise KeyError(name)
try:
return DBSession.query(
self.factory).filter(self.factory.name == name).one()
Expand Down
2 changes: 0 additions & 2 deletions kotti/tests/test_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ def test_upload_anonymous(root, dummy_request, browser):
browser.open(u'{0}/content_types'.format(BASE_URL))
assert browser.url.startswith(u'{0}/@@login'.format(BASE_URL))

# import pdb; pdb.set_trace()


@user('admin')
def test_upload_authenticated_wo_mimetype(root, dummy_request, browser):
Expand Down

0 comments on commit b406000

Please sign in to comment.