Annotation of VSyncer/VSyncer.py, revision 1.3
1.1 casties 1: # import stuff
2: from Globals import DTMLFile, MessageDialog, data_dir, Persistent
3: from DateTime import DateTime
4: from AccessControl import getSecurityManager
5: from StringIO import StringIO
6: import string, md5, difflib, time
7: from types import StringType
8: from cgi import escape
9: import logging
10: # import our own things.
11: from Config import *
12: from BaseSyncer import BaseSyncer
13: from SysConfig import *
14:
15: manage_addVSServerForm = DTMLFile('dtml/VAdd', globals())
16:
17:
18: def manage_addVSServer(self, id='SourceSyncer', title='', REQUEST=None):
19: """ adds a server """
20: if id in self.objectIds():
21: return MessageDialog(title = 'Unable to add',
22: message = '%s already exists)' % (id),
23: action = 'manage_main')
24: user = getSecurityManager().getUser().getUserName()
25: if user not in managers_list:
26: return MessageDialog(title ='permission denied!', message='You are not allowed to create this object', action='manage_main')
27:
28: if '/'.join(self.getPhysicalPath()) not in allowed_contexts:
29: if allowed_contexts:
30: msg = 'Sorry! This object can only be created in : \n <br>'
31: else:
32: msg = 'Please update allowed_contexts list in config.py file '
33:
34: for path in allowed_contexts:
35: msg += path + '\n <br>'
36: #example list
37: """allowed_contexts = ['', # if you want to instanciate in root
38: '/abc/xyz',
39: '/abc/def
40: ]
41: """
42: return MessageDialog(title="unable to add", message=msg, action="manage_main")
43: self._setObject(id, VSSyncer(id, title))
44:
45: if REQUEST is not None:
46: return MessageDialog(title = 'Added', message = "%s sucessfully added." % (id), action = '%s/manage_edit' % id, )
47:
48:
49:
50: class VSSyncer(BaseSyncer):
51: meta_type = 'VSSyncer'
52:
53: manage_main = manage_edit = DTMLFile('dtml/VEdit', globals())
54: manage_vsync = DTMLFile('dtml/VSync', globals())
55: manage_folders = DTMLFile('dtml/VFolder', globals())
56: manage_diff = DTMLFile('dtml/VDiff', globals())
57: manage_approval = DTMLFile('dtml/VApproval', globals())
58: diffable = diffable
59:
60: try: manage_edit._setName("manage_edit")
61: except AttributeError: pass # python 1.5.2
62:
63:
64: def __init__(self, id, title=''):
65: """initialize the class"""
66: BaseSyncer.__init__(self, id, title)
67:
68: ###################################################
69: # Code Display
70: ##################################################
71:
72: def manage_compare(self, folder):
73: """return allowed object for syncing"""
74: sync_obj_ids = self.filtered_objects
75:
76: try:
77: source = self.manage_listObjects(folder)
78: dest = self._get_list(self.dest_server, folder)
79: except IndexError:
80: raise 'Server Error', 'There was a problem syncing the servers'
81: items = self._compare_lists(source, dest)
82: return filter(lambda obj, sync_obj_ids=sync_obj_ids:obj['id'] in sync_obj_ids, items)
83:
84: def dump(self, tag, x, lo, hi, r):
85: r1=[]
86: r2=[]
87: for i in xrange(lo, hi):
88: r1.append(tag)
89: r2.append(x[i])
90: r.append("<tr>\n"
91: "<td><pre>\n%s\n</pre></td>\n"
92: "<td><pre>\n%s\n</pre></td>\n"
93: "</tr>\n"
94: % ('\n'.join(r1), escape('\n'.join(r2))))
95:
96:
97: def manage_srcXMLRPC(self, obj_path):
98: """ Get a src from an object (if allowed) suitable for diffing """
99: try:
100: obj = self.restrictedTraverse(obj_path)
101: except KeyError: return 404
102:
103: c = getSecurityManager().checkPermission
104: if not c('View management screens', obj):
105: return 403
106:
107: return self._encode(obj.document_src())
108:
109: def manage_getDiff(self, object):
110: """get the code diff between the server nad clients"""
111: source = self._decode(self.manage_srcXMLRPC(object))
112: dest = self._decode(self._srcXMLRPC(self.dest_server, object))
113: from_source = self.manage_getSourceCode(source, dest, bg_color='green')
114: from_dest = self.manage_getSourceCode(dest, source, bg_color='red')
115: return '<table><tr><td>' + from_source + '</td>' + '<td>' + from_dest + '</td></tr></table>'
116:
117: def manage_getSourceCode(self, source, dest, bg_color):
118: "return the source code"
119: a=dest.split('\n')
120: b=source.split('\n')
121: cruncher=difflib.SequenceMatcher()
122: cruncher.set_seqs(a,b)
123:
124: r=['<table border=1 width="100%"> ']
125: for tag, alo, ahi, blo, bhi in cruncher.get_opcodes():
126: if tag == 'replace':
127: (x, xlo, xhi, y, ylo, yhi, r) = (a, alo, ahi, b, blo, bhi, r)
128: rx1=[]
129: rx2=[]
130: for i in xrange(xlo, xhi):
131: rx1.append(' ')
132: rx2.append(' ')
133: r.append("<tr>\n"
134: "<td ><pre>\n%s\n</pre></td>\n"
135: "<td ><pre>\n%s\n</pre></td>\n"
136: "</tr>\n"
137: % ( '\n'.join(rx1),
138: escape('\n'.join(rx2))))
139:
140: ry1=[]
141: ry2=[]
142: for i in xrange(ylo, yhi):
143: ry1.append('+')
144: ry2.append(y[i])
145:
146:
147: r.append("<tr>\n"
148: "<td bgcolor=%s style='color:white;'><pre>\n%s\n</pre></td>\n"
149: "<td bgcolor=%s style='color:white;'><pre>\n%s\n</pre></td>\n"
150: "</tr>\n"
151: % (bg_color, '\n'.join(ry1), bg_color, escape('\n'.join(ry2))))
152:
153: elif tag == 'delete':
154: (tag, x, lo, hi, r) = ('-', a, alo, ahi, r)
155: r1=[]
156: r2=[]
157:
158: for i in xrange(lo, hi):
159: r1.append(' ')
160: r2.append(' ')
161: r.append("<tr>\n"
162: "<td ><pre>\n%s\n</pre></td>\n"
163: "<td ><pre>\n%s\n</pre></td>\n"
164: "</tr>\n"
165: % ('\n'.join(r1), escape('\n'.join(r2))))
166:
167: elif tag == 'insert':
168: (tag, x, lo, hi, r) = ('+', b, blo, bhi, r)
169: r1=[]
170: r2=[]
171: for i in xrange(lo, hi):
172: r1.append(tag)
173: r2.append(x[i])
174: r.append("<tr>\n"
175: "<td bgcolor=%s style='color:white;'><pre>\n%s\n</pre></td>\n"
176: "<td bgcolor=%s style='color:white;'><pre>\n%s\n</pre></td>\n"
177: "</tr>\n"
178: % (bg_color, '\n'.join(r1), bg_color, escape('\n'.join(r2))))
179:
180:
181: elif tag == 'equal':
182: self.dump(' ', a, alo, ahi, r)
183: else:
184: raise ValueError, 'unknown tag ' + `tag`
185:
186: r.append('</table>')
187:
188: return '\n'.join(r)
189:
190: ##########################################################
191:
192: def status_colour(self, status):
193: ''' gives a list of status colours for pretty html '''
194: return colours.get(status, 'white')
195:
196: def getFilterObjects(self):
197: """ do we filter objects? """
198: return getattr(self, 'filterObjects', 1)
199:
200: def manage_getAvailableObjects(self):
201: x= [[obj.getId(),obj.title_or_id()] for obj in self.aq_parent.objectValues() if obj.meta_type in syncable]
202: return x
203:
204: #TODO: we will delete this if it is not required
205: def manage_getSyncableObjs(self):
206: "return all syncable "
207: if not self.filtered_objects: return ["Sorry! There are no syncable items found for this context"]
208: return [item + '\n' for item in self.filtered_objects]
209:
210: def manage_listObjects(self, folder):
211: ''' Gets a list of the objects '''
212: obs = {}
213:
214: try:
215: folder_obj = self.restrictedTraverse(string.split(folder, '/'))
216: except:
217: return {}#because this folder is not found in on of the servers
218:
219: # go find the folder, then iterate through the subject
220: for ob in folder_obj.objectValues():
221: if ob.meta_type in syncable:
222: # we dont to sync ZSyncers, that way leads much confusion
223: # so we'll just sync objects we know work
224: # avoid broken objects
225: # allow people to turn this feature on and off
226: if self.getFilterObjects() and not self._isSyncable(ob.meta_type): continue
227: if ob.meta_type[:14] == 'Broken Because': continue
228:
229: o = {}
230: o['id'] = ob.getId()
231: o['path'] = ob.getPhysicalPath()
232:
233: # this should always be a string right?
234: o['meta_type'] = ob.meta_type
235:
236: # CMF fix, where apparently ob.icon is not always a string
237: o['icon'] = ob.icon
238: if not isinstance(o['icon'], StringType): o['icon'] = ob.icon()
239:
240: o['is_folder'] = getattr(ob, 'isPrincipiaFolderish', 0)
241: o['last_modified_time'] = str(ob.bobobase_modification_time().toZone('GMT'))
242: o['src_digest'] = self.manage_getSrcDigest(ob.getPhysicalPath())
243: # add this to the list of objects
244: obs[o['id']] = o
245: return obs
246:
247: def manage_updateXMLRPC(self, object=None, msgs=None, REQUEST=None ):
248: "updates the object using xml rpc"
249: if not object :
250: return MessageDialog(title='Please select', message='you have not selected any objects to sync', action='manage_vsync')
251: if msgs is None: msgs = []
252:
253: if type(object) == StringType: object = [object,]
254:
255: for ob in object:
1.2 casties 256: if allowed_objs_sync and ob not in allowed_objs_sync:
1.1 casties 257: msgs.append(403)
258: continue
259: try:
260: msgs.append('Object <b>%s</b> to <b>%s</b>' % (ob, self.dest_server))
261: msgs.append(self._editXMLRPC(ob, self.dest_server))
262: except:
263: msgs.append('<font color=red>object %s not found in source server</font>' % (ob))
264: return self._msg(msgs, REQUEST)
265:
266: def manage_editXMLRPC(self, data, obj_path, obj_id):
267: "edit the object here"
268:
269: if type(obj_path) == type('string'): obj_path = string.split(obj_path, '/')
270:
271: try: parent = self.restrictedTraverse(obj_path[:-1])
272: except KeyError: return 404
273:
274:
275: obj = getattr(parent, obj_id, None)
276: if obj is None:
277: self.manage_addXMLRPC(data, obj_path)
278: else:
279: data = self._decode(data)
280: obj = getattr(parent, obj_id)
281: self.manage_takeBackup(obj)
282: if obj.meta_type in diffable:
283: obj.manage_edit(data, obj.title)
284: else:
285: obj.manage_upload(data)
286:
287: return 200
288: # we have updated successfully:)
289:
290: def manage_getSrcDigest(self, object):
291: """returns the digest of the object source"""
292: try:
293: obj = self.restrictedTraverse(object)
294: except:
295: return 404 # what to do object not foud :(
296:
297: if obj.meta_type in diffable:
298: src = obj.document_src()
299: else:
300: data = obj.data
301: src = ''
302: if type(data) == type(''):
303: src = data
304: else:# more tricky. We are getting wrapper here. np :)
305: while data is not None:
306: src += data.data
307: data=data.next
308: #return self._encode(src)
1.3 ! casties 309: if isinstance(src,unicode):
! 310: # encode unicode to string
! 311: src = src.encode('utf-8')
1.1 casties 312: return self._encode(md5.new(src).digest())
313:
314: ###############################
315: # internal only
316: # protected by having _ as the first character
317:
318: def _isSyncable(self, m):
319: if m in self.syncable:
320: return 1
321:
322: def _compare_lists(self, s_dict, d_dict):
323: """compare the lists from 2 zopes"""
324: _l = []
325: d_keys = d_dict.keys()
326: s_keys = s_dict.keys()
327:
328:
329: for item in s_dict.keys():
330: if s_dict[item]['id'] not in d_keys:
331: s_dict[item]['status'] = 'missing'
332: _l.append(s_dict[item])
333: # ok its on the dest, We cont depend on time stamp because bothe servers are in different time zone.
334: # so we will check whether the sourse of the s_server and d_server same or not
335: elif self._decode(s_dict[item]['src_digest']) != self._decode(d_dict[item]['src_digest']) :
336: s_dict[item]['status'] = 'out of date'
337: _l.append(s_dict[item])
338: # not sure what else to check
339: else:
340: s_dict[item]['status'] = 'ok'
341: _l.append(s_dict[item])
342:
343: # ok check its on the destination, but not on the original
344: for item in d_dict.keys():
345: if d_dict[item]['id'] not in s_keys:
346: d_dict[item]['status'] = 'extra'
347: _l.append(d_dict[item])
348: return _l
349:
350: def _srcXMLRPC(self, server, object=None):
351: # call the src method
352: serverconn = self._serverConn(self._check_http(server))
353: return serverconn.manage_srcXMLRPC(object)
354:
355: def _get_list(self, server, folder):
356: ''' actually get the list '''
357: serverconn = self._serverConn(self._check_http(server))
358: return serverconn.manage_listObjects(folder)
359:
360: def _exportXMLRPC(self, object=None, dest_url=None, add_in=1):
361: """exports an object"""
362: # go get the object
363: obj = self.restrictedTraverse(object)
364: data = StringIO()
365: obj._p_jar.exportFile(obj._p_oid, data)
366:
367: # do xml-rpc
368: serverconn = self._serverConn(self._check_http(dest_url))
369: try: result = serverconn.manage_addXMLRPC(self._encode(data.getvalue()), obj.getPhysicalPath(), add_in)
370: except: return 500
371:
372: # return result
373: return result
374:
375: def _editXMLRPC(self, object=None, dest_url=None, add_in=1):
376: "edit the object"
377: logging.debug("_editXMLRPC object=%s dest_url=%s"%(object,dest_url))
378: obj = self.restrictedTraverse(object)
379: if obj.meta_type in diffable:
380: obj_data = obj.document_src()
381: else:
382: obj_data = obj.data
383: obj_id = obj.getId()
384:
385: # do xml-rpc
386: serverconn = self._serverConn(self._check_http(dest_url))
387: try: result = serverconn.manage_editXMLRPC(self._encode(obj_data), obj.getPhysicalPath(), obj_id)
388: except:
389: try:self._exportXMLRPC(object, dest_url)
390: except:return 500
391: return 200
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>