__init__.py
changeset 0 b97547f5f1fa
child 231 d740f5f55d30
equal deleted inserted replaced
-1:000000000000 0:b97547f5f1fa
       
     1 """CubicWeb is a generic framework to quickly build applications which describes
       
     2 relations between entitites.
       
     3 
       
     4 :organization: Logilab
       
     5 :copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     6 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     7 :license: General Public License version 2 - http://www.gnu.org/licenses
       
     8 """
       
     9 __docformat__ = "restructuredtext en"
       
    10 from cubicweb.__pkginfo__ import version as __version__
       
    11 
       
    12 import __builtin__
       
    13 # '_' is available in builtins to mark internationalized string but should
       
    14 # not be used to do the actual translation
       
    15 if not hasattr(__builtin__, '_'):
       
    16     __builtin__._ = unicode
       
    17 
       
    18 CW_SOFTWARE_ROOT = __path__[0]
       
    19 
       
    20 import sys, os, logging
       
    21 from StringIO import StringIO
       
    22 from urllib import quote as urlquote, unquote as urlunquote
       
    23 
       
    24 from logilab.common.decorators import cached
       
    25 
       
    26 
       
    27 LLDEBUG = 5
       
    28 logging.addLevelName(LLDEBUG, 'LLDEBUG')
       
    29 
       
    30 class CubicWebLogger(logging.Logger):
       
    31 
       
    32     def lldebug(self, msg, *args, **kwargs):
       
    33         """
       
    34         Log 'msg % args' with severity 'DEBUG'.
       
    35 
       
    36         To pass exception information, use the keyword argument exc_info with
       
    37         a true value, e.g.
       
    38 
       
    39         logger.debug("Houston, we have a %s", "thorny problem", exc_info=1)
       
    40         """
       
    41         if self.manager.disable >= LLDEBUG:
       
    42             return
       
    43         if LLDEBUG >= self.getEffectiveLevel():
       
    44             self._log(LLDEBUG, msg, args, **kwargs)
       
    45 
       
    46 logging.setLoggerClass(CubicWebLogger)
       
    47 
       
    48 def set_log_methods(cls, logger):
       
    49     """bind standart logger's methods as static methods on the class
       
    50     """
       
    51     cls._logger = logger
       
    52     for attr in ('lldebug', 'debug', 'info', 'warning', 'error', 'critical', 'exception'):
       
    53         setattr(cls, attr, getattr(logger, attr))
       
    54 
       
    55 if os.environ.get('APYCOT_ROOT'):
       
    56     logging.basicConfig(level=logging.CRITICAL)
       
    57 else:
       
    58     logging.basicConfig()
       
    59 
       
    60 
       
    61 set_log_methods(sys.modules[__name__], logging.getLogger('cubicweb'))
       
    62 
       
    63 # make all exceptions accessible from the package
       
    64 from cubicweb._exceptions import *
       
    65 
       
    66 # convert eid to the right type, raise ValueError if it's not a valid eid
       
    67 typed_eid = int
       
    68 
       
    69 
       
    70 #def log_thread(f, w, a):
       
    71 #    print f.f_code.co_filename, f.f_code.co_name
       
    72 #import threading
       
    73 #threading.settrace(log_thread)
       
    74 
       
    75 class Binary(StringIO):
       
    76     """customize StringIO to make sure we don't use unicode"""
       
    77     def __init__(self, buf= ''):
       
    78         assert isinstance(buf, (str, buffer)), \
       
    79                "Binary objects must use raw strings, not %s" % buf.__class__
       
    80         StringIO.__init__(self, buf)
       
    81 
       
    82     def write(self, data):
       
    83         assert isinstance(data, (str, buffer)), \
       
    84                "Binary objects must use raw strings, not %s" % data.__class__
       
    85         StringIO.write(self, data)
       
    86 
       
    87 
       
    88 class RequestSessionMixIn(object):
       
    89     """mixin class containing stuff shared by server session and web request
       
    90     """
       
    91     def __init__(self, vreg):
       
    92         self.vreg = vreg
       
    93         try:
       
    94             encoding = vreg.property_value('ui.encoding')
       
    95         except: # no vreg or property not registered
       
    96             encoding = 'utf-8'
       
    97         self.encoding = encoding
       
    98         # cache result of execution for (rql expr / eids),
       
    99         # should be emptied on commit/rollback of the server session / web 
       
   100         # connection
       
   101         self.local_perm_cache = {}
       
   102 
       
   103     def property_value(self, key):
       
   104         if self.user:
       
   105             return self.user.property_value(key)
       
   106         return self.vreg.property_value(key)
       
   107     
       
   108     def etype_rset(self, etype, size=1):
       
   109         """return a fake result set for a particular entity type"""
       
   110         from cubicweb.rset import ResultSet
       
   111         rset = ResultSet([('A',)]*size, '%s X' % etype,
       
   112                          description=[(etype,)]*size)
       
   113         def get_entity(row, col=0, etype=etype, vreg=self.vreg, rset=rset):
       
   114             return self.vreg.etype_class(etype)(self, rset, row, col)
       
   115         rset.get_entity = get_entity
       
   116         return self.decorate_rset(rset)
       
   117 
       
   118     def eid_rset(self, eid, etype=None):
       
   119         """return a result set for the given eid without doing actual query
       
   120         (we have the eid, we can suppose it exists and user has access to the
       
   121         entity)
       
   122         """
       
   123         from cubicweb.rset import ResultSet
       
   124         eid = typed_eid(eid)
       
   125         if etype is None:
       
   126             etype = self.describe(eid)[0]
       
   127         rset = ResultSet([(eid,)], 'Any X WHERE X eid %(x)s', {'x': eid},
       
   128                          [(etype,)])
       
   129         return self.decorate_rset(rset)
       
   130 
       
   131     def entity_from_eid(self, eid, etype=None):
       
   132         rset = self.eid_rset(eid, etype)
       
   133         if rset:
       
   134             return rset.get_entity(0, 0)
       
   135         else:
       
   136             return None
       
   137 
       
   138     # url generation methods ##################################################
       
   139     
       
   140     def build_url(self, method, base_url=None, **kwargs):
       
   141         """return an absolute URL using params dictionary key/values as URL
       
   142         parameters. Values are automatically URL quoted, and the
       
   143         publishing method to use may be specified or will be guessed.
       
   144         """
       
   145         if base_url is None:
       
   146             base_url = self.base_url()
       
   147         if '_restpath' in kwargs:
       
   148             assert method == 'view', method
       
   149             path = kwargs.pop('_restpath')
       
   150         else:
       
   151             path = method
       
   152         if not kwargs:
       
   153             return u'%s%s' % (base_url, path)
       
   154         return u'%s%s?%s' % (base_url, path, self.build_url_params(**kwargs))
       
   155         
       
   156 
       
   157     def build_url_params(self, **kwargs):
       
   158         """return encoded params to incorporate them in an URL"""
       
   159         args = []
       
   160         for param, values in kwargs.items():
       
   161             if not isinstance(values, (list, tuple)):
       
   162                 values = (values,)
       
   163             for value in values:
       
   164                 args.append(u'%s=%s' % (param, self.url_quote(value)))
       
   165         return '&'.join(args)
       
   166 
       
   167     def url_quote(self, value, safe=''):
       
   168         """urllib.quote is not unicode safe, use this method to do the
       
   169         necessary encoding / decoding. Also it's designed to quote each
       
   170         part of a url path and so the '/' character will be encoded as well.
       
   171         """
       
   172         if isinstance(value, unicode):
       
   173             quoted = urlquote(value.encode(self.encoding), safe=safe)
       
   174             return unicode(quoted, self.encoding)
       
   175         return urlquote(str(value), safe=safe)
       
   176 
       
   177     def url_unquote(self, quoted):
       
   178         """returns a unicode unquoted string
       
   179         
       
   180         decoding is based on `self.encoding` which is the encoding
       
   181         used in `url_quote`
       
   182         """
       
   183         if isinstance(quoted, unicode):
       
   184             quoted = quoted.encode(self.encoding)
       
   185         try:
       
   186             return unicode(urlunquote(quoted), self.encoding)
       
   187         except UnicodeDecodeError: # might occurs on manually typed URLs
       
   188             return unicode(urlunquote(quoted), 'iso-8859-1')
       
   189     
       
   190 
       
   191     # session's user related methods #####################################
       
   192     
       
   193     @cached
       
   194     def user_data(self):
       
   195         """returns a dictionnary with this user's information"""
       
   196         userinfo = {}
       
   197         if self.is_internal_session:
       
   198             userinfo['login'] = "cubicweb"
       
   199             userinfo['name'] = "cubicweb"
       
   200             userinfo['email'] = ""
       
   201             return userinfo
       
   202         user = self.actual_session().user
       
   203         rql = "Any F,S,A where U eid %(x)s, U firstname F, U surname S, U primary_email E, E address A"
       
   204         try:
       
   205             firstname, lastname, email = self.execute(rql, {'x': user.eid}, 'x')[0]
       
   206             if firstname is None and lastname is None:
       
   207                 userinfo['name'] = ''
       
   208             else:
       
   209                 userinfo['name'] = ("%s %s" % (firstname, lastname))
       
   210             userinfo['email'] = email
       
   211         except IndexError:
       
   212             userinfo['name'] = None
       
   213             userinfo['email'] = None
       
   214         userinfo['login'] = user.login
       
   215         return userinfo
       
   216 
       
   217     def is_internal_session(self):
       
   218         """overrided on the server-side"""
       
   219         return False
       
   220 
       
   221     # abstract methods to override according to the web front-end #############
       
   222     
       
   223     def base_url(self):
       
   224         """return the root url of the application"""
       
   225         raise NotImplementedError
       
   226     
       
   227     def decorate_rset(self, rset):
       
   228         """add vreg/req (at least) attributes to the given result set """
       
   229         raise NotImplementedError
       
   230     
       
   231     def describe(self, eid):
       
   232         """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
       
   233         raise NotImplementedError
       
   234         
       
   235 
       
   236 # XXX 2.45 is allowing nicer entity type names, use this map for bw compat    
       
   237 ETYPE_NAME_MAP = {'Eetype': 'EEType',
       
   238                   'Ertype': 'ERType',
       
   239                   'Efrdef': 'EFRDef',
       
   240                   'Enfrdef': 'ENFRDef',
       
   241                   'Econstraint': 'EConstraint',
       
   242                   'Econstrainttype': 'EConstraintType',
       
   243                   'Epermission': 'EPermission',
       
   244                   'Egroup': 'EGroup',
       
   245                   'Euser': 'EUser',
       
   246                   'Eproperty': 'EProperty',
       
   247                   'Emailaddress': 'EmailAddress',
       
   248                   'Rqlexpression': 'RQLExpression',
       
   249                   'Trinfo': 'TrInfo',
       
   250                   }
       
   251 
       
   252 
       
   253 
       
   254 # XXX cubic web cube migration map
       
   255 CW_MIGRATION_MAP = {'erudi': 'cubicweb',
       
   256 
       
   257                     'eaddressbook': 'addressbook',
       
   258                     'ebasket': 'basket',
       
   259                     'eblog': 'blog',
       
   260                     'ebook': 'book',
       
   261                     'ecomment': 'comment',
       
   262                     'ecompany': 'company',
       
   263                     'econference':  'conference',
       
   264                     'eemail': 'email',
       
   265                     'eevent': 'event',
       
   266                     'eexpense': 'expense',
       
   267                     'efile': 'file',
       
   268                     'einvoice': 'invoice',
       
   269                     'elink': 'link',
       
   270                     'emailinglist': 'mailinglist',
       
   271                     'eperson': 'person',
       
   272                     'eshopcart': 'shopcart',
       
   273                     'eskillmat': 'skillmat',
       
   274                     'etask': 'task',
       
   275                     'eworkcase': 'workcase',
       
   276                     'eworkorder': 'workorder',
       
   277                     'ezone': 'zone',
       
   278                     'i18ncontent': 'i18ncontent',
       
   279                     'svnfile': 'vcsfile',
       
   280                     
       
   281                     'eclassschemes': 'keyword',
       
   282                     'eclassfolders': 'folder',
       
   283                     'eclasstags': 'tag',
       
   284 
       
   285                     'jpl': 'jpl',
       
   286                     'jplintra': 'jplintra',
       
   287                     'jplextra': 'jplextra',
       
   288                     'jplorg': 'jplorg',
       
   289                     'jplrecia': 'jplrecia',
       
   290                     'crm': 'crm',
       
   291                     'agueol': 'agueol',
       
   292                     'docaster': 'docaster',
       
   293                     'asteretud': 'asteretud',
       
   294                     
       
   295                     # XXX temp
       
   296                     'keywords': 'keyword',
       
   297                     'folders': 'folder',
       
   298                     'tags': 'tag',
       
   299                     }