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 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
50 iscustprop = re.compile("^c[A-Z]").search
51
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
61
62 security = ClassSecurityInfo()
63
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
75
76
77 security.declareProtected(ZEN_VIEW, 'view')
79 '''
80 Returns the default view even if index_html is overridden.
81
82 @permission: ZEN_VIEW
83 '''
84 return self()
85
86
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
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
169 """
170 DEPRECATED Return an a link to this object with its id as the name.
171
172 @return: An HTML link to this object
173 @rtype: string
174
175 >>> dmd.Devices.getIdLink()
176 '<a href="/zport/dmd/Devices">/</a>'
177 """
178 return self.urlLink()
179
180
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
190
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
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
218 - def urlLink(self, text=None, url=None, attrs={}):
219 """
220 Return an anchor tag if the user has access to the remote object.
221
222 @param text: the text to place within the anchor tag or string.
223 Defaults to the id of this object.
224 @param url: url for the href. Default is getPrimaryUrlPath
225 @type attrs: dict
226 @param attrs: any other attributes to be place in the in the tag.
227 @return: An HTML link to this object
228 @rtype: string
229 """
230 if not text:
231 text = self.titleOrId()
232 text = escape(text)
233 if not self.checkRemotePerm("View", self):
234 return text
235 if not url:
236 url = self.getPrimaryUrlPath()
237 if len(attrs):
238 return '<a href="%s" %s>%s</a>' % (url,
239 ' '.join('%s="%s"' % (x,y) for x,y in attrs.items()),
240 text)
241 else:
242 return '<a href="%s">%s</a>' % (url, text)
243
244
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
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
306
307 return ZenModelBase.breadCrumbs(self, terminator, isOrganizer)
308
309
310 security.declareProtected(ZEN_COMMON, 'checkRemotePerm')
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')
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
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')
392
393
394 security.declareProtected(ZEN_VIEW, 'getPrimaryDmdId')
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
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
437 """
438 DEPRECATED Split a path on its '/'.
439 """
440 return zenpathsplit(path)
441
442
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
453 """
454 DEPRECATED this doesn't seem to be used anywere don't use it!!!
455 """
456 return getHierarchyObj(root, name, relpath)
457
458
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
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
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
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
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
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
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
544
545
546 security.declareProtected('Delete objects', 'manage_deleteObjects')
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
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
569 """
570 Return custom property definitions.
571
572 @rtype: [{'id':'cName','label':'Name', 'type':'string'},]
573 """
574 return self.zenPropertyMap(pfilt=iscustprop)
575
576
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')
596
597
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
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')
634 """
635 DEPRECATED Return a link to the objects help file.
636
637 @permission: ZEN_VIEW
638 """
639 path = self.__class__.__module__.split('.')
640 className = path[-1].replace('Class','')
641 product = path[-2]
642
643 path = ("", "Control_Panel", "Products", product, "Help",
644 "%s.stx"%className)
645
646
647 app = self.getPhysicalRoot()
648 try:
649 app.restrictedTraverse(path)
650 except (KeyError, Unauthorized):
651 return ""
652
653 url = "/HelpSys?help_url="+ "/".join(path)
654
655 return """<a class="tabletitle" href="%s" \
656 onClick="window.open('%s','zope_help','width=600,height=500, \
657 menubar=yes,toolbar=yes,scrollbars=yes,resizable=yes'); \
658 return false;" onMouseOver="window.status='Open online help'; \
659 return true;" onMouseOut="window.status=''; return true;">Help!</a>
660 """ % (url, url)
661
662
663 security.declareProtected(ZEN_VIEW, 'getIconPath')
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
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
701 zope.interface.implements(IZenDocProvider)
702 zope.component.adapts(ZenModelBase)
703
705 self._underlyingObject = zenModelBase
706
708 zendoc = self._underlyingObject._zendoc
709 if not zendoc and self._underlyingObject.aqBaseHasAttr( 'description' ):
710 zendoc = self._underlyingObject.description
711 return zendoc
712
714 self._underlyingObject._zendoc = zendocText
715
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