# 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