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