Package Products :: Package ZenModel :: Module ZenModelBase |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenModel.ZenModelBase

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 as published by
      8  # the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     11  #
     12  ###########################################################################
     13
     14  __doc__="""ZenModelBase
     15
     16  $Id: ZenModelBase.py,v 1.17 2004/04/23 19:11:58 edahl Exp $"""
     17
     18  __version__ = "$Revision: 1.17 $"[11:-2]
     19
     20  import re
     21  import time
     22
     23  import sys
     24  from urllib import unquote
     25  from cgi import escape
     26  from OFS.ObjectManager import checkValidId as globalCheckValidId
     27
     28  from AccessControl import ClassSecurityInfo, getSecurityManager, Unauthorized
     29  from Globals import InitializeClass
     30  from Acquisition import aq_base, aq_chain
     31
     32  from Products.ZenUtils.Utils import zenpathsplit, zenpathjoin
     33  from Products.ZenUtils.Utils import createHierarchyObj, getHierarchyObj
     34  from Products.ZenUtils.Utils import getObjByPath
     35
     36  from Products.ZenUtils.Utils import prepId as globalPrepId
     37  from Products.ZenWidgets import messaging
     38
     39  from ZenossSecurity import *
     40
     41
     42  # Custom device properties start with c
     43  iscustprop = re.compile("^c[A-Z]").search
     44
     45 -class ZenModelBase(object):
     46      """
     47      All ZenModel Persistent classes inherit from this class.  It provides some
     48      screen management functionality, and general utility methods.
     49      """
     50      sub_meta_types = ()
     51      #prodStateThreshold = 500
     52
     53      security = ClassSecurityInfo()
     54
     55 -    def __call__(self):
     56          """
     57          Invokes the default view.
     58          """
     59          view = "view"
     60          if hasattr(self, "factory_type_information"):
     61              view = self.factory_type_information[0]['immediate_view']
     62          else:
     63              raise NameError('Cannot find default view for "%s"' %
     64                              '/'.join(self.getPhysicalPath()))
     65          return self.restrictedTraverse(view)()
     66
     67      index_html = None  # This special value informs ZPublisher to use __call__
     68
     69
     70      security.declareProtected(ZEN_VIEW, 'view')
     71 -    def view(self):
     72          '''
     73          Returns the default view even if index_html is overridden.
     74
     75          @permission: ZEN_VIEW
     76          '''
     77          return self()
     78
     79
     80 -    def __hash__(self):
     81          return hash(self.id)
     82
     83 -    def prepId(self, id, subchar='_'):
     84          """
     85          Clean out an id of illegal characters.
     86
     87          @type id: string
     88          @param subchar: Character to be substituted with illegal characters
     89          @type subchar: string
     90          @rtype: string
     91
     92          >>> dmd.Devices.prepId('ab^*cd')
     93          'ab__cd'
     94          >>> dmd.Devices.prepId('ab^*cd', subchar='Z')
     95          'abZZcd'
     96          >>> dmd.Devices.prepId('/boot')
     97          'boot'
     98          >>> dmd.Devices.prepId('/')
     99          '-'
    100          >>> dmd.Devices.prepId(' mydev ')
    101          'mydev'
    102          """
    103          return globalPrepId(id, subchar)
    104
    105 -    def checkValidId(self, id, prep_id = False):
    106          """
    107          Checks that an id is a valid Zope id.  Looks for invalid characters and
    108          checks that the id doesn't already exist in this context.
    109
    110          @type id: string
    111          @type prep_id: boolean
    112          @rtype: boolean
    113
    114          >>> dmd.Devices.checkValidId('^*')
    115          'The id "^*" contains characters illegal in URLs.'
    116          >>> dmd.Devices.checkValidId('Server')
    117          'The id "Server" is invalid - it is already in use.'
    118          >>> dmd.Devices.checkValidId('ZenTestId')
    119          True
    120          """
    121          new_id = unquote(id)
    122          if prep_id: new_id = self.prepId(id)
    123          try:
    124              globalCheckValidId(self, new_id)
    125              return True
    126          except:
    127              return str(sys.exc_info()[1])
    128
    129
    130 -    def getUnusedId(self, relName, baseKey, extensionIter=None):
    131          """
    132          Return a new id that is not already in use in the relationship.  If
    133          baseKey is not already in use, return that.  Otherwise append values
    134          from extensionIter to baseKey until an used key is found.  The default
    135          extensionIter appends integers starting with 2 and counting up.
    136
    137          @type relName: string
    138          @type baseKey: string
    139          @type extensionIter: iterator
    140          @rtype: string
    141
    142          >>> id1 = dmd.Devices.getUnusedId('devices', 'dev')
    143          >>> id1
    144          'dev'
    145          >>> dmd.Devices.createInstance(id1)
    146          <Device at /zport/dmd/Devices/devices/dev>
    147          >>> id2 = dmd.Devices.getUnusedId('devices', 'dev')
    148          >>> id2
    149          'dev2'
    150          """
    151          import itertools
    152          if extensionIter is None:
    153              extensionIter = itertools.count(2)
    154          rel = getattr(self, relName)
    155          candidate = baseKey
    156          while candidate in rel.objectIds():
    157              candidate = self.prepId('%s%s' % (baseKey, extensionIter.next()))
    158          return candidate
    159
    160
    161 -    def getIdLink(self):
    162          """
    163          DEPRECATED Return an a link to this object with its id as the name.
    164
    165          @return: An HTML link to this object
    166          @rtype: string
    167
    168          >>> dmd.Devices.getIdLink()
    169          '<a href="/zport/dmd/Devices">/</a>'
    170          """
    171          return self.urlLink()
    172
    173
    174 -    def callZenScreen(self, REQUEST, redirect=False):
    175          """
    176          Call and return screen specified by zenScreenName value of REQUEST.
    177          If zenScreenName is not present call the default screen.  This is used
    178          in functions that are called from forms to get back to the correct
    179          screen with the correct context.
    180          """
    181          if REQUEST is None or getattr(REQUEST, 'dontRender', False):
    182              # EventView uses a FakeRequest class to avoid the overhead
    183              # of rendering pages as result of ajax calls.
    184              return ''
    185          screenName = REQUEST.get("zenScreenName", "")
    186          if redirect:
    187              nurl = "%s/%s" % (self.getPrimaryUrlPath(), screenName)
    188              REQUEST['RESPONSE'].redirect(nurl)
    189          else:
    190              REQUEST['URL'] = "%s/%s" % (self.absolute_url_path(), screenName)
    191              screen = getattr(self, screenName, False)
    192              if not screen: return self()
    193              return screen()
    194
    195
    196 -    def zenScreenUrl(self):
    197          """
    198          Return the url for the current screen as defined by zenScreenName.
    199          If zenScreenName is not found in the request the request url is used.
    200
    201          @return: An url to this object
    202          @rtype: string
    203          """
    204          screenName = self.REQUEST.get("zenScreenName", "")
    205          if not screenName: return self.REQUEST.URL
    206          return self.getPrimaryUrlPath() + "/" + screenName
    207
    208
    209 -    def urlLink(self, text=None, url=None, attrs={}):
    210          """
    211          Return an anchor tag if the user has access to the remote object.
    212
    213          @param text: the text to place within the anchor tag or string.
    214                       Defaults to the id of this object.
    215          @param url: url for the href. Default is getPrimaryUrlPath
    216          @type attrs: dict
    217          @param attrs: any other attributes to be place in the in the tag.
    218          @return: An HTML link to this object
    219          @rtype: string
    220          """
    221          if not text:
    222              text = self.titleOrId()
    223          text = escape(text)
    224          if not self.checkRemotePerm("View", self):
    225              return text
    226          if not url:
    227              url = self.getPrimaryUrlPath()
    228          if len(attrs):
    229              return '<a href="%s" %s>%s</a>' % (url,
    230                  ' '.join(['%s="%s"' % (x,y) for x,y in attrs.items()]),
    231                  text)
    232          else:
    233              return '<a href="%s">%s</a>' % (url, text)
    234
    235
    236 -    def getBreadCrumbUrlPath(self):
    237          """
    238          Return the url to be used in breadcrumbs for this object.  normally
    239          this is equal to getPrimaryUrlPath. It can be used as a hook to modify
    240          the url so that it points towards a different tab then the default.
    241
    242          @return: A url to this object
    243          @rtype: string
    244
    245          >>> dmd.Devices.getBreadCrumbUrlPath()
    246          '/zport/dmd/Devices'
    247          >>> rc = dmd.Reports._getOb('Graph Reports')
    248          >>> rc.manage_addGraphReport('test').getBreadCrumbUrlPath()
    249          '/zport/dmd/Reports/Graph%20Reports/test/editGraphReport'
    250          """
    251          return self.getPrimaryUrlPath()
    252
    253
    254 -    def breadCrumbs(self, terminator='dmd'):
    255          """
    256          Return the data to create the breadcrumb links for this object.
    257
    258          This is a list of tuples where the first value is the URL of the bread
    259          crumb and the second is the lable.
    260
    261          @return: List of tuples to create a bread crumbs
    262          @rtype: list
    263
    264          >>> dmd.Devices.Server.breadCrumbs()
    265          [('/zport/dmd/Devices', 'Devices'),
    266              ('/zport/dmd/Devices/Server', 'Server')]
    267          """
    268          links = []
    269          curDir = self.primaryAq()
    270          while curDir.id != terminator:
    271              if curDir.meta_type == 'ToManyContRelationship':
    272                  curDir = curDir.getPrimaryParent()
    273                  continue
    274              if not getattr(aq_base(curDir),"getBreadCrumbUrlPath", False):
    275                  break
    276              url = ""
    277              if self.checkRemotePerm("View", curDir):
    278                  url = curDir.getBreadCrumbUrlPath()
    279              links.append((url, curDir.titleOrId()))
    280              curDir = curDir.aq_parent
    281          links.reverse()
    282          return links
    283
    284      security.declareProtected(ZEN_COMMON, 'checkRemotePerm')
    285 -    def checkRemotePerm(self, permission, robject):
    286          """
    287          Look to see if the current user has permission on remote object.
    288
    289          @param permission: Zope permission to be tested. ie "View"
    290          @param robject: remote objecct on which test is run.  Will test on
    291          primary acquisition path.
    292          @rtype: boolean
    293          @permission: ZEN_COMMON
    294          """
    295          user = getSecurityManager().getUser()
    296          return user.has_permission(permission, robject.primaryAq())
    297
    298
    299
    300      security.declareProtected(ZEN_VIEW, 'zentinelTabs')
    301 -    def zentinelTabs(self, templateName):
    302          """
    303          Return a list of hashes that define the screen tabs for this object.
    304
    305          Keys in the hash are:
    306              - action = the name of the page template for this tab
    307              - name = the label used on the tab
    308              - permissions = a tuple of permissions to view this template
    309
    310          @permission: ZEN_VIEW
    311
    312          >>> dmd.Devices.zentinelTabs('deviceOrganizerStatus')
    313          [{'action': 'deviceOrganizerStatus', 'selected': True,
    314              'name': 'Classes', 'permissions': ('View',)},
    315          {'action': 'viewEvents', 'name': 'Events', 'permissions': ('View',)},
    316          {'action': 'zPropertyEdit', 'name': 'zProperties',
    317              'permissions': ('View',)},
    318          {'action': 'perfConfig', 'name': 'Templates',
    319              'permissions': ('Manage DMD',)}]
    320          """
    321          tabs = []
    322          user = getSecurityManager().getUser()
    323          actions = self.factory_type_information[0]['actions']
    324          for a in actions:
    325              def permfilter(p): return user.has_permission(p,self)
    326              permok = filter(permfilter, a['permissions'])
    327              if not a.get('visible', True) or not permok:
    328                  continue
    329              a = a.copy()
    330              if a['action'] == templateName: a['selected'] = True
    331              tabs.append(a)
    332          return tabs
    333
    334
    335      security.declareProtected(ZEN_MANAGE_DMD, 'zmanage_editProperties')
    336 -    def zmanage_editProperties(self, REQUEST=None, redirect=False):
    337          """
    338          Edit a ZenModel object and return its proper page template.
    339          Object will be reindexed if nessesary.
    340
    341          @permission: ZEN_MANAGE_DMD
    342          """
    343          self.manage_changeProperties(**REQUEST.form)
    344          index_object = getattr(self, 'index_object', lambda self: None)
    345          index_object()
    346          if REQUEST:
    347              from Products.ZenUtils.Time import SaveMessage
    348              messaging.IMessageSender(self).sendToBrowser(
    349                  'Properties Saved',
    350                  SaveMessage()
    351              )
    352              return self.callZenScreen(REQUEST, redirect=redirect)
    353
    354
    355      security.declareProtected(ZEN_VIEW, 'getPrimaryDmdId')
    356 -    def getPrimaryDmdId(self, rootName="dmd", subrel=""):
    357          """
    358          Return the full dmd id of this object for instance /Devices/Server.
    359          Everything before dmd is removed.  A different rootName can be passed
    360          to stop at a different object in the path.  If subrel is passed any
    361          relationship name in the path to the object will be removed.
    362
    363          @param rootName: Name of root
    364          @type rootName: string
    365          @param subrel: Name of relation
    366          @type subrel: string
    367          @return: Path to object
    368          @rtype: string
    369          @permission: ZEN_VIEW
    370
    371          >>> d = dmd.Devices.Server.createInstance('test')
    372          >>> d.getPrimaryDmdId()
    373          '/Devices/Server/devices/test'
    374          >>> d.getPrimaryDmdId('Devices')
    375          '/Server/devices/test'
    376          >>> d.getPrimaryDmdId('Devices','devices')
    377          '/Server/test'
    378          """
    379          path = list(self.getPrimaryPath())
    380          path = path[path.index(rootName)+1:]
    381          if subrel: path = filter(lambda x: x != subrel, path)
    382          return '/'+'/'.join(path)
    383
    384
    385 -    def zenpathjoin(self, path):
    386          """
    387          DEPRECATED Build a Zenoss path based on a list or tuple.
    388
    389          @type path: list or tuple
    390
    391          >>> dmd.zenpathjoin(('zport', 'dmd', 'Devices', 'Server'))
    392          '/zport/dmd/Devices/Server'
    393          """
    394          return zenpathjoin(path)
    395
    396
    397 -    def zenpathsplit(self, path):
    398          """
    399          DEPRECATED Split a path on its '/'.
    400          """
    401          return zenpathsplit(path)
    402
    403
    404 -    def createHierarchyObj(self, root, name, factory, relpath="", alog=None):
    405          """
    406          DEPRECATED this is only seems to be used in Organizer.createOrganizer -
    407          Create an object from its path we use relpath to skip down any missing
    408          relations in the path and factory is the constructor for this object.
    409          """
    410          return createHierarchyObj(root, name, factory, relpath, alog)
    411
    412
    413 -    def getHierarchyObj(self, root, name, relpath):
    414          """
    415          DEPRECATED this doesn't seem to be used anywere don't use it!!!
    416          """
    417          return getHierarchyObj(root, name, relpath)
    418
    419
    420 -    def getDmd(self):
    421          """
    422          DEPRECATED Return the dmd root object with unwraped acquisition path.
    423
    424          >>> dmd.Devices.Server.getDmd()
    425          <DataRoot at /zport/dmd>
    426          """
    427          for obj in aq_chain(self):
    428              if getattr(obj, 'id', None) == 'dmd': return obj
    429
    430
    431 -    def getDmdRoot(self, name):
    432          """
    433          Return a dmd root organizer such as "Systems".  The acquisition path
    434          will be cleaned so that it points directly to the root.
    435
    436          >>> dmd.Devices.Server.getDmdRoot("Systems")
    437          <System at /zport/dmd/Systems>
    438          """
    439          dmd = self.getDmd()
    440          return dmd._getOb(name)
    441
    442
    443 -    def getDmdObj(self, path):
    444          """
    445          DEPRECATED Return an object from path that starts at dmd.
    446
    447          >>> dmd.getDmdObj('/Devices/Server')
    448          <DeviceClass at /zport/dmd/Devices/Server>
    449          """
    450          if path.startswith("/"): path = path[1:]
    451          return self.getDmd().getObjByPath(path)
    452
    453
    454 -    def getZopeObj(self, path):
    455          """
    456          DEPRECATED Return an object from path tat starts at zope root.
    457
    458          >>> dmd.getZopeObj('/zport/dmd/Devices/Server')
    459          <DeviceClass at /zport/dmd/Devices/Server>
    460          """
    461          return self.getObjByPath(path)
    462
    463
    464 -    def getNowString(self):
    465          """
    466          Return the current time as a string in the format '2007/09/27 14:09:53'.
    467
    468          @rtype: string
    469          """
    470          return time.strftime("%Y/%m/%d %H:%M:%S", time.localtime())
    471
    472
    473 -    def todayDate(self):
    474          """
    475          Return today's date as a string in the format 'mm/dd/yyyy'.
    476
    477          @rtype: string
    478          """
    479          return time.strftime("%m/%d/%Y", time.localtime())
    480
    481
    482 -    def yesterdayDate(self):
    483          """
    484          Return yesterday's date as a string in the format 'mm/dd/yyyy'.
    485
    486          @rtype: string
    487          """
    488          yesterday = time.time() - 24*3600
    489          return time.strftime("%m/%d/%Y", time.localtime(yesterday))
    490
    491
    492 -    def all_meta_types(self, interfaces=None):
    493          """
    494          DEPRECATED Override the ObjectManager method that is used to control
    495          the items available in the add drop down in the ZMI.  It uses the
    496          attribute sub_menu_items to create the data structures.  This is a list
    497          of meta_types for the available classes.  This functionality is rarely
    498          used in Zenoss because the ZMI is not the perfered management
    499          interface.
    500          """
    501          mts = super(ZenModelBase,self).all_meta_types(interfaces)
    502          if self.sub_meta_types:
    503              mts = filter(lambda mt: mt['name'] in self.sub_meta_types, mts)
    504          return mts
    505
    506
    507      security.declareProtected('Delete objects', 'manage_deleteObjects')
    508 -    def manage_deleteObjects(self, ids=(), REQUEST=None):
    509          """
    510          Delete objects by id from this object and return to the current
    511          template as defined by callZenScreen.  Uses ObjectManager._delObject to
    512          remove the object.
    513
    514          @permission: ZEN_VIEW
    515          """
    516          for id in ids:  self._delObject(id)
    517          if REQUEST:
    518              return self.callZenScreen(REQUEST)
    519
    520
    521 -    def custPropertyIds(self):
    522          """
    523          List custom properties that are defined at root node. Custom properties
    524          start with a lower "c" followed by a uppercase character.
    525          """
    526          return self.zenPropertyIds(pfilt=iscustprop)
    527
    528
    529 -    def custPropertyMap(self):
    530          """
    531          Return custom property definitions.
    532
    533          @rtype: [{'id':'cName','label':'Name', 'type':'string'},]
    534          """
    535          return self.zenPropertyMap(pfilt=iscustprop)
    536
    537
    538 -    def visibleCustPropertyMap(self):
    539          """
    540          List custom property definitions that are visible using
    541          custPropertyMap::
    542
    543          @rtype: [{'id':'cName','label':'Name', 'type':'string'},]
    544          """
    545          return [ p for p in self.zenPropertyMap(pfilt=iscustprop) \
    546                      if p.get('visible', True) ]
    547
    548
    549      security.declareProtected(ZEN_MANAGE_DMD, 'saveCustProperties')
    550 -    def saveCustProperties(self, REQUEST):
    551          """
    552          Save custom properties from REQUEST.form.
    553
    554          @permission: ZEN_MANAGE_DMD
    555          """
    556          return self.saveZenProperties(iscustprop, REQUEST)
    557
    558
    559 -    def getObjByPath(self, path):
    560          """
    561          Lookup and object by its path.  Basically does a Zope unrestricted
    562          traverse on the path given.
    563
    564          @type path: list or string /zport/dmd/Devices
    565
    566          >>> dmd.getObjByPath(('zport','dmd','Devices'))
    567          <DeviceClass at /zport/dmd/Devices>
    568          >>> dmd.getObjByPath(('Devices','Server'))
    569          <DeviceClass at /zport/dmd/Devices/Server>
    570          >>> dmd.getObjByPath('/zport/dmd/Devices/Server')
    571          <DeviceClass at /zport/dmd/Devices/Server>
    572          >>> dmd.getObjByPath('Devices/Server')
    573          <DeviceClass at /zport/dmd/Devices/Server>
    574          """
    575          return getObjByPath(self, path)
    576
    577
    578 -    def isLocalName(self, name):
    579          """
    580          Check to see if a name is local to our current context or if it comes
    581          from our acquisition chain.
    582
    583          @rtype: boolean
    584
    585          >>> dmd.isLocalName('Devices')
    586          True
    587          >>> dmd.Devices.Server.isLocalName('Devices')
    588          False
    589          """
    590          v = getattr(aq_base(self), name, '__ZENMARKER__')
    591          return v != '__ZENMARKER__'
    592
    593      security.declareProtected(ZEN_VIEW, 'helpLink')
    594 -    def helpLink(self):
    595          """
    596          DEPRECATED Return a link to the objects help file.
    597
    598          @permission: ZEN_VIEW
    599          """
    600          path = self.__class__.__module__.split('.')
    601          className = path[-1].replace('Class','')
    602          product = path[-2]
    603
    604          path = ("", "Control_Panel", "Products", product, "Help",
    605                  "%s.stx"%className)
    606
    607          # check to see if we have a help screen
    608          app = self.getPhysicalRoot()
    609          try:
    610              app.restrictedTraverse(path)
    611          except (KeyError, Unauthorized):
    612              return ""
    613
    614          url = "/HelpSys?help_url="+ "/".join(path)
    615
    616          return """<a class="tabletitle" href="%s" \
    617              onClick="window.open('%s','zope_help','width=600,height=500, \
    618              menubar=yes,toolbar=yes,scrollbars=yes,resizable=yes');  \
    619              return false;" onMouseOver="window.status='Open online help'; \
    620              return true;" onMouseOut="window.status=''; return true;">Help!</a>
    621              """ % (url, url)
    622
    623
    624      security.declareProtected(ZEN_VIEW, 'getIconPath')
    625 -    def getIconPath(self):
    626          """
    627          Return the icon associated with this object.  The icon path is defined
    628          in the zProperty zIcon.
    629
    630          @return: Path to icon
    631          @rtype: string
    632          @permission: ZEN_VIEW
    633
    634          >>> dmd.Devices.Server._setProperty('zIcon', '/zport/dmd/img/icons/server.png')
    635          >>> d = dmd.Devices.Server.createInstance('test')
    636          >>> d.getIconPath()
    637          '/zport/dmd/img/icons/server.png'
    638          """
    639          try:
    640              return self.primaryAq().zIcon
    641          except AttributeError:
    642              return '/zport/dmd/img/icons/noicon.png'
    643
    644
    645 -    def aqBaseHasAttr(self, attr):
    646          """
    647          Return hasattr(aq_base(self), attr)
    648          This is a convenience function for use in templates, where it's not
    649          so easy to make a similar call directly.
    650          hasattr itself will swallow exceptions, so we don't want to use that.
    651          We also need to allow for values of None, so something like
    652          getattr(aq_base(self, attr, None) doesn't really tell us anything.
    653          Testing __dict__ is not a good choice because it doesn't allow
    654          for properties (and I believe __getitem__ calls.)
    655          So while this looks pretty attrocious, it might be the most sane
    656          solution.
    657          """
    658          base = aq_base(self)
    659          return getattr(base, attr, True) == getattr(base, attr, False)
    660
    661
    662  InitializeClass(ZenModelBase)
    663