# 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%s\n | \n"
"\n%s\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%s\n | \n"
"\n%s\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%s\n | \n"
"\n%s\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%s\n | \n"
"\n%s\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('
')
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 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