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, 5 months ago) by casties
Branches: MAIN
CVS tags: HEAD
more unicode fixes

    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 *
   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 allowed_objs_sync and 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(utf8ify(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>