# HG changeset patch
# User Rémi Cardona
# Date 1434976057 -7200
# Node ID 4ee15441f2eb3f326e10f35b8d4201b902e7b795
# Parent eb681a0306991e8e289e24ea7e8e83f538f36bb8# Parent 843e4b3ccfbc869e2be634601d1c340e2076deb8
merge 3.20.8 in 3.21
diff -r eb681a030699 -r 4ee15441f2eb .hgtags
--- a/.hgtags Mon Jun 22 14:15:16 2015 +0200
+++ b/.hgtags Mon Jun 22 14:27:37 2015 +0200
@@ -426,3 +426,73 @@
7f64859dcbcdc6394421b8a5175896ba2e5caeb5 cubicweb-version-3.20.6
7f64859dcbcdc6394421b8a5175896ba2e5caeb5 cubicweb-debian-version-3.20.6-1
7f64859dcbcdc6394421b8a5175896ba2e5caeb5 cubicweb-centos-version-3.20.6-1
+359d68bc12602c73559531b09d00399f4cbca785 cubicweb-version-3.20.7
+359d68bc12602c73559531b09d00399f4cbca785 cubicweb-debian-version-3.20.7-1
+359d68bc12602c73559531b09d00399f4cbca785 cubicweb-centos-version-3.20.7-1
+1141927b8494aabd16e31b0d0d9a50fe1fed5f2f 3.19.0
+1141927b8494aabd16e31b0d0d9a50fe1fed5f2f debian/3.19.0-1
+1141927b8494aabd16e31b0d0d9a50fe1fed5f2f centos/3.19.0-1
+1fe4bc4a8ac8831a379e9ebea08d75fbb6fc5c2a 3.19.1
+1fe4bc4a8ac8831a379e9ebea08d75fbb6fc5c2a debian/3.19.1-1
+1fe4bc4a8ac8831a379e9ebea08d75fbb6fc5c2a centos/3.19.1-1
+8ac2202866e747444ce12778ff8789edd9c92eae 3.19.2
+8ac2202866e747444ce12778ff8789edd9c92eae debian/3.19.2-1
+8ac2202866e747444ce12778ff8789edd9c92eae centos/3.19.2-1
+37f7c60f89f13dfcf326a4ea0a98ca20d959f7bd 3.19.3
+37f7c60f89f13dfcf326a4ea0a98ca20d959f7bd debian/3.19.3-1
+37f7c60f89f13dfcf326a4ea0a98ca20d959f7bd centos/3.19.3-1
+c4e740e50fc7d371d14df17d26bc42d1f8060261 3.19.4
+c4e740e50fc7d371d14df17d26bc42d1f8060261 debian/3.19.4-1
+c4e740e50fc7d371d14df17d26bc42d1f8060261 centos/3.19.4-1
+3ac86df519af2a1194cb3fc882d30d0e1bf44e3b 3.19.5
+3ac86df519af2a1194cb3fc882d30d0e1bf44e3b debian/3.19.5-1
+3ac86df519af2a1194cb3fc882d30d0e1bf44e3b centos/3.19.5-1
+934341b848a6874688314d7c154183aca3aed530 3.19.6
+934341b848a6874688314d7c154183aca3aed530 debian/3.19.6-1
+934341b848a6874688314d7c154183aca3aed530 centos/3.19.6-1
+ac4f5f615597575bec32f8f591260e5a91e53855 3.19.7
+ac4f5f615597575bec32f8f591260e5a91e53855 debian/3.19.7-1
+ac4f5f615597575bec32f8f591260e5a91e53855 centos/3.19.7-1
+efc8645ece4300958e3628db81464fef12d5f6e8 3.19.8
+efc8645ece4300958e3628db81464fef12d5f6e8 debian/3.19.8-1
+efc8645ece4300958e3628db81464fef12d5f6e8 centos/3.19.8-1
+b7c373d74754f5ba9344575cb179b47282c413b6 3.19.9
+b7c373d74754f5ba9344575cb179b47282c413b6 debian/3.19.9-1
+b7c373d74754f5ba9344575cb179b47282c413b6 centos/3.19.9-1
+3bab0b9b0ee7355a6fea45c2adca88bffe130e5d 3.19.10
+3bab0b9b0ee7355a6fea45c2adca88bffe130e5d debian/3.19.10-1
+3bab0b9b0ee7355a6fea45c2adca88bffe130e5d centos/3.19.10-1
+1ae64186af9448dffbeebdef910c8c7391c04313 3.19.11
+1ae64186af9448dffbeebdef910c8c7391c04313 debian/3.19.11-1
+1ae64186af9448dffbeebdef910c8c7391c04313 centos/3.19.11-1
+6d265ea7d56fe49e9dff261d3b2caf3c2b6f9409 debian/3.19.11-2
+5932de3d50bf023544c8f54b47898e4db35eac7c 3.19.12
+5932de3d50bf023544c8f54b47898e4db35eac7c debian/3.19.12-1
+5932de3d50bf023544c8f54b47898e4db35eac7c centos/3.19.12-1
+7e6b7739afe6128589ad51b0318decb767cbae36 3.20.0
+7e6b7739afe6128589ad51b0318decb767cbae36 debian/3.20.0-1
+7e6b7739afe6128589ad51b0318decb767cbae36 centos/3.20.0-1
+43eef610ef11673d01750459356aec5a96174ca0 3.20.1
+43eef610ef11673d01750459356aec5a96174ca0 debian/3.20.1-1
+43eef610ef11673d01750459356aec5a96174ca0 centos/3.20.1-1
+138464fc1c3397979b729cca3a30bc4481fd1e2d 3.20.2
+138464fc1c3397979b729cca3a30bc4481fd1e2d debian/3.20.2-1
+138464fc1c3397979b729cca3a30bc4481fd1e2d centos/3.20.2-1
+7d3a583ed5392ba528e56ef6902ced5468613f4d 3.20.3
+7d3a583ed5392ba528e56ef6902ced5468613f4d debian/3.20.3-1
+7d3a583ed5392ba528e56ef6902ced5468613f4d centos/3.20.3-1
+49831fdc84dc7e7bed01d5e8110a46242b5ccda6 3.20.4
+49831fdc84dc7e7bed01d5e8110a46242b5ccda6 debian/3.20.4-1
+49831fdc84dc7e7bed01d5e8110a46242b5ccda6 centos/3.20.4-1
+51aa56e7d507958b3326abbb6a31d0e6dde6b47b 3.20.5
+51aa56e7d507958b3326abbb6a31d0e6dde6b47b debian/3.20.5-1
+51aa56e7d507958b3326abbb6a31d0e6dde6b47b centos/3.20.5-1
+7f64859dcbcdc6394421b8a5175896ba2e5caeb5 3.20.6
+7f64859dcbcdc6394421b8a5175896ba2e5caeb5 debian/3.20.6-1
+7f64859dcbcdc6394421b8a5175896ba2e5caeb5 centos/3.20.6-1
+359d68bc12602c73559531b09d00399f4cbca785 3.20.7
+359d68bc12602c73559531b09d00399f4cbca785 debian/3.20.7-1
+359d68bc12602c73559531b09d00399f4cbca785 centos/3.20.7-1
+ec284980ed9e214fe6c15cc4cf9617961d88928d 3.20.8
+ec284980ed9e214fe6c15cc4cf9617961d88928d debian/3.20.8-1
+ec284980ed9e214fe6c15cc4cf9617961d88928d centos/3.20.8-1
diff -r eb681a030699 -r 4ee15441f2eb __pkginfo__.py
--- a/__pkginfo__.py Mon Jun 22 14:15:16 2015 +0200
+++ b/__pkginfo__.py Mon Jun 22 14:27:37 2015 +0200
@@ -22,7 +22,7 @@
modname = distname = "cubicweb"
-numversion = (3, 20, 6)
+numversion = (3, 20, 8)
version = '.'.join(str(num) for num in numversion)
description = "a repository of entities / relations for knowledge management"
diff -r eb681a030699 -r 4ee15441f2eb cubicweb.spec
--- a/cubicweb.spec Mon Jun 22 14:15:16 2015 +0200
+++ b/cubicweb.spec Mon Jun 22 14:27:37 2015 +0200
@@ -7,7 +7,7 @@
%endif
Name: cubicweb
-Version: 3.20.6
+Version: 3.20.8
Release: logilab.1%{?dist}
Summary: CubicWeb is a semantic web application framework
Source0: http://download.logilab.org/pub/cubicweb/cubicweb-%{version}.tar.gz
diff -r eb681a030699 -r 4ee15441f2eb cwconfig.py
--- a/cwconfig.py Mon Jun 22 14:15:16 2015 +0200
+++ b/cwconfig.py Mon Jun 22 14:27:37 2015 +0200
@@ -1104,7 +1104,7 @@
self._gettext_init()
def _load_site_cubicweb(self, sitefile):
- # overriden to register cube specific options
+ # overridden to register cube specific options
mod = super(CubicWebConfiguration, self)._load_site_cubicweb(sitefile)
if getattr(mod, 'options', None):
self.register_options(mod.options)
diff -r eb681a030699 -r 4ee15441f2eb debian/changelog
--- a/debian/changelog Mon Jun 22 14:15:16 2015 +0200
+++ b/debian/changelog Mon Jun 22 14:27:37 2015 +0200
@@ -1,3 +1,15 @@
+cubicweb (3.20.8-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Rémi Cardona Mon, 22 Jun 2015 12:50:11 +0200
+
+cubicweb (3.20.7-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Rémi Cardona Wed, 22 Apr 2015 17:47:35 +0200
+
cubicweb (3.20.6-1) unstable; urgency=low
* new upstream release
@@ -46,7 +58,13 @@
-- Julien Cristau Tue, 06 Jan 2015 18:11:03 +0100
-cubicweb (3.19.11-2) UNRELEASED; urgency=low
+cubicweb (3.19.12-1) unstable; urgency=low
+
+ * New upstream release
+
+ -- Rémi Cardona Fri, 19 Jun 2015 10:51:23 +0200
+
+cubicweb (3.19.11-2) unstable; urgency=low
* Fix cubicweb-dev dependencies.
diff -r eb681a030699 -r 4ee15441f2eb doc/book/en/annexes/faq.rst
--- a/doc/book/en/annexes/faq.rst Mon Jun 22 14:15:16 2015 +0200
+++ b/doc/book/en/annexes/faq.rst Mon Jun 22 14:27:37 2015 +0200
@@ -102,21 +102,17 @@
How to change the instance logo ?
---------------------------------
-There are two ways of changing the logo.
-
-1. The easiest way to use a different logo is to replace the existing
- ``logo.png`` in ``myapp/data`` by your prefered icon and refresh.
- By default all instance will look for a ``logo.png`` to be
- rendered in the logo section.
+The logo is managed by css. You must provide a custom css that will contain
+the code below:
- .. image:: ../images/lax-book_06-main-template-logo_en.png
+::
+
+ #logo {
+ background-image: url("logo.jpg");
+ }
-2. In your cube directory, you can specify which file to use for the logo.
- This is configurable in ``mycube/uiprops.py``: ::
- LOGO = data('mylogo.gif')
-
- ``mylogo.gif`` is in ``mycube/data`` directory.
+``logo.jpg`` is in ``mycube/data`` directory.
How to create an anonymous user ?
---------------------------------
diff -r eb681a030699 -r 4ee15441f2eb doc/book/en/annexes/rql/language.rst
--- a/doc/book/en/annexes/rql/language.rst Mon Jun 22 14:15:16 2015 +0200
+++ b/doc/book/en/annexes/rql/language.rst Mon Jun 22 14:27:37 2015 +0200
@@ -131,7 +131,7 @@
+----------+---------------------+-----------+--------+
| & | bitwise AND | 91 & 15 | 11 |
+----------+---------------------+-----------+--------+
-| | | bitwise OR | 32 | 3 | 35 |
+| `|` | bitwise OR | 32 | 3 | 35 |
+----------+---------------------+-----------+--------+
| # | bitwise XOR | 17 # 5 | 20 |
+----------+---------------------+-----------+--------+
diff -r eb681a030699 -r 4ee15441f2eb doc/book/en/tutorials/advanced/part02_security.rst
--- a/doc/book/en/tutorials/advanced/part02_security.rst Mon Jun 22 14:15:16 2015 +0200
+++ b/doc/book/en/tutorials/advanced/part02_security.rst Mon Jun 22 14:27:37 2015 +0200
@@ -313,8 +313,7 @@
class SecurityTC(CubicWebTC):
- def test_visibility_propagation(self):
-
+ def test_visibility_propagation(self):
with self.admin_access.repo_cnx() as cnx:
# create a user for later security checks
toto = self.create_user(cnx, 'toto')
@@ -322,9 +321,9 @@
# init some data using the default manager connection
folder = cnx.create_entity('Folder',
name=u'restricted',
- visibility=u'restricted')
+ visibility=u'restricted')
photo1 = cnx.create_entity('File',
- data_name=u'photo1.jpg',
+ data_name=u'photo1.jpg',
data=Binary('xxx'),
filed_under=folder)
cnx.commit()
@@ -333,29 +332,30 @@
# unless explicitly specified
photo2 = cnx.create_entity('File',
data_name=u'photo2.jpg',
- data=Binary('xxx'),
- visibility=u'public',
- filed_under=folder)
+ data=Binary('xxx'),
+ visibility=u'public',
+ filed_under=folder)
cnx.commit()
self.assertEquals(photo2.visibility, 'public')
-
with self.new_access('toto').repo_cnx() as cnx:
# test security
self.assertEqual(1, len(cnx.execute('File X'))) # only the public one
self.assertEqual(0, len(cnx.execute('Folder X'))) # restricted...
+ with self.admin_access.repo_cnx() as cnx:
# may_be_read_by propagation
folder = cnx.entity_from_eid(folder.eid)
folder.cw_set(may_be_read_by=toto)
cnx.commit()
- photo1 = cnx.entity_from_eid(photo1)
+ with self.new_access('toto').repo_cnx() as cnx:
+ photo1 = cnx.entity_from_eid(photo1.eid)
self.failUnless(photo1.may_be_read_by)
# test security with permissions
self.assertEquals(2, len(cnx.execute('File X'))) # now toto has access to photo2
self.assertEquals(1, len(cnx.execute('Folder X'))) # and to restricted folder
if __name__ == '__main__':
- from logilab.common.testlib import unittest_main
- unittest_main()
+ from logilab.common.testlib import unittest_main
+ unittest_main()
It's not complete, but shows most things you'll want to do in tests: adding some
content, creating users and connecting as them in the test, etc...
@@ -433,7 +433,7 @@
To migrate my instance I simply type::
- cubicweb-ctl upgrade sytweb
+ cubicweb-ctl upgrade sytweb_instance
You'll then be asked some questions to do the migration step by step. You should say
YES when it asks if a backup of your database should be done, so you can get back
diff -r eb681a030699 -r 4ee15441f2eb doc/book/en/tutorials/advanced/part03_bfss.rst
--- a/doc/book/en/tutorials/advanced/part03_bfss.rst Mon Jun 22 14:15:16 2015 +0200
+++ b/doc/book/en/tutorials/advanced/part03_bfss.rst Mon Jun 22 14:27:37 2015 +0200
@@ -62,7 +62,7 @@
::
- $ cubicweb-ctl shell sytweb
+ $ cubicweb-ctl shell sytweb_instance
entering the migration python shell
just type migration commands or arbitrary python code and type ENTER to execute it
type "exit" or Ctrl-D to quit the shell and resume operation
@@ -81,7 +81,7 @@
site. For instance if I have a 'photos/201005WePyrenees' containing pictures for
a particular event, I can import it to my web site by typing ::
- $ cubicweb-ctl fsimport -F sytweb photos/201005WePyrenees/
+ $ cubicweb-ctl fsimport -F sytweb_instance photos/201005WePyrenees/
** importing directory /home/syt/photos/201005WePyrenees
importing IMG_8314.JPG
importing IMG_8274.JPG
diff -r eb681a030699 -r 4ee15441f2eb doc/book/en/tutorials/advanced/part04_ui-base.rst
--- a/doc/book/en/tutorials/advanced/part04_ui-base.rst Mon Jun 22 14:15:16 2015 +0200
+++ b/doc/book/en/tutorials/advanced/part04_ui-base.rst Mon Jun 22 14:27:37 2015 +0200
@@ -26,6 +26,7 @@
from cubicweb.predicates import is_instance
from cubicweb.web import component
from cubicweb.web.views import error
+ from cubicweb.predicates import anonymous_user
class FourOhFour(error.FourOhFour):
__select__ = error.FourOhFour.__select__ & anonymous_user()
diff -r eb681a030699 -r 4ee15441f2eb entities/__init__.py
--- a/entities/__init__.py Mon Jun 22 14:15:16 2015 +0200
+++ b/entities/__init__.py Mon Jun 22 14:27:37 2015 +0200
@@ -68,7 +68,7 @@
if rschema.meta:
continue
value = self.cw_attr_value(rschema.type)
- if value:
+ if value is not None:
# make the value printable (dates, floats, bytes, etc.)
return self.printable_value(rschema.type, value, attrschema.type,
format='text/plain')
diff -r eb681a030699 -r 4ee15441f2eb entities/test/data/schema.py
--- a/entities/test/data/schema.py Mon Jun 22 14:15:16 2015 +0200
+++ b/entities/test/data/schema.py Mon Jun 22 14:27:37 2015 +0200
@@ -17,10 +17,11 @@
# with CubicWeb. If not, see .
"""entities tests schema"""
-from yams.buildobjs import EntityType, String, RichString
+from yams.buildobjs import EntityType, String, RichString, Int
from cubicweb.schema import make_workflowable
class Company(EntityType):
+ order = Int()
name = String()
description = RichString()
diff -r eb681a030699 -r 4ee15441f2eb entities/test/unittest_base.py
--- a/entities/test/unittest_base.py Mon Jun 22 14:15:16 2015 +0200
+++ b/entities/test/unittest_base.py Mon Jun 22 14:27:37 2015 +0200
@@ -137,10 +137,16 @@
self.assertEqual(e.dc_title(), 'member')
self.assertEqual(e.name(), u'bouah lôt')
+ def test_falsey_dc_title(self):
+ with self.admin_access.repo_cnx() as cnx:
+ e = cnx.create_entity('Company', order=0, name=u'pythonian')
+ cnx.commit()
+ self.assertEqual(u'0', e.dc_title())
+
def test_allowed_massmail_keys(self):
with self.admin_access.repo_cnx() as cnx:
e = cnx.execute('CWUser U WHERE U login "member"').get_entity(0, 0)
- # Bytes/Password attributes should be omited
+ # Bytes/Password attributes should be omitted
self.assertEqual(e.cw_adapt_to('IEmailable').allowed_massmail_keys(),
set(('surname', 'firstname', 'login', 'last_login_time',
'creation_date', 'modification_date', 'cwuri', 'eid'))
diff -r eb681a030699 -r 4ee15441f2eb hooks/security.py
--- a/hooks/security.py Mon Jun 22 14:15:16 2015 +0200
+++ b/hooks/security.py Mon Jun 22 14:27:37 2015 +0200
@@ -136,6 +136,19 @@
self.entity.cw_check_perm('delete')
+def skip_inlined_relation_security(cnx, rschema, eid):
+ """return True if security for the given inlined relation should be skipped,
+ in case where the relation has been set through modification of
+ `entity.cw_edited` in a hook
+ """
+ assert rschema.inlined
+ try:
+ entity = cnx.transaction_data['ecache'][eid]
+ except KeyError:
+ return False
+ return rschema.type in entity.cw_edited.skip_security
+
+
class BeforeAddRelationSecurityHook(SecurityHook):
__regid__ = 'securitybeforeaddrelation'
events = ('before_add_relation',)
@@ -146,6 +159,9 @@
if (self.eidfrom, self.rtype, self.eidto) in nocheck:
return
rschema = self._cw.repo.schema[self.rtype]
+ if rschema.inlined and skip_inlined_relation_security(
+ self._cw, rschema, self.eidfrom):
+ return
rdef = rschema.rdef(self._cw.entity_metas(self.eidfrom)['type'],
self._cw.entity_metas(self.eidto)['type'])
rdef.check_perm(self._cw, 'add', fromeid=self.eidfrom, toeid=self.eidto)
@@ -156,11 +172,14 @@
events = ('after_add_relation',)
def __call__(self):
- if not self.rtype in BEFORE_ADD_RELATIONS:
+ if self.rtype not in BEFORE_ADD_RELATIONS:
nocheck = self._cw.transaction_data.get('skip-security', ())
if (self.eidfrom, self.rtype, self.eidto) in nocheck:
return
rschema = self._cw.repo.schema[self.rtype]
+ if rschema.inlined and skip_inlined_relation_security(
+ self._cw, rschema, self.eidfrom):
+ return
if self.rtype in ON_COMMIT_ADD_RELATIONS:
CheckRelationPermissionOp.get_instance(self._cw).add_data(
('add', rschema, self.eidfrom, self.eidto) )
@@ -179,6 +198,9 @@
if (self.eidfrom, self.rtype, self.eidto) in nocheck:
return
rschema = self._cw.repo.schema[self.rtype]
+ if rschema.inlined and skip_inlined_relation_security(
+ self._cw, rschema, self.eidfrom):
+ return
rdef = rschema.rdef(self._cw.entity_metas(self.eidfrom)['type'],
self._cw.entity_metas(self.eidto)['type'])
rdef.check_perm(self._cw, 'delete', fromeid=self.eidfrom, toeid=self.eidto)
diff -r eb681a030699 -r 4ee15441f2eb hooks/synccomputed.py
--- a/hooks/synccomputed.py Mon Jun 22 14:15:16 2015 +0200
+++ b/hooks/synccomputed.py Mon Jun 22 14:27:37 2015 +0200
@@ -43,18 +43,16 @@
for computed_attribute_rdef, eids in self.get_data().iteritems():
attr = computed_attribute_rdef.rtype
formula = computed_attribute_rdef.formula
- rql = formula.replace('Any ', 'Any X, ', 1)
- kwargs = None
- # add constraint on X to the formula
- if None in eids : # recompute for all etype if None is found
- rql += ', X is %s' % computed_attribute_rdef.subject
- elif len(eids) == 1:
- rql += ', X eid %(x)s'
- kwargs = {'x': eids.pop()}
+ select = self.cnx.repo.vreg.rqlhelper.parse(formula).children[0]
+ xvar = select.get_variable('X')
+ select.add_selected(xvar, index=0)
+ select.add_group_var(xvar, index=0)
+ if None in eids:
+ select.add_type_restriction(xvar, computed_attribute_rdef.subject)
else:
- rql += ', X eid IN (%s)' % ', '.join((str(eid) for eid in eids))
+ select.add_eid_restriction(xvar, eids)
update_rql = 'SET X %s %%(value)s WHERE X eid %%(x)s' % attr
- for eid, value in self.cnx.execute(rql, kwargs):
+ for eid, value in self.cnx.execute(select.as_string()):
self.cnx.execute(update_rql, {'value': value, 'x': eid})
diff -r eb681a030699 -r 4ee15441f2eb hooks/test/data/bootstrap_cubes
--- a/hooks/test/data/bootstrap_cubes Mon Jun 22 14:15:16 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-email
diff -r eb681a030699 -r 4ee15441f2eb hooks/test/data/schema.py
--- a/hooks/test/data/schema.py Mon Jun 22 14:15:16 2015 +0200
+++ b/hooks/test/data/schema.py Mon Jun 22 14:27:37 2015 +0200
@@ -16,7 +16,13 @@
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see .
-from yams.buildobjs import RelationDefinition, EntityType, String
+from yams.buildobjs import (RelationDefinition, RelationType, EntityType,
+ String, Datetime, Int)
+from yams.reader import context
+
+from cubicweb.schema import ERQLExpression
+
+_ = unicode
class friend(RelationDefinition):
subject = ('CWUser', 'CWGroup')
@@ -36,3 +42,44 @@
subject = 'Folder'
object = 'Folder'
composite = 'subject'
+
+
+class Email(EntityType):
+ """electronic mail"""
+ subject = String(fulltextindexed=True)
+ date = Datetime(description=_('UTC time on which the mail was sent'))
+ messageid = String(required=True, indexed=True)
+ headers = String(description=_('raw headers'))
+
+
+
+class EmailPart(EntityType):
+ """an email attachment"""
+ __permissions__ = {
+ 'read': ('managers', 'users', 'guests',), # XXX if E parts X, U has_read_permission E
+ 'add': ('managers', ERQLExpression('E parts X, U has_update_permission E'),),
+ 'delete': ('managers', ERQLExpression('E parts X, U has_update_permission E')),
+ 'update': ('managers', 'owners',),
+ }
+
+ content = String(fulltextindexed=True)
+ content_format = String(required=True, maxsize=50)
+ ordernum = Int(required=True)
+
+
+class parts(RelationType):
+ subject = 'Email'
+ object = 'EmailPart'
+ cardinality = '*1'
+ composite = 'subject'
+ fulltext_container = 'subject'
+
+class sender(RelationDefinition):
+ subject = 'Email'
+ object = 'EmailAddress'
+ cardinality = '?*'
+ inlined = True
+
+class recipients(RelationDefinition):
+ subject = 'Email'
+ object = 'EmailAddress'
diff -r eb681a030699 -r 4ee15441f2eb hooks/test/unittest_security.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hooks/test/unittest_security.py Mon Jun 22 14:27:37 2015 +0200
@@ -0,0 +1,56 @@
+# copyright 2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb 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 Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb. If not, see .
+
+from cubicweb.devtools.testlib import CubicWebTC
+from cubicweb.server import hook
+from cubicweb.predicates import is_instance
+
+
+class SecurityHooksTC(CubicWebTC):
+ def setup_database(self):
+ with self.admin_access.repo_cnx() as cnx:
+ self.add_eid = cnx.create_entity('EmailAddress',
+ address=u'hop@perdu.com',
+ reverse_use_email=cnx.user.eid).eid
+ cnx.commit()
+
+ def test_inlined_cw_edited_relation(self):
+ """modification of cw_edited to add an inlined relation shouldn't trigger a security error.
+
+ Test for https://www.cubicweb.org/ticket/5477315
+ """
+ sender = self.repo.schema['Email'].rdef('sender')
+ with self.temporary_permissions((sender, {'add': ()})):
+
+ class MyHook(hook.Hook):
+ __regid__ = 'test.pouet'
+ __select__ = hook.Hook.__select__ & is_instance('Email')
+ events = ('before_add_entity',)
+
+ def __call__(self):
+ self.entity.cw_edited['sender'] = self._cw.user.primary_email[0].eid
+
+ with self.temporary_appobjects(MyHook):
+ with self.admin_access.repo_cnx() as cnx:
+ email = cnx.create_entity('Email', messageid=u'1234')
+ cnx.commit()
+ self.assertEqual(email.sender[0].eid, self.add_eid)
+
+if __name__ == '__main__':
+ from logilab.common.testlib import unittest_main
+ unittest_main()
diff -r eb681a030699 -r 4ee15441f2eb i18n/fr.po
--- a/i18n/fr.po Mon Jun 22 14:15:16 2015 +0200
+++ b/i18n/fr.po Mon Jun 22 14:27:37 2015 +0200
@@ -727,7 +727,7 @@
"the source."
msgstr ""
"Configuration de la source pour un hôte spécifique. Une clé=valeur par "
-"ligne, les clés autorisées dépendantes du type de source. Les valeur "
+"ligne, les clés autorisées dépendantes du type de source. Les valeurs "
"surchargent celles définies sur la source."
msgid "Startup views"
diff -r eb681a030699 -r 4ee15441f2eb mail.py
--- a/mail.py Mon Jun 22 14:15:16 2015 +0200
+++ b/mail.py Mon Jun 22 14:27:37 2015 +0200
@@ -25,6 +25,7 @@
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.header import Header
+from email.utils import formatdate
from socket import gethostname
def header(ustring):
@@ -110,6 +111,7 @@
msg['Message-id'] = msgid
if references:
msg['References'] = ', '.join(references)
+ msg['Date'] = formatdate()
return msg
diff -r eb681a030699 -r 4ee15441f2eb misc/migration/3.20.7_Any.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.20.7_Any.py Mon Jun 22 14:27:37 2015 +0200
@@ -0,0 +1,2 @@
+if repo.system_source.dbdriver == 'postgres':
+ install_custom_sql_scripts()
diff -r eb681a030699 -r 4ee15441f2eb misc/migration/3.20.8_Any.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.20.8_Any.py Mon Jun 22 14:27:37 2015 +0200
@@ -0,0 +1,1 @@
+sync_schema_props_perms('cwuri')
diff -r eb681a030699 -r 4ee15441f2eb predicates.py
--- a/predicates.py Mon Jun 22 14:15:16 2015 +0200
+++ b/predicates.py Mon Jun 22 14:27:37 2015 +0200
@@ -188,6 +188,7 @@
from warnings import warn
from operator import eq
+from logilab.common.deprecation import deprecated
from logilab.common.registry import Predicate, objectify_predicate, yes
from yams.schema import BASE_TYPES, role_name
@@ -195,12 +196,10 @@
from cubicweb import (Unauthorized, NoSelectableObject, NotAnEntity,
CW_EVENT_MANAGER, role)
-# even if not used, let yes here so it's importable through this module
from cubicweb.uilib import eid_param
from cubicweb.schema import split_expression
-# remember, these imports are there for bw compat only
-__BACKWARD_COMPAT_IMPORTS = (yes,)
+yes = deprecated('[3.15] import yes() from use logilab.common.registry')(yes)
# abstract predicates / mixin helpers ###########################################
diff -r eb681a030699 -r 4ee15441f2eb schemas/_regproc.postgres.sql
--- a/schemas/_regproc.postgres.sql Mon Jun 22 14:15:16 2015 +0200
+++ b/schemas/_regproc.postgres.sql Mon Jun 22 14:27:37 2015 +0200
@@ -11,6 +11,7 @@
$$ LANGUAGE SQL;;
+DROP FUNCTION IF EXISTS cw_array_append_unique (anyarray, anyelement) CASCADE;
CREATE FUNCTION cw_array_append_unique (anyarray, anyelement) RETURNS anyarray AS $$
SELECT array_append($1, (SELECT $2 WHERE $2 <> ALL($1)))
$$ LANGUAGE SQL;;
@@ -25,7 +26,6 @@
);;
-
DROP FUNCTION IF EXISTS limit_size (fulltext text, format text, maxsize integer);
CREATE FUNCTION limit_size (fulltext text, format text, maxsize integer) RETURNS text AS $$
DECLARE
@@ -35,7 +35,7 @@
RETURN fulltext;
END IF;
IF format = 'text/html' OR format = 'text/xhtml' OR format = 'text/xml' THEN
- plaintext := regexp_replace(fulltext, '<[\\w/][^>]+>', '', 'g');
+ plaintext := regexp_replace(fulltext, '<[a-zA-Z/][^>]*>', '', 'g');
ELSE
plaintext := fulltext;
END IF;
diff -r eb681a030699 -r 4ee15441f2eb schemas/base.py
--- a/schemas/base.py Mon Jun 22 14:15:16 2015 +0200
+++ b/schemas/base.py Mon Jun 22 14:27:37 2015 +0200
@@ -26,7 +26,8 @@
Boolean, UniqueConstraint)
from cubicweb.schema import (
RQLConstraint, WorkflowableEntityType, ERQLExpression, RRQLExpression,
- PUB_SYSTEM_ENTITY_PERMS, PUB_SYSTEM_REL_PERMS, PUB_SYSTEM_ATTR_PERMS)
+ PUB_SYSTEM_ENTITY_PERMS, PUB_SYSTEM_REL_PERMS, PUB_SYSTEM_ATTR_PERMS,
+ RO_ATTR_PERMS)
class CWUser(WorkflowableEntityType):
"""define a CubicWeb user"""
@@ -160,7 +161,7 @@
class cwuri(RelationType):
"""internal entity uri"""
- __permissions__ = PUB_SYSTEM_ATTR_PERMS
+ __permissions__ = RO_ATTR_PERMS
cardinality = '11'
subject = '*'
object = 'String'
diff -r eb681a030699 -r 4ee15441f2eb server/schemaserial.py
--- a/server/schemaserial.py Mon Jun 22 14:15:16 2015 +0200
+++ b/server/schemaserial.py Mon Jun 22 14:27:37 2015 +0200
@@ -21,8 +21,9 @@
import os
import json
+import sys
-from logilab.common.shellutils import ProgressBar
+from logilab.common.shellutils import ProgressBar, DummyProgressBar
from yams import BadSchemaDefinition, schema as schemamod, buildobjs as ybo
@@ -348,7 +349,10 @@
pb_size = (len(eschemas + schema.relations())
+ len(CONSTRAINTS)
+ len([x for x in eschemas if x.specializes()]))
- pb = ProgressBar(pb_size, title=_title)
+ if sys.stdout.isatty():
+ pb = ProgressBar(pb_size, title=_title)
+ else:
+ pb = DummyProgressBar()
groupmap = group_mapping(cnx, interactive=False)
# serialize all entity types, assuring CWEType is serialized first for proper
# is / is_instance_of insertion
diff -r eb681a030699 -r 4ee15441f2eb server/serverctl.py
--- a/server/serverctl.py Mon Jun 22 14:15:16 2015 +0200
+++ b/server/serverctl.py Mon Jun 22 14:27:37 2015 +0200
@@ -972,13 +972,19 @@
name = 'source-sync'
arguments = ' '
min_args = max_args = 2
+ options = (
+ ('loglevel',
+ {'short': 'l', 'type' : 'choice', 'metavar': '',
+ 'default': 'info', 'choices': ('debug', 'info', 'warning', 'error'),
+ }),
+ )
def run(self, args):
+ from cubicweb.cwctl import init_cmdline_log_threshold
config = ServerConfiguration.config_for(args[0])
config.global_set_option('log-file', None)
config.log_format = '%(levelname)s %(name)s: %(message)s'
- logger = logging.getLogger('cubicweb.sources')
- logger.setLevel(logging.INFO)
+ init_cmdline_log_threshold(config, self['loglevel'])
# only retrieve cnx to trigger authentication, close it right away
repo, cnx = repo_cnx(config)
cnx.close()
diff -r eb681a030699 -r 4ee15441f2eb server/sources/native.py
--- a/server/sources/native.py Mon Jun 22 14:15:16 2015 +0200
+++ b/server/sources/native.py Mon Jun 22 14:27:37 2015 +0200
@@ -718,7 +718,7 @@
if mo is not None:
raise UniqueTogetherError(cnx, cstrname=mo.group(0))
# old sqlite
- mo = re.search('columns (.*) are not unique', arg)
+ mo = re.search('columns? (.*) (?:is|are) not unique', arg)
if mo is not None: # sqlite in use
# we left chop the 'cw_' prefix of attribute names
rtypes = [c.strip()[3:]
diff -r eb681a030699 -r 4ee15441f2eb server/sqlutils.py
--- a/server/sqlutils.py Mon Jun 22 14:15:16 2015 +0200
+++ b/server/sqlutils.py Mon Jun 22 14:27:37 2015 +0200
@@ -28,7 +28,7 @@
from logging import getLogger
from logilab import database as db, common as lgc
-from logilab.common.shellutils import ProgressBar
+from logilab.common.shellutils import ProgressBar, DummyProgressBar
from logilab.common.deprecation import deprecated
from logilab.common.logging_ext import set_log_methods
from logilab.database.sqlgen import SQLGenerator
@@ -72,7 +72,10 @@
sqlstmts_as_string = True
sqlstmts = sqlstmts.split(delimiter)
if withpb:
- pb = ProgressBar(len(sqlstmts), title=pbtitle)
+ if sys.stdout.isatty():
+ pb = ProgressBar(len(sqlstmts), title=pbtitle)
+ else:
+ pb = DummyProgressBar()
failed = []
for sql in sqlstmts:
sql = sql.strip()
diff -r eb681a030699 -r 4ee15441f2eb server/test/unittest_postgres.py
--- a/server/test/unittest_postgres.py Mon Jun 22 14:15:16 2015 +0200
+++ b/server/test/unittest_postgres.py Mon Jun 22 14:27:37 2015 +0200
@@ -113,6 +113,24 @@
self.assertEqual(datenaiss.tzinfo, None)
self.assertEqual(datenaiss.utctimetuple()[:5], (1977, 6, 7, 2, 0))
+class PostgresLimitSizeTC(CubicWebTC):
+ configcls = PostgresApptestConfiguration
+
+ def test(self):
+ with self.admin_access.repo_cnx() as cnx:
+ def sql(string):
+ return cnx.system_sql(string).fetchone()[0]
+ yield self.assertEqual, sql("SELECT limit_size('hello
', 'text/html', 20)"), \
+ 'hello
'
+ yield self.assertEqual, sql("SELECT limit_size('hello
', 'text/html', 2)"), \
+ 'he...'
+ yield self.assertEqual, sql("SELECT limit_size('
hello', 'text/html', 2)"), \
+ 'he...'
+ yield self.assertEqual, sql("SELECT limit_size('hello', 'text/html', 2)"), \
+ 'he...'
+ yield self.assertEqual, sql("SELECT limit_size('a>b', 'text/html', 2)"), \
+ 'a>...'
+
if __name__ == '__main__':
from logilab.common.testlib import unittest_main
unittest_main()
diff -r eb681a030699 -r 4ee15441f2eb server/test/unittest_querier.py
--- a/server/test/unittest_querier.py Mon Jun 22 14:15:16 2015 +0200
+++ b/server/test/unittest_querier.py Mon Jun 22 14:27:37 2015 +0200
@@ -360,7 +360,7 @@
result, descr = rset.rows, rset.description
self.assertEqual(descr[0][0], 'String')
self.assertEqual(descr[0][1], 'Int')
- self.assertEqual(result[0][0], 'CWRelation') # XXX may change as schema evolve
+ self.assertEqual(result[0][0], 'RQLExpression') # XXX may change as schema evolve
def test_select_groupby_orderby(self):
rset = self.qexecute('Any N GROUPBY N ORDERBY N WHERE X is CWGroup, X name N')
diff -r eb681a030699 -r 4ee15441f2eb test/unittest_mail.py
--- a/test/unittest_mail.py Mon Jun 22 14:15:16 2015 +0200
+++ b/test/unittest_mail.py Mon Jun 22 14:27:37 2015 +0200
@@ -21,6 +21,7 @@
"""
import os
+import re
import sys
from logilab.common.testlib import unittest_main
@@ -51,7 +52,9 @@
mail = format_mail({'name': 'oim', 'email': 'oim@logilab.fr'},
['test@logilab.fr'], u'un petit cöucou', u'bïjour',
config=self.config)
- self.assertMultiLineEqual(mail.as_string(), """\
+ result = mail.as_string()
+ result = re.sub('^Date: .*$', 'Date: now', result, flags=re.MULTILINE)
+ self.assertMultiLineEqual(result, """\
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
@@ -60,6 +63,7 @@
Reply-to: =?utf-8?q?oim?= , =?utf-8?q?BimBam?=
X-CW: data
To: test@logilab.fr
+Date: now
dW4gcGV0aXQgY8O2dWNvdQ==
""")
@@ -74,7 +78,9 @@
def test_format_mail_euro(self):
mail = format_mail({'name': u'oîm', 'email': u'oim@logilab.fr'},
['test@logilab.fr'], u'un petit cöucou €', u'bïjour €')
- self.assertMultiLineEqual(mail.as_string(), """\
+ result = mail.as_string()
+ result = re.sub('^Date: .*$', 'Date: now', result, flags=re.MULTILINE)
+ self.assertMultiLineEqual(result, """\
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
@@ -82,6 +88,7 @@
From: =?utf-8?q?o=C3=AEm?=
Reply-to: =?utf-8?q?o=C3=AEm?=
To: test@logilab.fr
+Date: now
dW4gcGV0aXQgY8O2dWNvdSDigqw=
""")
diff -r eb681a030699 -r 4ee15441f2eb test/unittest_uilib.py
--- a/test/unittest_uilib.py Mon Jun 22 14:15:16 2015 +0200
+++ b/test/unittest_uilib.py Mon Jun 22 14:27:37 2015 +0200
@@ -30,7 +30,7 @@
from logilab.common.testlib import DocTest, TestCase, unittest_main
-from cubicweb import uilib
+from cubicweb import uilib, utils as cwutils
lxml_version = pkg_resources.get_distribution('lxml').version.split('.')
@@ -171,6 +171,11 @@
'cw.pouet(1,"2")')
self.assertEqual(str(uilib.js.cw.pouet(1, "2").pouet(None)),
'cw.pouet(1,"2").pouet(null)')
+ self.assertEqual(str(uilib.js.cw.pouet(1, cwutils.JSString("$")).pouet(None)),
+ 'cw.pouet(1,$).pouet(null)')
+ self.assertEqual(str(uilib.js.cw.pouet(1, {'callback': cwutils.JSString("cw.cb")}).pouet(None)),
+ 'cw.pouet(1,{callback: cw.cb}).pouet(null)')
+
def test_embedded_css(self):
incoming = u"""voir le ticket
text
"""
diff -r eb681a030699 -r 4ee15441f2eb uilib.py
--- a/uilib.py Mon Jun 22 14:15:16 2015 +0200
+++ b/uilib.py Mon Jun 22 14:27:37 2015 +0200
@@ -32,7 +32,7 @@
from logilab.common.date import ustrftime
from logilab.common.deprecation import deprecated
-from cubicweb.utils import JSString, json_dumps
+from cubicweb.utils import js_dumps
def rql_for_eid(eid):
@@ -353,10 +353,7 @@
def __unicode__(self):
args = []
for arg in self.args:
- if isinstance(arg, JSString):
- args.append(arg)
- else:
- args.append(json_dumps(arg))
+ args.append(js_dumps(arg))
if self.parent:
return u'%s(%s)' % (self.parent, ','.join(args))
return ','.join(args)
@@ -378,6 +375,8 @@
'cw.pouet(1,"2").pouet(null)'
>>> str(js.cw.pouet(1, JSString("$")).pouet(None))
'cw.pouet(1,$).pouet(null)'
+>>> str(js.cw.pouet(1, {'callback': JSString("cw.cb")}).pouet(None))
+'cw.pouet(1,{callback: cw.cb}).pouet(null)'
"""
def domid(string):
diff -r eb681a030699 -r 4ee15441f2eb web/application.py
--- a/web/application.py Mon Jun 22 14:15:16 2015 +0200
+++ b/web/application.py Mon Jun 22 14:27:37 2015 +0200
@@ -473,7 +473,6 @@
req.data['ex'] = ex
if tb:
req.data['excinfo'] = excinfo
- req.form['vid'] = 'error'
errview = self.vreg['views'].select('error', req)
template = self.main_template_id(req)
content = self.vreg['views'].main_template(req, template, view=errview)
diff -r eb681a030699 -r 4ee15441f2eb web/data/cubicweb.ajax.js
--- a/web/data/cubicweb.ajax.js Mon Jun 22 14:15:16 2015 +0200
+++ b/web/data/cubicweb.ajax.js Mon Jun 22 14:27:37 2015 +0200
@@ -363,7 +363,7 @@
}
/**
- * .. function:: loadRemote(url, form, reqtype='GET', sync=false)
+ * .. function:: loadRemote(url, form, reqtype='POST', sync=false)
*
* Asynchronously (unless `sync` argument is set to true) load a URL or path
* and return a deferred whose callbacks args are decoded according to the
diff -r eb681a030699 -r 4ee15441f2eb web/data/cubicweb.edition.js
--- a/web/data/cubicweb.edition.js Mon Jun 22 14:15:16 2015 +0200
+++ b/web/data/cubicweb.edition.js Mon Jun 22 14:27:37 2015 +0200
@@ -573,6 +573,8 @@
width: '0px',
height: '0px'
}));
+ form.removeAttr('cubicweb:target'); // useles from now on, pop it
+ // to make IE9 happy
}
});
}
diff -r eb681a030699 -r 4ee15441f2eb web/data/cubicweb.js
--- a/web/data/cubicweb.js Mon Jun 22 14:15:16 2015 +0200
+++ b/web/data/cubicweb.js Mon Jun 22 14:27:37 2015 +0200
@@ -15,13 +15,10 @@
removeEventListener: function() {},
detachEvent: function() {},
- log: function () {
- var args = [];
- for (var i = 0; i < arguments.length; i++) {
- args.push(arguments[i]);
- }
+ log: function log() {
if (typeof(window) != "undefined" && window.console && window.console.log) {
- window.console.log(args.join(' '));
+ // NOTE console.log requires "console" to be the console to be "this"
+ window.console.log.apply(console, arguments);
}
},
diff -r eb681a030699 -r 4ee15441f2eb web/formwidgets.py
--- a/web/formwidgets.py Mon Jun 22 14:15:16 2015 +0200
+++ b/web/formwidgets.py Mon Jun 22 14:27:37 2015 +0200
@@ -400,6 +400,9 @@
class TextArea(FieldWidget):
"""Simple