VCardConduit.py 8.57 KB
Newer Older
1
# -*- coding: utf-8 -*-
2 3
##############################################################################
#
Fabien Morin's avatar
Fabien Morin committed
4
# Copyright (c) 2007 Nexedi SARL and Contributors. All Rights Reserved.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
#          Fabien Morin <fabien.morin@gmail.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 Products.ERP5SyncML.Conduit.ERP5Conduit import ERP5Conduit
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
33
import difflib
34 35 36

from zLOG import LOG

37
class VCardConduit(ERP5Conduit):
38 39 40
  """
  A conduit is in charge to read data from a particular structure,
  and then to save this data in another structure.
Nicolas Delaby's avatar
Nicolas Delaby committed
41

42
  VCardConduit is a piece of code to update VCards from text stream
43 44 45 46 47
  """


  # Declarative security
  security = ClassSecurityInfo()
Nicolas Delaby's avatar
Nicolas Delaby committed
48

49

Fabien Morin's avatar
Fabien Morin committed
50
  security.declareProtected(Permissions.ModifyPortalContent, 'addNode')
Nicolas Delaby's avatar
Nicolas Delaby committed
51
  def addNode(self, xml=None, object=None, previous_xml=None,
52 53 54 55 56
      object_id=None, sub_object=None, force=0, simulate=0, **kw):
    """
    add a new person corresponding to the vcard
    if the person already exist, she's updated
    """
57 58
    #LOG('VCardConduit',0,'addNode, object=%s, object_id=%s, sub_object:%s, \
        #xml:\n%s' % (str(object), str(object_id), str(sub_object), xml))
59 60
    if not isinstance(xml, str):
      xml = self.nodeToString(xml)
61
    portal_type = 'Person' #the VCard can just use Person
Nicolas Delaby's avatar
Nicolas Delaby committed
62
    if sub_object is None:
63

64 65
      new_object, reset_local_roles, reset_workflow =\
        ERP5Conduit.constructContent(self, object, object_id, portal_type)
66
    else: #if the object exist, it juste must be update
Nicolas Delaby's avatar
Nicolas Delaby committed
67
      new_object = sub_object
68 69 70 71
    #LOG('addNode', 0, 'new_object:%s, sub_object:%s' % (new_object, sub_object)) 
    self.updateNode(xml=xml,
                    object=new_object,
                    force=force,
72 73 74
                    simulate=simulate,
                    **kw)
    #in a first time, conflict are not used
75
    return {'conflict_list':[], 'object': new_object}
76

Fabien Morin's avatar
Fabien Morin committed
77
  security.declareProtected(Permissions.ModifyPortalContent, 'deleteNode')
78
  def deleteNode(self, xml=None, object=None, object_id=None, force=None,
79 80 81 82
      simulate=0, **kw):
    """
    A node is deleted
    """
Nicolas Delaby's avatar
Nicolas Delaby committed
83
    #LOG('deleteNode :', 0, 'object:%s, object_id:%s' % (str(object), str(object_id)))
84 85 86 87
    try:
      object._delObject(object_id)
    except (AttributeError, KeyError):
      LOG('VCardConduit',0,'deleteNode, Unable to delete: %s' % str(object_id))
88
    return []
89

Fabien Morin's avatar
Fabien Morin committed
90
  security.declareProtected(Permissions.ModifyPortalContent, 'updateNode')
91 92 93 94 95
  def updateNode(self, xml=None, object=None, previous_xml=None, force=0, 
      simulate=0,  **kw):
    """
    A node is updated
    """
Nicolas Delaby's avatar
Nicolas Delaby committed
96
    #LOG('updateNode :',0, 'xml:%s, object:%s, previous_xml:%s, force:%s,simulate:%s, kw:%s' % (xml, object, previous_xml, force, simulate, kw))
97 98 99 100 101 102 103 104
    vcard_dict = self.vcard2Dict(xml)
    object.edit(**vcard_dict)
    return []

  def getCapabilitiesCTTypeList(self):
    """
    return the a list of CTType capabilities supported
    """
105
    return ('text/xml', 'text/vcard', 'text/x-vcard',)
106 107 108 109 110 111 112

  def getCapabilitiesVerCTList(self, capabilities_ct_type):
    """
    return a list of version of the CTType supported
    """
    #add here the other version supported
    verCTTypeList = {}
113 114
    verCTTypeList['text/vcard'] = ('3.0',)
    verCTTypeList['text/x-vcard'] = ('2.1',)
115 116 117 118 119 120 121 122 123 124 125 126 127
    return verCTTypeList[capabilities_ct_type]

  def getPreferedCapabilitieVerCT(self):
    """
    return the prefered capabilitie VerCT
    """
    prefered_version = '2.1'
    return prefered_version

  def getPreferedCapabilitieCTType(self):
    """
    return the prefered capabilitie VerCT
    """
128
    prefered_type = 'text/x-vcard'
129 130 131 132 133 134 135 136 137 138
    return prefered_type
  
  def changePropertyEncoding(self, property_parameters_list, 
      property_value_list):
    """
    if there is a property 'ENCODING', change the string encoding to utf-8
    """
    encoding=''

    for item in property_parameters_list :
139
      if ENCODING in item:
140 141 142 143 144 145 146 147 148 149
        encoding = item['ENCODING']

    property_value_list_well_incoded=[]
    if encoding == 'QUOTED-PRINTABLE':
      import mimify
      for property_value in property_value_list:
        property_value = mimify.mime_decode(property_value)
        property_value_list_well_incoded.append(property_value)
    #elif ... put here the other encodings
    else:
150
      property_value_list_well_incoded = property_value_list
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

    return property_value_list_well_incoded

  def vcard2Dict(self, vcard):
    """
    transalate the vcard to a dict understandable by erp5 like
    {'fisrt_name':'MORIN', 'last_name':'Fabien'}
    """
    #LOG('vcard =',0,vcard)
    convert_dict = {}
    convert_dict['FN'] = 'first_name'
    convert_dict['N'] = 'last_name'
    convert_dict['TEL'] = 'default_telephone_text'
    edit_dict = {}
    vcard_list = vcard.split('\n')
    for vcard_line in vcard_list:
      if ':' in vcard_line:
        property, property_value = vcard_line.split(':')
169
        property_value_list = property_value.split(';')
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
        property_parameters_list = []
        property_name = ''
        if ';' in property:
          property_list = property.split(';')
          property_name = property_list[0] #the property name is the 1st element
          if len(property_list) > 1 and property_list[1] != '':
            property_parameters_list = property_list[1:len(property_list)]
            tmp = []
            for property_parameter in property_parameters_list:
              if '=' in property_parameter:
                property_parameter_name, property_parameter_value = \
                    property_parameter.split('=')
              else:
                property_parameter_name = property_parameter
                property_parameter_value = None
              tmp.append({property_parameter_name:property_parameter_value})
            property_parameters_list = tmp
            #now property_parameters_list looks like :
            # [{'ENCODING':'QUOTED-PRINTABLE'}, {'CHARSET':'UTF-8'}]
189

190
            property_value_list = \
191 192
                self.changePropertyEncoding(property_parameters_list,
                                            property_value_list)
193 194 195

        else:
          property_name=property
196
        if isinstance(property_name, unicode):
197 198
          property_name = property_name.encode('utf-8')

199
        tmp = []
200
        for property_value in property_value_list:
201
          if isinstance(property_value, unicode):
202 203
            property_value = property_value.encode('utf-8')
          tmp.append(property_value)
204
        property_value_list = tmp
205 206
        if property_name in convert_dict.keys():
          if property_name == 'N' and len(property_value_list) > 1:
207 208
            edit_dict[convert_dict['N']] = property_value_list[0]
            edit_dict[convert_dict['FN']] = property_value_list[1]
209
          else:
210
            edit_dict[convert_dict[property_name]] = property_value_list[0]
211 212 213
    #LOG('edit_dict =',0,edit_dict)
    return edit_dict

214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
  security.declareProtected(Permissions.ModifyPortalContent,
                            'replaceIdFromXML')
  def replaceIdFromXML(self, xml, attribute_name, new_id, as_string=True):
    """
      Return the Same vlue
    """
    return xml

  def getContentType(self):
    """Content-Type of binded data
    """
    return 'text/vcard'

  def generateDiff(self, old_data, new_data):
    """return unified diff for plain-text documents
    """
    diff = '\n'.join(difflib.unified_diff(old_data.splitlines(),
                                          new_data.splitlines()))
    return diff


  def applyDiff(self, original_data, diff):
    """Use difflib to patch original_data
    """
    raise NotImplementedError('patch unified diff')