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 import types
27
28
29 from PrimaryPathObjectManager import PrimaryPathObjectManager
30 from ZenPropertyManager import ZenPropertyManager
31
32 from Globals import DTMLFile
33 from Globals import InitializeClass
34 from AccessControl import ClassSecurityInfo
35 from Acquisition import aq_base
36 from App.Management import Tabs
37
38 from RelSchema import *
39 from Exceptions import *
40
41 from Products.ZenUtils.Utils import unused
42
43 zenmarker = "__ZENMARKER__"
44
51
52
53 addRelationshipManager = DTMLFile('dtml/addRelationshipManager',globals())
54
55
57 """
58 RelationshipManger is an ObjectManager like class that can contain
59 relationships (in fact relationships can only be added to a
60 RelationshipManager).
61
62 Relationships are defined on an RM by the hash _relations. It
63 should be defined on the class so that it isn't stored in the database.
64 If there is inheritance involved remember to add the base class _relations
65 definition to the current class so that all relationships for the class
66 are defined on it.
67
68 remoteClassStr - is a string that represents the full path to the remote
69 class. Its a string because in most cases the classes
70 will be in different modules which would cause a recursive
71 import of the two modules.
72
73 _relations = (
74 ("toonename", ToOne(ToMany, remoteClassStr, remoteName)),
75 ("tomanyname", ToMany(ToMany, remoteClassStr, remoteName)),
76 )
77 """
78
79 _relations = ()
80
81 meta_type = 'Relationship Manager'
82
83 security = ClassSecurityInfo()
84
85 manage_options = (
86 PrimaryPathObjectManager.manage_options +
87 ZenPropertyManager.manage_options
88 )
89
90 manage_main=DTMLFile('dtml/RelationshipManagerMain', globals())
91
92
93 _operation = -1
94
95 - def __init__(self, id, title=None, buildRelations=True):
99
100
102 """
103 Return our simple id if we are called from our primary path
104 else return the full primary id.
105 """
106 if self.getPhysicalPath() == self.getPrimaryPath(): return self.id
107 return self.getPrimaryId()
108
109
110
111
112
113
114
115
116
118 """Form a bi-directional relationship."""
119 rel = getattr(self, name, None)
120 if rel == None:
121 raise AttributeError("Relationship %s, not found" % name)
122 rel.addRelation(obj)
123
124
126 """
127 Remove an object from a relationship.
128 If no object is passed all objects are removed.
129 """
130 rel = getattr(self, name, None)
131 if rel == None:
132 raise AttributeError("Relationship %s, not found" % name)
133 rel.removeRelation(obj)
134
135
136 - def _setObject(self,id,object,roles=None,user=None,set_owner=1):
144
145
147 """
148 handle cut/past vs. delete
149 If we are being moved (cut/past) don't clear relationshp
150 if we are being deleted set all relationship to None so
151 that our related object don't have dangling references
152 """
153 PrimaryPathObjectManager.manage_beforeDelete(self, item, container)
154 if self._operation > -1: self._operation = -1
155
156
157
158
159
160
161
162
164 """
165 Create a copy of this relationship manager. This involes copying
166 relationships and removing invalid relations (ie ones with ToOne)
167 and performing copies of any contained objects.
168 Properties are also set on the new object.
169 """
170 id = self.id
171 if getattr(aq_base(container), id, zenmarker) is not zenmarker:
172 id = "copy_of_" + id
173 cobj = self.__class__(id, buildRelations=False)
174 cobj = cobj.__of__(container)
175 for objid, sobj in self.objectItems():
176
177 csobj = sobj._getCopy(cobj)
178 cobj._setObject(csobj.id, csobj)
179 for name, value in self.propertyItems():
180 cobj._updateProperty(name, value)
181 return aq_base(cobj)
182
183
185 """Manage copy/move/rename state for use in manage_beforeDelete."""
186 unused(container)
187 self._operation = op
188
189
195
196
198 """
199 Move a relationship manager without deleting its relationships.
200 """
201 self._operation = 1
202 srcRelationship._delObject(self.id)
203 self = aq_base(self)
204 destRelationship._setObject(self.id, self)
205 return destRelationship._getOb(self.id)
206
207
208
210 """
211 Move obj from this RM to the destination RM
212 """
213 self._operation = 1
214 self._delObject(obj.id)
215 obj = aq_base(obj)
216 destination._setObject(obj.id, obj)
217 return destination._getOb(obj.id)
218
219
220
221
222
223
224
225
226
227
238
239
241 """
242 Lookup the schema definition for a relationship.
243 All base classes are checked until RelationshipManager is found.
244 """
245 for name, schema in cls._relations:
246 if name == relname: return schema
247 raise ZenSchemaError("Schema for relation %s not found on %s" %
248 (relname, cls.__name__))
249 lookupSchema = classmethod(lookupSchema)
250
251
253 """Returns a dictionary of relationship objects keyed by their names"""
254 return self.objectValues(spec=RELMETATYPES)
255
256
260
261
267
268
269
270
271
272
273
274
275 - def exportXml(self, ofile, ignorerels=[], root=False):
276 """Return an xml based representation of a RelationshipManager
277 <object id='/Devices/Servers/Windows/dhcp160.confmon.loc'
278 module='Products.Confmon.IpInterface' class='IpInterface'>
279 <property id='name'>jim</property>
280 <toone></toone>
281 <tomany></tomany>
282 <tomanycont></tomanycont>
283 </object>
284 """
285 modname = self.__class__.__module__
286 classname = self.__class__.__name__
287 id = root and self.getPrimaryId() or self.id
288 stag = "<object id='%s' module='%s' class='%s'>\n" % (
289 id , modname, classname)
290 ofile.write(stag)
291 self.exportXmlProperties(ofile)
292 self.exportXmlRelationships(ofile, ignorerels)
293 exportHook = getattr(aq_base(self), 'exportXmlHook', None)
294 if exportHook and callable(exportHook):
295 self.exportXmlHook(ofile, ignorerels)
296 ofile.write("</object>\n")
297
298
300 """Return an xml representation of a RelationshipManagers properties
301 <property id='name' type='type' mode='w' select_variable='selectvar'>
302 value
303 </property>
304 value will be converted to is correct python type on import
305 """
306 for prop in self._properties:
307 if not prop.has_key('id'): continue
308 id = prop['id']
309 ptype = prop['type']
310 value = getattr(aq_base(self), id, None)
311 if not value and ptype not in ("int","float","boolean"): continue
312 stag = []
313 stag.append('<property')
314 for k, v in prop.items():
315 if ptype != 'selection' and k == 'select_variable': continue
316 v = saxutils.quoteattr(str(v))
317 stag.append('%s=%s' % (k, v))
318 stag.append('>')
319 ofile.write(' '.join(stag)+"\n")
320 if type(value) not in types.StringTypes:
321 value = unicode(value)
322 elif type(value) == types.StringType:
323 value = value.decode('latin-1')
324 ofile.write(saxutils.escape(value).encode('utf-8')+"\n")
325 ofile.write("</property>\n")
326
327
329 """Return an xml representation of Relationships"""
330 for rel in self.getRelationships():
331 if rel.id in ignorerels: continue
332 rel.exportXml(ofile, ignorerels)
333
334
335
336
337
338
339
340
341 security.declareProtected('Manage Relations', 'manage_addRelation')
346
347
348 security.declareProtected('Manage Relations', 'manage_removeRelation')
350 """remove a relationship to be called from UI"""
351 rel = getattr(self, name, None)
352 if rel == None:
353 raise AttributeError("Relationship %s, not found" % name)
354 rel._delObject(id)
355 if REQUEST: return self.callZenScreen(REQUEST)
356
357
359 """return the workspace of the related object using its primary path"""
360 url = REQUEST['URL']
361 myp = self.getPrimaryUrlPath()
362 if url.find(myp) > 0:
363 Tabs.manage_workspace(self, REQUEST)
364 else:
365 from zExceptions import Redirect
366 raise Redirect( myp+'/manage_workspace' )
367
368
369
370 InitializeClass(RelationshipManager)
371