File:  [Repository] / VSyncer / VSyncer.py
Revision 1.4: download - view: text, annotated - select for diffs - revision graph
Fri Jan 4 15:34:43 2008 UTC (16 years, 4 months ago) by casties
Branches: MAIN
CVS tags: HEAD
more unicode fixes

# 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 *
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 <br>'
        else:
            msg = 'Please update allowed_contexts list in config.py file '
            
        for path in allowed_contexts:
            msg += path + '\n <br>'
        #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("<tr>\n"
                 "<td><pre>\n%s\n</pre></td>\n"
                 "<td><pre>\n%s\n</pre></td>\n"
                 "</tr>\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 '<table><tr><td>' + from_source + '</td>' + '<td>' + from_dest + '</td></tr></table>'

    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=['<table border=1 width="100%"> ']
        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("<tr>\n"
                         "<td ><pre>\n%s\n</pre></td>\n"
                         "<td ><pre>\n%s\n</pre></td>\n"
                         "</tr>\n"
                         % ( '\n'.join(rx1),
                            escape('\n'.join(rx2))))
                
                ry1=[]
                ry2=[]
                for i in xrange(ylo, yhi):
                    ry1.append('+')
                    ry2.append(y[i])


                r.append("<tr>\n"
                         "<td bgcolor=%s style='color:white;'><pre>\n%s\n</pre></td>\n"
                         "<td bgcolor=%s style='color:white;'><pre>\n%s\n</pre></td>\n"
                         "</tr>\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("<tr>\n"
                 "<td ><pre>\n%s\n</pre></td>\n"
                 "<td ><pre>\n%s\n</pre></td>\n"
                 "</tr>\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("<tr>\n"
                 "<td bgcolor=%s style='color:white;'><pre>\n%s\n</pre></td>\n"
                 "<td bgcolor=%s style='color:white;'><pre>\n%s\n</pre></td>\n"
                 "</tr>\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('</table>')
 
        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 <b>%s</b> to <b>%s</b>' % (ob, self.dest_server))
                msgs.append(self._editXMLRPC(ob, self.dest_server))
            except:
                msgs.append('<font color=red>object %s not found in source server</font>' % (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(utf8ify(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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>