# import stuff from Globals import DTMLFile, MessageDialog, data_dir, Persistent from DateTime import DateTime from AccessControl import getSecurityManager from StringIO import StringIO import string, md5, difflib, time from types import StringType from cgi import escape import logging # import our own things. from Config import * from BaseSyncer import BaseSyncer from SysConfig import * manage_addVSServerForm = DTMLFile('dtml/VAdd', globals()) def manage_addVSServer(self, id='SourceSyncer', title='', REQUEST=None): """ adds a server """ if id in self.objectIds(): return MessageDialog(title = 'Unable to add', message = '%s already exists)' % (id), action = 'manage_main') user = getSecurityManager().getUser().getUserName() if user not in managers_list: return MessageDialog(title ='permission denied!', message='You are not allowed to create this object', action='manage_main') if '/'.join(self.getPhysicalPath()) not in allowed_contexts: if allowed_contexts: msg = 'Sorry! This object can only be created in : \n
' else: msg = 'Please update allowed_contexts list in config.py file ' for path in allowed_contexts: msg += path + '\n
' #example list """allowed_contexts = ['', # if you want to instanciate in root '/abc/xyz', '/abc/def ] """ return MessageDialog(title="unable to add", message=msg, action="manage_main") self._setObject(id, VSSyncer(id, title)) if REQUEST is not None: return MessageDialog(title = 'Added', message = "%s sucessfully added." % (id), action = '%s/manage_edit' % id, ) class VSSyncer(BaseSyncer): meta_type = 'VSSyncer' manage_main = manage_edit = DTMLFile('dtml/VEdit', globals()) manage_vsync = DTMLFile('dtml/VSync', globals()) manage_folders = DTMLFile('dtml/VFolder', globals()) manage_diff = DTMLFile('dtml/VDiff', globals()) manage_approval = DTMLFile('dtml/VApproval', globals()) diffable = diffable try: manage_edit._setName("manage_edit") except AttributeError: pass # python 1.5.2 def __init__(self, id, title=''): """initialize the class""" BaseSyncer.__init__(self, id, title) ################################################### # Code Display ################################################## def manage_compare(self, folder): """return allowed object for syncing""" sync_obj_ids = self.filtered_objects try: source = self.manage_listObjects(folder) dest = self._get_list(self.dest_server, folder) except IndexError: raise 'Server Error', 'There was a problem syncing the servers' items = self._compare_lists(source, dest) return filter(lambda obj, sync_obj_ids=sync_obj_ids:obj['id'] in sync_obj_ids, items) def dump(self, tag, x, lo, hi, r): r1=[] r2=[] for i in xrange(lo, hi): r1.append(tag) r2.append(x[i]) r.append("\n" "
\n%s\n
\n" "
\n%s\n
\n" "\n" % ('\n'.join(r1), escape('\n'.join(r2)))) def manage_srcXMLRPC(self, obj_path): """ Get a src from an object (if allowed) suitable for diffing """ try: obj = self.restrictedTraverse(obj_path) except KeyError: return 404 c = getSecurityManager().checkPermission if not c('View management screens', obj): return 403 return self._encode(obj.document_src()) def manage_getDiff(self, object): """get the code diff between the server nad clients""" source = self._decode(self.manage_srcXMLRPC(object)) dest = self._decode(self._srcXMLRPC(self.dest_server, object)) from_source = self.manage_getSourceCode(source, dest, bg_color='green') from_dest = self.manage_getSourceCode(dest, source, bg_color='red') return '' + '
' + from_source + '' + from_dest + '
' def manage_getSourceCode(self, source, dest, bg_color): "return the source code" a=dest.split('\n') b=source.split('\n') cruncher=difflib.SequenceMatcher() cruncher.set_seqs(a,b) r=[' '] for tag, alo, ahi, blo, bhi in cruncher.get_opcodes(): if tag == 'replace': (x, xlo, xhi, y, ylo, yhi, r) = (a, alo, ahi, b, blo, bhi, r) rx1=[] rx2=[] for i in xrange(xlo, xhi): rx1.append(' ') rx2.append(' ') r.append("\n" "\n" "\n" "\n" % ( '\n'.join(rx1), escape('\n'.join(rx2)))) ry1=[] ry2=[] for i in xrange(ylo, yhi): ry1.append('+') ry2.append(y[i]) r.append("\n" "\n" "\n" "\n" % (bg_color, '\n'.join(ry1), bg_color, escape('\n'.join(ry2)))) elif tag == 'delete': (tag, x, lo, hi, r) = ('-', a, alo, ahi, r) r1=[] r2=[] for i in xrange(lo, hi): r1.append(' ') r2.append(' ') r.append("\n" "\n" "\n" "\n" % ('\n'.join(r1), escape('\n'.join(r2)))) elif tag == 'insert': (tag, x, lo, hi, r) = ('+', b, blo, bhi, r) r1=[] r2=[] for i in xrange(lo, hi): r1.append(tag) r2.append(x[i]) r.append("\n" "\n" "\n" "\n" % (bg_color, '\n'.join(r1), bg_color, escape('\n'.join(r2)))) elif tag == 'equal': self.dump(' ', a, alo, ahi, r) else: raise ValueError, 'unknown tag ' + `tag` r.append('
\n%s\n
\n%s\n
\n%s\n
\n%s\n
\n%s\n
\n%s\n
\n%s\n
\n%s\n
') return '\n'.join(r) ########################################################## def status_colour(self, status): ''' gives a list of status colours for pretty html ''' return colours.get(status, 'white') def getFilterObjects(self): """ do we filter objects? """ return getattr(self, 'filterObjects', 1) def manage_getAvailableObjects(self): x= [[obj.getId(),obj.title_or_id()] for obj in self.aq_parent.objectValues() if obj.meta_type in syncable] return x #TODO: we will delete this if it is not required def manage_getSyncableObjs(self): "return all syncable " if not self.filtered_objects: return ["Sorry! There are no syncable items found for this context"] return [item + '\n' for item in self.filtered_objects] def manage_listObjects(self, folder): ''' Gets a list of the objects ''' obs = {} try: folder_obj = self.restrictedTraverse(string.split(folder, '/')) except: return {}#because this folder is not found in on of the servers # go find the folder, then iterate through the subject for ob in folder_obj.objectValues(): if ob.meta_type in syncable: # we dont to sync ZSyncers, that way leads much confusion # so we'll just sync objects we know work # avoid broken objects # allow people to turn this feature on and off if self.getFilterObjects() and not self._isSyncable(ob.meta_type): continue if ob.meta_type[:14] == 'Broken Because': continue o = {} o['id'] = ob.getId() o['path'] = ob.getPhysicalPath() # this should always be a string right? o['meta_type'] = ob.meta_type # CMF fix, where apparently ob.icon is not always a string o['icon'] = ob.icon if not isinstance(o['icon'], StringType): o['icon'] = ob.icon() o['is_folder'] = getattr(ob, 'isPrincipiaFolderish', 0) o['last_modified_time'] = str(ob.bobobase_modification_time().toZone('GMT')) o['src_digest'] = self.manage_getSrcDigest(ob.getPhysicalPath()) # add this to the list of objects obs[o['id']] = o return obs def manage_updateXMLRPC(self, object=None, msgs=None, REQUEST=None ): "updates the object using xml rpc" if not object : return MessageDialog(title='Please select', message='you have not selected any objects to sync', action='manage_vsync') if msgs is None: msgs = [] if type(object) == StringType: object = [object,] for ob in object: if allowed_objs_sync and ob not in allowed_objs_sync: msgs.append(403) continue try: msgs.append('Object %s to %s' % (ob, self.dest_server)) msgs.append(self._editXMLRPC(ob, self.dest_server)) except: msgs.append('object %s not found in source server' % (ob)) return self._msg(msgs, REQUEST) def manage_editXMLRPC(self, data, obj_path, obj_id): "edit the object here" if type(obj_path) == type('string'): obj_path = string.split(obj_path, '/') try: parent = self.restrictedTraverse(obj_path[:-1]) except KeyError: return 404 obj = getattr(parent, obj_id, None) if obj is None: self.manage_addXMLRPC(data, obj_path) else: data = self._decode(data) obj = getattr(parent, obj_id) self.manage_takeBackup(obj) if obj.meta_type in diffable: obj.manage_edit(data, obj.title) else: obj.manage_upload(data) return 200 # we have updated successfully:) def manage_getSrcDigest(self, object): """returns the digest of the object source""" try: obj = self.restrictedTraverse(object) except: return 404 # what to do object not foud :( if obj.meta_type in diffable: src = obj.document_src() else: data = obj.data src = '' if type(data) == type(''): src = data else:# more tricky. We are getting wrapper here. np :) while data is not None: src += data.data data=data.next #return self._encode(src) return self._encode(md5.new(src).digest()) ############################### # internal only # protected by having _ as the first character def _isSyncable(self, m): if m in self.syncable: return 1 def _compare_lists(self, s_dict, d_dict): """compare the lists from 2 zopes""" _l = [] d_keys = d_dict.keys() s_keys = s_dict.keys() for item in s_dict.keys(): if s_dict[item]['id'] not in d_keys: s_dict[item]['status'] = 'missing' _l.append(s_dict[item]) # ok its on the dest, We cont depend on time stamp because bothe servers are in different time zone. # so we will check whether the sourse of the s_server and d_server same or not elif self._decode(s_dict[item]['src_digest']) != self._decode(d_dict[item]['src_digest']) : s_dict[item]['status'] = 'out of date' _l.append(s_dict[item]) # not sure what else to check else: s_dict[item]['status'] = 'ok' _l.append(s_dict[item]) # ok check its on the destination, but not on the original for item in d_dict.keys(): if d_dict[item]['id'] not in s_keys: d_dict[item]['status'] = 'extra' _l.append(d_dict[item]) return _l def _srcXMLRPC(self, server, object=None): # call the src method serverconn = self._serverConn(self._check_http(server)) return serverconn.manage_srcXMLRPC(object) def _get_list(self, server, folder): ''' actually get the list ''' serverconn = self._serverConn(self._check_http(server)) return serverconn.manage_listObjects(folder) def _exportXMLRPC(self, object=None, dest_url=None, add_in=1): """exports an object""" # go get the object obj = self.restrictedTraverse(object) data = StringIO() obj._p_jar.exportFile(obj._p_oid, data) # do xml-rpc serverconn = self._serverConn(self._check_http(dest_url)) try: result = serverconn.manage_addXMLRPC(self._encode(data.getvalue()), obj.getPhysicalPath(), add_in) except: return 500 # return result return result def _editXMLRPC(self, object=None, dest_url=None, add_in=1): "edit the object" logging.debug("_editXMLRPC object=%s dest_url=%s"%(object,dest_url)) obj = self.restrictedTraverse(object) if obj.meta_type in diffable: obj_data = obj.document_src() else: obj_data = obj.data obj_id = obj.getId() # do xml-rpc serverconn = self._serverConn(self._check_http(dest_url)) try: result = serverconn.manage_editXMLRPC(self._encode(obj_data), obj.getPhysicalPath(), obj_id) except: try:self._exportXMLRPC(object, dest_url) except:return 500 return 200