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

Source Code for Module Products.ZenModel.ZenModelBase

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