entities/adapters.py
changeset 10974 6557833657d6
parent 10973 0939ad2edf63
child 11044 00c5ee272a6d
equal deleted inserted replaced
10973:0939ad2edf63 10974:6557833657d6
     1 # copyright 2010-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     1 # copyright 2010-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     3 #
     3 #
     4 # This file is part of CubicWeb.
     4 # This file is part of CubicWeb.
     5 #
     5 #
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
    16 # You should have received a copy of the GNU Lesser General Public License along
    16 # You should have received a copy of the GNU Lesser General Public License along
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    18 """some basic entity adapter implementations, for interfaces used in the
    18 """some basic entity adapter implementations, for interfaces used in the
    19 framework itself.
    19 framework itself.
    20 """
    20 """
    21 
       
    22 __docformat__ = "restructuredtext en"
       
    23 from cubicweb import _
    21 from cubicweb import _
    24 
    22 
    25 from itertools import chain
    23 from itertools import chain
    26 from warnings import warn
       
    27 from hashlib import md5
    24 from hashlib import md5
    28 
    25 
    29 from logilab.mtconverter import TransformError
    26 from logilab.mtconverter import TransformError
    30 from logilab.common.decorators import cached
    27 from logilab.common.decorators import cached
    31 
    28 
    32 from cubicweb import ValidationError, view, ViolatedConstraint
    29 from cubicweb import ValidationError, view, ViolatedConstraint, UniqueTogetherError
    33 from cubicweb.schema import CONSTRAINTS
       
    34 from cubicweb.predicates import is_instance, relation_possible, match_exception
    30 from cubicweb.predicates import is_instance, relation_possible, match_exception
    35 
    31 
    36 
    32 
    37 class IEmailableAdapter(view.EntityAdapter):
    33 class IEmailableAdapter(view.EntityAdapter):
    38     __regid__ = 'IEmailable'
    34     __regid__ = 'IEmailable'
    61         build email bodies.
    57         build email bodies.
    62 
    58 
    63         NOTE: the dictionary keys should match the list returned by the
    59         NOTE: the dictionary keys should match the list returned by the
    64         `allowed_massmail_keys` method.
    60         `allowed_massmail_keys` method.
    65         """
    61         """
    66         return dict( (attr, getattr(self.entity, attr))
    62         return dict((attr, getattr(self.entity, attr))
    67                      for attr in self.allowed_massmail_keys() )
    63                     for attr in self.allowed_massmail_keys())
    68 
    64 
    69 
    65 
    70 class INotifiableAdapter(view.EntityAdapter):
    66 class INotifiableAdapter(view.EntityAdapter):
    71     __regid__ = 'INotifiable'
    67     __regid__ = 'INotifiable'
    72     __select__ = is_instance('Any')
    68     __select__ = is_instance('Any')
   154                 words.setdefault(weight, []).extend(tokenize(value))
   150                 words.setdefault(weight, []).extend(tokenize(value))
   155         for rschema, role in entity.e_schema.fulltext_relations():
   151         for rschema, role in entity.e_schema.fulltext_relations():
   156             if role == 'subject':
   152             if role == 'subject':
   157                 for entity_ in getattr(entity, rschema.type):
   153                 for entity_ in getattr(entity, rschema.type):
   158                     merge_weight_dict(words, entity_.cw_adapt_to('IFTIndexable').get_words())
   154                     merge_weight_dict(words, entity_.cw_adapt_to('IFTIndexable').get_words())
   159             else: # if role == 'object':
   155             else:  # if role == 'object':
   160                 for entity_ in getattr(entity, 'reverse_%s' % rschema.type):
   156                 for entity_ in getattr(entity, 'reverse_%s' % rschema.type):
   161                     merge_weight_dict(words, entity_.cw_adapt_to('IFTIndexable').get_words())
   157                     merge_weight_dict(words, entity_.cw_adapt_to('IFTIndexable').get_words())
   162         return words
   158         return words
   163 
   159 
       
   160 
   164 def merge_weight_dict(maindict, newdict):
   161 def merge_weight_dict(maindict, newdict):
   165     for weight, words in newdict.items():
   162     for weight, words in newdict.items():
   166         maindict.setdefault(weight, []).extend(words)
   163         maindict.setdefault(weight, []).extend(words)
       
   164 
   167 
   165 
   168 class IDownloadableAdapter(view.EntityAdapter):
   166 class IDownloadableAdapter(view.EntityAdapter):
   169     """interface for downloadable entities"""
   167     """interface for downloadable entities"""
   170     __regid__ = 'IDownloadable'
   168     __regid__ = 'IDownloadable'
   171     __abstract__ = True
   169     __abstract__ = True
   172 
   170 
   173     def download_url(self, **kwargs): # XXX not really part of this interface
   171     def download_url(self, **kwargs):  # XXX not really part of this interface
   174         """return a URL to download entity's content
   172         """return a URL to download entity's content
   175 
   173 
   176         It should be a unicode object containing url-encoded ASCII."""
   174         It should be a unicode object containing url-encoded ASCII.
       
   175         """
   177         raise NotImplementedError
   176         raise NotImplementedError
   178 
   177 
   179     def download_content_type(self):
   178     def download_content_type(self):
   180         """return MIME type (unicode) of the downloadable content"""
   179         """return MIME type (unicode) of the downloadable content"""
   181         raise NotImplementedError
   180         raise NotImplementedError
   189         raise NotImplementedError
   188         raise NotImplementedError
   190 
   189 
   191     def download_data(self):
   190     def download_data(self):
   192         """return actual data (bytes) of the downloadable content"""
   191         """return actual data (bytes) of the downloadable content"""
   193         raise NotImplementedError
   192         raise NotImplementedError
       
   193 
   194 
   194 
   195 # XXX should propose to use two different relations for children/parent
   195 # XXX should propose to use two different relations for children/parent
   196 class ITreeAdapter(view.EntityAdapter):
   196 class ITreeAdapter(view.EntityAdapter):
   197     """This adapter provides a tree interface.
   197     """This adapter provides a tree interface.
   198 
   198 
   337                 break
   337                 break
   338             path.append(entity.eid)
   338             path.append(entity.eid)
   339             try:
   339             try:
   340                 # check we are not jumping to another tree
   340                 # check we are not jumping to another tree
   341                 if (adapter.tree_relation != self.tree_relation or
   341                 if (adapter.tree_relation != self.tree_relation or
   342                     adapter.child_role != self.child_role):
   342                         adapter.child_role != self.child_role):
   343                     break
   343                     break
   344                 entity = adapter.parent()
   344                 entity = adapter.parent()
   345                 adapter = entity.cw_adapt_to('ITree')
   345                 adapter = entity.cw_adapt_to('ITree')
   346             except AttributeError:
   346             except AttributeError:
   347                 break
   347                 break
   375         return data
   375         return data
   376 
   376 
   377 
   377 
   378 # error handling adapters ######################################################
   378 # error handling adapters ######################################################
   379 
   379 
   380 from cubicweb import UniqueTogetherError
       
   381 
   380 
   382 class IUserFriendlyError(view.EntityAdapter):
   381 class IUserFriendlyError(view.EntityAdapter):
   383     __regid__ = 'IUserFriendlyError'
   382     __regid__ = 'IUserFriendlyError'
   384     __abstract__ = True
   383     __abstract__ = True
   385 
   384 
   406 
   405 
   407 class IUserFriendlyCheckConstraint(IUserFriendlyError):
   406 class IUserFriendlyCheckConstraint(IUserFriendlyError):
   408     __select__ = match_exception(ViolatedConstraint)
   407     __select__ = match_exception(ViolatedConstraint)
   409 
   408 
   410     def raise_user_exception(self):
   409     def raise_user_exception(self):
   411         _ = self._cw._
       
   412         cstrname = self.exc.cstrname
   410         cstrname = self.exc.cstrname
   413         eschema = self.entity.e_schema
   411         eschema = self.entity.e_schema
   414         for rschema, attrschema in eschema.attribute_definitions():
   412         for rschema, attrschema in eschema.attribute_definitions():
   415             rdef = rschema.rdef(eschema, attrschema)
   413             rdef = rschema.rdef(eschema, attrschema)
   416             for constraint in rdef.constraints:
   414             for constraint in rdef.constraints:
   417                 if cstrname == 'cstr' + md5((eschema.type + rschema.type + constraint.type() + (constraint.serialize() or '')).encode('ascii')).hexdigest():
   415                 if cstrname == 'cstr' + md5(
       
   416                         (eschema.type + rschema.type + constraint.type() +
       
   417                          (constraint.serialize() or '')).encode('ascii')).hexdigest():
   418                     break
   418                     break
   419             else:
   419             else:
   420                 continue
   420                 continue
   421             break
   421             break
   422         else:
   422         else: