# HG changeset patch
# User Julien Cristau
# Date 1389617267 -3600
# Node ID 2c48c091b6a23d3c312ece6e5d1d171c2dfb45f9
# Parent aff75b69db92f7321723df08e84884124edaa426# Parent 17dc43c1f1bd6dd4da12cbbc70fe79b2f7125f0f
merge 3.18.0 in 3.19 branch
diff -r aff75b69db92 -r 2c48c091b6a2 .hgtags
--- a/.hgtags Tue Jul 02 17:09:04 2013 +0200
+++ b/.hgtags Mon Jan 13 13:47:47 2014 +0100
@@ -30,11 +30,7 @@
c9c492787a8aa1b7916e22eb6498cba1c8fa316c cubicweb-debian-version-3_2_0-1
634c251dd032894850080c4e5aeb0a4e09f888c0 cubicweb-version-3_2_1
e784f8847a124a93e5b385d7a92a2772c050fe82 cubicweb-debian-version-3_2_1-1
-6539ce84f04357ef65ccee0896a30997b16a4ece cubicweb-version-3_2_2
-92d1a15f08f7c5fa87643ffb4273d12cb3f41c63 cubicweb-debian-version-3_2_2-1
-6539ce84f04357ef65ccee0896a30997b16a4ece cubicweb-version-3_2_2
9b21e068fef73c37bcb4e53d006a7bde485f390b cubicweb-version-3_2_2
-92d1a15f08f7c5fa87643ffb4273d12cb3f41c63 cubicweb-debian-version-3_2_2-1
0e07514264aa1b0b671226f41725ea4c066c210a cubicweb-debian-version-3_2_2-1
f60bb84b86cf371f1f25197e00c778b469297721 cubicweb-version-3_2_3
4003d24974f15f17bd03b7efd6a5047cad4e4c41 cubicweb-debian-version-3_2_3-1
@@ -43,10 +39,8 @@
a356da3e725bfcb59d8b48a89d04be05ea261fd3 3.3.1
e3aeb6e6c3bb5c18e8dcf61bae9d654beda6c036 cubicweb-version-3_3_2
bef5e74e53f9de8220451dca4b5863a24a0216fb cubicweb-debian-version-3_3_2-1
-1cf9e44e2f1f4415253b8892a0adfbd3b69e84fd cubicweb-version-3_3_3
+47b5236774a0cf3b1cfe75f6d4bd2ec989644ace cubicweb-version-3_3_3
81973c897c9e78e5e52643e03628654916473196 cubicweb-debian-version-3_3_3-1
-1cf9e44e2f1f4415253b8892a0adfbd3b69e84fd cubicweb-version-3_3_3
-47b5236774a0cf3b1cfe75f6d4bd2ec989644ace cubicweb-version-3_3_3
2ba27ce8ecd9828693ec53c517e1c8810cbbe33e cubicweb-debian-version-3_3_3-2
d46363eac5d71bc1570d69337955154dfcd8fcc8 cubicweb-version-3.3.4
7dc22caa7640bf70fcae55afb6d2326829dacced cubicweb-debian-version-3.3.4-1
@@ -82,11 +76,7 @@
37d025b2aa7735dae4a861059014c560b45b19e6 cubicweb-debian-version-3.5.4-1
1eca47d59fd932fe23f643ca239cf2408e5b1856 cubicweb-version-3.5.5
aad818d9d9b6fdb2ffea56c0a9af718c0b69899d cubicweb-debian-version-3.5.5-1
-b79f361839a7251b35eb8378fbc0773de7c8a815 cubicweb-version-3.5.6
-e6225e8e36c6506c774e0a76acc301d8ae1c1028 cubicweb-debian-version-3.5.6-1
-b79f361839a7251b35eb8378fbc0773de7c8a815 cubicweb-version-3.5.6
4e619e97b3fd70769a0f454963193c10cb87f9d4 cubicweb-version-3.5.6
-e6225e8e36c6506c774e0a76acc301d8ae1c1028 cubicweb-debian-version-3.5.6-1
5f7c939301a1b915e17eec61c05e8e9ab8bdc182 cubicweb-debian-version-3.5.6-1
0fc300eb4746e01f2755b9eefd986d58d8366ccf cubicweb-version-3.5.7
7a96c0544c138a0c5f452e5b2428ce6e2b7cb378 cubicweb-debian-version-3.5.7-1
@@ -98,8 +88,6 @@
4920121d41f28c8075a4f00461911677396fc566 cubicweb-debian-version-3.5.11-1
98af3d02b83e7635207781289cc3445fb0829951 cubicweb-version-3.5.12
4281e1e2d76b9a37f38c0eeb1cbdcaa2fac6533c cubicweb-debian-version-3.5.12-1
-5f957e351b0a60d5c5fff60c560b04e666c3a8c6 cubicweb-version-3.6.0
-17e88f2485d1ea1fb8a3926a274637ce19e95d69 cubicweb-debian-version-3.6.0-1
450804da3ab2476b7ede0c1f956235b4c239734f cubicweb-version-3.6.0
d2ba93fcb8da95ceab08f48f8149a480215f149c cubicweb-debian-version-3.6.0-1
4ae30c9ca11b1edad67d25b76fce672171d02023 cubicweb-version-3.6.1
@@ -107,12 +95,12 @@
0a16f07112b90fb61d2e905855fece77e5a7e39c cubicweb-debian-version-3.6.1-2
bfebe3d14d5390492925fc294dfdafad890a7104 cubicweb-version-3.6.2
f3b4bb9121a0e7ee5961310ff79e61c890948a77 cubicweb-debian-version-3.6.2-1
+9c342fa4f1b73e06917d7dc675949baff442108b cubicweb-version-3.6.3
+f9fce56d6a0c2bc6c4b497b66039a8bbbbdc8074 cubicweb-debian-version-3.6.3-1
270aba1e6fa21dac6b070e7815e6d1291f9c87cd cubicweb-version-3.7.0
0c9ff7e496ce344b7e6bf5c9dd2847daf9034e5e cubicweb-debian-version-3.7.0-1
6b0832bbd1daf27c2ce445af5b5222e1e522fb90 cubicweb-version-3.7.1
9194740f070e64da5a89f6a9a31050a8401ebf0c cubicweb-debian-version-3.7.1-1
-9c342fa4f1b73e06917d7dc675949baff442108b cubicweb-version-3.6.3
-f9fce56d6a0c2bc6c4b497b66039a8bbbbdc8074 cubicweb-debian-version-3.6.3-1
d010f749c21d55cd85c5feb442b9cf816282953c cubicweb-version-3.7.2
8fda29a6c2191ba3cc59242c17b28b34127c75fa cubicweb-debian-version-3.7.2-1
768beb8e15f15e079f8ee6cfc35125e12b19e140 cubicweb-version-3.7.3
@@ -135,10 +123,11 @@
5d05b08adeab1ea301e49ed8537e35ede6db92f6 cubicweb-debian-version-3.8.5-1
1a24c62aefc5e57f61be3d04affd415288e81904 cubicweb-version-3.8.6
607a90073911b6bb941a49b5ec0b0d2a9cd479af cubicweb-debian-version-3.8.6-1
+a1a334d934390043a4293a4ee42bdceb1343246e cubicweb-version-3.8.7
+1cccf88d6dfe42986e1091de4c364b7b5814c54f cubicweb-debian-version-3.8.7-1
+48f468f33704e401a8e7907e258bf1ac61eb8407 cubicweb-version-3.9.x
d9936c39d478b6701a4adef17bc28888ffa011c6 cubicweb-version-3.9.0
eda4940ffef8b7d36127e68de63a52388374a489 cubicweb-debian-version-3.9.0-1
-a1a334d934390043a4293a4ee42bdceb1343246e cubicweb-version-3.8.7
-1cccf88d6dfe42986e1091de4c364b7b5814c54f cubicweb-debian-version-3.8.7-1
4d75f743ed49dd7baf8bde7b0e475244933fa08e cubicweb-version-3.9.1
9bd75af3dca36d7be5d25fc5ab1b89b34c811456 cubicweb-debian-version-3.9.1-1
e51796b9caf389c224c6f66dcb8aa75bf1b82eff cubicweb-version-3.9.2
@@ -155,6 +144,11 @@
1c01f9dffd64d507863c9f8f68e3585b7aa24374 cubicweb-debian-version-3.9.7-1
eed788018b595d46a55805bd8d2054c401812b2b cubicweb-version-3.9.8
e4dba8ae963701a36be94ae58c790bc97ba029bb cubicweb-debian-version-3.9.8-1
+df0b2de62cec10c84a2fff5233db05852cbffe93 cubicweb-version-3.9.9
+1ba51b00fc44faa0d6d57448000aaa1fd5c6ab57 cubicweb-debian-version-3.9.9-1
+b7db1f59355832a409d2032e19c84cfffdb3b265 cubicweb-debian-version-3.9.9-2
+09c98763ae9d43616d047c1b25d82b4e41a4362f cubicweb-debian-version-3.9.9-3
+a62f24e1497e953fbaed5894f6064a64f7ac0be3 cubicweb-version-3.10.x
0793fe84651be36f8de9b4faba3781436dc07be0 cubicweb-version-3.10.0
9ef1347f8d99e7daad290738ef93aa894a2c03ce cubicweb-debian-version-3.10.0-1
6c6859a676732c845af69f92e74d4aafae12f83a cubicweb-version-3.10.1
@@ -163,15 +157,7 @@
4a87c8af6f3ffe59c6048ebbdc1b6b204d0b9c7f cubicweb-debian-version-3.10.2-1
8eb58d00a0cedcf7b275b1c7f43b08e2165f655c cubicweb-version-3.10.3
303b150ebb7a92b2904efd52b446457999cab370 cubicweb-debian-version-3.10.3-1
-3829498510a754b1b8a40582cb8dcbca9145fc9d cubicweb-version-3.10.4
-49f1226f2fab6d9ff17eb27d5a66732a4e5b5add cubicweb-debian-version-3.10.4-1
-df0b2de62cec10c84a2fff5233db05852cbffe93 cubicweb-version-3.9.9
-1ba51b00fc44faa0d6d57448000aaa1fd5c6ab57 cubicweb-debian-version-3.9.9-1
-b7db1f59355832a409d2032e19c84cfffdb3b265 cubicweb-debian-version-3.9.9-2
-09c98763ae9d43616d047c1b25d82b4e41a4362f cubicweb-debian-version-3.9.9-3
-3829498510a754b1b8a40582cb8dcbca9145fc9d cubicweb-version-3.10.4
d73733479a3af453f06b849ed88d120784ce9224 cubicweb-version-3.10.4
-49f1226f2fab6d9ff17eb27d5a66732a4e5b5add cubicweb-debian-version-3.10.4-1
7b41930e1d32fea3989a85f6ea7281983300adb1 cubicweb-debian-version-3.10.4-1
159d0dbe07d9eb1c6ace4c5e160d1ec6e6762086 cubicweb-version-3.10.5
e2e7410e994777589aec218d31eef9ff8d893f92 cubicweb-debian-version-3.10.5-1
@@ -181,21 +167,20 @@
bf5d9a1415e3c9abe6b68ba3b24a8ad741f9de3c cubicweb-debian-version-3.10.7-1
e581a86a68f089946a98c966ebca7aee58a5718f cubicweb-version-3.10.8
132b525de25bc75ed6389c45aee77e847cb3a437 cubicweb-debian-version-3.10.8-1
-48f468f33704e401a8e7907e258bf1ac61eb8407 cubicweb-version-3.9.x
37432cede4fe55b97fc2e9be0a2dd20e8837a848 cubicweb-version-3.11.0
8daabda9f571863e8754f8ab722744c417ba3abf cubicweb-debian-version-3.11.0-1
d0410eb4d8bbf657d7f32b0c681db09b1f8119a0 cubicweb-version-3.11.1
77318f1ec4aae3523d455e884daf3708c3c79af7 cubicweb-debian-version-3.11.1-1
56ae3cd5f8553678a2b1d4121b61241598d0ca68 cubicweb-version-3.11.2
954b5b51cd9278eb45d66be1967064d01ab08453 cubicweb-debian-version-3.11.2-1
+b7a124f9aed2c7c9c86c6349ddd9f0a07023f0ca cubicweb-version-3.11.3
+b3c6702761a18a41fdbb7bc1083f92aefce07765 cubicweb-debian-version-3.11.3-1
fd502219eb76f4bfd239d838a498a1d1e8204baf cubicweb-version-3.12.0
92b56939b7c77bbf443b893c495a20f19bc30702 cubicweb-debian-version-3.12.0-1
59701627adba73ee97529f6ea0e250a0f3748e32 cubicweb-version-3.12.1
07e2c9c7df2617c5ecfa84cb819b3ee8ef91d1f2 cubicweb-debian-version-3.12.1-1
5a9b6bc5653807500c30a7eb0e95b90fd714fec3 cubicweb-version-3.12.2
6d418fb3ffed273562aae411efe323d5138b592a cubicweb-debian-version-3.12.2-1
-b7a124f9aed2c7c9c86c6349ddd9f0a07023f0ca cubicweb-version-3.11.3
-b3c6702761a18a41fdbb7bc1083f92aefce07765 cubicweb-debian-version-3.11.3-1
e712bc6f1f71684f032bfcb9bb151a066c707dec cubicweb-version-3.12.3
ba8fe4f2e408c3fdf6c297cd42c2577dcac50e71 cubicweb-debian-version-3.12.3-1
5cd0dbc26882f60e3f11ec55e7f058d94505e7ed cubicweb-version-3.12.4
@@ -204,14 +189,16 @@
6dfe78a0797ccc34962510f8c2a57f63d65ce41e cubicweb-debian-version-3.12.5-1
a18dac758150fe9c1f9e4958d898717c32a8f679 cubicweb-version-3.12.6
105767487c7075dbcce36474f1af0485985cbf2c cubicweb-debian-version-3.12.6-1
-b661ef475260ca7d9ea5c36ba2cc86e95e5b17d3 cubicweb-version-3.13.0
-a96137858f571711678954477da6f7f435870cea cubicweb-debian-version-3.13.0-1
628fe57ce746c1dac87fb1b078b2026057df894e cubicweb-version-3.12.7
a07517985136bbbfa6610c428a1b42cd04cd530b cubicweb-debian-version-3.12.7-1
50122a47ce4fb2ecbf3cf20ed2777f4276c93609 cubicweb-version-3.12.8
cf49ed55685a810d8d73585330ad1a57cc76260d cubicweb-debian-version-3.12.8-1
cb2990aaa63cbfe593bcf3afdbb9071e4c76815a cubicweb-version-3.12.9
92464e39134c70e4ddbe6cd78a6e3338a3b88b05 cubicweb-debian-version-3.12.9-1
+074c848a3712a77737d9a1bfbb618c75f5c0cbfa cubicweb-version-3.12.10
+9dfd21fa0a8b9f121a08866ad3e2ebd1dd06790d cubicweb-debian-version-3.12.10-1
+b661ef475260ca7d9ea5c36ba2cc86e95e5b17d3 cubicweb-version-3.13.0
+a96137858f571711678954477da6f7f435870cea cubicweb-debian-version-3.13.0-1
7d84317ef185a10c5eb78e6086f2297d2f4bd1e3 cubicweb-version-3.13.1
cc0578049cbe8b1d40009728e36c17e45da1fc6b cubicweb-debian-version-3.13.1-1
f9227b9d61835f03163b8133a96da35db37a0c8d cubicweb-version-3.13.2
@@ -220,11 +207,8 @@
fb48c55cb80234bc0164c9bcc0e2cfc428836e5f cubicweb-debian-version-3.13.3-1
223ecf0620b6c87d997f8011aca0d9f0ee4750af cubicweb-version-3.13.4
52f26475d764129c5559b2d80fd57e6ea1bdd6ba cubicweb-debian-version-3.13.4-1
-a62f24e1497e953fbaed5894f6064a64f7ac0be3 cubicweb-version-3.10.x
20d9c550c57eb6f9adcb0cfab1c11b6b8793afb6 cubicweb-version-3.13.5
2e9dd7d945557c210d3b79153c65f6885e755315 cubicweb-debian-version-3.13.5-1
-074c848a3712a77737d9a1bfbb618c75f5c0cbfa cubicweb-version-3.12.10
-9dfd21fa0a8b9f121a08866ad3e2ebd1dd06790d cubicweb-debian-version-3.12.10-1
17c007ad845abbac82e12146abab32a634657574 cubicweb-version-3.13.6
8a8949ca5351d48c5cf795ccdff06c1d4aab2ce0 cubicweb-debian-version-3.13.6-1
68e8c81fa96d6bcd21cc17bc9832d388ce05a9eb cubicweb-version-3.13.7
@@ -233,10 +217,10 @@
43f83f5d0a4d57a06e9a4990bc957fcfa691eec3 cubicweb-debian-version-3.13.8-1
07afe32945aa275052747f78ef1f55858aaf6fa9 cubicweb-version-3.13.9
0a3cb5e60d57a7a9851371b4ae487094ec2bf614 cubicweb-debian-version-3.13.9-1
+2ad4e5173c73a43804c265207bcabb8940bd42f4 cubicweb-version-3.13.10
+2eab9a5a6bf8e3b0cf706bee8cdf697759c0a33a cubicweb-debian-version-3.13.10-1
5c4390eb10c3fe76a81e6fccec109d7097dc1a8d cubicweb-version-3.14.0
0bfe22fceb383b46d62b437bf5dd0141a714afb8 cubicweb-debian-version-3.14.0-1
-2ad4e5173c73a43804c265207bcabb8940bd42f4 cubicweb-version-3.13.10
-2eab9a5a6bf8e3b0cf706bee8cdf697759c0a33a cubicweb-debian-version-3.13.10-1
793d2d327b3ebf0b82b2735cf3ccb86467d1c08a cubicweb-version-3.14.1
6928210da4fc25d086b5b8d5ff2029da41aade2e cubicweb-debian-version-3.14.1-1
049a3819f03dc79d803be054cc3bfe8425313f63 cubicweb-version-3.14.2
@@ -250,8 +234,6 @@
55fc796ed5d5f31245ae60bd148c9e42657a1af6 cubicweb-debian-version-3.14.5-1
db021578232b885dc5e55dfca045332ce01e7f35 cubicweb-version-3.14.6
75364c0994907764715bd5011f6a59d934dbeb7d cubicweb-debian-version-3.14.6-1
-0642b2d03acaa5e065cae7590e82b388a280ca22 cubicweb-version-3.15.0
-925db25a3250c5090cf640fc2b02bde5818b9798 cubicweb-debian-version-3.15.0-1
3ba3ee5b3a89a54d1dc12ed41d5c12232eda1952 cubicweb-version-3.14.7
20ee573bd2379a00f29ff27bb88a8a3344d4cdfe cubicweb-debian-version-3.14.7-1
15fe07ff687238f8cc09d8e563a72981484085b3 cubicweb-version-3.14.8
@@ -260,6 +242,8 @@
68c762adf2d5a2c338910ef1091df554370586f0 cubicweb-debian-version-3.14.9-1
0ff798f80138ca8f50a59f42284380ce8f6232e8 cubicweb-version-3.14.10
197bcd087c87cd3de9f21f5bf40bd6203c074f1f cubicweb-debian-version-3.14.10-1
+0642b2d03acaa5e065cae7590e82b388a280ca22 cubicweb-version-3.15.0
+925db25a3250c5090cf640fc2b02bde5818b9798 cubicweb-debian-version-3.15.0-1
783a5df54dc742e63c8a720b1582ff08366733bd cubicweb-version-3.15.1
fe5e60862b64f1beed2ccdf3a9c96502dfcd811b cubicweb-debian-version-3.15.1-1
2afc157ea9b2b92eccb0f2d704094e22ce8b5a05 cubicweb-version-3.15.2
@@ -291,19 +275,51 @@
ee860c51f56bd65c4f6ea363462c02700d1dab5a cubicweb-version-3.16.3
ee860c51f56bd65c4f6ea363462c02700d1dab5a cubicweb-debian-version-3.16.3-1
ee860c51f56bd65c4f6ea363462c02700d1dab5a cubicweb-centos-version-3.16.3-1
-cc1a0aad580cf93d26959f97d8d6638e786c1082 cubicweb-version-3.17.0
-22be40c492e9034483bfec379ca11462ea97825b cubicweb-debian-version-3.17.0-1
-09a0c7ea6c3cb97bbbeed3795b3c3715ceb9566b cubicweb-debian-version-3.17.0-2
041804bc48e91e440a5b573ceb0df5bf22863b80 cubicweb-version-3.16.4
041804bc48e91e440a5b573ceb0df5bf22863b80 cubicweb-debian-version-3.16.4-1
041804bc48e91e440a5b573ceb0df5bf22863b80 cubicweb-centos-version-3.16.4-1
810a05fba1a46ab893b6cadac109097a047f8355 cubicweb-version-3.16.5
810a05fba1a46ab893b6cadac109097a047f8355 cubicweb-debiann-version-3.16.5-1
810a05fba1a46ab893b6cadac109097a047f8355 cubicweb-centos-version-3.16.5-1
-f98d1c46ed9fd5db5262cf5be1c8e159c90efc8b cubicweb-version-3.17.1
+b4ccaf13081d2798c0414d002e743cb0bf6d81f8 cubicweb-version-3.16.6
+b4ccaf13081d2798c0414d002e743cb0bf6d81f8 cubicweb-centos-version-3.16.6-1
+b4ccaf13081d2798c0414d002e743cb0bf6d81f8 cubicweb-debian-version-3.16.6-1
+cc1a0aad580cf93d26959f97d8d6638e786c1082 cubicweb-version-3.17.0
+22be40c492e9034483bfec379ca11462ea97825b cubicweb-debian-version-3.17.0-1
+09a0c7ea6c3cb97bbbeed3795b3c3715ceb9566b cubicweb-debian-version-3.17.0-2
f98d1c46ed9fd5db5262cf5be1c8e159c90efc8b cubicweb-version-3.17.1
-73f2ad404716cd211b735e67ee16875f1fff7374 cubicweb-debian-version-3.17.1-1
f98d1c46ed9fd5db5262cf5be1c8e159c90efc8b cubicweb-debian-version-3.17.1-1
f98d1c46ed9fd5db5262cf5be1c8e159c90efc8b cubicweb-centos-version-3.17.1-1
+965f894b63cb7c4456acd82257709f563bde848f cubicweb-centos-version-3.17.1-2
195e519fe97c8d1a5ab5ccb21bf7c88e5801b657 cubicweb-version-3.17.2
195e519fe97c8d1a5ab5ccb21bf7c88e5801b657 cubicweb-debian-version-3.17.2-1
+32b4d5314fd90fe050c931886190f9a372686148 cubicweb-version-3.17.3
+32b4d5314fd90fe050c931886190f9a372686148 cubicweb-debian-version-3.17.3-1
+32b4d5314fd90fe050c931886190f9a372686148 cubicweb-centos-version-3.17.3-1
+c7ba8e5d2e45e3d1289c1403df40d7dcb5e62acb cubicweb-version-3.17.4
+c7ba8e5d2e45e3d1289c1403df40d7dcb5e62acb cubicweb-debian-version-3.17.4-1
+c7ba8e5d2e45e3d1289c1403df40d7dcb5e62acb cubicweb-centos-version-3.17.4-1
+15dd5b37998b8ef5e8fab1ea0491e6bd8e9f3355 cubicweb-centos-version-3.17.5-1
+15dd5b37998b8ef5e8fab1ea0491e6bd8e9f3355 cubicweb-version-3.17.5
+15dd5b37998b8ef5e8fab1ea0491e6bd8e9f3355 cubicweb-debian-version-3.17.5-1
+5b9fedf67a2912a80fe315a477df9e3ab104c734 cubicweb-centos-version-3.17.6-1
+5b9fedf67a2912a80fe315a477df9e3ab104c734 cubicweb-version-3.17.6
+5b9fedf67a2912a80fe315a477df9e3ab104c734 cubicweb-debian-version-3.17.6-1
+483181543899a762d068cfdc3ae751b54adc3f14 cubicweb-centos-version-3.17.7-1
+483181543899a762d068cfdc3ae751b54adc3f14 cubicweb-version-3.17.7
+483181543899a762d068cfdc3ae751b54adc3f14 cubicweb-debian-version-3.17.7-1
+909eb8b584c437b3d2580beff1325c3d5b5dcfb5 cubicweb-centos-version-3.17.8-1
+909eb8b584c437b3d2580beff1325c3d5b5dcfb5 cubicweb-version-3.17.8
+909eb8b584c437b3d2580beff1325c3d5b5dcfb5 cubicweb-debian-version-3.17.8-1
+5668d210e49c910180ff27712b6ae9ce8286e06c cubicweb-version-3.17.9
+5668d210e49c910180ff27712b6ae9ce8286e06c cubicweb-debian-version-3.17.9-1
+fe0e1863a13772836f40f743cc6fe4865f288ed3 cubicweb-centos-version-3.17.10-1
+fe0e1863a13772836f40f743cc6fe4865f288ed3 cubicweb-version-3.17.10
+fe0e1863a13772836f40f743cc6fe4865f288ed3 cubicweb-debian-version-3.17.10-1
+7f67db7c848ec20152daf489d9e11f0fc8402e9b cubicweb-centos-version-3.17.11-1
+7f67db7c848ec20152daf489d9e11f0fc8402e9b cubicweb-version-3.17.11
+7f67db7c848ec20152daf489d9e11f0fc8402e9b cubicweb-debian-version-3.17.11-1
+b02e2912cad5d80395e488c55b548495e8320198 cubicweb-debian-version-3.17.11-2
+db37bf35a1474843ded0a537f9cb4838f4a78cda cubicweb-version-3.18.0
+db37bf35a1474843ded0a537f9cb4838f4a78cda cubicweb-debian-version-3.18.0-1
+db37bf35a1474843ded0a537f9cb4838f4a78cda cubicweb-centos-version-3.18.0-1
diff -r aff75b69db92 -r 2c48c091b6a2 README
--- a/README Tue Jul 02 17:09:04 2013 +0200
+++ b/README Mon Jan 13 13:47:47 2014 +0100
@@ -34,4 +34,4 @@
Look in the doc/ subdirectory or read http://docs.cubicweb.org/
-
+It includes the Entypo pictograms by Daniel Bruce — www.entypo.com
diff -r aff75b69db92 -r 2c48c091b6a2 __init__.py
--- a/__init__.py Tue Jul 02 17:09:04 2013 +0200
+++ b/__init__.py Mon Jan 13 13:47:47 2014 +0100
@@ -22,6 +22,8 @@
# ignore the pygments UserWarnings
import warnings
+import cPickle
+import zlib
warnings.filterwarnings('ignore', category=UserWarning,
message='.*was already imported',
module='.*pygments')
@@ -120,6 +122,26 @@
binary.seek(0)
return binary
+ def __eq__(self, other):
+ if not isinstance(other, Binary):
+ return False
+ return self.getvalue(), other.getvalue()
+
+
+ # Binary helpers to store/fetch python objects
+
+ @classmethod
+ def zpickle(cls, obj):
+ """ return a Binary containing a gzipped pickle of obj """
+ retval = cls()
+ retval.write(zlib.compress(cPickle.dumps(obj, protocol=2)))
+ return retval
+
+ def unzpickle(self):
+ """ decompress and loads the stream before returning it """
+ return cPickle.loads(zlib.decompress(self.getvalue()))
+
+
def str_or_binary(value):
if isinstance(value, Binary):
return value
@@ -127,7 +149,6 @@
BASE_CONVERTERS['Password'] = str_or_binary
-
# use this dictionary to rename entity types while keeping bw compat
ETYPE_NAME_MAP = {}
diff -r aff75b69db92 -r 2c48c091b6a2 __pkginfo__.py
--- a/__pkginfo__.py Tue Jul 02 17:09:04 2013 +0200
+++ b/__pkginfo__.py Mon Jan 13 13:47:47 2014 +0100
@@ -22,7 +22,7 @@
modname = distname = "cubicweb"
-numversion = (3, 17, 2)
+numversion = (3, 18, 0)
version = '.'.join(str(num) for num in numversion)
description = "a repository of entities / relations for knowledge management"
@@ -40,10 +40,10 @@
]
__depends__ = {
- 'logilab-common': '>= 0.59.0',
+ 'logilab-common': '>= 0.60.0',
'logilab-mtconverter': '>= 0.8.0',
'rql': '>= 0.31.2',
- 'yams': '>= 0.37.0',
+ 'yams': '>= 0.39.0',
#gettext # for xgettext, msgcat, etc...
# web dependancies
'simplejson': '>= 2.0.9',
@@ -51,8 +51,7 @@
'Twisted': '',
# XXX graphviz
# server dependencies
- 'logilab-database': '>= 1.10',
- 'pysqlite': '>= 2.5.5', # XXX install pysqlite2
+ 'logilab-database': '>= 1.11',
'passlib': '',
}
diff -r aff75b69db92 -r 2c48c091b6a2 _exceptions.py
--- a/_exceptions.py Tue Jul 02 17:09:04 2013 +0200
+++ b/_exceptions.py Mon Jan 13 13:47:47 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -19,6 +19,10 @@
__docformat__ = "restructuredtext en"
+from warnings import warn
+
+from logilab.common.decorators import cachedproperty
+
from yams import ValidationError as ValidationError
# abstract exceptions #########################################################
@@ -61,7 +65,7 @@
"""
class AuthenticationError(ConnectionError):
- """raised when when an attempt to establish a connection failed do to wrong
+ """raised when an attempt to establish a connection failed due to wrong
connection information (login / password or other authentication token)
"""
@@ -81,6 +85,26 @@
class UniqueTogetherError(RepositoryError):
"""raised when a unique_together constraint caused an IntegrityError"""
+ def __init__(self, session, **kwargs):
+ self.session = session
+ assert 'rtypes' in kwargs or 'cstrname' in kwargs
+ self.kwargs = kwargs
+
+ @cachedproperty
+ def rtypes(self):
+ if 'rtypes' in self.kwargs:
+ return self.kwargs['rtypes']
+ cstrname = unicode(self.kwargs['cstrname'])
+ cstr = self.session.find('CWUniqueTogetherConstraint', name=cstrname).one()
+ return sorted(rtype.name for rtype in cstr.relations)
+
+ @cachedproperty
+ def args(self):
+ warn('[3.18] UniqueTogetherError.args is deprecated, just use '
+ 'the .rtypes accessor.',
+ DeprecationWarning)
+ # the first argument, etype, is never used and was never garanteed anyway
+ return None, self.rtypes
# security exceptions #########################################################
@@ -134,6 +158,15 @@
a non final entity
"""
+class MultipleResultsError(CubicWebRuntimeError):
+ """raised when ResultSet.one() is called on a resultset with multiple rows
+ of multiple columns.
+ """
+
+class NoResultError(CubicWebRuntimeError):
+ """raised when no result is found but at least one is expected.
+ """
+
class UndoTransactionException(QueryError):
"""Raised when undoing a transaction could not be performed completely.
diff -r aff75b69db92 -r 2c48c091b6a2 cubicweb.spec
--- a/cubicweb.spec Tue Jul 02 17:09:04 2013 +0200
+++ b/cubicweb.spec Mon Jan 13 13:47:47 2014 +0100
@@ -7,7 +7,7 @@
%endif
Name: cubicweb
-Version: 3.17.2
+Version: 3.18.0
Release: logilab.1%{?dist}
Summary: CubicWeb is a semantic web application framework
Source0: http://download.logilab.org/pub/cubicweb/cubicweb-%{version}.tar.gz
@@ -20,11 +20,11 @@
BuildArch: noarch
Requires: %{python}
-Requires: %{python}-logilab-common >= 0.59.0
+Requires: %{python}-logilab-common >= 0.60.0
Requires: %{python}-logilab-mtconverter >= 0.8.0
Requires: %{python}-rql >= 0.31.2
-Requires: %{python}-yams >= 0.37.0
-Requires: %{python}-logilab-database >= 1.10.0
+Requires: %{python}-yams >= 0.39.0
+Requires: %{python}-logilab-database >= 1.11.0
Requires: %{python}-passlib
Requires: %{python}-lxml
Requires: %{python}-twisted-web
@@ -46,11 +46,13 @@
%install
NO_SETUPTOOLS=1 %{__python} setup.py --quiet install --no-compile --prefix=%{_prefix} --root="$RPM_BUILD_ROOT"
+mkdir -p $RPM_BUILD_ROOT/var/log/cubicweb
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-, root, root)
+%dir /var/log/cubicweb
/*
diff -r aff75b69db92 -r 2c48c091b6a2 cwconfig.py
--- a/cwconfig.py Tue Jul 02 17:09:04 2013 +0200
+++ b/cwconfig.py Mon Jan 13 13:47:47 2014 +0100
@@ -53,8 +53,7 @@
If you are not administrator of you machine or if you need to play with some
specific version of |cubicweb| you can use `virtualenv`_ a tool to create
-isolated Python environments. Since version 3.9 |cubicweb| is **`virtualenv`
-friendly** and won't write any file outside the virtualenv directory.
+isolated Python environments.
- instances are stored in :file:`/etc/cubicweb.d`
- temporary files (such as pid file) in :file:`/var/run/cubicweb`
@@ -206,7 +205,7 @@
"""return a list of installed configurations in a directory
according to \*-ctl files
"""
- return [name for name in ('repository', 'twisted', 'all-in-one')
+ return [name for name in ('repository', 'all-in-one')
if exists(join(directory, '%s.conf' % name))]
def guess_configuration(directory):
@@ -328,7 +327,7 @@
# the format below can be useful to debug multi thread issues:
# log_format = '%(asctime)s - [%(threadName)s] (%(name)s) %(levelname)s: %(message)s'
# nor remove appobjects based on unused interface [???]
- cleanup_interface_sobjects = True
+ cleanup_unused_appobjects = True
if (CWDEV and _forced_mode != 'system'):
mode = 'user'
@@ -499,21 +498,11 @@
try:
gendeps = getattr(pkginfo, key.replace('_cubes', ''))
except AttributeError:
- # bw compat
- if hasattr(pkginfo, oldkey):
- warn('[3.8] cube %s: %s is deprecated, use %s dict'
- % (cube, oldkey, key), DeprecationWarning)
- deps = getattr(pkginfo, oldkey)
- else:
- deps = {}
+ deps = {}
else:
deps = dict( (x[len('cubicweb-'):], v)
for x, v in gendeps.iteritems()
if x.startswith('cubicweb-'))
- if not isinstance(deps, dict):
- deps = dict((key, None) for key in deps)
- warn('[3.8] cube %s should define %s as a dict' % (cube, key),
- DeprecationWarning)
for depcube in deps:
try:
newname = CW_MIGRATION_MAP[depcube]
@@ -940,10 +929,9 @@
' "cubicweb-ctl list")' % appid)
return home
- MODES = ('common', 'repository', 'Any', 'web')
+ MODES = ('common', 'repository', 'Any')
MCOMPAT = {'all-in-one': MODES,
- 'repository': ('common', 'repository', 'Any'),
- 'twisted' : ('common', 'web'),}
+ 'repository': ('common', 'repository', 'Any')}
@classmethod
def accept_mode(cls, mode):
#assert mode in cls.MODES, mode
@@ -1189,11 +1177,13 @@
sourcedirs.append(self.i18n_lib_dir())
return i18n.compile_i18n_catalogs(sourcedirs, i18ndir, langs)
- def sendmails(self, msgs):
+ def sendmails(self, msgs, fromaddr=None):
"""msgs: list of 2-uple (message object, recipients). Return False
if connection to the smtp server failed, else True.
"""
server, port = self['smtp-host'], self['smtp-port']
+ if fromaddr is None:
+ fromaddr = '%s <%s>' % (self['sender-name'], self['sender-addr'])
SMTP_LOCK.acquire()
try:
try:
@@ -1202,10 +1192,9 @@
self.exception("can't connect to smtp server %s:%s (%s)",
server, port, ex)
return False
- heloaddr = '%s <%s>' % (self['sender-name'], self['sender-addr'])
for msg, recipients in msgs:
try:
- smtp.sendmail(heloaddr, recipients, msg.as_string())
+ smtp.sendmail(fromaddr, recipients, msg.as_string())
except Exception as ex:
self.exception("error sending mail to %s (%s)",
recipients, ex)
diff -r aff75b69db92 -r 2c48c091b6a2 cwctl.py
--- a/cwctl.py Tue Jul 02 17:09:04 2013 +0200
+++ b/cwctl.py Mon Jan 13 13:47:47 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -48,13 +48,9 @@
from cubicweb.toolsutils import Command, rm, create_dir, underline_title
from cubicweb.__pkginfo__ import version
-if support_args(CommandLine, 'check_duplicated_command'):
- # don't check duplicated commands, it occurs when reloading site_cubicweb
- CWCTL = CommandLine('cubicweb-ctl', 'The CubicWeb swiss-knife.',
- version=version, check_duplicated_command=False)
-else:
- CWCTL = CommandLine('cubicweb-ctl', 'The CubicWeb swiss-knife.',
- version=version)
+# don't check duplicated commands, it occurs when reloading site_cubicweb
+CWCTL = CommandLine('cubicweb-ctl', 'The CubicWeb swiss-knife.',
+ version=version, check_duplicated_command=False)
def wait_process_end(pid, maxtry=10, waittime=1):
"""wait for a process to actually die"""
@@ -266,12 +262,7 @@
if tinfo:
descr = getattr(tinfo, 'description', '')
if not descr:
- descr = getattr(tinfo, 'short_desc', '')
- if descr:
- warn('[3.8] short_desc is deprecated, update %s'
- ' pkginfo' % cube, DeprecationWarning)
- else:
- descr = tinfo.__doc__
+ descr = tinfo.__doc__
if descr:
print ' '+ ' \n'.join(descr.splitlines())
modes = detect_available_modes(cwcfg.cube_dir(cube))
@@ -357,7 +348,7 @@
}),
('config',
{'short': 'c', 'type' : 'choice', 'metavar': '',
- 'choices': ('all-in-one', 'repository', 'twisted'),
+ 'choices': ('all-in-one', 'repository'),
'default': 'all-in-one',
'help': 'installation type, telling which part of an instance '
'should be installed. You can list available configurations using the'
@@ -751,6 +742,7 @@
print '\n' + underline_title('Upgrading the instance %s' % appid)
from logilab.common.changelog import Version
config = cwcfg.config_for(appid)
+ instance_running = exists(config['pid-file'])
config.repairing = True # notice we're not starting the server
config.verbosity = self.config.verbosity
set_sources_mode = getattr(config, 'set_sources_mode', None)
@@ -760,6 +752,7 @@
mih = config.migration_handler()
repo = mih.repo_connect()
vcconf = repo.get_versions()
+ helper = self.config_helper(config, required=False)
if self.config.force_cube_version:
for cube, version in self.config.force_cube_version.iteritems():
vcconf[cube] = Version(version)
@@ -782,7 +775,7 @@
if cubicwebversion > applcubicwebversion:
toupgrade.append(('cubicweb', applcubicwebversion, cubicwebversion))
# only stop once we're sure we have something to do
- if not (CWDEV or self.config.nostartstop):
+ if instance_running and not (CWDEV or self.config.nostartstop):
StopInstanceCommand(self.logger).stop_instance(appid)
# run cubicweb/componants migration scripts
if self.config.fs_only or toupgrade:
@@ -798,8 +791,10 @@
if not self.i18nupgrade(config):
return
print
+ if helper:
+ helper.postupgrade(repo)
print '-> instance migrated.'
- if not (CWDEV or self.config.nostartstop):
+ if instance_running and not (CWDEV or self.config.nostartstop):
# restart instance through fork to get a proper environment, avoid
# uicfg pb (and probably gettext catalogs, to check...)
forkcmd = '%s start %s' % (sys.argv[0], appid)
@@ -1038,9 +1033,56 @@
raise ConfigurationError('unknown configuration key "%s" for mode %s' % (key, appcfg.name))
appcfg.save()
+
+# WSGI #########
+
+def wsgichoices():
+ try:
+ from werkzeug import serving
+ except ImportError:
+ return ('stdlib',)
+ return ('stdlib', 'werkzeug')
+
+class WSGIDebugStartHandler(InstanceCommand):
+ """Start an interactive wsgi server """
+ name = 'wsgi'
+ actionverb = 'started'
+ arguments = ''
+ options = (
+ ('method',
+ {'short': 'm',
+ 'type': 'choice',
+ 'metavar': '',
+ 'default': 'stdlib',
+ 'choices': wsgichoices(),
+ 'help': 'wsgi utility/method'}),
+ ('loglevel',
+ {'short': 'l',
+ 'type' : 'choice',
+ 'metavar': '',
+ 'default': 'debug',
+ 'choices': ('debug', 'info', 'warning', 'error'),
+ 'help': 'debug if -D is set, error otherwise',
+ }),
+ )
+
+ def wsgi_instance(self, appid):
+ config = cwcfg.config_for(appid, debugmode=1)
+ init_cmdline_log_threshold(config, self['loglevel'])
+ assert config.name == 'all-in-one'
+ meth = self['method']
+ if meth == 'stdlib':
+ from cubicweb.wsgi import server
+ else:
+ from cubicweb.wsgi import wz as server
+ return server.run(config)
+
+
+
for cmdcls in (ListCommand,
CreateInstanceCommand, DeleteInstanceCommand,
StartInstanceCommand, StopInstanceCommand, RestartInstanceCommand,
+ WSGIDebugStartHandler,
ReloadConfigurationCommand, StatusCommand,
UpgradeInstanceCommand,
ListVersionsInstanceCommand,
@@ -1051,6 +1093,8 @@
):
CWCTL.register(cmdcls)
+
+
def run(args):
"""command line tool"""
import os
diff -r aff75b69db92 -r 2c48c091b6a2 cwvreg.py
--- a/cwvreg.py Tue Jul 02 17:09:04 2013 +0200
+++ b/cwvreg.py Mon Jan 13 13:47:47 2014 +0100
@@ -211,8 +211,7 @@
from cubicweb import (CW_SOFTWARE_ROOT, ETYPE_NAME_MAP, CW_EVENT_MANAGER,
onevent, Binary, UnknownProperty, UnknownEid)
-from cubicweb.predicates import (implements, appobject_selectable,
- _reset_is_instance_cache)
+from cubicweb.predicates import appobject_selectable, _reset_is_instance_cache
@onevent('before-registry-reload')
@@ -230,15 +229,6 @@
sys.modules.pop('cubicweb.web.uicfg', None)
sys.modules.pop('cubicweb.web.uihelper', None)
-def use_interfaces(obj):
- """return interfaces required by the given object by searching for
- `implements` predicate
- """
- impl = obj.__select__.search_selector(implements)
- if impl:
- return sorted(impl.expected_ifaces)
- return ()
-
def require_appobject(obj):
"""return appobjects required by the given object by searching for
`appobject_selectable` predicate
@@ -568,7 +558,6 @@
def reset(self):
CW_EVENT_MANAGER.emit('before-registry-reset', self)
super(CWRegistryStore, self).reset()
- self._needs_iface = {}
self._needs_appobject = {}
# two special registries, propertydefs which care all the property
# definitions, and propertyvals which contains values for those
@@ -626,6 +615,15 @@
self.register_objects(path)
CW_EVENT_MANAGER.emit('after-registry-reload')
+ def load_file(self, filepath, modname):
+ # override to allow some instrumentation (eg localperms)
+ modpath = modname.split('.')
+ try:
+ self.currently_loading_cube = modpath[modpath.index('cubes') + 1]
+ except ValueError:
+ self.currently_loading_cube = 'cubicweb'
+ return super(CWRegistryStore, self).load_file(filepath, modname)
+
def _set_schema(self, schema):
"""set instance'schema"""
self.schema = schema
@@ -641,20 +639,6 @@
for obj in objects:
obj.schema = schema
- @deprecated('[3.9] use .register instead')
- def register_if_interface_found(self, obj, ifaces, **kwargs):
- """register `obj` but remove it if no entity class implements one of
- the given `ifaces` interfaces at the end of the registration process.
-
- Extra keyword arguments are given to the
- :meth:`~cubicweb.cwvreg.CWRegistryStore.register` function.
- """
- self.register(obj, **kwargs)
- if not isinstance(ifaces, (tuple, list)):
- self._needs_iface[obj] = (ifaces,)
- else:
- self._needs_iface[obj] = ifaces
-
def register(self, obj, *args, **kwargs):
"""register `obj` application object into `registryname` or
`obj.__registry__` if not specified, with identifier `oid` or
@@ -665,15 +649,6 @@
"""
obj = related_appobject(obj)
super(CWRegistryStore, self).register(obj, *args, **kwargs)
- # XXX bw compat
- ifaces = use_interfaces(obj)
- if ifaces:
- if not obj.__name__.endswith('Adapter') and \
- any(iface for iface in ifaces if not isinstance(iface, basestring)):
- warn('[3.9] %s: interfaces in implements selector are '
- 'deprecated in favor of adapters / adaptable '
- 'selector' % obj.__name__, DeprecationWarning)
- self._needs_iface[obj] = ifaces
depends_on = require_appobject(obj)
if depends_on is not None:
self._needs_appobject[obj] = depends_on
@@ -687,41 +662,14 @@
def initialization_completed(self):
"""cw specific code once vreg initialization is completed:
- * remove objects requiring a missing interface, unless
- config.cleanup_interface_sobjects is false
+ * remove objects requiring a missing appobject, unless
+ config.cleanup_unused_appobjects is false
* init rtags
"""
# we may want to keep interface dependent objects (e.g.for i18n
# catalog generation)
- if self.config.cleanup_interface_sobjects:
- # XXX deprecated with cw 3.9: remove appobjects that don't support
- # any available interface
- implemented_interfaces = set()
- if 'Any' in self.get('etypes', ()):
- for etype in self.schema.entities():
- if etype.final:
- continue
- cls = self['etypes'].etype_class(etype)
- if cls.__implements__:
- warn('[3.9] %s: using __implements__/interfaces are '
- 'deprecated in favor of adapters' % cls.__name__,
- DeprecationWarning)
- for iface in cls.__implements__:
- implemented_interfaces.update(iface.__mro__)
- implemented_interfaces.update(cls.__mro__)
- for obj, ifaces in self._needs_iface.items():
- ifaces = frozenset(isinstance(iface, basestring)
- and iface in self.schema
- and self['etypes'].etype_class(iface)
- or iface
- for iface in ifaces)
- if not ('Any' in ifaces or ifaces & implemented_interfaces):
- reg = self[obj_registries(obj)[0]]
- self.debug('unregister %s (no implemented '
- 'interface among %s)', reg.objid(obj), ifaces)
- self.unregister(obj)
- # since 3.9: remove appobjects which depending on other, unexistant
- # appobjects
+ if self.config.cleanup_unused_appobjects:
+ # remove appobjects which depend on other, unexistant appobjects
for obj, (regname, regids) in self._needs_appobject.items():
try:
registry = self[regname]
@@ -740,8 +688,8 @@
if 'uicfg' in self: # 'uicfg' is not loaded in a pure repository mode
for rtags in self['uicfg'].itervalues():
for rtag in rtags:
- # don't check rtags if we don't want to cleanup_interface_sobjects
- rtag.init(self.schema, check=self.config.cleanup_interface_sobjects)
+ # don't check rtags if we don't want to cleanup_unused_appobjects
+ rtag.init(self.schema, check=self.config.cleanup_unused_appobjects)
# rql parsing utilities ####################################################
diff -r aff75b69db92 -r 2c48c091b6a2 dataimport.py
--- a/dataimport.py Tue Jul 02 17:09:04 2013 +0200
+++ b/dataimport.py Mon Jan 13 13:47:47 2014 +0100
@@ -105,8 +105,8 @@
return i+1
def ucsvreader_pb(stream_or_path, encoding='utf-8', separator=',', quote='"',
- skipfirst=False, withpb=True):
- """same as ucsvreader but a progress bar is displayed as we iter on rows"""
+ skipfirst=False, withpb=True, skip_empty=True):
+ """same as :func:`ucsvreader` but a progress bar is displayed as we iter on rows"""
if isinstance(stream_or_path, basestring):
if not osp.exists(stream_or_path):
raise Exception("file doesn't exists: %s" % stream_or_path)
@@ -118,23 +118,30 @@
rowcount -= 1
if withpb:
pb = shellutils.ProgressBar(rowcount, 50)
- for urow in ucsvreader(stream, encoding, separator, quote, skipfirst):
+ for urow in ucsvreader(stream, encoding, separator, quote,
+ skipfirst=skipfirst, skip_empty=skip_empty):
yield urow
if withpb:
pb.update()
print ' %s rows imported' % rowcount
def ucsvreader(stream, encoding='utf-8', separator=',', quote='"',
- skipfirst=False, ignore_errors=False):
+ skipfirst=False, ignore_errors=False, skip_empty=True):
"""A csv reader that accepts files with any encoding and outputs unicode
strings
+
+ if skip_empty (the default), lines without any values specified (only
+ separators) will be skipped. This is useful for Excel exports which may be
+ full of such lines.
"""
it = iter(csv.reader(stream, delimiter=separator, quotechar=quote))
if not ignore_errors:
if skipfirst:
it.next()
for row in it:
- yield [item.decode(encoding) for item in row]
+ decoded = [item.decode(encoding) for item in row]
+ if not skip_empty or any(decoded):
+ yield [item.decode(encoding) for item in row]
else:
# Skip first line
try:
@@ -151,7 +158,10 @@
# Error in CSV, ignore line and continue
except csv.Error:
continue
- yield [item.decode(encoding) for item in row]
+ decoded = [item.decode(encoding) for item in row]
+ if not skip_empty or any(decoded):
+ yield decoded
+
def callfunc_every(func, number, iterable):
"""yield items of `iterable` one by one and call function `func`
@@ -792,6 +802,9 @@
assert not rtype.startswith('reverse_')
self.add_relation(self.session, eid_from, rtype, eid_to,
self.rschema(rtype).inlined)
+ if self.rschema[rtype].symmetric:
+ self.add_relation(self.session, eid_to, rtype, eid_from,
+ self.rschema(rtype).inlined)
self._nb_inserted_relations += 1
@property
@@ -918,6 +931,9 @@
# XXX Could subjtype be inferred ?
self.source.add_relation(self.session, subj_eid, rtype, obj_eid,
self.rschema(rtype).inlined, **kwargs)
+ if self.rschema[rtype].symmetric:
+ self.source.add_relation(self.session, obj_eid, rtype, subj_eid,
+ self.rschema(rtype).inlined, **kwargs)
def drop_indexes(self, etype):
"""Drop indexes for a given entity type"""
diff -r aff75b69db92 -r 2c48c091b6a2 dbapi.py
--- a/dbapi.py Tue Jul 02 17:09:04 2013 +0200
+++ b/dbapi.py Mon Jan 13 13:47:47 2014 +0100
@@ -179,7 +179,7 @@
puri = urlparse(database)
method = puri.scheme.lower()
if method == 'inmemory':
- config = cwconfig.instance_configuration(puri.path)
+ config = cwconfig.instance_configuration(puri.netloc)
else:
config = cwconfig.CubicWebNoAppConfiguration()
repo = get_repository(database, config=config)
@@ -226,7 +226,7 @@
raises an AuthenticationError if anonymous usage is not allowed
"""
anoninfo = vreg.config.anonymous_user()
- if anoninfo is None: # no anonymous user
+ if anoninfo[0] is None: # no anonymous user
raise AuthenticationError('anonymous access is not authorized')
anon_login, anon_password = anoninfo
# use vreg's repository cache
@@ -365,25 +365,6 @@
"""return the definition of sources used by the repository."""
return self.cnx.source_defs()
- @deprecated('[3.8] use direct access to req.session.data dictionary')
- def session_data(self):
- """return a dictionary containing session data"""
- return self.session.data
-
- @deprecated('[3.8] use direct access to req.session.data dictionary')
- def get_session_data(self, key, default=None, pop=False):
- if pop:
- return self.session.data.pop(key, default)
- return self.session.data.get(key, default)
-
- @deprecated('[3.8] use direct access to req.session.data dictionary')
- def set_session_data(self, key, value):
- self.session.data[key] = value
-
- @deprecated('[3.8] use direct access to req.session.data dictionary')
- def del_session_data(self, key):
- self.session.data.pop(key, None)
-
# these are overridden by set_log_methods below
# only defining here to prevent pylint from complaining
info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None
@@ -419,7 +400,7 @@
def _txid(self):
return self.connection._txid(self)
- def execute(self, rql, args=None, eid_key=None, build_descr=True):
+ def execute(self, rql, args=None, build_descr=True):
"""execute a rql query, return resulting rows and their description in
a :class:`~cubicweb.rset.ResultSet` object
@@ -450,10 +431,6 @@
execute('Any X WHERE X eid %(x)s', {'x': 123})
"""
- if eid_key is not None:
- warn('[3.8] eid_key is deprecated, you can safely remove this argument',
- DeprecationWarning, stacklevel=2)
- # XXX use named argument for build_descr in case repo is < 3.8
rset = self._repo.execute(self._sessid, rql, args,
build_descr=build_descr, **self._txid())
rset.req = self.req
diff -r aff75b69db92 -r 2c48c091b6a2 debian.hardy/compat
--- a/debian.hardy/compat Tue Jul 02 17:09:04 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-5
diff -r aff75b69db92 -r 2c48c091b6a2 debian.hardy/rules
--- a/debian.hardy/rules Tue Jul 02 17:09:04 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-#!/usr/bin/make -f
-# Sample debian/rules that uses debhelper.
-# GNU copyright 1997 to 1999 by Joey Hess.
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-PY_VERSION:=$(shell pyversions -d)
-
-build: build-stamp
-build-stamp:
- dh_testdir
- # XXX doesn't work if logilab-doctools, logilab-xml are not in build depends
- # and I can't get pbuilder find them in its chroot :(
- # cd doc && make
- # FIXME cleanup and use sphinx-build as build-depends ?
- NO_SETUPTOOLS=1 python setup.py build
- touch build-stamp
-
-clean:
- dh_testdir
- dh_testroot
- rm -f build-stamp configure-stamp
- rm -rf build
- #rm -rf debian/cubicweb-*/
- find . -name "*.pyc" -delete
- rm -f $(basename $(wildcard debian/*.in))
- dh_clean
-
-install: build $(basename $(wildcard debian/*.in))
- dh_testdir
- dh_testroot
- dh_clean
- dh_installdirs
-
- NO_SETUPTOOLS=1 python setup.py -q install --no-compile --prefix=debian/tmp/usr
-
- # Put all the python library and data in cubicweb-common
- # and scripts in cubicweb-server
- dh_install -vi
- # cwctl in the cubicweb-ctl package
- rm -f debian/cubicweb-common/usr/share/pyshared/cubicweb/cwctl.py
-
-
- # Remove unittests directory (should be available in cubicweb-dev only)
- rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/site-packages/cubicweb/server/test
- rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/site-packages/cubicweb/hooks/test
- rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/site-packages/cubicweb/sobjects/test
- rm -rf debian/cubicweb-web/usr/lib/${PY_VERSION}/site-packages/cubicweb/web/test
- rm -rf debian/cubicweb-twisted/usr/lib/${PY_VERSION}/site-packages/cubicweb/etwist/test
- rm -rf debian/cubicweb-common/usr/lib/${PY_VERSION}/site-packages/cubicweb/ext/test
- rm -rf debian/cubicweb-common/usr/lib/${PY_VERSION}/site-packages/cubicweb/entities/test
-
- # cubes directory must be managed as a valid python module
- touch debian/cubicweb-common/usr/share/cubicweb/cubes/__init__.py
-
-%: %.in
- sed "s/PY_VERSION/${PY_VERSION}/g" < $< > $@
-
-# Build architecture-independent files here.
-binary-indep: build install
- dh_testdir
- dh_testroot -i
- dh_pycentral -i
- dh_installinit -i -n --name cubicweb -u"defaults 99"
- dh_installlogrotate -i
- dh_installdocs -i -A README
- dh_installman -i
- dh_installchangelogs -i
- dh_link -i
- dh_compress -i -X.py -X.ini -X.xml
- dh_fixperms -i
- dh_installdeb -i
- dh_gencontrol -i
- dh_md5sums -i
- dh_builddeb -i
-
-binary-arch:
-
-binary: binary-indep
-.PHONY: build clean binary binary-indep binary-arch
-
diff -r aff75b69db92 -r 2c48c091b6a2 debian/changelog
--- a/debian/changelog Tue Jul 02 17:09:04 2013 +0200
+++ b/debian/changelog Mon Jan 13 13:47:47 2014 +0100
@@ -1,3 +1,70 @@
+cubicweb (3.18.0-1) unstable; urgency=low
+
+ * new upstream release.
+
+ -- Julien Cristau Fri, 10 Jan 2014 17:14:18 +0100
+
+cubicweb (3.17.11-2) unstable; urgency=low
+
+ * Override lintian false-positive about debian/rules.tmpl in the cube
+ skeleton.
+
+ -- Julien Cristau Wed, 11 Dec 2013 15:56:39 +0100
+
+cubicweb (3.17.11-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Aurelien Campeas Fri, 06 Dec 2013 15:55:48 +0100
+
+cubicweb (3.17.10-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Julien Cristau Wed, 23 Oct 2013 17:23:45 +0200
+
+cubicweb (3.17.9-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Julien Cristau Tue, 08 Oct 2013 17:57:04 +0200
+
+cubicweb (3.17.8-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- David Douard Thu, 03 Oct 2013 15:12:32 +0200
+
+cubicweb (3.17.7-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- David Douard Thu, 26 Sep 2013 15:13:39 +0200
+
+cubicweb (3.17.6-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- David Douard Tue, 06 Aug 2013 11:49:04 +0200
+
+cubicweb (3.17.5-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- David Douard Wed, 31 Jul 2013 16:58:19 +0200
+
+cubicweb (3.17.4-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- David Douard Fri, 26 Jul 2013 09:44:19 +0200
+
+cubicweb (3.17.3-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- David Douard Tue, 09 Jul 2013 15:10:16 +0200
+
cubicweb (3.17.2-1) unstable; urgency=low
* new upstream release
@@ -22,6 +89,12 @@
-- Pierre-Yves David Mon, 29 Apr 2013 11:20:56 +0200
+cubicweb (3.16.6-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Florent Cayré Sat, 13 Jul 2013 05:10:23 +0200
+
cubicweb (3.16.5-1) unstable; urgency=low
* new upstream release
diff -r aff75b69db92 -r 2c48c091b6a2 debian/control
--- a/debian/control Tue Jul 02 17:09:04 2013 +0200
+++ b/debian/control Mon Jan 13 13:47:47 2014 +0100
@@ -10,13 +10,12 @@
Build-Depends:
debhelper (>= 7),
python (>= 2.6),
- python-central (>= 0.5),
python-sphinx,
python-logilab-common,
- python-unittest2,
+ python-unittest2 | python (>= 2.7),
python-logilab-mtconverter,
python-rql,
- python-yams (>= 0.37),
+ python-yams (>= 0.39),
python-lxml,
Standards-Version: 3.9.1
Homepage: http://www.cubicweb.org
@@ -24,7 +23,6 @@
Package: cubicweb
Architecture: all
-XB-Python-Version: ${python:Versions}
Depends:
${misc:Depends},
${python:Depends},
@@ -45,7 +43,6 @@
Package: cubicweb-server
Architecture: all
-XB-Python-Version: ${python:Versions}
Conflicts: cubicweb-multisources
Replaces: cubicweb-multisources
Provides: cubicweb-multisources
@@ -54,7 +51,7 @@
${python:Depends},
cubicweb-common (= ${source:Version}),
cubicweb-ctl (= ${source:Version}),
- python-logilab-database (>= 1.10.0),
+ python-logilab-database (>= 1.11.0),
cubicweb-postgresql-support
| cubicweb-mysql-support
| python-pysqlite2,
@@ -101,7 +98,6 @@
Package: cubicweb-twisted
Architecture: all
-XB-Python-Version: ${python:Versions}
Provides: cubicweb-web-frontend
Depends:
${misc:Depends},
@@ -123,7 +119,6 @@
Package: cubicweb-web
Architecture: all
-XB-Python-Version: ${python:Versions}
Depends:
${misc:Depends},
${python:Depends},
@@ -136,6 +131,8 @@
python-fyzz,
python-imaging,
python-rdflib
+Breaks:
+ cubicweb-inlinedit (<< 1.1.1),
Description: web interface library for the CubicWeb framework
CubicWeb is a semantic web application framework.
.
@@ -148,15 +145,14 @@
Package: cubicweb-common
Architecture: all
-XB-Python-Version: ${python:Versions}
Depends:
${misc:Depends},
${python:Depends},
graphviz,
gettext,
python-logilab-mtconverter (>= 0.8.0),
- python-logilab-common (>= 0.59.0),
- python-yams (>= 0.37.0),
+ python-logilab-common (>= 0.60.0),
+ python-yams (>= 0.39.0),
python-rql (>= 0.31.2),
python-lxml
Recommends:
@@ -164,6 +160,10 @@
python-crypto
Conflicts: cubicweb-core
Replaces: cubicweb-core
+Breaks:
+ cubicweb-comment (<< 1.9.1),
+ cubicweb-person (<< 1.8.0),
+ cubicweb-geocoding (<< 0.2.0),
Description: common library for the CubicWeb framework
CubicWeb is a semantic web application framework.
.
@@ -173,7 +173,6 @@
Package: cubicweb-ctl
Architecture: all
-XB-Python-Version: ${python:Versions}
Depends:
${misc:Depends},
${python:Depends},
@@ -188,7 +187,6 @@
Package: cubicweb-dev
Architecture: all
-XB-Python-Version: ${python:Versions}
Depends:
${misc:Depends},
${python:Depends},
diff -r aff75b69db92 -r 2c48c091b6a2 debian/copyright
--- a/debian/copyright Tue Jul 02 17:09:04 2013 +0200
+++ b/debian/copyright Mon Jan 13 13:47:47 2014 +0100
@@ -8,7 +8,7 @@
Copyright:
- Copyright (c) 2003-2011 LOGILAB S.A. (Paris, FRANCE).
+ Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
http://www.logilab.fr/ -- mailto:contact@logilab.fr
License:
@@ -29,3 +29,18 @@
On Debian systems, the complete text of the GNU Lesser General Public License
may be found in '/usr/share/common-licenses/LGPL-2.1'.
+
+Entypo pictograms:
+
+Author:
+
+ Daniel Bruce (www.entypo.com)
+
+Licence:
+
+ Entypo pictograms are licensed under CC BY-SA 3.0 and the font
+ under SIL Open Font License.
+
+ The rights to each pictogram in the social extension are either
+ trademarked or copyrighted by the respective company.
+
diff -r aff75b69db92 -r 2c48c091b6a2 debian/cubicweb-common.install.in
--- a/debian/cubicweb-common.install.in Tue Jul 02 17:09:04 2013 +0200
+++ b/debian/cubicweb-common.install.in Mon Jan 13 13:47:47 2014 +0100
@@ -1,4 +1,4 @@
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/entities/ usr/lib/PY_VERSION/site-packages/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/ext/ usr/lib/PY_VERSION/site-packages/cubicweb
-debian/tmp/usr/share/cubicweb/cubes/ usr/share/cubicweb/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/*.py usr/share/pyshared/cubicweb
+usr/lib/PY_VERSION/*-packages/cubicweb/entities/
+usr/lib/PY_VERSION/*-packages/cubicweb/ext/
+usr/share/cubicweb/cubes/
+usr/lib/PY_VERSION/*-packages/cubicweb/*.py
diff -r aff75b69db92 -r 2c48c091b6a2 debian/cubicweb-ctl.dirs
--- a/debian/cubicweb-ctl.dirs Tue Jul 02 17:09:04 2013 +0200
+++ b/debian/cubicweb-ctl.dirs Mon Jan 13 13:47:47 2014 +0100
@@ -1,4 +1,3 @@
-usr/lib/python2.4/site-packages/cubicweb/
etc/init.d
etc/cubicweb.d
etc/bash_completion.d
diff -r aff75b69db92 -r 2c48c091b6a2 debian/cubicweb-ctl.install.in
--- a/debian/cubicweb-ctl.install.in Tue Jul 02 17:09:04 2013 +0200
+++ b/debian/cubicweb-ctl.install.in Mon Jan 13 13:47:47 2014 +0100
@@ -1,3 +1,3 @@
-debian/tmp/usr/bin/cubicweb-ctl usr/bin/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/cwctl.py usr/lib/PY_VERSION/site-packages/cubicweb
-debian/cubicweb-ctl.bash_completion etc/bash_completion.d/cubicweb-ctl
+usr/bin/cubicweb-ctl usr/bin/
+usr/lib/PY_VERSION/*-packages/cubicweb/cwctl.py
+../cubicweb-ctl.bash_completion etc/bash_completion.d/cubicweb-ctl
diff -r aff75b69db92 -r 2c48c091b6a2 debian/cubicweb-dev.install.in
--- a/debian/cubicweb-dev.install.in Tue Jul 02 17:09:04 2013 +0200
+++ b/debian/cubicweb-dev.install.in Mon Jan 13 13:47:47 2014 +0100
@@ -1,10 +1,10 @@
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/devtools/ usr/lib/PY_VERSION/site-packages/cubicweb/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/skeleton/ usr/lib/PY_VERSION/site-packages/cubicweb/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/test usr/lib/PY_VERSION/site-packages/cubicweb/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/entities/test usr/lib/PY_VERSION/site-packages/cubicweb/entities/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/ext/test usr/lib/PY_VERSION/site-packages/cubicweb/ext/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/server/test usr/lib/PY_VERSION/site-packages/cubicweb/server/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/sobjects/test usr/lib/PY_VERSION/site-packages/cubicweb/sobjects/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/hooks/test usr/lib/PY_VERSION/site-packages/cubicweb/sobjects/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/web/test usr/lib/PY_VERSION/site-packages/cubicweb/web/
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/etwist/test usr/lib/PY_VERSION/site-packages/cubicweb/etwist/
+usr/lib/PY_VERSION/*-packages/cubicweb/devtools/
+usr/lib/PY_VERSION/*-packages/cubicweb/skeleton/
+usr/lib/PY_VERSION/*-packages/cubicweb/test
+usr/lib/PY_VERSION/*-packages/cubicweb/entities/test
+usr/lib/PY_VERSION/*-packages/cubicweb/ext/test
+usr/lib/PY_VERSION/*-packages/cubicweb/server/test
+usr/lib/PY_VERSION/*-packages/cubicweb/sobjects/test
+usr/lib/PY_VERSION/*-packages/cubicweb/hooks/test
+usr/lib/PY_VERSION/*-packages/cubicweb/web/test
+usr/lib/PY_VERSION/*-packages/cubicweb/etwist/test
diff -r aff75b69db92 -r 2c48c091b6a2 debian/cubicweb-dev.lintian-overrides
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/cubicweb-dev.lintian-overrides Mon Jan 13 13:47:47 2014 +0100
@@ -0,0 +1,1 @@
+missing-dep-for-interpreter make => make | build-essential | dpkg-dev (usr/share/pyshared/cubicweb/skeleton/debian/rules.tmpl)
diff -r aff75b69db92 -r 2c48c091b6a2 debian/cubicweb-documentation.install.in
--- a/debian/cubicweb-documentation.install.in Tue Jul 02 17:09:04 2013 +0200
+++ b/debian/cubicweb-documentation.install.in Mon Jan 13 13:47:47 2014 +0100
@@ -1,3 +1,3 @@
-doc/book usr/share/doc/cubicweb-documentation
-doc/html usr/share/doc/cubicweb-documentation
-debian/cubicweb-doc usr/share/doc-base/cubicweb-doc
+../../doc/book usr/share/doc/cubicweb-documentation
+../../doc/html usr/share/doc/cubicweb-documentation
+../../debian/cubicweb-doc usr/share/doc-base/cubicweb-doc
diff -r aff75b69db92 -r 2c48c091b6a2 debian/cubicweb-server.install.in
--- a/debian/cubicweb-server.install.in Tue Jul 02 17:09:04 2013 +0200
+++ b/debian/cubicweb-server.install.in Mon Jan 13 13:47:47 2014 +0100
@@ -1,5 +1,5 @@
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/server/ usr/lib/PY_VERSION/site-packages/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/hooks/ usr/lib/PY_VERSION/site-packages/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/sobjects/ usr/lib/PY_VERSION/site-packages/cubicweb
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/schemas/ usr/lib/PY_VERSION/site-packages/cubicweb
-debian/tmp/usr/share/cubicweb/migration/ usr/share/cubicweb/
+usr/lib/PY_VERSION/*-packages/cubicweb/server/
+usr/lib/PY_VERSION/*-packages/cubicweb/hooks/
+usr/lib/PY_VERSION/*-packages/cubicweb/sobjects/
+usr/lib/PY_VERSION/*-packages/cubicweb/schemas/
+usr/share/cubicweb/migration/
diff -r aff75b69db92 -r 2c48c091b6a2 debian/cubicweb-twisted.install.in
--- a/debian/cubicweb-twisted.install.in Tue Jul 02 17:09:04 2013 +0200
+++ b/debian/cubicweb-twisted.install.in Mon Jan 13 13:47:47 2014 +0100
@@ -1,1 +1,1 @@
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/etwist/ usr/lib/PY_VERSION/site-packages/cubicweb/
+usr/lib/PY_VERSION/*-packages/cubicweb/etwist/
diff -r aff75b69db92 -r 2c48c091b6a2 debian/cubicweb-web.install.in
--- a/debian/cubicweb-web.install.in Tue Jul 02 17:09:04 2013 +0200
+++ b/debian/cubicweb-web.install.in Mon Jan 13 13:47:47 2014 +0100
@@ -1,3 +1,3 @@
-debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/web usr/lib/PY_VERSION/site-packages/cubicweb
-debian/tmp/usr/share/cubicweb/cubes/shared/data usr/share/cubicweb/cubes/shared
-debian/tmp/usr/share/cubicweb/cubes/shared/wdoc usr/share/cubicweb/cubes/shared
+usr/lib/PY_VERSION/*-packages/cubicweb/web
+usr/share/cubicweb/cubes/shared/data
+usr/share/cubicweb/cubes/shared/wdoc
diff -r aff75b69db92 -r 2c48c091b6a2 debian/rules
--- a/debian/rules Tue Jul 02 17:09:04 2013 +0200
+++ b/debian/rules Mon Jan 13 13:47:47 2014 +0100
@@ -18,17 +18,11 @@
# distributions and we don't want to block a new release of Cubicweb
# because of documentation issues.
-PYTHONPATH=$${PYTHONPATH:+$${PYTHONPATH}:}$(CURDIR)/debian/pythonpath $(MAKE) -C doc/book/en all
- # squeeze has a broken combination of jquery and sphinx, fix it up so search works(ish)
- if grep -q jQuery\\.className doc/html/_static/doctools.js && grep -q "jQuery JavaScript Library v1\.4\." doc/html/_static/jquery.js; then \
- echo 'Patching doctools.js for jQuery 1.4 compat'; \
- sed -i 's/jQuery\.className.has(node\.parentNode, className)/jQuery(node.parentNode).hasClass(className)/' doc/html/_static/doctools.js; \
- fi
rm -rf debian/pythonpath
touch build-stamp
clean:
dh_testdir
- dh_testroot
rm -f build-stamp configure-stamp
rm -rf build
#rm -rf debian/cubicweb-*/
@@ -42,27 +36,26 @@
dh_clean
dh_installdirs
- #python setup.py install_lib --no-compile --install-dir=debian/cubicweb-common/usr/lib/python2.4/site-packages/
NO_SETUPTOOLS=1 python setup.py -q install --no-compile --prefix=debian/tmp/usr
# Put all the python library and data in cubicweb-common
# and scripts in cubicweb-server
- dh_install -vi
+ dh_install -vi --sourcedir=debian/tmp
# cwctl in the cubicweb-ctl package
- rm -f debian/cubicweb-common/usr/share/pyshared/cubicweb/cwctl.py
+ rm -f debian/cubicweb-common/usr/lib/python*/*/cubicweb/cwctl.py
# wdoc in the cubicweb-web package
rm -rf debian/cubicweb-common/usr/share/cubicweb/cubes/shared/wdoc
rm -rf debian/cubicweb-common/usr/share/cubicweb/cubes/shared/data
dh_lintian
# Remove unittests directory (should be available in cubicweb-dev only)
- rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/site-packages/cubicweb/server/test
- rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/site-packages/cubicweb/hooks/test
- rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/site-packages/cubicweb/sobjects/test
- rm -rf debian/cubicweb-web/usr/lib/${PY_VERSION}/site-packages/cubicweb/web/test
- rm -rf debian/cubicweb-twisted/usr/lib/${PY_VERSION}/site-packages/cubicweb/etwist/test
- rm -rf debian/cubicweb-common/usr/lib/${PY_VERSION}/site-packages/cubicweb/ext/test
- rm -rf debian/cubicweb-common/usr/lib/${PY_VERSION}/site-packages/cubicweb/entities/test
+ rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/*-packages/cubicweb/server/test
+ rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/*-packages/cubicweb/hooks/test
+ rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/*-packages/cubicweb/sobjects/test
+ rm -rf debian/cubicweb-web/usr/lib/${PY_VERSION}/*-packages/cubicweb/web/test
+ rm -rf debian/cubicweb-twisted/usr/lib/${PY_VERSION}/*-packages/cubicweb/etwist/test
+ rm -rf debian/cubicweb-common/usr/lib/${PY_VERSION}/*-packages/cubicweb/ext/test
+ rm -rf debian/cubicweb-common/usr/lib/${PY_VERSION}/*-packages/cubicweb/entities/test
%: %.in
@@ -72,7 +65,7 @@
binary-indep: build install
dh_testdir
dh_testroot -i
- dh_pycentral -i
+ dh_python2 -i
dh_installinit -i -n --name cubicweb -u"defaults 99"
dh_installlogrotate -i
dh_installdocs -i -A README
diff -r aff75b69db92 -r 2c48c091b6a2 devtools/__init__.py
--- a/devtools/__init__.py Tue Jul 02 17:09:04 2013 +0200
+++ b/devtools/__init__.py Mon Jan 13 13:47:47 2014 +0100
@@ -26,6 +26,7 @@
import pickle
import glob
import warnings
+import tempfile
from hashlib import sha1 # pylint: disable=E0611
from datetime import timedelta
from os.path import (abspath, join, exists, split, isabs, isdir)
@@ -37,7 +38,7 @@
from cubicweb import ExecutionError, BadConnectionId
from cubicweb import schema, cwconfig
from cubicweb.server.serverconfig import ServerConfiguration
-from cubicweb.etwist.twconfig import TwistedConfiguration
+from cubicweb.etwist.twconfig import WebConfigurationBase
cwconfig.CubicWebConfiguration.cls_adjust_sys_path()
@@ -213,12 +214,12 @@
return BASE_URL
-class BaseApptestConfiguration(TestServerConfiguration, TwistedConfiguration):
+class BaseApptestConfiguration(TestServerConfiguration, WebConfigurationBase):
name = 'all-in-one' # so it search for all-in-one.conf, not repository.conf
options = cwconfig.merge_options(TestServerConfiguration.options
- + TwistedConfiguration.options)
- cubicweb_appobject_path = TestServerConfiguration.cubicweb_appobject_path | TwistedConfiguration.cubicweb_appobject_path
- cube_appobject_path = TestServerConfiguration.cube_appobject_path | TwistedConfiguration.cube_appobject_path
+ + WebConfigurationBase.options)
+ cubicweb_appobject_path = TestServerConfiguration.cubicweb_appobject_path | WebConfigurationBase.cubicweb_appobject_path
+ cube_appobject_path = TestServerConfiguration.cube_appobject_path | WebConfigurationBase.cube_appobject_path
def available_languages(self, *args):
return self.cw_languages()
@@ -287,8 +288,11 @@
The function create it if necessary"""
backupdir = join(self.config.apphome, 'database')
- if not isdir(backupdir):
+ try:
os.makedirs(backupdir)
+ except:
+ if not isdir(backupdir):
+ raise
return backupdir
def config_path(self, db_id):
@@ -324,8 +328,9 @@
# XXX we dump a dict of the config
# This is an experimental to help config dependant setup (like BFSS) to
# be propertly restored
- with open(config_path, 'wb') as conf_file:
+ with tempfile.NamedTemporaryFile(dir=os.path.dirname(config_path), delete=False) as conf_file:
conf_file.write(pickle.dumps(dict(self.config)))
+ os.rename(conf_file.name, config_path)
self.db_cache[self.db_cache_key(db_id)] = (backup_data, config_path)
def _backup_database(self, db_id):
diff -r aff75b69db92 -r 2c48c091b6a2 devtools/devctl.py
--- a/devtools/devctl.py Tue Jul 02 17:09:04 2013 +0200
+++ b/devtools/devctl.py Mon Jan 13 13:47:47 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -46,7 +46,7 @@
a cube or for cubicweb (without a home)
"""
creating = True
- cleanup_interface_sobjects = False
+ cleanup_unused_appobjects = False
cubicweb_appobject_path = (ServerConfiguration.cubicweb_appobject_path
| WebConfiguration.cubicweb_appobject_path)
@@ -130,24 +130,31 @@
w('# singular and plural forms for each entity type\n')
w('\n')
vregdone = set()
+ afss = vreg['uicfg']['autoform_section']
+ aiams = vreg['uicfg']['actionbox_appearsin_addmenu']
if libconfig is not None:
+ # processing a cube, libconfig being a config with all its dependencies
+ # (cubicweb incl.)
from cubicweb.cwvreg import CWRegistryStore
libschema = libconfig.load_schema(remove_unused_rtypes=False)
- afs = vreg['uicfg'].select('autoform_section')
- appearsin_addmenu = vreg['uicfg'].select('actionbox_appearsin_addmenu')
cleanup_sys_modules(libconfig)
libvreg = CWRegistryStore(libconfig)
libvreg.set_schema(libschema) # trigger objects registration
- libafs = libvreg['uicfg'].select('autoform_section')
- libappearsin_addmenu = libvreg['uicfg'].select('actionbox_appearsin_addmenu')
+ libafss = libvreg['uicfg']['autoform_section']
+ libaiams = libvreg['uicfg']['actionbox_appearsin_addmenu']
# prefill vregdone set
list(_iter_vreg_objids(libvreg, vregdone))
+
+ def is_in_lib(rtags, eschema, rschema, role, tschema, predicate=bool):
+ return any(predicate(rtag.etype_get(eschema, rschema, role, tschema))
+ for rtag in rtags)
else:
+ # processing cubicweb itself
libschema = {}
- afs = vreg['uicfg'].select('autoform_section')
- appearsin_addmenu = vreg['uicfg'].select('actionbox_appearsin_addmenu')
for cstrtype in CONSTRAINTS:
add_msg(w, cstrtype)
+ libafss = libaiams = None
+ is_in_lib = lambda *args: False
done = set()
for eschema in sorted(schema.entities()):
if eschema.type in libschema:
@@ -169,32 +176,34 @@
if rschema.final:
continue
for tschema in targetschemas:
- fsections = afs.etype_get(eschema, rschema, role, tschema)
- if 'main_inlined' in fsections and \
- (libconfig is None or not
- 'main_inlined' in libafs.etype_get(
- eschema, rschema, role, tschema)):
- add_msg(w, 'add a %s' % tschema,
- 'inlined:%s.%s.%s' % (etype, rschema, role))
- add_msg(w, str(tschema),
- 'inlined:%s.%s.%s' % (etype, rschema, role))
- if appearsin_addmenu.etype_get(eschema, rschema, role, tschema):
- if libconfig is not None and libappearsin_addmenu.etype_get(
- eschema, rschema, role, tschema):
- if eschema in libschema and tschema in libschema:
- continue
- if role == 'subject':
- label = 'add %s %s %s %s' % (eschema, rschema,
- tschema, role)
- label2 = "creating %s (%s %%(linkto)s %s %s)" % (
- tschema, eschema, rschema, tschema)
- else:
- label = 'add %s %s %s %s' % (tschema, rschema,
- eschema, role)
- label2 = "creating %s (%s %s %s %%(linkto)s)" % (
- tschema, tschema, rschema, eschema)
- add_msg(w, label)
- add_msg(w, label2)
+
+ for afs in afss:
+ fsections = afs.etype_get(eschema, rschema, role, tschema)
+ if 'main_inlined' in fsections and not \
+ is_in_lib(libafss, eschema, rschema, role, tschema,
+ lambda x: 'main_inlined' in x):
+ add_msg(w, 'add a %s' % tschema,
+ 'inlined:%s.%s.%s' % (etype, rschema, role))
+ add_msg(w, str(tschema),
+ 'inlined:%s.%s.%s' % (etype, rschema, role))
+ break
+
+ for aiam in aiams:
+ if aiam.etype_get(eschema, rschema, role, tschema) and not \
+ is_in_lib(libaiams, eschema, rschema, role, tschema):
+ if role == 'subject':
+ label = 'add %s %s %s %s' % (eschema, rschema,
+ tschema, role)
+ label2 = "creating %s (%s %%(linkto)s %s %s)" % (
+ tschema, eschema, rschema, tschema)
+ else:
+ label = 'add %s %s %s %s' % (tschema, rschema,
+ eschema, role)
+ label2 = "creating %s (%s %s %s %%(linkto)s)" % (
+ tschema, tschema, rschema, eschema)
+ add_msg(w, label)
+ add_msg(w, label2)
+ break
# XXX also generate "creating ...' messages for actions in the
# addrelated submenu
w('# subject and object forms for each relation type\n')
diff -r aff75b69db92 -r 2c48c091b6a2 devtools/fill.py
--- a/devtools/fill.py Tue Jul 02 17:09:04 2013 +0200
+++ b/devtools/fill.py Mon Jan 13 13:47:47 2014 +0100
@@ -139,7 +139,7 @@
def generate_integer(self, entity, attrname, index):
"""generates a consistent value for 'attrname' if it's an integer"""
return self._constrained_generate(entity, attrname, 0, 1, index)
- generate_int = generate_integer
+ generate_int = generate_bigint = generate_integer
def generate_float(self, entity, attrname, index):
"""generates a consistent value for 'attrname' if it's a float"""
diff -r aff75b69db92 -r 2c48c091b6a2 devtools/httptest.py
--- a/devtools/httptest.py Tue Jul 02 17:09:04 2013 +0200
+++ b/devtools/httptest.py Mon Jan 13 13:47:47 2014 +0100
@@ -20,6 +20,7 @@
"""
__docformat__ = "restructuredtext en"
+import random
import threading
import socket
import httplib
@@ -46,6 +47,8 @@
.. see:: :func:`test.test_support.bind_port`
"""
+ ports_scan = list(ports_scan)
+ random.shuffle(ports_scan) # lower the chance of race condition
for port in ports_scan:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
diff -r aff75b69db92 -r 2c48c091b6a2 devtools/instrument.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/devtools/instrument.py Mon Jan 13 13:47:47 2014 +0100
@@ -0,0 +1,224 @@
+# copyright 2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program 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.
+#
+# This program 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 this program. If not, see .
+"""Instrumentation utilities"""
+
+import os
+
+try:
+ import pygraphviz
+except ImportError:
+ pygraphviz = None
+
+from cubicweb.cwvreg import CWRegistryStore
+from cubicweb.devtools.devctl import DevConfiguration
+
+
+ALL_COLORS = [
+ "00FF00", "0000FF", "FFFF00", "FF00FF", "00FFFF", "000000",
+ "800000", "008000", "000080", "808000", "800080", "008080", "808080",
+ "C00000", "00C000", "0000C0", "C0C000", "C000C0", "00C0C0", "C0C0C0",
+ "400000", "004000", "000040", "404000", "400040", "004040", "404040",
+ "200000", "002000", "000020", "202000", "200020", "002020", "202020",
+ "600000", "006000", "000060", "606000", "600060", "006060", "606060",
+ "A00000", "00A000", "0000A0", "A0A000", "A000A0", "00A0A0", "A0A0A0",
+ "E00000", "00E000", "0000E0", "E0E000", "E000E0", "00E0E0", "E0E0E0",
+ ]
+_COLORS = {}
+def get_color(key):
+ try:
+ return _COLORS[key]
+ except KeyError:
+ _COLORS[key] = '#'+ALL_COLORS[len(_COLORS) % len(ALL_COLORS)]
+ return _COLORS[key]
+
+def warn(msg, *args):
+ print 'WARNING: %s' % (msg % args)
+
+def info(msg):
+ print 'INFO: ' + msg
+
+
+class PropagationAnalyzer(object):
+ """Abstract propagation analyzer, providing utility function to extract
+ entities involved in propagation from a schema, as well as propagation
+ rules from hooks (provided they use intrumentalized sets, see
+ :class:`CubeTracerSet`).
+
+ Concrete classes should at least define `prop_rel` class attribute and
+ implements the `is_root` method.
+
+ See `localperms` or `nosylist` cubes for example usage (`ccplugin` module).
+ """
+ prop_rel = None # name of the propagation relation
+
+ def init(self, cube):
+ """Initialize analyze for the given cube, returning the (already loaded)
+ vregistry and a set of entities which we're interested in.
+ """
+ config = DevConfiguration(cube)
+ schema = config.load_schema()
+ vreg = CWRegistryStore(config)
+ vreg.set_schema(schema) # set_schema triggers objects registrations
+ eschemas = set(eschema for eschema in schema.entities()
+ if self.should_include(eschema))
+ return vreg, eschemas
+
+ def is_root(self, eschema):
+ """Return `True` if given entity schema is a root of the graph"""
+ raise NotImplementedError()
+
+ def should_include(self, eschema):
+ """Return `True` if given entity schema should be included by the graph.
+ """
+
+ if self.prop_rel in eschema.subjrels or self.is_root(eschema):
+ return True
+ return False
+
+ def prop_edges(self, s_rels, o_rels, eschemas):
+ """Return a set of edges where propagation has been detected.
+
+ Each edge is defined by a 4-uple (from node, to node, rtype, package)
+ where `rtype` is the relation type bringing from to and `package` is the cube adding the rule to the propagation
+ control set (see see :class:`CubeTracerSet`).
+ """
+ schema = iter(eschemas).next().schema
+ prop_edges = set()
+ for rtype in s_rels:
+ found = False
+ for subj, obj in schema.rschema(rtype).rdefs:
+ if subj in eschemas and obj in eschemas:
+ found = True
+ prop_edges.add( (subj, obj, rtype, s_rels.value_cube[rtype]) )
+ if not found:
+ warn('no rdef match for %s', rtype)
+ for rtype in o_rels:
+ found = False
+ for subj, obj in schema.rschema(rtype).rdefs:
+ if subj in eschemas and obj in eschemas:
+ found = True
+ prop_edges.add( (obj, subj, rtype, o_rels.value_cube[rtype]) )
+ if not found:
+ warn('no rdef match for %s', rtype)
+ return prop_edges
+
+ def detect_problems(self, eschemas, edges):
+ """Given the set of analyzed entity schemas and edges between them,
+ return a set of entity schemas where a problem has been detected.
+ """
+ problematic = set()
+ for eschema in eschemas:
+ if self.has_problem(eschema, edges):
+ problematic.add(eschema)
+ not_problematic = set(eschemas).difference(problematic)
+ if not_problematic:
+ info('nothing problematic in: %s' %
+ ', '.join(e.type for e in not_problematic))
+ return problematic
+
+ def has_problem(self, eschema, edges):
+ """Return `True` if the given schema is considered problematic,
+ considering base propagation rules.
+ """
+ root = self.is_root(eschema)
+ has_prop_rel = self.prop_rel in eschema.subjrels
+ # root but no propagation relation
+ if root and not has_prop_rel:
+ warn('%s is root but miss %s', eschema, self.prop_rel)
+ return True
+ # propagated but without propagation relation / not propagated but
+ # with propagation relation
+ if not has_prop_rel and \
+ any(edge for edge in edges if edge[1] == eschema):
+ warn("%s miss %s but is reached by propagation",
+ eschema, self.prop_rel)
+ return True
+ elif has_prop_rel and not root:
+ rdef = eschema.rdef(self.prop_rel, takefirst=True)
+ edges = [edge for edge in edges if edge[1] == eschema]
+ if not edges:
+ warn("%s has %s but isn't reached by "
+ "propagation", eschema, self.prop_rel)
+ return True
+ # require_permission relation / propagation rule not added by
+ # the same cube
+ elif not any(edge for edge in edges if edge[-1] == rdef.package):
+ warn('%s has %s relation / propagation rule'
+ ' not added by the same cube (%s / %s)', eschema,
+ self.prop_rel, rdef.package, edges[0][-1])
+ return True
+ return False
+
+ def init_graph(self, eschemas, edges, problematic):
+ """Initialize and return graph, adding given nodes (entity schemas) and
+ edges between them.
+
+ Require pygraphviz installed.
+ """
+ if pygraphviz is None:
+ raise RuntimeError('pygraphviz is not installed')
+ graph = pygraphviz.AGraph(strict=False, directed=True)
+ for eschema in eschemas:
+ if eschema in problematic:
+ params = {'color': '#ff0000', 'fontcolor': '#ff0000'}
+ else:
+ params = {}#'color': get_color(eschema.package)}
+ graph.add_node(eschema.type, **params)
+ for subj, obj, rtype, package in edges:
+ graph.add_edge(str(subj), str(obj), label=rtype,
+ color=get_color(package))
+ return graph
+
+ def add_colors_legend(self, graph):
+ """Add a legend of used colors to the graph."""
+ for package, color in sorted(_COLORS.iteritems()):
+ graph.add_node(package, color=color, fontcolor=color, shape='record')
+
+
+class CubeTracerSet(object):
+ """Dumb set implementation whose purpose is to keep track of which cube is
+ being loaded when something is added to the set.
+
+ Results will be found in the `value_cube` attribute dictionary.
+
+ See `localperms` or `nosylist` cubes for example usage (`hooks` module).
+ """
+ def __init__(self, vreg, wrapped):
+ self.vreg = vreg
+ self.wrapped = wrapped
+ self.value_cube = {}
+
+ def add(self, value):
+ self.wrapped.add(value)
+ cube = self.vreg.currently_loading_cube
+ if value in self.value_cube:
+ warn('%s is propagated by cube %s and cube %s',
+ value, self.value_cube[value], cube)
+ else:
+ self.value_cube[value] = cube
+
+ def __iter__(self):
+ return iter(self.wrapped)
+
+ def __ior__(self, other):
+ for value in other:
+ self.add(value)
+ return self
+
+ def __ror__(self, other):
+ other |= self.wrapped
+ return other
diff -r aff75b69db92 -r 2c48c091b6a2 devtools/test/data/cubes/__init__.py
diff -r aff75b69db92 -r 2c48c091b6a2 devtools/test/data/cubes/i18ntestcube/__init__.py
diff -r aff75b69db92 -r 2c48c091b6a2 devtools/test/data/cubes/i18ntestcube/__pkginfo__.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/devtools/test/data/cubes/i18ntestcube/__pkginfo__.py Mon Jan 13 13:47:47 2014 +0100
@@ -0,0 +1,18 @@
+# pylint: disable=W0622
+"""cubicweb i18n test cube application packaging information"""
+
+modname = 'i18ntestcube'
+distname = 'cubicweb-i18ntestcube'
+
+numversion = (0, 1, 0)
+version = '.'.join(str(num) for num in numversion)
+
+license = 'LGPL'
+author = 'LOGILAB S.A. (Paris, FRANCE)'
+author_email = 'contact@logilab.fr'
+description = 'forum'
+web = 'http://www.cubicweb.org/project/%s' % distname
+
+__depends__ = {'cubicweb': '>= 3.16.4',
+ }
+__recommends__ = {}
diff -r aff75b69db92 -r 2c48c091b6a2 devtools/test/data/cubes/i18ntestcube/i18n/en.po.ref
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/devtools/test/data/cubes/i18ntestcube/i18n/en.po.ref Mon Jan 13 13:47:47 2014 +0100
@@ -0,0 +1,170 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: cubicweb 3.16.5\n"
+"PO-Revision-Date: 2008-03-28 18:14+0100\n"
+"Last-Translator: Logilab Team \n"
+"Language-Team: fr \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: cubicweb-devtools\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+# schema pot file, generated on 2013-07-12 16:18:12
+#
+# singular and plural forms for each entity type
+# subject and object forms for each relation type
+# (no object form for final or symmetric relation types)
+msgid "Forum"
+msgstr ""
+
+msgid "Forum_plural"
+msgstr ""
+
+msgid "This Forum"
+msgstr ""
+
+msgid "New Forum"
+msgstr ""
+
+msgctxt "inlined:Forum.in_forum.object"
+msgid "add a ForumThread"
+msgstr ""
+
+msgctxt "inlined:Forum.in_forum.object"
+msgid "ForumThread"
+msgstr ""
+
+msgid "add ForumThread in_forum Forum object"
+msgstr ""
+
+msgid "creating ForumThread (ForumThread in_forum Forum %(linkto)s)"
+msgstr ""
+
+msgid "ForumThread"
+msgstr ""
+
+msgid "ForumThread_plural"
+msgstr ""
+
+msgid "This ForumThread"
+msgstr ""
+
+msgid "New ForumThread"
+msgstr ""
+
+msgid "content"
+msgstr ""
+
+msgctxt "ForumThread"
+msgid "content"
+msgstr ""
+
+msgid "content_format"
+msgstr ""
+
+msgctxt "ForumThread"
+msgid "content_format"
+msgstr ""
+
+msgctxt "Forum"
+msgid "description"
+msgstr ""
+
+msgctxt "Forum"
+msgid "description_format"
+msgstr ""
+
+msgid "in_forum"
+msgstr ""
+
+msgctxt "ForumThread"
+msgid "in_forum"
+msgstr ""
+
+msgctxt "Forum"
+msgid "in_forum_object"
+msgstr ""
+
+msgid "in_forum_object"
+msgstr ""
+
+msgid "interested_in"
+msgstr ""
+
+msgctxt "CWUser"
+msgid "interested_in"
+msgstr ""
+
+msgctxt "ForumThread"
+msgid "interested_in_object"
+msgstr ""
+
+msgctxt "Forum"
+msgid "interested_in_object"
+msgstr ""
+
+msgid "interested_in_object"
+msgstr ""
+
+msgid "nosy_list"
+msgstr ""
+
+msgctxt "ForumThread"
+msgid "nosy_list"
+msgstr ""
+
+msgctxt "Forum"
+msgid "nosy_list"
+msgstr ""
+
+msgctxt "CWUser"
+msgid "nosy_list_object"
+msgstr ""
+
+msgid "nosy_list_object"
+msgstr ""
+
+msgctxt "ForumThread"
+msgid "title"
+msgstr ""
+
+msgid "topic"
+msgstr ""
+
+msgctxt "Forum"
+msgid "topic"
+msgstr ""
+
+msgid "Topic"
+msgstr ""
+
+msgid "Description"
+msgstr ""
+
+msgid "Number of threads"
+msgstr ""
+
+msgid "Last activity"
+msgstr ""
+
+msgid ""
+"a long\n"
+"tranlated line\n"
+"hop."
+msgstr ""
+
+msgid "Subject"
+msgstr ""
+
+msgid "Created"
+msgstr ""
+
+msgid "Answers"
+msgstr ""
+
+msgid "Last answered"
+msgstr ""
+
+msgid "This forum does not have any thread yet."
+msgstr ""
diff -r aff75b69db92 -r 2c48c091b6a2 devtools/test/data/cubes/i18ntestcube/schema.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/devtools/test/data/cubes/i18ntestcube/schema.py Mon Jan 13 13:47:47 2014 +0100
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+# copyright 2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program 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.
+#
+# This program 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 this program. If not, see .
+
+"""cubicweb-forum schema"""
+
+from yams.buildobjs import (String, RichString, EntityType,
+ RelationDefinition, SubjectRelation)
+from yams.reader import context
+
+class Forum(EntityType):
+ topic = String(maxsize=50, required=True, unique=True)
+ description = RichString()
+
+class ForumThread(EntityType):
+ __permissions__ = {
+ 'read': ('managers', 'users'),
+ 'add': ('managers', 'users'),
+ 'update': ('managers', 'owners'),
+ 'delete': ('managers', 'owners')
+ }
+ title = String(required=True, fulltextindexed=True, maxsize=256)
+ content = RichString(required=True, fulltextindexed=True)
+ in_forum = SubjectRelation('Forum', cardinality='1*', inlined=True,
+ composite='object')
+class interested_in(RelationDefinition):
+ subject = 'CWUser'
+ object = ('ForumThread', 'Forum')
+
+class nosy_list(RelationDefinition):
+ subject = ('Forum', 'ForumThread')
+ object = 'CWUser'
diff -r aff75b69db92 -r 2c48c091b6a2 devtools/test/data/cubes/i18ntestcube/views.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/devtools/test/data/cubes/i18ntestcube/views.py Mon Jan 13 13:47:47 2014 +0100
@@ -0,0 +1,64 @@
+# copyright 2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program 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.
+#
+# This program 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 this program. If not, see .
+
+"""cubicweb-forum views/forms/actions/components for web ui"""
+
+from cubicweb import view
+from cubicweb.predicates import is_instance
+from cubicweb.web.views import primary, baseviews, uicfg
+from cubicweb.web.views.uicfg import autoform_section as afs
+
+class MyAFS(uicfg.AutoformSectionRelationTags):
+ __select__ = is_instance('ForumThread')
+
+_myafs = MyAFS()
+
+# XXX useless ASA logilab.common.registry is fixed
+_myafs.__module__ = "cubes.i18ntestcube.views"
+
+_myafs.tag_object_of(('*', 'in_forum', 'Forum'), 'main', 'inlined')
+
+afs.tag_object_of(('*', 'in_forum', 'Forum'), 'main', 'inlined')
+
+
+class ForumSameETypeListView(baseviews.SameETypeListView):
+ __select__ = baseviews.SameETypeListView.__select__ & is_instance('Forum')
+
+ def call(self, **kwargs):
+ _ = self._cw._
+ _('Topic'), _('Description')
+ _('Number of threads'), _('Last activity')
+ _('''a long
+tranlated line
+hop.''')
+
+
+class ForumLastActivity(view.EntityView):
+ __regid__ = 'forum_last_activity'
+ __select__ = view.EntityView.__select__ & is_instance('Forum')
+
+
+class ForumPrimaryView(primary.PrimaryView):
+ __select__ = primary.PrimaryView.__select__ & is_instance('Forum')
+
+ def render_entity_attributes(self, entity):
+ _ = self._cw._
+ _('Subject'), _('Created'), _('Answers'),
+ _('Last answered')
+ _('This forum does not have any thread yet.')
+
+class ForumThreadPrimaryView(primary.PrimaryView):
+ __select__ = primary.PrimaryView.__select__ & is_instance('ForumThread')
diff -r aff75b69db92 -r 2c48c091b6a2 devtools/test/unittest_i18n.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/devtools/test/unittest_i18n.py Mon Jan 13 13:47:47 2014 +0100
@@ -0,0 +1,79 @@
+# -*- coding: iso-8859-1 -*-
+# copyright 2003-2013 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 .
+"""unit tests for i18n messages generator"""
+
+import os, os.path as osp
+import sys
+
+from logilab.common.testlib import TestCase, unittest_main
+
+from cubicweb.cwconfig import CubicWebNoAppConfiguration
+
+DATADIR = osp.join(osp.abspath(osp.dirname(__file__)), 'data')
+
+def load_po(fname):
+ """load a po file and return a set of encountered (msgid, msgctx)"""
+ msgs = set()
+ msgid = msgctxt = None
+ for line in open(fname):
+ if line.strip() in ('', '#'):
+ continue
+ if line.startswith('msgstr'):
+ assert not (msgid, msgctxt) in msgs
+ msgs.add( (msgid, msgctxt) )
+ msgid = msgctxt = None
+ elif line.startswith('msgid'):
+ msgid = line.split(' ', 1)[1][1:-1]
+ elif line.startswith('msgctx'):
+ msgctxt = line.split(' ', 1)[1][1: -1]
+ elif msgid is not None:
+ msgid += line[1:-1]
+ elif msgctxt is not None:
+ msgctxt += line[1:-1]
+ return msgs
+
+
+class cubePotGeneratorTC(TestCase):
+ """test case for i18n pot file generator"""
+
+ def setUp(self):
+ self._CUBES_PATH = CubicWebNoAppConfiguration.CUBES_PATH[:]
+ CubicWebNoAppConfiguration.CUBES_PATH.append(osp.join(DATADIR, 'cubes'))
+ CubicWebNoAppConfiguration.cls_adjust_sys_path()
+
+ def tearDown(self):
+ CubicWebNoAppConfiguration.CUBES_PATH[:] = self._CUBES_PATH
+
+ def test_i18ncube(self):
+ # MUST import here to make, since the import statement fire
+ # the cube paths setup (and then must occur after the setUp)
+ from cubicweb.devtools.devctl import update_cube_catalogs
+ cube = osp.join(DATADIR, 'cubes', 'i18ntestcube')
+ msgs = load_po(osp.join(cube, 'i18n', 'en.po.ref'))
+ update_cube_catalogs(cube)
+ newmsgs = load_po(osp.join(cube, 'i18n', 'en.po'))
+ self.assertEqual(msgs, newmsgs)
+
+if __name__ == '__main__':
+ # XXX dirty hack to make this test runnable using python (works
+ # fine with pytest, but not with python directly if this hack is
+ # not present)
+ # XXX to remove ASA logilab.common is fixed
+ sys.path.append('')
+ unittest_main()
diff -r aff75b69db92 -r 2c48c091b6a2 devtools/test/unittest_testlib.py
--- a/devtools/test/unittest_testlib.py Tue Jul 02 17:09:04 2013 +0200
+++ b/devtools/test/unittest_testlib.py Mon Jan 13 13:47:47 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -20,7 +20,9 @@
from cStringIO import StringIO
from unittest import TextTestRunner
+
from logilab.common.testlib import TestSuite, TestCase, unittest_main
+from logilab.common.registry import yes
from cubicweb.devtools import htmlparser
from cubicweb.devtools.testlib import CubicWebTC
@@ -172,8 +174,23 @@
self.assertTrue(rdef.permissions['add'])
self.assertTrue(rdef.permissions['read'], ())
+ def test_temporary_appobjects_registered(self):
+ class AnAppobject(object):
+ __registries__ = ('hip',)
+ __regid__ = 'hop'
+ __select__ = yes()
+ registered = None
+ @classmethod
+ def __registered__(cls, reg):
+ cls.registered = reg
+
+ with self.temporary_appobjects(AnAppobject):
+ self.assertEqual(self.vreg['hip'], AnAppobject.registered)
+ self.assertIn(AnAppobject, self.vreg['hip']['hop'])
+ self.assertNotIn(AnAppobject, self.vreg['hip']['hop'])
+
+
class RepoAccessTC(CubicWebTC):
-
def test_repo_connection(self):
acc = self.new_access('admin')
with acc.repo_cnx() as cnx:
diff -r aff75b69db92 -r 2c48c091b6a2 devtools/testlib.py
--- a/devtools/testlib.py Tue Jul 02 17:09:04 2013 +0200
+++ b/devtools/testlib.py Mon Jan 13 13:47:47 2014 +0100
@@ -90,7 +90,7 @@
MAILBOX = []
-class Email:
+class Email(object):
"""you'll get instances of Email into MAILBOX during tests that trigger
some notification.
@@ -99,7 +99,8 @@
* `recipients` is a list of email address which are the recipients of this
message
"""
- def __init__(self, recipients, msg):
+ def __init__(self, fromaddr, recipients, msg):
+ self.fromaddr = fromaddr
self.recipients = recipients
self.msg = msg
@@ -126,8 +127,8 @@
pass
def close(self):
pass
- def sendmail(self, helo_addr, recipients, msg):
- MAILBOX.append(Email(recipients, msg))
+ def sendmail(self, fromaddr, recipients, msg):
+ MAILBOX.append(Email(fromaddr, recipients, msg))
cwconfig.SMTP = MockSMTP
@@ -391,13 +392,10 @@
@nocoverage
@deprecated('[4.0] explicitly use RepoAccess object in test instead')
- def execute(self, rql, args=None, eidkey=None, req=None):
+ def execute(self, rql, args=None, req=None):
"""executes , builds a resultset, and returns a couple (rset, req)
where req is a FakeRequest
"""
- if eidkey is not None:
- warn('[3.8] eidkey is deprecated, you can safely remove this argument',
- DeprecationWarning, stacklevel=2)
req = req or self.request(rql=rql)
return req.execute(unicode(rql), args)
@@ -432,10 +430,7 @@
# server side db api #######################################################
@deprecated('[4.0] explicitly use RepoAccess object in test instead')
- def sexecute(self, rql, args=None, eid_key=None):
- if eid_key is not None:
- warn('[3.8] eid_key is deprecated, you can safely remove this argument',
- DeprecationWarning, stacklevel=2)
+ def sexecute(self, rql, args=None):
self.session.set_cnxset()
return self.session.execute(rql, args)
@@ -620,6 +615,10 @@
self.vreg._loadedmods.setdefault(self.__module__, {})
for obj in appobjects:
self.vreg.register(obj)
+ registered = getattr(obj, '__registered__', None)
+ if registered:
+ for registry in obj.__registries__:
+ registered(self.vreg[registry])
try:
yield
finally:
@@ -1128,15 +1127,6 @@
self.assertEqual(len(MAILBOX), nb_msgs)
return messages
- # deprecated ###############################################################
-
- @deprecated('[3.8] use self.execute(...).get_entity(0, 0)')
- def entity(self, rql, args=None, eidkey=None, req=None):
- if eidkey is not None:
- warn('[3.8] eidkey is deprecated, you can safely remove this argument',
- DeprecationWarning, stacklevel=2)
- return self.execute(rql, args, req=req).get_entity(0, 0)
-
# auto-populating test classes and utilities ###################################
diff -r aff75b69db92 -r 2c48c091b6a2 doc/3.15.rst
--- a/doc/3.15.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/3.15.rst Mon Jan 13 13:47:47 2014 +0100
@@ -13,7 +13,7 @@
used for entity cache invalidation.
* Improved WSGI support. While there is still some caveats, most of the code
- which as twisted only is now generic and allows related functionalities to work
+ which was twisted only is now generic and allows related functionalities to work
with a WSGI front-end.
* Full undo/transaction support : undo of modification has eventually been
diff -r aff75b69db92 -r 2c48c091b6a2 doc/3.18.rst
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/3.18.rst Mon Jan 13 13:47:47 2014 +0100
@@ -0,0 +1,101 @@
+What's new in CubicWeb 3.18?
+============================
+
+The migration script does not handle sqlite nor mysql instances.
+
+
+New functionalities
+--------------------
+
+* add a security debugging tool
+ (see `#2920304 `_)
+
+* introduce an `add` permission on attributes, to be interpreted at
+ entity creation time only and allow the implementation of complex
+ `update` rules that don't block entity creation (before that the
+ `update` attribute permission was interpreted at entity creation and
+ update time)
+
+* the primary view display controller (uicfg) now has a
+ `set_fields_order` method similar to the one available for forms
+
+* new method `ResultSet.one(col=0)` to retrive a single entity and enforce the
+ result has only one row (see `#3352314 https://www.cubicweb.org/ticket/3352314`_)
+
+* new method `RequestSessionBase.find` to look for entities
+ (see `#3361290 https://www.cubicweb.org/ticket/3361290`_)
+
+* the embedded jQuery copy has been updated to version 1.10.2, and jQuery UI to
+ version 1.10.3.
+
+* initial support for wsgi for the debug mode, available through the new
+ ``wsgi`` cubicweb-ctl command, which can use either python's builtin
+ wsgi server or the werkzeug module if present.
+
+* a ``rql-table`` directive is now available in ReST fields
+
+* cubicweb-ctl upgrade can now generate the static data resource directory
+ directly, without a manual call to gen-static-datadir.
+
+API changes
+-----------
+
+* not really an API change, but the entity permission checks are now
+ systematically deferred to an operation, instead of a) trying in a
+ hook and b) if it failed, retrying later in an operation
+
+* The default value storage for attributes is no longer String, but
+ Bytes. This opens the road to storing arbitrary python objects, e.g.
+ numpy arrays, and fixes a bug where default values whose truth value
+ was False were not properly migrated.
+
+* `symmetric` relations are no more handled by an rql rewrite but are
+ now handled with hooks (from the `activeintegrity` category); this
+ may have some consequences for applications that do low-level database
+ manipulations or at times disable (some) hooks.
+
+* `unique together` constraints (multi-columns unicity constraints)
+ get a `name` attribute that maps the CubicWeb contraint entities to
+ corresponding backend index.
+
+* BreadCrumbEntityVComponent's open_breadcrumbs method now includes
+ the first breadcrumbs separator
+
+* entities can be compared for equality and hashed
+
+* the ``on_fire_transition`` predicate accepts a sequence of possible
+ transition names
+
+* the GROUP_CONCAT rql aggregate function no longer repeats duplicate
+ values, on the sqlite and postgresql backends
+
+Deprecation
+---------------------
+
+* ``pyrorql`` sources have been deprecated. Multisource will be fully dropped
+ in the next version. If you are still using pyrorql, switch to ``datafeed``
+ **NOW**!
+
+* the old multi-source system
+
+* `find_one_entity` and `find_entities` in favor of `find`
+ (see `#3361290 https://www.cubicweb.org/ticket/3361290`_)
+
+* the `TmpFileViewMixin` and `TmpPngView` classes (see `#3400448
+ https://www.cubicweb.org/ticket/3400448`_)
+
+Deprecated Code Drops
+----------------------
+
+* ``ldapuser`` have been dropped; use ``ldapfeed`` now
+ (see `#2936496 `_)
+
+* action ``GotRhythm`` was removed, make sure you do not
+ import it in your cubes (even to unregister it)
+ (see `#3093362 `_)
+
+* all 3.8 backward compat is gone
+
+* all 3.9 backward compat (including the javascript side) is gone
+
+* the ``twisted`` (web-only) instance type has been removed
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/admin/config.rst
--- a/doc/book/en/admin/config.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/admin/config.rst Mon Jan 13 13:47:47 2014 +0100
@@ -57,19 +57,28 @@
PostgreSQL
~~~~~~~~~~
-For installation, please refer to the `PostgreSQL project online documentation`_.
-
-.. _`PostgreSQL project online documentation`: http://www.postgresql.org/
+Many Linux distributions ship with the appropriate PostgreSQL packages.
+Basically, you need to install the following packages:
-You need to install the three following packages: `postgresql-8.X`,
-`postgresql-client-8.X`, and `postgresql-plpython-8.X`. If you run postgres
-version prior to 8.3, you'll also need the `postgresql-contrib-8.X` package for
-full-text search extension.
+* `postgresql` and `postgresql-client`, which will pull the respective
+ versioned packages (e.g. `postgresql-9.1` and `postgresql-client-9.1`) and,
+ optionally,
+* a `postgresql-plpython-X.Y` package with a version corresponding to that of
+ the aforementioned packages (e.g. `postgresql-plpython-9.1`).
+
+If you run postgres version prior to 8.3, you'll also need the
+`postgresql-contrib-8.X` package for full-text search extension.
If you run postgres on another host than the |cubicweb| repository, you should
install the `postgresql-client` package on the |cubicweb| host, and others on the
database host.
+For extra details concerning installation, please refer to the `PostgreSQL
+project online documentation`_.
+
+.. _`PostgreSQL project online documentation`: http://www.postgresql.org/docs
+
+
Database cluster
++++++++++++++++
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/admin/setup.rst
--- a/doc/book/en/admin/setup.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/admin/setup.rst Mon Jan 13 13:47:47 2014 +0100
@@ -121,35 +121,50 @@
`Virtualenv` install
--------------------
-Since version 3.9, |cubicweb| can be safely installed, used and contained inside
-a `virtualenv`_. You can use either :ref:`pip ` or
-:ref:`easy_install ` to install |cubicweb| inside an
-activated virtual environment.
+|cubicweb| can be safely installed, used and contained inside a
+`virtualenv`_. You can use either :ref:`pip ` or
+:ref:`easy_install ` to install |cubicweb|
+inside an activated virtual environment.
.. _PipInstallation:
`pip` install
-------------
-pip_ is a python utility that helps downloading, building, installing, and
-managing python packages and their dependencies. It is fully compatible with
-`virtualenv`_ and installs the packages from sources published on the
-`The Python Package Index`_.
+`pip `_ is a python tool that helps downloading,
+building, installing, and managing Python packages and their dependencies. It
+is fully compatible with `virtualenv`_ and installs the packages from sources
+published on the `The Python Package Index`_.
-.. _`pip`: http://pip.openplans.org/
.. _`virtualenv`: http://virtualenv.openplans.org/
A working compilation chain is needed to build the modules that include C
-extensions. If you definitively wont, installing `Lxml `_,
+extensions. If you really do not want to compile anything, installing `Lxml `_,
`Twisted Web `_ and `libgecode
`_ will help.
-To install |cubicweb| and its dependencies, just run::
+For Debian, these minimal dependencies can be obtained by doing::
+
+ apt-get install gcc python-pip python-dev python-lxml
+
+or, if you prefer to get as much as possible from pip::
+
+ apt-get install gcc python-pip python-dev libxslt1-dev libxml2-dev
+
+For Windows, you can install pre-built packages (possible `source
+`_). For a minimal setup, install
+`pip `_, `setuptools
+`_, `libxml-python
+`_, `lxml
+`_ and `twisted
+`_ from this source making
+sure to choose the correct architecture and version of Python.
+
+Finally, install |cubicweb| and its dependencies, by running::
pip install cubicweb
-There is also a wide variety of :ref:`cubes `. You can access a
-list of available cubes on
+Many other :ref:`cubes ` are available. A list is available at
`PyPI `_
or at the `CubicWeb.org forge`_.
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/annexes/rql/language.rst
--- a/doc/book/en/annexes/rql/language.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/annexes/rql/language.rst Mon Jan 13 13:47:47 2014 +0100
@@ -81,7 +81,7 @@
are expressed as explained below:
* string should be between double or single quotes. If the value contains a
- quote, it should be preceded by a backslash '\'
+ quote, it should be preceded by a backslash '\\'
* floats separator is dot '.'
@@ -747,7 +747,7 @@
.. sourcecode:: sql
- INSERT Person X: X name 'foo', X friend Y WHERE name 'nice'
+ INSERT Person X: X name 'foo', X friend Y WHERE Y name 'nice'
.. _RQLSetQuery:
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/devrepo/datamodel/definition.rst
--- a/doc/book/en/devrepo/datamodel/definition.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/devrepo/datamodel/definition.rst Mon Jan 13 13:47:47 2014 +0100
@@ -226,13 +226,13 @@
* `SizeConstraint`: allows to specify a minimum and/or maximum size on
string (generic case of `maxsize`)
-* `BoundConstraint`: allows to specify a minimum and/or maximum value
+* `BoundaryConstraint`: allows to specify a minimum and/or maximum value
on numeric types and date
.. sourcecode:: python
- from yams.constraints import BoundConstraint, TODAY
- BoundConstraint('<=', TODAY())
+ from yams.constraints import BoundaryConstraint, TODAY
+ BoundaryConstraint('<=', TODAY())
* `IntervalBoundConstraint`: allows to specify an interval with
included values
@@ -330,7 +330,8 @@
For a relation, the possible actions are `read`, `add`, and `delete`.
-For an attribute, the possible actions are `read`, and `update`.
+For an attribute, the possible actions are `read`, `add` and `update`,
+and they are a refinement of an entity type permission.
For each access type, a tuple indicates the name of the authorized groups and/or
one or multiple RQL expressions to satisfy to grant access. The access is
@@ -364,7 +365,8 @@
.. sourcecode:: python
__permissions__ = {'read': ('managers', 'users', 'guests',),
- 'update': ('managers', ERQLExpression('U has_update_permission X')),}
+ 'add': ('managers', ERQLExpression('U has_add_permission X'),
+ 'update': ('managers', ERQLExpression('U has_update_permission X')),}
The standard user groups
````````````````````````
@@ -476,13 +478,8 @@
Here are the current rules:
-1. permission to add/update entity and its attributes are checked:
-
- - on commit if the entity has been added
-
- - in an 'after_update_entity' hook if the entity has been updated. If it fails
- at this time, it will be retried on commit (hence you get the permission if
- you have it just after the modification or *at* commit time)
+1. permission to add/update entity and its attributes are checked on
+ commit
2. permission to delete an entity is checked in 'before_delete_entity' hook
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/devrepo/devcore/dbapi.rst
--- a/doc/book/en/devrepo/devcore/dbapi.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/devrepo/devcore/dbapi.rst Mon Jan 13 13:47:47 2014 +0100
@@ -29,6 +29,11 @@
Also, a rollback is automatically done if an error occurs during commit.
+.. note::
+
+ A :exc:`ValidationError` has a `entity` attribute. In CubicWeb,
+ this atttribute is set to the entity's eid (not a reference to the
+ entity itself).
Executing RQL queries from a view or a hook
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/devrepo/entityclasses/adapters.rst
--- a/doc/book/en/devrepo/entityclasses/adapters.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/devrepo/entityclasses/adapters.rst Mon Jan 13 13:47:47 2014 +0100
@@ -10,13 +10,7 @@
.. _`interfaces`: http://java.sun.com/docs/books/tutorial/java/concepts/interface.html
.. _`adapter`: http://en.wikipedia.org/wiki/Adapter_pattern
-In |cubicweb| adapters provide logical functionalities to entity types. They
-are introduced in version `3.9`. Before that one had to implement Interfaces in
-entity classes to achieve a similar goal. However, the problem with this
-approach is that is clutters the entity class's namespace, exposing name
-collision risks with schema attributes/relations or even methods names
-(different interfaces may define the same method with not necessarily the same
-behaviour expected).
+In |cubicweb| adapters provide logical functionalities to entity types.
Definition of an adapter is quite trivial. An excerpt from cubicweb
itself (found in :mod:`cubicweb.entities.adapters`):
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/devrepo/entityclasses/application-logic.rst
--- a/doc/book/en/devrepo/entityclasses/application-logic.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/devrepo/entityclasses/application-logic.rst Mon Jan 13 13:47:47 2014 +0100
@@ -2,7 +2,7 @@
----------------------------------------
The previous chapters detailed the classes and methods available to
-the developper at the so-called `ORM`_ level. However they say little
+the developer at the so-called `ORM`_ level. However they say little
about the common patterns of usage of these objects.
.. _`ORM`: http://en.wikipedia.org/wiki/Object-relational_mapping
@@ -13,22 +13,22 @@
Hooks and Operations provide support for the implementation of rules
such as computed attributes, coherency invariants, etc (they play the
-same role as database triggers, but in a way that is independant of
+same role as database triggers, but in a way that is independent of
the actual data sources).
So a lot of an application's business rules will be written in Hooks
(or Operations).
On the web side, views also typically operate using entity
-objects. Obvious entity methods for use in views are the dublin code
-method like dc_title, etc. For separation of concerns reasons, one
+objects. Obvious entity methods for use in views are the Dublin Core
+methods like ``dc_title``. For separation of concerns reasons, one
should ensure no ui logic pervades the entities level, and also no
business logic should creep into the views.
In the duration of a transaction, entities objects can be instantiated
many times, in views and hooks, even for the same database entity. For
instance, in a classic CubicWeb deployment setup, the repository and
-the web frontend are separated process communicating over the
+the web front-end are separated process communicating over the
wire. There is no way state can be shared between these processes
(there is a specific API for that). Hence, it is not possible to use
entity objects as messengers between these components of an
@@ -38,24 +38,24 @@
object was built.
Setting an attribute or relation value can be done in the context of a
-Hook/Operation, using the obj.cw_set(x=42) notation or a plain
-RQL SET expression.
+Hook/Operation, using the ``obj.cw_set(x=42)`` notation or a plain
+RQL ``SET`` expression.
In views, it would be preferable to encapsulate the necessary logic in
a method of an adapter for the concerned entity class(es). But of
-course, this advice is also reasonnable for Hooks/Operations, though
+course, this advice is also reasonable for Hooks/Operations, though
the separation of concerns here is less stringent than in the case of
views.
This leads to the practical role of objects adapters: it's where an
-important part of the application logic lie (the other part being
+important part of the application logic lies (the other part being
located in the Hook/Operations).
Anatomy of an entity class
--------------------------
We can look now at a real life example coming from the `tracker`_
-cube. Let us begin to study the entities/project.py content.
+cube. Let us begin to study the ``entities/project.py`` content.
.. sourcecode:: python
@@ -77,10 +77,10 @@
The fact that the `Project` entity type implements an ``ITree``
interface is materialized by the ``ProjectAdapter`` class (inheriting
-the pre-defined ``ITreeAdapter`` whose __regid__ is of course
+the pre-defined ``ITreeAdapter`` whose ``__regid__`` is of course
``ITree``), which will be selected on `Project` entity types because
of its selector. On this adapter, we redefine the ``tree_relation``
-attribute of the ITreeAdapter class.
+attribute of the ``ITreeAdapter`` class.
This is typically used in views concerned with the representation of
tree-like structures (CubicWeb provides several such views).
@@ -95,16 +95,16 @@
about the transitive closure of the child relation). This is a further
argument to implement it at entity class level.
-`fetch_attrs` configures which attributes should be prefetched when using ORM
-methods retrieving entity of this type. In a same manner, the `cw_fetch_order` is
+``fetch_attrs`` configures which attributes should be pre-fetched when using ORM
+methods retrieving entity of this type. In a same manner, the ``cw_fetch_order`` is
a class method allowing to control sort order. More on this in :ref:`FetchAttrs`.
-We can observe the big TICKET_DEFAULT_STATE_RESTR is a pure
+We can observe the big ``TICKET_DEFAULT_STATE_RESTR`` is a pure
application domain piece of data. There is, of course, no limitation
to the amount of class attributes of this kind.
The ``dc_title`` method provides a (unicode string) value likely to be
-consummed by views, but note that here we do not care about output
+consumed by views, but note that here we do not care about output
encodings. We care about providing data in the most universal format
possible, because the data could be used by a web view (which would be
responsible of ensuring XHTML compliance), or a console or file
@@ -113,8 +113,8 @@
.. note::
- The dublin code `dc_xxx` methods are not moved to an adapter as they
- are extremely prevalent in cubicweb and assorted cubes and should be
+ The Dublin Core `dc_xxx` methods are not moved to an adapter as they
+ are extremely prevalent in CubicWeb and assorted cubes and should be
available for all entity types.
Let us now dig into more substantial pieces of code, continuing the
@@ -159,8 +159,8 @@
* entity code is concerned with the application domain
-* it is NOT concerned with database coherency (this is the realm of
- Hooks/Operations); in other words, it assumes a coherent world
+* it is NOT concerned with database consistency (this is the realm of
+ Hooks/Operations); in other words, it assumes a consistent world
* it is NOT (directly) concerned with end-user interfaces
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/devrepo/migration.rst
--- a/doc/book/en/devrepo/migration.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/devrepo/migration.rst Mon Jan 13 13:47:47 2014 +0100
@@ -46,7 +46,7 @@
Again in the directory `migration`, the file `depends.map` allows to indicate
that for the migration to a particular model version, you always have to first
migrate to a particular *CubicWeb* version. This file can contain comments (lines
-starting by `#`) and a dependancy is listed as follows: ::
+starting with `#`) and a dependency is listed as follows: ::
:
@@ -170,9 +170,9 @@
* `rql(rql, kwargs=None, cachekey=None, ask_confirm=True)`, executes an arbitrary RQL
query, either to interrogate or update. A result set object is returned.
-* `add_entity(etype, *args, **kwargs)`, adds a nes entity type of the given
- type. The attribute and relation values are specified using the named and
- positionned parameters.
+* `add_entity(etype, *args, **kwargs)`, adds a new entity of the given type.
+ The attribute and relation values are specified as named positional
+ arguments.
Workflow creation
-----------------
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/devrepo/repo/hooks.rst
--- a/doc/book/en/devrepo/repo/hooks.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/devrepo/repo/hooks.rst Mon Jan 13 13:47:47 2014 +0100
@@ -237,7 +237,7 @@
interface. Hence its constructor is different from the default Exception
constructor. It accepts, positionally:
-* an entity eid,
+* an entity eid (**not the entity itself**),
* a dict whose keys represent attribute (or relation) names and values
an end-user facing message (hence properly translated) relating the
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/devrepo/repo/sessions.rst
--- a/doc/book/en/devrepo/repo/sessions.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/devrepo/repo/sessions.rst Mon Jan 13 13:47:47 2014 +0100
@@ -59,7 +59,7 @@
other credentials elements (calling `authentication_information`),
giving the request object each time
- * the default retriever (oddly named `LoginPasswordRetreiver`)
+ * the default retriever (named `LoginPasswordRetriever`)
will in turn defer login and password fetching to the request
object (which, depending on the authentication mode (`cookie`
or `http`), will do the appropriate things and return a login
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/devrepo/testing.rst
--- a/doc/book/en/devrepo/testing.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/devrepo/testing.rst Mon Jan 13 13:47:47 2014 +0100
@@ -18,12 +18,7 @@
convenience methods to help test all of this.
In the realm of views, automatic tests check that views are valid
-XHTML. See :ref:`automatic_views_tests` for details. Since 3.9, bases
-for web functional testing using `windmill
-`_ are set. See test cases in
-cubicweb/web/test/windmill and python wrapper in
-cubicweb/web/test_windmill/ if you want to use this in your own cube.
-
+XHTML. See :ref:`automatic_views_tests` for details.
Most unit tests need a live database to work against. This is achieved
by CubicWeb using automatically sqlite (bundled with Python, see
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/devrepo/vreg.rst
--- a/doc/book/en/devrepo/vreg.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/devrepo/vreg.rst Mon Jan 13 13:47:47 2014 +0100
@@ -81,7 +81,6 @@
.. autoclass:: cubicweb.predicates.has_mimetype
.. autoclass:: cubicweb.predicates.is_in_state
.. autofunction:: cubicweb.predicates.on_fire_transition
-.. autoclass:: cubicweb.predicates.implements
Logged user predicates
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/devweb/request.rst
--- a/doc/book/en/devweb/request.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/devweb/request.rst Mon Jan 13 13:47:47 2014 +0100
@@ -30,11 +30,7 @@
* `Session data handling`
- * `session_data()`, returns a dictionary containing all the session data
- * `get_session_data(key, default=None)`, returns a value associated to the given
- key or the value `default` if the key is not defined
- * `set_session_data(key, value)`, assign a value to a key
- * `del_session_data(key)`, suppress the value associated to a key
+ * `session.data` is the dictionnary of the session data; it can be manipulated like an ordinary Python dictionnary
* `Edition` (utilities for edition control):
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/devweb/views/wdoc.rst
--- a/doc/book/en/devweb/views/wdoc.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/devweb/views/wdoc.rst Mon Jan 13 13:47:47 2014 +0100
@@ -8,10 +8,8 @@
Help views
----------
.. autoclass:: cubicweb.web.views.wdoc.InlineHelpView
-.. autoclass:: cubicweb.web.views.wdoc.ChangeLogView
Actions
-------
.. autoclass:: cubicweb.web.views.wdoc.HelpAction
-.. autoclass:: cubicweb.web.views.wdoc.ChangeLogAction
.. autoclass:: cubicweb.web.views.wdoc.AboutAction
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/tutorials/advanced/part02_security.rst
--- a/doc/book/en/tutorials/advanced/part02_security.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/tutorials/advanced/part02_security.rst Mon Jan 13 13:47:47 2014 +0100
@@ -259,26 +259,26 @@
# relations where the "parent" entity is the object
O_RELS = set(('filed_under', 'comments',))
- class AddEntitySecurityPropagationHook(hook.PropagateSubjectRelationHook):
+ class AddEntitySecurityPropagationHook(hook.PropagateRelationHook):
"""propagate permissions when new entity are added"""
__regid__ = 'sytweb.addentity_security_propagation'
- __select__ = (hook.PropagateSubjectRelationHook.__select__
+ __select__ = (hook.PropagateRelationHook.__select__
& hook.match_rtype_sets(S_RELS, O_RELS))
main_rtype = 'may_be_read_by'
subject_relations = S_RELS
object_relations = O_RELS
- class AddPermissionSecurityPropagationHook(hook.PropagateSubjectRelationAddHook):
+ class AddPermissionSecurityPropagationHook(hook.PropagateRelationAddHook):
"""propagate permissions when new entity are added"""
__regid__ = 'sytweb.addperm_security_propagation'
- __select__ = (hook.PropagateSubjectRelationAddHook.__select__
+ __select__ = (hook.PropagateRelationAddHook.__select__
& hook.match_rtype('may_be_read_by',))
subject_relations = S_RELS
object_relations = O_RELS
- class DelPermissionSecurityPropagationHook(hook.PropagateSubjectRelationDelHook):
+ class DelPermissionSecurityPropagationHook(hook.PropagateRelationDelHook):
__regid__ = 'sytweb.delperm_security_propagation'
- __select__ = (hook.PropagateSubjectRelationDelHook.__select__
+ __select__ = (hook.PropagateRelationDelHook.__select__
& hook.match_rtype('may_be_read_by',))
subject_relations = S_RELS
object_relations = O_RELS
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/tutorials/advanced/part04_ui-base.rst
--- a/doc/book/en/tutorials/advanced/part04_ui-base.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/tutorials/advanced/part04_ui-base.rst Mon Jan 13 13:47:47 2014 +0100
@@ -194,8 +194,6 @@
.. Note::
- * Adapters have been introduced in CubicWeb 3.9 / cubicweb-folder 1.8.
-
* As seen earlier, we want to **replace** the folder's `ITree` adapter by our
implementation, hence the custom `registration_callback` method.
@@ -241,12 +239,6 @@
ascendant/descendant ordering and a strict comparison with current file's name
(the "X" variable representing the current file).
-.. Note::
-
- * Former `implements` selector should be replaced by one of `is_instance` /
- `adaptable` selector with CubicWeb >= 3.9. In our case, `is_instance` to
- tell our adapter is able to adapt `File` entities.
-
Notice that this query supposes we wont have two files of the same name in the
same folder, else things may go wrong. Fixing this is out of the scope of this
blog. And as I would like to have at some point a smarter, context sensitive
@@ -358,7 +350,7 @@
You'll have to answer some questions, as we've seen in `an earlier post`_.
Now that everything is tested, I can transfer the new code to the production
-server, `apt-get upgrade` cubicweb 3.9 and its dependencies, and eventually
+server, `apt-get upgrade` cubicweb and its dependencies, and eventually
upgrade the production instance.
diff -r aff75b69db92 -r 2c48c091b6a2 doc/book/en/tutorials/advanced/part05_ui-advanced.rst
--- a/doc/book/en/tutorials/advanced/part05_ui-advanced.rst Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/book/en/tutorials/advanced/part05_ui-advanced.rst Mon Jan 13 13:47:47 2014 +0100
@@ -1,8 +1,6 @@
Building my photos web site with |cubicweb| part V: let's make it even more user friendly
=========================================================================================
-We'll now see how to benefit from features introduced in 3.9 and 3.10 releases of CubicWeb
-
.. _uiprops:
Step 1: tired of the default look?
@@ -29,9 +27,9 @@
LOGO = data('logo.jpg')
-The uiprops machinery has been introduced in `CubicWeb 3.9`_. It is used to define
-some static file resources, such as the logo, default Javascript / CSS files, as
-well as CSS properties (we'll see that later).
+The uiprops machinery is used to define some static file resources,
+such as the logo, default Javascript / CSS files, as well as CSS
+properties (we'll see that later).
.. Note::
This file is imported specifically by |cubicweb|, with a predefined name space,
@@ -373,5 +371,4 @@
.. _`CubicWeb 3.10`: http://www.cubicweb.org/blogentry/1330518
-.. _`CubicWeb 3.9`: http://www.cubicweb.org/blogentry/1179899
.. _`here`: http://webdesign.about.com/od/css3/f/blfaqbgsize.htm
diff -r aff75b69db92 -r 2c48c091b6a2 doc/tools/pyjsrest.py
--- a/doc/tools/pyjsrest.py Tue Jul 02 17:09:04 2013 +0200
+++ b/doc/tools/pyjsrest.py Mon Jan 13 13:47:47 2014 +0100
@@ -136,7 +136,6 @@
'cubicweb.preferences',
'cubicweb.edition',
'cubicweb.reledit',
- 'cubicweb.rhythm',
'cubicweb.timeline-ext',
]
diff -r aff75b69db92 -r 2c48c091b6a2 entities/__init__.py
--- a/entities/__init__.py Tue Jul 02 17:09:04 2013 +0200
+++ b/entities/__init__.py Mon Jan 13 13:47:47 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -33,6 +33,11 @@
__regid__ = 'Any'
__implements__ = ()
+ @classproperty
+ def cw_etype(cls):
+ """entity type as a string"""
+ return cls.__regid__
+
@classmethod
def cw_create_url(cls, req, **kwargs):
""" return the url of the entity creation form for this entity type"""
@@ -58,11 +63,6 @@
# meta data api ###########################################################
- @classproperty
- def cw_etype(self):
- """entity Etype as a string"""
- return self.__regid__
-
def dc_title(self):
"""return a suitable *unicode* title for this entity"""
for rschema, attrschema in self.e_schema.attribute_definitions():
diff -r aff75b69db92 -r 2c48c091b6a2 entities/adapters.py
--- a/entities/adapters.py Tue Jul 02 17:09:04 2013 +0200
+++ b/entities/adapters.py Mon Jan 13 13:47:47 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2010-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2010-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -28,9 +28,7 @@
from logilab.common.decorators import cached
from cubicweb import ValidationError, view
-from cubicweb.predicates import (implements, is_instance, relation_possible,
- match_exception)
-from cubicweb.interfaces import IDownloadable, ITree
+from cubicweb.predicates import is_instance, relation_possible, match_exception
class IEmailableAdapter(view.EntityAdapter):
@@ -67,11 +65,9 @@
class INotifiableAdapter(view.EntityAdapter):
- __needs_bw_compat__ = True
__regid__ = 'INotifiable'
__select__ = is_instance('Any')
- @view.implements_adapter_compat('INotifiableAdapter')
def notification_references(self, view):
"""used to control References field of email send on notification
for this entity. `view` is the notification view.
@@ -167,27 +163,25 @@
class IDownloadableAdapter(view.EntityAdapter):
"""interface for downloadable entities"""
- __needs_bw_compat__ = True
__regid__ = 'IDownloadable'
- __select__ = implements(IDownloadable, warn=False) # XXX for bw compat, else should be abstract
+ __abstract__ = True
- @view.implements_adapter_compat('IDownloadable')
def download_url(self, **kwargs): # XXX not really part of this interface
"""return an url to download entity's content"""
raise NotImplementedError
- @view.implements_adapter_compat('IDownloadable')
+
def download_content_type(self):
"""return MIME type of the downloadable content"""
raise NotImplementedError
- @view.implements_adapter_compat('IDownloadable')
+
def download_encoding(self):
"""return encoding of the downloadable content"""
raise NotImplementedError
- @view.implements_adapter_compat('IDownloadable')
+
def download_file_name(self):
"""return file name of the downloadable content"""
raise NotImplementedError
- @view.implements_adapter_compat('IDownloadable')
+
def download_data(self):
"""return actual data of the downloadable content"""
raise NotImplementedError
@@ -219,27 +213,16 @@
.. automethod: children_rql
.. automethod: path
"""
- __needs_bw_compat__ = True
__regid__ = 'ITree'
- __select__ = implements(ITree, warn=False) # XXX for bw compat, else should be abstract
+ __abstract__ = True
child_role = 'subject'
parent_role = 'object'
- @property
- def tree_relation(self):
- warn('[3.9] tree_attribute is deprecated, define tree_relation on a custom '
- 'ITree for %s instead' % (self.entity.__class__),
- DeprecationWarning)
- return self.entity.tree_attribute
-
- # XXX should be removed from the public interface
- @view.implements_adapter_compat('ITree')
def children_rql(self):
"""Returns RQL to get the children of the entity."""
return self.entity.cw_related_rql(self.tree_relation, self.parent_role)
- @view.implements_adapter_compat('ITree')
def different_type_children(self, entities=True):
"""Return children entities of different type as this entity.
@@ -253,7 +236,6 @@
return [e for e in res if e.e_schema != eschema]
return res.filtered_rset(lambda x: x.e_schema != eschema, self.entity.cw_col)
- @view.implements_adapter_compat('ITree')
def same_type_children(self, entities=True):
"""Return children entities of the same type as this entity.
@@ -267,23 +249,19 @@
return [e for e in res if e.e_schema == eschema]
return res.filtered_rset(lambda x: x.e_schema is eschema, self.entity.cw_col)
- @view.implements_adapter_compat('ITree')
def is_leaf(self):
"""Returns True if the entity does not have any children."""
return len(self.children()) == 0
- @view.implements_adapter_compat('ITree')
def is_root(self):
"""Returns true if the entity is root of the tree (e.g. has no parent).
"""
return self.parent() is None
- @view.implements_adapter_compat('ITree')
def root(self):
"""Return the root entity of the tree."""
return self._cw.entity_from_eid(self.path()[0])
- @view.implements_adapter_compat('ITree')
def parent(self):
"""Returns the parent entity if any, else None (e.g. if we are on the
root).
@@ -294,7 +272,6 @@
except (KeyError, IndexError):
return None
- @view.implements_adapter_compat('ITree')
def children(self, entities=True, sametype=False):
"""Return children entities.
@@ -307,7 +284,6 @@
return self.entity.related(self.tree_relation, self.parent_role,
entities=entities)
- @view.implements_adapter_compat('ITree')
def iterparents(self, strict=True):
"""Return an iterator on the parents of the entity."""
def _uptoroot(self):
@@ -322,7 +298,6 @@
return chain([self.entity], _uptoroot(self))
return _uptoroot(self)
- @view.implements_adapter_compat('ITree')
def iterchildren(self, _done=None):
"""Return an iterator over the item's children."""
if _done is None:
@@ -334,7 +309,6 @@
yield child
_done.add(child.eid)
- @view.implements_adapter_compat('ITree')
def prefixiter(self, _done=None):
"""Return an iterator over the item's descendants in a prefixed order."""
if _done is None:
@@ -347,7 +321,6 @@
for entity in child.cw_adapt_to('ITree').prefixiter(_done):
yield entity
- @view.implements_adapter_compat('ITree')
@cached
def path(self):
"""Returns the list of eids from the root object to this object."""
@@ -379,6 +352,7 @@
class IUserFriendlyError(view.EntityAdapter):
__regid__ = 'IUserFriendlyError'
__abstract__ = True
+
def __init__(self, *args, **kwargs):
self.exc = kwargs.pop('exc')
super(IUserFriendlyError, self).__init__(*args, **kwargs)
@@ -386,11 +360,16 @@
class IUserFriendlyUniqueTogether(IUserFriendlyError):
__select__ = match_exception(UniqueTogetherError)
+
def raise_user_exception(self):
- etype, rtypes = self.exc.args
- msg = self._cw._('violates unique_together constraints (%s)') % (
- ', '.join([self._cw._(rtype) for rtype in rtypes]))
- raise ValidationError(self.entity.eid, dict((col, msg) for col in rtypes))
+ _ = self._cw._
+ rtypes = self.exc.rtypes
+ rtypes_msg = {}
+ for rtype in rtypes:
+ rtypes_msg[rtype] = _('%s is part of violated unicity constraint') % rtype
+ globalmsg = _('some relations violate a unicity constraint')
+ rtypes_msg['unicity constraint'] = globalmsg
+ raise ValidationError(self.entity.eid, rtypes_msg)
# deprecated ###################################################################
diff -r aff75b69db92 -r 2c48c091b6a2 entities/lib.py
--- a/entities/lib.py Tue Jul 02 17:09:04 2013 +0200
+++ b/entities/lib.py Mon Jan 13 13:47:47 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -15,14 +15,15 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see .
-"""entity classes for optional library entities
+"""entity classes for optional library entities"""
-"""
__docformat__ = "restructuredtext en"
from urlparse import urlsplit, urlunsplit
from datetime import datetime
+from logilab.mtconverter import xml_escape
+
from cubicweb import UnknownProperty
from cubicweb.entity import _marker
from cubicweb.entities import AnyEntity, fetch_config
@@ -81,7 +82,10 @@
format='text/html'):
"""overriden to return displayable address when necessary"""
if attr == 'address':
- return self.display_address()
+ address = self.display_address()
+ if format == 'text/html':
+ address = xml_escape(address)
+ return address
return super(EmailAddress, self).printable_value(attr, value, attrtype, format)
diff -r aff75b69db92 -r 2c48c091b6a2 entities/test/unittest_base.py
--- a/entities/test/unittest_base.py Tue Jul 02 17:09:04 2013 +0200
+++ b/entities/test/unittest_base.py Mon Jan 13 13:47:47 2014 +0100
@@ -25,7 +25,6 @@
from cubicweb.devtools.testlib import CubicWebTC
-from cubicweb.interfaces import IMileStone, ICalendarable
from cubicweb.entities import AnyEntity
@@ -82,12 +81,19 @@
self.assertEqual(email.display_address(), 'maarten.ter.huurne@philips.com')
self.assertEqual(email.printable_value('address'), 'maarten.ter.huurne@philips.com')
self.vreg.config.global_set_option('mangle-emails', True)
- self.assertEqual(email.display_address(), 'maarten.ter.huurne at philips dot com')
- self.assertEqual(email.printable_value('address'), 'maarten.ter.huurne at philips dot com')
- email = self.execute('INSERT EmailAddress X: X address "syt"').get_entity(0, 0)
- self.assertEqual(email.display_address(), 'syt')
- self.assertEqual(email.printable_value('address'), 'syt')
+ try:
+ self.assertEqual(email.display_address(), 'maarten.ter.huurne at philips dot com')
+ self.assertEqual(email.printable_value('address'), 'maarten.ter.huurne at philips dot com')
+ email = self.execute('INSERT EmailAddress X: X address "syt"').get_entity(0, 0)
+ self.assertEqual(email.display_address(), 'syt')
+ self.assertEqual(email.printable_value('address'), 'syt')
+ finally:
+ self.vreg.config.global_set_option('mangle-emails', False)
+ def test_printable_value_escape(self):
+ email = self.execute('INSERT EmailAddress X: X address "maarten&ter@philips.com"').get_entity(0, 0)
+ self.assertEqual(email.printable_value('address'), 'maarten&ter@philips.com')
+ self.assertEqual(email.printable_value('address', format='text/plain'), 'maarten&ter@philips.com')
class CWUserTC(BaseEntityTC):
@@ -127,27 +133,6 @@
self.request().create_entity('CWGroup', name=u'logilab', reverse_in_group=e)
-class InterfaceTC(CubicWebTC):
-
- def test_nonregr_subclasses_and_mixins_interfaces(self):
- from cubicweb.entities.wfobjs import WorkflowableMixIn
- WorkflowableMixIn.__implements__ = (ICalendarable,)
- CWUser = self.vreg['etypes'].etype_class('CWUser')
- class MyUser(CWUser):
- __implements__ = (IMileStone,)
- self.vreg._loadedmods[__name__] = {}
- self.vreg.register(MyUser)
- self.vreg['etypes'].initialization_completed()
- MyUser_ = self.vreg['etypes'].etype_class('CWUser')
- # a copy is done systematically
- self.assertTrue(issubclass(MyUser_, MyUser))
- self.assertTrue(implements(MyUser_, IMileStone))
- self.assertTrue(implements(MyUser_, ICalendarable))
- # original class should not have beed modified, only the copy
- self.assertTrue(implements(MyUser, IMileStone))
- self.assertFalse(implements(MyUser, ICalendarable))
-
-
class SpecializedEntityClassesTC(CubicWebTC):
def select_eclass(self, etype):
diff -r aff75b69db92 -r 2c48c091b6a2 entities/test/unittest_wfobjs.py
--- a/entities/test/unittest_wfobjs.py Tue Jul 02 17:09:04 2013 +0200
+++ b/entities/test/unittest_wfobjs.py Mon Jan 13 13:47:47 2014 +0100
@@ -75,7 +75,7 @@
wf.add_transition(u'baz', (bar,), foo)
with self.assertRaises(ValidationError) as cm:
self.commit()
- self.assertEqual(cm.exception.errors, {'name-subject': 'workflow already have a transition of that name'})
+ self.assertEqual(cm.exception.errors, {'name-subject': 'workflow already has a transition of that name'})
# no pb if not in the same workflow
wf2 = add_wf(self, 'Company')
foo = wf.add_state(u'foo', initial=True)
@@ -88,7 +88,7 @@
biz.cw_set(name=u'baz')
with self.assertRaises(ValidationError) as cm:
self.commit()
- self.assertEqual(cm.exception.errors, {'name-subject': 'workflow already have a transition of that name'})
+ self.assertEqual(cm.exception.errors, {'name-subject': 'workflow already has a transition of that name'})
class WorkflowTC(CubicWebTC):
diff -r aff75b69db92 -r 2c48c091b6a2 entities/wfobjs.py
--- a/entities/wfobjs.py Tue Jul 02 17:09:04 2013 +0200
+++ b/entities/wfobjs.py Mon Jan 13 13:47:47 2014 +0100
@@ -32,7 +32,6 @@
from cubicweb.entities import AnyEntity, fetch_config
from cubicweb.view import EntityAdapter
from cubicweb.predicates import relation_possible
-from cubicweb.mixins import MI_REL_TRIGGERS
class WorkflowException(Exception): pass
@@ -379,65 +378,8 @@
return self.by_transition and self.by_transition[0] or None
-class WorkflowableMixIn(object):
- """base mixin providing workflow helper methods for workflowable entities.
- This mixin will be automatically set on class supporting the 'in_state'
- relation (which implies supporting 'wf_info_for' as well)
- """
- @property
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').main_workflow")
- def main_workflow(self):
- return self.cw_adapt_to('IWorkflowable').main_workflow
- @property
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').current_workflow")
- def current_workflow(self):
- return self.cw_adapt_to('IWorkflowable').current_workflow
- @property
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').current_state")
- def current_state(self):
- return self.cw_adapt_to('IWorkflowable').current_state
- @property
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').state")
- def state(self):
- return self.cw_adapt_to('IWorkflowable').state
- @property
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').printable_state")
- def printable_state(self):
- return self.cw_adapt_to('IWorkflowable').printable_state
- @property
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').workflow_history")
- def workflow_history(self):
- return self.cw_adapt_to('IWorkflowable').workflow_history
-
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').cwetype_workflow()")
- def cwetype_workflow(self):
- return self.cw_adapt_to('IWorkflowable').main_workflow()
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').latest_trinfo()")
- def latest_trinfo(self):
- return self.cw_adapt_to('IWorkflowable').latest_trinfo()
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').possible_transitions()")
- def possible_transitions(self, type='normal'):
- return self.cw_adapt_to('IWorkflowable').possible_transitions(type)
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').fire_transition()")
- def fire_transition(self, tr, comment=None, commentformat=None):
- return self.cw_adapt_to('IWorkflowable').fire_transition(tr, comment, commentformat)
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').change_state()")
- def change_state(self, statename, comment=None, commentformat=None, tr=None):
- return self.cw_adapt_to('IWorkflowable').change_state(statename, comment, commentformat, tr)
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').subworkflow_input_trinfo()")
- def subworkflow_input_trinfo(self):
- return self.cw_adapt_to('IWorkflowable').subworkflow_input_trinfo()
- @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').subworkflow_input_transition()")
- def subworkflow_input_transition(self):
- return self.cw_adapt_to('IWorkflowable').subworkflow_input_transition()
-
-
-MI_REL_TRIGGERS[('in_state', 'subject')] = WorkflowableMixIn
-
-
-
-class IWorkflowableAdapter(WorkflowableMixIn, EntityAdapter):
+class IWorkflowableAdapter(EntityAdapter):
"""base adapter providing workflow helper methods for workflowable entities.
"""
__regid__ = 'IWorkflowable'
diff -r aff75b69db92 -r 2c48c091b6a2 entity.py
--- a/entity.py Tue Jul 02 17:09:04 2013 +0200
+++ b/entity.py Mon Jan 13 13:47:47 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -42,7 +42,6 @@
from cubicweb.rqlrewrite import RQLRewriter
from cubicweb.uilib import soup2xhtml
-from cubicweb.mixins import MI_REL_TRIGGERS
from cubicweb.mttransforms import ENGINE
_marker = object()
@@ -194,31 +193,11 @@
setattr(cls, rschema.type, Attribute(rschema.type))
mixins = []
for rschema, _, role in eschema.relation_definitions():
- if (rschema, role) in MI_REL_TRIGGERS:
- mixin = MI_REL_TRIGGERS[(rschema, role)]
- if not (issubclass(cls, mixin) or mixin in mixins): # already mixed ?
- mixins.append(mixin)
- for iface in getattr(mixin, '__implements__', ()):
- if not interface.implements(cls, iface):
- interface.extend(cls, iface)
if role == 'subject':
attr = rschema.type
else:
attr = 'reverse_%s' % rschema.type
setattr(cls, attr, Relation(rschema, role))
- if mixins:
- # see etype class instantation in cwvreg.ETypeRegistry.etype_class method:
- # due to class dumping, cls is the generated top level class with actual
- # user class as (only) parent. Since we want to be able to override mixins
- # method from this user class, we have to take care to insert mixins after that
- # class
- #
- # note that we don't plug mixins as user class parent since it causes pb
- # with some cases of entity classes inheritance.
- mixins.insert(0, cls.__bases__[0])
- mixins += cls.__bases__[1:]
- cls.__bases__ = tuple(mixins)
- cls.info('plugged %s mixins on %s', mixins, cls)
fetch_attrs = ('modification_date',)
@@ -308,7 +287,10 @@
select._varmaker = rqlvar_maker(defined=select.defined_vars,
aliases=select.aliases, index=26)
if settype:
- select.add_type_restriction(mainvar, cls.__regid__)
+ rel = select.add_type_restriction(mainvar, cls.__regid__)
+ # should use 'is_instance_of' instead of 'is' so we retrieve
+ # subclasses instances as well
+ rel.r_type = 'is_instance_of'
if fetchattrs is None:
fetchattrs = cls.fetch_attrs
cls._fetch_restrictions(mainvar, select, fetchattrs, user, ordermethod)
@@ -558,7 +540,14 @@
raise NotImplementedError('comparison not implemented for %s' % self.__class__)
def __eq__(self, other):
- raise NotImplementedError('comparison not implemented for %s' % self.__class__)
+ if isinstance(self.eid, (int, long)):
+ return self.eid == other.eid
+ return self is other
+
+ def __hash__(self):
+ if isinstance(self.eid, (int, long)):
+ return self.eid
+ return super(Entity, self).__hash__()
def _cw_update_attr_cache(self, attrcache):
# if context is a repository session, don't consider dont-cache-attrs as
@@ -983,7 +972,7 @@
return value
def related(self, rtype, role='subject', limit=None, entities=False, # XXX .cw_related
- safe=False):
+ safe=False, targettypes=None):
"""returns a resultset of related entities
:param rtype:
@@ -997,10 +986,13 @@
:param safe:
if True, an empty rset/list of entities will be returned in case of
:exc:`Unauthorized`, else (the default), the exception is propagated
+ :param targettypes:
+ a tuple of target entity types to restrict the query
"""
rtype = str(rtype)
- if limit is None:
- # we cannot do much wrt cache on limited queries
+ # Caching restricted/limited results is best avoided.
+ cacheable = limit is None and targettypes is None
+ if cacheable:
cache_key = '%s_%s' % (rtype, role)
if cache_key in self._cw_related_cache:
return self._cw_related_cache[cache_key][entities]
@@ -1008,7 +1000,7 @@
if entities:
return []
return self._cw.empty_rset()
- rql = self.cw_related_rql(rtype, role, limit=limit)
+ rql = self.cw_related_rql(rtype, role, limit=limit, targettypes=targettypes)
try:
rset = self._cw.execute(rql, {'x': self.eid})
except Unauthorized:
@@ -1016,9 +1008,9 @@
raise
rset = self._cw.empty_rset()
if entities:
- if limit is None:
+ if cacheable:
self.cw_set_relation_cache(rtype, role, rset)
- return self.related(rtype, role, limit, entities)
+ return self.related(rtype, role, entities=entities)
return list(rset.entities())
else:
return rset
@@ -1181,8 +1173,7 @@
if v in select.defined_vars and v in cstr.mainvars)
# rewrite constraint by constraint since we want a AND between
# expressions.
- rewriter.rewrite(select, [(varmap, (cstr,))], select.solutions,
- args, existant)
+ rewriter.rewrite(select, [(varmap, (cstr,))], args, existant)
# insert security RQL expressions granting the permission to 'add' the
# relation into the rql syntax tree, if necessary
rqlexprs = rdef.get_rqlexprs('add')
@@ -1194,8 +1185,7 @@
varmap = dict((v, v) for v in (searchedvar.name, evar.name)
if v in select.defined_vars)
# rewrite all expressions at once since we want a OR between them.
- rewriter.rewrite(select, [(varmap, rqlexprs)], select.solutions,
- args, existant)
+ rewriter.rewrite(select, [(varmap, rqlexprs)], args, existant)
# ensure we have an order defined
if not select.orderby:
select.add_sort_var(select.defined_vars[searchedvar.name])
@@ -1281,8 +1271,8 @@
>>> c = rql('Any X WHERE X is Company').get_entity(0, 0)
>>> p = rql('Any X WHERE X is Person').get_entity(0, 0)
- >>> c.set(name=u'Logilab')
- >>> p.set(firstname=u'John', lastname=u'Doe', works_for=c)
+ >>> c.cw_set(name=u'Logilab')
+ >>> p.cw_set(firstname=u'John', lastname=u'Doe', works_for=c)
You can also set relations where the entity has 'object' role by
prefixing the relation name by 'reverse_'. Also, relation values may be
@@ -1323,7 +1313,8 @@
@deprecated('[3.16] use cw_set() instead of set_attributes()')
def set_attributes(self, **kwargs): # XXX cw_set_attributes
- self.cw_set(**kwargs)
+ if kwargs:
+ self.cw_set(**kwargs)
@deprecated('[3.16] use cw_set() instead of set_relations()')
def set_relations(self, **kwargs): # XXX cw_set_relations
@@ -1334,40 +1325,13 @@
(meaning that all relations of the given type from or to this object
should be deleted).
"""
- self.cw_set(**kwargs)
+ if kwargs:
+ self.cw_set(**kwargs)
@deprecated('[3.13] use entity.cw_clear_all_caches()')
def clear_all_caches(self):
return self.cw_clear_all_caches()
- @deprecated('[3.9] use entity.cw_attr_value(attr)')
- def get_value(self, name):
- return self.cw_attr_value(name)
-
- @deprecated('[3.9] use entity.cw_delete()')
- def delete(self, **kwargs):
- return self.cw_delete(**kwargs)
-
- @deprecated('[3.9] use entity.cw_attr_metadata(attr, metadata)')
- def attr_metadata(self, attr, metadata):
- return self.cw_attr_metadata(attr, metadata)
-
- @deprecated('[3.9] use entity.cw_has_perm(action)')
- def has_perm(self, action):
- return self.cw_has_perm(action)
-
- @deprecated('[3.9] use entity.cw_set_relation_cache(rtype, role, rset)')
- def set_related_cache(self, rtype, role, rset):
- self.cw_set_relation_cache(rtype, role, rset)
-
- @deprecated('[3.9] use entity.cw_clear_relation_cache(rtype, role)')
- def clear_related_cache(self, rtype=None, role=None):
- self.cw_clear_relation_cache(rtype, role)
-
- @deprecated('[3.9] use entity.cw_related_rql(rtype, [role, [targettypes]])')
- def related_rql(self, rtype, role='subject', targettypes=None):
- return self.cw_related_rql(rtype, role, targettypes)
-
@property
@deprecated('[3.10] use entity.cw_edited')
def edited_attributes(self):
diff -r aff75b69db92 -r 2c48c091b6a2 etwist/request.py
--- a/etwist/request.py Tue Jul 02 17:09:04 2013 +0200
+++ b/etwist/request.py Mon Jan 13 13:47:47 2014 +0100
@@ -24,15 +24,21 @@
class CubicWebTwistedRequestAdapter(CubicWebRequestBase):
+ """ from twisted .req to cubicweb .form
+ req.files are put into .form[]
+ """
def __init__(self, req, vreg, https):
self._twreq = req
super(CubicWebTwistedRequestAdapter, self).__init__(
vreg, https, req.args, headers=req.received_headers)
- for key, (name, stream) in req.files.iteritems():
- if name is None:
- self.form[key] = (name, stream)
- else:
- self.form[key] = (unicode(name, self.encoding), stream)
+ for key, name_stream_list in req.files.iteritems():
+ for name, stream in name_stream_list:
+ if name is not None:
+ name = unicode(name, self.encoding)
+ self.form.setdefault(key, []).append((name, stream))
+ # 3.16.4 backward compat
+ if len(self.form[key]) == 1:
+ self.form[key] = self.form[key][0]
self.content = self._twreq.content # stream
def http_method(self):
diff -r aff75b69db92 -r 2c48c091b6a2 etwist/server.py
--- a/etwist/server.py Tue Jul 02 17:09:04 2013 +0200
+++ b/etwist/server.py Mon Jan 13 13:47:47 2014 +0100
@@ -244,7 +244,6 @@
self._do_process_multipart = True
self.process()
-
@monkeypatch(http.Request)
def process_multipart(self):
if not self._do_process_multipart:
@@ -254,16 +253,17 @@
keep_blank_values=1,
strict_parsing=1)
for key in form:
- value = form[key]
- if isinstance(value, list):
- self.args[key] = [v.value for v in value]
- elif value.filename:
- if value.done != -1: # -1 is transfer has been interrupted
- self.files[key] = (value.filename, value.file)
+ values = form[key]
+ if not isinstance(values, list):
+ values = [values]
+ for value in values:
+ if value.filename:
+ if value.done != -1: # -1 is transfer has been interrupted
+ self.files.setdefault(key, []).append((value.filename, value.file))
+ else:
+ self.files.setdefault(key, []).append((None, None))
else:
- self.files[key] = (None, None)
- else:
- self.args[key] = value.value
+ self.args.setdefault(key, []).append(value.value)
from logging import getLogger
from cubicweb import set_log_methods
diff -r aff75b69db92 -r 2c48c091b6a2 etwist/twconfig.py
--- a/etwist/twconfig.py Tue Jul 02 17:09:04 2013 +0200
+++ b/etwist/twconfig.py Mon Jan 13 13:47:47 2014 +0100
@@ -34,9 +34,8 @@
from cubicweb.web.webconfig import WebConfiguration
-class TwistedConfiguration(WebConfiguration):
+class WebConfigurationBase(WebConfiguration):
"""web instance (in a twisted web server) client of a RQL server"""
- name = 'twisted'
options = merge_options((
# ctl configuration
@@ -107,19 +106,17 @@
return 'http://%s:%s/' % (self['host'] or getfqdn(), self['port'] or 8080)
-CONFIGURATIONS.append(TwistedConfiguration)
-
try:
from cubicweb.server.serverconfig import ServerConfiguration
- class AllInOneConfiguration(TwistedConfiguration, ServerConfiguration):
+ class AllInOneConfiguration(WebConfigurationBase, ServerConfiguration):
"""repository and web instance in the same twisted process"""
name = 'all-in-one'
- options = merge_options(TwistedConfiguration.options
+ options = merge_options(WebConfigurationBase.options
+ ServerConfiguration.options)
- cubicweb_appobject_path = TwistedConfiguration.cubicweb_appobject_path | ServerConfiguration.cubicweb_appobject_path
- cube_appobject_path = TwistedConfiguration.cube_appobject_path | ServerConfiguration.cube_appobject_path
+ cubicweb_appobject_path = WebConfigurationBase.cubicweb_appobject_path | ServerConfiguration.cubicweb_appobject_path
+ cube_appobject_path = WebConfigurationBase.cube_appobject_path | ServerConfiguration.cube_appobject_path
def pyro_enabled(self):
"""tell if pyro is activated for the in memory repository"""
return self['pyro-server']
diff -r aff75b69db92 -r 2c48c091b6a2 etwist/twctl.py
--- a/etwist/twctl.py Tue Jul 02 17:09:04 2013 +0200
+++ b/etwist/twctl.py Mon Jan 13 13:47:47 2014 +0100
@@ -22,7 +22,7 @@
from logilab.common.shellutils import rm
from cubicweb.toolsutils import CommandHandler
-from cubicweb.web.webctl import WebCreateHandler
+from cubicweb.web.webctl import WebCreateHandler, WebUpgradeHandler
# trigger configuration registration
import cubicweb.etwist.twconfig # pylint: disable=W0611
@@ -48,6 +48,9 @@
def poststop(self):
pass
+class TWUpgradeHandler(WebUpgradeHandler):
+ cfgname = 'twisted'
+
try:
from cubicweb.server import serverctl
@@ -73,5 +76,8 @@
cfgname = 'all-in-one'
subcommand = 'cubicweb-twisted'
+ class AllInOneUpgradeHandler(TWUpgradeHandler):
+ cfgname = 'all-in-one'
+
except ImportError:
pass
diff -r aff75b69db92 -r 2c48c091b6a2 ext/rest.py
--- a/ext/rest.py Tue Jul 02 17:09:04 2013 +0200
+++ b/ext/rest.py Mon Jan 13 13:47:47 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -29,6 +29,8 @@
* `sourcecode` (if pygments is installed), source code colorization
+* `rql-table`, create a table from a RQL query
+
"""
__docformat__ = "restructuredtext en"
@@ -40,7 +42,7 @@
from docutils import statemachine, nodes, utils, io
from docutils.core import Publisher
-from docutils.parsers.rst import Parser, states, directives
+from docutils.parsers.rst import Parser, states, directives, Directive
from docutils.parsers.rst.roles import register_canonical_role, set_classes
from logilab.mtconverter import ESC_UCAR_TABLE, ESC_CAR_TABLE, xml_escape
@@ -251,6 +253,76 @@
winclude_directive.options = {'literal': directives.flag,
'encoding': directives.encoding}
+class RQLTableDirective(Directive):
+ """rql-table directive
+
+ Example:
+
+ .. rql-table::
+ :vid: mytable
+ :headers: , , progress
+ :colvids: 2=progress
+
+ Any X,U,X WHERE X is Project, X url U
+
+ All fields but the RQL string are optionnal. The ``:headers:`` option can
+ contain empty column names.
+ """
+
+ required_arguments = 0
+ optional_arguments = 0
+ has_content= True
+ final_argument_whitespace = True
+ option_spec = {'vid': directives.unchanged,
+ 'headers': directives.unchanged,
+ 'colvids': directives.unchanged}
+
+ def run(self):
+ errid = "rql-table directive"
+ self.assert_has_content()
+ if self.arguments:
+ raise self.warning('%s does not accept arguments' % errid)
+ rql = ' '.join([l.strip() for l in self.content])
+ _cw = self.state.document.settings.context._cw
+ _cw.ensure_ro_rql(rql)
+ try:
+ rset = _cw.execute(rql)
+ except Exception as exc:
+ raise self.error("fail to execute RQL query in %s: %r" %
+ (errid, exc))
+ if not rset:
+ raise self.warning("empty result set")
+ vid = self.options.get('vid', 'table')
+ try:
+ view = _cw.vreg['views'].select(vid, _cw, rset=rset)
+ except Exception as exc:
+ raise self.error("fail to select '%s' view in %s: %r" %
+ (vid, errid, exc))
+ headers = None
+ if 'headers' in self.options:
+ headers = [h.strip() for h in self.options['headers'].split(',')]
+ while headers.count(''):
+ headers[headers.index('')] = None
+ if len(headers) != len(rset[0]):
+ raise self.error("the number of 'headers' does not match the "
+ "number of columns in %s" % errid)
+ cellvids = None
+ if 'colvids' in self.options:
+ cellvids = {}
+ for f in self.options['colvids'].split(','):
+ try:
+ idx, vid = f.strip().split('=')
+ except ValueError:
+ raise self.error("malformatted 'colvids' option in %s" %
+ errid)
+ cellvids[int(idx.strip())] = vid.strip()
+ try:
+ content = view.render(headers=headers, cellvids=cellvids)
+ except Exception as exc:
+ raise self.error("Error rendering %s (%s)" % (errid, exc))
+ return [nodes.raw('', content, format='html')]
+
+
try:
from pygments import highlight
from pygments.lexers import get_lexer_by_name
@@ -385,3 +457,4 @@
directives.register_directive('winclude', winclude_directive)
if pygments_directive is not None:
directives.register_directive('sourcecode', pygments_directive)
+ directives.register_directive('rql-table', RQLTableDirective)
diff -r aff75b69db92 -r 2c48c091b6a2 ext/test/data/views.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ext/test/data/views.py Mon Jan 13 13:47:47 2014 +0100
@@ -0,0 +1,24 @@
+# copyright 2013 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.web.views import tableview
+
+class CustomRsetTableView(tableview.RsetTableView):
+ __regid__ = 'mytable'
+
diff -r aff75b69db92 -r 2c48c091b6a2 ext/test/unittest_rest.py
--- a/ext/test/unittest_rest.py Tue Jul 02 17:09:04 2013 +0200
+++ b/ext/test/unittest_rest.py Mon Jan 13 13:47:47 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -82,5 +82,133 @@
out = rest_publish(context, ':bookmark:`%s`' % eid)
self.assertEqual(out, u'
\n')
+ def test_rqltable_nocontent(self):
+ context = self.context()
+ out = rest_publish(context, """.. rql-table::""")
+ self.assertIn("System Message: ERROR", out)
+ self.assertIn("Content block expected for the "rql-table" "
+ "directive; none found" , out)
+
+ def test_rqltable_norset(self):
+ context = self.context()
+ rql = "Any X WHERE X is CWUser, X firstname 'franky'"
+ out = rest_publish(
+ context, """\
+.. rql-table::
+
+ %(rql)s""" % {'rql': rql})
+ self.assertIn("System Message: WARNING", out)
+ self.assertIn("empty result set", out)
+
+ def test_rqltable_nooptions(self):
+ rql = """Any S,F,L WHERE X is CWUser, X surname S,
+ X firstname F, X login L"""
+ out = rest_publish(
+ self.context(), """\
+.. rql-table::
+
+ %(rql)s
+ """ % {'rql': rql})
+ req = self.request()
+ view = self.vreg['views'].select('table', req, rset=req.execute(rql))
+ self.assertEqual(view.render(w=None)[49:], out[49:])
+
+ def test_rqltable_vid(self):
+ rql = """Any S,F,L WHERE X is CWUser, X surname S,
+ X firstname F, X login L"""
+ vid = 'mytable'
+ out = rest_publish(
+ self.context(), """\
+.. rql-table::
+ :vid: %(vid)s
+
+ %(rql)s
+ """ % {'rql': rql, 'vid': vid})
+ req = self.request()
+ view = self.vreg['views'].select(vid, req, rset=req.execute(rql))
+ self.assertEqual(view.render(w=None)[49:], out[49:])
+ self.assertIn(vid, out[:49])
+
+ def test_rqltable_badvid(self):
+ rql = """Any S,F,L WHERE X is CWUser, X surname S,
+ X firstname F, X login L"""
+ vid = 'mytabel'
+ out = rest_publish(
+ self.context(), """\
+.. rql-table::
+ :vid: %(vid)s
+
+ %(rql)s
+ """ % {'rql': rql, 'vid': vid})
+ self.assertIn("fail to select '%s' view" % vid, out)
+
+ def test_rqltable_headers(self):
+ rql = """Any S,F,L WHERE X is CWUser, X surname S,
+ X firstname F, X login L"""
+ headers = ["nom", "prenom", "identifiant"]
+ out = rest_publish(
+ self.context(), """\
+.. rql-table::
+ :headers: %(headers)s
+
+ %(rql)s
+ """ % {'rql': rql, 'headers': ', '.join(headers)})
+ req = self.request()
+ view = self.vreg['views'].select('table', req, rset=req.execute(rql))
+ view.headers = headers
+ self.assertEqual(view.render(w=None)[49:], out[49:])
+
+ def test_rqltable_headers_missing(self):
+ rql = """Any S,F,L WHERE X is CWUser, X surname S,
+ X firstname F, X login L"""
+ headers = ["nom", "", "identifiant"]
+ out = rest_publish(
+ self.context(), """\
+.. rql-table::
+ :headers: %(headers)s
+
+ %(rql)s
+ """ % {'rql': rql, 'headers': ', '.join(headers)})
+ req = self.request()
+ view = self.vreg['views'].select('table', req, rset=req.execute(rql))
+ view.headers = [headers[0], None, headers[2]]
+ self.assertEqual(view.render(w=None)[49:], out[49:])
+
+ def test_rqltable_headers_missing_edges(self):
+ rql = """Any S,F,L WHERE X is CWUser, X surname S,
+ X firstname F, X login L"""
+ headers = [" ", "prenom", ""]
+ out = rest_publish(
+ self.context(), """\
+.. rql-table::
+ :headers: %(headers)s
+
+ %(rql)s
+ """ % {'rql': rql, 'headers': ', '.join(headers)})
+ req = self.request()
+ view = self.vreg['views'].select('table', req, rset=req.execute(rql))
+ view.headers = [None, headers[1], None]
+ self.assertEqual(view.render(w=None)[49:], out[49:])
+
+ def test_rqltable_colvids(self):
+ rql = """Any X,S,F,L WHERE X is CWUser, X surname S,
+ X firstname F, X login L"""
+ colvids = {0: "oneline"}
+ out = rest_publish(
+ self.context(), """\
+.. rql-table::
+ :colvids: %(colvids)s
+
+ %(rql)s
+ """ % {'rql': rql,
+ 'colvids': ', '.join(["%d=%s" % (k, v)
+ for k, v in colvids.iteritems()])
+ })
+ req = self.request()
+ view = self.vreg['views'].select('table', req, rset=req.execute(rql))
+ view.cellvids = colvids
+ self.assertEqual(view.render(w=None)[49:], out[49:])
+
+
if __name__ == '__main__':
unittest_main()
diff -r aff75b69db92 -r 2c48c091b6a2 hooks/__init__.py
--- a/hooks/__init__.py Tue Jul 02 17:09:04 2013 +0200
+++ b/hooks/__init__.py Mon Jan 13 13:47:47 2014 +0100
@@ -59,7 +59,9 @@
def update_feeds(repo):
# don't iter on repo.sources which doesn't include copy based
# sources (the one we're looking for)
- for source in repo.sources_by_eid.itervalues():
+ # take a list to avoid iterating on a dictionary which size may
+ # change
+ for source in list(repo.sources_by_eid.values()):
if (not source.copy_based_source
or not repo.config.source_enabled(source)
or not source.config['synchronize']):
diff -r aff75b69db92 -r 2c48c091b6a2 hooks/integrity.py
--- a/hooks/integrity.py Tue Jul 02 17:09:04 2013 +0200
+++ b/hooks/integrity.py Mon Jan 13 13:47:47 2014 +0100
@@ -109,6 +109,30 @@
category = 'integrity'
+class EnsureSymmetricRelationsAdd(hook.Hook):
+ """ ensure X r Y => Y r X iff r is symmetric """
+ __regid__ = 'cw.add_ensure_symmetry'
+ category = 'activeintegrity'
+ events = ('after_add_relation',)
+ # __select__ is set in the registration callback
+
+ def __call__(self):
+ self._cw.repo.system_source.add_relation(self._cw, self.eidto,
+ self.rtype, self.eidfrom)
+
+
+class EnsureSymmetricRelationsDelete(hook.Hook):
+ """ ensure X r Y => Y r X iff r is symmetric """
+ __regid__ = 'cw.delete_ensure_symmetry'
+ category = 'activeintegrity'
+ events = ('after_delete_relation',)
+ # __select__ is set in the registration callback
+
+ def __call__(self):
+ self._cw.repo.system_source.delete_relation(self._cw, self.eidto,
+ self.rtype, self.eidfrom)
+
+
class CheckCardinalityHookBeforeDeleteRelation(IntegrityHook):
"""check cardinalities are satisfied"""
__regid__ = 'checkcard_before_delete_relation'
@@ -348,3 +372,11 @@
elif composite == 'object':
_DelayedDeleteSEntityOp.get_instance(self._cw).add_data(
(self.eidfrom, rtype))
+
+def registration_callback(vreg):
+ vreg.register_all(globals().values(), __name__)
+ symmetric_rtypes = [rschema.type for rschema in vreg.schema.relations()
+ if rschema.symmetric]
+ EnsureSymmetricRelationsAdd.__select__ = hook.Hook.__select__ & hook.match_rtype(*symmetric_rtypes)
+ EnsureSymmetricRelationsDelete.__select__ = hook.Hook.__select__ & hook.match_rtype(*symmetric_rtypes)
+
diff -r aff75b69db92 -r 2c48c091b6a2 hooks/metadata.py
--- a/hooks/metadata.py Tue Jul 02 17:09:04 2013 +0200
+++ b/hooks/metadata.py Mon Jan 13 13:47:47 2014 +0100
@@ -149,7 +149,7 @@
# entity source handling #######################################################
-class ChangeEntityUpdateCaches(hook.Operation):
+class ChangeEntitySourceUpdateCaches(hook.Operation):
oldsource = newsource = entity = None # make pylint happy
def postcommit_event(self):
@@ -221,6 +221,6 @@
'mtime': datetime.now()}
self._cw.system_sql(syssource.sqlgen.insert('entities', attrs), attrs)
# register an operation to update repository/sources caches
- ChangeEntityUpdateCaches(self._cw, entity=entity,
- oldsource=oldsource.repo_source,
- newsource=syssource)
+ ChangeEntitySourceUpdateCaches(self._cw, entity=entity,
+ oldsource=oldsource.repo_source,
+ newsource=syssource)
diff -r aff75b69db92 -r 2c48c091b6a2 hooks/notification.py
--- a/hooks/notification.py Tue Jul 02 17:09:04 2013 +0200
+++ b/hooks/notification.py Mon Jan 13 13:47:47 2014 +0100
@@ -52,7 +52,7 @@
All others Operations end up adding data to this Operation.
The notification are done on ``postcommit_event`` to make sure to prevent
- sending notification about rollbacked data.
+ sending notification about rolled back data.
"""
containercls = list
diff -r aff75b69db92 -r 2c48c091b6a2 hooks/security.py
--- a/hooks/security.py Tue Jul 02 17:09:04 2013 +0200
+++ b/hooks/security.py Mon Jan 13 13:47:47 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -20,14 +20,18 @@
"""
__docformat__ = "restructuredtext en"
+from warnings import warn
from logilab.common.registry import objectify_predicate
+from yams import buildobjs
+
from cubicweb import Unauthorized
from cubicweb.server import BEFORE_ADD_RELATIONS, ON_COMMIT_ADD_RELATIONS, hook
-def check_entity_attributes(session, entity, editedattrs=None, creation=False):
+
+def check_entity_attributes(session, entity, action, editedattrs=None):
eid = entity.eid
eschema = entity.e_schema
# ._cw_skip_security_attributes is there to bypass security for attributes
@@ -39,11 +43,26 @@
if attr in dontcheck:
continue
rdef = eschema.rdef(attr)
- if rdef.final: # non final relation are checked by other hooks
- # add/delete should be equivalent (XXX: unify them into 'update' ?)
- if creation and not rdef.permissions.get('update'):
+ if rdef.final: # non final relation are checked by standard hooks
+ perms = rdef.permissions.get(action)
+ # comparison below works because the default update perm is:
+ #
+ # ('managers', ERQLExpression(Any X WHERE U has_update_permission X,
+ # X eid %(x)s, U eid %(u)s))
+ #
+ # is deserialized in this order (groups first), and ERQLExpression
+ # implements comparison by rql expression.
+ if perms == buildobjs.DEFAULT_ATTRPERMS[action]:
+ # The default rule is to delegate to the entity
+ # rule. This is an historical artefact. Hence we take
+ # this object as a marker saying "no specific"
+ # permission rule for this attribute. Thus we just do
+ # nothing.
continue
- rdef.check_perm(session, 'update', eid=eid)
+ if perms == ():
+ # That means an immutable attribute.
+ raise Unauthorized(action, str(rdef))
+ rdef.check_perm(session, action, eid=eid)
class CheckEntityPermissionOp(hook.DataOperationMixIn, hook.LateOperation):
@@ -52,8 +71,7 @@
for eid, action, edited in self.get_data():
entity = session.entity_from_eid(eid)
entity.cw_check_perm(action)
- check_entity_attributes(session, entity, edited,
- creation=(action == 'add'))
+ check_entity_attributes(session, entity, action, edited)
class CheckRelationPermissionOp(hook.DataOperationMixIn, hook.LateOperation):
@@ -91,17 +109,11 @@
events = ('after_update_entity',)
def __call__(self):
- try:
- # check user has permission right now, if not retry at commit time
- self.entity.cw_check_perm('update')
- check_entity_attributes(self._cw, self.entity)
- except Unauthorized:
- self.entity._cw_clear_local_perm_cache('update')
- # save back editedattrs in case the entity is reedited later in the
- # same transaction, which will lead to cw_edited being
- # overwritten
- CheckEntityPermissionOp.get_instance(self._cw).add_data(
- (self.entity.eid, 'update', self.entity.cw_edited) )
+ # save back editedattrs in case the entity is reedited later in the
+ # same transaction, which will lead to cw_edited being
+ # overwritten
+ CheckEntityPermissionOp.get_instance(self._cw).add_data(
+ (self.entity.eid, 'update', self.entity.cw_edited) )
class BeforeDelEntitySecurityHook(SecurityHook):
diff -r aff75b69db92 -r 2c48c091b6a2 hooks/syncschema.py
--- a/hooks/syncschema.py Tue Jul 02 17:09:04 2013 +0200
+++ b/hooks/syncschema.py Mon Jan 13 13:47:47 2014 +0100
@@ -28,7 +28,7 @@
from copy import copy
from yams.schema import BASE_TYPES, RelationSchema, RelationDefinitionSchema
-from yams import buildobjs as ybo, schema2sql as y2sql
+from yams import buildobjs as ybo, schema2sql as y2sql, convert_default_value
from logilab.common.decorators import clear_cache
@@ -39,21 +39,6 @@
from cubicweb.server import hook, schemaserial as ss
from cubicweb.server.sqlutils import SQL_PREFIX
-
-TYPE_CONVERTER = { # XXX
- 'Boolean': bool,
- 'Int': int,
- 'BigInt': int,
- 'Float': float,
- 'Password': str,
- 'String': unicode,
- 'Date' : unicode,
- 'Datetime' : unicode,
- 'Time' : unicode,
- 'TZDatetime' : unicode,
- 'TZTime' : unicode,
- }
-
# core entity and relation types which can't be removed
CORE_TYPES = BASE_TYPES | SCHEMA_TYPES | META_RTYPES | set(
('CWUser', 'CWGroup','login', 'upassword', 'name', 'in_group'))
@@ -116,7 +101,7 @@
if (specialization, rdefdef.object) in rschema.rdefs:
continue
sperdef = RelationDefinitionSchema(specialization, rschema,
- object, props)
+ object, None, values=props)
ss.execschemarql(session.execute, sperdef,
ss.rdef2rql(sperdef, cstrtypemap, groupmap))
@@ -437,11 +422,11 @@
def precommit_event(self):
session = self.session
entity = self.entity
- # entity.defaultval is a string or None, but we need a correctly typed
+ # entity.defaultval is a Binary or None, but we need a correctly typed
# value
default = entity.defaultval
if default is not None:
- default = TYPE_CONVERTER[entity.otype.name](default)
+ default = default.unzpickle()
props = {'default': default,
'indexed': entity.indexed,
'fulltextindexed': entity.fulltextindexed,
@@ -493,20 +478,11 @@
# attribute is still set to False, so we've to ensure it's False
rschema.final = True
insert_rdef_on_subclasses(session, eschema, rschema, rdefdef, props)
- # set default value, using sql for performance and to avoid
- # modification_date update
- if default:
- if rdefdef.object in ('Date', 'Datetime', 'TZDatetime'):
- # XXX may may want to use creation_date
- if default == 'TODAY':
- default = syssource.dbhelper.sql_current_date()
- elif default == 'NOW':
- default = syssource.dbhelper.sql_current_timestamp()
- session.system_sql('UPDATE %s SET %s=%s'
- % (table, column, default))
- else:
- session.system_sql('UPDATE %s SET %s=%%(default)s' % (table, column),
- {'default': default})
+ # update existing entities with the default value of newly added attribute
+ if default is not None:
+ default = convert_default_value(self.rdefdef, default)
+ session.system_sql('UPDATE %s SET %s=%%(default)s' % (table, column),
+ {'default': default})
def revertprecommit_event(self):
# revert changes on in memory schema
@@ -738,44 +714,38 @@
class CWUniqueTogetherConstraintAddOp(MemSchemaOperation):
entity = None # make pylint happy
+
def precommit_event(self):
session = self.session
prefix = SQL_PREFIX
- table = '%s%s' % (prefix, self.entity.constraint_of[0].name)
- cols = ['%s%s' % (prefix, r.name) for r in self.entity.relations]
- dbhelper= session.cnxset.source('system').dbhelper
- sqls = dbhelper.sqls_create_multicol_unique_index(table, cols)
+ entity = self.entity
+ table = '%s%s' % (prefix, entity.constraint_of[0].name)
+ cols = ['%s%s' % (prefix, r.name) for r in entity.relations]
+ dbhelper = session.cnxset.source('system').dbhelper
+ sqls = dbhelper.sqls_create_multicol_unique_index(table, cols, entity.name)
for sql in sqls:
session.system_sql(sql)
- # XXX revertprecommit_event
-
def postcommit_event(self):
- eschema = self.session.vreg.schema.schema_by_eid(self.entity.constraint_of[0].eid)
- attrs = [r.name for r in self.entity.relations]
+ entity = self.entity
+ eschema = self.session.vreg.schema.schema_by_eid(entity.constraint_of[0].eid)
+ attrs = [r.name for r in entity.relations]
eschema._unique_together.append(attrs)
class CWUniqueTogetherConstraintDelOp(MemSchemaOperation):
- entity = oldcstr = None # for pylint
- cols = [] # for pylint
+ entity = cstrname = None # for pylint
+ cols = () # for pylint
+
def precommit_event(self):
session = self.session
prefix = SQL_PREFIX
table = '%s%s' % (prefix, self.entity.type)
- dbhelper= session.cnxset.source('system').dbhelper
+ dbhelper = session.cnxset.source('system').dbhelper
cols = ['%s%s' % (prefix, c) for c in self.cols]
- sqls = dbhelper.sqls_drop_multicol_unique_index(table, cols)
+ sqls = dbhelper.sqls_drop_multicol_unique_index(table, cols, self.cstrname)
for sql in sqls:
- try:
- session.system_sql(sql)
- except Exception as exc: # should be ProgrammingError
- if sql.startswith('DROP'):
- self.error('execute of `%s` failed (cause: %s)', sql, exc)
- continue
- raise
-
- # XXX revertprecommit_event
+ session.system_sql(sql)
def postcommit_event(self):
eschema = self.session.vreg.schema.schema_by_eid(self.entity.eid)
@@ -1195,9 +1165,9 @@
schema = self._cw.vreg.schema
cstr = self._cw.entity_from_eid(self.eidfrom)
entity = schema.schema_by_eid(self.eidto)
- cols = [r.name for r in cstr.relations]
+ cols = tuple(r.name for r in cstr.relations)
CWUniqueTogetherConstraintDelOp(self._cw, entity=entity,
- oldcstr=cstr, cols=cols)
+ cstrname=cstr.name, cols=cols)
# permissions synchronization hooks ############################################
diff -r aff75b69db92 -r 2c48c091b6a2 hooks/test/data/schema.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hooks/test/data/schema.py Mon Jan 13 13:47:47 2014 +0100
@@ -0,0 +1,25 @@
+# copyright 2003-2013 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 yams.buildobjs import RelationDefinition
+
+class friend(RelationDefinition):
+ subject = ('CWUser', 'CWGroup')
+ object = ('CWUser', 'CWGroup')
+ symmetric = True
+
diff -r aff75b69db92 -r 2c48c091b6a2 hooks/test/unittest_hooks.py
--- a/hooks/test/unittest_hooks.py Tue Jul 02 17:09:04 2013 +0200
+++ b/hooks/test/unittest_hooks.py Mon Jan 13 13:47:47 2014 +0100
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -39,6 +39,37 @@
rset = self.execute('Any S WHERE X sender S, X eid %s' % eeid)
self.assertEqual(len(rset), 1)
+ def test_symmetric(self):
+ req = self.request()
+ u1 = self.create_user(req, u'1')
+ u2 = self.create_user(req, u'2')
+ u3 = self.create_user(req, u'3')
+ ga = req.create_entity('CWGroup', name=u'A')
+ gb = req.create_entity('CWGroup', name=u'B')
+ u1.cw_set(friend=u2)
+ u2.cw_set(friend=u3)
+ ga.cw_set(friend=gb)
+ ga.cw_set(friend=u1)
+ self.commit()
+ req = self.request()
+ for l1, l2 in ((u'1', u'2'),
+ (u'2', u'3')):
+ self.assertTrue(req.execute('Any U1,U2 WHERE U1 friend U2, U1 login %(l1)s, U2 login %(l2)s',
+ {'l1': l1, 'l2': l2}))
+ self.assertTrue(req.execute('Any U1,U2 WHERE U2 friend U1, U1 login %(l1)s, U2 login %(l2)s',
+ {'l1': l1, 'l2': l2}))
+ self.assertTrue(req.execute('Any GA,GB WHERE GA friend GB, GA name "A", GB name "B"'))
+ self.assertTrue(req.execute('Any GA,GB WHERE GB friend GA, GA name "A", GB name "B"'))
+ self.assertTrue(req.execute('Any GA,U1 WHERE GA friend U1, GA name "A", U1 login "1"'))
+ self.assertTrue(req.execute('Any GA,U1 WHERE U1 friend GA, GA name "A", U1 login "1"'))
+ self.assertFalse(req.execute('Any GA,U WHERE GA friend U, GA name "A", U login "2"'))
+ for l1, l2 in ((u'1', u'3'),
+ (u'3', u'1')):
+ self.assertFalse(req.execute('Any U1,U2 WHERE U1 friend U2, U1 login %(l1)s, U2 login %(l2)s',
+ {'l1': l1, 'l2': l2}))
+ self.assertFalse(req.execute('Any U1,U2 WHERE U2 friend U1, U1 login %(l1)s, U2 login %(l2)s',
+ {'l1': l1, 'l2': l2}))
+
def test_html_tidy_hook(self):
req = self.request()
entity = req.create_entity('Workflow', name=u'wf1',
diff -r aff75b69db92 -r 2c48c091b6a2 hooks/test/unittest_integrity.py
--- a/hooks/test/unittest_integrity.py Tue Jul 02 17:09:04 2013 +0200
+++ b/hooks/test/unittest_integrity.py Mon Jan 13 13:47:47 2014 +0100
@@ -41,9 +41,6 @@
self.execute('SET X in_group Y WHERE X login "toto", Y name "guests"')
self.commit()
- def test_delete_required_relations_object(self):
- self.skipTest('no sample in the schema ! YAGNI ? Kermaat ?')
-
def test_static_vocabulary_check(self):
self.assertRaises(ValidationError,
self.execute,
diff -r aff75b69db92 -r 2c48c091b6a2 hooks/test/unittest_syncschema.py
--- a/hooks/test/unittest_syncschema.py Tue Jul 02 17:09:04 2013 +0200
+++ b/hooks/test/unittest_syncschema.py Mon Jan 13 13:47:47 2014 +0100
@@ -19,7 +19,7 @@
from logilab.common.testlib import TestCase, unittest_main
-from cubicweb import ValidationError
+from cubicweb import ValidationError, Binary
from cubicweb.schema import META_RTYPES
from cubicweb.devtools.testlib import CubicWebTC
from cubicweb.server.sqlutils import SQL_PREFIX
@@ -73,9 +73,10 @@
self.commit()
self.assertTrue(schema.has_entity('Societe2'))
self.assertTrue(schema.has_relation('concerne2'))
- attreid = self.execute('INSERT CWAttribute X: X cardinality "11", X defaultval "noname", '
+ attreid = self.execute('INSERT CWAttribute X: X cardinality "11", X defaultval %(default)s, '
' X indexed TRUE, X relation_type RT, X from_entity E, X to_entity F '
- 'WHERE RT name "name", E name "Societe2", F name "String"')[0][0]
+ 'WHERE RT name "name", E name "Societe2", F name "String"',
+ {'default': Binary.zpickle('noname')})[0][0]
self._set_attr_perms(attreid)
concerne2_rdef_eid = self.execute(
'INSERT CWRelation X: X cardinality "**", X relation_type RT, X from_entity E, X to_entity E '
@@ -289,8 +290,10 @@
def test_add_attribute_to_base_class(self):
- attreid = self.execute('INSERT CWAttribute X: X cardinality "11", X defaultval "noname", X indexed TRUE, X relation_type RT, X from_entity E, X to_entity F '
- 'WHERE RT name "messageid", E name "BaseTransition", F name "String"')[0][0]
+ attreid = self.execute('INSERT CWAttribute X: X cardinality "11", X defaultval %(default)s, '
+ 'X indexed TRUE, X relation_type RT, X from_entity E, X to_entity F '
+ 'WHERE RT name "messageid", E name "BaseTransition", F name "String"',
+ {'default': Binary.zpickle('noname')})[0][0]
assert self.execute('SET X read_permission Y WHERE X eid %(x)s, Y name "managers"',
{'x': attreid})
self.commit()
diff -r aff75b69db92 -r 2c48c091b6a2 i18n/de.po
--- a/i18n/de.po Tue Jul 02 17:09:04 2013 +0200
+++ b/i18n/de.po Mon Jan 13 13:47:47 2014 +0100
@@ -114,8 +114,8 @@
msgstr "%s Fehlerbericht"
#, python-format
-msgid "%s not estimated"
-msgstr "%s unbekannt(e)"
+msgid "%s is part of violated unicity constraint"
+msgstr ""
#, python-format
msgid "%s relation should not be in mapped"
@@ -439,10 +439,6 @@
msgid "DEBUG"
msgstr ""
-#, python-format
-msgid "Data connection graph for %s"
-msgstr "Graf der Datenverbindungen für %s"
-
msgid "Date"
msgstr "Datum"
@@ -518,15 +514,9 @@
msgid "FormatConstraint"
msgstr "Format-Einschränkung"
-msgid "From:"
-msgstr "Von:"
-
msgid "Garbage collection information"
msgstr "Information zur Speicherbereinigung"
-msgid "Got rhythm?"
-msgstr "Hast Du Rhythmus ?"
-
msgid "Help"
msgstr "Hilfe"
@@ -695,9 +685,6 @@
msgid "RQLVocabularyConstraint"
msgstr "RQL Wortschatz-Einschränkung"
-msgid "Recipients:"
-msgstr "Adressaten:"
-
msgid "RegexpConstraint"
msgstr "regulärer Ausdruck Einschränkung"
@@ -759,9 +746,6 @@
msgid "SubWorkflowExitPoint_plural"
msgstr "subworkflow Endpunkte"
-msgid "Subject:"
-msgstr "Subjekt :"
-
msgid "Submit bug report"
msgstr "Fehlerbericht senden"
@@ -929,9 +913,6 @@
msgid "Web server"
msgstr "Web-Server"
-msgid "What's new?"
-msgstr "Was ist neu?"
-
msgid "Workflow"
msgstr "Workflow"
@@ -965,9 +946,6 @@
"\"Durchsuchen\" oberhalb eine neue Datei hochladen, oder den Datei-Inhalt "
"mit dem Widget unterhalb editieren."
-msgid "You can use any of the following substitutions in your text"
-msgstr "Sie können die folgenden Ersetzungen in Ihrem Text verwenden:"
-
msgid "You can't change this relation"
msgstr ""
@@ -1023,6 +1001,9 @@
msgid "abstract base class for transitions"
msgstr "abstrakte Basisklasse für Übergänge"
+msgid "action menu"
+msgstr ""
+
msgid "action(s) on this selection"
msgstr "Aktionen(en) bei dieser Auswahl"
@@ -1041,6 +1022,9 @@
msgid "add Bookmark bookmarked_by CWUser object"
msgstr "Lesezeichen"
+msgid "add CWAttribute add_permission RQLExpression subject"
+msgstr ""
+
msgid "add CWAttribute constrained_by CWConstraint subject"
msgstr "Einschränkung"
@@ -1149,6 +1133,10 @@
msgid "add_permission"
msgstr "kann hinzugefügt werden durch"
+msgctxt "CWAttribute"
+msgid "add_permission"
+msgstr ""
+
# subject and object forms for each relation type
# (no object form for final relation types)
msgctxt "CWEType"
@@ -1185,6 +1173,9 @@
"Die Relation %(rtype)s von %(frometype)s #%(eidfrom)s zu %(toetype)s #"
"%(eidto)s wurde hinzugefügt."
+msgid "additional type specific properties"
+msgstr ""
+
msgid "addrelated"
msgstr "hinzufügen"
@@ -1672,9 +1663,6 @@
"core relation indicating the types (including specialized types) of an entity"
msgstr ""
-msgid "cost"
-msgstr "Kosten"
-
msgid "could not connect to the SMTP server"
msgstr "Keine Verbindung mit dem SMTP-Server"
@@ -1727,6 +1715,10 @@
msgstr "Erstelle E-Mail-Adresse für Nutzer %(linkto)s"
msgid ""
+"creating RQLExpression (CWAttribute %(linkto)s add_permission RQLExpression)"
+msgstr ""
+
+msgid ""
"creating RQLExpression (CWAttribute %(linkto)s read_permission RQLExpression)"
msgstr "RQL-Ausdruck für Leseberechtigung für %(linkto)s"
@@ -2099,6 +2091,9 @@
msgid "default value"
msgstr "Standardwert"
+msgid "default value as gziped pickled python object"
+msgstr ""
+
msgid "default workflow for an entity type"
msgstr "Standard-Workflow eines Entitätstyps"
@@ -2373,18 +2368,9 @@
msgid "eid"
msgstr ""
-msgid "emails successfully sent"
-msgstr "E-Mails erfolgreich versandt."
-
-msgid "embed"
-msgstr "einbetten"
-
msgid "embedded html"
msgstr "HTML-Inhalt"
-msgid "embedding this url is forbidden"
-msgstr "Einbettung dieses URLs ist nicht erlaubt."
-
msgid "end_timestamp"
msgstr ""
@@ -2437,9 +2423,6 @@
msgid "error"
msgstr ""
-msgid "error while embedding page"
-msgstr "Fehler beim Einbetten der Seite"
-
msgid "error while publishing ReST text"
msgstr "Fehler beim Übersetzen von reST"
@@ -2449,9 +2432,6 @@
"Fehler beim Zugriff auf Quelle %s, möglicherweise sind die Daten "
"unvollständig."
-msgid "eta_date"
-msgstr "Enddatum"
-
msgid "exit state must be a subworkflow state"
msgstr "Exit-Zustand muss ein Subworkflow-Zustand sein."
@@ -2465,9 +2445,6 @@
msgid "exiting from subworkflow %s"
msgstr "verlasse Subworkflow %s"
-msgid "expected:"
-msgstr "erwartet:"
-
msgid "expression"
msgstr "Ausdruck"
@@ -2482,8 +2459,12 @@
msgid "exprtype"
msgstr "Typ des Ausdrucks"
-msgid "external page"
-msgstr "externe Seite"
+msgid "extra_props"
+msgstr ""
+
+msgctxt "CWAttribute"
+msgid "extra_props"
+msgstr ""
msgid "facet-loading-msg"
msgstr ""
@@ -2901,10 +2882,6 @@
msgid "info"
msgstr "Information"
-#, python-format
-msgid "initial estimation %s"
-msgstr "Erste Schätzung %s"
-
msgid "initial state for this workflow"
msgstr "Anfangszustand für diesen Workflow"
@@ -3180,9 +3157,6 @@
msgid "message"
msgstr ""
-msgid "milestone"
-msgstr "Meilenstein"
-
#, python-format
msgid "missing parameters for entity %s"
msgstr "Fehlende Parameter für Entität %s"
@@ -3242,6 +3216,10 @@
msgid "name"
msgstr ""
+msgctxt "CWUniqueTogetherConstraint"
+msgid "name"
+msgstr ""
+
msgctxt "State"
msgid "name"
msgstr "Name"
@@ -3329,9 +3307,6 @@
msgid "no related entity"
msgstr "keine verknüpfte Entität"
-msgid "no related project"
-msgstr "kein verknüpftes Projekt"
-
msgid "no repository sessions found"
msgstr "keine Datenbank-Sitzung gefunden"
@@ -3531,15 +3506,6 @@
msgid "profile"
msgstr "Profil"
-msgid "progress"
-msgstr "Fortschritt"
-
-msgid "progress bar"
-msgstr "Fortschrittsbalken"
-
-msgid "project"
-msgstr "Projekt"
-
msgid "rdef-description"
msgstr "Beschreibung"
@@ -3808,9 +3774,6 @@
msgid "semantic description of this workflow"
msgstr "Semantische Beschreibung dieses Workflows"
-msgid "send email"
-msgstr "E-Mail senden"
-
msgid "september"
msgstr "September"
@@ -3839,9 +3802,6 @@
msgid "show filter form"
msgstr "Filter zeigen"
-msgid "sioc"
-msgstr "sioc"
-
msgid "site configuration"
msgstr "Konfiguration der Website"
@@ -3861,6 +3821,9 @@
"Eine oder mehrere frühere Transaktion(en) betreffen die Tntität. Machen Sie "
"sie zuerst rückgängig."
+msgid "some relations violate a unicity constraint"
+msgstr ""
+
msgid "sorry, the server is unable to handle this query"
msgstr "Der Server kann diese Anfrage leider nicht bearbeiten."
@@ -4056,9 +4019,6 @@
msgid "tablefilter"
msgstr "Tabellenfilter"
-msgid "task progression"
-msgstr "Fortschritt der Aufgabe"
-
msgid "text"
msgstr "Text"
@@ -4181,15 +4141,9 @@
msgid "to_state_object"
msgstr "Übergang zu diesem Zustand"
-msgid "todo_by"
-msgstr "zu erledigen bis"
-
msgid "toggle check boxes"
msgstr "Kontrollkästchen umkehren"
-msgid "toggle filter"
-msgstr "filter verbergen/zeigen"
-
msgid "tr_count"
msgstr ""
@@ -4480,6 +4434,14 @@
msgid "value %(KEY-value)s must be %(KEY-op)s %(KEY-boundary)s"
msgstr ""
+#, python-format
+msgid "value %(KEY-value)s must be <= %(KEY-boundary)s"
+msgstr ""
+
+#, python-format
+msgid "value %(KEY-value)s must be >= %(KEY-boundary)s"
+msgstr ""
+
msgid "value associated to this key is not editable manually"
msgstr ""
"Der mit diesem Schlüssele verbundene Wert kann n icht manuell geändert "
@@ -4523,10 +4485,6 @@
msgid "view_index"
msgstr "Index-Seite"
-#, python-format
-msgid "violates unique_together constraints (%s)"
-msgstr "Verletzung der unique_together-Einschränkung (%s)"
-
msgid "visible"
msgstr "sichtbar"
@@ -4536,6 +4494,9 @@
msgid "we are not yet ready to handle this query"
msgstr "Momentan können wir diese sparql-Anfrage noch nicht ausführen."
+msgid "web sessions without CNX"
+msgstr ""
+
msgid "wednesday"
msgstr "Mittwoch"
@@ -4566,11 +4527,11 @@
msgid "workflow"
msgstr "Workflow"
-msgid "workflow already have a state of that name"
-msgstr "Der Workflow hat bereits einen Zustand desselben Namens."
-
-msgid "workflow already have a transition of that name"
-msgstr "Der Workflow hat bereits einen Übergang desselben Namens."
+msgid "workflow already has a state of that name"
+msgstr ""
+
+msgid "workflow already has a transition of that name"
+msgstr ""
#, python-format
msgid "workflow changed to \"%s\""
@@ -4641,6 +4602,9 @@
#~ msgid "%(value)r doesn't match the %(regexp)r regular expression"
#~ msgstr "%(value)r entspricht nicht dem regulären Ausdruck %(regexp)r"
+#~ msgid "%s not estimated"
+#~ msgstr "%s unbekannt(e)"
+
#~ msgid ""
#~ "Can't restore relation %(rtype)s of entity %(eid)s, this relation does "
#~ "not exists anymore in the schema."
@@ -4648,11 +4612,98 @@
#~ "Kann die Relation %(rtype)s der Entität %(eid)s nicht wieder herstellen, "
#~ "diese Relation existiert nicht mehr in dem Schema."
+#~ msgid "Data connection graph for %s"
+#~ msgstr "Graf der Datenverbindungen für %s"
+
+#~ msgid "From:"
+#~ msgstr "Von:"
+
+#~ msgid "Got rhythm?"
+#~ msgstr "Hast Du Rhythmus ?"
+
+#~ msgid "Recipients:"
+#~ msgstr "Adressaten:"
+
+#~ msgid "Subject:"
+#~ msgstr "Subjekt :"
+
+#~ msgid "What's new?"
+#~ msgstr "Was ist neu?"
+
+#~ msgid "You can use any of the following substitutions in your text"
+#~ msgstr "Sie können die folgenden Ersetzungen in Ihrem Text verwenden:"
+
#~ msgid "can't change the %s attribute"
#~ msgstr "Kann das Attribut %s nicht ändern."
+#~ msgid "cost"
+#~ msgstr "Kosten"
+
+#~ msgid "emails successfully sent"
+#~ msgstr "E-Mails erfolgreich versandt."
+
+#~ msgid "embed"
+#~ msgstr "einbetten"
+
+#~ msgid "embedding this url is forbidden"
+#~ msgstr "Einbettung dieses URLs ist nicht erlaubt."
+
+#~ msgid "error while embedding page"
+#~ msgstr "Fehler beim Einbetten der Seite"
+
+#~ msgid "eta_date"
+#~ msgstr "Enddatum"
+
+#~ msgid "expected:"
+#~ msgstr "erwartet:"
+
+#~ msgid "external page"
+#~ msgstr "externe Seite"
+
#~ msgid "incorrect value (%(value)s) for type \"%(type)s\""
#~ msgstr "Wert %(value)s ungültig für den Typ \"%(type)s\""
+#~ msgid "initial estimation %s"
+#~ msgstr "Erste Schätzung %s"
+
#~ msgid "invalid value %(value)s, it must be one of %(choices)s"
#~ msgstr "Wert %(value)s ungültig, er muss zwischen %(choices)s"
+
+#~ msgid "milestone"
+#~ msgstr "Meilenstein"
+
+#~ msgid "no related project"
+#~ msgstr "kein verknüpftes Projekt"
+
+#~ msgid "progress"
+#~ msgstr "Fortschritt"
+
+#~ msgid "progress bar"
+#~ msgstr "Fortschrittsbalken"
+
+#~ msgid "project"
+#~ msgstr "Projekt"
+
+#~ msgid "send email"
+#~ msgstr "E-Mail senden"
+
+#~ msgid "sioc"
+#~ msgstr "sioc"
+
+#~ msgid "task progression"
+#~ msgstr "Fortschritt der Aufgabe"
+
+#~ msgid "todo_by"
+#~ msgstr "zu erledigen bis"
+
+#~ msgid "toggle filter"
+#~ msgstr "filter verbergen/zeigen"
+
+#~ msgid "violates unique_together constraints (%s)"
+#~ msgstr "Verletzung der unique_together-Einschränkung (%s)"
+
+#~ msgid "workflow already have a state of that name"
+#~ msgstr "Der Workflow hat bereits einen Zustand desselben Namens."
+
+#~ msgid "workflow already have a transition of that name"
+#~ msgstr "Der Workflow hat bereits einen Übergang desselben Namens."
diff -r aff75b69db92 -r 2c48c091b6a2 i18n/en.po
--- a/i18n/en.po Tue Jul 02 17:09:04 2013 +0200
+++ b/i18n/en.po Mon Jan 13 13:47:47 2014 +0100
@@ -106,7 +106,7 @@
msgstr ""
#, python-format
-msgid "%s not estimated"
+msgid "%s is part of violated unicity constraint"
msgstr ""
#, python-format
@@ -417,10 +417,6 @@
msgid "DEBUG"
msgstr ""
-#, python-format
-msgid "Data connection graph for %s"
-msgstr ""
-
msgid "Date"
msgstr "Date"
@@ -496,15 +492,9 @@
msgid "FormatConstraint"
msgstr "format constraint"
-msgid "From:"
-msgstr ""
-
msgid "Garbage collection information"
msgstr ""
-msgid "Got rhythm?"
-msgstr ""
-
msgid "Help"
msgstr ""
@@ -671,9 +661,6 @@
msgid "RQLVocabularyConstraint"
msgstr "RQL vocabulary constraint"
-msgid "Recipients:"
-msgstr ""
-
msgid "RegexpConstraint"
msgstr "regular expression constrainte"
@@ -735,9 +722,6 @@
msgid "SubWorkflowExitPoint_plural"
msgstr "subworkflow exit-points"
-msgid "Subject:"
-msgstr ""
-
msgid "Submit bug report"
msgstr ""
@@ -905,9 +889,6 @@
msgid "Web server"
msgstr ""
-msgid "What's new?"
-msgstr ""
-
msgid "Workflow"
msgstr "Workflow"
@@ -934,9 +915,6 @@
"content online with the widget below."
msgstr ""
-msgid "You can use any of the following substitutions in your text"
-msgstr ""
-
msgid "You can't change this relation"
msgstr ""
@@ -985,6 +963,9 @@
msgid "abstract base class for transitions"
msgstr ""
+msgid "action menu"
+msgstr ""
+
msgid "action(s) on this selection"
msgstr ""
@@ -1003,6 +984,9 @@
msgid "add Bookmark bookmarked_by CWUser object"
msgstr "bookmark"
+msgid "add CWAttribute add_permission RQLExpression subject"
+msgstr "rql expression for add permission"
+
msgid "add CWAttribute constrained_by CWConstraint subject"
msgstr "constraint"
@@ -1111,6 +1095,10 @@
msgid "add_permission"
msgstr "can be added by"
+msgctxt "CWAttribute"
+msgid "add_permission"
+msgstr "add permission"
+
# subject and object forms for each relation type
# (no object form for final relation types)
msgctxt "CWEType"
@@ -1145,6 +1133,9 @@
"%(eidto)s"
msgstr ""
+msgid "additional type specific properties"
+msgstr ""
+
msgid "addrelated"
msgstr "add"
@@ -1627,9 +1618,6 @@
"core relation indicating the types (including specialized types) of an entity"
msgstr ""
-msgid "cost"
-msgstr ""
-
msgid "could not connect to the SMTP server"
msgstr ""
@@ -1682,6 +1670,10 @@
msgstr "creating email address for user %(linkto)s"
msgid ""
+"creating RQLExpression (CWAttribute %(linkto)s add_permission RQLExpression)"
+msgstr "RQL expression granting add permission on %(linkto)s"
+
+msgid ""
"creating RQLExpression (CWAttribute %(linkto)s read_permission RQLExpression)"
msgstr "RQL expression granting read permission on %(linkto)s"
@@ -2056,6 +2048,9 @@
msgid "default value"
msgstr ""
+msgid "default value as gziped pickled python object"
+msgstr ""
+
msgid "default workflow for an entity type"
msgstr ""
@@ -2322,18 +2317,9 @@
msgid "eid"
msgstr ""
-msgid "emails successfully sent"
-msgstr ""
-
-msgid "embed"
-msgstr ""
-
msgid "embedded html"
msgstr ""
-msgid "embedding this url is forbidden"
-msgstr ""
-
msgid "end_timestamp"
msgstr "end timestamp"
@@ -2386,9 +2372,6 @@
msgid "error"
msgstr ""
-msgid "error while embedding page"
-msgstr ""
-
msgid "error while publishing ReST text"
msgstr ""
@@ -2396,9 +2379,6 @@
msgid "error while querying source %s, some data may be missing"
msgstr ""
-msgid "eta_date"
-msgstr "ETA date"
-
msgid "exit state must be a subworkflow state"
msgstr ""
@@ -2412,9 +2392,6 @@
msgid "exiting from subworkflow %s"
msgstr ""
-msgid "expected:"
-msgstr ""
-
msgid "expression"
msgstr ""
@@ -2429,7 +2406,11 @@
msgid "exprtype"
msgstr "expression type"
-msgid "external page"
+msgid "extra_props"
+msgstr ""
+
+msgctxt "CWAttribute"
+msgid "extra_props"
msgstr ""
msgid "facet-loading-msg"
@@ -2828,10 +2809,6 @@
msgid "info"
msgstr ""
-#, python-format
-msgid "initial estimation %s"
-msgstr ""
-
msgid "initial state for this workflow"
msgstr ""
@@ -3098,9 +3075,6 @@
msgid "message"
msgstr ""
-msgid "milestone"
-msgstr ""
-
#, python-format
msgid "missing parameters for entity %s"
msgstr ""
@@ -3160,6 +3134,10 @@
msgid "name"
msgstr "name"
+msgctxt "CWUniqueTogetherConstraint"
+msgid "name"
+msgstr ""
+
msgctxt "State"
msgid "name"
msgstr "name"
@@ -3245,9 +3223,6 @@
msgid "no related entity"
msgstr ""
-msgid "no related project"
-msgstr ""
-
msgid "no repository sessions found"
msgstr ""
@@ -3446,15 +3421,6 @@
msgid "profile"
msgstr ""
-msgid "progress"
-msgstr ""
-
-msgid "progress bar"
-msgstr ""
-
-msgid "project"
-msgstr ""
-
msgid "rdef-description"
msgstr "description"
@@ -3720,9 +3686,6 @@
msgid "semantic description of this workflow"
msgstr ""
-msgid "send email"
-msgstr ""
-
msgid "september"
msgstr ""
@@ -3748,9 +3711,6 @@
msgid "show filter form"
msgstr ""
-msgid "sioc"
-msgstr ""
-
msgid "site configuration"
msgstr ""
@@ -3766,6 +3726,9 @@
msgid "some later transaction(s) touch entity, undo them first"
msgstr ""
+msgid "some relations violate a unicity constraint"
+msgstr ""
+
msgid "sorry, the server is unable to handle this query"
msgstr ""
@@ -3957,9 +3920,6 @@
msgid "tablefilter"
msgstr "table filter"
-msgid "task progression"
-msgstr ""
-
msgid "text"
msgstr ""
@@ -4081,15 +4041,9 @@
msgid "to_state_object"
msgstr "transitions to this state"
-msgid "todo_by"
-msgstr "to do by"
-
msgid "toggle check boxes"
msgstr ""
-msgid "toggle filter"
-msgstr ""
-
msgid "tr_count"
msgstr "transition number"
@@ -4371,6 +4325,14 @@
msgid "value %(KEY-value)s must be %(KEY-op)s %(KEY-boundary)s"
msgstr ""
+#, python-format
+msgid "value %(KEY-value)s must be <= %(KEY-boundary)s"
+msgstr ""
+
+#, python-format
+msgid "value %(KEY-value)s must be >= %(KEY-boundary)s"
+msgstr ""
+
msgid "value associated to this key is not editable manually"
msgstr ""
@@ -4412,10 +4374,6 @@
msgid "view_index"
msgstr "index"
-#, python-format
-msgid "violates unique_together constraints (%s)"
-msgstr "violates unique_together constraints (%s)"
-
msgid "visible"
msgstr ""
@@ -4425,6 +4383,9 @@
msgid "we are not yet ready to handle this query"
msgstr ""
+msgid "web sessions without CNX"
+msgstr ""
+
msgid "wednesday"
msgstr ""
@@ -4453,10 +4414,10 @@
msgid "workflow"
msgstr ""
-msgid "workflow already have a state of that name"
-msgstr ""
-
-msgid "workflow already have a transition of that name"
+msgid "workflow already has a state of that name"
+msgstr ""
+
+msgid "workflow already has a transition of that name"
msgstr ""
#, python-format
@@ -4521,3 +4482,12 @@
#, python-format
msgid "you should un-inline relation %s which is supported and may be crossed "
msgstr ""
+
+#~ msgid "eta_date"
+#~ msgstr "ETA date"
+
+#~ msgid "todo_by"
+#~ msgstr "to do by"
+
+#~ msgid "violates unique_together constraints (%s)"
+#~ msgstr "violates unique_together constraints (%s)"
diff -r aff75b69db92 -r 2c48c091b6a2 i18n/es.po
--- a/i18n/es.po Tue Jul 02 17:09:04 2013 +0200
+++ b/i18n/es.po Mon Jan 13 13:47:47 2014 +0100
@@ -115,8 +115,8 @@
msgstr "%s reporte de errores"
#, python-format
-msgid "%s not estimated"
-msgstr "%s no estimado(s)"
+msgid "%s is part of violated unicity constraint"
+msgstr ""
#, python-format
msgid "%s relation should not be in mapped"
@@ -439,10 +439,6 @@
msgid "DEBUG"
msgstr ""
-#, python-format
-msgid "Data connection graph for %s"
-msgstr "Gráfica de conexión de datos para %s"
-
msgid "Date"
msgstr "Fecha"
@@ -518,15 +514,9 @@
msgid "FormatConstraint"
msgstr "Restricción de Formato"
-msgid "From:"
-msgstr "De: "
-
msgid "Garbage collection information"
msgstr "Recolector de basura en memoria"
-msgid "Got rhythm?"
-msgstr "Tenemos Ritmo?"
-
msgid "Help"
msgstr "Ayuda"
@@ -693,9 +683,6 @@
msgid "RQLVocabularyConstraint"
msgstr "Restricción RQL de Vocabulario"
-msgid "Recipients:"
-msgstr "Destinatarios :"
-
msgid "RegexpConstraint"
msgstr "restricción expresión regular"
@@ -760,9 +747,6 @@
msgid "SubWorkflowExitPoint_plural"
msgstr "Salidas de sub-workflow"
-msgid "Subject:"
-msgstr "Sujeto:"
-
msgid "Submit bug report"
msgstr "Enviar un reporte de error (bug)"
@@ -932,9 +916,6 @@
msgid "Web server"
msgstr "Servidor web"
-msgid "What's new?"
-msgstr "Lo más reciente"
-
msgid "Workflow"
msgstr "Workflow"
@@ -968,11 +949,6 @@
"\"buscar\" en la parte superior, o editar el contenido del archivo en línea\n"
"en el campo siguiente."
-msgid "You can use any of the following substitutions in your text"
-msgstr ""
-"Puede realizar cualquiera de las siguientes sustituciones en el contenido de "
-"su email."
-
msgid "You can't change this relation"
msgstr ""
@@ -1033,6 +1009,9 @@
msgid "abstract base class for transitions"
msgstr "Clase de base abstracta para la transiciones"
+msgid "action menu"
+msgstr ""
+
msgid "action(s) on this selection"
msgstr "Acción(es) en esta selección"
@@ -1051,6 +1030,9 @@
msgid "add Bookmark bookmarked_by CWUser object"
msgstr "Agregar a los favoritos "
+msgid "add CWAttribute add_permission RQLExpression subject"
+msgstr ""
+
msgid "add CWAttribute constrained_by CWConstraint subject"
msgstr "Restricción"
@@ -1159,6 +1141,10 @@
msgid "add_permission"
msgstr "Autorización para agregar"
+msgctxt "CWAttribute"
+msgid "add_permission"
+msgstr ""
+
# subject and object forms for each relation type
# (no object form for final relation types)
msgctxt "CWEType"
@@ -1195,6 +1181,9 @@
"la relación %(rtype)s de %(frometype)s #%(eidfrom)s a %(toetype)s #%(eidto)s "
"ha sido agregada"
+msgid "additional type specific properties"
+msgstr ""
+
msgid "addrelated"
msgstr "Agregar"
@@ -1692,9 +1681,6 @@
"Relación sistema indicando los tipos (incluídos los tipos padres) de una "
"entidad"
-msgid "cost"
-msgstr "Costo"
-
msgid "could not connect to the SMTP server"
msgstr "Imposible de conectarse al servidor SMTP"
@@ -1747,6 +1733,10 @@
msgstr "Creación de una dirección electrónica para el usuario %(linkto)s"
msgid ""
+"creating RQLExpression (CWAttribute %(linkto)s add_permission RQLExpression)"
+msgstr ""
+
+msgid ""
"creating RQLExpression (CWAttribute %(linkto)s read_permission RQLExpression)"
msgstr "creación de una expresión RQL por el derecho de lectura de %(linkto)s"
@@ -2129,6 +2119,9 @@
msgid "default value"
msgstr "Valor por defecto"
+msgid "default value as gziped pickled python object"
+msgstr ""
+
msgid "default workflow for an entity type"
msgstr "Workflow por defecto para un tipo de entidad"
@@ -2412,18 +2405,9 @@
msgid "eid"
msgstr "eid"
-msgid "emails successfully sent"
-msgstr "Mensajes enviados con éxito"
-
-msgid "embed"
-msgstr "Incrustado"
-
msgid "embedded html"
msgstr "Html incrustado"
-msgid "embedding this url is forbidden"
-msgstr "La inclusión de este url esta prohibida"
-
msgid "end_timestamp"
msgstr ""
@@ -2478,9 +2462,6 @@
msgid "error"
msgstr "error"
-msgid "error while embedding page"
-msgstr "Error durante la inclusión de la página"
-
msgid "error while publishing ReST text"
msgstr ""
"Se ha producido un error durante la interpretación del texto en formato ReST"
@@ -2491,9 +2472,6 @@
"Un error ha ocurrido al interrogar %s, es posible que los \n"
"datos visibles se encuentren incompletos"
-msgid "eta_date"
-msgstr "Fecha de fin"
-
msgid "exit state must be a subworkflow state"
msgstr "El estado de salida debe de ser un estado del Sub-Workflow"
@@ -2507,9 +2485,6 @@
msgid "exiting from subworkflow %s"
msgstr "Salida del subworkflow %s"
-msgid "expected:"
-msgstr "Previsto :"
-
msgid "expression"
msgstr "Expresión"
@@ -2524,8 +2499,12 @@
msgid "exprtype"
msgstr "Tipo"
-msgid "external page"
-msgstr "Página externa"
+msgid "extra_props"
+msgstr ""
+
+msgctxt "CWAttribute"
+msgid "extra_props"
+msgstr ""
msgid "facet-loading-msg"
msgstr ""
@@ -2942,10 +2921,6 @@
msgid "info"
msgstr "Información del Sistema"
-#, python-format
-msgid "initial estimation %s"
-msgstr "Estimación inicial %s"
-
msgid "initial state for this workflow"
msgstr "Estado inicial para este Workflow"
@@ -3221,9 +3196,6 @@
msgid "message"
msgstr ""
-msgid "milestone"
-msgstr "Milestone"
-
#, python-format
msgid "missing parameters for entity %s"
msgstr "Parámetros faltantes a la entidad %s"
@@ -3283,6 +3255,10 @@
msgid "name"
msgstr ""
+msgctxt "CWUniqueTogetherConstraint"
+msgid "name"
+msgstr ""
+
msgctxt "State"
msgid "name"
msgstr "nombre"
@@ -3370,9 +3346,6 @@
msgid "no related entity"
msgstr "No posee entidad asociada"
-msgid "no related project"
-msgstr "No tiene proyecto relacionado"
-
msgid "no repository sessions found"
msgstr "Ninguna sesión encontrada"
@@ -3572,15 +3545,6 @@
msgid "profile"
msgstr "perfil"
-msgid "progress"
-msgstr "Progreso"
-
-msgid "progress bar"
-msgstr "Barra de Progreso"
-
-msgid "project"
-msgstr "Proyecto"
-
msgid "rdef-description"
msgstr "Descripción"
@@ -3858,9 +3822,6 @@
msgid "semantic description of this workflow"
msgstr "Descripcion semántica de este Workflow"
-msgid "send email"
-msgstr "Enviar email"
-
msgid "september"
msgstr "Septiembre"
@@ -3890,9 +3851,6 @@
msgid "show filter form"
msgstr "Mostrar el Filtro"
-msgid "sioc"
-msgstr "SIOC"
-
msgid "site configuration"
msgstr "Configuración Sistema"
@@ -3909,6 +3867,9 @@
msgstr ""
"Las transacciones más recientes modificaron esta entidad, anúlelas primero"
+msgid "some relations violate a unicity constraint"
+msgstr ""
+
msgid "sorry, the server is unable to handle this query"
msgstr "Lo sentimos, el servidor no puede manejar esta consulta"
@@ -4106,9 +4067,6 @@
msgid "tablefilter"
msgstr "Tablero de Filtrado"
-msgid "task progression"
-msgstr "Progreso de la Acción"
-
msgid "text"
msgstr "Texto"
@@ -4231,15 +4189,9 @@
msgid "to_state_object"
msgstr "Transición hacia este Estado"
-msgid "todo_by"
-msgstr "Asignada a"
-
msgid "toggle check boxes"
msgstr "Cambiar valor"
-msgid "toggle filter"
-msgstr "esconder/mostrar el filtro"
-
msgid "tr_count"
msgstr "n° de transición"
@@ -4530,6 +4482,14 @@
msgid "value %(KEY-value)s must be %(KEY-op)s %(KEY-boundary)s"
msgstr ""
+#, python-format
+msgid "value %(KEY-value)s must be <= %(KEY-boundary)s"
+msgstr ""
+
+#, python-format
+msgid "value %(KEY-value)s must be >= %(KEY-boundary)s"
+msgstr ""
+
msgid "value associated to this key is not editable manually"
msgstr "El valor asociado a este elemento no es editable manualmente"
@@ -4571,10 +4531,6 @@
msgid "view_index"
msgstr "Inicio"
-#, python-format
-msgid "violates unique_together constraints (%s)"
-msgstr "viola el principio (o restricción) de singularidad (%s)"
-
msgid "visible"
msgstr "Visible"
@@ -4584,6 +4540,9 @@
msgid "we are not yet ready to handle this query"
msgstr "Aún no podemos manejar este tipo de consulta Sparql"
+msgid "web sessions without CNX"
+msgstr ""
+
msgid "wednesday"
msgstr "Miércoles"
@@ -4615,11 +4574,11 @@
msgid "workflow"
msgstr "Workflow"
-msgid "workflow already have a state of that name"
-msgstr "El Workflow ya tiene un Estado con ese nombre"
-
-msgid "workflow already have a transition of that name"
-msgstr "El Workflow ya tiene una transición con ese nombre"
+msgid "workflow already has a state of that name"
+msgstr ""
+
+msgid "workflow already has a transition of that name"
+msgstr ""
#, python-format
msgid "workflow changed to \"%s\""
@@ -4692,6 +4651,9 @@
#~ msgid "%(value)r doesn't match the %(regexp)r regular expression"
#~ msgstr "%(value)r no corresponde a la expresión regular %(regexp)r"
+#~ msgid "%s not estimated"
+#~ msgstr "%s no estimado(s)"
+
#~ msgid ""
#~ "Can't restore relation %(rtype)s of entity %(eid)s, this relation does "
#~ "not exists anymore in the schema."
@@ -4699,17 +4661,106 @@
#~ "No puede restaurar la relación %(rtype)s de la entidad %(eid)s, esta "
#~ "relación ya no existe en el esquema."
+#~ msgid "Data connection graph for %s"
+#~ msgstr "Gráfica de conexión de datos para %s"
+
+#~ msgid "From:"
+#~ msgstr "De: "
+
+#~ msgid "Got rhythm?"
+#~ msgstr "Tenemos Ritmo?"
+
+#~ msgid "Recipients:"
+#~ msgstr "Destinatarios :"
+
+#~ msgid "Subject:"
+#~ msgstr "Sujeto:"
+
+#~ msgid "What's new?"
+#~ msgstr "Lo más reciente"
+
+#~ msgid "You can use any of the following substitutions in your text"
+#~ msgstr ""
+#~ "Puede realizar cualquiera de las siguientes sustituciones en el contenido "
+#~ "de su email."
+
#~ msgid "can't change the %s attribute"
#~ msgstr "no puede modificar el atributo %s"
#~ msgid "can't change this relation"
#~ msgstr "no puede modificar esta relación"
+#~ msgid "cost"
+#~ msgstr "Costo"
+
+#~ msgid "emails successfully sent"
+#~ msgstr "Mensajes enviados con éxito"
+
+#~ msgid "embed"
+#~ msgstr "Incrustado"
+
+#~ msgid "embedding this url is forbidden"
+#~ msgstr "La inclusión de este url esta prohibida"
+
+#~ msgid "error while embedding page"
+#~ msgstr "Error durante la inclusión de la página"
+
+#~ msgid "eta_date"
+#~ msgstr "Fecha de fin"
+
+#~ msgid "expected:"
+#~ msgstr "Previsto :"
+
+#~ msgid "external page"
+#~ msgstr "Página externa"
+
#~ msgid "incorrect value (%(value)s) for type \"%(type)s\""
#~ msgstr "valor %(value)s incorrecto para el tipo \"%(type)s\""
+#~ msgid "initial estimation %s"
+#~ msgstr "Estimación inicial %s"
+
#~ msgid "invalid value %(value)s, it must be one of %(choices)s"
#~ msgstr "Valor %(value)s incorrecto, debe estar entre %(choices)s"
+#~ msgid "milestone"
+#~ msgstr "Milestone"
+
+#~ msgid "no related project"
+#~ msgstr "No tiene proyecto relacionado"
+
+#~ msgid "progress"
+#~ msgstr "Progreso"
+
+#~ msgid "progress bar"
+#~ msgstr "Barra de Progreso"
+
+#~ msgid "project"
+#~ msgstr "Proyecto"
+
+#~ msgid "send email"
+#~ msgstr "Enviar email"
+
+#~ msgid "sioc"
+#~ msgstr "SIOC"
+
+#~ msgid "task progression"
+#~ msgstr "Progreso de la Acción"
+
+#~ msgid "todo_by"
+#~ msgstr "Asignada a"
+
+#~ msgid "toggle filter"
+#~ msgstr "esconder/mostrar el filtro"
+
#~ msgid "unknown source type"
#~ msgstr "tipo de fuente desconocida"
+
+#~ msgid "violates unique_together constraints (%s)"
+#~ msgstr "viola el principio (o restricción) de singularidad (%s)"
+
+#~ msgid "workflow already have a state of that name"
+#~ msgstr "El Workflow ya tiene un Estado con ese nombre"
+
+#~ msgid "workflow already have a transition of that name"
+#~ msgstr "El Workflow ya tiene una transición con ese nombre"
diff -r aff75b69db92 -r 2c48c091b6a2 i18n/fr.po
--- a/i18n/fr.po Tue Jul 02 17:09:04 2013 +0200
+++ b/i18n/fr.po Mon Jan 13 13:47:47 2014 +0100
@@ -115,8 +115,8 @@
msgstr "%s rapport d'erreur"
#, python-format
-msgid "%s not estimated"
-msgstr "%s non estimé(s)"
+msgid "%s is part of violated unicity constraint"
+msgstr "%s appartient à une contrainte d'unicité transgressée"
#, python-format
msgid "%s relation should not be in mapped"
@@ -432,6 +432,8 @@
"Configuration of the system source goes to the 'sources' file, not in the "
"database"
msgstr ""
+"La configuration de la source système va dans le fichier 'sources' et non "
+"dans la base de données"
#, python-format
msgid "Created %(etype)s : %(entity)s"
@@ -440,10 +442,6 @@
msgid "DEBUG"
msgstr "DEBUG"
-#, python-format
-msgid "Data connection graph for %s"
-msgstr "Graphique de connection des données pour %s"
-
msgid "Date"
msgstr "Date"
@@ -519,15 +517,9 @@
msgid "FormatConstraint"
msgstr "contrainte de format"
-msgid "From:"
-msgstr "De :"
-
msgid "Garbage collection information"
msgstr "Information sur le ramasse-miette"
-msgid "Got rhythm?"
-msgstr "T'as le rythme ?"
-
msgid "Help"
msgstr "Aide"
@@ -694,9 +686,6 @@
msgid "RQLVocabularyConstraint"
msgstr "contrainte rql de vocabulaire"
-msgid "Recipients:"
-msgstr "Destinataires :"
-
msgid "RegexpConstraint"
msgstr "contrainte expression régulière"
@@ -761,9 +750,6 @@
msgid "SubWorkflowExitPoint_plural"
msgstr "Sorties de sous-workflow"
-msgid "Subject:"
-msgstr "Sujet :"
-
msgid "Submit bug report"
msgstr "Soumettre un rapport de bug"
@@ -935,9 +921,6 @@
msgid "Web server"
msgstr "Serveur web"
-msgid "What's new?"
-msgstr "Nouveautés"
-
msgid "Workflow"
msgstr "Workflow"
@@ -971,11 +954,6 @@
"\"parcourir\" ci-dessu, soit éditer le contenu du fichier en ligne\n"
"avec le champ ci-dessous."
-msgid "You can use any of the following substitutions in your text"
-msgstr ""
-"Vous pouvez utiliser n'importe quelle substitution parmi la liste suivante "
-"dans le contenu de votre courriel."
-
msgid "You can't change this relation"
msgstr "Vous ne pouvez pas modifier cette relation"
@@ -1036,6 +1014,9 @@
msgid "abstract base class for transitions"
msgstr "classe de base abstraite pour les transitions"
+msgid "action menu"
+msgstr "actions"
+
msgid "action(s) on this selection"
msgstr "action(s) sur cette sélection"
@@ -1054,6 +1035,9 @@
msgid "add Bookmark bookmarked_by CWUser object"
msgstr "signet"
+msgid "add CWAttribute add_permission RQLExpression subject"
+msgstr "définir une expression RQL d'ajout"
+
msgid "add CWAttribute constrained_by CWConstraint subject"
msgstr "contrainte"
@@ -1162,6 +1146,10 @@
msgid "add_permission"
msgstr "peut ajouter"
+msgctxt "CWAttribute"
+msgid "add_permission"
+msgstr "permission d'ajout"
+
# subject and object forms for each relation type
# (no object form for final relation types)
msgctxt "CWEType"
@@ -1198,6 +1186,9 @@
"la relation %(rtype)s de %(frometype)s #%(eidfrom)s vers %(toetype)s #"
"%(eidto)s a été ajoutée"
+msgid "additional type specific properties"
+msgstr "propriétés supplémentaires spécifiques au type"
+
msgid "addrelated"
msgstr "ajouter"
@@ -1704,9 +1695,6 @@
"relation système indiquant les types (y compris les types parents) d'une "
"entité"
-msgid "cost"
-msgstr "coût"
-
msgid "could not connect to the SMTP server"
msgstr "impossible de se connecter au serveur SMTP"
@@ -1759,6 +1747,10 @@
msgstr "création d'une adresse électronique pour l'utilisateur %(linkto)s"
msgid ""
+"creating RQLExpression (CWAttribute %(linkto)s add_permission RQLExpression)"
+msgstr "création d'une expression rql pour le droit d'ajout de %(linkto)s"
+
+msgid ""
"creating RQLExpression (CWAttribute %(linkto)s read_permission RQLExpression)"
msgstr "création d'une expression rql pour le droit de lecture de %(linkto)s"
@@ -2143,6 +2135,9 @@
msgid "default value"
msgstr "valeur par défaut"
+msgid "default value as gziped pickled python object"
+msgstr "valeur par défaut, sous forme d'objet python picklé zippé"
+
msgid "default workflow for an entity type"
msgstr "workflow par défaut pour un type d'entité"
@@ -2423,18 +2418,9 @@
msgid "eid"
msgstr "eid"
-msgid "emails successfully sent"
-msgstr "courriels envoyés avec succès"
-
-msgid "embed"
-msgstr "embarqué"
-
msgid "embedded html"
msgstr "HTML contenu"
-msgid "embedding this url is forbidden"
-msgstr "l'inclusion de cette url est interdite"
-
msgid "end_timestamp"
msgstr "horodate de fin"
@@ -2489,9 +2475,6 @@
msgid "error"
msgstr "erreur"
-msgid "error while embedding page"
-msgstr "erreur pendant l'inclusion de la page"
-
msgid "error while publishing ReST text"
msgstr ""
"une erreur s'est produite lors de l'interprétation du texte au format ReST"
@@ -2502,9 +2485,6 @@
"une erreur est survenue en interrogeant %s, il est possible que les\n"
"données affichées soient incomplètes"
-msgid "eta_date"
-msgstr "date de fin"
-
msgid "exit state must be a subworkflow state"
msgstr "l'état de sortie doit être un état du sous-workflow"
@@ -2518,9 +2498,6 @@
msgid "exiting from subworkflow %s"
msgstr "sortie du sous-workflow %s"
-msgid "expected:"
-msgstr "attendu :"
-
msgid "expression"
msgstr "expression"
@@ -2535,8 +2512,12 @@
msgid "exprtype"
msgstr "type"
-msgid "external page"
-msgstr "page externe"
+msgid "extra_props"
+msgstr ""
+
+msgctxt "CWAttribute"
+msgid "extra_props"
+msgstr "propriétés additionnelles"
msgid "facet-loading-msg"
msgstr "en cours de traitement, merci de patienter"
@@ -2951,10 +2932,6 @@
msgid "info"
msgstr "information"
-#, python-format
-msgid "initial estimation %s"
-msgstr "estimation initiale %s"
-
msgid "initial state for this workflow"
msgstr "état initial pour ce workflow"
@@ -3232,9 +3209,6 @@
msgid "message"
msgstr "message"
-msgid "milestone"
-msgstr "jalon"
-
#, python-format
msgid "missing parameters for entity %s"
msgstr "paramètres manquants pour l'entité %s"
@@ -3294,6 +3268,10 @@
msgid "name"
msgstr "nom"
+msgctxt "CWUniqueTogetherConstraint"
+msgid "name"
+msgstr "nom"
+
msgctxt "State"
msgid "name"
msgstr "nom"
@@ -3381,9 +3359,6 @@
msgid "no related entity"
msgstr "pas d'entité liée"
-msgid "no related project"
-msgstr "pas de projet rattaché"
-
msgid "no repository sessions found"
msgstr "aucune session trouvée"
@@ -3585,15 +3560,6 @@
msgid "profile"
msgstr "profil"
-msgid "progress"
-msgstr "avancement"
-
-msgid "progress bar"
-msgstr "barre d'avancement"
-
-msgid "project"
-msgstr "projet"
-
msgid "rdef-description"
msgstr "description"
@@ -3872,9 +3838,6 @@
msgid "semantic description of this workflow"
msgstr "description sémantique de ce workflow"
-msgid "send email"
-msgstr "envoyer un courriel"
-
msgid "september"
msgstr "septembre"
@@ -3903,9 +3866,6 @@
msgid "show filter form"
msgstr "afficher le filtre"
-msgid "sioc"
-msgstr "sioc"
-
msgid "site configuration"
msgstr "configuration du site"
@@ -3922,6 +3882,9 @@
msgstr ""
"des transactions plus récentes modifient cette entité, annulez les d'abord"
+msgid "some relations violate a unicity constraint"
+msgstr "certaines relations transgressent une contrainte d'unicité"
+
msgid "sorry, the server is unable to handle this query"
msgstr "désolé, le serveur ne peut traiter cette requête"
@@ -4121,9 +4084,6 @@
msgid "tablefilter"
msgstr "filtre de tableau"
-msgid "task progression"
-msgstr "avancement de la tâche"
-
msgid "text"
msgstr "text"
@@ -4246,15 +4206,9 @@
msgid "to_state_object"
msgstr "transition vers cet état"
-msgid "todo_by"
-msgstr "à faire par"
-
msgid "toggle check boxes"
msgstr "afficher/masquer les cases à cocher"
-msgid "toggle filter"
-msgstr "afficher/masquer le filtre"
-
msgid "tr_count"
msgstr "n° de transition"
@@ -4543,6 +4497,16 @@
msgid "value %(KEY-value)s must be %(KEY-op)s %(KEY-boundary)s"
msgstr "la valeur %(KEY-value)s n'est pas %(KEY-op)s %(KEY-boundary)s"
+#, python-format
+msgid "value %(KEY-value)s must be <= %(KEY-boundary)s"
+msgstr ""
+"la valeur %(KEY-value)s n'est pas inférieure ou égale à %(KEY-boundary)s"
+
+#, python-format
+msgid "value %(KEY-value)s must be >= %(KEY-boundary)s"
+msgstr ""
+"la valeur %(KEY-value)s n'est pas supérieure ou égale à %(KEY-boundary)s"
+
msgid "value associated to this key is not editable manually"
msgstr "la valeur associée à cette clé n'est pas éditable manuellement"
@@ -4588,10 +4552,6 @@
msgid "view_index"
msgstr "accueil"
-#, python-format
-msgid "violates unique_together constraints (%s)"
-msgstr "violation de contrainte unique_together (%s)"
-
msgid "visible"
msgstr "visible"
@@ -4602,6 +4562,9 @@
msgstr ""
"nous ne sommes pas capable de gérer ce type de requête sparql pour le moment"
+msgid "web sessions without CNX"
+msgstr "sessions web sans connexion associée"
+
msgid "wednesday"
msgstr "mercredi"
@@ -4633,10 +4596,10 @@
msgid "workflow"
msgstr "workflow"
-msgid "workflow already have a state of that name"
+msgid "workflow already has a state of that name"
msgstr "le workflow a déja un état du même nom"
-msgid "workflow already have a transition of that name"
+msgid "workflow already has a transition of that name"
msgstr "le workflow a déja une transition du même nom"
#, python-format
@@ -4703,27 +4666,3 @@
msgstr ""
"vous devriez enlevé la mise en ligne de la relation %s qui est supportée et "
"peut-être croisée"
-
-#~ msgid "Action"
-#~ msgstr "Action"
-
-#~ msgid "day"
-#~ msgstr "jour"
-
-#~ msgid "jump to selection"
-#~ msgstr "afficher cette sélection"
-
-#~ msgid "log out first"
-#~ msgstr "déconnecter vous d'abord"
-
-#~ msgid "month"
-#~ msgstr "mois"
-
-#~ msgid "today"
-#~ msgstr "aujourd'hui"
-
-#~ msgid "undo last change"
-#~ msgstr "annuler dernier changement"
-
-#~ msgid "week"
-#~ msgstr "semaine"
diff -r aff75b69db92 -r 2c48c091b6a2 interfaces.py
--- a/interfaces.py Tue Jul 02 17:09:04 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,214 +0,0 @@
-# copyright 2003-2010 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 .
-"""Standard interfaces. Deprecated in favor of adapters.
-
-.. note::
-
- The `implements` selector used to match not only entity classes but also their
- interfaces. This will disappear in a future version. You should define an
- adapter for that interface and use `adaptable('MyIFace')` selector on appobjects
- that require that interface.
-
-"""
-__docformat__ = "restructuredtext en"
-
-from logilab.common.interface import Interface
-
-
-# XXX deprecates in favor of IProgressAdapter
-class IProgress(Interface):
- """something that has a cost, a state and a progression"""
-
- @property
- def cost(self):
- """the total cost"""
-
- @property
- def done(self):
- """what is already done"""
-
- @property
- def todo(self):
- """what remains to be done"""
-
- def progress_info(self):
- """returns a dictionary describing progress/estimated cost of the
- version.
-
- - mandatory keys are (''estimated', 'done', 'todo')
-
- - optional keys are ('notestimated', 'notestimatedcorrected',
- 'estimatedcorrected')
-
- 'noestimated' and 'notestimatedcorrected' should default to 0
- 'estimatedcorrected' should default to 'estimated'
- """
-
- def finished(self):
- """returns True if status is finished"""
-
- def in_progress(self):
- """returns True if status is not finished"""
-
- def progress(self):
- """returns the % progress of the task item"""
-
-# XXX deprecates in favor of IMileStoneAdapter
-class IMileStone(IProgress):
- """represents an ITask's item"""
-
- parent_type = None # specify main task's type
-
- def get_main_task(self):
- """returns the main ITask entity"""
-
- def initial_prevision_date(self):
- """returns the initial expected end of the milestone"""
-
- def eta_date(self):
- """returns expected date of completion based on what remains
- to be done
- """
-
- def completion_date(self):
- """returns date on which the subtask has been completed"""
-
- def contractors(self):
- """returns the list of persons supposed to work on this task"""
-
-# XXX deprecates in favor of IEmbedableAdapter
-class IEmbedable(Interface):
- """interface for embedable entities"""
-
- def embeded_url(self):
- """embed action interface"""
-
-# XXX deprecates in favor of ICalendarViewsAdapter
-class ICalendarViews(Interface):
- """calendar views interface"""
- def matching_dates(self, begin, end):
- """
- :param begin: day considered as begin of the range (`DateTime`)
- :param end: day considered as end of the range (`DateTime`)
-
- :return:
- a list of dates (`DateTime`) in the range [`begin`, `end`] on which
- this entity apply
- """
-
-# XXX deprecates in favor of ICalendarableAdapter
-class ICalendarable(Interface):
- """interface for items that do have a begin date 'start' and an end date 'stop'
- """
-
- @property
- def start(self):
- """return start date"""
-
- @property
- def stop(self):
- """return stop state"""
-
-# XXX deprecates in favor of ICalendarableAdapter
-class ITimetableViews(Interface):
- """timetable views interface"""
- def timetable_date(self):
- """XXX explain
-
- :return: date (`DateTime`)
- """
-
-# XXX deprecates in favor of IGeocodableAdapter
-class IGeocodable(Interface):
- """interface required by geocoding views such as gmap-view"""
-
- @property
- def latitude(self):
- """returns the latitude of the entity"""
-
- @property
- def longitude(self):
- """returns the longitude of the entity"""
-
- def marker_icon(self):
- """returns the icon that should be used as the marker"""
-
-
-# XXX deprecates in favor of IEmailableAdapter
-class IFeed(Interface):
- """interface for entities with rss flux"""
-
- def rss_feed_url(self):
- """"""
-
-# XXX deprecates in favor of IDownloadableAdapter
-class IDownloadable(Interface):
- """interface for downloadable entities"""
-
- def download_url(self): # XXX not really part of this interface
- """return an url to download entity's content"""
- def download_content_type(self):
- """return MIME type of the downloadable content"""
- def download_encoding(self):
- """return encoding of the downloadable content"""
- def download_file_name(self):
- """return file name of the downloadable content"""
- def download_data(self):
- """return actual data of the downloadable content"""
-
-# XXX deprecates in favor of IPrevNextAdapter
-class IPrevNext(Interface):
- """interface for entities which can be linked to a previous and/or next
- entity
- """
-
- def next_entity(self):
- """return the 'next' entity"""
- def previous_entity(self):
- """return the 'previous' entity"""
-
-# XXX deprecates in favor of IBreadCrumbsAdapter
-class IBreadCrumbs(Interface):
-
- def breadcrumbs(self, view, recurs=False):
- pass
-
-# XXX deprecates in favor of ITreeAdapter
-class ITree(Interface):
-
- def parent(self):
- """returns the parent entity"""
-
- def children(self):
- """returns the item's children"""
-
- def children_rql(self):
- """XXX returns RQL to get children"""
-
- def iterchildren(self):
- """iterates over the item's children"""
-
- def is_leaf(self):
- """returns true if this node as no child"""
-
- def is_root(self):
- """returns true if this node has no parent"""
-
- def root(self):
- """returns the root object"""
-
diff -r aff75b69db92 -r 2c48c091b6a2 mail.py
--- a/mail.py Tue Jul 02 17:09:04 2013 +0200
+++ b/mail.py Mon Jan 13 13:47:47 2014 +0100
@@ -90,7 +90,7 @@
email = u''
if uinfo.get('name'):
name = uinfo['name']
- elif config and config['sender-addr']:
+ elif config and config['sender-name']:
name = unicode(config['sender-name'])
else:
name = u''
diff -r aff75b69db92 -r 2c48c091b6a2 migration.py
--- a/migration.py Tue Jul 02 17:09:04 2013 +0200
+++ b/migration.py Mon Jan 13 13:47:47 2014 +0100
@@ -257,7 +257,7 @@
home_key = 'HOME'
if sys.platform == 'win32':
home_key = 'USERPROFILE'
- histfile = os.path.join(os.environ[home_key], ".eshellhist")
+ histfile = os.path.join(os.environ[home_key], ".cwshell_history")
try:
readline.read_history_file(histfile)
except IOError:
diff -r aff75b69db92 -r 2c48c091b6a2 misc/migration/3.17.11_Any.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.17.11_Any.py Mon Jan 13 13:47:47 2014 +0100
@@ -0,0 +1,7 @@
+for table, column in [
+ ('transactions', 'tx_time'),
+ ('tx_entity_actions', 'tx_uuid'),
+ ('tx_relation_actions', 'tx_uuid')]:
+ session.cnxset.source('system').create_index(session, table, column)
+
+commit()
diff -r aff75b69db92 -r 2c48c091b6a2 misc/migration/3.18.0_Any.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.18.0_Any.py Mon Jan 13 13:47:47 2014 +0100
@@ -0,0 +1,129 @@
+driver = config.sources()['system']['db-driver']
+if not (driver == 'postgres' or driver.startswith('sqlserver')):
+ import sys
+ print >>sys.stderr, 'This migration is not supported for backends other than sqlserver or postgres (yet).'
+ sys.exit(1)
+
+sync_schema_props_perms('defaultval')
+
+def convert_defaultval(cwattr, default):
+ from decimal import Decimal
+ import yams
+ from cubicweb import Binary
+ if default is None:
+ return
+ atype = cwattr.to_entity[0].name
+ if atype == 'Boolean':
+ # boolean attributes with default=False were stored as ''
+ assert default in ('True', 'False', ''), repr(default)
+ default = default == 'True'
+ elif atype in ('Int', 'BigInt'):
+ default = int(default)
+ elif atype == 'Float':
+ default = float(default)
+ elif atype == 'Decimal':
+ default = Decimal(default)
+ elif atype in ('Date', 'Datetime', 'TZDatetime', 'Time'):
+ try:
+ # handle NOW and TODAY, keep them stored as strings
+ yams.KEYWORD_MAP[atype][default.upper()]
+ default = default.upper()
+ except KeyError:
+ # otherwise get an actual date or datetime
+ default = yams.DATE_FACTORY_MAP[atype](default)
+ else:
+ assert atype == 'String', atype
+ default = unicode(default)
+ return Binary.zpickle(default)
+
+dbh = repo.system_source.dbhelper
+
+
+sql('ALTER TABLE cw_cwattribute ADD new_defaultval %s' % dbh.TYPE_MAPPING['Bytes'])
+
+for cwattr in rql('CWAttribute X').entities():
+ olddefault = cwattr.defaultval
+ if olddefault is not None:
+ req = "UPDATE cw_cwattribute SET new_defaultval = %(val)s WHERE cw_eid = %(eid)s"
+ args = {'val': dbh.binary_value(convert_defaultval(cwattr, olddefault).getvalue()), 'eid': cwattr.eid}
+ sql(req, args, ask_confirm=False)
+
+sql('ALTER TABLE cw_cwattribute DROP COLUMN cw_defaultval')
+if driver == 'postgres':
+ sql('ALTER TABLE cw_cwattribute RENAME COLUMN new_defaultval TO cw_defaultval')
+else: # sqlserver
+ sql("sp_rename 'cw_cwattribute.new_defaultval', 'cw_defaultval', 'COLUMN'")
+
+
+# Set object type to "Bytes" for CWAttribute's "defaultval" attribute
+rql('SET X to_entity B WHERE X is CWAttribute, X from_entity Y, Y name "CWAttribute", '
+ 'X relation_type Z, Z name "defaultval", B name "Bytes"')
+
+from yams import buildobjs as ybo
+schema.add_relation_def(ybo.RelationDefinition('CWAttribute', 'defaultval', 'Bytes'))
+schema.del_relation_def('CWAttribute', 'defaultval', 'String')
+
+commit()
+
+for rschema in schema.relations():
+ if rschema.symmetric:
+ subjects = set(repr(e.type) for e in rschema.subjects())
+ objects = set(repr(e.type) for e in rschema.objects())
+ assert subjects == objects
+ martians = set(str(eid) for eid, in sql('SELECT eid_to FROM %s_relation, entities WHERE eid_to = eid AND type NOT IN (%s)' %
+ (rschema.type, ','.join(subjects))))
+ martians |= set(str(eid) for eid, in sql('SELECT eid_from FROM %s_relation, entities WHERE eid_from = eid AND type NOT IN (%s)' %
+ (rschema.type, ','.join(subjects))))
+ if martians:
+ martians = ','.join(martians)
+ print 'deleting broken relations %s for eids %s' % (rschema.type, martians)
+ sql('DELETE FROM %s_relation WHERE eid_from IN (%s) OR eid_to IN (%s)' % (rschema.type, martians, martians))
+ with session.deny_all_hooks_but():
+ rql('SET X %(r)s Y WHERE Y %(r)s X, NOT X %(r)s Y' % {'r': rschema.type})
+ commit()
+
+
+# multi columns unique constraints regeneration
+from cubicweb.server import schemaserial
+
+# syncschema hooks would try to remove indices but
+# 1) we already do that below
+# 2) the hook expects the CWUniqueTogetherConstraint.name attribute that hasn't
+# yet been added
+with session.allow_all_hooks_but('syncschema'):
+ rql('DELETE CWUniqueTogetherConstraint C')
+commit()
+
+add_attribute('CWUniqueTogetherConstraint', 'name')
+
+# low-level wipe code for postgres & sqlserver, plain sql ...
+if driver == 'postgres':
+ for indexname, in sql('select indexname from pg_indexes'):
+ if indexname.startswith('unique_'):
+ print 'dropping index', indexname
+ sql('DROP INDEX %s' % indexname)
+ commit()
+elif driver.startswith('sqlserver'):
+ for viewname, in sql('select name from sys.views'):
+ if viewname.startswith('utv_'):
+ print 'dropping view (index should be cascade-deleted)', viewname
+ sql('DROP VIEW %s' % viewname)
+ commit()
+
+# recreate the constraints, hook will lead to low-level recreation
+for eschema in sorted(schema.entities()):
+ if eschema._unique_together:
+ rql_args = schemaserial.uniquetogether2rqls(eschema)
+ for rql, args in rql_args:
+ args['x'] = eschema.eid
+ session.execute(rql, args)
+ commit()
+
+
+add_relation_definition('CWAttribute', 'add_permission', 'CWGroup')
+add_relation_definition('CWAttribute', 'add_permission', 'RQLExpression')
+
+# all attributes perms have to be refreshed ...
+for rschema in schema.relations():
+ if relation.final:
+ sync_schema_props_perms(rschema.type, syncprops=False)
diff -r aff75b69db92 -r 2c48c091b6a2 misc/migration/bootstrapmigration_repository.py
--- a/misc/migration/bootstrapmigration_repository.py Tue Jul 02 17:09:04 2013 +0200
+++ b/misc/migration/bootstrapmigration_repository.py Mon Jan 13 13:47:47 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -20,6 +20,7 @@
it should only include low level schema changes
"""
+from cubicweb import ConfigurationError
from cubicweb.server.session import hooks_control
from cubicweb.server import schemaserial as ss
@@ -37,19 +38,19 @@
if applcubicwebversion < (3, 17, 0) and cubicwebversion >= (3, 17, 0):
try:
add_cube('sioc', update_database=False)
- except ImportError:
+ except ConfigurationError:
if not confirm('In cubicweb 3.17 sioc views have been moved to the sioc '
'cube, which is not installed. Continue anyway?'):
raise
try:
add_cube('embed', update_database=False)
- except ImportError:
+ except ConfigurationError:
if not confirm('In cubicweb 3.17 embedding views have been moved to the embed '
'cube, which is not installed. Continue anyway?'):
raise
try:
add_cube('geocoding', update_database=False)
- except ImportError:
+ except ConfigurationError:
if not confirm('In cubicweb 3.17 geocoding views have been moved to the geocoding '
'cube, which is not installed. Continue anyway?'):
raise
@@ -72,7 +73,7 @@
from cubicweb import ExecutionError
try:
add_cube('localperms', update_database=False)
- except ImportError:
+ except ConfigurationError:
raise ExecutionError('In cubicweb 3.14, CWPermission and related stuff '
'has been moved to cube localperms. Install it first.')
diff -r aff75b69db92 -r 2c48c091b6a2 misc/scripts/ldapuser2ldapfeed.py
--- a/misc/scripts/ldapuser2ldapfeed.py Tue Jul 02 17:09:04 2013 +0200
+++ b/misc/scripts/ldapuser2ldapfeed.py Mon Jan 13 13:47:47 2014 +0100
@@ -95,5 +95,5 @@
commit()
else:
rollback()
- print 'rollbacked'
+ print 'rolled back'
diff -r aff75b69db92 -r 2c48c091b6a2 mixins.py
--- a/mixins.py Tue Jul 02 17:09:04 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,308 +0,0 @@
-# copyright 2003-2012 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 .
-"""mixins of entity/views organized somewhat in a graph or tree structure"""
-__docformat__ = "restructuredtext en"
-
-from itertools import chain
-
-from logilab.common.decorators import cached
-from logilab.common.deprecation import deprecated, class_deprecated
-
-from cubicweb.predicates import implements
-from cubicweb.interfaces import ITree
-
-
-class TreeMixIn(object):
- """base tree-mixin implementing the tree interface
-
- This mixin has to be inherited explicitly and configured using the
- tree_attribute, parent_target and children_target class attribute to
- benefit from this default implementation
- """
- __metaclass__ = class_deprecated
- __deprecation_warning__ = '[3.9] TreeMixIn is deprecated, use/override ITreeAdapter instead (%(cls)s)'
-
- tree_attribute = None
- # XXX misnamed
- parent_target = 'subject'
- children_target = 'object'
-
- def different_type_children(self, entities=True):
- """return children entities of different type as this entity.
-
- according to the `entities` parameter, return entity objects or the
- equivalent result set
- """
- res = self.related(self.tree_attribute, self.children_target,
- entities=entities)
- if entities:
- return [e for e in res if e.e_schema != self.e_schema]
- return res.filtered_rset(lambda x: x.e_schema != self.e_schema, self.cw_col)
-
- def same_type_children(self, entities=True):
- """return children entities of the same type as this entity.
-
- according to the `entities` parameter, return entity objects or the
- equivalent result set
- """
- res = self.related(self.tree_attribute, self.children_target,
- entities=entities)
- if entities:
- return [e for e in res if e.e_schema == self.e_schema]
- return res.filtered_rset(lambda x: x.e_schema is self.e_schema, self.cw_col)
-
- def iterchildren(self, _done=None):
- if _done is None:
- _done = set()
- for child in self.children():
- if child.eid in _done:
- self.error('loop in %s tree: %s', self.__regid__.lower(), child)
- continue
- yield child
- _done.add(child.eid)
-
- def prefixiter(self, _done=None):
- if _done is None:
- _done = set()
- if self.eid in _done:
- return
- _done.add(self.eid)
- yield self
- for child in self.same_type_children():
- for entity in child.prefixiter(_done):
- yield entity
-
- @cached
- def path(self):
- """returns the list of eids from the root object to this object"""
- path = []
- parent = self
- while parent:
- if parent.eid in path:
- self.error('loop in %s tree: %s', self.__regid__.lower(), parent)
- break
- path.append(parent.eid)
- try:
- # check we are not leaving the tree
- if (parent.tree_attribute != self.tree_attribute or
- parent.parent_target != self.parent_target):
- break
- parent = parent.parent()
- except AttributeError:
- break
-
- path.reverse()
- return path
-
- def iterparents(self, strict=True):
- def _uptoroot(self):
- curr = self
- while True:
- curr = curr.parent()
- if curr is None:
- break
- yield curr
- if not strict:
- return chain([self], _uptoroot(self))
- return _uptoroot(self)
-
- ## ITree interface ########################################################
- def parent(self):
- """return the parent entity if any, else None (e.g. if we are on the
- root
- """
- try:
- return self.related(self.tree_attribute, self.parent_target,
- entities=True)[0]
- except (KeyError, IndexError):
- return None
-
- def children(self, entities=True, sametype=False):
- """return children entities
-
- according to the `entities` parameter, return entity objects or the
- equivalent result set
- """
- if sametype:
- return self.same_type_children(entities)
- else:
- return self.related(self.tree_attribute, self.children_target,
- entities=entities)
-
- def children_rql(self):
- return self.cw_related_rql(self.tree_attribute, self.children_target)
-
- def is_leaf(self):
- return len(self.children()) == 0
-
- def is_root(self):
- return self.parent() is None
-
- def root(self):
- """return the root object"""
- return self._cw.entity_from_eid(self.path()[0])
-
-
-class EmailableMixIn(object):
- """base mixin providing the default get_email() method used by
- the massmailing view
-
- NOTE: The default implementation is based on the
- primary_email / use_email scheme
- """
- @deprecated("[3.9] use entity.cw_adapt_to('IEmailable').get_email()")
- def get_email(self):
- if getattr(self, 'primary_email', None):
- return self.primary_email[0].address
- if getattr(self, 'use_email', None):
- return self.use_email[0].address
- return None
-
-
-"""pluggable mixins system: plug classes registered in MI_REL_TRIGGERS on entity
-classes which have the relation described by the dict's key.
-
-NOTE: pluggable mixins can't override any method of the 'explicit' user classes tree
-(eg without plugged classes). This includes bases Entity and AnyEntity classes.
-"""
-MI_REL_TRIGGERS = {
- ('primary_email', 'subject'): EmailableMixIn,
- ('use_email', 'subject'): EmailableMixIn,
- }
-
-
-# XXX move to cubicweb.web.views.treeview once we delete usage from this file
-def _done_init(done, view, row, col):
- """handle an infinite recursion safety belt"""
- if done is None:
- done = set()
- entity = view.cw_rset.get_entity(row, col)
- if entity.eid in done:
- msg = entity._cw._('loop in %(rel)s relation (%(eid)s)') % {
- 'rel': entity.cw_adapt_to('ITree').tree_relation,
- 'eid': entity.eid
- }
- return None, msg
- done.add(entity.eid)
- return done, entity
-
-
-class TreeViewMixIn(object):
- """a recursive tree view"""
- __metaclass__ = class_deprecated
- __deprecation_warning__ = '[3.9] TreeViewMixIn is deprecated, use/override BaseTreeView instead (%(cls)s)'
-
- __regid__ = 'tree'
- __select__ = implements(ITree, warn=False)
- item_vid = 'treeitem'
-
- def call(self, done=None, **kwargs):
- if done is None:
- done = set()
- super(TreeViewMixIn, self).call(done=done, **kwargs)
-
- def cell_call(self, row, col=0, vid=None, done=None, maxlevel=None, **kwargs):
- assert maxlevel is None or maxlevel > 0
- done, entity = _done_init(done, self, row, col)
- if done is None:
- # entity is actually an error message
- self.w(u'