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

# Source Code for Module Products.ZenRelations.RelationshipManager

      1
      2  ##############################################################################
      3  #
      4  # Copyright (C) Zenoss, Inc. 2007, all rights reserved.
      5  #
      6  # This content is made available according to terms specified in
      7  # License.zenoss under the directory where your Zenoss product is installed.
      8  #
      9  ##############################################################################
     10
     11
     12  __doc__ = """RelationshipManager
     13
     14  RelationshipManager is a mix in class to manage relationships
     15  defined by the SchemaManager.
     16  """
     17
     18  from xml.sax import saxutils
     19
     20  import logging
     21  log = logging.getLogger("zen.Relations")
     22
     23  # Base classes for RelationshipManager
     24  from PrimaryPathObjectManager import PrimaryPathObjectManager
     25  from ZenPropertyManager import ZenPropertyManager
     26
     27  from Globals import DTMLFile
     28  from Globals import InitializeClass
     29  from AccessControl import ClassSecurityInfo
     30  from Acquisition import aq_base
     31  from App.Management import Tabs
     32  import OFS.subscribers
     33  import zope.interface
     34  import zope.component
     35
     36  from OFS.interfaces import IItem
     37
     38  from RelSchema import *
     39  from Exceptions import *
     40
     41  from Products.ZenUtils.Utils import unused
     42  from Products.ZenModel.interfaces import IZenDocProvider
     43
     44  zenmarker = "__ZENMARKER__"
     45
     46 -def manage_addRelationshipManager(context, id, title=None, REQUEST = None):
     47      """Relationship factory"""
     48      rm =  RelationshipManager(id)
     49      context._setObject(id, rm)
     50      if REQUEST:
     51          REQUEST['RESPONSE'].redirect(context.absolute_url()+'/manage_main')
     52
     53
     54  addRelationshipManager = DTMLFile('dtml/addRelationshipManager',globals())
     55
     56
     57 -class RelationshipManager(PrimaryPathObjectManager, ZenPropertyManager):
     58      """
     59      RelationshipManger is an ObjectManager like class that can contain
     60      relationships (in fact relationships can only be added to a
     61      RelationshipManager).
     62
     63      Relationships are defined on an RM by the hash _relations.  It
     64      should be defined on the class so that it isn't stored in the database.
     65      If there is inheritance involved remember to add the base class _relations
     66      definition to the current class so that all relationships for the class
     67      are defined on it.
     68
     69      remoteClassStr - is a string that represents the full path to the remote
     70                      class.  Its a string because in most cases the classes
     71                      will be in different modules which would cause a recursive
     72                      import of the two modules.
     73
     74      _relations = (
     75          ("toonename", ToOne(ToMany, remoteClassStr, remoteName)),
     76          ("tomanyname", ToMany(ToMany, remoteClassStr, remoteName)),
     77          )
     78      """
     79      zope.interface.implements(IItem)
     80
     81      _relations = ()
     82
     83      meta_type = 'Relationship Manager'
     84
     85      security = ClassSecurityInfo()
     86
     87      manage_options = (
     88          PrimaryPathObjectManager.manage_options +
     89          ZenPropertyManager.manage_options
     90          )
     91
     92      manage_main=DTMLFile('dtml/RelationshipManagerMain', globals())
     93
     94      # are we being deleted or moved
     95      _operation = -1
     96
     97 -    def __init__(self, id, title=None, buildRelations=True):
     98          unused(title)
     99          self.id = id
    100          if buildRelations: self.buildRelations()
    101
    102
    103 -    def getRelationshipManagerId(self):
    104          """
    105          Return our simple id if we are called from our primary path
    106          else return the full primary id.
    107          """
    108          if self.getPhysicalPath() == self.getPrimaryPath(): return self.id
    109          return self.getPrimaryId()
    110
    111
    112      ##########################################################################
    113      #
    114      # Methods for relationship management.
    115      #
    116      ##########################################################################
    117
    118
    119 -    def addRelation(self, name, obj):
    120          """Form a bi-directional relationship."""
    121          rel = getattr(self, name, None)
    122          if rel == None:
    123              raise AttributeError("Relationship %s, not found" % name)
    124          rel.addRelation(obj)
    125
    126
    127 -    def removeRelation(self, name, obj = None, suppress_events=False):
    128          """
    129          Remove an object from a relationship.
    130          If no object is passed all objects are removed.
    131          """
    132          rel = getattr(self, name, None)
    133          if rel == None:
    134              raise AttributeError("Relationship %s, not found" % name)
    135          rel.removeRelation(obj, suppress_events=suppress_events)
    136
    137
    138 -    def _setObject(self,id,object,roles=None,user=None,set_owner=1):
    139          if object.meta_type in RELMETATYPES:
    140              schema = self.lookupSchema(id)
    141              if not schema.checkType(object):
    142                  raise ZenSchemaError("Relaitonship %s type %s != %s" %
    143                              (id, object.meta_type, schema.__class__.__name__))
    144          return PrimaryPathObjectManager._setObject(self, id, object, roles,
    145                                              user, set_owner)
    146
    147
    148      ##########################################################################
    149      #
    150      # Methods for copy management
    151      #
    152      ##########################################################################
    153
    154 -    def _getCopy(self, container):
    155          """
    156          Create a copy of this relationship manager.  This involes copying
    157          relationships and removing invalid relations (ie ones with ToOne)
    158          and performing copies of any contained objects.
    159          Properties are also set on the new object.
    160          """
    161          id = self.id
    162          if getattr(aq_base(container), id, zenmarker) is not zenmarker:
    163              id = "copy_of_" + id
    164          cobj = self.__class__(id, buildRelations=False) #make new instance
    165          cobj = cobj.__of__(container) #give the copy container's aq chain
    166          for objid, sobj in self.objectItems():
    167              #if getattr(aq_base(self), objid, None): continue
    168              csobj = sobj._getCopy(cobj)
    169              cobj._setObject(csobj.id, csobj)
    170          for name, value in self.propertyItems():
    171              cobj._updateProperty(name, value)
    172          return aq_base(cobj)
    173
    174
    175 -    def _notifyOfCopyTo(self, container, op=0):
    176          """Manage copy/move/rename state for use in manage_beforeDelete."""
    177          unused(container)
    178          self._operation = op # 0 == copy, 1 == move, 2 == rename
    179
    180
    181 -    def cb_isMoveable(self):
    182          """Prevent move unless we are being called from our primary path."""
    183          if (self.getPhysicalPath() == self.getPrimaryPath()):
    184              return PrimaryPathObjectManager.cb_isMoveable(self)
    185          return 0
    186
    187
    188 -    def moveMeBetweenRels(self, srcRelationship, destRelationship):
    189          """
    190          Move a relationship manager without deleting its relationships.
    191          """
    192          self._operation = 1
    193          srcRelationship._delObject(self.id)
    194          self = aq_base(self)
    195          destRelationship._setObject(self.id, self)
    196          return destRelationship._getOb(self.id)
    197
    198
    199
    200 -    def moveObject(self, obj, destination):
    201          """
    202          Move obj from this RM to the destination RM
    203          """
    204          self._operation = 1
    205          self._delObject(obj.id)
    206          obj = aq_base(obj)
    207          destination._setObject(obj.id, obj)
    208          return destination._getOb(obj.id)
    209
    210
    211
    212      ##########################################################################
    213      #
    214      # Functions for examining a RelationshipManager's schema
    215      #
    216      ##########################################################################
    217
    218
    219 -    def buildRelations(self):
    220          """build our relations based on the schema defined in _relations"""
    221          if not getattr(self, "_relations", False): return
    222          relnames = self.getRelationshipNames()
    223          for name, schema in self._relations:
    224              if name not in relnames:
    225                  self._setObject(name, schema.createRelation(name))
    226              if name in relnames: relnames.remove(name)
    227          for rname in relnames:
    228              self._delObject(rname)
    229
    230
    231 -    def lookupSchema(cls, relname):
    232          """
    233          Lookup the schema definition for a relationship.
    234          All base classes are checked until RelationshipManager is found.
    235          """
    236          for name, schema in cls._relations:
    237              if name == relname: return schema
    238          raise ZenSchemaError("Schema for relation %s not found on %s" %
    239                                  (relname, cls.__name__))
    240      lookupSchema = classmethod(lookupSchema)
    241
    242
    243 -    def getRelationships(self):
    244          """Returns a dictionary of relationship objects keyed by their names"""
    245          return self.objectValues(spec=RELMETATYPES)
    246
    247
    248 -    def getRelationshipNames(self):
    249          """Return our relationship names"""
    250          return self.objectIds(spec=RELMETATYPES)
    251
    252
    253 -    def checkRelations(self, repair=False):
    254          """Confirm the integrity of all relations on this object"""
    255          log.debug("checking relations on object %s", self.getPrimaryId())
    256          for rel in self.getRelationships():
    257              rel.checkRelation(repair)
    258
    259
    260      ##########################################################################
    261      #
    262      # Functions for exporting RelationshipManager to XML
    263      #
    264      ##########################################################################
    265
    266 -    def exportXml(self, ofile, ignorerels=[], root=False, exportPasswords=False):
    267          """Return an xml based representation of a RelationshipManager
    268          <object id='/Devices/Servers/Windows/dhcp160.confmon.loc'
    269              module='Products.Confmon.IpInterface' class='IpInterface'>
    270              <property id='name'>jim</property>
    271              <toone></toone>
    272              <tomany></tomany>
    273              <tomanycont></tomanycont>
    274          </object>
    275          """
    276          modname = self.__class__.__module__
    277          classname = self.__class__.__name__
    278          id = root and self.getPrimaryId() or self.id
    279          stag = "<object id='%s' module='%s' class='%s'>\n" % (
    280                      id , modname, classname)
    281          ofile.write(stag)
    282          zendocAdapter = zope.component.queryAdapter( self, IZenDocProvider )
    283          if zendocAdapter is not None:
    284              zendocAdapter.exportZendoc( ofile )
    285          self.exportXmlProperties(ofile, exportPasswords)
    286          self.exportXmlRelationships(ofile, ignorerels)
    287          exportHook = getattr(aq_base(self), 'exportXmlHook', None)
    288          if exportHook and callable(exportHook):
    289              self.exportXmlHook(ofile, ignorerels)
    290          ofile.write("</object>\n")
    291
    292
    293 -    def exportXmlProperties(self,ofile, exportPasswords=False):
    294          """Return an xml representation of a RelationshipManagers properties
    295          <property id='name' type='type' mode='w' select_variable='selectvar'>
    296              value
    297          </property>
    298          value will be converted to is correct python type on import
    299          """
    300          for prop in self._properties:
    301              if not 'id' in prop: continue
    302              id = prop['id']
    303              ptype = prop['type']
    304              value = getattr(aq_base(self), id, None) # use aq_base?
    305              if not value:
    306                  if ptype in ("string","text","password"):
    307                      if not id.startswith('z'):
    308                          continue
    309                  elif ptype == "lines":
    310                      if value is None:
    311                          continue
    312                  elif ptype not in ("int","float","boolean","long"):
    313                      continue
    314              if ptype == "password" and not exportPasswords:
    315                  value = ''
    316              stag = []
    317              stag.append('<property')
    318              for k, v in prop.items():
    319                  if ptype != 'selection' and k == 'select_variable': continue
    320                  v = saxutils.quoteattr(str(v))
    321                  stag.append('%s=%s' % (k, v))
    322              stag.append('>')
    323              ofile.write(' '.join(stag)+"\n")
    324              if not isinstance(value, basestring):
    325                  value = unicode(value)
    326              elif isinstance(value, str):
    327                  value = value.decode('latin-1')
    328              valuestr = saxutils.escape(value).encode('utf-8').strip()
    329              if valuestr:
    330                  ofile.write(valuestr+"\n")
    331              ofile.write("</property>\n")
    332
    333
    334 -    def exportXmlRelationships(self, ofile, ignorerels=[]):
    335          """Return an xml representation of Relationships"""
    336          for rel in self.getRelationships():
    337              if rel.id in ignorerels: continue
    338              rel.exportXml(ofile, ignorerels)
    339
    340
    341      ##########################################################################
    342      #
    343      # Methods called from UI code.
    344      #
    345      ##########################################################################
    346
    347      security.declareProtected('Manage Relations', 'manage_addRelation')
    348 -    def manage_addRelation(self, name, obj, REQUEST=None):
    349          """make a relationship"""
    350          self.addRelation(name, obj)
    351          if REQUEST: return self.callZenScreen(REQUEST)
    352
    353
    354      security.declareProtected('Manage Relations', 'manage_removeRelation')
    355 -    def manage_removeRelation(self, name, id=None, REQUEST=None):
    356          """remove a relationship to be called from UI"""
    357          rel = getattr(self, name, None)
    358          if rel == None:
    359              raise AttributeError("Relationship %s, not found" % name)
    360          rel._delObject(id)
    361          if REQUEST: return self.callZenScreen(REQUEST)
    362
    363
    364 -    def manage_workspace(self, REQUEST):
    365          """return the workspace of the related object using its primary path"""
    366          url = REQUEST['URL']
    367          myp = self.getPrimaryUrlPath()
    368          if url.find(myp) > 0:
    369              Tabs.manage_workspace(self, REQUEST)
    370          else:
    371              from zExceptions import Redirect
    372              raise Redirect( myp+'/manage_workspace' )
    373
    374
    375
    376  InitializeClass(RelationshipManager)
    377