Package Products :: Package ZenRelations :: Module ToManyContRelationship |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenRelations.ToManyContRelationship

      1  ##############################################################################
      2  #
      3  # Copyright (C) Zenoss, Inc. 2007, all rights reserved.
      4  #
      5  # This content is made available according to terms specified in
      6  # License.zenoss under the directory where your Zenoss product is installed.
      7  #
      8  ##############################################################################
      9
     10
     11  __doc__ = """ToManyContRelationship
     12  A to-many container relationship
     13  """
     14
     15  import sys
     16  import logging
     17  log = logging.getLogger("zen.Relations")
     18
     19  from Globals import DTMLFile
     20  from Globals import InitializeClass
     21  from AccessControl import ClassSecurityInfo
     22  from Acquisition import aq_base
     23  from OFS.ObjectManager import checkValidId, BeforeDeleteException
     24  from ZODB.POSException import ConflictError
     25
     26  import OFS.subscribers
     27  from OFS.event import ObjectWillBeAddedEvent
     28  from OFS.event import ObjectWillBeRemovedEvent
     29  from zope.event import notify
     30  from zope.container.contained import ObjectAddedEvent
     31  from zope.container.contained import ObjectRemovedEvent
     32  from zope.container.contained import dispatchToSublocations
     33
     34  from BTrees.OOBTree import OOBTree
     35
     36  from ToManyRelationshipBase import ToManyRelationshipBase
     37
     38  from Products.ZenRelations.Exceptions import *
     39
     40  from Products.ZenUtils.Utils import unused
     41  from Products.ZenUtils.tbdetail import log_tb
     42
     43 -def manage_addToManyContRelationship(context, id, REQUEST=None):
     44      """factory for ToManyRelationship"""
     45      rel =  ToManyContRelationship(id)
     46      context._setObject(rel.id, rel)
     47      if REQUEST:
     48          REQUEST['RESPONSE'].redirect(context.absolute_url()+'/manage_main')
     49      return rel.id
     50
     51
     52  addToManyContRelationship = DTMLFile('dtml/addToManyContRelationship',globals())
     53
     54
     55 -class ToManyContRelationship(ToManyRelationshipBase):
     56      """
     57      ToManyContRelationship is the ToMany side of a realtionship that
     58      contains its related objects (like the normal Zope ObjectManager)
     59      """
     60
     61      meta_type = "ToManyContRelationship"
     62
     63      security = ClassSecurityInfo()
     64
     65
     66 -    def __init__(self, id):
     67          """set our instance values"""
     68          self.id = id
     69          self._objects = OOBTree()
     70
     71
     72 -    def _safeOfObjects(self):
     73          """
     74          Try to safely return ZenPack objects rather than
     75          causing imports to fail.
     76          """
     77          objs = []
     78          for ob in self._objects.values():
     79              try:
     80                  objs.append(ob.__of__(self))
     81              except AttributeError:
     82                  log.info("Ignoring unresolvable object '%s'", str(ob))
     83          return objs
     84
     85 -    def __call__(self):
     86          """when we are called return our related object in our aq context"""
     87          return self._safeOfObjects()
     88
     89
     90 -    def __getattr__(self, name):
     91          """look in the two object stores for related objects"""
     92          if '_objects' in self.__dict__:
     93              objects = self._objects
     94              if objects.has_key(name): return objects[name]
     95          raise AttributeError( "Unable to find the attribute '%s'" % name )
     96
     97
     98 -    def __hasattr__(self, name):
     99          """check to see if we have an object by an id
    100          this will fail if passed a short id and object is stored
    101          with fullid (ie: it is related not contained)
    102          use hasobject to get around this issue"""
    103          return self._objects.has_key(name)
    104
    105
    106 -    def hasobject(self, obj):
    107          "check to see if we have this object"
    108          return self._objects.get(obj.id) == obj
    109
    110
    111 -    def addRelation(self, obj):
    112          """Override base to run manage_afterAdd like ObjectManager"""
    113          if self._objects.has_key(obj.getId()):
    114              log.debug("obj %s already exists on %s", obj.getPrimaryId(),
    115                          self.getPrimaryId())
    116
    117          notify(ObjectWillBeAddedEvent(obj, self, obj.getId()))
    118          ToManyRelationshipBase.addRelation(self, obj)
    119          obj = obj.__of__(self)
    120          o = self._getOb(obj.id)
    121          notify(ObjectAddedEvent(o, self, obj.getId()))
    122
    123
    124 -    def _setObject(self,id,object,roles=None,user=None,set_owner=1):
    125          """ObjectManager interface to add contained object."""
    126          unused(user, roles, set_owner)
    127          object.__primary_parent__ = aq_base(self)
    128          self.addRelation(object)
    129          return object.getId()
    130
    131
    132 -    def manage_afterAdd(self, item, container):
    133          # Don't do recursion anymore, a subscriber does that.
    134          pass
    135      manage_afterAdd.__five_method__ = True
    136
    137 -    def manage_afterClone(self, item):
    138          # Don't do recursion anymore, a subscriber does that.
    139          pass
    140      manage_afterClone.__five_method__ = True
    141
    142 -    def manage_beforeDelete(self, item, container):
    143          # Don't do recursion anymore, a subscriber does that.
    144          pass
    145      manage_beforeDelete.__five_method__ = True
    146
    147 -    def _add(self,obj):
    148          """add an object to one side of a ToManyContRelationship.
    149          """
    150          id = obj.id
    151          if self._objects.has_key(id):
    152              raise RelationshipExistsError
    153          v=checkValidId(self, id)
    154          if v is not None: id=v
    155          self._objects[id] = aq_base(obj)
    156          obj = aq_base(obj).__of__(self)
    157          self.setCount()
    158
    159
    160 -    def _remove(self, obj=None, suppress_events=False):
    161          """remove object from our side of a relationship"""
    162          if obj: objs = [obj]
    163          else: objs = self.objectValuesAll()
    164          if not suppress_events:
    165              for robj in objs:
    166                  notify(ObjectWillBeRemovedEvent(robj, self, robj.getId()))
    167          if obj:
    168              id = obj.id
    169              if not self._objects.has_key(id):
    170                  raise ObjectNotFound(
    171                      "object %s not found on %s" % (
    172                      obj.getPrimaryId(), self.getPrimaryId()))
    173              del self._objects[id]
    174          else:
    175              self._objects = OOBTree()
    176              self.__primary_parent__._p_changed = True
    177          if not suppress_events:
    178              for robj in objs:
    179                  notify(ObjectRemovedEvent(robj, self, robj.getId()))
    180          self.setCount()
    181
    182
    183 -    def _remoteRemove(self, obj=None):
    184          """remove an object from the far side of this relationship
    185          if no object is passed in remove all objects"""
    186          if obj:
    187              if not self._objects.has_key(obj.id):
    188                  raise ObjectNotFound("object %s not found on %s" % (
    189                      obj.getPrimaryId(), self.getPrimaryId()))
    190              objs = [obj]
    191          else: objs = self.objectValuesAll()
    192          remoteName = self.remoteName()
    193          for obj in objs:
    194              rel = getattr(obj, remoteName)
    195              try:
    196                  rel._remove(self.__primary_parent__)
    197              except ObjectNotFound:
    198                  message = log_tb(sys.exc_info())
    199                  log.error('Remote remove failed. Run "zenchkrels -r -x1". ' + message)
    200                  continue
    201
    202
    203 -    def _getOb(self, id, default=zenmarker):
    204          """look up in our local store and wrap in our aq_chain"""
    205          if self._objects.has_key(id):
    206              return self._objects[id].__of__(self)
    207          elif default == zenmarker:
    208              raise AttributeError( "Unable to find %s" % id )
    209          return default
    210
    211
    212      security.declareProtected('View', 'objectIds')
    213 -    def objectIds(self, spec=None):
    214          """only return contained objects"""
    215          if spec:
    216              if isinstance(spec,basestring): spec=[spec]
    217              return [obj.id for obj in self._objects.values() \
    218                          if obj.meta_type in spec]
    219          return [ k for k in self._objects.keys() ]
    220      objectIdsAll = objectIds
    221
    222
    223      security.declareProtected('View', 'objectValues')
    224 -    def objectValues(self, spec=None):
    225          """override to only return owned objects for many to many rel"""
    226          if spec:
    227              if isinstance(spec,basestring): spec=[spec]
    228              return [ob.__of__(self) for ob in self._objects.values() \
    229                          if ob.meta_type in spec]
    230          return self._safeOfObjects()
    231      security.declareProtected('View', 'objectValuesAll')
    232      objectValuesAll = objectValues
    233
    234
    235 -    def objectValuesGen(self):
    236          """Generator that returns all related objects."""
    237          for obj in self._objects.values():
    238              yield obj.__of__(self)
    239
    240
    241 -    def objectItems(self, spec=None):
    242          """over ride to only return owned objects for many to many rel"""
    243          if spec:
    244              if isinstance(spec,basestring): spec=[spec]
    245              return [(key,value.__of__(self)) \
    246                  for (key,value) in self._objects.items() \
    247                      if value.meta_type in spec]
    248          return [(key,value.__of__(self)) \
    249                      for (key,value) in self._objects.items()]
    250      objectItemsAll = objectItems
    251
    252
    253  #FIXME - need to make this work
    254  #    def all_meta_types(self, interfaces=None):
    255  #        mts = []
    256  #        for mt in ToManyRelationshipBase.all_meta_types(self, interfaces):
    257  #            if (mt.has_key('instance') and mt['instance']):
    258  #                for cl in self.sub_classes:
    259  #                    if checkClass(mt['instance'], cl):
    260  #                        mts.append(mt)
    261  #        return mts
    262
    263
    264 -    def _getCopy(self, container):
    265          """
    266          make new relation add copies of contained objs
    267          and refs if the relation is a many to many
    268          """
    269          rel = self.__class__(self.id)
    270          rel.__primary_parent__ = container
    271          rel = rel.__of__(container)
    272          norelcopy = getattr(self, 'zNoRelationshipCopy', [])
    273          if self.id in norelcopy: return rel
    274          for oobj in self.objectValuesAll():
    275              cobj = oobj._getCopy(rel)
    276              rel._setObject(cobj.id, cobj)
    277          return rel
    278
    279 -    def checkValidId(self, id):
    280          """
    281          Is this a valid id for this container?
    282          """
    283          try:
    284              checkValidId(self, id)
    285          except:
    286              raise
    287          else:
    288              return True
    289
    290 -    def exportXml(self, ofile, ignorerels=[]):
    291          """Return an xml representation of a ToManyContRelationship
    292          <tomanycont id='interfaces'>
    293              <object id='hme0'
    294                  module='Products.Confmon.IpInterface' class='IpInterface'>
    295                  <property></property> etc....
    296              </object>
    297          </tomanycont>
    298          """
    299          if self.countObjects() == 0: return
    300          ofile.write("<tomanycont id='%s'>\n" % self.id)
    301          for obj in self.objectValues():
    302              obj.exportXml(ofile, ignorerels)
    303          ofile.write("</tomanycont>\n")
    304
    305
    306 -    def checkRelation(self, repair=False):
    307          """Check to make sure that relationship bidirectionality is ok.
    308          """
    309          if len(self._objects):
    310              log.debug("checking relation: %s", self.id)
    311          else:
    312              return
    313
    314          # look for objects that don't point back to us
    315          # or who should no longer exist in the database
    316          remoteName = self.remoteName()
    317          parentObject = self.getPrimaryParent()
    318          for obj in self._objects.values():
    319              rrel = getattr(obj, remoteName)
    320              if not rrel.hasobject(parentObject):
    321                  log.error("remote relation %s doesn't point back to %s",
    322                                  rrel.getPrimaryId(), self.getPrimaryId())
    323                  if repair:
    324                      log.warn("reconnecting relation %s to relation %s",
    325                              rrel.getPrimaryId(),self.getPrimaryId())
    326                      rrel._add(parentObject)
    327
    328
    329
    330  InitializeClass(ToManyContRelationship)
    331
    332
    333 -class ToManyContSublocations(object):
    334      """
    335      Adapter so the event dispatching can propagate to children.
    336      """
    337 -    def __init__(self, container):
    338          self.container = container
    339 -    def sublocations(self):
    340          return (ob for ob in self.container.objectValuesAll())
    341