Annotation of VSyncer/VSyncer.py, revision 1.1
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:
! 256: if ob not in allowed_objs_sync:
! 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)
! 309: return self._encode(md5.new(src).digest())
! 310:
! 311: ###############################
! 312: # internal only
! 313: # protected by having _ as the first character
! 314:
! 315: def _isSyncable(self, m):
! 316: if m in self.syncable:
! 317: return 1
! 318:
! 319: def _compare_lists(self, s_dict, d_dict):
! 320: """compare the lists from 2 zopes"""
! 321: _l = []
! 322: d_keys = d_dict.keys()
! 323: s_keys = s_dict.keys()
! 324:
! 325:
! 326: for item in s_dict.keys():
! 327: if s_dict[item]['id'] not in d_keys:
! 328: s_dict[item]['status'] = 'missing'
! 329: _l.append(s_dict[item])
! 330: # ok its on the dest, We cont depend on time stamp because bothe servers are in different time zone.
! 331: # so we will check whether the sourse of the s_server and d_server same or not
! 332: elif self._decode(s_dict[item]['src_digest']) != self._decode(d_dict[item]['src_digest']) :
! 333: s_dict[item]['status'] = 'out of date'
! 334: _l.append(s_dict[item])
! 335: # not sure what else to check
! 336: else:
! 337: s_dict[item]['status'] = 'ok'
! 338: _l.append(s_dict[item])
! 339:
! 340: # ok check its on the destination, but not on the original
! 341: for item in d_dict.keys():
! 342: if d_dict[item]['id'] not in s_keys:
! 343: d_dict[item]['status'] = 'extra'
! 344: _l.append(d_dict[item])
! 345: return _l
! 346:
! 347: def _srcXMLRPC(self, server, object=None):
! 348: # call the src method
! 349: serverconn = self._serverConn(self._check_http(server))
! 350: return serverconn.manage_srcXMLRPC(object)
! 351:
! 352: def _get_list(self, server, folder):
! 353: ''' actually get the list '''
! 354: serverconn = self._serverConn(self._check_http(server))
! 355: return serverconn.manage_listObjects(folder)
! 356:
! 357: def _exportXMLRPC(self, object=None, dest_url=None, add_in=1):
! 358: """exports an object"""
! 359: # go get the object
! 360: obj = self.restrictedTraverse(object)
! 361: data = StringIO()
! 362: obj._p_jar.exportFile(obj._p_oid, data)
! 363:
! 364: # do xml-rpc
! 365: serverconn = self._serverConn(self._check_http(dest_url))
! 366: try: result = serverconn.manage_addXMLRPC(self._encode(data.getvalue()), obj.getPhysicalPath(), add_in)
! 367: except: return 500
! 368:
! 369: # return result
! 370: return result
! 371:
! 372: def _editXMLRPC(self, object=None, dest_url=None, add_in=1):
! 373: "edit the object"
! 374: logging.debug("_editXMLRPC object=%s dest_url=%s"%(object,dest_url))
! 375: obj = self.restrictedTraverse(object)
! 376: if obj.meta_type in diffable:
! 377: obj_data = obj.document_src()
! 378: else:
! 379: obj_data = obj.data
! 380: obj_id = obj.getId()
! 381:
! 382: # do xml-rpc
! 383: serverconn = self._serverConn(self._check_http(dest_url))
! 384: try: result = serverconn.manage_editXMLRPC(self._encode(obj_data), obj.getPhysicalPath(), obj_id)
! 385: except:
! 386: try:self._exportXMLRPC(object, dest_url)
! 387: except:return 500
! 388: return 200
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>