1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 __doc__ = """RelationshipManager
16
17 RelationshipManager is a mix in class to manage relationships
18 defined by the SchemaManager.
19 """
20
21 from xml.sax import saxutils
22
23 import logging
24 log = logging.getLogger("zen.Relations")
25
26
27 from PrimaryPathObjectManager import PrimaryPathObjectManager
28 from ZenPropertyManager import ZenPropertyManager
29
30 from Globals import DTMLFile
31 from Globals import InitializeClass
32 from AccessControl import ClassSecurityInfo
33 from Acquisition import aq_base
34 from App.Management import Tabs
35 import OFS.subscribers
36 import zope.interface
37 import zope.component
38
39 from OFS.interfaces import IItem
40
41 from RelSchema import *
42 from Exceptions import *
43
44 from Products.ZenUtils.Utils import unused
45 from Products.ZenModel.interfaces import IZenDocProvider
46
47 zenmarker = "__ZENMARKER__"
48
55
56
57 addRelationshipManager = DTMLFile('dtml/addRelationshipManager',globals())
58
59
61 """
62 RelationshipManger is an ObjectManager like class that can contain
63 relationships (in fact relationships can only be added to a
64 RelationshipManager).
65
66 Relationships are defined on an RM by the hash _relations. It
67 should be defined on the class so that it isn't stored in the database.
68 If there is inheritance involved remember to add the base class _relations
69 definition to the current class so that all relationships for the class
70 are defined on it.
71
72 remoteClassStr - is a string that represents the full path to the remote
73 class. Its a string because in most cases the classes
74 will be in different modules which would cause a recursive
75 import of the two modules.
76
77 _relations = (
78 ("toonename", ToOne(ToMany, remoteClassStr, remoteName)),
79 ("tomanyname", ToMany(ToMany, remoteClassStr, remoteName)),
80 )
81 """
82 zope.interface.implements(IItem)
83
84 _relations = ()
85
86 meta_type = 'Relationship Manager'
87
88 security = ClassSecurityInfo()
89
90 manage_options = (
91 PrimaryPathObjectManager.manage_options +
92 ZenPropertyManager.manage_options
93 )
94
95 manage_main=DTMLFile('dtml/RelationshipManagerMain', globals())
96
97
98 _operation = -1
99
100 - def __init__(self, id, title=None, buildRelations=True):
104
105
107 """
108 Return our simple id if we are called from our primary path
109 else return the full primary id.
110 """
111 if self.getPhysicalPath() == self.getPrimaryPath(): return self.id
112 return self.getPrimaryId()
113
114
115
116
117
118
119
120
121
123 """Form a bi-directional relationship."""
124 rel = getattr(self, name, None)
125 if rel == None:
126 raise AttributeError("Relationship %s, not found" % name)
127 rel.addRelation(obj)
128
129
131 """
132 Remove an object from a relationship.
133 If no object is passed all objects are removed.
134 """
135 rel = getattr(self, name, None)
136 if rel == None:
137 raise AttributeError("Relationship %s, not found" % name)
138 rel.removeRelation(obj, suppress_events=suppress_events)
139
140
141 - def _setObject(self,id,object,roles=None,user=None,set_owner=1):
149
150
151
152
153
154
155
156
158 """
159 Create a copy of this relationship manager. This involes copying
160 relationships and removing invalid relations (ie ones with ToOne)
161 and performing copies of any contained objects.
162 Properties are also set on the new object.
163 """
164 id = self.id
165 if getattr(aq_base(container), id, zenmarker) is not zenmarker:
166 id = "copy_of_" + id
167 cobj = self.__class__(id, buildRelations=False)
168 cobj = cobj.__of__(container)
169 for objid, sobj in self.objectItems():
170
171 csobj = sobj._getCopy(cobj)
172 cobj._setObject(csobj.id, csobj)
173 for name, value in self.propertyItems():
174 cobj._updateProperty(name, value)
175 return aq_base(cobj)
176
177
179 """Manage copy/move/rename state for use in manage_beforeDelete."""
180 unused(container)
181 self._operation = op
182
183
189
190
192 """
193 Move a relationship manager without deleting its relationships.
194 """
195 self._operation = 1
196 srcRelationship._delObject(self.id)
197 self = aq_base(self)
198 destRelationship._setObject(self.id, self)
199 return destRelationship._getOb(self.id)
200
201
202
204 """
205 Move obj from this RM to the destination RM
206 """
207 self._operation = 1
208 self._delObject(obj.id)
209 obj = aq_base(obj)
210 destination._setObject(obj.id, obj)
211 return destination._getOb(obj.id)
212
213
214
215
216
217
218
219
220
221
232
233
235 """
236 Lookup the schema definition for a relationship.
237 All base classes are checked until RelationshipManager is found.
238 """
239 for name, schema in cls._relations:
240 if name == relname: return schema
241 raise ZenSchemaError("Schema for relation %s not found on %s" %
242 (relname, cls.__name__))
243 lookupSchema = classmethod(lookupSchema)
244
245
247 """Returns a dictionary of relationship objects keyed by their names"""
248 return self.objectValues(spec=RELMETATYPES)
249
250
254
255
261
262
263
264
265
266
267
268
269 - def exportXml(self, ofile, ignorerels=[], root=False):
270 """Return an xml based representation of a RelationshipManager
271 <object id='/Devices/Servers/Windows/dhcp160.confmon.loc'
272 module='Products.Confmon.IpInterface' class='IpInterface'>
273 <property id='name'>jim</property>
274 <toone></toone>
275 <tomany></tomany>
276 <tomanycont></tomanycont>
277 </object>
278 """
279 modname = self.__class__.__module__
280 classname = self.__class__.__name__
281 id = root and self.getPrimaryId() or self.id
282 stag = "<object id='%s' module='%s' class='%s'>\n" % (
283 id , modname, classname)
284 ofile.write(stag)
285 zendocAdapter = zope.component.queryAdapter( self, IZenDocProvider )
286 if zendocAdapter is not None:
287 zendocAdapter.exportZendoc( ofile )
288 self.exportXmlProperties(ofile)
289 self.exportXmlRelationships(ofile, ignorerels)
290 exportHook = getattr(aq_base(self), 'exportXmlHook', None)
291 if exportHook and callable(exportHook):
292 self.exportXmlHook(ofile, ignorerels)
293 ofile.write("</object>\n")
294
295
297 """Return an xml representation of a RelationshipManagers properties
298 <property id='name' type='type' mode='w' select_variable='selectvar'>
299 value
300 </property>
301 value will be converted to is correct python type on import
302 """
303 for prop in self._properties:
304 if not 'id' in prop: continue
305 id = prop['id']
306 ptype = prop['type']
307 value = getattr(aq_base(self), id, None)
308 if not value:
309 if ptype in ("string","text","password"):
310 if not id.startswith('z'):
311 continue
312 elif ptype == "lines":
313 if value is None:
314 continue
315 elif ptype not in ("int","float","boolean","long"):
316 continue
317 if ptype == "password":
318 value = ''
319 stag = []
320 stag.append('<property')
321 for k, v in prop.items():
322 if ptype != 'selection' and k == 'select_variable': continue
323 v = saxutils.quoteattr(str(v))
324 stag.append('%s=%s' % (k, v))
325 stag.append('>')
326 ofile.write(' '.join(stag)+"\n")
327 if not isinstance(value, basestring):
328 value = unicode(value)
329 elif isinstance(value, str):
330 value = value.decode('latin-1')
331 valuestr = saxutils.escape(value).encode('utf-8').strip()
332 if valuestr:
333 ofile.write(valuestr+"\n")
334 ofile.write("</property>\n")
335
336
338 """Return an xml representation of Relationships"""
339 for rel in self.getRelationships():
340 if rel.id in ignorerels: continue
341 rel.exportXml(ofile, ignorerels)
342
343
344
345
346
347
348
349
350 security.declareProtected('Manage Relations', 'manage_addRelation')
355
356
357 security.declareProtected('Manage Relations', 'manage_removeRelation')
359 """remove a relationship to be called from UI"""
360 rel = getattr(self, name, None)
361 if rel == None:
362 raise AttributeError("Relationship %s, not found" % name)
363 rel._delObject(id)
364 if REQUEST: return self.callZenScreen(REQUEST)
365
366
368 """return the workspace of the related object using its primary path"""
369 url = REQUEST['URL']
370 myp = self.getPrimaryUrlPath()
371 if url.find(myp) > 0:
372 Tabs.manage_workspace(self, REQUEST)
373 else:
374 from zExceptions import Redirect
375 raise Redirect( myp+'/manage_workspace' )
376
377
378
379 InitializeClass(RelationshipManager)
380