def test_replace(events): """If we try to replace an item without deleting it first, we'll get an error. """ container = {} # We create a first item item = Item() setitem(container, container.__setitem__, u'c', item) # We try to override with pytest.raises(KeyError): setitem(container, container.__setitem__, u'c', []) # We have to delete to replace a key del container[u'c'] setitem(container, container.__setitem__, u'c', []) assert len(events) == 4 event = events.popleft() assert IObjectAddedEvent.providedBy(event) event = events.popleft() assert IObjectModifiedEvent.providedBy(event) event = events.popleft() assert IObjectAddedEvent.providedBy(event) event = events.popleft() assert IObjectModifiedEvent.providedBy(event)
def test_fires_modified_event_on_change_props_per_changed_property(self): fti = DexterityFTI(u"testtype") fti.title = "Old title" fti.allow_discussion = False fti.global_allow = True notify_mock = self.mocker.replace('zope.event.notify') self.expect( notify_mock( mocker.MATCH( lambda x: IObjectModifiedEvent.providedBy(x) and len(x.descriptions) == 1 and x.descriptions[0].attribute == 'title' and x.descriptions[0].oldValue == "Old title" ) ) ) self.expect( notify_mock( mocker.MATCH( lambda x: IObjectModifiedEvent.providedBy(x) and len(x.descriptions) == 1 and x.descriptions[0].attribute == 'global_allow' and x.descriptions[0].oldValue is True ) ) ) self.replay() fti.manage_changeProperties( title="New title", allow_discussion=False, global_allow=False )
def test_fires_modified_event_on_change_props_per_changed_property(self): fti = DexterityFTI('testtype') fti.title = 'Old title' fti.allow_discussion = False fti.global_allow = True notify_mock = self.mocker.replace('zope.event.notify') self.expect( notify_mock( mocker.MATCH( lambda x: IObjectModifiedEvent.providedBy(x) and len( x.descriptions) == 1 and x.descriptions[0].attribute == 'title' and x.descriptions[0].oldValue == 'Old title'))) self.expect( notify_mock( mocker.MATCH( lambda x: IObjectModifiedEvent.providedBy(x) and len( x.descriptions) == 1 and x.descriptions[0].attribute == 'global_allow' and x.descriptions[0].oldValue is True))) self.replay() fti.manage_changeProperties(title='New title', allow_discussion=False, global_allow=False)
def extract_what(self): #Archetypes if IObjectInitializedEvent.providedBy(self.event): return 'created', self.get_object_modified_info() elif IObjectEditedEvent.providedBy(self.event): return 'modified', self.get_object_modified_info() # #TODO: plone.app.iterate # elif ICheckinEvent.providedBy(self.event): # return 'checkedin' # elif ICheckoutEvent.providedBy(self.event): # return 'checkedout' # elif IWorkingCopyDeletedEvent.providedBy(self.event): # return 'workingcopydeleted' # DCWorkflow elif IAfterTransitionEvent.providedBy(self.event): return 'statechanged', self.get_transition_info() # CMFCore (useless) # elif IActionSucceededEvent.providedBy(self.event): # return None # return 'statechanged', self.get_action_succeed_info() # zope # elif IObjectAddedEvent.providedBy(self.event): # return 'added', self.get_object_moved_info() elif IObjectCopiedEvent.providedBy(self.event): return 'copied', self.get_object_copied_info() elif IObjectMovedEvent.providedBy(self.event): return 'moved', self.get_object_moved_info() elif IObjectRemovedEvent.providedBy(self.event): return 'removed', self.get_object_moved_info() elif IObjectModifiedEvent.providedBy(self.event): return 'modified', self.get_object_modified_info() return None, {}
def set_sitting_parent_ids(ob, event): """We add the group ID/sesssion id if adding a sitting in contexts not bound to groups in traversal hierarchy """ scheduling_context = ISchedulingContext(ob.__parent__, None) if ob.group_id is None: if scheduling_context is not None: ob.group_id = removeSecurityProxy(scheduling_context).group_id if ob.session_id is None or IObjectModifiedEvent.providedBy(event): if scheduling_context is not None: group = scheduling_context.get_group() if interfaces.IParliament.providedBy(group): container = removeSecurityProxy(group).sessions else: return else: try: container = ob.group.sessions except AttributeError: return try: session_id = container._query.filter( sql.and_( domain.Session.start_date < ob.start_date, domain.Session.end_date > ob.end_date ) ).one().session_id ob.session_id = session_id except (orm.exc.NoResultFound, orm.exc.MultipleResultsFound): log.error("Could not determine session for sitting %s", ob)
def test_move_event(events): """If the item had a parent or name (as in a move or rename), we generate a move event, rather than an add event. """ container = {} # We create a first item item = Item() setitem(container, container.__setitem__, u'c1', item) # Add operation are "moved" events. assert len(events) == 2 event = events.popleft() assert IObjectAddedEvent.providedBy(event) event = events.popleft() assert IObjectModifiedEvent.providedBy(event) assert isinstance(event, ContainerModifiedEvent) # We created an item already contained. item = Item() item.__parent__, item.__name__ = container, 'c2' setitem(container, container.__setitem__, u'c2', item) assert not len(events) # We now rewrite 'c2' under another name # Thus, we created a move event : +1 modification +1 move. setitem(container, container.__setitem__, u'c3', item) assert len(container) == 3 event = events.popleft() assert IObjectMovedEvent.providedBy(event)
def test_order_events(self): # Prepare the setup:: from zope.container.contained import ContainerModifiedEvent # Prepare some objects:: from zope.container.ordered import OrderedContainer oc = OrderedContainer() oc['foo'] = 'bar' oc['baz'] = 'quux' oc['zork'] = 'grue' self.assertEqual(oc.keys(), ['foo', 'baz', 'zork']) # Now change the order:: clearEvents() oc.updateOrder(['baz', 'foo', 'zork']) self.assertEqual(oc.keys(), ['baz', 'foo', 'zork']) # Check what events have been sent:: events = getEvents() self.assertEqual(1, len(events)) self.assertIsInstance(events[0], ContainerModifiedEvent) # This is in fact a specialized modification event:: from zope.lifecycleevent.interfaces import IObjectModifiedEvent self.assertTrue(IObjectModifiedEvent.providedBy(events[0]))
def set_sitting_parent_ids(ob, event): """We add the group ID/sesssion id if adding a sitting in contexts not bound to groups in traversal hierarchy """ scheduling_context = ISchedulingContext(ob.__parent__, None) if ob.group_id is None: if scheduling_context is not None: ob.group_id = removeSecurityProxy(scheduling_context).group_id if ob.session_id is None or IObjectModifiedEvent.providedBy(event): if scheduling_context is not None: group = scheduling_context.get_group() if interfaces.IChamber.providedBy(group): container = removeSecurityProxy(group).sessions else: return else: try: container = ob.group.sessions except AttributeError: return try: session_id = container._query.filter( sql.and_( domain.Session.start_date < ob.start_date, domain.Session.end_date > ob.end_date)).one().session_id ob.session_id = session_id except (orm.exc.NoResultFound, orm.exc.MultipleResultsFound): log.error("Could not determine session for sitting %s", ob)
def test_event_descriptions(self): self.request.form = {'description': 'test'} view = ContentView(self.todo1, self.request) view.can_edit = True view.update() self.assertTrue(IObjectModifiedEvent.providedBy(self.event)) self.assertTrue(IAttributes.providedBy(self.event.descriptions[0])) self.assertEqual(self.event.descriptions[0].interface, IBasic) self.assertEqual(self.event.descriptions[0].attributes, ('description',))
def test_event_onsetProperty(self): request = TestRequest( set_properties = """<Dt:exampletextprop xmlns:Dt="DAVtest:">Example Text Prop</Dt:exampletextprop>""") resource = Resource("Text Prop", 10) propp = z3c.dav.proppatch.PROPPATCH(resource, request) propp.PROPPATCH() self.assertEqual(resource.text, "Example Text Prop") # property modified self.assertEqual(len(self.events), 1) self.assert_(IObjectModifiedEvent.providedBy(self.events[0])) self.assertEqual(self.events[0].object, resource)
def __call__(self, content, event): """Called by the event system.""" if IDexterityTranslatable.providedBy(content): self.canonical = ITranslationManager(content).query_canonical() if event.descriptions \ and len(event.descriptions) > 1 \ and event.descriptions[1] == self.canonical: return if IObjectModifiedEvent.providedBy(event): self.handle_modified(content)
def test_interface_providing(events): """If the object implements `ILocation`, but not `IContained`, set it's `__parent__` and `__name__` attributes *and* declare that it implements `IContained`. """ container = {} item = Location() assert not IContained.providedBy(item) setitem(container, container.__setitem__, u'l', item) assert container[u'l'] is item assert item.__parent__ is container assert item.__name__ == u'l' assert IContained.providedBy(item) # We get added and modification events: assert len(events) == 2 event = events.popleft() assert IObjectAddedEvent.providedBy(event) event = events.popleft() assert IObjectModifiedEvent.providedBy(event) # If the object doesn't even implement `ILocation`, put a # `ContainedProxy` around it: item = [] setitem(container, container.__setitem__, u'i', item) assert container[u'i'] == [] assert not container[u'i'] is item item = container[u'i'] assert item.__parent__ is container assert item.__name__ == u'i' assert IContained.providedBy(item) event = events.popleft() assert IObjectAddedEvent.providedBy(event) event = events.popleft() assert IObjectModifiedEvent.providedBy(event)
def test_multiple_event_descriptions(self): self.request.form = { 'title': 'Todo 2000', 'description': 'test', 'due': '2015-10-24', } view = ContentView(self.todo1, self.request) view.can_edit = True view.update() self.assertTrue(IObjectModifiedEvent.providedBy(self.event)) self.assertTrue(IAttributes.providedBy(self.event.descriptions[0])) self.assertTrue(IAttributes.providedBy(self.event.descriptions[1])) descs = dict([(d.interface, d.attributes) for d in self.event.descriptions]) self.assertEqual(descs[IBasic], ('description', 'title')) self.assertEqual(descs[ITodo], ('due', ))
def __call__(self, content, event): """Called by the event system.""" sdm = content.session_data_manager session = sdm.getSessionData() if "tg" in session.keys(): # In case it's a on the fly translation avoid return if IDexterityTranslatable.providedBy(content): self.canonical = ITranslationManager(content).query_canonical() if event.descriptions and len(event.descriptions) > 1 and event.descriptions[1] == self.canonical: return if IObjectModifiedEvent.providedBy(event): self.handle_modified(content)
def test_fires_modified_event_on_update_property_if_changed(self): fti = DexterityFTI(u"testtype") fti.title = u"Old title" fti.global_allow = False notify_mock = self.mocker.replace('zope.event.notify') self.expect(notify_mock(mocker.MATCH(lambda x: IObjectModifiedEvent.providedBy(x) \ and len(x.descriptions) == 1 \ and x.descriptions[0].attribute == 'title' \ and x.descriptions[0].oldValue == "Old title"))) self.replay() fti._updateProperty('title', "New title") # fires event caught above fti._updateProperty('allow_discussion', False) # does not fire
def test_event_multipleProperty(self): request = TestRequest( set_properties = """ <Dt:exampletextprop xmlns:Dt="DAVtest:">Text Prop</Dt:exampletextprop> <Dt:exampleintprop xmlns:Dt="DAVtest:">14</Dt:exampleintprop> """) resource = Resource("Text Prop", 10) propp = z3c.dav.proppatch.PROPPATCH(resource, request) propp.PROPPATCH() self.assertEqual(resource.text, "Text Prop") self.assertEqual(resource.intprop, 14) self.assertEqual(len(self.events), 1) self.assertEqual(IObjectModifiedEvent.providedBy(self.events[0]), True) self.assertEqual(self.events[0].object, resource)
def test_fires_modified_event_on_update_property_if_changed(self): fti = DexterityFTI(u"testtype") fti.title = u"Old title" fti.global_allow = False from zope.event import notify notify_mock = self.patch_global(notify) fti._updateProperty('title', "New title") # fires event caught above fti._updateProperty('allow_discussion', False) # does not fire event = notify_mock.call_args[0][0] self.assertTrue(IObjectModifiedEvent.providedBy(event)) self.assertEqual(len(event.descriptions), 1) self.assertEqual(event.descriptions[0].attribute, 'title') self.assertEqual(event.descriptions[0].oldValue, 'Old title')
def test_event_on_remove_property(self): request = TestRequest( remove_properties = """<Dt:exampledeadprop xmlns:Dt="example:">Example Text Prop</Dt:exampledeadprop>""") testprop = "{example:}exampledeadprop" resource = Resource("Text Prop", 10) deadprops = DEADProperties(resource) deadprops.setProperty(testprop, "Example Text Prop") propp = z3c.dav.proppatch.PROPPATCH(resource, request) propp.PROPPATCH() self.assertEqual(deadprops.hasProperty(testprop), False) self.assertEqual(len(self.events), 1) self.assertEqual(IObjectModifiedEvent.providedBy(self.events[0]), True) self.assertEqual(self.events[0].object, resource)
def test_order_events(events): oc = OrderedBTreeContainer() oc['foo'] = 'bar' oc['baz'] = 'quux' oc['zork'] = 'grue' assert oc.keys() == ['foo', 'baz', 'zork'] events.clear() oc.updateOrder(['baz', 'foo', 'zork']) assert oc.keys() == ['baz', 'foo', 'zork'] assert [event.__class__.__name__ for event in events] == [ 'ContainerModifiedEvent', ] assert IObjectModifiedEvent.providedBy(events.pop())
def __call__(self, content, event): """Called by the event system.""" request = getattr(event.object, 'REQUEST', getRequest()) translation_info = getattr(request, 'translation_info', {}) if 'tg' in translation_info.keys(): # In case it's a on the fly translation avoid return if IDexterityTranslatable.providedBy(content): self.canonical = ITranslationManager(content).query_canonical() if event.descriptions \ and len(event.descriptions) > 1 \ and event.descriptions[1] == self.canonical: return if IObjectModifiedEvent.providedBy(event): self.handle_modified(content)
def __call__(self, content, event): """Called by the event system.""" if aq_parent(content) is None: # The event is thrown on a non acquired object # so we can't get the session_data_manager sdm = getSite().session_data_manager else: sdm = getToolByName(content, 'session_data_manager') session = sdm.getSessionData() if 'tg' in session.keys(): # In case it's a on the fly translation avoid return if IDexterityTranslatable.providedBy(content): self.canonical = ITranslationManager(content).query_canonical() if event.descriptions \ and len(event.descriptions) > 1 \ and event.descriptions[1] == self.canonical: return if IObjectModifiedEvent.providedBy(event): self.handle_modified(content)
def getLogEntry(self): ''' Get's a log entry for your action ''' event = self.event obj = event.object data = {'info': ''} # order of those checks is important since some interfaces # base off the others if IPloneFormGenField.providedBy(obj): # if ploneformgen field, use parent object for modified data data['field'] = obj.getId() obj = aq_parent(obj) # the order of those interface checks matters since some interfaces # inherit from others if IObjectRemovedEvent.providedBy(event): # need to keep track of removed events so it doesn't get called # more than once for each object action = 'removed' elif ( IObjectInitializedEvent.providedBy(event) or IObjectCreatedEvent.providedBy(event) or IObjectAddedEvent.providedBy(event) ): action = 'added' elif IObjectMovedEvent.providedBy(event): # moves can also be renames. Check the parent object if event.oldParent == event.newParent: if 'Rename' not in self.rule.rule.title: # cut out here, double action for this event return {} data['info'] = 'previous id: %s' % event.oldName action = 'rename' else: if 'Moved' not in self.rule.rule.title: # step out immediately since this could be a double action return {} data['info'] = 'previous location: %s/%s' % ( '/'.join(event.oldParent.getPhysicalPath()), event.oldName, ) action = 'moved' elif IObjectModifiedEvent.providedBy(event): action = 'modified' elif IActionSucceededEvent.providedBy(event): data['info'] = 'workflow transition: %s; comments: %s' % ( event.action, self.get_history_comment(), ) action = 'workflow' elif IObjectClonedEvent.providedBy(event): action = 'copied' elif ICheckinEvent.providedBy(event): data['info'] = event.message action = 'checked in' self.request.environ['disable.auditlog'] = True data['working_copy'] = '/'.join(obj.getPhysicalPath()) obj = event.baseline elif IBeforeCheckoutEvent.providedBy(event): action = 'checked out' self.request.environ['disable.auditlog'] = True elif ICancelCheckoutEvent.providedBy(event): action = 'cancel check out' self.request.environ['disable.auditlog'] = True data['working_copy'] = '/'.join(obj.getPhysicalPath()) obj = event.baseline else: logger.warn('no action matched') return {} if IWorkingCopy.providedBy(obj): # if working copy, iterate, check if Track Working Copies is # enabled if not self.trackWorkingCopies: # if not enabled, we only care about checked messages if 'check' not in action: return {} # if enabled in control panel, use original object and move # working copy path to working_copy data['working_copy'] = '/'.join(obj.getPhysicalPath()) relationships = obj.getReferences( WorkingCopyRelation.relationship) # check relationships, if none, something is wrong, not logging # action if len(relationships) <= 0: return {} obj = relationships[0] data.update(self._getObjectInfo(obj)) data['action'] = action return data
def __call__(self, content, event): """Called by the event system.""" if IArchetypesTranslatable.providedBy(content): if IObjectModifiedEvent.providedBy(event): self.handle_modified(content)
def test_event_thrown_on_field_change(self): self.request.form = {'description': 'test'} view = ContentView(self.todo1, self.request) view.can_edit = True view.update() self.assertTrue(IObjectModifiedEvent.providedBy(self.event))
def getLogEntry(self): ''' Get's a log entry for your action ''' event = self.event obj = event.object data = {'info': ''} # order of those checks is important since some interfaces # base off the others if IPloneFormGenField.providedBy(obj): # if ploneformgen field, use parent object for modified data data['field'] = obj.getId() obj = aq_parent(obj) # the order of those interface checks matters since some interfaces # inherit from others if IObjectRemovedEvent.providedBy(event): # need to keep track of removed events so it doesn't get called # more than once for each object action = 'removed' elif (IObjectInitializedEvent.providedBy(event) or IObjectCreatedEvent.providedBy(event) or IObjectAddedEvent.providedBy(event)): action = 'added' elif IObjectMovedEvent.providedBy(event): # moves can also be renames. Check the parent object if event.oldParent == event.newParent: if 'Rename' not in self.rule.rule.title: # cut out here, double action for this event return {} data['info'] = 'previous id: %s' % event.oldName action = 'rename' else: if 'Moved' not in self.rule.rule.title: # step out immediately since this could be a double action return {} data['info'] = 'previous location: %s/%s' % ( '/'.join(event.oldParent.getPhysicalPath()), event.oldName, ) action = 'moved' elif IObjectModifiedEvent.providedBy(event): action = 'modified' elif IActionSucceededEvent.providedBy(event): data['info'] = 'workflow transition: %s; comments: %s' % ( event.action, self.get_history_comment(), ) action = 'workflow' elif IObjectClonedEvent.providedBy(event): action = 'copied' elif ICheckinEvent.providedBy(event): data['info'] = event.message action = 'checked in' self.request.environ['disable.auditlog'] = True data['working_copy'] = '/'.join(obj.getPhysicalPath()) obj = event.baseline elif IBeforeCheckoutEvent.providedBy(event): action = 'checked out' self.request.environ['disable.auditlog'] = True elif ICancelCheckoutEvent.providedBy(event): action = 'cancel check out' self.request.environ['disable.auditlog'] = True data['working_copy'] = '/'.join(obj.getPhysicalPath()) obj = event.baseline else: logger.warn('no action matched') return {} if IWorkingCopy.providedBy(obj): # if working copy, iterate, check if Track Working Copies is # enabled if not self.trackWorkingCopies: # if not enabled, we only care about checked messages if 'check' not in action: return {} # if enabled in control panel, use original object and move # working copy path to working_copy data['working_copy'] = '/'.join(obj.getPhysicalPath()) relationships = obj.getReferences(WorkingCopyRelation.relationship) # check relationships, if none, something is wrong, not logging # action if len(relationships) <= 0: return {} obj = relationships[0] data.update(self._getObjectInfo(obj)) data['action'] = action return data
def __call__(self): req = getRequest() if req.environ.get('disable.auditlog', False): return True event = self.event obj = event.object # order of those checks is important since some interfaces # base off the others rule = inspect.stack()[1][0].f_locals.get('self', None) registry = getUtility(IRegistry) trackWorkingCopies = registry['collective.auditlog.interfaces.IAuditLogSettings.trackworkingcopies'] # noqa if not self.canExecute(rule, req): return True # cut out early, we can't do this event data = { 'info': '' } if IPloneFormGenField.providedBy(obj): # if ploneformgen field, use parent object for modified data data['field'] = obj.getId() obj = aq_parent(obj) # the order of those interface checks matters since some interfaces # inherit from others if IObjectRemovedEvent.providedBy(event): # need to keep track of removed events so it doesn't get called # more than once for each object action = 'removed' elif (IObjectInitializedEvent.providedBy(event) or IObjectCreatedEvent.providedBy(event) or IObjectAddedEvent.providedBy(event)): action = 'added' elif IObjectMovedEvent.providedBy(event): # moves can also be renames. Check the parent object if event.oldParent == event.newParent: if rule is None or 'Rename' in rule.rule.title: info = {'previous_id': event.oldName} data['info'] = json.dumps(info) action = 'rename' else: # cut out here, double action for this event return True else: if rule is None or 'Moved' in rule.rule.title: parent_path = '/'.join(event.oldParent.getPhysicalPath()) previous_location = parent_path + '/' + event.oldName info = {'previous_location': previous_location} data['info'] = json.dumps(info) action = 'moved' else: # step out immediately since this could be a double action return True elif IObjectModifiedEvent.providedBy(event): action = 'modified' elif IActionSucceededEvent.providedBy(event): info = {'transition': event.action, 'comments': self.get_history_comment()} data['info'] = json.dumps(info) action = 'workflow' elif IObjectClonedEvent.providedBy(event): action = 'copied' elif ICheckinEvent.providedBy(event): info = {'message': event.message} data['info'] = json.dumps(info) action = 'checked in' req.environ['disable.auditlog'] = True data['working_copy'] = '/'.join(obj.getPhysicalPath()) obj = event.baseline elif IBeforeCheckoutEvent.providedBy(event): action = 'checked out' req.environ['disable.auditlog'] = True elif ICancelCheckoutEvent.providedBy(event): action = 'cancel check out' req.environ['disable.auditlog'] = True data['working_copy'] = '/'.join(obj.getPhysicalPath()) obj = event.baseline elif IUserLoggedInEvent.providedBy(event): action = 'logged in' info = {'user': event.object.getUserName()} data['info'] = json.dumps(info) elif IUserLoggedOutEvent.providedBy(event): action = 'logged out' else: logger.warn('no action matched') return True if IWorkingCopy.providedBy(obj): # if working copy, iterate, check if Track Working Copies is # enabled if trackWorkingCopies: # if enabled in control panel, use original object and move # working copy path to working_copy data['working_copy'] = '/'.join(obj.getPhysicalPath()) relationships = obj.getReferences( WorkingCopyRelation.relationship) # check relationships, if none, something is wrong, not logging # action if len(relationships) > 0: obj = relationships[0] else: return True else: # if not enabled, we only care about checked messages if 'check' not in action: return True data.update(getObjectInfo(obj)) data['action'] = action addLogEntry(obj, data) return True