def _principal_by_name(cls, name: str) -> Principal: query = bakery( lambda session: session.query(cls.factory).filter( cls.factory.name == bindparam("name") ) ) return query(DBSession()).params(name=name).one()
def initialize_sql(engine, drop_all=False): DBSession.registry.clear() DBSession.configure(bind=engine) metadata.bind = engine if drop_all or os.environ.get('KOTTI_TEST_DB_STRING'): metadata.reflect() metadata.drop_all(engine) # Allow users of Kotti to cherry pick the tables that they want to use: settings = get_current_registry().settings tables = settings['kotti.use_tables'].strip() or None if tables: tables = [metadata.tables[name] for name in tables.split()] if engine.dialect.name == 'mysql': # pragma: no cover from sqlalchemy.dialects.mysql.base import LONGBLOB File.__table__.c.data.type = LONGBLOB() metadata.create_all(engine, tables=tables) for populate in get_settings()['kotti.populators']: populate() commit() return DBSession()
def content(connection, settings): """sets up some default content using Kotti's testing populator.""" import transaction from kotti import DBSession from kotti import metadata from kotti.resources import get_root if connection.in_transaction(): transaction.abort() DBSession().close() metadata.drop_all(connection.engine) transaction.begin() metadata.create_all(connection.engine) # to create the default content with the correct workflow state # the workflow must be initialized first; please note that these # settings won't persist, though; use the `workflow` fixture if needed from zope.configuration import xmlconfig import kotti xmlconfig.file("workflow.zcml", kotti, execute=True) for populate in settings["kotti.populators"]: populate() # We set the path here since it's required for some integration # tests, and because the 'content' fixture does not depend on # 'event' and therefore the event handlers aren't fired for root # otherwise: get_root().path = "/" transaction.commit()
def traverse(root, vpath_tuple): """ :param root: The node where traversal should start :type root: :class:`kotti.resources.Node` :param vpath_tuple: Tuple of path segments to be traversed :type vpath_tuple: tuple :return: List of nodes, from root (excluded) to context (included). Each node has its parent set already, so that no subsequent queries will be be performed, e.g. when calling ``lineage(context)`` :rtype: list of :class:`kotti.resources.Node` """ conditions = [ (Node.path == root.path + '/'.join(vpath_tuple[:idx + 1]) + '/') for idx, item in enumerate(vpath_tuple) ] nodes = DBSession().query(Node)\ .order_by(Node.path)\ .filter(or_(*conditions))\ .all() for i, node in enumerate(nodes): if i == 0: setattr(node, 'parent', root) else: setattr(node, 'parent', nodes[i - 1]) return nodes
def run_migrations_online(): if DBSession.bind is None: raise ValueError( "\nYou must run Kotti's migration using the 'kotti-migrate' script" "\nand not through 'alembic' directly.") transaction.begin() connection = DBSession.connection() context.configure( connection=connection, target_metadata=metadata, ) try: context.run_migrations() mark_changed(DBSession()) except: traceback.print_exc() transaction.abort() else: transaction.commit() finally: #connection.close() pass
def tnc_db_session(tnc_config, tnc_connection, request): from transaction import abort trans = tnc_connection.begin() # begin a non-orm transaction request.addfinalizer(trans.rollback) request.addfinalizer(abort) from kotti import DBSession return DBSession()
def paste_node(context, request): session = DBSession() id, action = request.session['kotti.paste'] item = session.query(Node).get(id) if item is not None: if action == 'cut': if not has_permission('edit', item, request): raise Forbidden() item.__parent__.children.remove(item) context.children.append(item) del request.session['kotti.paste'] elif action == 'copy': copy = item.copy() name = copy.name if not name: # for root name = copy.title name = title_to_name(name, blacklist=context.keys()) copy.name = name context.children.append(copy) request.session.flash(_(u'${title} pasted.', mapping=dict(title=item.title)), 'success') else: request.session.flash( _(u'Could not paste node. It does not exist anymore.'), 'error') if not request.is_xhr: location = resource_url(context, request) return HTTPFound(location=location)
def view(self): session = DBSession() query = session.query(SoftwareProject).filter( SoftwareProject.parent_id == self.context.id) items = query.all() # [TODO] Are these calls too expensive? [item.refresh_pypi() for item in items] [item.refresh_github() for item in items] [item.refresh_bitbucket() for item in items] if self.context.sort_order_is_ascending: items = sorted(items, key=lambda x: x.date) else: items = sorted(items, key=lambda x: x.date, reverse=True) page = self.request.params.get('page', 1) settings = collection_settings() if settings['use_batching']: items = Batch.fromPagenumber(items, pagesize=settings['pagesize'], pagenumber=int(page)) return { 'api': template_api(self.context, self.request), 'macros': get_renderer('templates/macros.pt').implementation(), 'items': items, 'settings': settings, }
def __getitem__(self, name): name = unicode(name) session = DBSession() try: return session.query( self.factory).filter(self.factory.name == name).one() except NoResultFound: raise KeyError(name)
def __delitem__(self, name): name = unicode(name) session = DBSession() try: principal = session.query( self.factory).filter(self.factory.name == name).one() session.delete(principal) except NoResultFound: raise KeyError(name)
def do_stamp(rev, context, revision=revision): current = context._current_rev() if revision is None: revision = context.script.get_current_head() elif revision == 'None': revision = None context._update_current_rev(current, revision) mark_changed(DBSession()) return []
def db_session(config, content, connection, request): """ returns a db session object and sets up a db transaction savepoint, which will be rolled back after the test. """ from transaction import abort trans = connection.begin() # begin a non-orm transaction request.addfinalizer(trans.rollback) request.addfinalizer(abort) from kotti import DBSession return DBSession()
def test_view_permitted_yes(self): from kotti import DBSession from kotti.resources import Node from kotti.resources import Document self.config.testing_securitypolicy(permissive=True) self.config.include('kotti.views.edit') root = DBSession().query(Node).get(1) request = DummyRequest() self.assertEquals(Document.type_info.addable(root, request), True)
def test_get_non_existing_paste_item(self): from kotti import DBSession from kotti.resources import Node from kotti.views.edit import get_paste_item root = DBSession().query(Node).get(1) request = DummyRequest() request.session['kotti.paste'] = (1701, 'copy') item = get_paste_item(root, request) self.assertEqual(item, None)
def root_id(self): """ Query for the one node without a parent and return its id. :result: The root node's id. :rtype: int """ query = bakery(lambda session: session.query(Node).with_polymorphic( Node).add_columns(Node.id).enable_eagerloads(False).filter( Node.parent_id == None)) return query(DBSession()).one().id
def frontpage_view(context, request): session = DBSession() query = session.query(BlogEntry).order_by(BlogEntry.date.desc()) items = query.all()[:3] items = [ item for item in items if has_permission('view', item, request) ] return { 'api': template_api(context, request), 'items': items, }
def test_unique_constraint(self): from kotti import DBSession from kotti.resources import get_root from kotti.resources import Node # Try to add two children with the same name to the root node: session = DBSession() root = get_root() session.add(Node(name=u'child1', parent=root)) session.add(Node(name=u'child1', parent=root)) self.assertRaises(IntegrityError, session.flush)
def do_stamp(rev, context, revision=revision): if revision is None: revision = context.script.get_current_head() elif revision == "None": revision = None context.stamp(env.script_dir, revision) mark_changed(DBSession()) return []
def make(self, context=None, request=None, id=1, **kwargs): from kotti import DBSession from kotti.resources import Node from kotti.views.util import TemplateAPI if context is None: session = DBSession() context = session.query(Node).get(id) if request is None: request = DummyRequest() return TemplateAPI(context, request, **kwargs)
def db_session(config, content, connection): """ returns a db session object and sets up a db transaction savepoint, which will be rolled back after the test. """ import transaction trans = connection.begin() # begin a non-orm transaction from kotti import DBSession yield DBSession() trans.rollback() transaction.abort()
def get_paste_item(context, request): info = request.session.get('kotti.paste') if info: id, action = info item = DBSession().query(Node).get(id) if not item.type_info.addable(context, request): return if action == 'cut' and inside(context, item): return if context == item: return return item
def test_persistent_settings(self): from kotti import get_settings from kotti import get_version from kotti import DBSession from kotti.resources import Settings session = DBSession() [settings] = session.query(Settings).all() self.assertEqual(settings.data, {'kotti.db_version': get_version()}) self.assertEqual(get_settings()['kotti.db_version'], get_version()) settings.data['foo.bar'] = u'baz' self.assertEqual(get_settings()['foo.bar'], u'baz')
def test_container_methods(self): from kotti import DBSession from kotti.resources import get_root from kotti.resources import Node session = DBSession() # Test some of Node's container methods: root = get_root() self.assertEquals(root.keys(), []) child1 = Node(name=u'child1', parent=root) session.add(child1) self.assertEquals(root.keys(), [u'child1']) self.assertEquals(root[u'child1'], child1) del root[u'child1'] self.assertEquals(root.keys(), []) # When we delete a parent node, all its child nodes will be # released as well: root[u'child2'] = Node() root[u'child2'][u'subchild'] = Node() self.assertEquals( session.query(Node).filter(Node.name == u'subchild').count(), 1) del root[u'child2'] self.assertEquals( session.query(Node).filter(Node.name == u'subchild').count(), 0) # We can pass a tuple as the key to more efficiently reach # down to child objects: root[u'child3'] = Node() subchild33 = Node(name=u'subchild33', parent=root[u'child3']) session.add(subchild33) del root.__dict__['_children'] # force a different code path self.assertTrue( root[u'child3', u'subchild33'] is root[u'child3'][u'subchild33']) self.assertTrue(root[(u'child3', u'subchild33')] is subchild33) self.assertTrue(root[(u'child3', u'subchild33')] is subchild33) self.assertRaises(KeyError, root.__getitem__, (u'child3', u'bad-name')) root.children # force a different code path self.assertRaises(KeyError, root.__getitem__, (u'child3', u'bad-name')) del root[u'child3'] # Overwriting an existing Node is an error; first delete manually! child4 = Node(name=u'child4', parent=root) session.add(child4) self.assertEquals(root.keys(), [u'child4']) child44 = Node(name=u'child4') session.add(child44) root[u'child4'] = child44 self.assertRaises(SQLAlchemyError, session.flush)
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 session = DBSession() session.query(LocalGroup).filter(LocalGroup.node_id == context.id).filter( LocalGroup.principal_name == name).delete() for group_name in groups_to_set: session.add(LocalGroup(context, name, unicode(group_name)))
def test_paste_non_existing_node(self): from kotti import DBSession from kotti.resources import Node from kotti.views.edit import paste_node root = DBSession().query(Node).get(1) request = DummyRequest() for index, action in enumerate(['copy', 'cut']): request.session['kotti.paste'] = (1701, 'copy') response = paste_node(root, request) self.assertEqual(response.status, '302 Found') self.assertEqual(len(request.session['_f_error']), index + 1)
def view_calendar(context, request): kotti_calendar_resources.need() locale_name = get_locale_name(request) if locale_name in fullcalendar_locales: fullcalendar_locales[locale_name].need() else: # pragma: no cover (safety belt only, should never happen) fullcalendar_locales["en"].need() session = DBSession() now = datetime.datetime.now() query = session.query(Event).filter(Event.parent_id == context.id) future = or_(Event.start > now, Event.end > now) upcoming = query.filter(future).order_by(Event.start).all() past = query.filter(Event.start < now).order_by(desc(Event.start)).all() upcoming = [event for event in upcoming if has_permission('view', event, request)] past = [event for event in past if has_permission('view', event, request)] fmt = '%Y-%m-%d %H:%M:%S' fullcalendar_events = [] for event in (upcoming + past): json_event = { 'title': event.title, 'url': resource_url(event, request), 'start': event.start.strftime(fmt), 'allDay': event.all_day, } if event.end: json_event['end'] = event.end.strftime(fmt) fullcalendar_events.append(json_event) fullcalendar_options = { 'header': { 'left': 'prev,next today', 'center': 'title', 'right': 'month,agendaWeek,agendaDay' }, 'eventSources': context.feeds, 'weekends': context.weekends, 'events': fullcalendar_events, } return { 'api': template_api(context, request), 'upcoming_events': upcoming, 'past_events': past, 'fullcalendar_options': json.dumps(fullcalendar_options), }
def test_rename_to_empty_name(self): from kotti import DBSession from kotti.resources import Node from kotti.resources import Document from kotti.views.edit import rename_node root = DBSession().query(Node).get(1) child = root['child'] = Document(title=u"Child") request = DummyRequest() request.params['rename'] = u'on' request.params['name'] = u'' request.params['title'] = u'foo' rename_node(child, request) self.assertEqual(request.session.pop_flash('error'), [u'Name and title are required.'])
def __getitem__(self, path: Union[str, Iterable[str]]) -> "Node": db_session = DBSession() db_session._autoflush() # if not hasattr(path, '__iter__'): if isinstance(path, str): path = (path, ) path = [p for p in path] # Optimization: don't query children if self._children already there: if "_children" in self.__dict__: rest = path[1:] try: [child] = filter(lambda ch: ch.name == path[0], self._children) except ValueError: raise KeyError(path) if rest: return child[rest] else: return child baked_query = bakery(lambda session: session.query(Node)) if len(path) == 1: try: baked_query += lambda q: q.filter( Node.name == bindparam("name"), Node.parent_id == bindparam("parent_id"), ) return (baked_query(db_session).params( name=path[0], parent_id=self.id).one()) except NoResultFound: raise KeyError(path) # We have a path with more than one element, so let's be a # little clever about fetching the requested node: nodes = Node.__table__ conditions = [nodes.c.id == self.id] alias = nodes for name in path: alias, old_alias = nodes.alias(), alias conditions.append(alias.c.parent_id == old_alias.c.id) conditions.append(alias.c.name == name) expr = select([alias.c.id], and_(*conditions)) row = db_session.execute(expr).fetchone() if row is None: raise KeyError(path) return baked_query(db_session).get(row.id)
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 session = DBSession() principals = set() items = [context] if inherit: items = lineage(context) for item in items: principals.update( r[0] for r in session.query(LocalGroup.principal_name).filter( LocalGroup.node_id == item.id).group_by( LocalGroup.principal_name).all() if not r[0].startswith('role:')) return list(principals)
def search(self, **kwargs): if not kwargs: return [] filters = [] for key, value in kwargs.items(): col = getattr(self.factory, key) if '*' in value: value = value.replace('*', '%').lower() filters.append(func.lower(col).like(value)) else: filters.append(col == value) session = DBSession() query = session.query(self.factory) query = query.filter(or_(*filters)) return query