module.erp5.VCardConduit.py 8.92 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
#          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.
#
##############################################################################

30
from erp5.component.module.ERP5Conduit import ERP5Conduit
31 32
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
33
import difflib
Jérome Perrin's avatar
Jérome Perrin committed
34
import six
35 36 37

from zLOG import LOG

38
class VCardConduit(ERP5Conduit):
39 40 41
  """
  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
42

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


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

50

Fabien Morin's avatar
Fabien Morin committed
51
  security.declareProtected(Permissions.ModifyPortalContent, 'addNode')
52
  def addNode(self, xml=None, object=None, previous_xml=None, # pylint: disable=redefined-builtin
53 54 55 56 57
      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
    """
58 59
    #LOG('VCardConduit',0,'addNode, object=%s, object_id=%s, sub_object:%s, \
        #xml:\n%s' % (str(object), str(object_id), str(sub_object), xml))
Jérome Perrin's avatar
Jérome Perrin committed
60
    if not isinstance(xml, bytes):
61
      xml = self.nodeToString(xml)
62
    portal_type = 'Person' #the VCard can just use Person
Nicolas Delaby's avatar
Nicolas Delaby committed
63
    if sub_object is None:
64

65
      new_object, _, _ = 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
Aurel's avatar
Aurel committed
68
    #LOG('addNode', 0, 'new_object:%s, sub_object:%s' % (new_object, sub_object))
69 70 71
    self.updateNode(xml=xml,
                    object=new_object,
                    force=force,
72 73 74
                    simulate=simulate,
                    **kw)
    #in a first time, conflict are not used
Nicolas Delaby's avatar
Nicolas Delaby committed
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, # pylint: disable=redefined-builtin
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))
Nicolas Delaby's avatar
Nicolas Delaby committed
88
    return []
89

Fabien Morin's avatar
Fabien Morin committed
90
  security.declareProtected(Permissions.ModifyPortalContent, 'updateNode')
91
  def updateNode(self, xml=None, object=None, previous_xml=None, force=0, # pylint: disable=redefined-builtin
92 93 94 95
      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
    return prefered_type
Aurel's avatar
Aurel committed
130 131

  def changePropertyEncoding(self, property_parameters_list,
132 133 134 135 136 137
      property_value_list):
    """
    if there is a property 'ENCODING', change the string encoding to utf-8
    """
    encoding=''

Aurel's avatar
Aurel committed
138 139 140
#    for item in property_parameters_list :
#      if ENCODING in item:
#        encoding = item['ENCODING']
141 142 143 144 145 146 147 148 149

    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:
Nicolas Delaby's avatar
Nicolas Delaby committed
150
      property_value_list_well_incoded = property_value_list
151 152 153 154 155 156 157 158 159 160 161 162 163 164

    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 = {}
Jérome Perrin's avatar
Jérome Perrin committed
165 166 167
    if isinstance(vcard, bytes):
      vcard = vcard.decode('utf-8')
    vcard_list = vcard.splitlines()
168 169
    for vcard_line in vcard_list:
      if ':' in vcard_line:
170
        property_, property_value = vcard_line.split(':')
Nicolas Delaby's avatar
Nicolas Delaby committed
171
        property_value_list = property_value.split(';')
172 173
        property_parameters_list = []
        property_name = ''
174 175
        if ';' in property_:
          property_list = property_.split(';')
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
          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'}]
Nicolas Delaby's avatar
Nicolas Delaby committed
191

192
            property_value_list = \
Nicolas Delaby's avatar
Nicolas Delaby committed
193 194
                self.changePropertyEncoding(property_parameters_list,
                                            property_value_list)
195 196

        else:
197
          property_name=property_
Jérome Perrin's avatar
Jérome Perrin committed
198
        if six.PY2 and isinstance(property_name, six.text_type):
199 200
          property_name = property_name.encode('utf-8')

Nicolas Delaby's avatar
Nicolas Delaby committed
201
        tmp = []
202
        for property_value in property_value_list:
Jérome Perrin's avatar
Jérome Perrin committed
203
          if six.PY2 and isinstance(property_value, six.text_type):
204 205
            property_value = property_value.encode('utf-8')
          tmp.append(property_value)
Nicolas Delaby's avatar
Nicolas Delaby committed
206
        property_value_list = tmp
207 208
        if property_name in convert_dict.keys():
          if property_name == 'N' and len(property_value_list) > 1:
Nicolas Delaby's avatar
Nicolas Delaby committed
209 210
            edit_dict[convert_dict['N']] = property_value_list[0]
            edit_dict[convert_dict['FN']] = property_value_list[1]
211
          else:
Nicolas Delaby's avatar
Nicolas Delaby committed
212
            edit_dict[convert_dict[property_name]] = property_value_list[0]
213 214 215
    #LOG('edit_dict =',0,edit_dict)
    return edit_dict

216 217 218 219 220 221 222 223 224 225 226 227 228
  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'

Aurel's avatar
Aurel committed
229
  def generateDiff(self, new_data, former_data):
230 231
    """return unified diff for plain-text documents
    """
Jérome Perrin's avatar
Jérome Perrin committed
232 233 234 235
    if isinstance(new_data, bytes):
      new_data = new_data.decode('utf-8')
    if isinstance(former_data, bytes):
      former_data = former_data.decode('utf-8')
Aurel's avatar
Aurel committed
236 237
    diff = '\n'.join(difflib.unified_diff(new_data.splitlines(),
                                          former_data.splitlines()))
238 239 240 241 242 243 244
    return diff


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