<s> """<STR_LIT>""" <EOL> from collections import defaultdict <EOL> from datetime import datetime <EOL> try : <EOL> from collections import OrderedDict <EOL> except ImportError : <EOL> from ordereddict import OrderedDict <EOL> import sqlalchemy . event <EOL> from sqlalchemy . orm import load_only <EOL> import venusian <EOL> from sqlalchemy . orm import mapper <EOL> from sqlalchemy_utils . functions import has_changes <EOL> from pyramid . location import lineage <EOL> from pyramid . threadlocal import get_current_request <EOL> from zope . deprecation . deprecation import deprecated <EOL> from kotti import DBSession <EOL> from kotti import get_settings <EOL> from kotti . resources import Content <EOL> from kotti . resources import LocalGroup <EOL> from kotti . resources import Node <EOL> from kotti . resources import Tag <EOL> from kotti . resources import TagsToContents <EOL> from kotti . security import get_principals <EOL> from kotti . security import list_groups <EOL> from kotti . security import list_groups_raw <EOL> from kotti . security import Principal <EOL> from kotti . security import set_groups <EOL> from kotti . sqla import no_autoflush <EOL> class ObjectEvent ( object ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , obj , request = None ) : <EOL> """<STR_LIT>""" <EOL> self . object = obj <EOL> self . request = request <EOL> class ObjectInsert ( ObjectEvent ) : <EOL> """<STR_LIT>""" <EOL> class ObjectUpdate ( ObjectEvent ) : <EOL> """<STR_LIT>""" <EOL> class ObjectDelete ( ObjectEvent ) : <EOL> """<STR_LIT>""" <EOL> class ObjectAfterDelete ( ObjectEvent ) : <EOL> """<STR_LIT>""" <EOL> deprecated ( '<STR_LIT>' , <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" ) <EOL> class UserDeleted ( ObjectEvent ) : <EOL> """<STR_LIT>""" <EOL> class DispatcherDict ( defaultdict , OrderedDict ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , * args , ** kwargs ) : <EOL> defaultdict . __init__ ( self , list ) <EOL> OrderedDict . __init__ ( self , * args , ** kwargs ) <EOL> class Dispatcher ( DispatcherDict ) : <EOL> """<STR_LIT>""" <EOL> def __call__ ( self , event ) : <EOL> results = [ ] <EOL> for event_type , handlers in self . items ( ) : <EOL> if isinstance ( event , event_type ) : <EOL> for handler in handlers : <EOL> results . append ( handler ( event ) ) <EOL> return results <EOL> class ObjectEventDispatcher ( DispatcherDict ) : <EOL> """<STR_LIT>""" <EOL> def __call__ ( self , event ) : <EOL> results = [ ] <EOL> for ( evtype , objtype ) , handlers in self . items ( ) : <EOL> if ( isinstance ( event , evtype ) and <EOL> ( objtype is None or isinstance ( event . object , objtype ) ) ) : <EOL> for handler in handlers : <EOL> results . append ( handler ( event ) ) <EOL> return results <EOL> def clear ( ) : <EOL> listeners . clear ( ) <EOL> objectevent_listeners . clear ( ) <EOL> listeners [ ObjectEvent ] . append ( objectevent_listeners ) <EOL> listeners = Dispatcher ( ) <EOL> notify = listeners . __call__ <EOL> objectevent_listeners = ObjectEventDispatcher ( ) <EOL> clear ( ) <EOL> def _after_delete ( mapper , connection , target ) : <EOL> """<STR_LIT>""" <EOL> notify ( ObjectAfterDelete ( target , get_current_request ( ) ) ) <EOL> def _before_flush ( session , flush_context , instances ) : <EOL> """<STR_LIT>""" <EOL> req = get_current_request ( ) <EOL> for obj in session . dirty : <EOL> if session . is_modified ( obj , include_collections = False ) : <EOL> notify ( ObjectUpdate ( obj , req ) ) <EOL> for obj in session . new : <EOL> notify ( ObjectInsert ( obj , req ) ) <EOL> for obj in session . deleted : <EOL> notify ( ObjectDelete ( obj , req ) ) <EOL> def set_owner ( event ) : <EOL> """<STR_LIT>""" <EOL> obj , request = event . object , event . request <EOL> if request is not None and isinstance ( obj , Node ) : <EOL> userid = request . authenticated_userid <EOL> if userid is not None : <EOL> userid = unicode ( userid ) <EOL> if obj . owner is None : <EOL> obj . owner = userid <EOL> if u'<STR_LIT>' not in list_groups ( userid , obj ) : <EOL> groups = list_groups_raw ( userid , obj ) | { u'<STR_LIT>' } <EOL> set_groups ( userid , obj , groups ) <EOL> def set_creation_date ( event ) : <EOL> """<STR_LIT>""" <EOL> obj = event . object <EOL> if obj . creation_date is None : <EOL> obj . creation_date = obj . modification_date = datetime . now ( ) <EOL> def set_modification_date ( event ) : <EOL> """<STR_LIT>""" <EOL> exclude = [ ] <EOL> for e in get_settings ( ) [ '<STR_LIT>' ] : <EOL> if isinstance ( event . object , e . class_ ) : <EOL> exclude . append ( e . key ) <EOL> if has_changes ( event . object , exclude = exclude ) : <EOL> event . object . modification_date = datetime . now ( ) <EOL> def delete_orphaned_tags ( event ) : <EOL> """<STR_LIT>""" <EOL> DBSession . query ( Tag ) . filter ( ~ Tag . content_tags . any ( ) ) . delete ( <EOL> synchronize_session = False ) <EOL> def cleanup_user_groups ( event ) : <EOL> """<STR_LIT>""" <EOL> name = event . object . name <EOL> if name . startswith ( "<STR_LIT>" ) : <EOL> principals = get_principals ( ) <EOL> users_groups = [ p for p in principals if name in principals [ p ] . groups ] <EOL> for user_or_group in users_groups : <EOL> principals [ user_or_group ] . groups . remove ( name ) <EOL> DBSession . query ( LocalGroup ) . filter ( <EOL> LocalGroup . principal_name == name ) . delete ( ) <EOL> def reset_content_owner ( event ) : <EOL> """<STR_LIT>""" <EOL> contents = DBSession . query ( Content ) . filter ( <EOL> Content . owner == event . object . name ) . all ( ) <EOL> for content in contents : <EOL> content . owner = None <EOL> def _update_children_paths ( old_parent_path , new_parent_path ) : <EOL> for child in DBSession . query ( Node ) . options ( <EOL> load_only ( '<STR_LIT:path>' , '<STR_LIT:type>' ) ) . filter ( <EOL> Node . path . startswith ( old_parent_path ) ) . order_by ( Node . path ) : <EOL> if child . path == new_parent_path : <EOL> continue <EOL> child . path = new_parent_path + child . path [ len ( old_parent_path ) : ] <EOL> @ no_autoflush <EOL> def _set_path_for_new_name ( target , value , oldvalue , initiator ) : <EOL> """<STR_LIT>""" <EOL> if getattr ( target , '<STR_LIT>' , False ) : <EOL> return <EOL> if value is None : <EOL> return <EOL> if target . __parent__ is None and value != u'<STR_LIT>' : <EOL> return <EOL> old_path = target . path <EOL> line = tuple ( reversed ( tuple ( lineage ( target ) ) ) ) <EOL> target_path = u'<STR_LIT:/>' . join ( node . __name__ for node in line [ : - <NUM_LIT:1> ] ) <EOL> if target . __parent__ is None and value == u'<STR_LIT>' : <EOL> target_path = u'<STR_LIT:/>' <EOL> else : <EOL> target_path += u'<STR_LIT>' . format ( value ) <EOL> target . path = target_path <EOL> target . _kotti_set_path_for_new_name = True <EOL> try : <EOL> target . name = value <EOL> finally : <EOL> del target . _kotti_set_path_for_new_name <EOL> if old_path and target . id is not None : <EOL> _update_children_paths ( old_path , target_path ) <EOL> else : <EOL> for child in _all_children ( target ) : <EOL> child . path = u'<STR_LIT>' . format ( child . __parent__ . path , <EOL> child . __name__ ) <EOL> def _all_children ( item , _all = None ) : <EOL> if _all is None : <EOL> _all = [ ] <EOL> for child in item . children : <EOL> _all . append ( child ) <EOL> _all_children ( child , _all ) <EOL> return _all <EOL> @ no_autoflush <EOL> def _set_path_for_new_parent ( target , value , oldvalue , initiator ) : <EOL> """<STR_LIT>""" <EOL> if value is None or value == oldvalue : <EOL> return <EOL> if target . __name__ is None : <EOL> return <EOL> if value . __parent__ is None and value . __name__ != u'<STR_LIT>' : <EOL> return <EOL> old_path = target . path <EOL> line = tuple ( reversed ( tuple ( lineage ( value ) ) ) ) <EOL> names = [ node . __name__ for node in line ] <EOL> if None in names : <EOL> return <EOL> target_path = u'<STR_LIT:/>' . join ( node . __name__ for node in line ) <EOL> target_path += u'<STR_LIT>' . format ( target . __name__ ) <EOL> target . path = target_path <EOL> if old_path and target . id is not None : <EOL> _update_children_paths ( old_path , target_path ) <EOL> else : <EOL> for child in _all_children ( target ) : <EOL> child . path = u'<STR_LIT>' . format ( child . __parent__ . path , <EOL> child . __name__ ) <EOL> class subscribe ( object ) : <EOL> """<STR_LIT>""" <EOL> venusian = venusian <EOL> def __init__ ( self , evttype = object , objtype = None ) : <EOL> """<STR_LIT>""" <EOL> self . evttype = evttype <EOL> self . objtype = objtype <EOL> def register ( self , context , name , obj ) : <EOL> if issubclass ( self . evttype , ObjectEvent ) : <EOL> objectevent_listeners [ ( self . evttype , self . objtype ) ] . append ( obj ) <EOL> else : <EOL> listeners [ self . evttype ] . append ( obj ) <EOL> def __call__ ( self , wrapped ) : <EOL> self . venusian . attach ( wrapped , self . register , category = '<STR_LIT>' ) <EOL> return wrapped <EOL> _WIRED_SQLALCHMEY = False <EOL> def wire_sqlalchemy ( ) : <EOL> """<STR_LIT>""" <EOL> global _WIRED_SQLALCHMEY <EOL> if _WIRED_SQLALCHMEY : <EOL> return <EOL> else : <EOL> _WIRED_SQLALCHMEY = True <EOL> sqlalchemy . event . listen ( mapper , '<STR_LIT>' , _after_delete ) <EOL> sqlalchemy . event . listen ( DBSession , '<STR_LIT>' , _before_flush ) <EOL> sqlalchemy . event . listen ( <EOL> Node . name , '<STR_LIT>' , _set_path_for_new_name , propagate = True ) <EOL> sqlalchemy . event . listen ( <EOL> Node . parent , '<STR_LIT>' , _set_path_for_new_parent , propagate = True ) <EOL> def includeme ( config ) : <EOL> """<STR_LIT>""" <EOL> from kotti . workflow import initialize_workflow <EOL> wire_sqlalchemy ( ) <EOL> objectevent_listeners [ <EOL> ( ObjectInsert , Content ) ] . append ( set_owner ) <EOL> objectevent_listeners [ <EOL> ( ObjectInsert , Content ) ] . append ( set_creation_date ) <EOL> objectevent_listeners [ <EOL> ( ObjectUpdate , Content ) ] . append ( set_modification_date ) <EOL> objectevent_listeners [ <EOL> ( ObjectAfterDelete , TagsToContents ) ] . append ( delete_orphaned_tags ) <EOL> objectevent_listeners [ <EOL> ( ObjectInsert , Content ) ] . append ( initialize_workflow ) <EOL> objectevent_listeners [ <EOL> ( UserDeleted , Principal ) ] . append ( cleanup_user_groups ) <EOL> objectevent_listeners [ <EOL> ( UserDeleted , Principal ) ] . append ( reset_content_owner ) </s>
