Annotation of VSyncer/VSyncer.py, revision 1.2

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)
                    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>