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