# -*- coding: utf-8 -*- ############################################################################## # # Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved. # Danièle Vanbaelinghem <daniele@nexedi.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential # consequences resulting from its eventual inadequacies and bugs # End users who are looking for a ready-to-use solution with commercial # garantees and support are strongly adviced to contract a Free Software # Service Company # # This program is Free Software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## from hashlib import md5 from AccessControl import ClassSecurityInfo from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type import Permissions from Products.ERP5Type import PropertySheet from erp5.component.module.SyncMLUtils import PdataHelper from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter _MARKER = object() class SyncMLSignature(XMLObject): """ A Signature represent a document that is synchronized It contains as attribute the xml representation of the object which is used to generate the diff of the object between two synchronization It also contains the list of conflict as sub-objects when it happens """ meta_type = 'ERP5 Signature' portal_type = 'SyncML Signature' isIndexable = ConstantGetter('isIndexable', value=False) security = ClassSecurityInfo() security.declareObjectProtected(Permissions.AccessContentsInformation) # Declarative properties property_sheets = ( PropertySheet.Base , PropertySheet.XMLObject , PropertySheet.CategoryCore , PropertySheet.DublinCore , PropertySheet.Reference , PropertySheet.Data , PropertySheet.Document , PropertySheet.SyncMLSignature ) security.declareProtected(Permissions.ModifyPortalContent, 'synchronize') def synchronize(self): """ This is call when subscription get confirmation of the data synchronization This copy & reset some properties if needed """ edit_kw = {} temporary_data = self.getTemporaryData() if temporary_data is not None: # This happens when we have sent the xml # and we just get the confirmation self.setData(temporary_data) edit_kw["temporary_data"] = None if self.isForce(): edit_kw["force"] = False if self.hasPartialData(): edit_kw["partial_data"] = None if self.hasSubscriberXupdate(): edit_kw["subscriber_xupdate"] = None if self.hasPublisherXupdate(): edit_kw["publisher_xupdate"] = None if len(edit_kw): self.edit(**edit_kw) security.declareProtected(Permissions.ModifyPortalContent, 'setData') def setData(self, value): """ Set the XML corresponding to the object """ if value: # convert the string to Pdata pdata_wrapper = PdataHelper(self.getPortalObject(), value) self._setData(pdata_wrapper) self.setTemporaryData(None) # We make sure that the data will not be erased self.setContentMd5(pdata_wrapper.getContentMd5()) else: self._setData(None) self.setContentMd5(None) security.declareProtected(Permissions.AccessContentsInformation, 'getData') def getData(self, default=_MARKER): """ Get the XML corresponding to the object """ if self.hasData(): return str(self._baseGetData()) elif default is _MARKER: return self._baseGetData() else: return self._baseGetData(default) security.declareProtected(Permissions.ModifyPortalContent, 'setTemporaryData') def setTemporaryData(self, value): """ This is the xml temporarily saved, it will be stored with setXML when we will receive the confirmation of synchronization """ if value: self._setTemporaryData(PdataHelper(self.getPortalObject(), value)) else: self._setTemporaryData(None) security.declareProtected(Permissions.AccessContentsInformation, 'getTemporaryData') def getTemporaryData(self, default=_MARKER): """ Return the temp xml as string """ if self.hasTemporaryData(): return str(self._baseGetTemporaryData()) elif default is _MARKER: return self._baseGetTemporaryData() else: return self._baseGetTemporaryData(default) security.declareProtected(Permissions.AccessContentsInformation, 'checkMD5') def checkMD5(self, xml_string): """ Check if the given md5_object returns the same things as the one stored in this signature, this is very usefull if we want to know if an objects has changed or not Returns 1 if MD5 are equals, else it returns 0 """ if isinstance(xml_string, unicode): xml_string = xml_string.encode('utf-8') return md5(xml_string).hexdigest() == self.getContentMd5() security.declareProtected(Permissions.ModifyPortalContent, 'setPartialData') def setPartialData(self, value): """ Set the partial string we will have to deliver in the future """ if value: if not isinstance(value, PdataHelper): value = PdataHelper(self.getPortalObject(), value) self._setPartialData(value) self.setLastDataPartialData(value.getLastPdata()) else: self._setPartialData(None) self.setLastDataPartialData(None) security.declareProtected(Permissions.ModifyPortalContent, 'setLastData') def setLastData(self, value): """ This is the xml temporarily saved, it will be stored with setXML when we will receive the confirmation of synchronization """ if value: self._setLastData(PdataHelper(self.getPortalObject(), value)) else: self._setLastData(None) security.declareProtected(Permissions.AccessContentsInformation, 'getPartialData') def getPartialData(self, default=_MARKER): """ Return the patial xml as string """ if self.hasPartialData(): return str(self._baseGetPartialData()) elif default is _MARKER: return self._baseGetPartialData() else: return self._baseGetPartialData(default) security.declareProtected(Permissions.ModifyPortalContent, 'appendPartialData') def appendPartialData(self, value): """ Append the partial string we will have to deliver in the future """ if value: if not isinstance(value, PdataHelper): value = PdataHelper(self.getPortalObject(), value) last_data = value.getLastPdata() if self.hasLastDataPartialData(): last_data_partial_data = self.getLastDataPartialData() last_data_partial_data.next = value._data self.setLastDataPartialData(last_data_partial_data) else: self.setPartialData(value) self.setLastDataPartialData(last_data) security.declareProtected(Permissions.ModifyPortalContent, 'getFirstPdataChunk') def getFirstPdataChunk(self, max_len): """ """ partial_data = self._baseGetPartialData() chunk = partial_data[:max_len] rest_in_queue = partial_data[max_len:] if rest_in_queue is not None: self.setPartialData(rest_in_queue) return str(chunk) security.declareProtected(Permissions.ModifyPortalContent, 'setSubscriberXupdate') def setSubscriberXupdate(self, value): """ This is the xml temporarily saved, it will be stored with setXML when we will receive the confirmation of synchronization """ if value: self._setSubscriberXupdate(PdataHelper(self.getPortalObject(), value)) else: self._setSubscriberXupdate(None) security.declareProtected(Permissions.AccessContentsInformation, 'getSubscriberXupdate') def getSubscriberXupdate(self, default=_MARKER): """ Return the patial xml as string """ if self.hasSubscriberXupdate(): return str(self._baseGetSubscriberXupdate()) elif default is _MARKER: return self._baseGetSubscriberXupdate() else: return self._baseGetSubscriberXupdate(default) security.declareProtected(Permissions.ModifyPortalContent, 'setPublisherXupdate') def setPublisherXupdate(self, value): """ This is the xml temporarily saved, it will be stored with setXML when we will receive the confirmation of synchronization """ if value: self._setPublisherXupdate(PdataHelper(self.getPortalObject(), value)) else: self._setPublisherXupdate(None) security.declareProtected(Permissions.AccessContentsInformation, 'getPublisherXupdate') def getPublisherXupdate(self, default=_MARKER): """ Return the partial xml as string """ if self.hasPublisherXupdate(): return str(self._baseGetPublisherXupdate()) elif default is _MARKER: return self._baseGetPublisherXupdate() else: return self._baseGetPublisherXupdate(default) security.declareProtected(Permissions.ModifyPortalContent, 'getConflictList') def getConflictList(self): """ Return the actual action for a partial synchronization """ return self.contentValues() security.declareProtected(Permissions.ModifyPortalContent, 'delConflict') def delConflict(self, conflict): """ Delete provided conflict object """ self.manage_delObjects([conflict.getId(),])