vendor/Five/1.2b-r20590

view event.py @ 0:3673ed425f80

Vendor import of Five 1.2b+ (r20590)
author fguillaume
date Fri, 02 Dec 2005 20:25:42 +0000
parents
children
line source
1 ##############################################################################
2 #
3 # Copyright (c) 2004, 2005 Zope Corporation and Contributors.
4 # All Rights Reserved.
5 #
6 # This software is subject to the provisions of the Zope Public License,
7 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 # FOR A PARTICULAR PURPOSE.
12 #
13 ##############################################################################
14 """
15 Five event monkey patches.
17 $Id: event.py 20402 2005-11-29 16:18:20Z efge $
18 """
20 import warnings
21 import sys
22 from cgi import escape
23 from Acquisition import aq_base, aq_parent, aq_inner
24 from App.Dialogs import MessageDialog
25 from AccessControl import getSecurityManager
26 from ZODB.POSException import ConflictError
27 from OFS import Moniker
28 from OFS.CopySupport import CopyContainer
29 from OFS.CopySupport import CopyError # Yuck, a string exception
30 from OFS.CopySupport import eNoData, eNotFound, eInvalid, eNotSupported
31 from OFS.CopySupport import cookie_path, sanity_check, _cb_decode
32 from webdav.Lockable import ResourceLockedError
34 from zope.event import notify
35 from zope.app.container.contained import ObjectMovedEvent
36 from zope.app.container.contained import ObjectAddedEvent
37 from zope.app.container.contained import ObjectRemovedEvent
38 from zope.app.container.contained import notifyContainerModified
39 from zope.app.event.objectevent import ObjectCopiedEvent
40 from OFS.event import ObjectWillBeMovedEvent
41 from OFS.event import ObjectWillBeAddedEvent
42 from OFS.event import ObjectWillBeRemovedEvent
43 from OFS.event import ObjectClonedEvent
44 from OFS.subscribers import deprecatedManageAddDeleteClasses
45 from OFS.subscribers import compatibilityCall
46 from Products.Five.fiveconfigure import isFiveMethod
49 FIVE_ORIGINAL_PREFIX = '__five_original_'
52 hasContainerEvents = False
54 ##################################################
55 # Monkey patches
57 _marker = object()
59 # From ObjectManager / Item
60 def manage_afterAdd(self, item, container):
61 # Don't do recursion anymore, a subscriber does that.
62 pass
64 # From ObjectManager / Item
65 def manage_beforeDelete(self, item, container):
66 # Don't do recursion anymore, a subscriber does that.
67 pass
69 # From ObjectManager / Item
70 def manage_afterClone(self, item):
71 # Don't do recursion anymore, a subscriber does that.
72 pass
74 # From CatalogAware / CatalogPathAware
75 def CA_manage_afterAdd(self, item, container):
76 # Don't do recursion anymore, a subscriber does that.
77 self.index_object()
79 # From CatalogAware / CatalogPathAware
80 def CA_manage_beforeDelete(self, item, container):
81 # Don't do recursion anymore, a subscriber does that.
82 self.unindex_object()
84 # From CatalogAware / CatalogPathAware
85 def CA_manage_afterClone(self, item):
86 # Don't do recursion anymore, a subscriber does that.
87 self.index_object()
90 # From ObjectManager
91 def _setObject(self, id, object, roles=None, user=None, set_owner=1,
92 suppress_events=False):
93 """Set an object into this container.
95 Also sends IObjectAddedEvent.
96 """
97 ob = object # better name, keep original function signature
98 v = self._checkId(id)
99 if v is not None:
100 id = v
101 t = getattr(ob, 'meta_type', None)
103 # If an object by the given id already exists, remove it.
104 for object_info in self._objects:
105 if object_info['id'] == id:
106 self._delObject(id)
107 break
109 if not suppress_events:
110 notify(ObjectWillBeAddedEvent(ob, self, id))
112 self._objects = self._objects + ({'id': id, 'meta_type': t},)
113 self._setOb(id, ob)
114 ob = self._getOb(id)
116 if set_owner:
117 # TODO: eventify manage_fixupOwnershipAfterAdd
118 # This will be called for a copy/clone, or a normal _setObject.
119 ob.manage_fixupOwnershipAfterAdd()
121 if set_owner:
122 # Try to give user the local role "Owner", but only if
123 # no local roles have been set on the object yet.
124 if getattr(ob, '__ac_local_roles__', _marker) is None:
125 user = getSecurityManager().getUser()
126 if user is not None:
127 userid = user.getId()
128 if userid is not None:
129 ob.manage_setLocalRoles(userid, ['Owner'])
131 if not suppress_events:
132 notify(ObjectAddedEvent(ob, self, id))
133 notifyContainerModified(self)
135 compatibilityCall('manage_afterAdd', ob, ob, self)
137 return id
140 # From BTreeFolder2
141 def BT_setObject(self, id, object, roles=None, user=None, set_owner=1,
142 suppress_events=False):
143 ob = object # better name, keep original function signature
144 v = self._checkId(id)
145 if v is not None:
146 id = v
148 # If an object by the given id already exists, remove it.
149 if self.has_key(id):
150 self._delObject(id)
152 if not suppress_events:
153 notify(ObjectWillBeAddedEvent(ob, self, id))
155 self._setOb(id, ob)
156 ob = self._getOb(id)
158 if set_owner:
159 # TODO: eventify manage_fixupOwnershipAfterAdd
160 # This will be called for a copy/clone, or a normal _setObject.
161 ob.manage_fixupOwnershipAfterAdd()
163 if set_owner:
164 # Try to give user the local role "Owner", but only if
165 # no local roles have been set on the object yet.
166 if getattr(ob, '__ac_local_roles__', _marker) is None:
167 user = getSecurityManager().getUser()
168 if user is not None:
169 userid = user.getId()
170 if userid is not None:
171 ob.manage_setLocalRoles(userid, ['Owner'])
173 if not suppress_events:
174 notify(ObjectAddedEvent(ob, self, id))
175 notifyContainerModified(self)
177 compatibilityCall('manage_afterAdd', ob, ob, self)
179 return id
182 # From ObjectManager
183 def _delObject(self, id, dp=1, suppress_events=False):
184 """Delete an object from this container.
186 Also sends IObjectRemovedEvent.
187 """
188 ob = self._getOb(id)
190 compatibilityCall('manage_beforeDelete', ob, ob, self)
192 if not suppress_events:
193 notify(ObjectWillBeRemovedEvent(ob, self, id))
195 self._objects = tuple([i for i in self._objects
196 if i['id'] != id])
197 self._delOb(id)
199 # Indicate to the object that it has been deleted. This is
200 # necessary for object DB mount points. Note that we have to
201 # tolerate failure here because the object being deleted could
202 # be a Broken object, and it is not possible to set attributes
203 # on Broken objects.
204 try:
205 ob._v__object_deleted__ = 1
206 except:
207 pass
209 if not suppress_events:
210 notify(ObjectRemovedEvent(ob, self, id))
211 notifyContainerModified(self)
214 # From BTreeFolder2
215 def BT_delObject(self, id, dp=1, suppress_events=False):
216 ob = self._getOb(id)
218 compatibilityCall('manage_beforeDelete', ob, ob, self)
220 if not suppress_events:
221 notify(ObjectWillBeRemovedEvent(ob, self, id))
223 self._delOb(id)
225 if not suppress_events:
226 notify(ObjectRemovedEvent(ob, self, id))
227 notifyContainerModified(self)
229 # From CopyContainer
230 def manage_renameObject(self, id, new_id, REQUEST=None):
231 """Rename a particular sub-object.
232 """
233 try:
234 self._checkId(new_id)
235 except:
236 raise CopyError, MessageDialog(
237 title='Invalid Id',
238 message=sys.exc_info()[1],
239 action ='manage_main')
241 ob = self._getOb(id)
243 if ob.wl_isLocked():
244 raise ResourceLockedError, ('Object "%s" is locked via WebDAV'
245 % ob.getId())
246 if not ob.cb_isMoveable():
247 raise CopyError, eNotSupported % escape(id)
248 self._verifyObjectPaste(ob)
250 try:
251 ob._notifyOfCopyTo(self, op=1)
252 except ConflictError:
253 raise
254 except:
255 raise CopyError, MessageDialog(
256 title="Rename Error",
257 message=sys.exc_info()[1],
258 action ='manage_main')
260 notify(ObjectWillBeMovedEvent(ob, self, id, self, new_id))
262 try:
263 self._delObject(id, suppress_events=True)
264 except TypeError:
265 # BBB: removed in Zope 2.11
266 self._delObject(id)
267 warnings.warn(
268 "%s._delObject without suppress_events is deprecated "
269 "and will be removed in Zope 2.11." %
270 self.__class__.__name__, DeprecationWarning)
271 ob = aq_base(ob)
272 ob._setId(new_id)
274 # Note - because a rename always keeps the same context, we
275 # can just leave the ownership info unchanged.
276 try:
277 self._setObject(new_id, ob, set_owner=0, suppress_events=True)
278 except TypeError:
279 # BBB: removed in Zope 2.11
280 self._setObject(new_id, ob, set_owner=0)
281 warnings.warn(
282 "%s._setObject without suppress_events is deprecated "
283 "and will be removed in Zope 2.11." %
284 self.__class__.__name__, DeprecationWarning)
285 ob = self._getOb(new_id)
287 notify(ObjectMovedEvent(ob, self, id, self, new_id))
288 notifyContainerModified(self)
290 ob._postCopy(self, op=1)
292 if REQUEST is not None:
293 return self.manage_main(self, REQUEST, update_menu=1)
294 return None
297 # From CopyContainer
298 def manage_pasteObjects(self, cb_copy_data=None, REQUEST=None):
299 """Paste previously copied objects into the current object.
301 If calling manage_pasteObjects from python code, pass the result of a
302 previous call to manage_cutObjects or manage_copyObjects as the first
303 argument.
305 Also sends IObjectCopiedEvent or IObjectMovedEvent.
306 """
307 if cb_copy_data is not None:
308 cp = cb_copy_data
309 elif REQUEST is not None and REQUEST.has_key('__cp'):
310 cp = REQUEST['__cp']
311 else:
312 cp = None
313 if cp is None:
314 raise CopyError, eNoData
316 try:
317 op, mdatas = _cb_decode(cp)
318 except:
319 raise CopyError, eInvalid
321 oblist = []
322 app = self.getPhysicalRoot()
323 for mdata in mdatas:
324 m = Moniker.loadMoniker(mdata)
325 try:
326 ob = m.bind(app)
327 except ConflictError:
328 raise
329 except:
330 raise CopyError, eNotFound
331 self._verifyObjectPaste(ob, validate_src=op+1)
332 oblist.append(ob)
334 result = []
335 if op == 0:
336 # Copy operation
337 for ob in oblist:
338 orig_id = ob.getId()
339 if not ob.cb_isCopyable():
340 raise CopyError, eNotSupported % escape(orig_id)
342 try:
343 ob._notifyOfCopyTo(self, op=0)
344 except ConflictError:
345 raise
346 except:
347 raise CopyError, MessageDialog(
348 title="Copy Error",
349 message=sys.exc_info()[1],
350 action='manage_main')
352 id = self._get_id(orig_id)
353 result.append({'id': orig_id, 'new_id': id})
355 orig_ob = ob
356 ob = ob._getCopy(self)
357 ob._setId(id)
358 notify(ObjectCopiedEvent(ob, orig_ob))
360 self._setObject(id, ob)
361 ob = self._getOb(id)
362 ob.wl_clearLocks()
364 ob._postCopy(self, op=0)
366 compatibilityCall('manage_afterClone', ob, ob)
368 notify(ObjectClonedEvent(ob))
370 if REQUEST is not None:
371 return self.manage_main(self, REQUEST, update_menu=1,
372 cb_dataValid=1)
374 elif op == 1:
375 # Move operation
376 for ob in oblist:
377 orig_id = ob.getId()
378 if not ob.cb_isMoveable():
379 raise CopyError, eNotSupported % escape(orig_id)
381 try:
382 ob._notifyOfCopyTo(self, op=1)
383 except ConflictError:
384 raise
385 except:
386 raise CopyError, MessageDialog(
387 title="Move Error",
388 message=sys.exc_info()[1],
389 action='manage_main')
391 if not sanity_check(self, ob):
392 raise CopyError, "This object cannot be pasted into itself"
394 orig_container = aq_parent(aq_inner(ob))
395 if aq_base(orig_container) is aq_base(self):
396 id = orig_id
397 else:
398 id = self._get_id(orig_id)
399 result.append({'id': orig_id, 'new_id': id})
401 notify(ObjectWillBeMovedEvent(ob, orig_container, orig_id,
402 self, id))
404 # try to make ownership explicit so that it gets carried
405 # along to the new location if needed.
406 ob.manage_changeOwnershipType(explicit=1)
408 try:
409 orig_container._delObject(orig_id, suppress_events=True)
410 except TypeError:
411 # BBB: removed in Zope 2.11
412 orig_container._delObject(orig_id)
413 warnings.warn(
414 "%s._delObject without suppress_events is deprecated "
415 "and will be removed in Zope 2.11." %
416 orig_container.__class__.__name__, DeprecationWarning)
417 ob = aq_base(ob)
418 ob._setId(id)
420 try:
421 self._setObject(id, ob, set_owner=0, suppress_events=True)
422 except TypeError:
423 # BBB: removed in Zope 2.11
424 self._setObject(id, ob, set_owner=0)
425 warnings.warn(
426 "%s._setObject without suppress_events is deprecated "
427 "and will be removed in Zope 2.11." %
428 self.__class__.__name__, DeprecationWarning)
429 ob = self._getOb(id)
431 notify(ObjectMovedEvent(ob, orig_container, orig_id, self, id))
432 notifyContainerModified(orig_container)
433 if aq_base(orig_container) is not aq_base(self):
434 notifyContainerModified(self)
436 ob._postCopy(self, op=1)
437 # try to make ownership implicit if possible
438 ob.manage_changeOwnershipType(explicit=0)
440 if REQUEST is not None:
441 REQUEST['RESPONSE'].setCookie('__cp', 'deleted',
442 path='%s' % cookie_path(REQUEST),
443 expires='Wed, 31-Dec-97 23:59:59 GMT')
444 REQUEST['__cp'] = None
445 return self.manage_main(self, REQUEST, update_menu=1,
446 cb_dataValid=0)
448 return result
450 # From CopyContainer
451 def manage_clone(self, ob, id, REQUEST=None):
452 """Clone an object, creating a new object with the given id.
453 """
454 if not ob.cb_isCopyable():
455 raise CopyError, eNotSupported % escape(ob.getId())
456 try:
457 self._checkId(id)
458 except:
459 raise CopyError, MessageDialog(
460 title='Invalid Id',
461 message=sys.exc_info()[1],
462 action ='manage_main')
464 self._verifyObjectPaste(ob)
466 try:
467 ob._notifyOfCopyTo(self, op=0)
468 except ConflictError:
469 raise
470 except:
471 raise CopyError, MessageDialog(
472 title="Clone Error",
473 message=sys.exc_info()[1],
474 action='manage_main')
476 orig_ob = ob
477 ob = ob._getCopy(self)
478 ob._setId(id)
479 notify(ObjectCopiedEvent(ob, orig_ob))
481 self._setObject(id, ob)
482 ob = self._getOb(id)
484 ob._postCopy(self, op=0)
486 compatibilityCall('manage_afterClone', ob, ob)
488 notify(ObjectClonedEvent(ob))
490 return ob
492 # From OrderSupport
493 def moveObjectsByDelta(self, ids, delta, subset_ids=None,
494 suppress_events=False):
495 """ Move specified sub-objects by delta.
496 """
497 res = self.__five_original_moveObjectsByDelta(ids, delta, subset_ids)
498 if not suppress_events:
499 notifyContainerModified(self)
500 return res
502 def moveObjectToPosition(self, id, position, suppress_events=False):
503 """ Move specified object to absolute position.
504 """
505 delta = position - self.getObjectPosition(id)
506 return self.moveObjectsByDelta(id, delta, suppress_events=suppress_events)
508 def OS_manage_renameObject(self, id, new_id, REQUEST=None):
509 """ Rename a particular sub-object without changing its position.
510 """
511 old_position = self.getObjectPosition(id)
512 res = CopyContainer.manage_renameObject(self, id, new_id, REQUEST)
513 try:
514 self.moveObjectToPosition(new_id, old_position, suppress_events=True)
515 except TypeError:
516 # BBB: removed in Zope 2.11
517 self.moveObjectToPosition(new_id, old_position)
518 warnings.warn(
519 "%s.moveObjectToPosition without suppress_events is "
520 "deprecated and will be removed in Zope 2.11." %
521 self.__class__.__name__, DeprecationWarning)
522 return res
525 ##################################################
526 # Fix OFS.Application's creation of some objects.
527 #
528 # The application object creates root objects like error_log,
529 # browser_id_manager, session_data_manager
530 #
531 # They all expects their manage_afterAdd to be called, but they are
532 # created before Five 1.2 is initialized and has had a chance to do its
533 # patches. So we call manage_afterAddd by hand.
535 def install_errorlog(self):
536 app = self.getApp()
537 if app._getInitializerFlag('error_log'):
538 # do nothing if we've already installed one
539 return
540 # Install an error_log
541 if not hasattr(app, 'error_log'):
542 from Products.SiteErrorLog.SiteErrorLog import SiteErrorLog
543 error_log = SiteErrorLog()
544 app._setObject('error_log', error_log)
545 # Added for Five 1.2:
546 error_log = app.error_log
547 error_log.manage_afterAdd(error_log, app)
548 # End added
549 app._setInitializerFlag('error_log')
550 self.commit('Added site error_log at /error_log')
552 def install_browser_id_manager(self):
553 app = self.getApp()
554 if app._getInitializerFlag('browser_id_manager'):
555 # do nothing if we've already installed one
556 return
557 # Ensure that a browser ID manager exists
558 if not hasattr(app, 'browser_id_manager'):
559 from Products.Sessions.BrowserIdManager import BrowserIdManager
560 bid = BrowserIdManager('browser_id_manager', 'Browser Id Manager')
561 app._setObject('browser_id_manager', bid)
562 # Added for Five 1.2:
563 browser_id_manager = app.browser_id_manager
564 browser_id_manager.manage_afterAdd(browser_id_manager, app)
565 # End added
566 app._setInitializerFlag('browser_id_manager')
567 self.commit('Added browser_id_manager')
569 def install_session_data_manager(self):
570 app = self.getApp()
571 if app._getInitializerFlag('session_data_manager'):
572 # do nothing if we've already installed one
573 return
574 # Ensure that a session data manager exists
575 if not hasattr(app, 'session_data_manager'):
576 from Products.Sessions.SessionDataManager import SessionDataManager
577 sdm = SessionDataManager('session_data_manager',
578 title='Session Data Manager',
579 path='/temp_folder/session_data',
580 requestName='SESSION')
581 app._setObject('session_data_manager', sdm)
582 # Added for Five 1.2:
583 session_data_manager = app.session_data_manager
584 session_data_manager.manage_afterAdd(session_data_manager, app)
585 # End added
586 app._setInitializerFlag('session_data_manager')
587 self.commit('Added session_data_manager')
589 ##################################################
590 # Structured monkey-patching
592 import Products.Five
593 from Products.Five import zcml
594 from Products.Five.fiveconfigure import killMonkey
595 from zope.testing.cleanup import addCleanUp
597 _monkied = []
599 from OFS.SimpleItem import Item
600 from OFS.ObjectManager import ObjectManager
601 from OFS.OrderSupport import OrderSupport
602 from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2Base
603 from OFS.Application import AppInitializer
604 from Products.ZCatalog import CatalogAwareness, CatalogPathAwareness
606 def doMonkies():
607 """Monkey patch various methods to provide container events.
608 """
609 global hasContainerEvents
610 hasContainerEvents = True
612 patchMethod(ObjectManager, '_setObject',
613 _setObject)
614 patchMethod(ObjectManager, '_delObject',
615 _delObject)
616 patchMethod(ObjectManager, 'manage_afterAdd',
617 manage_afterAdd)
618 patchMethod(ObjectManager, 'manage_beforeDelete',
619 manage_beforeDelete)
620 patchMethod(ObjectManager, 'manage_afterClone',
621 manage_afterClone)
623 patchMethod(Item, 'manage_afterAdd',
624 manage_afterAdd)
625 patchMethod(Item, 'manage_beforeDelete',
626 manage_beforeDelete)
627 patchMethod(Item, 'manage_afterClone',
628 manage_afterClone)
630 patchMethod(BTreeFolder2Base, '_setObject',
631 BT_setObject)
632 patchMethod(BTreeFolder2Base, '_delObject',
633 BT_delObject)
635 patchMethod(CopyContainer, 'manage_renameObject',
636 manage_renameObject)
637 patchMethod(CopyContainer, 'manage_pasteObjects',
638 manage_pasteObjects)
639 patchMethod(CopyContainer, 'manage_clone',
640 manage_clone)
642 patchMethod(OrderSupport, 'moveObjectsByDelta',
643 moveObjectsByDelta)
644 patchMethod(OrderSupport, 'moveObjectToPosition',
645 moveObjectToPosition)
646 patchMethod(OrderSupport, 'manage_renameObject',
647 OS_manage_renameObject)
649 patchMethod(AppInitializer, 'install_errorlog',
650 install_errorlog)
651 patchMethod(AppInitializer, 'install_browser_id_manager',
652 install_browser_id_manager)
653 patchMethod(AppInitializer, 'install_session_data_manager',
654 install_session_data_manager)
656 patchMethod(CatalogAwareness.CatalogAware, 'manage_afterAdd',
657 CA_manage_afterAdd)
658 patchMethod(CatalogAwareness.CatalogAware, 'manage_beforeDelete',
659 CA_manage_beforeDelete)
660 patchMethod(CatalogAwareness.CatalogAware, 'manage_afterClone',
661 CA_manage_afterClone)
662 patchMethod(CatalogPathAwareness.CatalogAware, 'manage_afterAdd',
663 CA_manage_afterAdd)
664 patchMethod(CatalogPathAwareness.CatalogAware, 'manage_beforeDelete',
665 CA_manage_beforeDelete)
666 patchMethod(CatalogPathAwareness.CatalogAware, 'manage_afterClone',
667 CA_manage_afterClone)
669 zcml.load_config('event.zcml', Products.Five, execute=False)
671 addCleanUp(undoMonkies)
673 def patchMethod(class_, name, new_method):
674 method = getattr(class_, name, None)
675 if isFiveMethod(method):
676 return
677 setattr(class_, FIVE_ORIGINAL_PREFIX + name, method)
678 setattr(class_, name, new_method)
679 new_method.__five_method__ = True
680 _monkied.append((class_, name))
682 def undoMonkies():
683 """Undo monkey patches.
684 """
685 global hasContainerEvents
686 for class_, name in _monkied:
687 killMonkey(class_, name, FIVE_ORIGINAL_PREFIX + name)
688 hasContainerEvents = False
689 deprecatedManageAddDeleteClasses[:] = []