merge 3.5
authorAurelien Campeas <aurelien.campeas@logilab.fr>
Tue, 08 Sep 2009 20:08:10 +0200
branch3.5
changeset 3123 5ce118f80936
parent 3121 dbc02844d412 (diff)
parent 3122 0e49d2679c5c (current diff)
child 3125 52b6b8c2ccc5
child 3165 0a50ebe53db7
merge
--- a/__init__.py	Tue Sep 08 20:03:32 2009 +0200
+++ b/__init__.py	Tue Sep 08 20:08:10 2009 +0200
@@ -212,18 +212,9 @@
             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
+        userinfo['name'] = user.name()
+        userinfo['email'] = user.get_email()
         return userinfo
 
     def is_internal_session(self):
--- a/common/mail.py	Tue Sep 08 20:03:32 2009 +0200
+++ b/common/mail.py	Tue Sep 08 20:08:10 2009 +0200
@@ -21,6 +21,7 @@
         return 'XXX'
 
 from cubicweb.view import EntityView
+from cubicweb.entity import Entity
 
 def header(ustring):
     return Header(ustring.encode('UTF-8'), 'UTF-8')
@@ -176,28 +177,38 @@
         else:
             refs = ()
             msgid = None
-        userdata = self.req.user_data()
-        origlang = self.req.lang
-        for emailaddr, lang in recipients:
-            self.req.set_language(lang)
+        req = self.req
+        self.user_data = req.user_data()
+        origlang = req.lang
+        for something in recipients:
+            if isinstance(something, Entity):
+                # hi-jack self.req to get a session for the returned user
+                self.req = self.req.hijack_user(something)
+                emailaddr = something.get_email()
+            else:
+                emailaddr, lang = something
+                self.req.set_language(lang)
             # since the same view (eg self) may be called multiple time and we
             # need a fresh stream at each iteration, reset it explicitly
             self.w = None
             # XXX call render before subject to set .row/.col attributes on the
             #     view
-            content = self.render(row=0, col=0, **kwargs)
-            subject = self.subject()
-            msg = format_mail(userdata, [emailaddr], content, subject,
+            try:
+                content = self.render(row=0, col=0, **kwargs)
+                subject = self.subject()
+            except SkipEmail:
+                continue
+            msg = format_mail(self.user_data, [emailaddr], content, subject,
                               config=self.config, msgid=msgid, references=refs)
             yield [emailaddr], msg
         # restore language
-        self.req.set_language(origlang)
+        req.set_language(origlang)
 
     # recipients / email sending ###############################################
 
     def recipients(self):
-        """return a list of 2-uple (email, language) to who this email should be
-        sent
+        """return a list of either 2-uple (email, language) or user entity to
+        who this email should be sent
         """
         finder = self.vreg['components'].select('recipients_finder', self.req,
                                                 rset=self.rset,
@@ -230,7 +241,7 @@
         subject = self.req._(self.message)
         etype = entity.dc_type()
         eid = entity.eid
-        login = self.user_login()
+        login = self.user_data['login']
         return self.req._('%(subject)s %(etype)s #%(eid)s (%(login)s)') % locals()
 
     def context(self, **kwargs):
@@ -238,17 +249,13 @@
         for key, val in kwargs.iteritems():
             if val and isinstance(val, unicode) and val.strip():
                kwargs[key] = self.req._(val)
-        kwargs.update({'user': self.user_login(),
+        kwargs.update({'user': self.user_data['login'],
                        'eid': entity.eid,
                        'etype': entity.dc_type(),
                        'url': entity.absolute_url(),
                        'title': entity.dc_long_title(),})
         return kwargs
 
-    def user_login(self):
-        try:
-            # if req is actually a session (we are on the server side), and we
-            # have to prevent nested internal session
-            return self.req.actual_session().user.login
-        except AttributeError:
-            return self.req.user.login
+
+class SkipEmail(Exception):
+    """raise this if you decide to skip an email during its generation"""
--- a/dbapi.py	Tue Sep 08 20:03:32 2009 +0200
+++ b/dbapi.py	Tue Sep 08 20:08:10 2009 +0200
@@ -284,6 +284,12 @@
 
     # server session compat layer #############################################
 
+    def hijack_user(self, user):
+        """return a fake request/session using specified user"""
+        req = DBAPIRequest(self.vreg)
+        req.set_connection(self.cnx, user)
+        return req
+
     @property
     def user(self):
         if self._user is None and self.cnx:
--- a/doc/book/en/admin/setup.rst	Tue Sep 08 20:03:32 2009 +0200
+++ b/doc/book/en/admin/setup.rst	Tue Sep 08 20:08:10 2009 +0200
@@ -77,6 +77,133 @@
 In both cases, make sure you have installed the dependencies (see appendixes for
 the list).
 
+Windows installation
+````````````````````
+
+Base elements
+_____________
+
+Setting up a windows development environment is not too complicated
+but requires a series of small steps. What is proposed there is only
+an example of what can be done. We assume everything goes into C:\ in
+this document. Adjusting the installation drive should be
+straightforward.
+
+You should start by downloading and installing the Python(x,y)
+distribution. It contains python 2.5 plus numerous useful third-party
+modules and applications::
+
+  http://www.pythonxy.com/download_fr.php
+
+At the time of this writting, one gets version 2.1.15. Among the many
+things provided, one finds Eclipse + pydev (an arguably good IDE for
+python under windows).
+
+Then you must grab Twisted. There is a windows installer directly
+available from this page::
+
+  http://twistedmatrix.com/trac/
+
+A windows installer for lxml will be found there::
+
+  http://pypi.python.org/pypi/lxml/2.2.1
+
+Check out the lxml-2.2.1-win32-py2.5.exe file. More recent bugfix
+releases should probably work, too.
+
+You should find postgresql 8.4 there::
+
+  http://www.enterprisedb.com/products/pgdownload.do#windows
+
+The python drivers for posgtresql are to be found there::
+
+  http://www.stickpeople.com/projects/python/win-psycopg/#Version2
+
+Please be careful to select the right python (2.5) and postgres (8.4)
+versions.
+
+Having graphviz will allow schema drawings, which is quite recommended
+(albeit not mandatory). You should get an msi installer there::
+
+  http://www.graphviz.org/Download_windows.php
+
+Tools
+_____
+
+Get mercurial + its standard windows GUI (TortoiseHG) there (the
+latest is the greatest)::
+
+  http://bitbucket.org/tortoisehg/stable/wiki/download
+
+If you need to peruse mercurial over ssh, it can be helpful to get an
+ssh client like Putty::
+
+  http://www.putty.org/
+
+Integration of mercurial and Eclipse is convenient enough that we want
+it. Instructions are set there, in the `Download & Install` section::
+
+  http://www.vectrace.com/mercurialeclipse/
+
+Setting up the sources
+______________________
+
+You need to enable the mercurial forest extension. To do this, edit
+the file::
+
+  C:\Program Files\TortoiseHg\Mercurial.ini
+
+In the [extensions] section, add the following line::
+
+  forest=C:\Program Files\TortoiseHg\ext\forest\forest.py
+
+Now, you need to clone the cubicweb repository. We assume that you use
+Eclipse. From the IDE, choose File -> Import. In the box, select
+`Mercurial/Clone repository using MercurialEclipse`.
+
+In the import main panel you just have to:
+
+* fill the URL field with http://www.logilab.org/hg/forests/cubicwin32
+
+* check the 'Repository is a forest' box.
+
+Then, click on 'Finish'. It might take some time to get it all. Note
+that the `cubicwin32` forest contains additional python packages such
+as yapps, vobject, simplejson and twisted-web2 which are not provided
+with Python(x,y). This is provided for convenience, as we do not
+ensure the up-to-dateness of these packages, especially with respect
+to security fixes.
+
+Environment variables
+_____________________
+
+You will need some convenience environment variables once all is set
+up. These variables are settable through the GUI by getting at the
+'System properties' window (by righ-clicking on 'My Computer' ->
+properties).
+
+In the 'advanced' tab, there is an 'Environment variables'
+button. Click on it. That opens a small window allowing edition of
+user-related and system-wide variables.
+
+We will consider only user variables. First, the PATH variable. You
+should ensure it contains, separated by semi-colons, and assuming you
+are logged in as user Jane::
+
+  C:\Documents and Settings\Jane\My Documents\Python\cubicweb\cubicweb\bin
+  C:\Program Files\Graphviz2.24\bin
+
+The PYTHONPATH variable should also contain::
+
+  C:\Documents and Settings\Jane\My Documents\Python\cubicweb\
+
+From now, on a fresh `cmd` shell, you should be able to type::
+
+  cubicweb-ctl list
+
+... and get a meaningful output.
+
+
 PostgreSQL installation
 ```````````````````````
 
@@ -135,8 +262,6 @@
 Databases configuration
 -----------------------
 
-
-
 .. _ConfigurationPostgresql:
 
 PostgreSQL configuration
--- a/server/migractions.py	Tue Sep 08 20:03:32 2009 +0200
+++ b/server/migractions.py	Tue Sep 08 20:08:10 2009 +0200
@@ -166,14 +166,20 @@
                                            % (self.config.appid, backupfile)):
             return
         # unpack backup
-        bkup = tarfile.open(backupfile, 'r|gz')
-        for name in bkup.getnames():
-            if name[0] in '/.':
-                raise Exception('Security check failed, path starts with "/" or "."')
-        bkup.close() # XXX seek error if not close+open !?!
-        bkup = tarfile.open(backupfile, 'r|gz')
         tmpdir = tempfile.mkdtemp()
-        bkup.extractall(path=tmpdir)
+        try:
+            bkup = tarfile.open(backupfile, 'r|gz')
+        except tarfile.ReadError:
+            # assume restoring old backup
+            shutil.copy(backupfile, osp.join(tmpdir, 'system'))  
+        else:
+            for name in bkup.getnames():
+                if name[0] in '/.':
+                    raise Exception('Security check failed, path starts with "/" or "."')
+            bkup.close() # XXX seek error if not close+open !?!
+            bkup = tarfile.open(backupfile, 'r|gz')
+            bkup.extractall(path=tmpdir)
+            bkup.close()
 
         self.config.open_connections_pools = False
         repo = self.repo_connect()
@@ -186,7 +192,6 @@
                 print '-> error trying to restore [%s]' % exc
                 if not self.confirm('Continue anyway?', default='n'):
                     raise SystemExit(1)
-        bkup.close()
         shutil.rmtree(tmpdir)
         # call hooks
         repo.open_connections_pools()
--- a/server/querier.py	Tue Sep 08 20:03:32 2009 +0200
+++ b/server/querier.py	Tue Sep 08 20:08:10 2009 +0200
@@ -608,6 +608,8 @@
                     # return an empty result instead of raising UnknownEid
                     return empty_rset(session, rql, args)
                 cachekey.append(etype)
+                # ensure eid is correctly typed in args
+                args[key] = typed_eid(args[key])
             cachekey = tuple(cachekey)
         else:
             cachekey = rql
--- a/server/session.py	Tue Sep 08 20:03:32 2009 +0200
+++ b/server/session.py	Tue Sep 08 20:08:10 2009 +0200
@@ -78,6 +78,12 @@
     def schema(self):
         return self.repo.schema
 
+    def hijack_user(self, user):
+        """return a fake request/session using specified user"""
+        session = Session(user, self.repo)
+        session._threaddata = self._threaddata
+        return session
+
     def _change_relation(self, cb, fromeid, rtype, toeid):
         if self.is_super_session:
             cb(self, fromeid, rtype, toeid)
@@ -115,6 +121,8 @@
         self._change_relation(self.repo.glob_delete_relation,
                               fromeid, rtype, toeid)
 
+    # relations cache handling #################################################
+
     def update_rel_cache_add(self, subject, rtype, object, symetric=False):
         self._update_entity_rel_cache_add(subject, rtype, 'subject', object)
         if symetric:
--- a/server/test/unittest_querier.py	Tue Sep 08 20:03:32 2009 +0200
+++ b/server/test/unittest_querier.py	Tue Sep 08 20:08:10 2009 +0200
@@ -213,6 +213,11 @@
         # should return an empty result set
         self.failIf(self.execute('Any X WHERE X eid 99999999'))
 
+    def test_typed_eid(self):
+        # should return an empty result set
+        rset = self.execute('Any X WHERE X eid %(x)s', {'x': '1'}, 'x')
+        self.assertIsInstance(rset[0][0], (int, long))
+
     def test_bytes_storage(self):
         feid = self.execute('INSERT File X: X name "foo.pdf", X data_format "text/plain", X data %(data)s',
                             {'data': Binary("xxx")})[0][0]
--- a/sobjects/notification.py	Tue Sep 08 20:03:32 2009 +0200
+++ b/sobjects/notification.py	Tue Sep 08 20:08:10 2009 +0200
@@ -190,6 +190,6 @@
     def subject(self):
         entity = self.entity(self.row or 0, self.col or 0)
         return  u'%s #%s (%s)' % (self.req.__('New %s' % entity.e_schema),
-                                  entity.eid, self.user_login())
+                                  entity.eid, self.user_data['login'])
 
 NormalizedTextView = class_renamed('NormalizedTextView', ContentAddedView)
--- a/web/data/cubicweb.ajax.js	Tue Sep 08 20:03:32 2009 +0200
+++ b/web/data/cubicweb.ajax.js	Tue Sep 08 20:08:10 2009 +0200
@@ -392,7 +392,9 @@
     return stripped;
 }
 
-/* convenience function that returns a DOM node based on req's result. */
+/* convenience function that returns a DOM node based on req's result.
+ * XXX clarify the need to clone
+ * */
 function getDomFromResponse(response) {
     if (typeof(response) == 'string') {
 	var doc = html2dom(response);
--- a/web/data/cubicweb.facets.js	Tue Sep 08 20:03:32 2009 +0200
+++ b/web/data/cubicweb.facets.js	Tue Sep 08 20:08:10 2009 +0200
@@ -130,9 +130,10 @@
 		});
 		facet.find('div.facetCheckBox').click(function () {
 		    var $this = jQuery(this);
-		    if ($this.hasClass('facetValueDisabled')){
-		     	    return
-		    }
+		    // NOTE : add test on the facet operator (i.e. OR, AND)
+		    // if ($this.hasClass('facetValueDisabled')){
+		    //  	    return
+		    // }
 		    if ($this.hasClass('facetValueSelected')) {
 			$this.removeClass('facetValueSelected');
 			$this.find('img').each(function (i){
--- a/web/formwidgets.py	Tue Sep 08 20:03:32 2009 +0200
+++ b/web/formwidgets.py	Tue Sep 08 20:08:10 2009 +0200
@@ -430,8 +430,9 @@
         # XXX entity form specific
         entity = form.edited_entity
         attrs['cubicweb:etype_to'] = entity.e_schema
-        etype_from = entity.e_schema.subject_relation(self.name).objects(entity.e_schema)[0]
+        etype_from = entity.e_schema.subject_relation(field.name).objects(entity.e_schema)[0]
         attrs['cubicweb:etype_from'] = etype_from
+        return name, values, attrs
 
     def render(self, form, field, renderer):
         return super(AddComboBoxWidget, self).render(form, field, renderer) + u'''