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 or (at your 
  8  # option) any later version as published by 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  import sys 
 23   
 24  from xml.sax import saxutils 
 25  from urllib import unquote 
 26  from cgi import escape 
 27  import zope.component 
 28  import zope.interface 
 29   
 30  from OFS.ObjectManager import checkValidId as globalCheckValidId 
 31   
 32  from AccessControl import ClassSecurityInfo, getSecurityManager, Unauthorized 
 33  from Globals import InitializeClass 
 34  from Acquisition import aq_base, aq_chain 
 35   
 36  from Products.ZenModel.interfaces import IZenDocProvider 
 37  from Products.ZenUtils.Utils import zenpathsplit, zenpathjoin 
 38  from Products.ZenUtils.Utils import createHierarchyObj, getHierarchyObj 
 39  from Products.ZenUtils.Utils import getObjByPath 
 40   
 41  from Products.ZenUtils.Utils import prepId as globalPrepId, isXmlRpc 
 42  from Products.ZenWidgets import messaging 
 43  from Products.ZenUI3.browser.interfaces import INewPath 
 44   
 45  from ZenossSecurity import * 
 46   
 47  _MARKER = object() 
 48   
 49  # Custom device properties start with c 
 50  iscustprop = re.compile("^c[A-Z]").search 
 51   
52 -class ZenModelBase(object):
53 """ 54 All ZenModel Persistent classes inherit from this class. It provides some 55 screen management functionality, and general utility methods. 56 """ 57 _zendoc = '' 58 59 sub_meta_types = () 60 #prodStateThreshold = 500 61 62 security = ClassSecurityInfo() 63
64 - def __call__(self):
65 """ 66 Invokes the default view. 67 """ 68 if isXmlRpc(self.REQUEST): 69 return self 70 else: 71 newpath = INewPath(self) 72 self.REQUEST.response.redirect(newpath)
73 74 index_html = None # This special value informs ZPublisher to use __call__ 75 76 77 security.declareProtected(ZEN_VIEW, 'view')
78 - def view(self):
79 ''' 80 Returns the default view even if index_html is overridden. 81 82 @permission: ZEN_VIEW 83 ''' 84 return self()
85 86
87 - def __hash__(self):
88 return hash(self.id)
89
90 - def prepId(self, id, subchar='_'):
91 """ 92 Clean out an id of illegal characters. 93 94 @type id: string 95 @param subchar: Character to be substituted with illegal characters 96 @type subchar: string 97 @rtype: string 98 99 >>> dmd.Devices.prepId('ab^*cd') 100 'ab__cd' 101 >>> dmd.Devices.prepId('ab^*cd', subchar='Z') 102 'abZZcd' 103 >>> dmd.Devices.prepId('/boot') 104 'boot' 105 >>> dmd.Devices.prepId('/') 106 '-' 107 >>> dmd.Devices.prepId(' mydev ') 108 'mydev' 109 """ 110 return globalPrepId(id, subchar)
111
112 - def checkValidId(self, id, prep_id = False):
113 """ 114 Checks that an id is a valid Zope id. Looks for invalid characters and 115 checks that the id doesn't already exist in this context. 116 117 @type id: string 118 @type prep_id: boolean 119 @rtype: boolean 120 121 >>> dmd.Devices.checkValidId('^*') 122 'The id "^*" contains characters illegal in URLs.' 123 >>> dmd.Devices.checkValidId('Server') 124 'The id "Server" is invalid - it is already in use.' 125 >>> dmd.Devices.checkValidId('ZenTestId') 126 True 127 """ 128 new_id = unquote(id) 129 if prep_id: new_id = self.prepId(id) 130 try: 131 globalCheckValidId(self, new_id) 132 return True 133 except: 134 return str(sys.exc_info()[1])
135 136
137 - def getUnusedId(self, relName, baseKey, extensionIter=None):
138 """ 139 Return a new id that is not already in use in the relationship. If 140 baseKey is not already in use, return that. Otherwise append values 141 from extensionIter to baseKey until an used key is found. The default 142 extensionIter appends integers starting with 2 and counting up. 143 144 @type relName: string 145 @type baseKey: string 146 @type extensionIter: iterator 147 @rtype: string 148 149 >>> id1 = dmd.Devices.getUnusedId('devices', 'dev') 150 >>> id1 151 'dev' 152 >>> dmd.Devices.createInstance(id1) 153 <Device at /zport/dmd/Devices/devices/dev> 154 >>> id2 = dmd.Devices.getUnusedId('devices', 'dev') 155 >>> id2 156 'dev2' 157 """ 158 import itertools 159 if extensionIter is None: 160 extensionIter = itertools.count(2) 161 rel = getattr(self, relName) 162 candidate = baseKey 163 while candidate in rel.objectIds(): 164 candidate = self.prepId('%s%s' % (baseKey, extensionIter.next())) 165 return candidate
166 167 179 180
181 - def callZenScreen(self, REQUEST, redirect=False):
182 """ 183 Call and return screen specified by zenScreenName value of REQUEST. 184 If zenScreenName is not present call the default screen. This is used 185 in functions that are called from forms to get back to the correct 186 screen with the correct context. 187 """ 188 if REQUEST is None or getattr(REQUEST, 'dontRender', False): 189 # EventView uses a FakeRequest class to avoid the overhead 190 # of rendering pages as result of ajax calls. 191 return '' 192 screenName = REQUEST.get("zenScreenName", "") 193 if not redirect and REQUEST.get("redirect", None) : 194 redirect = True 195 if redirect: 196 nurl = "%s/%s" % (self.getPrimaryUrlPath(), screenName) 197 REQUEST['RESPONSE'].redirect(nurl) 198 else: 199 REQUEST['URL'] = "%s/%s" % (self.absolute_url_path(), screenName) 200 screen = getattr(self, screenName, False) 201 if not screen: return self() 202 return screen()
203 204
205 - def zenScreenUrl(self):
206 """ 207 Return the url for the current screen as defined by zenScreenName. 208 If zenScreenName is not found in the request the request url is used. 209 210 @return: An url to this object 211 @rtype: string 212 """ 213 screenName = self.REQUEST.get("zenScreenName", "") 214 if not screenName: return self.REQUEST.URL 215 return self.getPrimaryUrlPath() + "/" + screenName
216 217 243 244
245 - def getBreadCrumbUrlPath(self):
246 """ 247 Return the url to be used in breadcrumbs for this object. normally 248 this is equal to getPrimaryUrlPath. It can be used as a hook to modify 249 the url so that it points towards a different tab then the default. 250 251 @return: A url to this object 252 @rtype: string 253 254 >>> dmd.Devices.getBreadCrumbUrlPath() 255 '/zport/dmd/Devices' 256 >>> rc = dmd.Reports._getOb('Graph Reports') 257 >>> rc.manage_addGraphReport('test').getBreadCrumbUrlPath() 258 '/zport/dmd/Reports/Graph%20Reports/test/editGraphReport' 259 """ 260 return self.getPrimaryUrlPath()
261 262
263 - def getBreadCrumbName(self):
264 return self.title_or_id()
265 266
267 - def breadCrumbs(self, terminator='dmd', terminate=lambda x: False):
268 """ 269 Return the data to create the breadcrumb links for this object. 270 271 This is a list of tuples where the first value is the URL of the bread 272 crumb and the second is the lable. 273 274 @return: List of tuples to create a bread crumbs 275 @rtype: list 276 277 >>> dmd.Devices.Server.breadCrumbs() 278 [('/zport/dmd/Devices', 'Devices'), 279 ('/zport/dmd/Devices/Server', 'Server')] 280 """ 281 links = [] 282 curDir = self.primaryAq() 283 while curDir.id != terminator and not terminate(curDir): 284 if curDir.meta_type == 'ToManyContRelationship': 285 curDir = curDir.getPrimaryParent() 286 continue 287 if not getattr(aq_base(curDir),"getBreadCrumbUrlPath", False): 288 break 289 url = "" 290 if self.checkRemotePerm("View", curDir): 291 url = curDir.getBreadCrumbUrlPath() 292 links.append((url, curDir.getBreadCrumbName())) 293 curDir = curDir.aq_parent 294 links.reverse() 295 return links
296 297
298 - def upToOrganizerBreadCrumbs(self, terminator='dmd'):
299 300 def isOrganizer(curDir): 301 from Products.ZenModel.Organizer import Organizer 302 try: 303 return isinstance(curDir, Organizer) 304 except: 305 return False
306 307 return ZenModelBase.breadCrumbs(self, terminator, isOrganizer)
308 309 310 security.declareProtected(ZEN_COMMON, 'checkRemotePerm')
311 - def checkRemotePerm(self, permission, robject):
312 """ 313 Look to see if the current user has permission on remote object. 314 315 @param permission: Zope permission to be tested. ie "View" 316 @param robject: remote objecct on which test is run. Will test on 317 primary acquisition path. 318 @rtype: boolean 319 @permission: ZEN_COMMON 320 """ 321 user = getSecurityManager().getUser() 322 return user.has_permission(permission, robject.primaryAq())
323 324 325 326 security.declareProtected(ZEN_VIEW, 'zentinelTabs')
327 - def zentinelTabs(self, templateName, REQUEST=None):
328 """ 329 Return a list of hashes that define the screen tabs for this object. 330 331 Keys in the hash are: 332 - action = the name of the page template for this tab 333 - name = the label used on the tab 334 - permissions = a tuple of permissions to view this template 335 336 @permission: ZEN_VIEW 337 338 >>> dmd.Devices.zentinelTabs('deviceOrganizerStatus') 339 [{'action': 'deviceOrganizerStatus', 'selected': True, 340 'name': 'Classes', 'permissions': ('View',)}, 341 {'action': 'viewEvents', 'name': 'Events', 'permissions': ('View',)}, 342 {'action': 'zPropertyEdit', 'name': 'Configuration Properties', 343 'permissions': ('View',)}, 344 {'action': 'perfConfig', 'name': 'Templates', 345 'permissions': ('Manage DMD',)}] 346 """ 347 tabs = [] 348 user = getSecurityManager().getUser() 349 actions = self.factory_type_information[0]['actions'] 350 selectedTabName = self._selectedTabName(templateName, REQUEST) 351 for a in actions: 352 def permfilter(p): return user.has_permission(p,self) 353 permok = filter(permfilter, a['permissions']) 354 if not a.get('visible', True) or not permok: 355 continue 356 a = a.copy() 357 if a['action'] == selectedTabName: a['selected'] = True 358 tabs.append(a) 359 return tabs
360
361 - def _selectedTabName(self, templateName, REQUEST=None):
362 if REQUEST and REQUEST.get('selectedTabName', '') : 363 selectedTabName = REQUEST.get('selectedTabName', '') 364 else: 365 selectedTabName = templateName 366 requestUrl = REQUEST['URL'] if REQUEST else None 367 if not selectedTabName and requestUrl and requestUrl.rfind('/') != -1: 368 selectedTabName = requestUrl[requestUrl.rfind('/') + 1:] 369 if selectedTabName.startswith('@@'): 370 selectedTabName = selectedTabName[2:] 371 return selectedTabName
372 373 374 security.declareProtected(ZEN_MANAGE_DMD, 'zmanage_editProperties')
375 - def zmanage_editProperties(self, REQUEST=None, redirect=False):
376 """ 377 Edit a ZenModel object and return its proper page template. 378 Object will be reindexed if nessesary. 379 380 @permission: ZEN_MANAGE_DMD 381 """ 382 self.manage_changeProperties(**REQUEST.form) 383 index_object = getattr(self, 'index_object', lambda self: None) 384 index_object() 385 if REQUEST: 386 from Products.ZenUtils.Time import SaveMessage 387 messaging.IMessageSender(self).sendToBrowser( 388 'Properties Saved', 389 SaveMessage() 390 ) 391 return self.callZenScreen(REQUEST, redirect=redirect)
392 393 394 security.declareProtected(ZEN_VIEW, 'getPrimaryDmdId')
395 - def getPrimaryDmdId(self, rootName="dmd", subrel=""):
396 """ 397 Return the full dmd id of this object for instance /Devices/Server. 398 Everything before dmd is removed. A different rootName can be passed 399 to stop at a different object in the path. If subrel is passed any 400 relationship name in the path to the object will be removed. 401 402 @param rootName: Name of root 403 @type rootName: string 404 @param subrel: Name of relation 405 @type subrel: string 406 @return: Path to object 407 @rtype: string 408 @permission: ZEN_VIEW 409 410 >>> d = dmd.Devices.Server.createInstance('test') 411 >>> d.getPrimaryDmdId() 412 '/Devices/Server/devices/test' 413 >>> d.getPrimaryDmdId('Devices') 414 '/Server/devices/test' 415 >>> d.getPrimaryDmdId('Devices','devices') 416 '/Server/test' 417 """ 418 path = list(self.getPrimaryPath()) 419 path = path[path.index(rootName)+1:] 420 if subrel: path = filter(lambda x: x != subrel, path) 421 return '/'+'/'.join(path)
422 423
424 - def zenpathjoin(self, path):
425 """ 426 DEPRECATED Build a Zenoss path based on a list or tuple. 427 428 @type path: list or tuple 429 430 >>> dmd.zenpathjoin(('zport', 'dmd', 'Devices', 'Server')) 431 '/zport/dmd/Devices/Server' 432 """ 433 return zenpathjoin(path)
434 435
436 - def zenpathsplit(self, path):
437 """ 438 DEPRECATED Split a path on its '/'. 439 """ 440 return zenpathsplit(path)
441 442
443 - def createHierarchyObj(self, root, name, factory, relpath="", alog=None):
444 """ 445 DEPRECATED this is only seems to be used in Organizer.createOrganizer - 446 Create an object from its path we use relpath to skip down any missing 447 relations in the path and factory is the constructor for this object. 448 """ 449 return createHierarchyObj(root, name, factory, relpath, alog)
450 451
452 - def getHierarchyObj(self, root, name, relpath):
453 """ 454 DEPRECATED this doesn't seem to be used anywere don't use it!!! 455 """ 456 return getHierarchyObj(root, name, relpath)
457 458
459 - def getDmd(self):
460 """ 461 DEPRECATED Return the dmd root object with unwraped acquisition path. 462 463 >>> dmd.Devices.Server.getDmd() 464 <DataRoot at /zport/dmd> 465 """ 466 for obj in aq_chain(self): 467 if getattr(obj, 'id', None) == 'dmd': return obj
468 469
470 - def getDmdRoot(self, name):
471 """ 472 Return a dmd root organizer such as "Systems". The acquisition path 473 will be cleaned so that it points directly to the root. 474 475 >>> dmd.Devices.Server.getDmdRoot("Systems") 476 <System at /zport/dmd/Systems> 477 """ 478 dmd = self.getDmd() 479 return dmd._getOb(name)
480 481
482 - def getDmdObj(self, path):
483 """ 484 DEPRECATED Return an object from path that starts at dmd. 485 486 >>> dmd.getDmdObj('/Devices/Server') 487 <DeviceClass at /zport/dmd/Devices/Server> 488 """ 489 if path.startswith("/"): path = path[1:] 490 return self.getDmd().getObjByPath(path)
491 492
493 - def getZopeObj(self, path):
494 """ 495 DEPRECATED Return an object from path tat starts at zope root. 496 497 >>> dmd.getZopeObj('/zport/dmd/Devices/Server') 498 <DeviceClass at /zport/dmd/Devices/Server> 499 """ 500 return self.getObjByPath(path)
501 502
503 - def getNowString(self):
504 """ 505 Return the current time as a string in the format '2007/09/27 14:09:53'. 506 507 @rtype: string 508 """ 509 return time.strftime("%Y/%m/%d %H:%M:%S", time.localtime())
510 511
512 - def todayDate(self):
513 """ 514 Return today's date as a string in the format 'mm/dd/yyyy'. 515 516 @rtype: string 517 """ 518 return time.strftime("%m/%d/%Y", time.localtime())
519 520
521 - def yesterdayDate(self):
522 """ 523 Return yesterday's date as a string in the format 'mm/dd/yyyy'. 524 525 @rtype: string 526 """ 527 yesterday = time.time() - 24*3600 528 return time.strftime("%m/%d/%Y", time.localtime(yesterday))
529 530
531 - def all_meta_types(self, interfaces=None):
532 """ 533 DEPRECATED Override the ObjectManager method that is used to control 534 the items available in the add drop down in the ZMI. It uses the 535 attribute sub_menu_items to create the data structures. This is a list 536 of meta_types for the available classes. This functionality is rarely 537 used in Zenoss because the ZMI is not the perfered management 538 interface. 539 """ 540 mts = super(ZenModelBase,self).all_meta_types(interfaces) 541 if self.sub_meta_types: 542 mts = filter(lambda mt: mt['name'] in self.sub_meta_types, mts) 543 return mts
544 545 546 security.declareProtected('Delete objects', 'manage_deleteObjects')
547 - def manage_deleteObjects(self, ids=(), REQUEST=None):
548 """ 549 Delete objects by id from this object and return to the current 550 template as defined by callZenScreen. Uses ObjectManager._delObject to 551 remove the object. 552 553 @permission: ZEN_VIEW 554 """ 555 for id in ids: self._delObject(id) 556 if REQUEST: 557 return self.callZenScreen(REQUEST)
558 559
560 - def custPropertyIds(self):
561 """ 562 List custom properties that are defined at root node. Custom properties 563 start with a lower "c" followed by a uppercase character. 564 """ 565 return self.zenPropertyIds(pfilt=iscustprop)
566 567
568 - def custPropertyMap(self):
569 """ 570 Return custom property definitions. 571 572 @rtype: [{'id':'cName','label':'Name', 'type':'string'},] 573 """ 574 return self.zenPropertyMap(pfilt=iscustprop)
575 576
577 - def visibleCustPropertyMap(self):
578 """ 579 List custom property definitions that are visible using 580 custPropertyMap:: 581 582 @rtype: [{'id':'cName','label':'Name', 'type':'string'},] 583 """ 584 return [ p for p in self.zenPropertyMap(pfilt=iscustprop) \ 585 if p.get('visible', True) ]
586 587 588 security.declareProtected(ZEN_MANAGE_DMD, 'saveCustProperties')
589 - def saveCustProperties(self, REQUEST):
590 """ 591 Save custom properties from REQUEST.form. 592 593 @permission: ZEN_MANAGE_DMD 594 """ 595 return self.saveZenProperties(iscustprop, REQUEST)
596 597
598 - def getObjByPath(self, path):
599 """ 600 Lookup and object by its path. Basically does a Zope unrestricted 601 traverse on the path given. 602 603 @type path: list or string /zport/dmd/Devices 604 605 >>> dmd.getObjByPath(('zport','dmd','Devices')) 606 <DeviceClass at /zport/dmd/Devices> 607 >>> dmd.getObjByPath(('Devices','Server')) 608 <DeviceClass at /zport/dmd/Devices/Server> 609 >>> dmd.getObjByPath('/zport/dmd/Devices/Server') 610 <DeviceClass at /zport/dmd/Devices/Server> 611 >>> dmd.getObjByPath('Devices/Server') 612 <DeviceClass at /zport/dmd/Devices/Server> 613 """ 614 return getObjByPath(self, path)
615 616
617 - def isLocalName(self, name):
618 """ 619 Check to see if a name is local to our current context or if it comes 620 from our acquisition chain. 621 622 @rtype: boolean 623 624 >>> dmd.isLocalName('Devices') 625 True 626 >>> dmd.Devices.Server.isLocalName('Devices') 627 False 628 """ 629 v = getattr(aq_base(self), name, '__ZENMARKER__') 630 return v != '__ZENMARKER__'
631 632 security.declareProtected(ZEN_VIEW, 'helpLink') 661 662 663 security.declareProtected(ZEN_VIEW, 'getIconPath')
664 - def getIconPath(self):
665 """ 666 Return the icon associated with this object. The icon path is defined 667 in the zProperty zIcon. 668 669 @return: Path to icon 670 @rtype: string 671 @permission: ZEN_VIEW 672 673 >>> dmd.Devices.Server.zIcon = '/zport/dmd/img/icons/server.png' 674 >>> d = dmd.Devices.Server.createInstance('test') 675 >>> d.getIconPath() 676 '/zport/dmd/img/icons/server.png' 677 """ 678 try: 679 return self.primaryAq().zIcon 680 except AttributeError: 681 return '/zport/dmd/img/icons/noicon.png'
682 683
684 - def aqBaseHasAttr(self, attr):
685 """ 686 Return hasattr(aq_base(self), attr) 687 This is a convenience function for use in templates, where it's not 688 so easy to make a similar call directly. 689 hasattr itself will swallow exceptions, so we don't want to use that. 690 We also need to allow for values of None, so something like 691 getattr(aq_base(self, attr, None) doesn't really tell us anything. 692 Testing __dict__ is not a good choice because it doesn't allow 693 for properties (and I believe __getitem__ calls.) 694 So while this looks pretty attrocious, it might be the most sane 695 solution. 696 """ 697 return getattr(aq_base(self), attr, _MARKER) is not _MARKER
698 699
700 -class ZenModelZenDocProvider(object):
701 zope.interface.implements(IZenDocProvider) 702 zope.component.adapts(ZenModelBase) 703
704 - def __init__(self, zenModelBase):
705 self._underlyingObject = zenModelBase
706
707 - def getZendoc(self):
708 zendoc = self._underlyingObject._zendoc 709 if not zendoc and self._underlyingObject.aqBaseHasAttr( 'description' ): 710 zendoc = self._underlyingObject.description 711 return zendoc
712
713 - def setZendoc(self, zendocText):
714 self._underlyingObject._zendoc = zendocText
715
716 - def exportZendoc(self,ofile):
717 """Return an xml representation of a RelationshipManagers zendoc 718 <property id='_zendoc' type='string' mode='w'> 719 value 720 </property> 721 """ 722 value = self.getZendoc() 723 if not value: return 724 ofile.write("<property id='zendoc' type='string'>\n") 725 if not isinstance(value, basestring): 726 value = unicode(value) 727 elif isinstance(value, str): 728 value = value.decode('latin-1') 729 ofile.write(saxutils.escape(value).encode('utf-8')+"\n") 730 ofile.write("</property>\n")
731 732 733 InitializeClass(ZenModelBase) 734