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