--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/__init__.py Wed Nov 05 15:52:50 2008 +0100
@@ -0,0 +1,299 @@
+"""CubicWeb is a generic framework to quickly build applications which describes
+relations between entitites.
+
+:organization: Logilab
+:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: General Public License version 2 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
+from cubicweb.__pkginfo__ import version as __version__
+
+import __builtin__
+# '_' is available in builtins to mark internationalized string but should
+# not be used to do the actual translation
+if not hasattr(__builtin__, '_'):
+ __builtin__._ = unicode
+
+CW_SOFTWARE_ROOT = __path__[0]
+
+import sys, os, logging
+from StringIO import StringIO
+from urllib import quote as urlquote, unquote as urlunquote
+
+from logilab.common.decorators import cached
+
+
+LLDEBUG = 5
+logging.addLevelName(LLDEBUG, 'LLDEBUG')
+
+class CubicWebLogger(logging.Logger):
+
+ def lldebug(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'DEBUG'.
+
+ To pass exception information, use the keyword argument exc_info with
+ a true value, e.g.
+
+ logger.debug("Houston, we have a %s", "thorny problem", exc_info=1)
+ """
+ if self.manager.disable >= LLDEBUG:
+ return
+ if LLDEBUG >= self.getEffectiveLevel():
+ self._log(LLDEBUG, msg, args, **kwargs)
+
+logging.setLoggerClass(CubicWebLogger)
+
+def set_log_methods(cls, logger):
+ """bind standart logger's methods as static methods on the class
+ """
+ cls._logger = logger
+ for attr in ('lldebug', 'debug', 'info', 'warning', 'error', 'critical', 'exception'):
+ setattr(cls, attr, getattr(logger, attr))
+
+if os.environ.get('APYCOT_ROOT'):
+ logging.basicConfig(level=logging.CRITICAL)
+else:
+ logging.basicConfig()
+
+
+set_log_methods(sys.modules[__name__], logging.getLogger('cubicweb'))
+
+# make all exceptions accessible from the package
+from cubicweb._exceptions import *
+
+# convert eid to the right type, raise ValueError if it's not a valid eid
+typed_eid = int
+
+
+#def log_thread(f, w, a):
+# print f.f_code.co_filename, f.f_code.co_name
+#import threading
+#threading.settrace(log_thread)
+
+class Binary(StringIO):
+ """customize StringIO to make sure we don't use unicode"""
+ def __init__(self, buf= ''):
+ assert isinstance(buf, (str, buffer)), \
+ "Binary objects must use raw strings, not %s" % buf.__class__
+ StringIO.__init__(self, buf)
+
+ def write(self, data):
+ assert isinstance(data, (str, buffer)), \
+ "Binary objects must use raw strings, not %s" % data.__class__
+ StringIO.write(self, data)
+
+
+class RequestSessionMixIn(object):
+ """mixin class containing stuff shared by server session and web request
+ """
+ def __init__(self, vreg):
+ self.vreg = vreg
+ try:
+ encoding = vreg.property_value('ui.encoding')
+ except: # no vreg or property not registered
+ encoding = 'utf-8'
+ self.encoding = encoding
+ # cache result of execution for (rql expr / eids),
+ # should be emptied on commit/rollback of the server session / web
+ # connection
+ self.local_perm_cache = {}
+
+ def property_value(self, key):
+ if self.user:
+ return self.user.property_value(key)
+ return self.vreg.property_value(key)
+
+ def etype_rset(self, etype, size=1):
+ """return a fake result set for a particular entity type"""
+ from cubicweb.rset import ResultSet
+ rset = ResultSet([('A',)]*size, '%s X' % etype,
+ description=[(etype,)]*size)
+ def get_entity(row, col=0, etype=etype, vreg=self.vreg, rset=rset):
+ return self.vreg.etype_class(etype)(self, rset, row, col)
+ rset.get_entity = get_entity
+ return self.decorate_rset(rset)
+
+ def eid_rset(self, eid, etype=None):
+ """return a result set for the given eid without doing actual query
+ (we have the eid, we can suppose it exists and user has access to the
+ entity)
+ """
+ from cubicweb.rset import ResultSet
+ eid = typed_eid(eid)
+ if etype is None:
+ etype = self.describe(eid)[0]
+ rset = ResultSet([(eid,)], 'Any X WHERE X eid %(x)s', {'x': eid},
+ [(etype,)])
+ return self.decorate_rset(rset)
+
+ def entity_from_eid(self, eid, etype=None):
+ rset = self.eid_rset(eid, etype)
+ if rset:
+ return rset.get_entity(0, 0)
+ else:
+ return None
+
+ # url generation methods ##################################################
+
+ def build_url(self, method, base_url=None, **kwargs):
+ """return an absolute URL using params dictionary key/values as URL
+ parameters. Values are automatically URL quoted, and the
+ publishing method to use may be specified or will be guessed.
+ """
+ if base_url is None:
+ base_url = self.base_url()
+ if '_restpath' in kwargs:
+ assert method == 'view', method
+ path = kwargs.pop('_restpath')
+ else:
+ path = method
+ if not kwargs:
+ return u'%s%s' % (base_url, path)
+ return u'%s%s?%s' % (base_url, path, self.build_url_params(**kwargs))
+
+
+ def build_url_params(self, **kwargs):
+ """return encoded params to incorporate them in an URL"""
+ args = []
+ for param, values in kwargs.items():
+ if not isinstance(values, (list, tuple)):
+ values = (values,)
+ for value in values:
+ args.append(u'%s=%s' % (param, self.url_quote(value)))
+ return '&'.join(args)
+
+ def url_quote(self, value, safe=''):
+ """urllib.quote is not unicode safe, use this method to do the
+ necessary encoding / decoding. Also it's designed to quote each
+ part of a url path and so the '/' character will be encoded as well.
+ """
+ if isinstance(value, unicode):
+ quoted = urlquote(value.encode(self.encoding), safe=safe)
+ return unicode(quoted, self.encoding)
+ return urlquote(str(value), safe=safe)
+
+ def url_unquote(self, quoted):
+ """returns a unicode unquoted string
+
+ decoding is based on `self.encoding` which is the encoding
+ used in `url_quote`
+ """
+ if isinstance(quoted, unicode):
+ quoted = quoted.encode(self.encoding)
+ try:
+ return unicode(urlunquote(quoted), self.encoding)
+ except UnicodeDecodeError: # might occurs on manually typed URLs
+ return unicode(urlunquote(quoted), 'iso-8859-1')
+
+
+ # session's user related methods #####################################
+
+ @cached
+ def user_data(self):
+ """returns a dictionnary with this user's information"""
+ userinfo = {}
+ if self.is_internal_session:
+ userinfo['login'] = "cubicweb"
+ userinfo['name'] = "cubicweb"
+ userinfo['email'] = ""
+ return userinfo
+ user = self.actual_session().user
+ rql = "Any F,S,A where U eid %(x)s, U firstname F, U surname S, U primary_email E, E address A"
+ try:
+ firstname, lastname, email = self.execute(rql, {'x': user.eid}, 'x')[0]
+ if firstname is None and lastname is None:
+ userinfo['name'] = ''
+ else:
+ userinfo['name'] = ("%s %s" % (firstname, lastname))
+ userinfo['email'] = email
+ except IndexError:
+ userinfo['name'] = None
+ userinfo['email'] = None
+ userinfo['login'] = user.login
+ return userinfo
+
+ def is_internal_session(self):
+ """overrided on the server-side"""
+ return False
+
+ # abstract methods to override according to the web front-end #############
+
+ def base_url(self):
+ """return the root url of the application"""
+ raise NotImplementedError
+
+ def decorate_rset(self, rset):
+ """add vreg/req (at least) attributes to the given result set """
+ raise NotImplementedError
+
+ def describe(self, eid):
+ """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
+ raise NotImplementedError
+
+
+# XXX 2.45 is allowing nicer entity type names, use this map for bw compat
+ETYPE_NAME_MAP = {'Eetype': 'EEType',
+ 'Ertype': 'ERType',
+ 'Efrdef': 'EFRDef',
+ 'Enfrdef': 'ENFRDef',
+ 'Econstraint': 'EConstraint',
+ 'Econstrainttype': 'EConstraintType',
+ 'Epermission': 'EPermission',
+ 'Egroup': 'EGroup',
+ 'Euser': 'EUser',
+ 'Eproperty': 'EProperty',
+ 'Emailaddress': 'EmailAddress',
+ 'Rqlexpression': 'RQLExpression',
+ 'Trinfo': 'TrInfo',
+ }
+
+
+
+# XXX cubic web cube migration map
+CW_MIGRATION_MAP = {'erudi': 'cubicweb',
+
+ 'eaddressbook': 'addressbook',
+ 'ebasket': 'basket',
+ 'eblog': 'blog',
+ 'ebook': 'book',
+ 'ecomment': 'comment',
+ 'ecompany': 'company',
+ 'econference': 'conference',
+ 'eemail': 'email',
+ 'eevent': 'event',
+ 'eexpense': 'expense',
+ 'efile': 'file',
+ 'einvoice': 'invoice',
+ 'elink': 'link',
+ 'emailinglist': 'mailinglist',
+ 'eperson': 'person',
+ 'eshopcart': 'shopcart',
+ 'eskillmat': 'skillmat',
+ 'etask': 'task',
+ 'eworkcase': 'workcase',
+ 'eworkorder': 'workorder',
+ 'ezone': 'zone',
+ 'i18ncontent': 'i18ncontent',
+ 'svnfile': 'vcsfile',
+
+ 'eclassschemes': 'keyword',
+ 'eclassfolders': 'folder',
+ 'eclasstags': 'tag',
+
+ 'jpl': 'jpl',
+ 'jplintra': 'jplintra',
+ 'jplextra': 'jplextra',
+ 'jplorg': 'jplorg',
+ 'jplrecia': 'jplrecia',
+ 'crm': 'crm',
+ 'agueol': 'agueol',
+ 'docaster': 'docaster',
+ 'asteretud': 'asteretud',
+
+ # XXX temp
+ 'keywords': 'keyword',
+ 'folders': 'folder',
+ 'tags': 'tag',
+ }