Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1a1492b89 | ||
|
|
382a092010 | ||
|
|
7ea5098754 | ||
|
|
941b57af02 | ||
|
|
d64edceb58 | ||
|
|
c642a9b0c1 | ||
|
|
dff4eb2241 | ||
|
|
8f4ecc311b | ||
|
|
34bbd12920 | ||
|
|
9259845f93 | ||
|
|
dccb411d7f | ||
|
|
3164ea2040 | ||
|
|
c455d3c614 | ||
|
|
e149e0bdd9 | ||
|
|
5bedd2b6da | ||
|
|
8a4e152f8e | ||
|
|
2f26ac67ce | ||
|
|
b68175970a | ||
|
|
6b9e112448 | ||
|
|
ac76517411 | ||
|
|
370d322e32 | ||
|
|
3bfbea6e43 | ||
|
|
628072b6e2 | ||
|
|
962305a955 | ||
|
|
6425a0759c | ||
|
|
85cd73b479 | ||
|
|
0eb535fafc | ||
|
|
bcd5926fa2 | ||
|
|
33b374a7fe | ||
|
|
e26853c4b6 | ||
|
|
4414c66423 | ||
|
|
447175af18 | ||
|
|
706e52ed21 | ||
|
|
7b2d513a8e | ||
|
|
40902a74fd | ||
|
|
fef05cabdf | ||
|
|
8fbd4fd0a6 | ||
|
|
ac64b76654 | ||
|
|
7651c0127e | ||
|
|
9c5f99f9eb | ||
|
|
20f95807a4 | ||
|
|
9f16969ee7 | ||
|
|
53518b91fc | ||
|
|
70142436ae | ||
|
|
01cbda0183 | ||
|
|
159b597896 | ||
|
|
82acedefee | ||
|
|
6ede6f59c6 | ||
|
|
8ebae458c6 | ||
|
|
19abf1c4c4 | ||
|
|
e5f7d0ec38 | ||
|
|
f9a8b38b60 | ||
|
|
ea2ce14b75 | ||
|
|
b4eee45e9b | ||
|
|
dee09dfe09 | ||
|
|
17a6868a04 | ||
|
|
42bd941c46 | ||
|
|
84575c4497 | ||
|
|
2df20b1a40 | ||
|
|
3941e426e4 | ||
|
|
c59b8fcfe7 | ||
|
|
d99f1cfc3e | ||
|
|
55ce5c0dd2 | ||
|
|
758da5b987 | ||
|
|
1a5179cc17 | ||
|
|
2865f19afd | ||
|
|
ae74399b0b | ||
|
|
d0d119cd85 | ||
|
|
27cf2ab9c7 | ||
|
|
583afaa85a | ||
|
|
95f9944ade | ||
|
|
48a1b845a2 | ||
|
|
bfe7002961 | ||
|
|
21d34a3b9f | ||
|
|
2d79296b28 | ||
|
|
164d74bdad | ||
|
|
23419bab0c | ||
|
|
ba4ef1478d | ||
|
|
f4f0ee7da9 | ||
|
|
d1aba902f8 | ||
|
|
a86dce368a | ||
|
|
d19a54a14d | ||
|
|
eaf7959b6c | ||
|
|
75fb886c9a | ||
|
|
ebde18f595 | ||
|
|
31fe02c988 | ||
|
|
608932e277 | ||
|
|
8dc6e6f0cf | ||
|
|
99dc645adb | ||
|
|
4dc7320a8f | ||
|
|
d0c3b3fb6d | ||
|
|
1c30252764 | ||
|
|
2caeb3cd10 | ||
|
|
c8c2113e3c | ||
|
|
ded7fd0886 | ||
|
|
f625d421dd | ||
|
|
966592f1bc | ||
|
|
aaac7fa4ce | ||
|
|
6620bdf26b | ||
|
|
e4d461cd41 | ||
|
|
980c9219d4 | ||
|
|
d5ad2286f3 | ||
|
|
02cb24901d | ||
|
|
dca9fee375 | ||
|
|
00dcc99a84 | ||
|
|
83acd7e57c | ||
|
|
ef60fe921d | ||
|
|
be96744dd5 | ||
|
|
6eb3e9ecee | ||
|
|
7a25cb843f | ||
|
|
fa07552860 | ||
|
|
4172a4e5f1 | ||
|
|
3c3dbed015 | ||
|
|
863dc524e4 | ||
|
|
cad66b335d | ||
|
|
400a5e8a1e | ||
|
|
ad736afd73 | ||
|
|
edf6d6f310 | ||
|
|
6d037d1e0a | ||
|
|
70261b5d6d | ||
|
|
24224bbe16 | ||
|
|
c29bb785ae | ||
|
|
a1082d4460 | ||
|
|
63d518cfc2 | ||
|
|
b04c13800d | ||
|
|
ab0c0d20b7 | ||
|
|
ac38a667ab | ||
|
|
c74b507aef | ||
|
|
49f52642b0 | ||
|
|
051b659f43 | ||
|
|
221ebecccb | ||
|
|
209cb7867a | ||
|
|
f97342a8de | ||
|
|
c47ec74fde | ||
|
|
89a84ec573 | ||
|
|
fef0afab61 | ||
|
|
6658dfdb4f | ||
|
|
ac8f28fcb1 | ||
|
|
9b00f57d4e | ||
|
|
68c4059b3b | ||
|
|
2ebee013bb | ||
|
|
0eac11c9e5 | ||
|
|
c860332416 | ||
|
|
7fa8c6eddf | ||
|
|
ac65fcdf82 | ||
|
|
088a3e3947 | ||
|
|
8ab83d455f | ||
|
|
8b1e41ca57 | ||
|
|
052a3dc2e7 | ||
|
|
6b1096cffa | ||
|
|
be884f93fa | ||
|
|
19fc0e7d87 | ||
|
|
de65142029 | ||
|
|
ecaf740fe0 | ||
|
|
6d2c9a5f93 | ||
|
|
5c7f0bd0b8 | ||
|
|
3ba27f1196 | ||
|
|
ac20f11f71 | ||
|
|
f54050b492 | ||
|
|
025ea301e1 | ||
|
|
80e3332a5c | ||
|
|
5ab35027a7 | ||
|
|
97841b9820 | ||
|
|
a07432c3d7 | ||
|
|
d531308db7 | ||
|
|
d259a3f2c9 | ||
|
|
0bec2e4dfb | ||
|
|
9283515050 | ||
|
|
c431e76d4d | ||
|
|
a438614571 | ||
|
|
ad59e92a45 | ||
|
|
13c112c904 | ||
|
|
aa76055ad4 | ||
|
|
f3e1d7815e | ||
|
|
11a3a08120 | ||
|
|
b38bfd7315 | ||
|
|
6fec11c8a7 | ||
|
|
f6210664c6 | ||
|
|
7c8ab4b974 | ||
|
|
deb219fc05 | ||
|
|
26d3af5ccc | ||
|
|
de4a27144c | ||
|
|
032e5e201e | ||
|
|
16320c0063 | ||
|
|
2de88b7ebd | ||
|
|
1a015c834c | ||
|
|
1281fdd074 | ||
|
|
384dd26fff | ||
|
|
f758a46292 | ||
|
|
641b1f5b4d | ||
|
|
e4b728f952 | ||
|
|
ffeeaf8497 | ||
|
|
33db2e0e07 | ||
|
|
c105dab42e | ||
|
|
fc65ab06e9 | ||
|
|
b962fee6eb | ||
|
|
2f7864eab4 | ||
|
|
e63911cd07 | ||
|
|
f1f2bf8d1e | ||
|
|
a97d0e6fc5 | ||
|
|
a9080bf4c6 | ||
|
|
ab24d1b4f4 | ||
|
|
a379f700b4 | ||
|
|
51df78a7d0 | ||
|
|
42c19ccc14 | ||
|
|
819923978e | ||
|
|
9337096004 | ||
|
|
0fd4ea3e4e | ||
|
|
d40d58f744 | ||
|
|
f0928cf71b | ||
|
|
db80429033 | ||
|
|
641c9984ad | ||
|
|
95c8b8cd11 | ||
|
|
1ad9f7f98c | ||
|
|
5eed528c76 | ||
|
|
3cfd30b14a | ||
|
|
22cb25f03d | ||
|
|
3a0684c87c | ||
|
|
493dc087c0 | ||
|
|
1c6535c30b | ||
|
|
07fbdc6385 | ||
|
|
6c6ec2787c | ||
|
|
a95f0fc33e | ||
|
|
92bb6c846a | ||
|
|
151e78b28b | ||
|
|
5c0e561ad8 | ||
|
|
2299c6ba4d | ||
|
|
c0becc9749 | ||
|
|
24656251cb | ||
|
|
b25a5f210d | ||
|
|
b047429ce0 | ||
|
|
1ba08f0f39 | ||
|
|
9461b8af65 | ||
|
|
db4866b822 | ||
|
|
ee4a5ef3b5 | ||
|
|
2591925556 | ||
|
|
e8166aa806 | ||
|
|
fee6304d76 | ||
|
|
8397ac6e38 | ||
|
|
40607f6d09 | ||
|
|
2f58a588a5 | ||
|
|
420a0b3d8d | ||
|
|
415b9e9a65 | ||
|
|
61b805e5fe | ||
|
|
b1a758b650 | ||
|
|
01f0698861 | ||
|
|
b98b1d8317 | ||
|
|
f2c046306a | ||
|
|
d9ea5f6a74 | ||
|
|
1cb8f0d5a1 | ||
|
|
977111b064 | ||
|
|
c0ad45f3f4 | ||
|
|
0bf0fbd043 | ||
|
|
a06f6f937a | ||
|
|
dfe70328bc | ||
|
|
164b6c2d9c | ||
|
|
9e10378c99 | ||
|
|
5313d52a52 | ||
|
|
b86a461c57 | ||
|
|
c5b11d6f50 | ||
|
|
d045d66f3a | ||
|
|
27045ad2ab | ||
|
|
a66fe0de69 | ||
|
|
3ac57c8588 | ||
|
|
cbcd9bdba7 | ||
|
|
5f0a43688d | ||
|
|
b7866d7b0c | ||
|
|
43caae661b | ||
|
|
e4c1837540 | ||
|
|
2251a9553b | ||
|
|
f6a3e260c1 | ||
|
|
746e24afdb | ||
|
|
1ed5688d99 | ||
|
|
b868f2beeb | ||
|
|
d55d649f15 | ||
|
|
bacd85ddec | ||
|
|
fb8caabb9a | ||
|
|
2c5aa37e08 | ||
|
|
8814936910 | ||
|
|
d98baa8728 | ||
|
|
0fb44504fa | ||
|
|
4fc4a53bc4 | ||
|
|
6260e46b97 | ||
|
|
86b0fa2497 | ||
|
|
cc09ab7093 | ||
|
|
144c5ccd02 | ||
|
|
0f820a6371 | ||
|
|
11eb1d50c0 | ||
|
|
17aa538084 | ||
|
|
bee5d2e7b5 | ||
|
|
7faad47330 | ||
|
|
dae85be57a | ||
|
|
f81b6b8fd1 | ||
|
|
8d8b5acc57 | ||
|
|
1c9c1976de | ||
|
|
ec8dbdb5e2 | ||
|
|
57773ecf31 | ||
|
|
a9e5c8cb4b | ||
|
|
d5696e455c | ||
|
|
120bcc8b38 | ||
|
|
c3056443f3 | ||
|
|
22ca030dbc | ||
|
|
9dfa4935c8 | ||
|
|
68cddda069 | ||
|
|
bcc4f24d43 | ||
|
|
2205f5ce7f | ||
|
|
245d326794 | ||
|
|
d39d7a97a6 | ||
|
|
9fcf104e0c | ||
|
|
0bfd80683e | ||
|
|
4418d0427f | ||
|
|
ff409a1d37 | ||
|
|
24970104fa | ||
|
|
fa855f0871 | ||
|
|
f4e897e5dc | ||
|
|
c0f8b5c689 | ||
|
|
47f9cd3eb9 | ||
|
|
f8d4753010 | ||
|
|
86627ee1a6 | ||
|
|
40018ce71b | ||
|
|
23e49331fe | ||
|
|
73365dd578 | ||
|
|
9b35daf6cd | ||
|
|
d62e525a3c | ||
|
|
d1aacad4b7 | ||
|
|
65ddcc3c17 | ||
|
|
769a86c0c7 | ||
|
|
aa8f429fd6 | ||
|
|
29e98f6d59 | ||
|
|
cd9ea163ce | ||
|
|
f6325850ad | ||
|
|
8add9b4f9f | ||
|
|
e909c0bf23 | ||
|
|
6143101396 | ||
|
|
befae376a3 | ||
|
|
a5f39fe22c | ||
|
|
4ca3cefd10 | ||
|
|
ff99deef3c | ||
|
|
95471a450b | ||
|
|
7beaac0b1c | ||
|
|
604c5c1c32 | ||
|
|
715e221152 | ||
|
|
0a58c6b069 | ||
|
|
40f4476275 | ||
|
|
c0b338d8b0 | ||
|
|
05d1150a09 | ||
|
|
612c825f19 | ||
|
|
4ad7c32e2a | ||
|
|
65281b414c | ||
|
|
9b161e82e7 | ||
|
|
ae5ae68552 | ||
|
|
a82dbc62b9 | ||
|
|
7a6761bfbd | ||
|
|
ad70c54f2c | ||
|
|
20e48199a8 | ||
|
|
c7b8727a84 | ||
|
|
71dcf4252f | ||
|
|
d08f2fc9f5 | ||
|
|
b6646a5ad5 | ||
|
|
dbf42eff1c | ||
|
|
af5cbde4ff | ||
|
|
13ebbd0377 | ||
|
|
bf7a45e807 | ||
|
|
49dc8791bc | ||
|
|
e02cd73519 | ||
|
|
acf78456ca | ||
|
|
761260ab17 | ||
|
|
1b4b74240a | ||
|
|
bed6474037 | ||
|
|
dc80fb25df | ||
|
|
753ede9406 | ||
|
|
58ac9a7628 | ||
|
|
cde84d2b60 | ||
|
|
5e414985c6 | ||
|
|
5dbbd15ae1 | ||
|
|
20dd9f6182 | ||
|
|
7c8bda91db | ||
|
|
5c52e5e8eb | ||
|
|
9aa11aebbf | ||
|
|
f6277b127c | ||
|
|
9de54969af | ||
|
|
a0816fc4cf | ||
|
|
3c7c44fa7b | ||
|
|
9a0bc9610a | ||
|
|
dec8596d4f | ||
|
|
f3c070059f | ||
|
|
c0f4a63c04 | ||
|
|
5af564879b | ||
|
|
9cc59028f9 | ||
|
|
64d362c881 | ||
|
|
f5b2203582 | ||
|
|
2c73268add | ||
|
|
0e183e66ab | ||
|
|
57de3b4612 | ||
|
|
3da4284956 | ||
|
|
82c57fffda | ||
|
|
9a2d9cda7e | ||
|
|
6be397b10f | ||
|
|
aea4b348a4 | ||
|
|
5addc5acca | ||
|
|
6b2baa3ec1 | ||
|
|
f3344e281d | ||
|
|
fb17ad2475 | ||
|
|
1ce8fd6269 | ||
|
|
6cdf89e476 | ||
|
|
cdb1482e53 | ||
|
|
dbb65ca38b | ||
|
|
95782b39ff | ||
|
|
99a9a5b3da | ||
|
|
c126f29b2e | ||
|
|
bddd0baf6d | ||
|
|
6201552f28 | ||
|
|
def0814961 | ||
|
|
88a2ff7d23 | ||
|
|
935da20766 | ||
|
|
286920a27c | ||
|
|
6b7636900b | ||
|
|
ce530eb72b | ||
|
|
3b7215ca01 | ||
|
|
33016beee9 | ||
|
|
5cf7aa2a93 | ||
|
|
a266be0c84 | ||
|
|
77efda00ba | ||
|
|
8005ead5db | ||
|
|
d9a184051d | ||
|
|
32041dd26d | ||
|
|
8b5ee08ee1 | ||
|
|
ee585da1eb | ||
|
|
ab38e40e41 | ||
|
|
06c0c2c6b0 | ||
|
|
bdd9315546 | ||
|
|
18982da2e4 | ||
|
|
aff8cded2c | ||
|
|
ce5455f337 | ||
|
|
de9c6b2f78 | ||
|
|
f368fe48e6 | ||
|
|
2435771ba5 | ||
|
|
f291ffa84d | ||
|
|
83ecb2ffcc | ||
|
|
beff81da41 | ||
|
|
84319aa7e1 | ||
|
|
253a293b72 | ||
|
|
c8b6319d5f | ||
|
|
587037fee9 | ||
|
|
7157f9d082 | ||
|
|
54fcc484bc | ||
|
|
087101ef69 | ||
|
|
8ddfe80697 | ||
|
|
7473c15265 | ||
|
|
c00be11990 | ||
|
|
9a8a9ed46e | ||
|
|
350ca83642 | ||
|
|
8a7ccafd33 | ||
|
|
cd73105f53 | ||
|
|
092bb5b407 | ||
|
|
9f727e29d9 | ||
|
|
bf21d65c15 | ||
|
|
02c2ab4623 | ||
|
|
8314a7d2a2 | ||
|
|
7fd87659e2 | ||
|
|
0b22ced669 | ||
|
|
2b767aaebf | ||
|
|
fcbf173cff | ||
|
|
b8a3578ce0 | ||
|
|
03b520cd7f | ||
|
|
880f1255f8 | ||
|
|
9dccaa4e66 | ||
|
|
ee2ed40158 | ||
|
|
82709f3aa7 | ||
|
|
c032114859 | ||
|
|
d90276193e | ||
|
|
18746f198d | ||
|
|
2a86a21084 | ||
|
|
bfd5c416b4 | ||
|
|
361e7e086a | ||
|
|
c43910f686 | ||
|
|
d9a5238571 | ||
|
|
e1319ed1c4 | ||
|
|
40f5b1b7b5 | ||
|
|
ca9872284d | ||
|
|
3e25fa5352 | ||
|
|
739e1a411c | ||
|
|
e251624c0b | ||
|
|
0e37211a5d | ||
|
|
9898b8b5ba | ||
|
|
7d85e571fc | ||
|
|
66eb23a0f0 | ||
|
|
48d1833ba7 | ||
|
|
2f86b1ac39 | ||
|
|
2f0b297433 | ||
|
|
d6b864f086 | ||
|
|
c6be1dbee6 | ||
|
|
4bd1012cb8 | ||
|
|
d0521ba109 | ||
|
|
e106371bcb | ||
|
|
cd91d957bd | ||
|
|
d123c7d8c8 | ||
|
|
64b55d4a06 | ||
|
|
6b361ccb03 | ||
|
|
b5d1286480 | ||
|
|
453342b5c3 | ||
|
|
8be6e6f1cc | ||
|
|
587b2fc921 | ||
|
|
50b2997b6e | ||
|
|
349cae3b95 | ||
|
|
c5e30ea9e7 | ||
|
|
66495b7b78 | ||
|
|
401654988b | ||
|
|
824adaf58d | ||
|
|
048ec9e7a5 | ||
|
|
3875084426 | ||
|
|
9028b73711 | ||
|
|
ee7e422fd2 | ||
|
|
091c69f667 | ||
|
|
418455ab37 | ||
|
|
7940cdfe98 | ||
|
|
a6ac78ce5f | ||
|
|
b028d6ff72 | ||
|
|
c3f3af4965 | ||
|
|
4ba6a1fbab | ||
|
|
b469298864 | ||
|
|
90a6ef437b | ||
|
|
ccff3fe240 | ||
|
|
6e364c98f8 | ||
|
|
1de02028a8 | ||
|
|
dcb9221200 | ||
|
|
3edcbe4eb6 | ||
|
|
d03f413d93 | ||
|
|
1a0ae09122 | ||
|
|
941226ab9f | ||
|
|
2a73ad4e0a | ||
|
|
20fb703965 | ||
|
|
afc14eb0bd | ||
|
|
21a7e55b3d | ||
|
|
1b235edd62 | ||
|
|
79cf7e6252 | ||
|
|
5056fa2127 | ||
|
|
80f00cf5d9 | ||
|
|
0db0931928 | ||
|
|
cdbaf3875f | ||
|
|
9e81e2be6d | ||
|
|
7108273f0d | ||
|
|
68021670ad | ||
|
|
85f17ca304 | ||
|
|
828574545f | ||
|
|
cf1d718657 | ||
|
|
838a69a57f | ||
|
|
408431e416 | ||
|
|
78430308e2 | ||
|
|
503fbedf71 | ||
|
|
ab88ea73bd | ||
|
|
0a300c1a28 | ||
|
|
8c5d4a57e3 | ||
|
|
34a1b4ec49 | ||
|
|
f6225338f7 | ||
|
|
4de70c5a24 | ||
|
|
b53eb4b841 | ||
|
|
ecbdb91fa7 | ||
|
|
0877f8bb31 | ||
|
|
0e3f1cc640 | ||
|
|
c16e47a6f3 | ||
|
|
daba5978ca | ||
|
|
baf9c8b844 | ||
|
|
dc98c0111b | ||
|
|
ea53c48be4 | ||
|
|
f11d8e5a4e | ||
|
|
3516431e80 | ||
|
|
9bec69a92f | ||
|
|
38955c35a6 | ||
|
|
28529ec644 | ||
|
|
04c2634a42 | ||
|
|
ad977eb4d5 | ||
|
|
895e9bf767 | ||
|
|
d193acf6b8 | ||
|
|
33dda4ef77 | ||
|
|
1011af41bc | ||
|
|
3ab5ee3f58 | ||
|
|
d75b8599c6 | ||
|
|
1afbee8665 | ||
|
|
cbaf921fe6 | ||
|
|
acad6d1134 | ||
|
|
dca734a084 | ||
|
|
7c506235ee | ||
|
|
8884b0b7fa | ||
|
|
8f4f797854 | ||
|
|
715df571ad | ||
|
|
84ff134ecc | ||
|
|
ef088ee595 | ||
|
|
f4268fa249 | ||
|
|
f4e40fada9 | ||
|
|
ec92c03503 | ||
|
|
05a896dfad | ||
|
|
698fb92d3d | ||
|
|
aa5e185a27 | ||
|
|
92b899afac | ||
|
|
09f5eb8085 | ||
|
|
3c220d079f | ||
|
|
28b0c0396a | ||
|
|
9df3804f53 | ||
|
|
3885d307fd | ||
|
|
bf633a2230 | ||
|
|
63794daf6d | ||
|
|
db5b36db5b | ||
|
|
902375d0a5 | ||
|
|
5af4623d46 | ||
|
|
b374c57bb7 | ||
|
|
cc1a9fbd42 | ||
|
|
e8186e250d | ||
|
|
e388bc0b89 | ||
|
|
57ad31b43d | ||
|
|
95a5926441 | ||
|
|
8a09f92cd7 | ||
|
|
4ef6c1319d | ||
|
|
1d009e0a67 | ||
|
|
90203d9c5f | ||
|
|
29ffc00ef3 | ||
|
|
9baceceeda | ||
|
|
02dbc076b3 | ||
|
|
63a35cff4e | ||
|
|
99a914c08e | ||
|
|
1ead1d5346 | ||
|
|
f733029884 | ||
|
|
f2eb1bb0f0 | ||
|
|
dcec8d5b51 | ||
|
|
3f4997d44f | ||
|
|
101027c71a | ||
|
|
848df1d853 | ||
|
|
c52ce48cbc | ||
|
|
d9e712f0b5 | ||
|
|
27a1acab75 | ||
|
|
f8d579c792 | ||
|
|
4b06326764 | ||
|
|
85aaf28288 | ||
|
|
78ad9b91aa | ||
|
|
abe19e990b | ||
|
|
1e50eb9353 | ||
|
|
fd0834a9df | ||
|
|
d52e6e86bd | ||
|
|
6323d78295 | ||
|
|
2839478e71 | ||
|
|
4d19f46568 | ||
|
|
12acfe294c | ||
|
|
aec6687ff5 | ||
|
|
c8f06b5e5f | ||
|
|
3ecf871a5f | ||
|
|
59e5356cb7 | ||
|
|
5f24b02c9e | ||
|
|
3a907afe58 | ||
|
|
d3d1f37f0f | ||
|
|
c10ee157ee | ||
|
|
1ab0c4f1ad | ||
|
|
757d836a26 | ||
|
|
9bd94b5c80 | ||
|
|
e3c088a9cd | ||
|
|
d6e160e589 | ||
|
|
0b0b4bb436 | ||
|
|
3f3a96336c | ||
|
|
19b5badaa7 | ||
|
|
d423a0bd7d | ||
|
|
f1e7a084ee | ||
|
|
f74d431cf2 | ||
|
|
588c41832a | ||
|
|
4560601126 | ||
|
|
2eaf0bda32 | ||
|
|
cdce902077 | ||
|
|
5be0b74691 | ||
|
|
18eb8acd10 | ||
|
|
7ce981054c | ||
|
|
b16c2449d3 | ||
|
|
b620d7b0a5 | ||
|
|
878f09e09e | ||
|
|
efaeeb0749 | ||
|
|
f2a040b63d | ||
|
|
87ba58aa73 | ||
|
|
013b2edf08 | ||
|
|
434a2ad6c4 | ||
|
|
b46380a007 | ||
|
|
64c179a7eb | ||
|
|
a5dae7d827 | ||
|
|
42cad70dbd | ||
|
|
714436fce6 | ||
|
|
2f6a1e39fd | ||
|
|
cfa1500c55 | ||
|
|
d04a3eb4b1 | ||
|
|
13842766b5 | ||
|
|
37790f5d4a | ||
|
|
80a1309a76 | ||
|
|
4318dbee28 | ||
|
|
cefaec7697 | ||
|
|
edeeedd695 | ||
|
|
f7435f9f34 | ||
|
|
631222f3d2 | ||
|
|
539c78eb1f | ||
|
|
2ba883d885 | ||
|
|
81285e42f8 | ||
|
|
1a8fd910da | ||
|
|
1c5eb7bcbf | ||
|
|
fe46361a98 | ||
|
|
0839781833 | ||
|
|
b7ab3a1cca | ||
|
|
71a12072cc | ||
|
|
3c4a1cf9fb | ||
|
|
0a4cdce648 | ||
|
|
026b0cb602 | ||
|
|
eff8c00345 | ||
|
|
3864d6effc | ||
|
|
5fccc7dbbc | ||
|
|
673d525f32 | ||
|
|
7725f10b70 | ||
|
|
ea423ff43a | ||
|
|
4a75faec8d | ||
|
|
f56f661ba2 | ||
|
|
afe960491a | ||
|
|
f3ca16c181 | ||
|
|
82cf5629d9 | ||
|
|
a61fddd83f | ||
|
|
7fe76e57ee | ||
|
|
611567445a | ||
|
|
7396751787 | ||
|
|
560767fa54 | ||
|
|
706d026eac | ||
|
|
6119ebe449 | ||
|
|
f4d1974751 | ||
|
|
4c5c205edc | ||
|
|
a789783f4b | ||
|
|
f9191ecf26 | ||
|
|
9a2113e907 | ||
|
|
27be973b02 | ||
|
|
d682bc38f7 | ||
|
|
8f6440b7e2 | ||
|
|
1b15aae7b9 | ||
|
|
8f4a97fae6 | ||
|
|
f92532d791 | ||
|
|
f0f6420c98 | ||
|
|
9f7e199906 | ||
|
|
51ba30f54f | ||
|
|
b28a782647 | ||
|
|
686fc39e22 | ||
|
|
e0ce35d20a | ||
|
|
e3e153d1ac | ||
|
|
63d66f3cae | ||
|
|
c94780fd96 | ||
|
|
531b1cb19e | ||
|
|
95d7e5b3bf | ||
|
|
5ada7d1ecb | ||
|
|
5ed840d0a0 | ||
|
|
66c84693b7 | ||
|
|
a19923b682 | ||
|
|
f653ecf650 | ||
|
|
0646a091bf | ||
|
|
6234fc7383 | ||
|
|
d8f2f37041 | ||
|
|
8ada716f66 | ||
|
|
5f8ce6af30 | ||
|
|
fb6da2cb9a | ||
|
|
3b09eece17 | ||
|
|
f19980e8f9 | ||
|
|
3c85094ff8 | ||
|
|
ddbe67a8db | ||
|
|
9cbb2bd3b5 | ||
|
|
53f5d573ef | ||
|
|
e127953593 | ||
|
|
71892221f8 | ||
|
|
deca37a7db | ||
|
|
e1462e3d02 | ||
|
|
eee4107a9f | ||
|
|
605680535f | ||
|
|
6137ab3051 | ||
|
|
07e4420558 | ||
|
|
41bc68394c | ||
|
|
5796a7732b | ||
|
|
60abf4f4e9 | ||
|
|
bf123ccd18 | ||
|
|
cc5ece1958 | ||
|
|
379e120659 | ||
|
|
3c4ad7c5f8 | ||
|
|
0dbb961f84 | ||
|
|
584a37b2c4 | ||
|
|
1e811ed447 | ||
|
|
bd881f9e41 | ||
|
|
e68806eb66 | ||
|
|
963da14e55 | ||
|
|
25134276ce | ||
|
|
e313fb4846 | ||
|
|
9e7e3f3f4c | ||
|
|
b48c483ac3 | ||
|
|
0308569c22 | ||
|
|
d6b7906864 | ||
|
|
d6f0dc4c00 | ||
|
|
67ed61fc80 | ||
|
|
2f6778b514 | ||
|
|
03759a4b77 | ||
|
|
2ef8998a69 | ||
|
|
3acb583b3d | ||
|
|
c704ee9e2f | ||
|
|
e0d7fda618 | ||
|
|
89a95b7393 | ||
|
|
3a2f192e01 | ||
|
|
7db2b259c2 | ||
|
|
85e42b988e | ||
|
|
de0feb5e4f | ||
|
|
cb836a668b | ||
|
|
db2266b226 | ||
|
|
dabf832a10 | ||
|
|
3ecf9cafb0 | ||
|
|
176eced3ac | ||
|
|
960a3a419e | ||
|
|
f5b2581d56 | ||
|
|
6a7882c08b | ||
|
|
817a3f7665 | ||
|
|
5378787aeb | ||
|
|
b635256ff0 | ||
|
|
d0b78a929e | ||
|
|
2e0e2368e7 | ||
|
|
6299d0b349 | ||
|
|
db176ba5eb | ||
|
|
6542278b06 | ||
|
|
71f000d4db | ||
|
|
f2a08daffb | ||
|
|
6f862a10f9 | ||
|
|
b07f117109 | ||
|
|
46b858d37b | ||
|
|
83f5574fd6 | ||
|
|
afc212e56e | ||
|
|
176eee5afd | ||
|
|
a6b11c912a | ||
|
|
18db9633ce | ||
|
|
17b563198f | ||
|
|
0783d9666d | ||
|
|
c987183200 | ||
|
|
9f0fc1ebb5 | ||
|
|
eb59c2d76c | ||
|
|
956adf84f7 | ||
|
|
3ff61e49fc | ||
|
|
b8ae69b432 | ||
|
|
f6c7a19da1 | ||
|
|
0443e13269 | ||
|
|
e3325762fa | ||
|
|
04009db72c | ||
|
|
94ba53c50f | ||
|
|
c5ef3951e3 | ||
|
|
93c7786311 | ||
|
|
e405a3997b | ||
|
|
953f4f8823 | ||
|
|
bcc85aecdb | ||
|
|
e0e5bf2ee2 | ||
|
|
91cd32361e | ||
|
|
550e521703 | ||
|
|
ce2752038f | ||
|
|
4485569908 | ||
|
|
ea7a6edd6f | ||
|
|
ac0c6b80c6 | ||
|
|
b1f1b59326 | ||
|
|
6d6c749f8d | ||
|
|
b94bde5576 | ||
|
|
9063c50e3c | ||
|
|
617dfcb8e3 | ||
|
|
ae46fe9a84 | ||
|
|
d19bf94aa0 | ||
|
|
844b13761a | ||
|
|
f0b5d641b6 | ||
|
|
cae3cfec19 | ||
|
|
46377ba223 | ||
|
|
16b4e1823e | ||
|
|
ea40f9b401 | ||
|
|
44aa44cc7b | ||
|
|
07d84f5d67 | ||
|
|
a86da508c6 | ||
|
|
ac3bc6a1f7 | ||
|
|
81dfdd2957 | ||
|
|
f9cfd54e88 | ||
|
|
a4ebb68e00 | ||
|
|
1712e27deb | ||
|
|
b507064f0f | ||
|
|
6c3cdfccbe | ||
|
|
5e7b033e95 | ||
|
|
1e1033f739 | ||
|
|
5497a89840 | ||
|
|
fb6ec1eeaf | ||
|
|
b997f92217 | ||
|
|
f3a9723412 | ||
|
|
2fa7c67818 | ||
|
|
aca3c77ad9 | ||
|
|
6a2b44af70 | ||
|
|
dc57e9167d | ||
|
|
6d11366974 | ||
|
|
9e799da1b4 | ||
|
|
f74293f285 | ||
|
|
a0339f97e6 | ||
|
|
344a62654c | ||
|
|
5cf7ce9d62 | ||
|
|
42674c2c40 | ||
|
|
23eae2a648 | ||
|
|
1e1059cd79 | ||
|
|
8219212310 | ||
|
|
531af988dc | ||
|
|
64f1f337d5 | ||
|
|
8a909ee469 | ||
|
|
c6b8b6cd96 | ||
|
|
3df86e1554 | ||
|
|
a0b4ade529 | ||
|
|
7d5dee5d9a | ||
|
|
7814d39c26 | ||
|
|
e1bc682233 | ||
|
|
e13147bc1e | ||
|
|
30102630bd | ||
|
|
f2fdcb9814 | ||
|
|
962e7ea829 | ||
|
|
a149035d27 | ||
|
|
f4fc235fd5 | ||
|
|
52732a6e56 | ||
|
|
afd69f8c8e | ||
|
|
87ce512bcb | ||
|
|
31c0bcfbcd | ||
|
|
7da03d9a3d | ||
|
|
1c16db5142 | ||
|
|
52197058ab | ||
|
|
eb581ddd9f | ||
|
|
223c669b8a | ||
|
|
fd92a19983 | ||
|
|
b60e3d3f27 | ||
|
|
6595261618 | ||
|
|
9d18957071 | ||
|
|
156ce289d8 | ||
|
|
2c3a9163bd | ||
|
|
a383227338 | ||
|
|
81dad76461 | ||
|
|
5079c29bcb | ||
|
|
948728de8a | ||
|
|
f4817756ec | ||
|
|
9a51ec09f9 | ||
|
|
3b1eda8b73 | ||
|
|
7e26623838 | ||
|
|
6286234571 | ||
|
|
fa867d0347 | ||
|
|
01e835a317 | ||
|
|
73649c0f5a | ||
|
|
921f1066b5 | ||
|
|
38cc676149 | ||
|
|
6185f04dbc | ||
|
|
84218bf5f3 | ||
|
|
240ddde9b5 | ||
|
|
01c9ec7e54 | ||
|
|
d3dae607d4 | ||
|
|
40cc5ec7fe | ||
|
|
8e84857ecb | ||
|
|
3e7b96017c | ||
|
|
fa3b04f78e | ||
|
|
3774f9e1b2 | ||
|
|
73ed0de28f | ||
|
|
ca20d18b6c | ||
|
|
086e3b2566 | ||
|
|
604b549a77 | ||
|
|
db97171f10 | ||
|
|
1256484959 | ||
|
|
b64ff60062 | ||
|
|
50de783f34 | ||
|
|
347c9c9d34 | ||
|
|
f64686c47c | ||
|
|
5ba1ba9bbc | ||
|
|
00c2121796 | ||
|
|
bb53c4247c | ||
|
|
a41e9ab189 | ||
|
|
a3519a8ad7 | ||
|
|
0861cd3c3d | ||
|
|
2437562612 | ||
|
|
d1ad218320 | ||
|
|
41bb007818 | ||
|
|
f662f8c675 | ||
|
|
dbe96d79b3 | ||
|
|
ec61b6efa2 | ||
|
|
9e5523f58e | ||
|
|
bc2a15ae96 | ||
|
|
6eb03c2bc6 | ||
|
|
b3217e779e | ||
|
|
1fa9b18510 | ||
|
|
3d2e0b8f1f | ||
|
|
c7f42395e9 | ||
|
|
272a11d847 | ||
|
|
305b3f5d12 | ||
|
|
81d9cee407 | ||
|
|
eab0644c96 | ||
|
|
41d5ab2111 | ||
|
|
4c6cea68f7 | ||
|
|
b52033d0cc | ||
|
|
85adb19632 | ||
|
|
6f38d84ca7 | ||
|
|
ec1fc4dddf | ||
|
|
7150d111dd | ||
|
|
5249fa07d7 | ||
|
|
cdb8ab9d86 | ||
|
|
e83c0c919b | ||
|
|
54e13745a7 | ||
|
|
7feabe77ff | ||
|
|
a220425353 | ||
|
|
5cb0d95c55 | ||
|
|
ee92d2a415 | ||
|
|
90b2c8a05f |
+2
-2
@@ -5,11 +5,11 @@ source =
|
||||
branch = False
|
||||
omit =
|
||||
cms/migrations/*
|
||||
cms/migrations_django/*
|
||||
cms/south_migrations/*
|
||||
cms/tests/*
|
||||
cms/test_utils/*
|
||||
menus/migrations/*
|
||||
menus/migrations_django/*
|
||||
menus/south_migrations/*
|
||||
docs/*
|
||||
env/*
|
||||
env*
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{scss,js,html}]
|
||||
indent_style = tab
|
||||
|
||||
[*.rst]
|
||||
max_line_length = 80
|
||||
+2
-2
@@ -38,5 +38,5 @@ include/
|
||||
lib/
|
||||
man/
|
||||
share/
|
||||
.editorconfig
|
||||
cms/static/cms/compass_app_log.txt
|
||||
!.editorconfig
|
||||
cms/static/cms/compass_app_log.txt
|
||||
|
||||
+125
-41
@@ -1,6 +1,7 @@
|
||||
language: python
|
||||
|
||||
python:
|
||||
- 3.5
|
||||
- 3.4
|
||||
- 3.3
|
||||
- 2.7
|
||||
@@ -10,25 +11,23 @@ sudo: false
|
||||
|
||||
env:
|
||||
global:
|
||||
- SAUCE_USERNAME=digi604
|
||||
- secure: NWiSBCHFB6LbTMget2qLIdqZlx0zeu3j+Y7Lsqb8kuYXyT2IUBGFVedcGWuGv/9Mzypb80EQWtVTokA3/3QIbesqr29uG95pMPHiYWLdnTO6UHcLMcNXiSzhBGdRDZ40iHSVv2dDHs4GNwGOH5+UCA0z3j7SWmChuFbNXh+Vsqw=
|
||||
matrix:
|
||||
- DJANGO=1.4 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- DJANGO=1.4 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- DJANGO=1.4 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1
|
||||
- DJANGO=1.5 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- DJANGO=1.5 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- DJANGO=1.5 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- DJANGO=1.5 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='emailuserapp.EmailUser'
|
||||
- DJANGO=1.6 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- DJANGO=1.6 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- DJANGO=1.6 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- DJANGO=1.6 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='emailuserapp.EmailUser'
|
||||
- DJANGO=1.6 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='customuserapp.User'
|
||||
- DJANGO=1.7 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- DJANGO=1.7 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- DJANGO=1.7 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- DJANGO=1.7 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='emailuserapp.EmailUser'
|
||||
- DJANGO=1.6 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='customuserapp.User'
|
||||
- DJANGO=1.8 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- DJANGO=1.8 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- DJANGO=1.8 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- DJANGO=1.8 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='emailuserapp.EmailUser'
|
||||
- DJANGO=1.8 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='customuserapp.User'
|
||||
- DJANGO=1.9 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='customuserapp.User'
|
||||
|
||||
before_script:
|
||||
- sh -c "if [ '$DATABASE_URL' = 'postgres://postgres@127.0.0.1/djangocms_test' ];
|
||||
@@ -39,30 +38,41 @@ before_script:
|
||||
-e 'create database IF NOT EXISTS djangocms_test CHARACTER SET utf8 COLLATE utf8_general_ci;';
|
||||
fi"
|
||||
|
||||
before_install:
|
||||
- "export DISPLAY=:99.0"
|
||||
- "sh -e /etc/init.d/xvfb start"
|
||||
|
||||
install:
|
||||
- pip install -q -r "test_requirements/django-$DJANGO.txt"
|
||||
- if [ $DATABASE_URL == 'postgres://postgres@127.0.0.1/djangocms_test' ]; then pip
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install 'xvfbwrapper<0.2.7'; else pip install xvfbwrapper; fi
|
||||
- if [[ $DATABASE_URL == 'postgres://postgres@127.0.0.1/djangocms_test' ]]; then pip
|
||||
install -q psycopg2 ; fi
|
||||
- if [ $DATABASE_URL == 'mysql://root@127.0.0.1/djangocms_test' ]; then pip install
|
||||
-q mysql-python ; fi
|
||||
- if [[ $DATABASE_URL == 'mysql://root@127.0.0.1/djangocms_test' ]]; then pip install
|
||||
-q mysqlclient ; fi
|
||||
|
||||
script: coverage run --rcfile=.coverage.rc develop.py test --migrate
|
||||
script: coverage run --rcfile=.coverage.rc develop.py test --migrate --xvfb
|
||||
|
||||
after_success: coveralls --config_file=.coverage.rc --coveralls_yaml=.coveralls.yml
|
||||
after_success: coveralls --config_file=.coverage.rc
|
||||
|
||||
notifications:
|
||||
email:
|
||||
recipients:
|
||||
- digi@treepy.com
|
||||
on_success: never
|
||||
on_failure: always
|
||||
irc:
|
||||
- irc.freenode.org#django-cms
|
||||
- irc.freenode.org#django-cms-sprint
|
||||
webhooks:
|
||||
- http://addons.us-iad-rs.aldryn.io/en/travis-endpoint/
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
|
||||
- python: 2.6
|
||||
env: DJANGO=1.6 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- python: 2.6
|
||||
env: DJANGO=1.6 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 2.6
|
||||
env: DJANGO=1.6 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 2.6
|
||||
env: DJANGO=1.6 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='emailuserapp.EmailUser'
|
||||
|
||||
- python: 2.6
|
||||
env: DJANGO=1.7 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- python: 2.6
|
||||
@@ -72,34 +82,108 @@ matrix:
|
||||
- python: 2.6
|
||||
env: DJANGO=1.7 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='emailuserapp.EmailUser'
|
||||
|
||||
- python: 3.3
|
||||
env: DJANGO=1.4 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- python: 3.3
|
||||
env: DJANGO=1.4 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.3
|
||||
env: DJANGO=1.5 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.3
|
||||
env: DJANGO=1.6 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.3
|
||||
env: DJANGO=1.7 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.3
|
||||
env: DJANGO=1.4 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1
|
||||
- python: 2.6
|
||||
env: DJANGO=1.8 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- python: 2.6
|
||||
env: DJANGO=1.8 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 2.6
|
||||
env: DJANGO=1.8 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 2.6
|
||||
env: DJANGO=1.8 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='emailuserapp.EmailUser'
|
||||
- python: 2.6
|
||||
env: DJANGO=1.8 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='customuserapp.User'
|
||||
|
||||
- python: 3.4
|
||||
env: DJANGO=1.4 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- python: 3.4
|
||||
env: DJANGO=1.4 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.4
|
||||
env: DJANGO=1.5 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.4
|
||||
- python: 2.7
|
||||
env: DJANGO=1.6 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- python: 2.7
|
||||
env: DJANGO=1.6 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 2.7
|
||||
env: DJANGO=1.6 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='emailuserapp.EmailUser'
|
||||
|
||||
- python: 2.7
|
||||
env: DJANGO=1.7 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- python: 2.7
|
||||
env: DJANGO=1.7 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
|
||||
- python: 3.3
|
||||
env: DJANGO=1.6 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- python: 3.3
|
||||
env: DJANGO=1.6 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.4
|
||||
- python: 3.3
|
||||
env: DJANGO=1.6 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.3
|
||||
env: DJANGO=1.6 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='emailuserapp.EmailUser'
|
||||
|
||||
- python: 3.3
|
||||
env: DJANGO=1.7 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- python: 3.3
|
||||
env: DJANGO=1.7 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.4
|
||||
env: DJANGO=1.4 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1
|
||||
- python: 3.3
|
||||
env: DJANGO=1.7 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
|
||||
- python: 3.3
|
||||
env: DJANGO=1.8 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- python: 3.3
|
||||
env: DJANGO=1.8 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.3
|
||||
env: DJANGO=1.8 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.3
|
||||
env: DJANGO=1.8 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='emailuserapp.EmailUser'
|
||||
|
||||
- python: 3.5
|
||||
env: DJANGO=1.6 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- python: 3.5
|
||||
env: DJANGO=1.6 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.5
|
||||
env: DJANGO=1.6 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.5
|
||||
env: DJANGO=1.6 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='emailuserapp.EmailUser'
|
||||
- python: 3.5
|
||||
env: DJANGO=1.6 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='customuserapp.User'
|
||||
|
||||
- python: 3.5
|
||||
env: DJANGO=1.7 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
|
||||
- python: 3.5
|
||||
env: DJANGO=1.7 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.5
|
||||
env: DJANGO=1.7 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.5
|
||||
env: DJANGO=1.7 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='emailuserapp.EmailUser'
|
||||
|
||||
- python: 2.6
|
||||
env: DJANGO=1.9 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='customuserapp.User'
|
||||
- python: 2.7
|
||||
env: DJANGO=1.9 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='customuserapp.User'
|
||||
- python: 3.3
|
||||
env: DJANGO=1.9 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='customuserapp.User'
|
||||
- python: 3.5
|
||||
env: DJANGO=1.9 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='customuserapp.User'
|
||||
|
||||
allow_failures:
|
||||
- python: 2.7
|
||||
env: DJANGO=1.7 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 2.7
|
||||
env: DJANGO=1.8 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
|
||||
- python: 3.3
|
||||
env: DJANGO=1.6 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.3
|
||||
env: DJANGO=1.7 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.3
|
||||
env: DJANGO=1.8 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
|
||||
- python: 3.4
|
||||
env: DJANGO=1.6 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.4
|
||||
env: DJANGO=1.7 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
- python: 3.4
|
||||
env: DJANGO=1.8 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
|
||||
- python: 3.5
|
||||
env: DJANGO=1.8 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
|
||||
|
||||
- python: 3.4
|
||||
env: DJANGO=1.9 DATABASE_URL='postgres://postgres@127.0.0.1/djangocms_test' SELENIUM=1 AUTH_USER_MODEL='customuserapp.User'
|
||||
|
||||
fast_finish: true
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Current and previous core designers:
|
||||
* Christian Bertschy
|
||||
|
||||
|
||||
Contributors (based on gitlog) (450):
|
||||
Contributors (based on gitlog) (456 authors):
|
||||
|
||||
* A. Bram Neijt
|
||||
* aaloy
|
||||
@@ -30,7 +30,6 @@ Contributors (based on gitlog) (450):
|
||||
* aball
|
||||
* Adi Sieker
|
||||
* Admin Adminaras
|
||||
* Adrián Ribao
|
||||
* Adrián Ribao Martínez
|
||||
* Alberto Paro
|
||||
* Ales Kocjancic
|
||||
@@ -41,7 +40,6 @@ Contributors (based on gitlog) (450):
|
||||
* Alex Marandon
|
||||
* Alexandre Leray
|
||||
* Alexey Subbotin
|
||||
* Aleš Kocjančič
|
||||
* Alvin Mites
|
||||
* Anatoly Ivanov
|
||||
* Andi Albrecht
|
||||
@@ -49,13 +47,12 @@ Contributors (based on gitlog) (450):
|
||||
* Andrea Stagi
|
||||
* Andrei Avram
|
||||
* Andrei Fokau
|
||||
* Andrew Cassidy
|
||||
* Andrew Schoen
|
||||
* Angelo Dini
|
||||
* angular_circle
|
||||
* Antoine Catton
|
||||
* Anton Parkhomenko
|
||||
* Antoni Aloy López
|
||||
* AQNOUCH Mohammed
|
||||
* Arcady Usov
|
||||
* Arne Gellhaus
|
||||
* Arne Schauf
|
||||
@@ -63,13 +60,12 @@ Contributors (based on gitlog) (450):
|
||||
* Arthur Debert
|
||||
* Artis Avotins
|
||||
* Aymeric Augustin
|
||||
* Bashar Al-Abdulhadi
|
||||
* Batiste Bieler
|
||||
* Basile LEGAL
|
||||
* behrooz
|
||||
* Behrooz Nobakht
|
||||
* Belegnar
|
||||
* Ben Northway
|
||||
* Ben Stähli
|
||||
* Beni Wohlwend
|
||||
* Benjamin
|
||||
* Benjamin Liles
|
||||
* Benjamin Wohlwend
|
||||
@@ -83,13 +79,14 @@ Contributors (based on gitlog) (450):
|
||||
* Bjorn Sandberg
|
||||
* Bob Karreman
|
||||
* Bouke Haarsma
|
||||
* Brad Walker
|
||||
* Braden MacDonald
|
||||
* Brian Schott
|
||||
* brightwhitefox
|
||||
* Calvin Cheng
|
||||
* Carlo Ascani
|
||||
* Carlos de Paula
|
||||
* casio
|
||||
* Catalin Dragomirescu
|
||||
* centralniak
|
||||
* Chanita Siridechkun
|
||||
* Charlie Denton
|
||||
@@ -102,14 +99,14 @@ Contributors (based on gitlog) (450):
|
||||
* Chris Jones
|
||||
* Chris Wesseling
|
||||
* Chris Wilson
|
||||
* Christian Bertschy
|
||||
* Christian Strappazzon
|
||||
* Christof Hagedorn
|
||||
* Christopher Grebs
|
||||
* Christos Kopanos
|
||||
* cip
|
||||
* Claudio Bartolini
|
||||
* Colin Wirz
|
||||
* Conrado Buhrer
|
||||
* Conrado Bührer
|
||||
* Corey Farwell
|
||||
* Craig de Stigter
|
||||
* Craig Ricciuto
|
||||
@@ -132,6 +129,7 @@ Contributors (based on gitlog) (450):
|
||||
* David Lam
|
||||
* David Lee
|
||||
* David Thompson
|
||||
* Davide Setti
|
||||
* derek73
|
||||
* deshack
|
||||
* digi604
|
||||
@@ -142,13 +140,14 @@ Contributors (based on gitlog) (450):
|
||||
* Donatas
|
||||
* Donatas Kučinskas
|
||||
* Douwe van der Meij
|
||||
* Dries Desmet
|
||||
* dstufft
|
||||
* Dylann CORDEL
|
||||
* DylannCordel
|
||||
* DZ
|
||||
* eged
|
||||
* Egor V. Nazarkin
|
||||
* Ekrem Seren
|
||||
* Ekrem SEREN
|
||||
* Elias Probst
|
||||
* Eric Amador
|
||||
* Eric Eldredge
|
||||
@@ -156,14 +155,13 @@ Contributors (based on gitlog) (450):
|
||||
* ericr
|
||||
* Erik Allik
|
||||
* Erlend Dalen
|
||||
* Eugen MechanisM
|
||||
* Eugene MechanisM
|
||||
* Evandro Miquelito
|
||||
* Evgeny Demchenko
|
||||
* evildmp
|
||||
* F. Gabriel Gosselin
|
||||
* f4nt
|
||||
* fcurella
|
||||
* febsn
|
||||
* Filip Kazimierczak
|
||||
* FinalAngel
|
||||
* fivethreeo
|
||||
@@ -179,28 +177,25 @@ Contributors (based on gitlog) (450):
|
||||
* gentleShark
|
||||
* Geoffrey Hing
|
||||
* George Marshall
|
||||
* Gerard Świdersk
|
||||
* Gerard Świderski
|
||||
* Germano Gabbianelli
|
||||
* Germano Gabbianellii
|
||||
* Gilles Lenfant
|
||||
* Gleb Chipiga
|
||||
* Gregory Klupar
|
||||
* guandalino
|
||||
* Hamish Downer
|
||||
* Hans Andersen
|
||||
* hedberg
|
||||
* henning
|
||||
* Henning Spran
|
||||
* Henning Sprang
|
||||
* Herbert Poul
|
||||
* hjkelly
|
||||
* homebrew79
|
||||
* Horst Gutmann
|
||||
* hysia
|
||||
* Iacopo Spalletti
|
||||
* Ian Lewis
|
||||
* ikudryavtsev
|
||||
* indexofire
|
||||
* Ionel Cristian Maries
|
||||
* Ionel Maries Cristian
|
||||
* Ivan Vershigora
|
||||
* Iván Raskovsky
|
||||
@@ -212,19 +207,21 @@ Contributors (based on gitlog) (450):
|
||||
* Jameel Al-Aziz
|
||||
* James Richards
|
||||
* James Rutherford
|
||||
* Jana Deutschlaender
|
||||
* Jannis Leidel
|
||||
* Janusz Harkot
|
||||
* Jared Proffitt
|
||||
* Jason Brown
|
||||
* Jason Davies
|
||||
* Jason Jenkins
|
||||
* Jason Robinson
|
||||
* Jason Zylks
|
||||
* Javi Nievas
|
||||
* Jeffrey Goettsch
|
||||
* JensDiemer
|
||||
* Jeroen Noten
|
||||
* Jessica Tallon
|
||||
* Jimmy Lam
|
||||
* Johannes Bornhold
|
||||
* John Bazik
|
||||
* John-Scott Atlakson
|
||||
* Jon Prindiville
|
||||
* Jonas Obrist
|
||||
@@ -232,32 +229,33 @@ Contributors (based on gitlog) (450):
|
||||
* Jonathan Stoppani
|
||||
* jordanjambazov
|
||||
* Jorge Vargas
|
||||
* Jorge Vargas (elpargo)
|
||||
* Jos van Velzen
|
||||
* Joseph Bergantine
|
||||
* Joseph Melettukunnel
|
||||
* Josh Kalderimis
|
||||
* Josh Schneier
|
||||
* João Luiz Lorencetti
|
||||
* Julien Poissonnier
|
||||
* Kalle Bronsen
|
||||
* kar1m
|
||||
* kater169
|
||||
* kblomqvis
|
||||
* kblomqvist
|
||||
* Kegan Holtzhausen
|
||||
* Keryn Knight
|
||||
* Kevin Richardson
|
||||
* Kim Blomqvist
|
||||
* Kim Thoenen
|
||||
* kochin
|
||||
* Kristian Øllegaard
|
||||
* Krzysztof Bandurski
|
||||
* kunitoki
|
||||
* Lars Smit
|
||||
* Lars van de Kerkhof
|
||||
* limpbrains :P
|
||||
* Lio Mendonca
|
||||
* Lionardo Mendonça
|
||||
* littlepea
|
||||
* lovmat
|
||||
* Lucas Vogelsang
|
||||
* Lucio Asnaghi
|
||||
* lug
|
||||
* Luis Diego Garcia
|
||||
* Luke Crooks
|
||||
* Luke Plant
|
||||
* m000
|
||||
@@ -267,19 +265,21 @@ Contributors (based on gitlog) (450):
|
||||
* Manuel Schmidt
|
||||
* Marc-Olivier Titeux
|
||||
* Marco Bonetti
|
||||
* marco federighi
|
||||
* Marco Paolini
|
||||
* Marco Rimoldi
|
||||
* marcor
|
||||
* Marijn Goedegebure
|
||||
* Mark Rogers
|
||||
* Markus Holtermann
|
||||
* Markus Zapke-Gründemann
|
||||
* Markus Zapke-Gründemann
|
||||
* Marti Raudsepp
|
||||
* Martin
|
||||
* Martin Bommeli
|
||||
* Martin Bambas
|
||||
* Martin Brochhaus
|
||||
* Martin Koistinen
|
||||
* Martin Kosír
|
||||
* Martin Owens
|
||||
* Martin Pajuste
|
||||
* martinkosir
|
||||
* Matas Dailyda
|
||||
@@ -287,6 +287,8 @@ Contributors (based on gitlog) (450):
|
||||
* Mateusz Marzantowicz
|
||||
* mathijs
|
||||
* Matt Chisholm
|
||||
* Matteo Rosati
|
||||
* Mattia Larentis
|
||||
* Max Shkurygin
|
||||
* Maxim Bodyansky
|
||||
* Maxime Haineault
|
||||
@@ -300,6 +302,7 @@ Contributors (based on gitlog) (450):
|
||||
* Mike Dory
|
||||
* Mike Johnson
|
||||
* mikek
|
||||
* Mikko Ahlroth
|
||||
* Milo Price
|
||||
* Mitar
|
||||
* Mokys
|
||||
@@ -312,7 +315,6 @@ Contributors (based on gitlog) (450):
|
||||
* Nelson Brochado
|
||||
* neoprolog
|
||||
* Nick Jones
|
||||
* Nickolay V. Shmyrev
|
||||
* nikolas
|
||||
* nsh
|
||||
* o-zander
|
||||
@@ -324,7 +326,7 @@ Contributors (based on gitlog) (450):
|
||||
* Oyvind Saltvik
|
||||
* padelt
|
||||
* Pankrat
|
||||
* Paolo
|
||||
* Paolo Leggio
|
||||
* Pascal Mouret
|
||||
* pascal.beyeler
|
||||
* Patrick Arminio
|
||||
@@ -341,19 +343,20 @@ Contributors (based on gitlog) (450):
|
||||
* pcicman
|
||||
* Pedro Gracia
|
||||
* Pete Loggie
|
||||
* Peter Ciciman
|
||||
* Peter Farrell
|
||||
* Peter J. Farrell
|
||||
* Peter Landry
|
||||
* Peter-Paul van Gemerden
|
||||
* peterfarrell
|
||||
* Petteri
|
||||
* pgcd
|
||||
* Philipp Bosch
|
||||
* philomat
|
||||
* phuihock
|
||||
* Pigletto
|
||||
* Piotr Jakimiak
|
||||
* Piotr Kilczuk
|
||||
* piquadrat
|
||||
* poweredbypenguins
|
||||
* Pratik Vyas
|
||||
* pumalo
|
||||
* Rafal Radulski
|
||||
* raidsan
|
||||
@@ -373,7 +376,7 @@ Contributors (based on gitlog) (450):
|
||||
* Robert Feldbinder
|
||||
* Robert Pogorzelski
|
||||
* Roberto
|
||||
* Roberto Lopez
|
||||
* Roberto Bampi
|
||||
* Robin Lewis
|
||||
* robint
|
||||
* Rodolfo Carvalho
|
||||
@@ -381,11 +384,12 @@ Contributors (based on gitlog) (450):
|
||||
* root
|
||||
* rtpm
|
||||
* Russ Ferriday
|
||||
* Sam Manzi
|
||||
* Samuel Lahti
|
||||
* Samuel Luescher
|
||||
* Samuel Lüscher
|
||||
* sbussetti
|
||||
* Schoen
|
||||
* Scott Barnham
|
||||
* scottbarnham
|
||||
* sealibora
|
||||
* Sean Bleier
|
||||
@@ -407,18 +411,18 @@ Contributors (based on gitlog) (450):
|
||||
* ssteinerX
|
||||
* Stavros Korokithakis
|
||||
* Stefan Foulis
|
||||
* Stefan T. Oertel
|
||||
* Stefan Wehrmeyer
|
||||
* stefanfoulis
|
||||
* Stefano Brentegani
|
||||
* Stefano Crosta
|
||||
* Stefano Morandi
|
||||
* Steffen Jasper
|
||||
* Stephan
|
||||
* Stephan Hepper
|
||||
* Stephan Herzog
|
||||
* Stephan Jaekel
|
||||
* Stephen Muss
|
||||
* Stephen Watkin
|
||||
* Steve R. Jones
|
||||
* Steve Steiner
|
||||
* Steven Laroche
|
||||
* Sylvain Fankhauser
|
||||
@@ -428,7 +432,6 @@ Contributors (based on gitlog) (450):
|
||||
* Thomas Parslow
|
||||
* Thomas Remmert
|
||||
* Thomas Woolford
|
||||
* Tibor Balogh
|
||||
* Tim Anderegg
|
||||
* Tim Davies
|
||||
* Tim Graham
|
||||
@@ -437,19 +440,20 @@ Contributors (based on gitlog) (450):
|
||||
* tiret
|
||||
* Tobias von Klipstein
|
||||
* Tom
|
||||
* Tom Berger
|
||||
* Tom de Simone
|
||||
* Tom S
|
||||
* Tom V
|
||||
* Tom Wardill
|
||||
* Ulrich Petri
|
||||
* unknown
|
||||
* User
|
||||
* Vasil Vangelovski
|
||||
* Vadim Sikora
|
||||
* Venelin Stoykov
|
||||
* Viktor Nagy
|
||||
* Viliam Segeda
|
||||
* Vinit (Vermicelli on IRC)
|
||||
* vinit kumar
|
||||
* Vitor Figueiro
|
||||
* Vladimir Bolshakov
|
||||
* Vladyslav
|
||||
* vvangelovski
|
||||
@@ -459,6 +463,7 @@ Contributors (based on gitlog) (450):
|
||||
* wildermesser
|
||||
* Xavier Fernandez
|
||||
* Xavier Ordoquy
|
||||
* xie wei
|
||||
* xray7224
|
||||
* Yann Malet
|
||||
* yann.malet@gmail.com
|
||||
@@ -471,4 +476,5 @@ Contributors (based on gitlog) (450):
|
||||
* zundoya
|
||||
* Óscar M. Lage
|
||||
* Øyvind Saltvik
|
||||
* Антон Евжаков
|
||||
* Žan Anderle
|
||||
* Антон Евжаков
|
||||
+157
-2
@@ -292,7 +292,7 @@ Please see Install/2.4 release notes *before* attempting to upgrade to version 2
|
||||
- Added list of retired core developers
|
||||
- Added branch policy documentaion
|
||||
|
||||
=== 3.0.8 (2015-01-11) ===
|
||||
==== 3.0.8 (2015-01-11) ===
|
||||
- Add require_parent option to CMS_PLACEHOLDER_CONF
|
||||
- Fix django-mptt version depenency to be PEP440 compatible
|
||||
- Fix some Django 1.4 compatibility issues
|
||||
@@ -306,7 +306,7 @@ Please see Install/2.4 release notes *before* attempting to upgrade to version 2
|
||||
- Documentation fixes
|
||||
- Minor code cleanups
|
||||
|
||||
=== 3.0.9 (2015-01-11) ===
|
||||
==== 3.0.9 (2015-01-11) ===
|
||||
- Revert a change that caused a regression in toolbar login
|
||||
- Fix an error in a translated phrase
|
||||
- Fix error when moving items in the page tree
|
||||
@@ -381,3 +381,158 @@ Please see Install/2.4 release notes *before* attempting to upgrade to version 2
|
||||
- Pin South version to 1.0.2
|
||||
- Pin Html5lib version to 0.999 until a current bug is fixed
|
||||
- Fix language chooser template
|
||||
|
||||
=== 3.0.15 (2015-09-01) ===
|
||||
|
||||
- Relax html5lib versions
|
||||
- Fix redirect when deleting a page
|
||||
- Correct South migration error
|
||||
- Correct validation on numeric fields in modal popups
|
||||
- Exclude scssc from manifest
|
||||
- Remove unpublished pages from menu
|
||||
- Remove page from menu items for performance reason
|
||||
- Fix reachability of pages with expired ancestors
|
||||
- Don't try to modify an immutable QueryDict
|
||||
- Only attempt to delete cache keys if there are some to be deleted
|
||||
- Update documentation section
|
||||
- Fix language chooser template
|
||||
- Cast to int cache version
|
||||
- Fix extensions copy when using duplicate page/create page type
|
||||
|
||||
=== 3.0.16 (2015-11-24) ===
|
||||
|
||||
- Fix JS error when using PageSelectWidget
|
||||
- Fix whitespace markup issues in draft mode
|
||||
- Detect plugin migrations layout in tests
|
||||
|
||||
=== 3.0.17 (unreleased) ===
|
||||
|
||||
- Fix ExtensionToolbar when language is removed but titles still exists…
|
||||
- Fix PageSelectWidget JS syntax
|
||||
- Fix cache settings
|
||||
|
||||
=== 3.1 (2015-04-20) ===
|
||||
|
||||
- Remove django-mptt in favor of django-treebeard
|
||||
- Remove compatibility with Django 1.4 / 1.5
|
||||
- General code cleanup
|
||||
- Simplify loading of view restrictions in the menu
|
||||
- South is not marked as optional; to use south on Django 1.6 install django-cms[south]
|
||||
- Add system_plugin attribute to CMSPluginBase that allow the plugin to override any configured restriction
|
||||
- Change placeholder language fallback default to True
|
||||
- Remove plugin table naming compatibility layer
|
||||
- Remove deprecated cms.context_processors.media context processor
|
||||
- Add templatetag render_plugin_block
|
||||
- Add templatetag render_model_add_block
|
||||
- Add "Structure mode" permission
|
||||
|
||||
=== 3.1.1 (2015-06-27) ===
|
||||
|
||||
- Add Django 1.8 support
|
||||
- Tutorial updates and improvements
|
||||
- Fix issue with causes menu classes to be duplicated in advanced settings
|
||||
- Fix issue with breadcrumbs not showing
|
||||
- Fix issues with show_menu templatetags
|
||||
- Minor documentation fixes
|
||||
- Revert whitespace cleanup on flash player to fix it
|
||||
- Correctly restore previous status of dragbars
|
||||
- Add copy_site command
|
||||
- Fix an issue related to "Empty all" Placeholder feature
|
||||
- Fix plugin sorting in py3
|
||||
- Fix language-related issues when retrieving page URL
|
||||
- Add setting to disable toolbar for anonymous users
|
||||
- Fix search results number and items alignment in page changelist
|
||||
- Preserve information regarding the current view when applying the CMS decorator
|
||||
- Fix errors with toolbar population
|
||||
- Fix error with watch_models type
|
||||
- Fix error with plugin breadcrumbs order
|
||||
- Change the label "Save and close" to "Save as draft"
|
||||
- Fix X-Frame-Options on top-level pages
|
||||
- Fix order of which application urls are injected into urlpatterns
|
||||
- Fix delete non existing page language
|
||||
- Fix language fallback for nested plugins
|
||||
- Fix render_model template tag doesn't show correct change list
|
||||
- Fix Scanning for placeholders fails on include tags with a variable as an argument
|
||||
- Fix handling of plugin position attribute
|
||||
- Fix for some structureboard issues
|
||||
- Add setting to hide toolbar when a URL is not handled by django CMS
|
||||
- Add editorconfig configuration
|
||||
- Make shift tab work correctly in submenu
|
||||
- Fix get_language_from_request if POST and GET exists
|
||||
- Fix an error in placeholder cache
|
||||
- Fix language chooser template
|
||||
|
||||
=== 3.1.2 (2015-07-02) ===
|
||||
|
||||
- Fix placeholder cache invalidation under some circumstances
|
||||
- Update translations
|
||||
|
||||
=== 3.1.3 (2015-09-01) ===
|
||||
|
||||
- Add missing migration
|
||||
- Exclude PageUser manager from migrations
|
||||
- Fix check for template instance in Django 1.8.x
|
||||
- Fix error in PageField for Django 1.8
|
||||
- Fix some Page tree bugs
|
||||
- Declare Django 1.6.9 dependency in setup.py
|
||||
- Make sure cache version returned is an int
|
||||
- Fix issue preventing migrations to run on a new database (django 1.8)
|
||||
- Fix get User model in 0010 migration
|
||||
- Fix support for unpublished language pages
|
||||
- Add documentation for plugins datamigration
|
||||
- Fix getting request in _show_placeholder_for_page on Django 1.8
|
||||
- Fix template inheritance order
|
||||
- Fix xframe options inheritance order
|
||||
- Fix placeholder inheritance order
|
||||
- Fix language chooser template
|
||||
- Relax html5lib versions
|
||||
- Fix redirect when deleting a page
|
||||
- Correct South migration error
|
||||
- Correct validation on numeric fields in modal popups
|
||||
- Exclude scssc from manifest
|
||||
- Remove unpublished pages from menu
|
||||
- Remove page from menu items for performance reason
|
||||
- Fix reachability of pages with expired ancestors
|
||||
- Don't try to modify an immutable QueryDict
|
||||
- Only attempt to delete cache keys if there are some to be deleted
|
||||
- Update documentation section
|
||||
- Fix language chooser template
|
||||
- Cast to int cache version
|
||||
- Fix extensions copy when using duplicate page/create page type
|
||||
|
||||
=== 3.1.4 (2015-11-24) ===
|
||||
|
||||
- Fix use structure migration
|
||||
- Fix fix_tree command
|
||||
- Remove some warning for Django 1.9
|
||||
- Fix using scroll bar of plugin menu in Firefox & IE moves plugins
|
||||
- Fix JS error when using PageSelectWidget
|
||||
- Fix whitespace markup issues in draft mode
|
||||
- Detect plugin migrations layout in tests
|
||||
- Fix some treebeard corruption issues
|
||||
|
||||
=== 3.1.5 (2016-01-29) ===
|
||||
|
||||
- Fixed a tree corruption when pasting a nested plugin under another plugin.
|
||||
- Improve CMSPluginBase.render documentation
|
||||
- Fix CMSEditableObject context generation which generates to errors with django-classy-tags 0.7.1
|
||||
- Fix error in toolbar when LocaleMiddleware is not used
|
||||
- Move templates validation in app.ready
|
||||
- Fix ExtensionToolbar when language is removed but titles still exists
|
||||
- Fix pages menu missing on fresh install 3.1
|
||||
- Fix incorrect language on placeholder text for redirect field
|
||||
- Fix PageSelectWidget JS syntax
|
||||
- Fix redirect when disabling toolbar
|
||||
- Fix CMS_TOOLBAR_HIDE causes 'WSGIRequest' object has no attribute 'toolbar'
|
||||
|
||||
=== 3.1.6 (unreleased) ===
|
||||
|
||||
- Fix cache settings
|
||||
- Fix user lookup for view restrictions/page permissions when using raw id field
|
||||
- Fixes an issue relating to uninstalling a namespaced application
|
||||
- Adds "Can change page" permission
|
||||
- Addresses security vulnerabilities in the `render_model` template tag that
|
||||
could lead to escalation of privileges or other security issues.
|
||||
- Addresses a security vulnerability in the cms' usage of the messages framework
|
||||
- Fixes security vulnerabilities in custom FormFields that could lead to
|
||||
escalation of privileges or other security issues.
|
||||
|
||||
+2
-1
@@ -9,4 +9,5 @@ recursive-include cms/plugins *
|
||||
recursive-include menus/templates *
|
||||
recursive-include stacks/templates *
|
||||
recursive-include docs *
|
||||
recursive-exclude * *.pyc
|
||||
recursive-exclude * *.pyc
|
||||
recursive-exclude * *.scssc
|
||||
|
||||
+30
-9
@@ -3,18 +3,30 @@ django CMS
|
||||
##########
|
||||
.. image:: https://travis-ci.org/divio/django-cms.svg?branch=support%2F3.0.x
|
||||
:target: http://travis-ci.org/divio/django-cms
|
||||
.. image:: https://pypip.in/v/django-cms/badge.svg
|
||||
.. image:: https://img.shields.io/pypi/v/django-cms.svg
|
||||
:target: https://pypi.python.org/pypi/django-cms/
|
||||
.. image:: https://pypip.in/d/django-cms/badge.svg
|
||||
.. image:: https://img.shields.io/pypi/dm/django-cms.svg
|
||||
:target: https://pypi.python.org/pypi/django-cms/
|
||||
.. image:: https://pypip.in/wheel/django-cms/badge.svg
|
||||
.. image:: https://img.shields.io/badge/wheel-yes-green.svg
|
||||
:target: https://pypi.python.org/pypi/django-cms/
|
||||
.. image:: https://pypip.in/license/django-cms/badge.svg
|
||||
.. image:: https://img.shields.io/pypi/l/django-cms.svg
|
||||
:target: https://pypi.python.org/pypi/django-cms/
|
||||
|
||||
.. image:: https://codeclimate.com/github/divio/django-cms/badges/gpa.svg
|
||||
:target: https://codeclimate.com/github/divio/django-cms
|
||||
:alt: Code Climate
|
||||
|
||||
Open source enterprise content management system based on the django framework.
|
||||
|
||||
.. ATTENTION:: To propose features, always open pull requests on the **develop** branch.
|
||||
It's the branch for features that will go into the next django CMS feature release.
|
||||
|
||||
For fixes for 3.0.x releases, please work on **support/3.0.x** which contains
|
||||
the next patch release for 3.0.x series.
|
||||
|
||||
The **master** branch is the current stable release, the one released on PyPI.
|
||||
Changes based on **master** will not be accepted.
|
||||
|
||||
|
||||
********
|
||||
Features
|
||||
********
|
||||
@@ -37,11 +49,11 @@ You can define editable areas, called placeholders, in your templates which you
|
||||
with many different so called CMS content plugins.
|
||||
A list of all the plugins can be found here:
|
||||
|
||||
`3rd party plugins <http://www.djangopackages.com/grids/g/django-cms/>`_
|
||||
`3rd party plugins <http://www.djangopackages.com/grids/g/django-cms/>`_
|
||||
|
||||
Should you be unable to find a suitable plugin for you needs, writing your own is very simple.
|
||||
|
||||
More information on `our website <http://www.django-cms.org>`_.
|
||||
More information on `our website <http://www.django-cms.org>`_.
|
||||
|
||||
*************
|
||||
Documentation
|
||||
@@ -66,8 +78,10 @@ You can use the `django CMS installer <https://github.com/nephila/djangocms-inst
|
||||
$ virtualenv env
|
||||
$ source env/bin/activate
|
||||
(env) $ pip install djangocms-installer
|
||||
(env) $ mkdir myproject && cd myproject
|
||||
(env) $ djangocms -p . my_demo
|
||||
(env) $ cd my_demo
|
||||
(env) $ python manage.py
|
||||
|
||||
|
||||
************
|
||||
Getting Help
|
||||
@@ -76,13 +90,20 @@ Getting Help
|
||||
Please head over to our IRC channel, #django-cms, on irc.freenode.net or write
|
||||
to our `mailing list <https://groups.google.com/forum/#!forum/django-cms>`_.
|
||||
|
||||
******************
|
||||
Commercial support
|
||||
******************
|
||||
|
||||
This project is backed by `Divio <https://divio.ch>`_. If you need help
|
||||
implementing or hosting django CMS, please contact us: sales@divio.ch.
|
||||
|
||||
*******
|
||||
Credits
|
||||
*******
|
||||
|
||||
* Includes icons from `FamFamFam <http://www.famfamfam.com>`_.
|
||||
* Python tree engine powered by
|
||||
`django-mptt <https://github.com/django-mptt/django-mptt>`_.
|
||||
`django-treebeard <https://tabo.pe/projects/django-treebeard/>`_.
|
||||
* Javascript tree in admin uses `jsTree <http://www.jstree.com>`_.
|
||||
* Many thanks to the
|
||||
`over 250 contributors <https://github.com/divio/django-cms/blob/master/AUTHORS>`_
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
__version__ = '3.0.14'
|
||||
|
||||
__version__ = '3.1.6'
|
||||
|
||||
default_app_config = 'cms.apps.CMSConfig'
|
||||
|
||||
+30
-34
@@ -2,13 +2,12 @@
|
||||
import bisect
|
||||
from cms.models import Title, Page, EmptyTitle
|
||||
from cms.utils import get_language_list
|
||||
from cms.utils.compat import DJANGO_1_5
|
||||
from cms.utils.conf import get_cms_setting
|
||||
from cms.utils.permissions import get_user_sites_queryset
|
||||
from cms.utils.permissions import get_user_sites_queryset, load_view_restrictions
|
||||
from django.contrib.admin.views.main import ChangeList, ALL_VAR, IS_POPUP_VAR, \
|
||||
ORDER_TYPE_VAR, ORDER_VAR, SEARCH_VAR
|
||||
from django.contrib.sites.models import Site
|
||||
import django
|
||||
|
||||
|
||||
COPY_VAR = "copy"
|
||||
|
||||
@@ -20,7 +19,7 @@ def cache_tree_children(queryset):
|
||||
item, which would otherwise (if '_cached_children' is not set) cause a
|
||||
database query.
|
||||
|
||||
The queryset must be ordered by 'lft', or the function will put the children
|
||||
The queryset must be ordered by 'path', or the function will put the children
|
||||
in the wrong order.
|
||||
"""
|
||||
parents_dict = {}
|
||||
@@ -46,12 +45,13 @@ class CMSChangeList(ChangeList):
|
||||
real_queryset = False
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
from cms.utils.plugins import current_site
|
||||
from cms.utils.helpers import current_site
|
||||
|
||||
self._current_site = current_site(request)
|
||||
super(CMSChangeList, self).__init__(request, *args, **kwargs)
|
||||
try:
|
||||
self.queryset = self.get_query_set(request)
|
||||
except:
|
||||
self.queryset = self.get_queryset(request)
|
||||
except: # pragma: no cover
|
||||
raise
|
||||
self.get_results(request)
|
||||
|
||||
@@ -59,33 +59,26 @@ class CMSChangeList(ChangeList):
|
||||
request.session['cms_admin_site'] = self._current_site.pk
|
||||
self.set_sites(request)
|
||||
|
||||
def get_query_set(self, request=None):
|
||||
def get_queryset(self, request):
|
||||
if COPY_VAR in self.params:
|
||||
del self.params[COPY_VAR]
|
||||
if 'language' in self.params:
|
||||
del self.params['language']
|
||||
if 'page_id' in self.params:
|
||||
del self.params['page_id']
|
||||
if django.VERSION[1] > 3:
|
||||
qs = super(CMSChangeList, self).get_query_set(request).drafts()
|
||||
else:
|
||||
qs = super(CMSChangeList, self).get_query_set().drafts()
|
||||
if request:
|
||||
site = self.current_site()
|
||||
permissions = Page.permissions.get_change_id_list(request.user, site)
|
||||
if permissions != Page.permissions.GRANT_ALL:
|
||||
qs = qs.filter(pk__in=permissions)
|
||||
# root_query_set is a read-only property in Django 1.6
|
||||
# and will be removed in Django 1.8.
|
||||
queryset_attr = 'root_query_set' if DJANGO_1_5 else 'root_queryset'
|
||||
setattr(self, queryset_attr, self.root_query_set.filter(pk__in=permissions))
|
||||
self.real_queryset = True
|
||||
qs = qs.filter(site=self._current_site)
|
||||
qs = super(CMSChangeList, self).get_queryset(request).drafts()
|
||||
site = self.current_site()
|
||||
permissions = Page.permissions.get_change_id_list(request.user, site)
|
||||
if permissions != Page.permissions.GRANT_ALL:
|
||||
qs = qs.filter(pk__in=permissions)
|
||||
self.root_queryset = self.root_queryset.filter(pk__in=permissions)
|
||||
self.real_queryset = True
|
||||
qs = qs.filter(site=self._current_site)
|
||||
return qs
|
||||
|
||||
def is_filtered(self):
|
||||
from cms.utils.plugins import SITE_VAR
|
||||
lookup_params = self.params.copy() # a dictionary of the query string
|
||||
from cms.utils.helpers import SITE_VAR
|
||||
lookup_params = self.params.copy() # a dictionary of the query string
|
||||
for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR, SITE_VAR, 'language', 'page_id'):
|
||||
if i in lookup_params:
|
||||
del lookup_params[i]
|
||||
@@ -97,15 +90,15 @@ class CMSChangeList(ChangeList):
|
||||
if self.real_queryset:
|
||||
super(CMSChangeList, self).get_results(request)
|
||||
if not self.is_filtered():
|
||||
self.full_result_count = self.result_count = self.root_query_set.drafts().count()
|
||||
self.full_result_count = self.result_count = self.root_queryset.drafts().count()
|
||||
else:
|
||||
self.full_result_count = self.root_query_set.drafts().count()
|
||||
self.full_result_count = self.root_queryset.drafts().count()
|
||||
|
||||
def set_items(self, request):
|
||||
site = self.current_site()
|
||||
# Get all the pages, ordered by tree ID (it's convenient to build the
|
||||
# tree using a stack now)
|
||||
pages = self.get_query_set(request).drafts().order_by('tree_id', 'lft').select_related('publisher_public')
|
||||
pages = self.get_queryset(request).drafts().order_by('path').select_related('publisher_public')
|
||||
|
||||
# Get lists of page IDs for which the current user has
|
||||
# "permission to..." on the current site.
|
||||
@@ -118,19 +111,22 @@ class CMSChangeList(ChangeList):
|
||||
pages = pages.filter(pk__in=perm_edit_ids)
|
||||
|
||||
root_pages = []
|
||||
# Cache view restrictions for the is_restricted template tag
|
||||
load_view_restrictions(request, pages)
|
||||
pages = list(pages)
|
||||
all_pages = pages[:] # That is, basically, a copy.
|
||||
|
||||
# Unfortunately we cannot use the MPTT builtin code for pre-caching
|
||||
# the children here, because MPTT expects the tree to be 'complete'
|
||||
# and otherwise complaints about 'invalid item order'
|
||||
cache_tree_children(pages)
|
||||
ids = dict((page.id, page) for page in pages)
|
||||
|
||||
parent_ids = {}
|
||||
for page in pages:
|
||||
|
||||
children = list(page.get_children())
|
||||
|
||||
if not page.parent_id in parent_ids:
|
||||
parent_ids[page.parent_id] = []
|
||||
parent_ids[page.parent_id].append(page)
|
||||
for page in pages:
|
||||
children = parent_ids.get(page.pk, [])
|
||||
# If the parent page is not among the nodes shown, this node should
|
||||
# be a "root node". The filtering for this has already been made, so
|
||||
# using the ids dictionary means this check is constant time
|
||||
@@ -154,7 +150,7 @@ class CMSChangeList(ChangeList):
|
||||
page.menu_level = 0
|
||||
root_pages.append(page)
|
||||
if page.parent_id:
|
||||
page.get_cached_ancestors(ascending=True)
|
||||
page.get_cached_ancestors()
|
||||
else:
|
||||
page.ancestors_ascending = []
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.shortcuts import get_object_or_404, render_to_response
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
|
||||
from cms.models import Page
|
||||
from cms.utils import get_cms_setting
|
||||
@@ -14,17 +14,18 @@ def get_copy_dialog(request, page_id):
|
||||
if not get_cms_setting('PERMISSION'):
|
||||
return HttpResponse('')
|
||||
|
||||
target_id = request.GET.get('target', False) or request.POST.get('target', False)
|
||||
callback = request.GET.get('callback', False) or request.POST.get('callback', False)
|
||||
page = get_object_or_404(Page, pk=page_id)
|
||||
target = get_object_or_404(Page, pk=request.REQUEST['target'])
|
||||
target = get_object_or_404(Page, pk=target_id)
|
||||
|
||||
if not page.has_change_permission(request) or \
|
||||
not target.has_add_permission(request):
|
||||
if not page.has_change_permission(request) or not target.has_add_permission(request):
|
||||
raise Http404
|
||||
|
||||
context = {
|
||||
'dialog_id': 'dialog-copy',
|
||||
'form': PermissionForm(), # class needs to be instantiated
|
||||
'callback': request.REQUEST['callback'],
|
||||
'callback': callback,
|
||||
}
|
||||
|
||||
return render_to_response("admin/cms/page/tree/copy_premissions.html", context)
|
||||
return render(request, "admin/cms/page/tree/copy_premissions.html", context)
|
||||
|
||||
+36
-45
@@ -1,11 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django import forms
|
||||
from django.contrib.auth import get_user_model, get_permission_codename
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.exceptions import ValidationError, ObjectDoesNotExist
|
||||
from django.db.models.fields import BooleanField
|
||||
from django.forms.util import ErrorList
|
||||
try:
|
||||
from django.forms.utils import ErrorList
|
||||
except ImportError:
|
||||
from django.forms.util import ErrorList
|
||||
from django.forms.widgets import HiddenInput
|
||||
from django.template.defaultfilters import slugify
|
||||
from django.utils.encoding import force_text
|
||||
@@ -14,11 +18,11 @@ from django.utils.translation import ugettext_lazy as _, get_language
|
||||
from cms.apphook_pool import apphook_pool
|
||||
from cms.constants import PAGE_TYPES_ID
|
||||
from cms.forms.widgets import UserSelectAdminWidget, AppHookSelect, ApplicationConfigSelect
|
||||
from cms.models import Page, PagePermission, PageUser, ACCESS_PAGE, PageUserGroup, Title, EmptyTitle
|
||||
from cms.utils.compat.dj import get_user_model, force_unicode
|
||||
from cms.models import (Page, PagePermission, PageUser, ACCESS_PAGE, PageUserGroup, Title,
|
||||
EmptyTitle, GlobalPagePermission)
|
||||
from cms.utils.compat.forms import UserCreationForm
|
||||
from cms.utils.conf import get_cms_setting
|
||||
from cms.utils.i18n import get_language_tuple, get_language_list
|
||||
from cms.utils.i18n import get_language_tuple
|
||||
from cms.utils.mail import mail_page_user_change
|
||||
from cms.utils.page import is_valid_page_slug
|
||||
from cms.utils.page_resolver import is_valid_url
|
||||
@@ -28,9 +32,9 @@ from cms.utils.permissions import (get_current_user, get_subordinate_users,
|
||||
from menus.menu_pool import menu_pool
|
||||
|
||||
|
||||
def get_permission_acessor(obj):
|
||||
def get_permission_accessor(obj):
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
if isinstance(obj, (PageUser, User,)):
|
||||
rel_name = 'user_permissions'
|
||||
else:
|
||||
@@ -48,17 +52,17 @@ def save_permissions(data, obj):
|
||||
if not obj.pk:
|
||||
# save obj, otherwise we can't assign permissions to him
|
||||
obj.save()
|
||||
permission_acessor = get_permission_acessor(obj)
|
||||
permission_accessor = get_permission_accessor(obj)
|
||||
for model, name in models:
|
||||
content_type = ContentType.objects.get_for_model(model)
|
||||
for t in ('add', 'change', 'delete'):
|
||||
# add permission `t` to model `model`
|
||||
codename = getattr(model._meta, 'get_%s_permission' % t)()
|
||||
for key in ('add', 'change', 'delete'):
|
||||
# add permission `key` for model `model`
|
||||
codename = get_permission_codename(key, model._meta)
|
||||
permission = Permission.objects.get(content_type=content_type, codename=codename)
|
||||
if data.get('can_%s_%s' % (t, name), None):
|
||||
permission_acessor.add(permission)
|
||||
if data.get('can_%s_%s' % (key, name), None):
|
||||
permission_accessor.add(permission)
|
||||
else:
|
||||
permission_acessor.remove(permission)
|
||||
permission_accessor.remove(permission)
|
||||
|
||||
|
||||
class PageForm(forms.ModelForm):
|
||||
@@ -113,22 +117,14 @@ class PageForm(forms.ModelForm):
|
||||
def clean(self):
|
||||
cleaned_data = self.cleaned_data
|
||||
slug = cleaned_data.get('slug', '')
|
||||
|
||||
|
||||
page = self.instance
|
||||
lang = cleaned_data.get('language', None)
|
||||
# No language, can not go further, but validation failed already
|
||||
if not lang:
|
||||
return cleaned_data
|
||||
|
||||
if 'parent' not in cleaned_data:
|
||||
cleaned_data['parent'] = None
|
||||
parent = cleaned_data.get('parent', None)
|
||||
|
||||
try:
|
||||
site = self.cleaned_data.get('site', Site.objects.get_current())
|
||||
except Site.DoesNotExist:
|
||||
raise ValidationError("No site found for current settings.")
|
||||
|
||||
site = self.cleaned_data.get('site', Site.objects.get_current())
|
||||
if parent and parent.site != site:
|
||||
raise ValidationError("Site doesn't match the parent's page site")
|
||||
|
||||
@@ -154,7 +150,7 @@ class PageForm(forms.ModelForm):
|
||||
if hasattr(exc, 'messages'):
|
||||
errors = exc.messages
|
||||
else:
|
||||
errors = [force_unicode(exc.message)]
|
||||
errors = [force_text(exc.message)]
|
||||
self._errors['slug'] = ErrorList(errors)
|
||||
return cleaned_data
|
||||
|
||||
@@ -164,12 +160,6 @@ class PageForm(forms.ModelForm):
|
||||
raise ValidationError(_("Slug must not be empty."))
|
||||
return slug
|
||||
|
||||
def clean_language(self):
|
||||
language = self.cleaned_data['language']
|
||||
if not language in get_language_list():
|
||||
raise ValidationError("Given language does not match language settings.")
|
||||
return language
|
||||
|
||||
|
||||
class PublicationDatesForm(forms.ModelForm):
|
||||
language = forms.ChoiceField(label=_("Language"), choices=get_language_tuple(),
|
||||
@@ -399,10 +389,10 @@ class PagePermissionInlineAdminForm(forms.ModelForm):
|
||||
"""
|
||||
Page permission inline admin form used in inline admin. Required, because
|
||||
user and group queryset must be changed. User can see only users on the same
|
||||
level or under him in choosen page tree, and users which were created by him,
|
||||
level or under him in choosen page tree, and users which were created by him,
|
||||
but aren't assigned to higher page level than current user.
|
||||
"""
|
||||
page = forms.ModelChoiceField(Page, label=_('user'), widget=HiddenInput(), required=True)
|
||||
page = forms.ModelChoiceField(Page.objects.all(), label=_('user'), widget=HiddenInput(), required=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PagePermissionInlineAdminForm, self).__init__(*args, **kwargs)
|
||||
@@ -458,13 +448,12 @@ class PagePermissionInlineAdminForm(forms.ModelForm):
|
||||
continue
|
||||
name = field.name
|
||||
self.cleaned_data[name] = self.cleaned_data.get(name, False)
|
||||
|
||||
can_add = self.cleaned_data['can_add']
|
||||
can_edit = self.cleaned_data['can_change']
|
||||
# check if access for childrens, or descendants is granted
|
||||
if can_add and self.cleaned_data['grant_on'] == ACCESS_PAGE:
|
||||
# this is a missconfiguration - user can add/move page to current
|
||||
# page but after he does this, he will not have permissions to
|
||||
# page but after he does this, he will not have permissions to
|
||||
# access this page anymore, so avoid this
|
||||
raise forms.ValidationError(_("Add page permission requires also "
|
||||
"access to children, or descendants, otherwise added page "
|
||||
@@ -494,6 +483,7 @@ class PagePermissionInlineAdminForm(forms.ModelForm):
|
||||
return instance
|
||||
|
||||
class Meta:
|
||||
fields = '__all__'
|
||||
model = PagePermission
|
||||
|
||||
|
||||
@@ -502,7 +492,7 @@ class ViewRestrictionInlineAdminForm(PagePermissionInlineAdminForm):
|
||||
|
||||
def clean_can_view(self):
|
||||
self.cleaned_data["can_view"] = True
|
||||
return self.cleaned_data
|
||||
return True
|
||||
|
||||
|
||||
class GlobalPagePermissionAdminForm(forms.ModelForm):
|
||||
@@ -512,6 +502,10 @@ class GlobalPagePermissionAdminForm(forms.ModelForm):
|
||||
raise forms.ValidationError(_('Please select user or group first.'))
|
||||
return self.cleaned_data
|
||||
|
||||
class Meta:
|
||||
fields = '__all__'
|
||||
model = GlobalPagePermission
|
||||
|
||||
|
||||
class GenericCmsPermissionForm(forms.ModelForm):
|
||||
"""Generic form for User & Grup permissions in cms
|
||||
@@ -535,14 +529,14 @@ class GenericCmsPermissionForm(forms.ModelForm):
|
||||
"""Read out permissions from permission system.
|
||||
"""
|
||||
initials = {}
|
||||
permission_acessor = get_permission_acessor(obj)
|
||||
permission_accessor = get_permission_accessor(obj)
|
||||
for model in (Page, PageUser, PagePermission):
|
||||
name = model.__name__.lower()
|
||||
content_type = ContentType.objects.get_for_model(model)
|
||||
permissions = permission_acessor.filter(content_type=content_type).values_list('codename', flat=True)
|
||||
for t in ('add', 'change', 'delete'):
|
||||
codename = getattr(model._meta, 'get_%s_permission' % t)()
|
||||
initials['can_%s_%s' % (t, name)] = codename in permissions
|
||||
permissions = permission_accessor.filter(content_type=content_type).values_list('codename', flat=True)
|
||||
for key in ('add', 'change', 'delete'):
|
||||
codename = get_permission_codename(key, model._meta)
|
||||
initials['can_%s_%s' % (key, name)] = codename in permissions
|
||||
return initials
|
||||
|
||||
|
||||
@@ -552,6 +546,7 @@ class PageUserForm(UserCreationForm, GenericCmsPermissionForm):
|
||||
'Send email notification to user about username or password change. Requires user email.'))
|
||||
|
||||
class Meta:
|
||||
fields = '__all__'
|
||||
model = PageUser
|
||||
|
||||
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
|
||||
@@ -606,7 +601,7 @@ class PageUserForm(UserCreationForm, GenericCmsPermissionForm):
|
||||
return cleaned_data
|
||||
|
||||
def save(self, commit=True):
|
||||
"""Create user, assign him to staff users, and create permissions for
|
||||
"""Create user, assign him to staff users, and create permissions for
|
||||
him if required. Also assigns creator to user.
|
||||
"""
|
||||
Super = self._password_change and PageUserForm or UserCreationForm
|
||||
@@ -644,15 +639,11 @@ class PageUserGroupForm(GenericCmsPermissionForm):
|
||||
|
||||
def save(self, commit=True):
|
||||
group = super(GenericCmsPermissionForm, self).save(commit=False)
|
||||
|
||||
created = not bool(group.pk)
|
||||
# assign creator to user
|
||||
if created:
|
||||
group.created_by = get_current_user()
|
||||
|
||||
if commit:
|
||||
group.save()
|
||||
|
||||
save_permissions(self.cleaned_data, group)
|
||||
|
||||
return group
|
||||
|
||||
+147
-234
@@ -3,23 +3,33 @@ import copy
|
||||
from functools import wraps
|
||||
import json
|
||||
import sys
|
||||
from cms.utils.compat import DJANGO_1_7
|
||||
|
||||
import django
|
||||
from django.contrib.admin.helpers import AdminForm
|
||||
from django.conf import settings
|
||||
from django.conf.urls import url
|
||||
from django.contrib import admin, messages
|
||||
from django.contrib.admin.models import LogEntry, CHANGE
|
||||
from django.contrib.admin.options import IncorrectLookupParameters
|
||||
from django.contrib.admin.util import get_deleted_objects
|
||||
try:
|
||||
from django.contrib.admin.utils import get_deleted_objects
|
||||
except ImportError:
|
||||
from django.contrib.admin.util import get_deleted_objects
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.sites.models import Site, get_current_site
|
||||
from django.contrib.sites.models import Site
|
||||
try:
|
||||
from django.contrib.sites.shortcuts import get_current_site
|
||||
except ImportError:
|
||||
from django.contrib.sites.models import get_current_site
|
||||
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist, ValidationError
|
||||
from django.db import router
|
||||
from django.db import router, transaction
|
||||
from django.db.models import Q
|
||||
from django.http import HttpResponseRedirect, HttpResponse, Http404, HttpResponseBadRequest, HttpResponseForbidden
|
||||
from django.shortcuts import render_to_response, get_object_or_404
|
||||
from django.template.context import RequestContext
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.template.defaultfilters import escape
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.six.moves.urllib.parse import unquote
|
||||
from django.utils.translation import ugettext_lazy as _, get_language
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.http import require_POST
|
||||
@@ -39,14 +49,10 @@ from cms.toolbar_pool import toolbar_pool
|
||||
from cms.utils import helpers, permissions, get_language_from_request, admin as admin_utils, copy_plugins
|
||||
from cms.utils.i18n import get_language_list, get_language_tuple, get_language_object, force_language
|
||||
from cms.utils.admin import jsonify_request
|
||||
from cms.utils.compat import DJANGO_1_4
|
||||
from cms.utils.compat.dj import force_unicode, is_installed
|
||||
from cms.utils.compat.urls import unquote
|
||||
from cms.utils.compat.dj import is_installed
|
||||
from cms.utils.conf import get_cms_setting
|
||||
from cms.utils.helpers import find_placeholder_relation
|
||||
from cms.utils.helpers import find_placeholder_relation, current_site
|
||||
from cms.utils.permissions import has_global_page_permission, has_generic_permission
|
||||
from cms.utils.plugins import current_site
|
||||
from cms.utils.transaction import wrap_transaction
|
||||
from cms.utils.urlutils import add_url_parameters, admin_reverse
|
||||
|
||||
require_POST = method_decorator(require_POST)
|
||||
@@ -106,14 +112,10 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
def get_urls(self):
|
||||
"""Get the admin urls
|
||||
"""
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
info = "%s_%s" % (self.model._meta.app_label, self.model._meta.module_name)
|
||||
info = "%s_%s" % (self.model._meta.app_label, self.model._meta.model_name)
|
||||
pat = lambda regex, fn: url(regex, self.admin_site.admin_view(fn), name='%s_%s' % (info, fn.__name__))
|
||||
|
||||
url_patterns = patterns(
|
||||
'',
|
||||
|
||||
url_patterns = [
|
||||
pat(r'^([0-9]+)/advanced-settings/$', self.advanced),
|
||||
pat(r'^([0-9]+)/dates/$', self.dates),
|
||||
pat(r'^([0-9]+)/permission-settings/$', self.permissions),
|
||||
@@ -123,7 +125,6 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
pat(r'^([0-9]+)/copy-language/$', self.copy_language),
|
||||
pat(r'^([0-9]+)/dialog/copy/$', get_copy_dialog), # copy dialog
|
||||
pat(r'^([0-9]+)/change-navigation/$', self.change_innavigation),
|
||||
pat(r'^([0-9]+)/jsi18n/$', self.redirect_jsi18n),
|
||||
pat(r'^([0-9]+)/permissions/$', self.get_permissions),
|
||||
pat(r'^([0-9]+)/undo/$', self.undo),
|
||||
pat(r'^([0-9]+)/redo/$', self.redo),
|
||||
@@ -137,7 +138,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
pat(r'^add-page-type/$', self.add_page_type),
|
||||
pat(r'^published-pages/$', self.get_published_pagelist),
|
||||
url(r'^resolve/$', self.resolve, name="cms_page_resolve"),
|
||||
)
|
||||
]
|
||||
|
||||
if plugin_pool.get_all_plugins():
|
||||
url_patterns += plugin_pool.get_patterns()
|
||||
@@ -145,9 +146,6 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
url_patterns += super(PageAdmin, self).get_urls()
|
||||
return url_patterns
|
||||
|
||||
def redirect_jsi18n(self, request):
|
||||
return HttpResponseRedirect(admin_reverse('jsi18n'))
|
||||
|
||||
def get_revision_instances(self, request, object):
|
||||
"""Returns all the instances to be used in the object's revision."""
|
||||
if isinstance(object, Title):
|
||||
@@ -174,32 +172,39 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
Move the page in the tree if necessary and save every placeholder
|
||||
Content object.
|
||||
"""
|
||||
from cms.extensions import extension_pool
|
||||
|
||||
target = request.GET.get('target', None)
|
||||
position = request.GET.get('position', None)
|
||||
|
||||
if 'recover' in request.path_info:
|
||||
pk = obj.pk
|
||||
if obj.parent_id:
|
||||
parent = Page.objects.get(pk=obj.parent_id)
|
||||
try:
|
||||
parent = Page.objects.get(pk=obj.parent_id)
|
||||
except Page.DoesNotExist:
|
||||
parent = None
|
||||
else:
|
||||
parent = None
|
||||
obj.lft = 0
|
||||
obj.rght = 0
|
||||
obj.tree_id = 0
|
||||
obj.level = 0
|
||||
obj.pk = None
|
||||
obj.insert_at(parent, save=False)
|
||||
obj.pk = pk
|
||||
obj.save(no_signals=True)
|
||||
|
||||
obj.path = None
|
||||
obj.numchild = 0
|
||||
obj.depth = 0
|
||||
if parent:
|
||||
saved_obj = parent.add_child(instance=obj)
|
||||
else:
|
||||
saved_obj = obj.add_root(instance=obj)
|
||||
tmp_pk = saved_obj.pk
|
||||
saved_obj.pk = pk
|
||||
Page.objects.get(pk=tmp_pk).delete()
|
||||
saved_obj.save(no_signals=True)
|
||||
else:
|
||||
if 'history' in request.path_info:
|
||||
old_obj = Page.objects.get(pk=obj.pk)
|
||||
obj.level = old_obj.level
|
||||
obj.depth = old_obj.depth
|
||||
obj.parent_id = old_obj.parent_id
|
||||
obj.rght = old_obj.rght
|
||||
obj.lft = old_obj.lft
|
||||
obj.tree_id = old_obj.tree_id
|
||||
obj.path = old_obj.path
|
||||
obj.numchild = old_obj.numchild
|
||||
new = False
|
||||
if not obj.pk:
|
||||
new = True
|
||||
@@ -214,21 +219,27 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
except self.model.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
obj.move_to(target, position)
|
||||
if position == 'last-child' or position == 'first-child':
|
||||
obj.parent_id = target.pk
|
||||
else:
|
||||
obj.parent_id = target.parent_id
|
||||
obj.save()
|
||||
obj = obj.move(target, pos=position)
|
||||
page_type_id = form.cleaned_data.get('page_type')
|
||||
copy_target_id = request.GET.get('copy_target')
|
||||
copy_target = None
|
||||
if copy_target_id or page_type_id:
|
||||
if page_type_id:
|
||||
copy_target_id = page_type_id
|
||||
copy_target = Page.objects.get(pk=copy_target_id)
|
||||
if not copy_target.has_view_permission(request):
|
||||
raise PermissionDenied()
|
||||
obj = Page.objects.get(pk=obj.pk) #mptt reload
|
||||
obj = obj.reload()
|
||||
copy_target._copy_attributes(obj, clean=True)
|
||||
obj.save()
|
||||
for lang in copy_target.languages.split(','):
|
||||
for lang in copy_target.get_languages():
|
||||
copy_target._copy_contents(obj, lang)
|
||||
if not 'permission' in request.path_info:
|
||||
if 'permission' not in request.path_info:
|
||||
language = form.cleaned_data['language']
|
||||
Title.objects.set_or_create(
|
||||
request,
|
||||
@@ -236,6 +247,8 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
form,
|
||||
language,
|
||||
)
|
||||
if copy_target:
|
||||
extension_pool.copy_extensions(copy_target, obj)
|
||||
# is it home? publish it right away
|
||||
if new and Page.objects.filter(site_id=obj.site_id).count() == 1:
|
||||
obj.publish(language)
|
||||
@@ -349,12 +362,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
return self.change_view(request, object_id, extra_context={'show_permissions': True, 'title': _("Change Permissions")})
|
||||
|
||||
def get_inline_instances(self, request, obj=None):
|
||||
if DJANGO_1_4:
|
||||
inlines = super(PageAdmin, self).get_inline_instances(request)
|
||||
if hasattr(self, '_current_page'):
|
||||
obj = self._current_page
|
||||
else:
|
||||
inlines = super(PageAdmin, self).get_inline_instances(request, obj)
|
||||
inlines = super(PageAdmin, self).get_inline_instances(request, obj)
|
||||
if get_cms_setting('PERMISSION') and obj:
|
||||
filtered_inlines = []
|
||||
for inline in inlines:
|
||||
@@ -435,12 +443,6 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
|
||||
extra_context.update(self.get_unihandecode_context(tab_language))
|
||||
|
||||
# get_inline_instances will need access to 'obj' so that it can
|
||||
# determine if current user has enough rights to see PagePermissionInlineAdmin
|
||||
# because in django versions <1.5 get_inline_instances doesn't receive 'obj'
|
||||
# as a parameter, the workaround is to set it as an attribute...
|
||||
if DJANGO_1_4:
|
||||
self._current_page = obj
|
||||
response = super(PageAdmin, self).change_view(
|
||||
request, object_id, form_url=form_url, extra_context=extra_context)
|
||||
if tab_language and response.status_code == 302 and response._headers['location'][1] == request.path_info:
|
||||
@@ -603,7 +605,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
|
||||
def post_add_plugin(self, request, placeholder, plugin):
|
||||
if is_installed('reversion') and placeholder.page:
|
||||
plugin_name = force_unicode(plugin_pool.get_plugin(plugin.plugin_type).name)
|
||||
plugin_name = force_text(plugin_pool.get_plugin(plugin.plugin_type).name)
|
||||
message = _(u"%(plugin_name)s plugin added to %(placeholder)s") % {
|
||||
'plugin_name': plugin_name, 'placeholder': placeholder}
|
||||
self.cleanup_history(placeholder.page)
|
||||
@@ -621,7 +623,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
if page:
|
||||
# if reversion is installed, save version of the page plugins
|
||||
if is_installed('reversion') and page:
|
||||
plugin_name = force_unicode(plugin_pool.get_plugin(plugin.plugin_type).name)
|
||||
plugin_name = force_text(plugin_pool.get_plugin(plugin.plugin_type).name)
|
||||
message = _(
|
||||
u"%(plugin_name)s plugin edited at position %(position)s in %(placeholder)s") % {
|
||||
'plugin_name': plugin_name,
|
||||
@@ -638,7 +640,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
helpers.make_revision_with_plugins(page, request.user, _(u"Plugins were moved"))
|
||||
|
||||
def post_delete_plugin(self, request, plugin):
|
||||
plugin_name = force_unicode(plugin_pool.get_plugin(plugin.plugin_type).name)
|
||||
plugin_name = force_text(plugin_pool.get_plugin(plugin.plugin_type).name)
|
||||
page = plugin.placeholder.page
|
||||
if page:
|
||||
page.save()
|
||||
@@ -656,7 +658,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
if page:
|
||||
page.save()
|
||||
comment = _('All plugins in the placeholder "%(name)s" were deleted.') % {
|
||||
'name': force_unicode(placeholder)
|
||||
'name': force_text(placeholder)
|
||||
}
|
||||
if is_installed('reversion'):
|
||||
self.cleanup_history(page)
|
||||
@@ -674,7 +676,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
opts = self.model._meta
|
||||
app_label = opts.app_label
|
||||
if not self.has_change_permission(request, None):
|
||||
return HttpResponseForbidden(force_unicode(_("You do not have permission to change pages.")))
|
||||
return HttpResponseForbidden(force_text(_("You do not have permission to change pages.")))
|
||||
try:
|
||||
cl = CMSChangeList(request, self.model, self.list_display, self.list_display_links, self.list_filter,
|
||||
self.date_hierarchy, self.search_fields, self.list_select_related, self.list_per_page,
|
||||
@@ -686,7 +688,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
# the 'invalid=1' parameter was already in the query string, something
|
||||
# is screwed up with the database, so display an error page.
|
||||
if ERROR_FLAG in request.GET.keys():
|
||||
return render_to_response('admin/invalid_setup.html', {'title': _('Database error')})
|
||||
return render(request, 'admin/invalid_setup.html', {'title': _('Database error')})
|
||||
return HttpResponseRedirect(request.path_info + '?' + ERROR_FLAG + '=1')
|
||||
cl.set_items(request)
|
||||
|
||||
@@ -729,11 +731,11 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
context['has_recover_permission'] = self.has_recover_permission(request)
|
||||
context['has_change_permission'] = self.has_change_permission(request)
|
||||
context.update(extra_context or {})
|
||||
return render_to_response(self.change_list_template or [
|
||||
return render(request, self.change_list_template or [
|
||||
'admin/%s/%s/change_list.html' % (app_label, opts.object_name.lower()),
|
||||
'admin/%s/change_list.html' % app_label,
|
||||
'admin/change_list.html'
|
||||
], context, context_instance=RequestContext(request))
|
||||
], context)
|
||||
|
||||
def recoverlist_view(self, request, extra_context=None):
|
||||
if not self.has_recover_permission(request):
|
||||
@@ -780,133 +782,38 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
def undo(self, request, object_id):
|
||||
if not is_installed('reversion'):
|
||||
return HttpResponseBadRequest('django reversion not installed')
|
||||
from reversion.models import Revision
|
||||
from cms.utils.page_resolver import is_valid_url
|
||||
import reversion
|
||||
|
||||
page = get_object_or_404(Page, pk=object_id)
|
||||
old_titles = list(page.title_set.all())
|
||||
if not page.publisher_is_draft:
|
||||
page = page.publisher_draft
|
||||
if not page.has_change_permission(request):
|
||||
return HttpResponseForbidden(force_unicode(_("You do not have permission to change this page")))
|
||||
versions = reversion.get_for_object(page)
|
||||
if page.revision_id:
|
||||
current_revision = Revision.objects.get(pk=page.revision_id)
|
||||
else:
|
||||
try:
|
||||
current_version = versions[0]
|
||||
except IndexError:
|
||||
return HttpResponseBadRequest("no current revision found")
|
||||
current_revision = current_version.revision
|
||||
return HttpResponseForbidden(force_text(_("You do not have permission to change this page")))
|
||||
try:
|
||||
previous_version = versions.filter(revision__pk__lt=current_revision.pk)[0]
|
||||
except IndexError:
|
||||
return HttpResponseBadRequest("no previous revision found")
|
||||
previous_revision = previous_version.revision
|
||||
# clear all plugins
|
||||
placeholders = page.placeholders.all()
|
||||
placeholder_ids = []
|
||||
for placeholder in placeholders:
|
||||
placeholder_ids.append(placeholder.pk)
|
||||
plugins = CMSPlugin.objects.filter(placeholder__in=placeholder_ids).order_by('-level')
|
||||
for plugin in plugins:
|
||||
plugin._no_reorder = True
|
||||
plugin.delete()
|
||||
# TODO: delete placeholders instead of finding duplicates for 3.1
|
||||
#page.placeholders.all().delete()
|
||||
reverted, clean = page.undo()
|
||||
if not clean:
|
||||
messages.error(request, _("Page reverted but slug stays the same because of url collisions."))
|
||||
except IndexError as e:
|
||||
return HttpResponseBadRequest(e.message)
|
||||
|
||||
previous_revision.revert(True)
|
||||
rev_page = get_object_or_404(Page, pk=page.pk)
|
||||
rev_page.revision_id = previous_revision.pk
|
||||
rev_page.publisher_public_id = page.publisher_public_id
|
||||
rev_page.save()
|
||||
new_placeholders = rev_page.placeholders.all()
|
||||
slots = {}
|
||||
for new_ph in new_placeholders:
|
||||
if not new_ph.slot in slots:
|
||||
slots[new_ph.slot] = new_ph
|
||||
else:
|
||||
if new_ph in placeholder_ids:
|
||||
new_ph.delete()
|
||||
elif slots[new_ph.slot] in placeholder_ids:
|
||||
slots[new_ph.slot].delete()
|
||||
new_titles = rev_page.title_set.all()
|
||||
for title in new_titles:
|
||||
try:
|
||||
is_valid_url(title.path, rev_page)
|
||||
except ValidationError:
|
||||
for old_title in old_titles:
|
||||
if old_title.language == title.language:
|
||||
title.slug = old_title.slug
|
||||
title.save()
|
||||
messages.error(request, _("Page reverted but slug stays the same because of url collisions."))
|
||||
return HttpResponse("ok")
|
||||
|
||||
@require_POST
|
||||
def redo(self, request, object_id):
|
||||
if not is_installed('reversion'):
|
||||
return HttpResponseBadRequest('django reversion not installed')
|
||||
from reversion.models import Revision
|
||||
import reversion
|
||||
from cms.utils.page_resolver import is_valid_url
|
||||
|
||||
page = get_object_or_404(Page, pk=object_id)
|
||||
old_titles = list(page.title_set.all())
|
||||
if not page.publisher_is_draft:
|
||||
page = page.publisher_draft
|
||||
if not page.has_change_permission(request):
|
||||
return HttpResponseForbidden(force_unicode(_("You do not have permission to change this page")))
|
||||
versions = reversion.get_for_object(page)
|
||||
if page.revision_id:
|
||||
current_revision = Revision.objects.get(pk=page.revision_id)
|
||||
else:
|
||||
try:
|
||||
current_version = versions[0]
|
||||
except IndexError:
|
||||
return HttpResponseBadRequest("no current revision found")
|
||||
current_revision = current_version.revision
|
||||
return HttpResponseForbidden(force_text(_("You do not have permission to change this page")))
|
||||
try:
|
||||
previous_version = versions.filter(revision__pk__gt=current_revision.pk).order_by('pk')[0]
|
||||
except IndexError:
|
||||
return HttpResponseBadRequest("no next revision found")
|
||||
next_revision = previous_version.revision
|
||||
# clear all plugins
|
||||
placeholders = page.placeholders.all()
|
||||
placeholder_ids = []
|
||||
for placeholder in placeholders:
|
||||
placeholder_ids.append(placeholder.pk)
|
||||
plugins = CMSPlugin.objects.filter(placeholder__in=placeholder_ids).order_by('-level')
|
||||
for plugin in plugins:
|
||||
plugin._no_reorder = True
|
||||
plugin.delete()
|
||||
# TODO: 3.1 remove the placeholder matching from below and just delete them
|
||||
#page.placeholders.all().delete()
|
||||
next_revision.revert(True)
|
||||
rev_page = get_object_or_404(Page, pk=page.pk)
|
||||
rev_page.revision_id = next_revision.pk
|
||||
rev_page.publisher_public_id = page.publisher_public_id
|
||||
rev_page.save()
|
||||
new_placeholders = rev_page.placeholders.all()
|
||||
slots = {}
|
||||
for new_ph in new_placeholders:
|
||||
if not new_ph.slot in slots:
|
||||
slots[new_ph.slot] = new_ph
|
||||
else:
|
||||
if new_ph in placeholder_ids:
|
||||
new_ph.delete()
|
||||
elif slots[new_ph.slot] in placeholder_ids:
|
||||
slots[new_ph.slot].delete()
|
||||
new_titles = rev_page.title_set.all()
|
||||
for title in new_titles:
|
||||
try:
|
||||
is_valid_url(title.path, rev_page)
|
||||
except ValidationError:
|
||||
for old_title in old_titles:
|
||||
if old_title.language == title.language:
|
||||
title.slug = old_title.slug
|
||||
title.save()
|
||||
messages.error(request, _("Page reverted but slug stays the same because of url collisions."))
|
||||
reverted, clean = page.redo()
|
||||
if not clean:
|
||||
messages.error(request, _("Page reverted but slug stays the same because of url collisions."))
|
||||
except IndexError as e:
|
||||
return HttpResponseBadRequest(e.message)
|
||||
|
||||
return HttpResponse("ok")
|
||||
|
||||
@require_POST
|
||||
@@ -914,11 +821,11 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
def change_template(self, request, object_id):
|
||||
page = get_object_or_404(Page, pk=object_id)
|
||||
if not page.has_change_permission(request):
|
||||
return HttpResponseForbidden(force_unicode(_("You do not have permission to change the template")))
|
||||
return HttpResponseForbidden(force_text(_("You do not have permission to change the template")))
|
||||
|
||||
to_template = request.POST.get("template", None)
|
||||
if to_template not in dict(get_cms_setting('TEMPLATES')):
|
||||
return HttpResponseBadRequest(force_unicode(_("Template not valid")))
|
||||
return HttpResponseBadRequest(force_text(_("Template not valid")))
|
||||
|
||||
page.template = to_template
|
||||
page.save()
|
||||
@@ -926,10 +833,10 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
message = _("Template changed to %s") % dict(get_cms_setting('TEMPLATES'))[to_template]
|
||||
self.cleanup_history(page)
|
||||
helpers.make_revision_with_plugins(page, request.user, message)
|
||||
return HttpResponse(force_unicode(_("The template was successfully changed")))
|
||||
return HttpResponse(force_text(_("The template was successfully changed")))
|
||||
|
||||
@require_POST
|
||||
@wrap_transaction
|
||||
@transaction.atomic
|
||||
def move_page(self, request, page_id, extra_context=None):
|
||||
"""
|
||||
Move the page to the requested target, at the given position
|
||||
@@ -949,7 +856,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
if not page.has_move_page_permission(request) or \
|
||||
not target.has_add_permission(request):
|
||||
return jsonify_request(
|
||||
HttpResponseForbidden(force_unicode(_("Error! You don't have permissions to move this page. Please reload the page"))))
|
||||
HttpResponseForbidden(force_text(_("Error! You don't have permissions to move this page. Please reload the page"))))
|
||||
# move page
|
||||
page.move_page(target, position)
|
||||
if is_installed('reversion'):
|
||||
@@ -988,24 +895,24 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
'page': page,
|
||||
'permission_set': permission_set,
|
||||
}
|
||||
return render_to_response('admin/cms/page/permissions.html', context)
|
||||
return render(request, 'admin/cms/page/permissions.html', context)
|
||||
|
||||
@require_POST
|
||||
@wrap_transaction
|
||||
@transaction.atomic
|
||||
def copy_language(self, request, page_id):
|
||||
with create_revision():
|
||||
source_language = request.POST.get('source_language')
|
||||
target_language = request.POST.get('target_language')
|
||||
page = Page.objects.get(pk=page_id)
|
||||
placeholders = page.placeholders.all()
|
||||
placeholders = page.get_placeholders()
|
||||
|
||||
if not target_language or not target_language in get_language_list():
|
||||
return HttpResponseBadRequest(force_unicode(_("Language must be set to a supported language!")))
|
||||
return HttpResponseBadRequest(force_text(_("Language must be set to a supported language!")))
|
||||
for placeholder in placeholders:
|
||||
plugins = list(
|
||||
placeholder.cmsplugin_set.filter(language=source_language).order_by('tree_id', 'level', 'position'))
|
||||
placeholder.cmsplugin_set.filter(language=source_language).order_by('path'))
|
||||
if not self.has_copy_plugin_permission(request, placeholder, placeholder, plugins):
|
||||
return HttpResponseForbidden(force_unicode(_('You do not have permission to copy these plugins.')))
|
||||
return HttpResponseForbidden(force_text(_('You do not have permission to copy these plugins.')))
|
||||
copy_plugins.copy_plugins_to(plugins, placeholder, target_language)
|
||||
if page and is_installed('reversion'):
|
||||
message = _(u"Copied plugins from %(source_language)s to %(target_language)s") % {
|
||||
@@ -1015,7 +922,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
return HttpResponse("ok")
|
||||
|
||||
@require_POST
|
||||
@wrap_transaction
|
||||
@transaction.atomic
|
||||
def copy_page(self, request, page_id, extra_context=None):
|
||||
"""
|
||||
Copy the page and all its plugins and descendants to the requested target, at the given position
|
||||
@@ -1034,11 +941,13 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
site = Site.objects.get(pk=site)
|
||||
except (ObjectDoesNotExist, AssertionError):
|
||||
return HttpResponse("error")
|
||||
#context.update({'error': _('Page could not been moved.')})
|
||||
else:
|
||||
try:
|
||||
permissions = request.GET.get('copy_permissions', False)
|
||||
if not permissions:
|
||||
permissions = request.POST.get('copy_permissions', False)
|
||||
kwargs = {
|
||||
'copy_permissions': request.REQUEST.get('copy_permissions', False),
|
||||
'copy_permissions': permissions,
|
||||
}
|
||||
page.copy_page(target, site, position, **kwargs)
|
||||
return jsonify_request(HttpResponse("ok"))
|
||||
@@ -1049,7 +958,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
return HttpResponseRedirect('../../')
|
||||
|
||||
@require_POST
|
||||
@wrap_transaction
|
||||
@transaction.atomic
|
||||
@create_revision()
|
||||
def publish_page(self, request, page_id, language):
|
||||
try:
|
||||
@@ -1060,13 +969,13 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
all_published = True
|
||||
if page:
|
||||
if not page.has_publish_permission(request):
|
||||
return HttpResponseForbidden(force_unicode(_("You do not have permission to publish this page")))
|
||||
return HttpResponseForbidden(force_text(_("You do not have permission to publish this page")))
|
||||
published = page.publish(language)
|
||||
if not published:
|
||||
all_published = False
|
||||
statics = request.GET.get('statics', '')
|
||||
if not statics and not page:
|
||||
return Http404("No page or stack found for publishing.")
|
||||
raise Http404("No page or stack found for publishing.")
|
||||
if statics:
|
||||
static_ids = statics .split(',')
|
||||
for pk in static_ids:
|
||||
@@ -1096,7 +1005,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
self.cleanup_history(page, publish=True)
|
||||
helpers.make_revision_with_plugins(page, request.user, PUBLISH_COMMENT)
|
||||
# create a new publish reversion
|
||||
if 'node' in request.REQUEST:
|
||||
if 'node' in request.GET or 'node' in request.POST:
|
||||
# if request comes from tree..
|
||||
return admin_utils.render_admin_menu_item(request, page)
|
||||
|
||||
@@ -1150,7 +1059,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
deleted.append(revision.pk)
|
||||
|
||||
@require_POST
|
||||
@wrap_transaction
|
||||
@transaction.atomic
|
||||
def unpublish(self, request, page_id, language):
|
||||
"""
|
||||
Publish or unpublish a language of a page
|
||||
@@ -1158,9 +1067,9 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
site = Site.objects.get_current()
|
||||
page = get_object_or_404(Page, pk=page_id)
|
||||
if not page.has_publish_permission(request):
|
||||
return HttpResponseForbidden(force_unicode(_("You do not have permission to unpublish this page")))
|
||||
return HttpResponseForbidden(force_text(_("You do not have permission to unpublish this page")))
|
||||
if not page.publisher_public_id:
|
||||
return HttpResponseForbidden(force_unicode(_("This page was never published")))
|
||||
return HttpResponseForbidden(force_text(_("This page was never published")))
|
||||
try:
|
||||
page.unpublish(language)
|
||||
message = _('The %(language)s page "%(page)s" was successfully unpublished') % {
|
||||
@@ -1186,25 +1095,24 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
return HttpResponseRedirect(path)
|
||||
|
||||
@require_POST
|
||||
@wrap_transaction
|
||||
@transaction.atomic
|
||||
def revert_page(self, request, page_id, language):
|
||||
page = get_object_or_404(Page, id=page_id)
|
||||
# ensure user has permissions to publish this page
|
||||
if not page.has_change_permission(request):
|
||||
return HttpResponseForbidden(force_unicode(_("You do not have permission to change this page")))
|
||||
return HttpResponseForbidden(force_text(_("You do not have permission to change this page")))
|
||||
|
||||
page.revert(language)
|
||||
|
||||
messages.info(request, _('The page "%s" was successfully reverted.') % page)
|
||||
|
||||
if 'node' in request.REQUEST:
|
||||
if 'node' in request.GET or 'node' in request.POST:
|
||||
# if request comes from tree..
|
||||
return admin_utils.render_admin_menu_item(request, page)
|
||||
|
||||
referer = request.META.get('HTTP_REFERER', '')
|
||||
path = '../../'
|
||||
if admin_reverse('index') not in referer:
|
||||
path = '%s?%s' % (referer.split('?')[0], get_cms_setting('CMS_TOOLBAR_URL__EDIT_OFF'))
|
||||
# TODO: This should never fail, but it may be a POF
|
||||
path = page.get_absolute_url(language=language)
|
||||
path = '%s?%s' % (path, get_cms_setting('CMS_TOOLBAR_URL__EDIT_OFF'))
|
||||
return HttpResponseRedirect(path)
|
||||
|
||||
@create_revision()
|
||||
@@ -1220,7 +1128,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
pluginopts = CMSPlugin._meta
|
||||
|
||||
try:
|
||||
obj = self.queryset(request).get(pk=unquote(object_id))
|
||||
obj = self.get_queryset(request).get(pk=unquote(object_id))
|
||||
except self.model.DoesNotExist:
|
||||
# Don't raise Http404 just yet, because we haven't checked
|
||||
# permissions yet. We don't want an unauthenticated user to be able
|
||||
@@ -1228,12 +1136,12 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
obj = None
|
||||
|
||||
if not self.has_delete_permission(request, obj):
|
||||
return HttpResponseForbidden(force_unicode(_("You do not have permission to change this page")))
|
||||
return HttpResponseForbidden(force_text(_("You do not have permission to change this page")))
|
||||
|
||||
if obj is None:
|
||||
raise Http404(
|
||||
_('%(name)s object with primary key %(key)r does not exist.') % {
|
||||
'name': force_unicode(opts.verbose_name),
|
||||
'name': force_text(opts.verbose_name),
|
||||
'key': escape(object_id)
|
||||
})
|
||||
|
||||
@@ -1249,16 +1157,29 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
'user': request.user,
|
||||
'using': using
|
||||
}
|
||||
deleted_objects, perms_needed = get_deleted_objects(
|
||||
[titleobj],
|
||||
titleopts,
|
||||
**kwargs
|
||||
)[:2]
|
||||
to_delete_plugins, perms_needed_plugins = get_deleted_objects(
|
||||
saved_plugins,
|
||||
pluginopts,
|
||||
**kwargs
|
||||
)[:2]
|
||||
|
||||
if DJANGO_1_7:
|
||||
deleted_objects, perms_needed = get_deleted_objects(
|
||||
[titleobj],
|
||||
titleopts,
|
||||
**kwargs
|
||||
)[:2]
|
||||
to_delete_plugins, perms_needed_plugins = get_deleted_objects(
|
||||
saved_plugins,
|
||||
pluginopts,
|
||||
**kwargs
|
||||
)[:2]
|
||||
else:
|
||||
deleted_objects, __, perms_needed = get_deleted_objects(
|
||||
[titleobj],
|
||||
titleopts,
|
||||
**kwargs
|
||||
)[:3]
|
||||
to_delete_plugins, __, perms_needed_plugins = get_deleted_objects(
|
||||
saved_plugins,
|
||||
pluginopts,
|
||||
**kwargs
|
||||
)[:3]
|
||||
|
||||
deleted_objects.append(to_delete_plugins)
|
||||
perms_needed = set(list(perms_needed) + list(perms_needed_plugins))
|
||||
@@ -1268,7 +1189,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
raise PermissionDenied
|
||||
|
||||
message = _('Title and plugins with language %(language)s was deleted') % {
|
||||
'language': force_unicode(get_language_object(language)['name'])
|
||||
'language': force_text(get_language_object(language)['name'])
|
||||
}
|
||||
self.log_change(request, titleobj, message)
|
||||
messages.info(request, message)
|
||||
@@ -1291,7 +1212,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
|
||||
context = {
|
||||
"title": _("Are you sure?"),
|
||||
"object_name": force_unicode(titleopts.verbose_name),
|
||||
"object_name": force_text(titleopts.verbose_name),
|
||||
"object": titleobj,
|
||||
"deleted_objects": deleted_objects,
|
||||
"perms_lacking": perms_needed,
|
||||
@@ -1300,12 +1221,12 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
"app_label": app_label,
|
||||
}
|
||||
context.update(extra_context or {})
|
||||
context_instance = RequestContext(request, current_app=self.admin_site.name)
|
||||
return render_to_response(self.delete_confirmation_template or [
|
||||
request.current_app = self.admin_site.name
|
||||
return render(request, self.delete_confirmation_template or [
|
||||
"admin/%s/%s/delete_confirmation.html" % (app_label, titleopts.object_name.lower()),
|
||||
"admin/%s/delete_confirmation.html" % app_label,
|
||||
"admin/delete_confirmation.html"
|
||||
], context, context_instance=context_instance)
|
||||
], context)
|
||||
|
||||
def preview_page(self, request, object_id, language):
|
||||
"""Redirecting preview function based on draft_id
|
||||
@@ -1331,7 +1252,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
page.toggle_in_navigation()
|
||||
language = request.GET.get('language') or get_language_from_request(request)
|
||||
return admin_utils.render_admin_menu_item(request, page, language=language)
|
||||
return HttpResponseForbidden(force_unicode(_("You do not have permission to change this page's in_navigation status")))
|
||||
return HttpResponseForbidden(force_text(_("You do not have permission to change this page's in_navigation status")))
|
||||
|
||||
def descendants(self, request, page_id, language):
|
||||
"""
|
||||
@@ -1362,10 +1283,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
|
||||
def resolve(self, request):
|
||||
if not request.user.is_staff:
|
||||
if DJANGO_1_4:
|
||||
return HttpResponse('/', mimetype='text/plain')
|
||||
else:
|
||||
return HttpResponse('/', content_type='text/plain')
|
||||
return HttpResponse('/', content_type='text/plain')
|
||||
obj = False
|
||||
url = False
|
||||
if request.session.get('cms_log_latest', False):
|
||||
@@ -1379,14 +1297,14 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
# This is a test if the object url can be retrieved
|
||||
# In case it can't, object it's not taken into account
|
||||
try:
|
||||
force_unicode(obj.get_absolute_url())
|
||||
force_text(obj.get_absolute_url())
|
||||
except:
|
||||
obj = None
|
||||
else:
|
||||
obj = None
|
||||
if not obj:
|
||||
pk = request.REQUEST.get('pk')
|
||||
full_model = request.REQUEST.get('model')
|
||||
pk = request.GET.get('pk', False) or request.POST.get('pk', False)
|
||||
full_model = request.GET.get('model') or request.POST.get('model', False)
|
||||
if pk and full_model:
|
||||
app_label, model = full_model.split('.')
|
||||
if pk and app_label:
|
||||
@@ -1396,11 +1314,11 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
except ctype.model_class().DoesNotExist:
|
||||
obj = None
|
||||
try:
|
||||
force_unicode(obj.get_absolute_url())
|
||||
force_text(obj.get_absolute_url())
|
||||
except:
|
||||
obj = None
|
||||
if obj:
|
||||
if not request.toolbar or not request.toolbar.edit_mode:
|
||||
if not getattr(request, 'toolbar', False) or not getattr(request.toolbar, 'edit_mode', False):
|
||||
if isinstance(obj, Page):
|
||||
if obj.get_public_object():
|
||||
url = obj.get_public_object().get_absolute_url()
|
||||
@@ -1414,7 +1332,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
else:
|
||||
url = obj.get_absolute_url()
|
||||
if url:
|
||||
return HttpResponse(force_unicode(url), content_type='text/plain')
|
||||
return HttpResponse(force_text(url), content_type='text/plain')
|
||||
return HttpResponse('', content_type='text/plain')
|
||||
|
||||
def lookup_allowed(self, key, *args, **kwargs):
|
||||
@@ -1436,7 +1354,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
|
||||
if not has_generic_permission(title.page.pk, request.user, "change",
|
||||
title.page.site.pk):
|
||||
return HttpResponseForbidden(force_unicode(_("You do not have permission to edit this page")))
|
||||
return HttpResponseForbidden(force_text(_("You do not have permission to edit this page")))
|
||||
|
||||
class PageTitleForm(django.forms.ModelForm):
|
||||
"""
|
||||
@@ -1476,10 +1394,10 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
context.update({
|
||||
'cancel': True,
|
||||
})
|
||||
return render_to_response('admin/cms/page/plugin/confirm_form.html', context, RequestContext(request))
|
||||
return render(request, 'admin/cms/page/plugin/confirm_form.html', context)
|
||||
if not cancel_clicked and request.method == 'POST' and saved_successfully:
|
||||
return render_to_response('admin/cms/page/plugin/confirm_form.html', context, RequestContext(request))
|
||||
return render_to_response('admin/cms/page/plugin/change_form.html', context, RequestContext(request))
|
||||
return render(request, 'admin/cms/page/plugin/confirm_form.html', context)
|
||||
return render(request, 'admin/cms/page/plugin/change_form.html', context)
|
||||
|
||||
def get_published_pagelist(self, *args, **kwargs):
|
||||
"""
|
||||
@@ -1507,12 +1425,7 @@ class PageAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
'redirect_url': page.get_absolute_url(language=language_code)
|
||||
}
|
||||
)
|
||||
|
||||
if DJANGO_1_4:
|
||||
return HttpResponse(json.dumps(results), mimetype='application/json')
|
||||
else:
|
||||
return HttpResponse(json.dumps(results), content_type='application/json')
|
||||
|
||||
return HttpResponse(json.dumps(results), content_type='application/json')
|
||||
else:
|
||||
return HttpResponseForbidden()
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
from copy import deepcopy
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin import site
|
||||
from django.contrib.auth import get_user_model, get_permission_codename
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.db import OperationalError
|
||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||
|
||||
from cms.admin.forms import GlobalPagePermissionAdminForm, PagePermissionInlineAdminForm, ViewRestrictionInlineAdminForm
|
||||
from cms.exceptions import NoPermissionsException
|
||||
from cms.models import Page, PagePermission, GlobalPagePermission, PageUser
|
||||
from cms.utils.compat.dj import get_user_model
|
||||
from cms.utils.conf import get_cms_setting
|
||||
from cms.utils.helpers import classproperty
|
||||
from cms.utils.permissions import get_user_permission_level
|
||||
@@ -37,14 +38,23 @@ class PagePermissionInlineAdmin(TabularInline):
|
||||
def raw_id_fields(cls):
|
||||
# Dynamically set raw_id_fields based on settings
|
||||
threshold = get_cms_setting('RAW_ID_USERS')
|
||||
if threshold and get_user_model().objects.count() > threshold:
|
||||
return ['user']
|
||||
return []
|
||||
|
||||
def queryset(self, request):
|
||||
# Given a fresh django-cms install and a django settings with the
|
||||
# CMS_RAW_ID_USERS = CMS_PERMISSION = True
|
||||
# django throws an OperationalError when running
|
||||
# ./manage migrate
|
||||
# because auth_user doesn't exists yet
|
||||
try:
|
||||
threshold = threshold and get_user_model().objects.count() > threshold
|
||||
except OperationalError:
|
||||
threshold = False
|
||||
|
||||
return ['user'] if threshold else []
|
||||
|
||||
def get_queryset(self, request):
|
||||
"""
|
||||
Queryset change, so user with global change permissions can see
|
||||
all permissions. Otherwise can user see only permissions for
|
||||
all permissions. Otherwise can user see only permissions for
|
||||
peoples which are under him (he can't see his permissions, because
|
||||
this will lead to violation, when he can add more power to itself)
|
||||
"""
|
||||
@@ -77,7 +87,7 @@ class PagePermissionInlineAdmin(TabularInline):
|
||||
exclude.append('can_move_page')
|
||||
formset_cls = super(PagePermissionInlineAdmin, self
|
||||
).get_formset(request, obj=None, exclude=exclude, **kwargs)
|
||||
qs = self.queryset(request)
|
||||
qs = self.get_queryset(request)
|
||||
if obj is not None:
|
||||
qs = qs.filter(page=obj)
|
||||
formset_cls._queryset = qs
|
||||
@@ -102,13 +112,13 @@ class ViewRestrictionInlineAdmin(PagePermissionInlineAdmin):
|
||||
flag, he can't change assign can_publish permissions.
|
||||
"""
|
||||
formset_cls = super(PagePermissionInlineAdmin, self).get_formset(request, obj, **kwargs)
|
||||
qs = self.queryset(request)
|
||||
qs = self.get_queryset(request)
|
||||
if obj is not None:
|
||||
qs = qs.filter(page=obj)
|
||||
formset_cls._queryset = qs
|
||||
return formset_cls
|
||||
|
||||
def queryset(self, request):
|
||||
def get_queryset(self, request):
|
||||
"""
|
||||
Returns a QuerySet of all model instances that can be edited by the
|
||||
admin site. This is used by changelist_view.
|
||||
@@ -145,18 +155,18 @@ class GenericCmsPermissionAdmin(object):
|
||||
"""
|
||||
fieldsets = deepcopy(self.fieldsets)
|
||||
perm_models = (
|
||||
(Page, _('Page permissions')),
|
||||
(PageUser, _('User & Group permissions')),
|
||||
(PagePermission, _('Page permissions management')),
|
||||
(Page, ugettext('Page permissions')),
|
||||
(PageUser, ugettext('User & Group permissions')),
|
||||
(PagePermission, ugettext('Page permissions management')),
|
||||
)
|
||||
for i, perm_model in enumerate(perm_models):
|
||||
model, title = perm_model
|
||||
opts, fields = model._meta, []
|
||||
name = model.__name__.lower()
|
||||
for t in ('add', 'change', 'delete'):
|
||||
fn = getattr(opts, 'get_%s_permission' % t)
|
||||
if request.user.has_perm(opts.app_label + '.' + fn()):
|
||||
fields.append('can_%s_%s' % (t, name))
|
||||
for key in ('add', 'change', 'delete'):
|
||||
perm_code = '%s.%s' % (opts.app_label, get_permission_codename(key, opts))
|
||||
if request.user.has_perm(perm_code):
|
||||
fields.append('can_%s_%s' % (key, name))
|
||||
if fields:
|
||||
fieldsets.insert(2 + i, (title, {'fields': (fields,)}))
|
||||
return fieldsets
|
||||
|
||||
+132
-159
@@ -1,39 +1,51 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from cms.models.placeholderpluginmodel import PlaceholderReference
|
||||
from cms.utils.urlutils import admin_reverse
|
||||
from django.contrib.admin.helpers import AdminForm
|
||||
from django.utils.decorators import method_decorator
|
||||
import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls import url
|
||||
from django.contrib.admin.helpers import AdminForm
|
||||
try:
|
||||
from django.contrib.admin.utils import get_deleted_objects
|
||||
except ImportError:
|
||||
from django.contrib.admin.util import get_deleted_objects
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db import router, transaction
|
||||
from django.http import (
|
||||
HttpResponse,
|
||||
HttpResponseBadRequest,
|
||||
HttpResponseForbidden,
|
||||
HttpResponseNotFound,
|
||||
HttpResponseRedirect,
|
||||
)
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.template.defaultfilters import force_escape, escapejs
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.decorators.clickjacking import xframe_options_sameorigin
|
||||
from django.views.decorators.http import require_POST
|
||||
|
||||
from cms.constants import PLUGIN_COPY_ACTION, PLUGIN_MOVE_ACTION
|
||||
from cms.exceptions import PluginLimitReached
|
||||
from cms.models.placeholdermodel import Placeholder
|
||||
from cms.models.placeholderpluginmodel import PlaceholderReference
|
||||
from cms.models.pluginmodel import CMSPlugin
|
||||
from cms.plugin_pool import plugin_pool
|
||||
from cms.utils import get_cms_setting
|
||||
from cms.utils.compat.dj import force_unicode
|
||||
from cms.utils.plugins import requires_reload, has_reached_plugin_limit
|
||||
from django.contrib.admin import ModelAdmin
|
||||
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotFound
|
||||
from django.shortcuts import render_to_response, get_object_or_404
|
||||
from django.template import RequestContext
|
||||
from django.template.defaultfilters import force_escape, escapejs
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.conf import settings
|
||||
from django.views.decorators.http import require_POST
|
||||
import warnings
|
||||
from django.template.response import TemplateResponse
|
||||
|
||||
from django.contrib.admin.util import get_deleted_objects
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db import router
|
||||
from django.http import HttpResponseRedirect
|
||||
|
||||
from cms.utils import copy_plugins, permissions, get_language_from_request
|
||||
from cms.utils.compat import DJANGO_1_4
|
||||
from cms.utils import (
|
||||
copy_plugins,
|
||||
get_cms_setting,
|
||||
get_language_from_request,
|
||||
permissions,
|
||||
)
|
||||
from cms.utils.compat import DJANGO_1_7
|
||||
from cms.utils.i18n import get_language_list
|
||||
from cms.utils.transaction import wrap_transaction
|
||||
from cms.utils.plugins import (
|
||||
requires_reload,
|
||||
has_reached_plugin_limit,
|
||||
reorder_plugins
|
||||
)
|
||||
from cms.utils.urlutils import admin_reverse
|
||||
|
||||
|
||||
class FrontendEditableAdminMixin(object):
|
||||
@@ -43,16 +55,12 @@ class FrontendEditableAdminMixin(object):
|
||||
"""
|
||||
Register the url for the single field edit view
|
||||
"""
|
||||
from django.conf.urls import patterns, url
|
||||
from cms.urls import SLUG_REGEXP
|
||||
|
||||
info = "%s_%s" % (self.model._meta.app_label, self.model._meta.module_name)
|
||||
info = "%s_%s" % (self.model._meta.app_label, self.model._meta.model_name)
|
||||
pat = lambda regex, fn: url(regex, self.admin_site.admin_view(fn), name='%s_%s' % (info, fn.__name__))
|
||||
|
||||
url_patterns = patterns(
|
||||
'',
|
||||
url_patterns = [
|
||||
pat(r'edit-field/(%s)/([a-z\-]+)/$' % SLUG_REGEXP, self.edit_field),
|
||||
)
|
||||
]
|
||||
return url_patterns + super(FrontendEditableAdminMixin, self).get_urls()
|
||||
|
||||
def _get_object_for_single_field(self, object_id, language):
|
||||
@@ -73,16 +81,16 @@ class FrontendEditableAdminMixin(object):
|
||||
if not fields:
|
||||
context = {
|
||||
'opts': opts,
|
||||
'message': force_unicode(_("Field %s not found")) % raw_fields
|
||||
'message': force_text(_("Field %s not found")) % raw_fields
|
||||
}
|
||||
return render_to_response('admin/cms/page/plugin/error_form.html', context, RequestContext(request))
|
||||
return render(request, 'admin/cms/page/plugin/error_form.html', context)
|
||||
if not request.user.has_perm("{0}.change_{1}".format(self.model._meta.app_label,
|
||||
self.model._meta.module_name)):
|
||||
self.model._meta.model_name)):
|
||||
context = {
|
||||
'opts': opts,
|
||||
'message': force_unicode(_("You do not have permission to edit this item"))
|
||||
'message': force_text(_("You do not have permission to edit this item"))
|
||||
}
|
||||
return render_to_response('admin/cms/page/plugin/error_form.html', context, RequestContext(request))
|
||||
return render(request, 'admin/cms/page/plugin/error_form.html', context)
|
||||
# Dinamically creates the form class with only `field_name` field
|
||||
# enabled
|
||||
form_class = self.get_form(request, obj, fields=fields)
|
||||
@@ -116,10 +124,10 @@ class FrontendEditableAdminMixin(object):
|
||||
context.update({
|
||||
'cancel': True,
|
||||
})
|
||||
return render_to_response('admin/cms/page/plugin/confirm_form.html', context, RequestContext(request))
|
||||
return render(request, 'admin/cms/page/plugin/confirm_form.html', context)
|
||||
if not cancel_clicked and request.method == 'POST' and saved_successfully:
|
||||
return render_to_response('admin/cms/page/plugin/confirm_form.html', context, RequestContext(request))
|
||||
return render_to_response('admin/cms/page/plugin/change_form.html', context, RequestContext(request))
|
||||
return render(request, 'admin/cms/page/plugin/confirm_form.html', context)
|
||||
return render(request, 'admin/cms/page/plugin/change_form.html', context)
|
||||
|
||||
|
||||
class PlaceholderAdminMixin(object):
|
||||
@@ -127,21 +135,17 @@ class PlaceholderAdminMixin(object):
|
||||
"""
|
||||
Register the plugin specific urls (add/edit/copy/remove/move)
|
||||
"""
|
||||
from django.conf.urls import patterns, url
|
||||
from cms.urls import SLUG_REGEXP
|
||||
|
||||
info = "%s_%s" % (self.model._meta.app_label, self.model._meta.module_name)
|
||||
info = "%s_%s" % (self.model._meta.app_label, self.model._meta.model_name)
|
||||
pat = lambda regex, fn: url(regex, self.admin_site.admin_view(fn), name='%s_%s' % (info, fn.__name__))
|
||||
|
||||
url_patterns = patterns(
|
||||
'',
|
||||
url_patterns = [
|
||||
pat(r'copy-plugins/$', self.copy_plugins),
|
||||
pat(r'add-plugin/$', self.add_plugin),
|
||||
pat(r'edit-plugin/(%s)/$' % SLUG_REGEXP, self.edit_plugin),
|
||||
pat(r'delete-plugin/(%s)/$' % SLUG_REGEXP, self.delete_plugin),
|
||||
pat(r'clear-placeholder/(%s)/$' % SLUG_REGEXP, self.clear_placeholder),
|
||||
pat(r'move-plugin/$', self.move_plugin),
|
||||
)
|
||||
]
|
||||
return url_patterns + super(PlaceholderAdminMixin, self).get_urls()
|
||||
|
||||
def has_add_plugin_permission(self, request, placeholder, plugin_type):
|
||||
@@ -219,20 +223,14 @@ class PlaceholderAdminMixin(object):
|
||||
- plugin_language
|
||||
- plugin_parent (optional)
|
||||
"""
|
||||
plugin_type = request.POST['plugin_type']
|
||||
|
||||
placeholder_id = request.POST.get('placeholder_id', None)
|
||||
parent_id = request.POST.get('parent_id', None)
|
||||
if parent_id:
|
||||
warnings.warn("parent_id is deprecated and will be removed in 3.1, use plugin_parent instead",
|
||||
DeprecationWarning)
|
||||
if not parent_id:
|
||||
parent_id = request.POST.get('plugin_parent', None)
|
||||
placeholder = get_object_or_404(Placeholder, pk=placeholder_id)
|
||||
if not self.has_add_plugin_permission(request, placeholder, plugin_type):
|
||||
return HttpResponseForbidden(force_unicode(_('You do not have permission to add a plugin')))
|
||||
parent = None
|
||||
plugin_type = request.POST['plugin_type']
|
||||
placeholder_id = request.POST.get('placeholder_id', None)
|
||||
placeholder = get_object_or_404(Placeholder, pk=placeholder_id)
|
||||
parent_id = request.POST.get('plugin_parent', None)
|
||||
language = request.POST.get('plugin_language') or get_language_from_request(request)
|
||||
if not self.has_add_plugin_permission(request, placeholder, plugin_type):
|
||||
return HttpResponseForbidden(force_text(_('You do not have permission to add a plugin')))
|
||||
try:
|
||||
has_reached_plugin_limit(placeholder, plugin_type, language,
|
||||
template=self.get_placeholder_template(request, placeholder))
|
||||
@@ -240,7 +238,6 @@ class PlaceholderAdminMixin(object):
|
||||
return HttpResponseBadRequest(er)
|
||||
# page add-plugin
|
||||
if not parent_id:
|
||||
|
||||
position = request.POST.get('plugin_order',
|
||||
CMSPlugin.objects.filter(language=language, placeholder=placeholder).count())
|
||||
# in-plugin add-plugin
|
||||
@@ -254,35 +251,32 @@ class PlaceholderAdminMixin(object):
|
||||
# Sanity check to make sure we're not getting bogus values from JavaScript:
|
||||
if settings.USE_I18N:
|
||||
if not language or not language in [lang[0] for lang in settings.LANGUAGES]:
|
||||
return HttpResponseBadRequest(force_unicode(_("Language must be set to a supported language!")))
|
||||
return HttpResponseBadRequest(force_text(_("Language must be set to a supported language!")))
|
||||
if parent and parent.language != language:
|
||||
return HttpResponseBadRequest(force_unicode(_("Parent plugin language must be same as language!")))
|
||||
return HttpResponseBadRequest(force_text(_("Parent plugin language must be same as language!")))
|
||||
else:
|
||||
language = settings.LANGUAGE_CODE
|
||||
plugin = CMSPlugin(language=language, plugin_type=plugin_type, position=position, placeholder=placeholder)
|
||||
|
||||
if parent:
|
||||
plugin.position = CMSPlugin.objects.filter(parent=parent).count()
|
||||
plugin.insert_at(parent, position='last-child', save=False)
|
||||
plugin.parent_id = parent.pk
|
||||
plugin.save()
|
||||
self.post_add_plugin(request, placeholder, plugin)
|
||||
response = {
|
||||
'url': force_unicode(
|
||||
admin_reverse("%s_%s_edit_plugin" % (self.model._meta.app_label, self.model._meta.module_name),
|
||||
'url': force_text(
|
||||
admin_reverse("%s_%s_edit_plugin" % (self.model._meta.app_label, self.model._meta.model_name),
|
||||
args=[plugin.pk])),
|
||||
'delete': force_unicode(
|
||||
admin_reverse("%s_%s_delete_plugin" % (self.model._meta.app_label, self.model._meta.module_name),
|
||||
'delete': force_text(
|
||||
admin_reverse("%s_%s_delete_plugin" % (self.model._meta.app_label, self.model._meta.model_name),
|
||||
args=[plugin.pk])),
|
||||
'breadcrumb': plugin.get_breadcrumb(),
|
||||
}
|
||||
if DJANGO_1_4:
|
||||
return HttpResponse(json.dumps(response), mimetype='application/json')
|
||||
else:
|
||||
return HttpResponse(json.dumps(response), content_type='application/json')
|
||||
return HttpResponse(json.dumps(response), content_type='application/json')
|
||||
|
||||
@method_decorator(require_POST)
|
||||
@xframe_options_sameorigin
|
||||
@wrap_transaction
|
||||
@transaction.atomic
|
||||
def copy_plugins(self, request):
|
||||
"""
|
||||
POST request should have the following data:
|
||||
@@ -303,7 +297,7 @@ class PlaceholderAdminMixin(object):
|
||||
source_placeholder = get_object_or_404(Placeholder, pk=source_placeholder_id)
|
||||
target_placeholder = get_object_or_404(Placeholder, pk=target_placeholder_id)
|
||||
if not target_language or not target_language in get_language_list():
|
||||
return HttpResponseBadRequest(force_unicode(_("Language must be set to a supported language!")))
|
||||
return HttpResponseBadRequest(force_text(_("Language must be set to a supported language!")))
|
||||
if source_plugin_id:
|
||||
source_plugin = get_object_or_404(CMSPlugin, pk=source_plugin_id)
|
||||
reload_required = requires_reload(PLUGIN_COPY_ACTION, [source_plugin])
|
||||
@@ -313,16 +307,16 @@ class PlaceholderAdminMixin(object):
|
||||
plugins = inst.placeholder_ref.get_plugins_list()
|
||||
else:
|
||||
plugins = list(
|
||||
source_placeholder.cmsplugin_set.filter(tree_id=source_plugin.tree_id, lft__gte=source_plugin.lft,
|
||||
rght__lte=source_plugin.rght).order_by('tree_id', 'level',
|
||||
'position'))
|
||||
source_placeholder.cmsplugin_set.filter(
|
||||
path__startswith=source_plugin.path,
|
||||
depth__gte=source_plugin.depth).order_by('path')
|
||||
)
|
||||
else:
|
||||
plugins = list(
|
||||
source_placeholder.cmsplugin_set.filter(language=source_language).order_by('tree_id', 'level',
|
||||
'position'))
|
||||
source_placeholder.cmsplugin_set.filter(language=source_language).order_by('path'))
|
||||
reload_required = requires_reload(PLUGIN_COPY_ACTION, plugins)
|
||||
if not self.has_copy_plugin_permission(request, source_placeholder, target_placeholder, plugins):
|
||||
return HttpResponseForbidden(force_unicode(_('You do not have permission to copy these plugins.')))
|
||||
return HttpResponseForbidden(force_text(_('You do not have permission to copy these plugins.')))
|
||||
if target_placeholder.pk == request.toolbar.clipboard.pk and not source_plugin_id and not target_plugin_id:
|
||||
# if we copy a whole placeholder to the clipboard create PlaceholderReference plugin instead and fill it
|
||||
# the content of the source_placeholder.
|
||||
@@ -336,43 +330,42 @@ class PlaceholderAdminMixin(object):
|
||||
else:
|
||||
copy_plugins.copy_plugins_to(plugins, target_placeholder, target_language, target_plugin_id)
|
||||
plugin_list = CMSPlugin.objects.filter(language=target_language, placeholder=target_placeholder).order_by(
|
||||
'tree_id', 'level', 'position')
|
||||
'path')
|
||||
reduced_list = []
|
||||
for plugin in plugin_list:
|
||||
reduced_list.append(
|
||||
{
|
||||
'id': plugin.pk, 'type': plugin.plugin_type, 'parent': plugin.parent_id,
|
||||
'position': plugin.position, 'desc': force_unicode(plugin.get_short_description()),
|
||||
'position': plugin.position, 'desc': force_text(plugin.get_short_description()),
|
||||
'language': plugin.language, 'placeholder_id': plugin.placeholder_id
|
||||
}
|
||||
)
|
||||
self.post_copy_plugins(request, source_placeholder, target_placeholder, plugins)
|
||||
json_response = {'plugin_list': reduced_list, 'reload': reload_required}
|
||||
if DJANGO_1_4:
|
||||
return HttpResponse(json.dumps(json_response), mimetype='application/json')
|
||||
else:
|
||||
return HttpResponse(json.dumps(json_response), content_type='application/json')
|
||||
return HttpResponse(json.dumps(json_response), content_type='application/json')
|
||||
|
||||
@xframe_options_sameorigin
|
||||
def edit_plugin(self, request, plugin_id):
|
||||
try:
|
||||
plugin_id = int(plugin_id)
|
||||
except ValueError:
|
||||
return HttpResponseNotFound(force_unicode(_("Plugin not found")))
|
||||
return HttpResponseNotFound(force_text(_("Plugin not found")))
|
||||
cms_plugin = get_object_or_404(CMSPlugin.objects.select_related('placeholder'), pk=plugin_id)
|
||||
|
||||
instance, plugin_admin = cms_plugin.get_plugin_instance(self.admin_site)
|
||||
if not self.has_change_plugin_permission(request, cms_plugin):
|
||||
return HttpResponseForbidden(force_unicode(_("You do not have permission to edit this plugin")))
|
||||
return HttpResponseForbidden(force_text(_("You do not have permission to edit this plugin")))
|
||||
plugin_admin.cms_plugin_instance = cms_plugin
|
||||
try:
|
||||
plugin_admin.placeholder = cms_plugin.placeholder
|
||||
except Placeholder.DoesNotExist:
|
||||
pass
|
||||
if request.method == "POST":
|
||||
# set the continue flag, otherwise will plugin_admin make redirect to list
|
||||
# view, which actually doesn't exists
|
||||
request.POST['_continue'] = True
|
||||
# set the continue flag, otherwise plugin_admin will make redirect
|
||||
# to list view, which actually doesn't exists
|
||||
mutable_post = request.POST.copy()
|
||||
mutable_post['_continue'] = True
|
||||
request.POST = mutable_post
|
||||
if request.POST.get("_cancel", False):
|
||||
# cancel button was clicked
|
||||
context = {
|
||||
@@ -387,15 +380,15 @@ class PlaceholderAdminMixin(object):
|
||||
}
|
||||
instance = cms_plugin.get_plugin_instance()[0]
|
||||
if instance:
|
||||
context['name'] = force_unicode(instance)
|
||||
context['name'] = force_text(instance)
|
||||
else:
|
||||
# cancelled before any content was added to plugin
|
||||
cms_plugin.delete()
|
||||
context.update({
|
||||
"deleted": True,
|
||||
'name': force_unicode(cms_plugin),
|
||||
'name': force_text(cms_plugin),
|
||||
})
|
||||
return render_to_response('admin/cms/page/plugin/confirm_form.html', context, RequestContext(request))
|
||||
return render(request, 'admin/cms/page/plugin/confirm_form.html', context)
|
||||
|
||||
if not instance:
|
||||
# instance doesn't exist, call add view
|
||||
@@ -413,13 +406,13 @@ class PlaceholderAdminMixin(object):
|
||||
'CMS_MEDIA_URL': get_cms_setting('MEDIA_URL'),
|
||||
'plugin': saved_object,
|
||||
'is_popup': True,
|
||||
'name': force_unicode(saved_object),
|
||||
'name': force_text(saved_object),
|
||||
"type": saved_object.get_plugin_name(),
|
||||
'plugin_id': plugin_id,
|
||||
'icon': force_escape(saved_object.get_instance_icon_src()),
|
||||
'alt': force_escape(saved_object.get_instance_icon_alt()),
|
||||
}
|
||||
return render_to_response('admin/cms/page/plugin/confirm_form.html', context, RequestContext(request))
|
||||
return render(request, 'admin/cms/page/plugin/confirm_form.html', context)
|
||||
return response
|
||||
|
||||
@method_decorator(require_POST)
|
||||
@@ -446,79 +439,68 @@ class PlaceholderAdminMixin(object):
|
||||
language = plugin.language
|
||||
order = request.POST.getlist("plugin_order[]")
|
||||
if not self.has_move_plugin_permission(request, plugin, placeholder):
|
||||
return HttpResponseForbidden(force_unicode(_("You have no permission to move this plugin")))
|
||||
if plugin.parent_id != parent_id:
|
||||
if parent_id:
|
||||
parent = CMSPlugin.objects.get(pk=parent_id)
|
||||
if parent.placeholder_id != placeholder.pk:
|
||||
return HttpResponseBadRequest(force_unicode('parent must be in the same placeholder'))
|
||||
if parent.language != language:
|
||||
return HttpResponseBadRequest(force_unicode('parent must be in the same language as plugin_language'))
|
||||
else:
|
||||
parent = None
|
||||
plugin.move_to(parent, position='last-child')
|
||||
return HttpResponseForbidden(force_text(_("You have no permission to move this plugin")))
|
||||
if not placeholder == source_placeholder:
|
||||
try:
|
||||
template = self.get_placeholder_template(request, placeholder)
|
||||
has_reached_plugin_limit(placeholder, plugin.plugin_type, plugin.language, template=template)
|
||||
except PluginLimitReached as er:
|
||||
return HttpResponseBadRequest(er)
|
||||
|
||||
plugin.save()
|
||||
for child in plugin.get_descendants(include_self=True):
|
||||
if parent_id:
|
||||
if plugin.parent_id != parent_id:
|
||||
parent = CMSPlugin.objects.get(pk=parent_id)
|
||||
if parent.placeholder_id != placeholder.pk:
|
||||
return HttpResponseBadRequest(force_text('parent must be in the same placeholder'))
|
||||
if parent.language != language:
|
||||
return HttpResponseBadRequest(force_text('parent must be in the same language as plugin_language'))
|
||||
plugin.parent_id = parent.pk
|
||||
plugin.save()
|
||||
plugin = plugin.move(parent, pos='last-child')
|
||||
else:
|
||||
sibling = CMSPlugin.get_last_root_node()
|
||||
plugin.parent_id = None
|
||||
plugin.save()
|
||||
plugin = plugin.move(sibling, pos='right')
|
||||
for child in [plugin] + list(plugin.get_descendants()):
|
||||
child.placeholder = placeholder
|
||||
child.language = language
|
||||
child.save()
|
||||
plugins = CMSPlugin.objects.filter(parent=parent_id, placeholder=placeholder, language=language).order_by('position')
|
||||
x = 0
|
||||
for level_plugin in plugins:
|
||||
if order:
|
||||
x = 0
|
||||
found = False
|
||||
for pk in order:
|
||||
if level_plugin.pk == int(pk):
|
||||
level_plugin.position = x
|
||||
level_plugin.save()
|
||||
found = True
|
||||
break
|
||||
x += 1
|
||||
if not found:
|
||||
return HttpResponseBadRequest('order parameter did not have all plugins of the same level in it')
|
||||
else:
|
||||
level_plugin.position = x
|
||||
level_plugin.save()
|
||||
x += 1
|
||||
plugins = reorder_plugins(placeholder, parent_id, language, order)
|
||||
if not plugins:
|
||||
return HttpResponseBadRequest('order parameter did not have all plugins of the same level in it')
|
||||
|
||||
self.post_move_plugin(request, source_placeholder, placeholder, plugin)
|
||||
json_response = {'reload': requires_reload(PLUGIN_MOVE_ACTION, [plugin])}
|
||||
if DJANGO_1_4:
|
||||
return HttpResponse(json.dumps(json_response), mimetype='application/json')
|
||||
else:
|
||||
return HttpResponse(json.dumps(json_response), content_type='application/json')
|
||||
return HttpResponse(json.dumps(json_response), content_type='application/json')
|
||||
|
||||
@xframe_options_sameorigin
|
||||
def delete_plugin(self, request, plugin_id):
|
||||
plugin = get_object_or_404(CMSPlugin.objects.select_related('placeholder'), pk=plugin_id)
|
||||
if not self.has_delete_plugin_permission(request, plugin):
|
||||
return HttpResponseForbidden(force_unicode(_("You do not have permission to delete this plugin")))
|
||||
return HttpResponseForbidden(force_text(_("You do not have permission to delete this plugin")))
|
||||
plugin_cms_class = plugin.get_plugin_class()
|
||||
plugin_class = plugin_cms_class.model
|
||||
opts = plugin_class._meta
|
||||
using = router.db_for_write(plugin_class)
|
||||
app_label = opts.app_label
|
||||
(deleted_objects, perms_needed, protected) = get_deleted_objects(
|
||||
[plugin], opts, request.user, self.admin_site, using)
|
||||
if DJANGO_1_7:
|
||||
deleted_objects, perms_needed, protected = get_deleted_objects(
|
||||
[plugin], opts, request.user, self.admin_site, using)
|
||||
else:
|
||||
deleted_objects, __, perms_needed, protected = get_deleted_objects(
|
||||
[plugin], opts, request.user, self.admin_site, using)
|
||||
|
||||
if request.POST: # The user has already confirmed the deletion.
|
||||
if perms_needed:
|
||||
raise PermissionDenied(_("You do not have permission to delete this plugin"))
|
||||
obj_display = force_unicode(plugin)
|
||||
obj_display = force_text(plugin)
|
||||
self.log_deletion(request, plugin, obj_display)
|
||||
plugin.delete()
|
||||
self.message_user(request, _('The %(name)s plugin "%(obj)s" was deleted successfully.') % {
|
||||
'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj_display)})
|
||||
'name': force_text(opts.verbose_name), 'obj': force_text(obj_display)})
|
||||
self.post_delete_plugin(request, plugin)
|
||||
return HttpResponseRedirect(admin_reverse('index', current_app=self.admin_site.name))
|
||||
plugin_name = force_unicode(plugin_pool.get_plugin(plugin.plugin_type).name)
|
||||
plugin_name = force_text(plugin_pool.get_plugin(plugin.plugin_type).name)
|
||||
if perms_needed or protected:
|
||||
title = _("Cannot delete %(name)s") % {"name": plugin_name}
|
||||
else:
|
||||
@@ -540,22 +522,27 @@ class PlaceholderAdminMixin(object):
|
||||
def clear_placeholder(self, request, placeholder_id):
|
||||
placeholder = get_object_or_404(Placeholder, pk=placeholder_id)
|
||||
if not self.has_clear_placeholder_permission(request, placeholder):
|
||||
return HttpResponseForbidden(force_unicode(_("You do not have permission to clear this placeholder")))
|
||||
return HttpResponseForbidden(force_text(_("You do not have permission to clear this placeholder")))
|
||||
language = request.GET.get('language', None)
|
||||
plugins = placeholder.get_plugins(language)
|
||||
opts = Placeholder._meta
|
||||
using = router.db_for_write(Placeholder)
|
||||
app_label = opts.app_label
|
||||
(deleted_objects, perms_needed, protected) = get_deleted_objects(
|
||||
plugins, opts, request.user, self.admin_site, using)
|
||||
obj_display = force_unicode(placeholder)
|
||||
if DJANGO_1_7:
|
||||
deleted_objects, perms_needed, protected = get_deleted_objects(
|
||||
plugins, opts, request.user, self.admin_site, using)
|
||||
else:
|
||||
deleted_objects, __, perms_needed, protected = get_deleted_objects(
|
||||
plugins, opts, request.user, self.admin_site, using)
|
||||
|
||||
obj_display = force_text(placeholder)
|
||||
if request.POST: # The user has already confirmed the deletion.
|
||||
if perms_needed:
|
||||
return HttpResponseForbidden(force_unicode(_("You do not have permission to clear this placeholder")))
|
||||
return HttpResponseForbidden(force_text(_("You do not have permission to clear this placeholder")))
|
||||
self.log_deletion(request, placeholder, obj_display)
|
||||
placeholder.clear(language)
|
||||
self.message_user(request, _('The placeholder "%(obj)s" was cleared successfully.') % {
|
||||
'obj': force_unicode(obj_display)})
|
||||
'obj': force_text(obj_display)})
|
||||
self.post_clear_placeholder(request, placeholder)
|
||||
return HttpResponseRedirect(admin_reverse('index', current_app=self.admin_site.name))
|
||||
if perms_needed or protected:
|
||||
@@ -574,17 +561,3 @@ class PlaceholderAdminMixin(object):
|
||||
}
|
||||
return TemplateResponse(request, "admin/cms/page/plugin/delete_confirmation.html", context,
|
||||
current_app=self.admin_site.name)
|
||||
|
||||
|
||||
class PlaceholderAdmin(PlaceholderAdminMixin, ModelAdmin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
warnings.warn("Class PlaceholderAdmin is deprecated and will be removed in 3.1. "
|
||||
"Instead, combine PlaceholderAdminMixin with admin.ModelAdmin.", DeprecationWarning)
|
||||
super(PlaceholderAdmin, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class FrontendEditableAdmin(FrontendEditableAdminMixin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
warnings.warn("Class FrontendEditableAdmin is deprecated and will be removed in 3.1. "
|
||||
"Instead, use FrontendEditableAdminMixin.", DeprecationWarning)
|
||||
super(FrontendEditableAdmin, self).__init__(*args, **kwargs)
|
||||
|
||||
+13
-28
@@ -1,34 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import json
|
||||
from functools import update_wrapper
|
||||
import json
|
||||
|
||||
from django.conf.urls import url
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin import ModelAdmin
|
||||
from django.contrib.auth.admin import csrf_protect_m
|
||||
from django.db import transaction
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.utils.translation import override
|
||||
|
||||
from cms.models import UserSettings
|
||||
from cms.utils.compat import DJANGO_1_4
|
||||
from cms.utils.transaction import wrap_transaction
|
||||
from cms.utils.urlutils import admin_reverse
|
||||
|
||||
|
||||
class SettingsAdmin(ModelAdmin):
|
||||
def get_urls(self):
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
def wrap(view):
|
||||
def wrapper(*args, **kwargs):
|
||||
return self.admin_site.admin_view(view)(*args, **kwargs)
|
||||
|
||||
return update_wrapper(wrapper, view)
|
||||
|
||||
info = self.model._meta.app_label, self.model._meta.module_name
|
||||
info = self.model._meta.app_label, self.model._meta.model_name
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
return [
|
||||
url(r'^session_store/$',
|
||||
self.session_store,
|
||||
name='%s_%s_session_store' % info),
|
||||
@@ -38,11 +34,10 @@ class SettingsAdmin(ModelAdmin):
|
||||
url(r'^(.+)/$',
|
||||
wrap(self.change_view),
|
||||
name='%s_%s_change' % info),
|
||||
)
|
||||
return urlpatterns
|
||||
]
|
||||
|
||||
@csrf_protect_m
|
||||
@wrap_transaction
|
||||
@transaction.atomic
|
||||
def change_view(self, request, id=None):
|
||||
model = self.model
|
||||
try:
|
||||
@@ -57,25 +52,15 @@ class SettingsAdmin(ModelAdmin):
|
||||
POST should have a settings parameter
|
||||
"""
|
||||
if not request.user.is_staff:
|
||||
if DJANGO_1_4:
|
||||
return HttpResponse(json.dumps(""),
|
||||
mimetype="application/json")
|
||||
else:
|
||||
return HttpResponse(json.dumps(""),
|
||||
content_type="application/json")
|
||||
return HttpResponse(json.dumps(""),
|
||||
content_type="application/json")
|
||||
if request.method == "POST":
|
||||
request.session['cms_settings'] = request.POST['settings']
|
||||
request.session.save()
|
||||
if DJANGO_1_4:
|
||||
return HttpResponse(
|
||||
json.dumps(request.session.get('cms_settings', '')),
|
||||
mimetype="application/json"
|
||||
)
|
||||
else:
|
||||
return HttpResponse(
|
||||
json.dumps(request.session.get('cms_settings', '')),
|
||||
content_type="application/json"
|
||||
)
|
||||
return HttpResponse(
|
||||
json.dumps(request.session.get('cms_settings', '')),
|
||||
content_type="application/json"
|
||||
)
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
obj.user = request.user
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from cms.admin.forms import PageUserForm, PageUserGroupForm
|
||||
from cms.admin.permissionadmin import GenericCmsPermissionAdmin
|
||||
from cms.exceptions import NoPermissionsException
|
||||
from cms.models import PageUser, PageUserGroup
|
||||
from cms.utils.compat.dj import get_user_model
|
||||
from cms.utils.compat.forms import UserAdmin
|
||||
from cms.utils.conf import get_cms_setting
|
||||
from cms.utils.permissions import get_subordinate_users
|
||||
@@ -21,24 +21,24 @@ class PageUserAdmin(admin_class, GenericCmsPermissionAdmin):
|
||||
form = PageUserForm
|
||||
add_form = PageUserForm
|
||||
model = PageUser
|
||||
|
||||
def queryset(self, request):
|
||||
qs = super(PageUserAdmin, self).queryset(request)
|
||||
|
||||
def get_queryset(self, request):
|
||||
qs = super(PageUserAdmin, self).get_queryset(request)
|
||||
try:
|
||||
user_id_set = get_subordinate_users(request.user).values_list('id', flat=True)
|
||||
return qs.filter(pk__in=user_id_set)
|
||||
except NoPermissionsException:
|
||||
return self.model.objects.get_empty_query_set()
|
||||
|
||||
|
||||
|
||||
class PageUserGroupAdmin(admin.ModelAdmin, GenericCmsPermissionAdmin):
|
||||
form = PageUserGroupForm
|
||||
list_display = ('name', 'created_by')
|
||||
|
||||
|
||||
fieldsets = [
|
||||
(None, {'fields': ('name',)}),
|
||||
]
|
||||
|
||||
|
||||
def get_fieldsets(self, request, obj=None):
|
||||
return self.update_permission_fieldsets(request, obj)
|
||||
|
||||
|
||||
+3
-3
@@ -18,7 +18,7 @@ def revert_plugins(request, version_id, obj):
|
||||
for rev in revs:
|
||||
obj = rev.object
|
||||
if obj.__class__ == Placeholder:
|
||||
placeholders[obj.pk] = obj
|
||||
placeholders[obj.pk] = obj
|
||||
if obj.__class__ == CMSPlugin:
|
||||
cms_plugin_list.append(obj)
|
||||
elif hasattr(obj, 'cmsplugin_ptr_id'):
|
||||
@@ -27,14 +27,14 @@ def revert_plugins(request, version_id, obj):
|
||||
pass
|
||||
#page = obj #Page.objects.get(pk=obj.pk)
|
||||
elif obj.__class__ == Title:
|
||||
titles.append(obj)
|
||||
titles.append(obj)
|
||||
else:
|
||||
others.append(rev)
|
||||
if not page.has_change_permission(request):
|
||||
raise Http404
|
||||
current_plugins = list(CMSPlugin.objects.filter(placeholder__page=page))
|
||||
for pk, placeholder in placeholders.items():
|
||||
# admin has already created the placeholders/ get them instead
|
||||
# admin has already created the placeholders/ get them instead
|
||||
try:
|
||||
placeholders[pk] = page.placeholders.get(slot=placeholder.slot)
|
||||
except Placeholder.DoesNotExist:
|
||||
|
||||
+118
-38
@@ -8,6 +8,7 @@ calling these methods!
|
||||
"""
|
||||
import datetime
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.exceptions import FieldError
|
||||
from django.core.exceptions import PermissionDenied
|
||||
@@ -15,7 +16,9 @@ from django.core.exceptions import ValidationError
|
||||
from django.template.defaultfilters import slugify
|
||||
from django.template.loader import get_template
|
||||
from django.utils import six
|
||||
from django.utils.translation import activate
|
||||
|
||||
from cms import constants
|
||||
from cms.admin.forms import save_permissions
|
||||
from cms.app_base import CMSApp
|
||||
from cms.apphook_pool import apphook_pool
|
||||
@@ -29,21 +32,12 @@ from cms.models.titlemodels import Title
|
||||
from cms.plugin_base import CMSPluginBase
|
||||
from cms.plugin_pool import plugin_pool
|
||||
from cms.utils import copy_plugins
|
||||
from cms.utils.compat.dj import get_user_model
|
||||
from cms.utils.conf import get_cms_setting
|
||||
from cms.utils.i18n import get_language_list
|
||||
from cms.utils.permissions import _thread_locals
|
||||
from cms.utils.permissions import _thread_locals, current_user, has_page_change_permission
|
||||
from menus.menu_pool import menu_pool
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Constants
|
||||
#===============================================================================
|
||||
|
||||
VISIBILITY_ALL = None
|
||||
VISIBILITY_USERS = 1
|
||||
VISIBILITY_STAFF = 2
|
||||
|
||||
#===============================================================================
|
||||
# Helpers/Internals
|
||||
#===============================================================================
|
||||
@@ -59,13 +53,14 @@ def _generate_valid_slug(source, parent, language):
|
||||
qs = Title.objects.filter(language=language, page__parent=parent)
|
||||
else:
|
||||
qs = Title.objects.filter(language=language, page__parent__isnull=True)
|
||||
used = qs.values_list('slug', flat=True)
|
||||
used = list(qs.values_list('slug', flat=True))
|
||||
baseslug = slugify(source)
|
||||
slug = baseslug
|
||||
i = 1
|
||||
while slug in used:
|
||||
slug = '%s-%s' % (baseslug, i)
|
||||
i += 1
|
||||
if used:
|
||||
while slug in used:
|
||||
slug = '%s-%s' % (baseslug, i)
|
||||
i += 1
|
||||
return slug
|
||||
|
||||
|
||||
@@ -103,7 +98,7 @@ def _verify_plugin_type(plugin_type):
|
||||
(plugin_model, plugin_type)
|
||||
"""
|
||||
if (hasattr(plugin_type, '__module__') and
|
||||
issubclass(plugin_type, CMSPluginBase)):
|
||||
issubclass(plugin_type, CMSPluginBase)):
|
||||
plugin_pool.set_plugin_meta()
|
||||
plugin_model = plugin_type.model
|
||||
assert plugin_type in plugin_pool.plugins.values()
|
||||
@@ -119,8 +114,9 @@ def _verify_plugin_type(plugin_type):
|
||||
raise TypeError('plugin_type must be CMSPluginBase subclass or string')
|
||||
return plugin_model, plugin_type
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Public API
|
||||
# Public API
|
||||
#===============================================================================
|
||||
|
||||
def create_page(title, template, language, menu_title=None, slug=None,
|
||||
@@ -129,11 +125,11 @@ def create_page(title, template, language, menu_title=None, slug=None,
|
||||
publication_date=None, publication_end_date=None,
|
||||
in_navigation=False, soft_root=False, reverse_id=None,
|
||||
navigation_extenders=None, published=False, site=None,
|
||||
login_required=False, limit_visibility_in_menu=VISIBILITY_ALL,
|
||||
login_required=False, limit_visibility_in_menu=constants.VISIBILITY_ALL,
|
||||
position="last-child", overwrite_url=None, xframe_options=Page.X_FRAME_OPTIONS_INHERIT):
|
||||
"""
|
||||
Create a CMS Page and it's title for the given language
|
||||
|
||||
|
||||
See docs/extending_cms/api_reference.rst for more info
|
||||
"""
|
||||
# ugly permissions hack
|
||||
@@ -181,11 +177,18 @@ def create_page(title, template, language, menu_title=None, slug=None,
|
||||
assert navigation_extenders in menus
|
||||
|
||||
# validate menu visibility
|
||||
accepted_limitations = (VISIBILITY_ALL, VISIBILITY_USERS, VISIBILITY_STAFF)
|
||||
accepted_limitations = (constants.VISIBILITY_ALL, constants.VISIBILITY_USERS, constants.VISIBILITY_ANONYMOUS)
|
||||
assert limit_visibility_in_menu in accepted_limitations
|
||||
|
||||
# validate position
|
||||
assert position in ('last-child', 'first-child', 'left', 'right')
|
||||
if parent:
|
||||
if position in ('last-child', 'first-child'):
|
||||
parent_id = parent.pk
|
||||
else:
|
||||
parent_id = parent.parent_id
|
||||
else:
|
||||
parent_id = None
|
||||
# validate and normalize apphook
|
||||
if apphook:
|
||||
application_urls = _verify_apphook(apphook, apphook_namespace)
|
||||
@@ -199,7 +202,7 @@ def create_page(title, template, language, menu_title=None, slug=None,
|
||||
page = Page(
|
||||
created_by=created_by,
|
||||
changed_by=created_by,
|
||||
parent=parent,
|
||||
parent_id=parent_id,
|
||||
publication_date=publication_date,
|
||||
publication_end_date=publication_end_date,
|
||||
in_navigation=in_navigation,
|
||||
@@ -212,10 +215,12 @@ def create_page(title, template, language, menu_title=None, slug=None,
|
||||
site=site,
|
||||
login_required=login_required,
|
||||
limit_visibility_in_menu=limit_visibility_in_menu,
|
||||
xframe_options=xframe_options,
|
||||
xframe_options=xframe_options,
|
||||
)
|
||||
page.insert_at(parent, position)
|
||||
page.save()
|
||||
page = page.add_root(instance=page)
|
||||
|
||||
if parent:
|
||||
page = page.move(target=parent, pos=position)
|
||||
|
||||
create_title(
|
||||
language=language,
|
||||
@@ -240,9 +245,9 @@ def create_title(language, title, page, menu_title=None, slug=None,
|
||||
parent=None, overwrite_url=None):
|
||||
"""
|
||||
Create a title.
|
||||
|
||||
|
||||
Parent is only used if slug=None.
|
||||
|
||||
|
||||
See docs/extending_cms/api_reference.rst for more info
|
||||
"""
|
||||
# validate page
|
||||
@@ -277,7 +282,7 @@ def add_plugin(placeholder, plugin_type, language, position='last-child',
|
||||
target=None, **data):
|
||||
"""
|
||||
Add a plugin to a placeholder
|
||||
|
||||
|
||||
See docs/extending_cms/api_reference.rst for more info
|
||||
"""
|
||||
# validate placeholder
|
||||
@@ -287,29 +292,58 @@ def add_plugin(placeholder, plugin_type, language, position='last-child',
|
||||
plugin_model, plugin_type = _verify_plugin_type(plugin_type)
|
||||
if target:
|
||||
if position == 'last-child':
|
||||
new_pos = CMSPlugin.objects.filter(language=language, parent=target, tree_id=target.tree_id).count()
|
||||
if CMSPlugin.node_order_by:
|
||||
position = 'sorted-child'
|
||||
new_pos = CMSPlugin.objects.filter(parent=target).count()
|
||||
parent_id = target.pk
|
||||
elif position == 'first-child':
|
||||
new_pos = 0
|
||||
if CMSPlugin.node_order_by:
|
||||
position = 'sorted-child'
|
||||
parent_id = target.pk
|
||||
elif position == 'left':
|
||||
new_pos = target.position
|
||||
if CMSPlugin.node_order_by:
|
||||
position = 'sorted-sibling'
|
||||
parent_id = target.parent_id
|
||||
elif position == 'right':
|
||||
new_pos = target.position + 1
|
||||
if CMSPlugin.node_order_by:
|
||||
position = 'sorted-sibling'
|
||||
parent_id = target.parent_id
|
||||
else:
|
||||
raise Exception('position not supported: %s' % position)
|
||||
for pl in CMSPlugin.objects.filter(language=language, parent=target.parent_id, tree_id=target.tree_id, position__gte=new_pos):
|
||||
if position == 'last-child' or position == 'first-child':
|
||||
qs = CMSPlugin.objects.filter(language=language, parent=target, position__gte=new_pos,
|
||||
placeholder=placeholder)
|
||||
else:
|
||||
qs = CMSPlugin.objects.filter(language=language, parent=target.parent_id, position__gte=new_pos,
|
||||
placeholder=placeholder)
|
||||
for pl in qs:
|
||||
pl.position += 1
|
||||
pl.save()
|
||||
else:
|
||||
new_pos = CMSPlugin.objects.filter(language=language, parent__isnull=True, placeholder=placeholder).count()
|
||||
|
||||
if position == 'last-child':
|
||||
new_pos = CMSPlugin.objects.filter(language=language, parent__isnull=True, placeholder=placeholder).count()
|
||||
else:
|
||||
new_pos = 0
|
||||
for pl in CMSPlugin.objects.filter(language=language, parent__isnull=True, position__gte=new_pos,
|
||||
placeholder=placeholder):
|
||||
pl.position += 1
|
||||
pl.save()
|
||||
parent_id = None
|
||||
plugin_base = CMSPlugin(
|
||||
plugin_type=plugin_type,
|
||||
placeholder=placeholder,
|
||||
position=new_pos,
|
||||
language=language
|
||||
language=language,
|
||||
parent_id=parent_id,
|
||||
)
|
||||
plugin_base.insert_at(target, position=position, save=False)
|
||||
|
||||
plugin_base = plugin_base.add_root(instance=plugin_base)
|
||||
|
||||
if target:
|
||||
plugin_base = plugin_base.move(target, pos=position)
|
||||
plugin = plugin_model(**data)
|
||||
plugin_base.set_base_attr(plugin)
|
||||
plugin.save()
|
||||
@@ -326,7 +360,7 @@ def create_page_user(created_by, user,
|
||||
can_delete_pagepermission=True, grant_all=False):
|
||||
"""
|
||||
Creates a page user.
|
||||
|
||||
|
||||
See docs/extending_cms/api_reference.rst for more info
|
||||
"""
|
||||
if grant_all:
|
||||
@@ -369,7 +403,7 @@ def assign_user_to_page(page, user, grant_on=ACCESS_PAGE_AND_DESCENDANTS,
|
||||
grant_all=False, global_permission=False):
|
||||
"""
|
||||
Assigns given user to page, and gives him requested permissions.
|
||||
|
||||
|
||||
See docs/extending_cms/api_reference.rst for more info
|
||||
"""
|
||||
grant_all = grant_all and not global_permission
|
||||
@@ -399,7 +433,7 @@ def publish_page(page, user, language):
|
||||
"""
|
||||
Publish a page. This sets `page.published` to `True` and calls publish()
|
||||
which does the actual publishing.
|
||||
|
||||
|
||||
See docs/extending_cms/api_reference.rst for more info
|
||||
"""
|
||||
page = page.reload()
|
||||
@@ -411,10 +445,41 @@ def publish_page(page, user, language):
|
||||
request = FakeRequest(user)
|
||||
if not page.has_publish_permission(request):
|
||||
raise PermissionDenied()
|
||||
page.publish(language)
|
||||
# Set the current_user to have the page's changed_by
|
||||
# attribute set correctly.
|
||||
# 'user' is a user object, but current_user() just wants the username (a string).
|
||||
with current_user(user.get_username()):
|
||||
page.publish(language)
|
||||
return page.reload()
|
||||
|
||||
|
||||
def publish_pages(include_unpublished=False, language=None, site=None):
|
||||
"""
|
||||
Create published public version of selected drafts.
|
||||
"""
|
||||
qs = Page.objects.drafts()
|
||||
if not include_unpublished:
|
||||
qs = qs.filter(title_set__published=True).distinct()
|
||||
if site:
|
||||
qs = qs.filter(site=site)
|
||||
|
||||
output_language = None
|
||||
for i, page in enumerate(qs):
|
||||
add = True
|
||||
titles = page.title_set
|
||||
if not include_unpublished:
|
||||
titles = titles.filter(published=True)
|
||||
for lang in titles.values_list("language", flat=True):
|
||||
if language is None or lang == language:
|
||||
if not output_language:
|
||||
output_language = lang
|
||||
if not page.publish(lang):
|
||||
add = False
|
||||
# we may need to activate the first (main) language for proper page title rendering
|
||||
activate(output_language)
|
||||
yield (page, add)
|
||||
|
||||
|
||||
def get_page_draft(page):
|
||||
"""
|
||||
Returns the draft version of a page, regardless if the passed in
|
||||
@@ -456,13 +521,28 @@ def copy_plugins_to_language(page, source_language, target_language,
|
||||
:return int: number of copied plugins
|
||||
"""
|
||||
copied = 0
|
||||
placeholders = page.placeholders.all()
|
||||
placeholders = page.get_placeholders()
|
||||
for placeholder in placeholders:
|
||||
# only_empty is True we check if the placeholder already has plugins and
|
||||
# we skip it if has some
|
||||
if not only_empty or not placeholder.cmsplugin_set.filter(language=target_language).exists():
|
||||
plugins = list(
|
||||
placeholder.cmsplugin_set.filter(language=source_language).order_by('tree_id', 'level', 'position'))
|
||||
placeholder.cmsplugin_set.filter(language=source_language).order_by('path'))
|
||||
copied_plugins = copy_plugins.copy_plugins_to(plugins, placeholder, target_language)
|
||||
copied += len(copied_plugins)
|
||||
return copied
|
||||
|
||||
|
||||
def can_change_page(request):
|
||||
"""
|
||||
Check whether a user has the permission to change the page.
|
||||
|
||||
This will work across all permission-related setting, with a unified interface
|
||||
to permission checking.
|
||||
"""
|
||||
# check global permissions if CMS_PERMISSION is active
|
||||
global_permission = get_cms_setting('PERMISSION') and has_page_change_permission(request)
|
||||
# check if user has page edit permission
|
||||
page_permission = request.current_page and request.current_page.has_change_permission(request)
|
||||
|
||||
return global_permission or page_permission
|
||||
|
||||
+8
-6
@@ -6,15 +6,18 @@ try:
|
||||
except ImportError:
|
||||
# Python < 2.7
|
||||
from django.utils.datastructures import SortedDict as OrderedDict
|
||||
try:
|
||||
from importlib import import_module
|
||||
except ImportError:
|
||||
# Python < 2.7
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls import patterns
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.urlresolvers import (RegexURLResolver, Resolver404, reverse,
|
||||
RegexURLPattern)
|
||||
RegexURLPattern)
|
||||
from django.utils import six
|
||||
from django.utils.importlib import import_module
|
||||
from django.utils.translation import get_language
|
||||
|
||||
from cms.apphook_pool import apphook_pool
|
||||
@@ -209,7 +212,7 @@ def get_app_patterns():
|
||||
|
||||
# Loop over all titles with an application hooked to them
|
||||
# TODO: Need to be fixed for django-treebeard when forward ported to 3.1
|
||||
for title in title_qs.exclude(page__application_urls=None).exclude(page__application_urls='').order_by('-page__level').select_related():
|
||||
for title in title_qs.exclude(page__application_urls=None).exclude(page__application_urls='').order_by('-page__path').select_related():
|
||||
path = title.path
|
||||
mix_id = "%s:%s:%s" % (path + "/", title.page.application_urls, title.language)
|
||||
if mix_id in included:
|
||||
@@ -236,8 +239,7 @@ def get_app_patterns():
|
||||
if app.permissions:
|
||||
_set_permissions(current_patterns, app.exclude_permissions)
|
||||
|
||||
extra_patterns = patterns('', *current_patterns)
|
||||
resolver.url_patterns_dict[lang] = extra_patterns
|
||||
resolver.url_patterns_dict[lang] = current_patterns
|
||||
app_patterns.append(resolver)
|
||||
APP_RESOLVERS.append(resolver)
|
||||
return app_patterns
|
||||
|
||||
@@ -10,5 +10,3 @@ class CMSConfig(AppConfig):
|
||||
|
||||
def ready(self):
|
||||
setup()
|
||||
from cms.plugin_pool import plugin_pool
|
||||
plugin_pool.set_plugin_meta()
|
||||
|
||||
Vendored
+87
@@ -0,0 +1,87 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
from cms.utils import get_cms_setting
|
||||
|
||||
CMS_PAGE_CACHE_VERSION_KEY = get_cms_setting("CACHE_PREFIX") + 'CMS_PAGE_CACHE_VERSION'
|
||||
|
||||
|
||||
def _get_cache_version():
|
||||
"""
|
||||
Returns the current page cache version, explicitly setting one if not
|
||||
defined.
|
||||
"""
|
||||
from django.core.cache import cache
|
||||
|
||||
version = cache.get(CMS_PAGE_CACHE_VERSION_KEY)
|
||||
|
||||
if version:
|
||||
return version
|
||||
else:
|
||||
_set_cache_version(1)
|
||||
return 1
|
||||
|
||||
|
||||
def _set_cache_version(version):
|
||||
"""
|
||||
Set the cache version to the specified value.
|
||||
"""
|
||||
from django.core.cache import cache
|
||||
|
||||
cache.set(
|
||||
CMS_PAGE_CACHE_VERSION_KEY,
|
||||
version,
|
||||
get_cms_setting('CACHE_DURATIONS')['content']
|
||||
)
|
||||
|
||||
|
||||
def invalidate_cms_page_cache():
|
||||
"""
|
||||
Invalidates the CMS PAGE CACHE.
|
||||
"""
|
||||
|
||||
#
|
||||
# NOTE: We're using a cache versioning strategy for invalidating the page
|
||||
# cache when necessary. Instead of wiping all the old entries, we simply
|
||||
# increment the version number rendering all previous entries
|
||||
# inaccessible and left to expire naturally.
|
||||
#
|
||||
# ALSO NOTE: According to the Django documentation, a timeout value of
|
||||
# `None' (in version 1.6+) is supposed to mean "cache forever", however,
|
||||
# this is actually only implemented as only slightly less than 30 days in
|
||||
# some backends (memcached, in particular). In older Djangos, `None' means
|
||||
# "use default value". To avoid issues arising from different Django
|
||||
# versions and cache backend implementations, we will explicitly set the
|
||||
# lifespan of the CMS_PAGE_CACHE_VERSION entry to whatever is set in
|
||||
# settings.CACHE_DURATIONS['content']. This allows users to adjust as
|
||||
# necessary for their backend.
|
||||
#
|
||||
# To prevent writing cache entries that will live longer than our version
|
||||
# key, we will always re-write the current version number into the cache
|
||||
# just after we write any new cache entries, thus ensuring that the
|
||||
# version number will always outlive any entries written against that
|
||||
# version. This is a cheap operation.
|
||||
#
|
||||
# If there are no new cache writes before the version key expires, its
|
||||
# perfectly OK, since any previous entries cached against that version
|
||||
# will have also expired, so, it'd be pointless to try to access them
|
||||
# anyway.
|
||||
#
|
||||
version = _get_cache_version()
|
||||
_set_cache_version(version + 1)
|
||||
|
||||
|
||||
CLEAN_KEY_PATTERN = re.compile(r'[^a-zA-Z0-9_-]')
|
||||
|
||||
|
||||
def _clean_key(key):
|
||||
return CLEAN_KEY_PATTERN.sub('-', key)
|
||||
|
||||
|
||||
def _get_cache_key(name, page_lookup, lang, site_id):
|
||||
from cms.models import Page
|
||||
if isinstance(page_lookup, Page):
|
||||
page_key = str(page_lookup.pk)
|
||||
else:
|
||||
page_key = str(page_lookup)
|
||||
page_key = _clean_key(page_key)
|
||||
return get_cms_setting('CACHE_PREFIX') + name + '__page_lookup:' + page_key + '_site:' + str(site_id) + '_lang:' + str(lang)
|
||||
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.conf import settings
|
||||
|
||||
from cms.utils import get_cms_setting
|
||||
|
||||
|
||||
def _site_cache_key(lang):
|
||||
return "%s-%s" %(get_cms_setting('SITE_CHOICES_CACHE_KEY'), lang)
|
||||
|
||||
|
||||
def _page_cache_key(lang):
|
||||
return "%s-%s" %(get_cms_setting('PAGE_CHOICES_CACHE_KEY'), lang)
|
||||
|
||||
|
||||
def _clean_many(prefix):
|
||||
from django.core.cache import cache
|
||||
keys = []
|
||||
if settings.USE_I18N:
|
||||
for lang in [language[0] for language in settings.LANGUAGES]:
|
||||
keys.append("%s-%s" %(prefix, lang))
|
||||
else:
|
||||
keys = ["%s-%s" %(prefix, settings.LANGUAGE_CODE)]
|
||||
cache.delete_many(keys)
|
||||
|
||||
|
||||
def clean_site_choices_cache(sender, **kwargs):
|
||||
_clean_many(get_cms_setting('SITE_CHOICES_CACHE_KEY'))
|
||||
|
||||
|
||||
def clean_page_choices_cache(sender, **kwargs):
|
||||
_clean_many(get_cms_setting('PAGE_CHOICES_CACHE_KEY'))
|
||||
Vendored
+97
@@ -0,0 +1,97 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import hashlib
|
||||
from django.conf import settings
|
||||
from django.utils.cache import add_never_cache_headers
|
||||
from django.utils.encoding import iri_to_uri, force_text
|
||||
from django.utils.timezone import get_current_timezone_name
|
||||
|
||||
from cms.cache import _get_cache_version, _set_cache_version, _get_cache_key
|
||||
from cms.utils import get_cms_setting
|
||||
|
||||
|
||||
def _page_cache_key(request):
|
||||
#md5 key of current path
|
||||
cache_key = "%s:%d:%s" % (
|
||||
get_cms_setting("CACHE_PREFIX"),
|
||||
settings.SITE_ID,
|
||||
hashlib.md5(iri_to_uri(request.get_full_path()).encode('utf-8')).hexdigest()
|
||||
)
|
||||
if settings.USE_TZ:
|
||||
# The datetime module doesn't restrict the output of tzname().
|
||||
# Windows is known to use non-standard, locale-dependant names.
|
||||
# User-defined tzinfo classes may return absolutely anything.
|
||||
# Hence this paranoid conversion to create a valid cache key.
|
||||
tz_name = force_text(get_current_timezone_name(), errors='ignore')
|
||||
cache_key += '.%s' % tz_name.encode('ascii', 'ignore').decode('ascii').replace(' ', '_')
|
||||
return cache_key
|
||||
|
||||
|
||||
def set_page_cache(response):
|
||||
from django.core.cache import cache
|
||||
|
||||
if not get_cms_setting('PAGE_CACHE'):
|
||||
return response
|
||||
request = response._request
|
||||
save_cache = True
|
||||
for placeholder in getattr(request, 'placeholders', []):
|
||||
if not placeholder.cache_placeholder:
|
||||
save_cache = False
|
||||
break
|
||||
if hasattr(request, 'toolbar'):
|
||||
if request.toolbar.edit_mode or request.toolbar.show_toolbar:
|
||||
save_cache = False
|
||||
if request.user.is_authenticated():
|
||||
save_cache = False
|
||||
if not save_cache:
|
||||
add_never_cache_headers(response)
|
||||
return response
|
||||
else:
|
||||
version = _get_cache_version()
|
||||
ttl = get_cms_setting('CACHE_DURATIONS')['content']
|
||||
|
||||
cache.set(
|
||||
_page_cache_key(request),
|
||||
(response.content, response._headers),
|
||||
ttl,
|
||||
version=version
|
||||
)
|
||||
# See note in invalidate_cms_page_cache()
|
||||
_set_cache_version(version)
|
||||
|
||||
|
||||
def get_page_cache(request):
|
||||
from django.core.cache import cache
|
||||
|
||||
return cache.get(_page_cache_key(request),
|
||||
version=_get_cache_version())
|
||||
|
||||
|
||||
def get_xframe_cache(page):
|
||||
from django.core.cache import cache
|
||||
return cache.get('cms:xframe_options:%s' % page.pk)
|
||||
|
||||
|
||||
def set_xframe_cache(page, xframe_options):
|
||||
from django.core.cache import cache
|
||||
cache.set('cms:xframe_options:%s' % page.pk,
|
||||
xframe_options,
|
||||
version=_get_cache_version())
|
||||
_set_cache_version(_get_cache_version())
|
||||
|
||||
|
||||
def _page_url_key(page_lookup, lang, site_id):
|
||||
return _get_cache_key('page_url', page_lookup, lang, site_id) + '_type:absolute_url'
|
||||
|
||||
|
||||
def set_page_url_cache(page_lookup, lang, site_id, url):
|
||||
from django.core.cache import cache
|
||||
cache.set(_page_url_key(page_lookup, lang, site_id),
|
||||
url,
|
||||
get_cms_setting('CACHE_DURATIONS')['content'], version=_get_cache_version())
|
||||
_set_cache_version(_get_cache_version())
|
||||
|
||||
|
||||
def get_page_url_cache(page_lookup, lang, site_id):
|
||||
from django.core.cache import cache
|
||||
return cache.get(_page_url_key(page_lookup, lang, site_id),
|
||||
version=_get_cache_version())
|
||||
Vendored
+18
-14
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from cms.utils import get_cms_setting
|
||||
from cms.utils.compat.dj import get_user_model
|
||||
|
||||
|
||||
PERMISSION_KEYS = [
|
||||
@@ -15,15 +16,18 @@ def get_cache_key(user, key):
|
||||
return "%s:permission:%s:%s" % (
|
||||
get_cms_setting('CACHE_PREFIX'), username, key)
|
||||
|
||||
def get_cache_version_key():
|
||||
|
||||
def get_cache_permission_version_key():
|
||||
return "%s:permission:version" % (get_cms_setting('CACHE_PREFIX'),)
|
||||
|
||||
def get_cache_version():
|
||||
|
||||
def get_cache_permission_version():
|
||||
from django.core.cache import cache
|
||||
version = cache.get(get_cache_version_key())
|
||||
if version is None:
|
||||
try:
|
||||
version = int(cache.get(get_cache_permission_version_key()))
|
||||
except Exception:
|
||||
version = 1
|
||||
return version
|
||||
return int(version)
|
||||
|
||||
|
||||
def get_permission_cache(user, key):
|
||||
@@ -31,7 +35,7 @@ def get_permission_cache(user, key):
|
||||
Helper for reading values from cache
|
||||
"""
|
||||
from django.core.cache import cache
|
||||
return cache.get(get_cache_key(user, key), version=get_cache_version())
|
||||
return cache.get(get_cache_key(user, key), version=get_cache_permission_version())
|
||||
|
||||
|
||||
def set_permission_cache(user, key, value):
|
||||
@@ -43,8 +47,8 @@ def set_permission_cache(user, key, value):
|
||||
# store this key, so we can clean it when required
|
||||
cache_key = get_cache_key(user, key)
|
||||
cache.set(cache_key, value,
|
||||
get_cms_setting('CACHE_DURATIONS')['permissions'],
|
||||
version=get_cache_version())
|
||||
get_cms_setting('CACHE_DURATIONS')['permissions'],
|
||||
version=get_cache_permission_version())
|
||||
|
||||
|
||||
def clear_user_permission_cache(user):
|
||||
@@ -53,14 +57,14 @@ def clear_user_permission_cache(user):
|
||||
"""
|
||||
from django.core.cache import cache
|
||||
for key in PERMISSION_KEYS:
|
||||
cache.delete(get_cache_key(user, key), version=get_cache_version())
|
||||
cache.delete(get_cache_key(user, key), version=get_cache_permission_version())
|
||||
|
||||
|
||||
def clear_permission_cache():
|
||||
from django.core.cache import cache
|
||||
version = get_cache_version()
|
||||
version = get_cache_permission_version()
|
||||
if version > 1:
|
||||
cache.incr(get_cache_version_key())
|
||||
cache.incr(get_cache_permission_version_key())
|
||||
else:
|
||||
cache.set(get_cache_version_key(), 2,
|
||||
get_cms_setting('CACHE_DURATIONS')['permissions'])
|
||||
cache.set(get_cache_permission_version_key(), 2,
|
||||
get_cms_setting('CACHE_DURATIONS')['permissions'])
|
||||
|
||||
Vendored
+63
@@ -0,0 +1,63 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.conf import settings
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.timezone import get_current_timezone_name
|
||||
from cms.cache import _get_cache_version, _set_cache_version, _clean_key, _get_cache_key
|
||||
from cms.utils import get_cms_setting
|
||||
|
||||
|
||||
def _placeholder_cache_key(placeholder, lang):
|
||||
cache_key = '%srender_placeholder:%s.%s' % (get_cms_setting("CACHE_PREFIX"), placeholder.pk, str(lang))
|
||||
if settings.USE_TZ:
|
||||
tz_name = force_text(get_current_timezone_name(), errors='ignore')
|
||||
cache_key += '.%s' % tz_name.encode('ascii', 'ignore').decode('ascii').replace(' ', '_')
|
||||
return cache_key
|
||||
|
||||
|
||||
def set_placeholder_cache(placeholder, lang, content):
|
||||
"""
|
||||
Caches the rendering of a placeholder
|
||||
"""
|
||||
from django.core.cache import cache
|
||||
cache.set(_placeholder_cache_key(placeholder, lang),
|
||||
content,
|
||||
get_cms_setting('CACHE_DURATIONS')['content'],
|
||||
version=_get_cache_version())
|
||||
_set_cache_version(_get_cache_version())
|
||||
|
||||
|
||||
def get_placeholder_cache(placeholder, lang):
|
||||
"""
|
||||
Retrieves the cached content of a placeholder
|
||||
"""
|
||||
from django.core.cache import cache
|
||||
|
||||
return cache.get(_placeholder_cache_key(placeholder, lang),
|
||||
version=_get_cache_version())
|
||||
|
||||
|
||||
def clear_placeholder_cache(placeholder, lang):
|
||||
from django.core.cache import cache
|
||||
|
||||
cache.delete(_placeholder_cache_key(placeholder, lang), version=_get_cache_version())
|
||||
|
||||
|
||||
def _placeholder_page_cache_key(page_lookup, lang, site_id, placeholder_name):
|
||||
base_key = _get_cache_key('_show_placeholder_for_page', page_lookup, lang, site_id)
|
||||
return _clean_key('%s_placeholder:%s' % (base_key, placeholder_name))
|
||||
|
||||
|
||||
def get_placeholder_page_cache(page_lookup, lang, site_id, placeholder_name):
|
||||
from django.core.cache import cache
|
||||
|
||||
return cache.get(_placeholder_page_cache_key(page_lookup, lang, site_id, placeholder_name),
|
||||
version=_get_cache_version())
|
||||
|
||||
|
||||
def set_placeholder_page_cache(page_lookup, lang, site_id, placeholder_name, content):
|
||||
from django.core.cache import cache
|
||||
|
||||
cache.set(_placeholder_page_cache_key(page_lookup, lang, site_id, placeholder_name),
|
||||
content,
|
||||
get_cms_setting('CACHE_DURATIONS')['content'], version=_get_cache_version())
|
||||
_set_cache_version(_get_cache_version())
|
||||
+7
-7
@@ -5,9 +5,8 @@ from cms.models.placeholderpluginmodel import PlaceholderReference
|
||||
from cms.plugin_base import CMSPluginBase, PluginMenuItem
|
||||
from cms.plugin_pool import plugin_pool
|
||||
from cms.plugin_rendering import render_placeholder
|
||||
from cms.utils.plugins import downcast_plugins, build_plugin_tree
|
||||
from cms.utils.urlutils import admin_reverse
|
||||
from django.conf.urls import patterns, url
|
||||
from django.conf.urls import url
|
||||
from django.http import HttpResponseForbidden, HttpResponseBadRequest, HttpResponse
|
||||
from django.middleware.csrf import get_token
|
||||
from django.utils.safestring import mark_safe
|
||||
@@ -20,6 +19,7 @@ class PlaceholderPlugin(CMSPluginBase):
|
||||
#require_parent = True
|
||||
render_plugin = False
|
||||
admin_preview = False
|
||||
system = True
|
||||
|
||||
model = PlaceholderReference
|
||||
|
||||
@@ -32,13 +32,15 @@ class AliasPlugin(CMSPluginBase):
|
||||
allow_children = False
|
||||
model = AliasPluginModel
|
||||
render_template = "cms/plugins/alias.html"
|
||||
system = True
|
||||
|
||||
def render(self, context, instance, placeholder):
|
||||
from cms.utils.plugins import downcast_plugins, build_plugin_tree
|
||||
context['instance'] = instance
|
||||
context['placeholder'] = placeholder
|
||||
if instance.plugin_id:
|
||||
plugins = instance.plugin.get_descendants(include_self=True).order_by('placeholder', 'tree_id', 'level',
|
||||
'position')
|
||||
plugins = instance.plugin.get_descendants().order_by('placeholder', 'path')
|
||||
plugins = [instance.plugin] + list(plugins)
|
||||
plugins = downcast_plugins(plugins)
|
||||
plugins[0].parent_id = None
|
||||
plugins = build_plugin_tree(plugins)
|
||||
@@ -67,11 +69,9 @@ class AliasPlugin(CMSPluginBase):
|
||||
]
|
||||
|
||||
def get_plugin_urls(self):
|
||||
urlpatterns = [
|
||||
return [
|
||||
url(r'^create_alias/$', self.create_alias, name='cms_create_alias'),
|
||||
]
|
||||
urlpatterns = patterns('', *urlpatterns)
|
||||
return urlpatterns
|
||||
|
||||
def create_alias(self, request):
|
||||
if not request.user.is_staff:
|
||||
|
||||
+89
-59
@@ -1,28 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse, NoReverseMatch, resolve, Resolver404
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.models import User, AnonymousUser
|
||||
from django.contrib.auth import get_permission_codename, get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.urlresolvers import reverse, NoReverseMatch, resolve, Resolver404
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
try:
|
||||
from django.contrib.auth import get_user_model
|
||||
except ImportError:
|
||||
get_user_model = lambda: User
|
||||
|
||||
from cms.api import get_page_draft
|
||||
from cms.api import get_page_draft, can_change_page
|
||||
from cms.constants import TEMPLATE_INHERITANCE_MAGIC, PUBLISHER_STATE_PENDING
|
||||
from cms.models import Title, Page
|
||||
from cms.toolbar.items import TemplateItem
|
||||
from cms.toolbar_base import CMSToolbar
|
||||
from cms.toolbar_pool import toolbar_pool
|
||||
from cms.utils.compat import DJANGO_1_4
|
||||
from cms.utils.i18n import get_language_tuple, force_language, get_language_dict
|
||||
from cms.utils.compat.dj import is_installed
|
||||
from cms.utils import get_cms_setting
|
||||
from cms.utils.permissions import get_user_sites_queryset, has_page_change_permission
|
||||
from cms.utils.permissions import (
|
||||
get_user_sites_queryset,
|
||||
has_auth_page_permission,
|
||||
)
|
||||
from cms.utils.urlutils import add_url_parameters, admin_reverse
|
||||
from menus.utils import DefaultLanguageChanger
|
||||
|
||||
@@ -47,6 +47,7 @@ USER_SETTINGS_BREAK = 'User Settings Break'
|
||||
ADD_PAGE_LANGUAGE_BREAK = "Add page language Break"
|
||||
REMOVE_PAGE_LANGUAGE_BREAK = "Remove page language Break"
|
||||
COPY_PAGE_LANGUAGE_BREAK = "Copy page language Break"
|
||||
TOOLBAR_DISABLE_BREAK = 'Toolbar disable Break'
|
||||
|
||||
|
||||
@toolbar_pool.register
|
||||
@@ -86,9 +87,12 @@ class PlaceholderToolbar(CMSToolbar):
|
||||
build_mode = self.toolbar.build_mode
|
||||
build_url = '?%s' % get_cms_setting('CMS_TOOLBAR_URL__BUILD')
|
||||
edit_url = '?%s' % get_cms_setting('CMS_TOOLBAR_URL__EDIT_ON')
|
||||
switcher = self.toolbar.add_button_list('Mode Switcher', side=self.toolbar.RIGHT, extra_classes=extra_classes)
|
||||
switcher.add_button(_('Structure'), build_url, active=build_mode, disabled=not build_mode)
|
||||
switcher.add_button(_('Content'), edit_url, active=not build_mode, disabled=build_mode)
|
||||
|
||||
if self.request.user.has_perm("cms.use_structure"):
|
||||
switcher = self.toolbar.add_button_list('Mode Switcher', side=self.toolbar.RIGHT,
|
||||
extra_classes=extra_classes)
|
||||
switcher.add_button(_('Structure'), build_url, active=build_mode, disabled=not build_mode)
|
||||
switcher.add_button(_('Content'), edit_url, active=not build_mode, disabled=build_mode)
|
||||
|
||||
|
||||
@toolbar_pool.register
|
||||
@@ -96,46 +100,56 @@ class BasicToolbar(CMSToolbar):
|
||||
"""
|
||||
Basic Toolbar for site and languages menu
|
||||
"""
|
||||
page = None
|
||||
_language_menu = None
|
||||
_admin_menu = None
|
||||
|
||||
def init_from_request(self):
|
||||
self.page = get_page_draft(self.request.current_page)
|
||||
|
||||
def populate(self):
|
||||
self.init_from_request()
|
||||
if not self.page:
|
||||
self.init_from_request()
|
||||
|
||||
self.add_admin_menu()
|
||||
self.add_language_menu()
|
||||
self.add_admin_menu()
|
||||
self.add_language_menu()
|
||||
user_settings = self.request.toolbar.get_user_settings()
|
||||
self.clipboard = user_settings.clipboard
|
||||
|
||||
def add_admin_menu(self):
|
||||
admin_menu = self.toolbar.get_or_create_menu(ADMIN_MENU_IDENTIFIER, self.current_site.name)
|
||||
if not self._admin_menu:
|
||||
self._admin_menu = self.toolbar.get_or_create_menu(ADMIN_MENU_IDENTIFIER, self.current_site.name)
|
||||
# Users button
|
||||
self.add_users_button(self._admin_menu)
|
||||
|
||||
# Users button
|
||||
self.add_users_button(admin_menu)
|
||||
# sites menu
|
||||
if get_cms_setting('PERMISSION'):
|
||||
sites_queryset = get_user_sites_queryset(self.request.user)
|
||||
else:
|
||||
sites_queryset = Site.objects.all()
|
||||
|
||||
# sites menu
|
||||
if get_cms_setting('PERMISSION'):
|
||||
sites_queryset = get_user_sites_queryset(self.request.user)
|
||||
else:
|
||||
sites_queryset = Site.objects.all()
|
||||
if len(sites_queryset) > 1:
|
||||
sites_menu = self._admin_menu.get_or_create_menu('sites', _('Sites'))
|
||||
sites_menu.add_sideframe_item(_('Admin Sites'), url=admin_reverse('sites_site_changelist'))
|
||||
sites_menu.add_break(ADMIN_SITES_BREAK)
|
||||
for site in sites_queryset:
|
||||
sites_menu.add_link_item(site.name, url='http://%s' % site.domain,
|
||||
active=site.pk == self.current_site.pk)
|
||||
|
||||
if len(sites_queryset) > 1:
|
||||
sites_menu = admin_menu.get_or_create_menu('sites', _('Sites'))
|
||||
sites_menu.add_sideframe_item(_('Admin Sites'), url=admin_reverse('sites_site_changelist'))
|
||||
sites_menu.add_break(ADMIN_SITES_BREAK)
|
||||
for site in sites_queryset:
|
||||
sites_menu.add_link_item(site.name, url='http://%s' % site.domain,
|
||||
active=site.pk == self.current_site.pk)
|
||||
# admin
|
||||
self._admin_menu.add_sideframe_item(_('Administration'), url=admin_reverse('index'))
|
||||
self._admin_menu.add_break(ADMINISTRATION_BREAK)
|
||||
|
||||
# admin
|
||||
admin_menu.add_sideframe_item(_('Administration'), url=admin_reverse('index'))
|
||||
admin_menu.add_break(ADMINISTRATION_BREAK)
|
||||
# cms users
|
||||
self._admin_menu.add_sideframe_item(_('User settings'), url=admin_reverse('cms_usersettings_change'))
|
||||
self._admin_menu.add_break(USER_SETTINGS_BREAK)
|
||||
|
||||
# cms users
|
||||
admin_menu.add_sideframe_item(_('User settings'), url=admin_reverse('cms_usersettings_change'))
|
||||
admin_menu.add_break(USER_SETTINGS_BREAK)
|
||||
# Disable toolbar
|
||||
self._admin_menu.add_link_item(_('Disable toolbar'), url='?%s' % get_cms_setting('CMS_TOOLBAR_URL__DISABLE'))
|
||||
self._admin_menu.add_break(TOOLBAR_DISABLE_BREAK)
|
||||
|
||||
# logout
|
||||
self.add_logout_button(admin_menu)
|
||||
# logout
|
||||
self.add_logout_button(self._admin_menu)
|
||||
|
||||
def add_users_button(self, parent):
|
||||
User = get_user_model()
|
||||
@@ -143,8 +157,8 @@ class BasicToolbar(CMSToolbar):
|
||||
if User in admin.site._registry:
|
||||
opts = User._meta
|
||||
|
||||
if self.request.user.has_perm('%s.%s' % (opts.app_label, opts.get_change_permission())):
|
||||
user_changelist_url = admin_reverse('%s_%s_changelist' % (opts.app_label, opts.module_name))
|
||||
if self.request.user.has_perm('%s.%s' % (opts.app_label, get_permission_codename('change', opts))):
|
||||
user_changelist_url = admin_reverse('%s_%s_changelist' % (opts.app_label, opts.model_name))
|
||||
parent.add_sideframe_item(_('Users'), url=user_changelist_url)
|
||||
|
||||
def add_logout_button(self, parent):
|
||||
@@ -169,15 +183,15 @@ class BasicToolbar(CMSToolbar):
|
||||
parent.add_ajax_item(logout_menu_text, action=admin_reverse('logout'), active=True, on_success=on_success)
|
||||
|
||||
def add_language_menu(self):
|
||||
if settings.USE_I18N:
|
||||
language_menu = self.toolbar.get_or_create_menu(LANGUAGE_MENU_IDENTIFIER, _('Language'))
|
||||
if settings.USE_I18N and not self._language_menu:
|
||||
self._language_menu = self.toolbar.get_or_create_menu(LANGUAGE_MENU_IDENTIFIER, _('Language'))
|
||||
language_changer = getattr(self.request, '_language_changer', DefaultLanguageChanger(self.request))
|
||||
for code, name in get_language_tuple(self.current_site.pk):
|
||||
try:
|
||||
url = language_changer(code)
|
||||
except NoReverseMatch:
|
||||
url = DefaultLanguageChanger(self.request)(code)
|
||||
language_menu.add_link_item(name, url=url, active=self.current_lang == code)
|
||||
self._language_menu.add_link_item(name, url=url, active=self.current_lang == code)
|
||||
|
||||
def get_username(self, user=None, default=''):
|
||||
user = user or self.request.user
|
||||
@@ -185,16 +199,27 @@ class BasicToolbar(CMSToolbar):
|
||||
name = user.get_full_name()
|
||||
if name:
|
||||
return name
|
||||
elif DJANGO_1_4:
|
||||
return user.username
|
||||
else:
|
||||
return user.get_username()
|
||||
except (AttributeError, NotImplementedError):
|
||||
return default
|
||||
|
||||
def get_clipboard_plugins(self):
|
||||
self.populate()
|
||||
if not hasattr(self, "clipboard"):
|
||||
return []
|
||||
return self.clipboard.get_plugins()
|
||||
|
||||
def render_addons(self, context):
|
||||
context.push()
|
||||
context['local_toolbar'] = self
|
||||
clipboard = mark_safe(render_to_string('cms/toolbar/clipboard.html', context))
|
||||
context.pop()
|
||||
return [clipboard]
|
||||
|
||||
@toolbar_pool.register
|
||||
class PageToolbar(CMSToolbar):
|
||||
_changed_admin_menu = None
|
||||
watch_models = [Page]
|
||||
|
||||
# Helpers
|
||||
@@ -231,14 +256,16 @@ class PageToolbar(CMSToolbar):
|
||||
|
||||
def has_page_change_permission(self):
|
||||
if not hasattr(self, 'page_change_permission'):
|
||||
# check global permissions if CMS_PERMISSIONS is active
|
||||
global_permission = self.permissions_activated and has_page_change_permission(self.request)
|
||||
|
||||
# check if user has page edit permission
|
||||
page_permission = self.page and self.page.has_change_permission(self.request)
|
||||
|
||||
self.page_change_permission = global_permission or page_permission
|
||||
|
||||
if not self.page and not get_cms_setting('PERMISSION'):
|
||||
# We can't check permissions for an individual page
|
||||
# and can't check global cms permissions because
|
||||
# user opted out of them.
|
||||
# So just check django auth permissions.
|
||||
user = self.request.user
|
||||
can_change = has_auth_page_permission(user, action='change')
|
||||
else:
|
||||
can_change = can_change_page(self.request)
|
||||
self.page_change_permission = can_change
|
||||
return self.page_change_permission
|
||||
|
||||
def page_is_pending(self, page, language):
|
||||
@@ -280,8 +307,8 @@ class PageToolbar(CMSToolbar):
|
||||
def post_template_populate(self):
|
||||
self.init_placeholders_from_request()
|
||||
|
||||
self.add_publish_button()
|
||||
self.add_draft_live()
|
||||
self.add_publish_button()
|
||||
|
||||
# Buttons
|
||||
|
||||
@@ -386,7 +413,7 @@ class PageToolbar(CMSToolbar):
|
||||
question=question % name, on_success=self.toolbar.REFRESH_PAGE)
|
||||
|
||||
def change_admin_menu(self):
|
||||
if self.has_page_change_permission():
|
||||
if not self._changed_admin_menu and self.has_page_change_permission():
|
||||
admin_menu = self.toolbar.get_or_create_menu(ADMIN_MENU_IDENTIFIER)
|
||||
url = admin_reverse('cms_page_changelist') # cms page admin
|
||||
params = {'language': self.toolbar.language}
|
||||
@@ -394,6 +421,8 @@ class PageToolbar(CMSToolbar):
|
||||
params['page_id'] = self.page.pk
|
||||
url = add_url_parameters(url, params)
|
||||
admin_menu.add_sideframe_item(_('Pages'), url=url, position=0)
|
||||
# Used to prevent duplicates
|
||||
self._changed_admin_menu = True
|
||||
|
||||
def add_page_menu(self):
|
||||
if self.page and self.has_page_change_permission():
|
||||
@@ -523,7 +552,8 @@ class PageToolbar(CMSToolbar):
|
||||
|
||||
revert_action = admin_reverse('cms_page_revert_page', args=(self.page.pk, self.current_lang))
|
||||
revert_question = _('Are you sure you want to revert to live?')
|
||||
is_enabled = self.page.is_dirty(self.current_lang) and self.page.publisher_public
|
||||
history_menu.add_ajax_item(_('Revert to live'), action=revert_action, question=revert_question,
|
||||
disabled=not self.page.is_dirty(self.current_lang),
|
||||
on_success=refresh, extra_classes=('cms_toolbar-revert',))
|
||||
disabled=not is_enabled,
|
||||
on_success=refresh, extra_classes=('cms-toolbar-revert',))
|
||||
history_menu.add_modal_item(_('View history'), url=admin_reverse('cms_page_history', args=(self.page.pk,)))
|
||||
|
||||
@@ -18,3 +18,9 @@ PUBLISHER_STATE_DIRTY = 1
|
||||
PUBLISHER_STATE_PENDING = 4
|
||||
|
||||
PAGE_TYPES_ID = "page_types"
|
||||
|
||||
VISIBILITY_ALL = None
|
||||
VISIBILITY_USERS = 1
|
||||
VISIBILITY_ANONYMOUS = 2
|
||||
|
||||
PAGE_USERNAME_MAX_LENGTH = 255
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from cms.utils.conf import get_cms_setting
|
||||
from cms.utils import get_template_from_request
|
||||
import warnings
|
||||
|
||||
|
||||
def cms_settings(request):
|
||||
"""
|
||||
@@ -12,10 +12,3 @@ def cms_settings(request):
|
||||
'CMS_MEDIA_URL': get_cms_setting('MEDIA_URL'),
|
||||
'CMS_TEMPLATE': lambda: get_template_from_request(request),
|
||||
}
|
||||
|
||||
|
||||
def media(request):
|
||||
warnings.warn('cms.context_processors.media has been deprecated in favor of '
|
||||
'cms.context_processors.cms_settings. Please update your '
|
||||
'configuration', DeprecationWarning)
|
||||
return cms_settings(request)
|
||||
|
||||
+2
-2
@@ -21,6 +21,7 @@ class AppAlreadyRegistered(Exception):
|
||||
class ToolbarAlreadyRegistered(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ToolbarNotRegistered(Exception):
|
||||
pass
|
||||
|
||||
@@ -47,7 +48,7 @@ class PermissionsException(Exception):
|
||||
|
||||
|
||||
class NoPermissionsException(PermissionsException):
|
||||
"""Can be fired when some violate action is performed on permission system.
|
||||
"""Can be fired when some violate action is performed on permission system.
|
||||
"""
|
||||
|
||||
|
||||
@@ -81,4 +82,3 @@ class PluginConsistencyError(Exception): pass
|
||||
|
||||
|
||||
class PlaceholderNotFound(Exception): pass
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ class PageExtensionAdmin(ExtensionAdmin):
|
||||
"""
|
||||
return {}
|
||||
|
||||
def queryset(self, request):
|
||||
return super(PageExtensionAdmin, self).queryset(request).filter(extended_object__publisher_is_draft=True)
|
||||
def get_queryset(self, request):
|
||||
return super(PageExtensionAdmin, self).get_queryset(request).filter(extended_object__publisher_is_draft=True)
|
||||
|
||||
@csrf_protect_m
|
||||
def add_view(self, request, form_url='', extra_context=None):
|
||||
@@ -49,7 +49,7 @@ class PageExtensionAdmin(ExtensionAdmin):
|
||||
extension = self.model.objects.get(extended_object=page)
|
||||
opts = self.model._meta
|
||||
change_url = reverse('admin:%s_%s_change' %
|
||||
(opts.app_label, opts.module_name),
|
||||
(opts.app_label, opts.model_name),
|
||||
args=(extension.pk,),
|
||||
current_app=self.admin_site.name)
|
||||
return HttpResponseRedirect(change_url)
|
||||
@@ -81,8 +81,8 @@ class TitleExtensionAdmin(ExtensionAdmin):
|
||||
"""
|
||||
return {}
|
||||
|
||||
def queryset(self, request):
|
||||
return super(TitleExtensionAdmin, self).queryset(request).filter(extended_object__page__publisher_is_draft=True)
|
||||
def get_queryset(self, request):
|
||||
return super(TitleExtensionAdmin, self).get_queryset(request).filter(extended_object__page__publisher_is_draft=True)
|
||||
|
||||
@csrf_protect_m
|
||||
def add_view(self, request, form_url='', extra_context=None):
|
||||
@@ -96,11 +96,10 @@ class TitleExtensionAdmin(ExtensionAdmin):
|
||||
extension = self.model.objects.get(extended_object=title)
|
||||
opts = self.model._meta
|
||||
change_url = reverse('admin:%s_%s_change' %
|
||||
(opts.app_label, opts.module_name),
|
||||
(opts.app_label, opts.model_name),
|
||||
args=(extension.pk,),
|
||||
current_app=self.admin_site.name)
|
||||
return HttpResponseRedirect(change_url)
|
||||
except self.model.DoesNotExist:
|
||||
pass
|
||||
return super(ExtensionAdmin, self).add_view(request, form_url, extra_context)
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ class ExtensionPool(object):
|
||||
|
||||
def copy_extensions(self, source_page, target_page, languages=None):
|
||||
if not languages:
|
||||
languages = source_page.get_languages()
|
||||
languages = target_page.get_languages()
|
||||
if self.page_extensions:
|
||||
self._copy_page_extensions(source_page, target_page, None, clone=True)
|
||||
self._remove_orphaned_page_extensions()
|
||||
|
||||
@@ -116,6 +116,3 @@ class TitleExtension(BaseExtension):
|
||||
Title.objects.filter(pk=self.extended_object.pk).update(
|
||||
publisher_state=PUBLISHER_STATE_DIRTY) # mark title dirty
|
||||
return super(BaseExtension, self).delete(*args, **kwargs)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
from cms.utils.urlutils import admin_reverse
|
||||
from cms.api import get_page_draft
|
||||
from cms.toolbar_base import CMSToolbar
|
||||
from cms.utils import get_cms_setting
|
||||
from cms.utils import get_cms_setting, get_language_list
|
||||
from cms.utils.permissions import has_page_change_permission
|
||||
from django.core.urlresolvers import NoReverseMatch
|
||||
|
||||
@@ -99,13 +99,14 @@ class ExtensionToolbar(CMSToolbar):
|
||||
except self.model.DoesNotExist:
|
||||
page_extension = None
|
||||
try:
|
||||
model_name = self.model.__name__.lower()
|
||||
if page_extension:
|
||||
admin_url = admin_reverse(
|
||||
'%s_%s_change' % (self.model._meta.app_label, self.model._meta.model_name),
|
||||
'%s_%s_change' % (self.model._meta.app_label, model_name),
|
||||
args=(page_extension.pk,))
|
||||
else:
|
||||
admin_url = "%s?extended_object=%s" % (
|
||||
admin_reverse('%s_%s_add' % (self.model._meta.app_label, self.model._meta.model_name)),
|
||||
admin_reverse('%s_%s_add' % (self.model._meta.app_label, model_name)),
|
||||
self.page.pk)
|
||||
except NoReverseMatch: # pragma: no cover
|
||||
admin_url = None
|
||||
@@ -125,7 +126,7 @@ class ExtensionToolbar(CMSToolbar):
|
||||
if language:
|
||||
titles = page.get_title_obj(language),
|
||||
else:
|
||||
titles = page.title_set.all()
|
||||
titles = page.title_set.filter(language__in=get_language_list(page.site_id))
|
||||
# Titles
|
||||
for title in titles:
|
||||
try:
|
||||
@@ -133,13 +134,14 @@ class ExtensionToolbar(CMSToolbar):
|
||||
except self.model.DoesNotExist:
|
||||
title_extension = None
|
||||
try:
|
||||
model_name = self.model.__name__.lower()
|
||||
if title_extension:
|
||||
admin_url = admin_reverse(
|
||||
'%s_%s_change' % (self.model._meta.app_label, self.model._meta.model_name),
|
||||
'%s_%s_change' % (self.model._meta.app_label, model_name),
|
||||
args=(title_extension.pk,))
|
||||
else:
|
||||
admin_url = "%s?extended_object=%s" % (
|
||||
admin_reverse('%s_%s_add' % (self.model._meta.app_label, self.model._meta.model_name)),
|
||||
admin_reverse('%s_%s_add' % (self.model._meta.app_label, model_name)),
|
||||
title.pk)
|
||||
except NoReverseMatch: # pragma: no cover
|
||||
admin_url = None
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
|
||||
+5
-5
@@ -1,12 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import six
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django import forms
|
||||
from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
|
||||
from django.forms.fields import EMPTY_VALUES
|
||||
from cms.models.pagemodel import Page
|
||||
from cms.forms.widgets import PageSelectWidget, PageSmartLinkWidget
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from cms.forms.utils import get_site_choices, get_page_choices
|
||||
from cms.forms.widgets import PageSelectWidget, PageSmartLinkWidget
|
||||
from cms.models.pagemodel import Page
|
||||
|
||||
|
||||
class SuperLazyIterator(object):
|
||||
@@ -84,5 +84,5 @@ class PageSmartLinkField(forms.CharField):
|
||||
|
||||
def widget_attrs(self, widget):
|
||||
attrs = super(PageSmartLinkField, self).widget_attrs(widget)
|
||||
attrs.update({'placeholder_text': six.text_type(self.placeholder_text)})
|
||||
attrs.update({'placeholder_text': self.placeholder_text})
|
||||
return attrs
|
||||
|
||||
+21
-47
@@ -1,26 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from collections import defaultdict
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
from django.utils.datastructures import SortedDict as OrderedDict
|
||||
|
||||
from cms.exceptions import LanguageError
|
||||
from cms.models import Page
|
||||
from cms.models.titlemodels import Title
|
||||
from cms.utils import i18n
|
||||
from cms.utils.conf import get_cms_setting
|
||||
from django.conf import settings
|
||||
from django.contrib.sites.models import Site
|
||||
from django.db.models.signals import post_save, post_delete
|
||||
from django.utils.datastructures import SortedDict
|
||||
from django.utils.html import escape
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from cms.cache.choices import (
|
||||
clean_site_choices_cache, clean_page_choices_cache,
|
||||
_site_cache_key, _page_cache_key)
|
||||
from cms.exceptions import LanguageError
|
||||
from cms.models import Page, Title
|
||||
from cms.utils import i18n
|
||||
|
||||
|
||||
def update_site_and_page_choices(lang=None):
|
||||
lang = lang or i18n.get_current_language()
|
||||
SITE_CHOICES_KEY = get_site_cache_key(lang)
|
||||
PAGE_CHOICES_KEY = get_page_cache_key(lang)
|
||||
SITE_CHOICES_KEY = _site_cache_key(lang)
|
||||
PAGE_CHOICES_KEY = _page_cache_key(lang)
|
||||
title_queryset = (Title.objects.drafts()
|
||||
.select_related('page', 'page__site')
|
||||
.order_by('page__tree_id', 'page__lft', 'page__rght'))
|
||||
pages = defaultdict(SortedDict)
|
||||
.select_related('page', 'page__site')
|
||||
.order_by('page__path'))
|
||||
pages = defaultdict(OrderedDict)
|
||||
sites = {}
|
||||
for title in title_queryset:
|
||||
page = pages[title.page.site.pk].get(title.page.pk, {})
|
||||
@@ -50,8 +55,8 @@ def update_site_and_page_choices(lang=None):
|
||||
if not title:
|
||||
continue
|
||||
|
||||
indent = u" " * title.page.level
|
||||
page_title = mark_safe(u"%s%s" % (indent, title.title))
|
||||
indent = u" " * (title.page.depth - 1)
|
||||
page_title = mark_safe(u"%s%s" % (indent, escape(title.title)))
|
||||
site_page_choices.append((title.page.pk, page_title))
|
||||
|
||||
page_choices.append((sitename, site_page_choices))
|
||||
@@ -65,7 +70,7 @@ def update_site_and_page_choices(lang=None):
|
||||
def get_site_choices(lang=None):
|
||||
from django.core.cache import cache
|
||||
lang = lang or i18n.get_current_language()
|
||||
site_choices = cache.get(get_site_cache_key(lang))
|
||||
site_choices = cache.get(_site_cache_key(lang))
|
||||
if site_choices is None:
|
||||
site_choices, page_choices = update_site_and_page_choices(lang)
|
||||
return site_choices
|
||||
@@ -74,43 +79,12 @@ def get_site_choices(lang=None):
|
||||
def get_page_choices(lang=None):
|
||||
from django.core.cache import cache
|
||||
lang = lang or i18n.get_current_language()
|
||||
page_choices = cache.get(get_page_cache_key(lang))
|
||||
page_choices = cache.get(_page_cache_key(lang))
|
||||
if page_choices is None:
|
||||
site_choices, page_choices = update_site_and_page_choices(lang)
|
||||
return page_choices
|
||||
|
||||
|
||||
def _get_key(prefix, lang):
|
||||
return "%s-%s" % (prefix, lang)
|
||||
|
||||
|
||||
def get_site_cache_key(lang):
|
||||
return _get_key(get_cms_setting('SITE_CHOICES_CACHE_KEY'), lang)
|
||||
|
||||
|
||||
def get_page_cache_key(lang):
|
||||
return _get_key(get_cms_setting('PAGE_CHOICES_CACHE_KEY'), lang)
|
||||
|
||||
|
||||
def _clean_many(prefix):
|
||||
from django.core.cache import cache
|
||||
keys = []
|
||||
if settings.USE_I18N:
|
||||
for lang in [language[0] for language in settings.LANGUAGES]:
|
||||
keys.append(_get_key(prefix, lang))
|
||||
else:
|
||||
keys = [_get_key(prefix, settings.LANGUAGE_CODE)]
|
||||
cache.delete_many(keys)
|
||||
|
||||
|
||||
def clean_site_choices_cache(sender, **kwargs):
|
||||
_clean_many(get_cms_setting('SITE_CHOICES_CACHE_KEY'))
|
||||
|
||||
|
||||
def clean_page_choices_cache(sender, **kwargs):
|
||||
_clean_many(get_cms_setting('PAGE_CHOICES_CACHE_KEY'))
|
||||
|
||||
|
||||
post_save.connect(clean_page_choices_cache, sender=Page)
|
||||
post_save.connect(clean_site_choices_cache, sender=Site)
|
||||
post_delete.connect(clean_page_choices_cache, sender=Page)
|
||||
|
||||
+20
-17
@@ -3,17 +3,18 @@
|
||||
from itertools import chain
|
||||
|
||||
from django.contrib.admin.templatetags.admin_static import static
|
||||
from django.contrib.auth import get_permission_codename
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.urlresolvers import NoReverseMatch, reverse_lazy
|
||||
from django.forms.widgets import Select, MultiWidget, TextInput
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.html import escape, escapejs
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from cms.forms.utils import get_site_choices, get_page_choices
|
||||
from cms.models import Page, PageUser
|
||||
from cms.templatetags.cms_admin import CMS_ADMIN_ICON_BASE
|
||||
from cms.utils.compat.dj import force_unicode
|
||||
|
||||
|
||||
class PageSelectWidget(MultiWidget):
|
||||
@@ -39,11 +40,11 @@ class PageSelectWidget(MultiWidget):
|
||||
return [site.pk, page.pk, page.pk]
|
||||
site = Site.objects.get_current()
|
||||
return [site.pk,None,None]
|
||||
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
# THIS IS A COPY OF django.forms.widgets.Widget._has_changed()
|
||||
# (except for the first if statement)
|
||||
|
||||
|
||||
"""
|
||||
Return True if data differs from initial.
|
||||
"""
|
||||
@@ -58,14 +59,14 @@ class PageSelectWidget(MultiWidget):
|
||||
initial_value = u''
|
||||
else:
|
||||
initial_value = initial
|
||||
if force_unicode(initial_value) != force_unicode(data_value):
|
||||
if force_text(initial_value) != force_text(data_value):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def render(self, name, value, attrs=None):
|
||||
# THIS IS A COPY OF django.forms.widgets.MultiWidget.render()
|
||||
# (except for the last line)
|
||||
|
||||
|
||||
# value is a list of values, each corresponding to a widget
|
||||
# in self.widgets.
|
||||
|
||||
@@ -102,9 +103,11 @@ class PageSelectWidget(MultiWidget):
|
||||
var handlePageChange = function(page_id) {
|
||||
if (page_id) {
|
||||
$("#id_%(name)s_2 option").attr('selected', false);
|
||||
$("#id_%(name)s_2 option[value=" + page_id + "]").attr('selected', true);
|
||||
$("#id_%(name)s_2 option[value='" + page_id + "']").attr('selected', true);
|
||||
} else {
|
||||
$("#id_%(name)s_2 option[value=]").attr('selected', true);
|
||||
if ($("#id_%(name)s_2").length) {
|
||||
$("#id_%(name)s_2 option[value='']").attr('selected', true);
|
||||
}
|
||||
};
|
||||
};
|
||||
$("#id_%(name)s_0").change(function(){
|
||||
@@ -122,7 +125,7 @@ class PageSelectWidget(MultiWidget):
|
||||
})(django.jQuery);
|
||||
</script>''' % {'name': name})
|
||||
return mark_safe(self.format_output(output))
|
||||
|
||||
|
||||
def format_output(self, rendered_widgets):
|
||||
return u' '.join(rendered_widgets)
|
||||
|
||||
@@ -187,7 +190,7 @@ class PageSmartLinkWidget(TextInput):
|
||||
'element_id': id_,
|
||||
'placeholder_text': final_attrs.get('placeholder_text', ''),
|
||||
'language_code': self.language,
|
||||
'ajax_url': force_unicode(self.ajax_url)
|
||||
'ajax_url': force_text(self.ajax_url)
|
||||
}]
|
||||
|
||||
output.append(super(PageSmartLinkWidget, self).render(name, value, attrs))
|
||||
@@ -207,14 +210,14 @@ class UserSelectAdminWidget(Select):
|
||||
"""Special widget used in page permission inlines, because we have to render
|
||||
an add user (plus) icon, but point it somewhere else - to special user creation
|
||||
view, which is accessible only if user haves "add user" permissions.
|
||||
|
||||
Current user should be assigned to widget in form constructor as an user
|
||||
|
||||
Current user should be assigned to widget in form constructor as an user
|
||||
attribute.
|
||||
"""
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
output = [super(UserSelectAdminWidget, self).render(name, value, attrs, choices)]
|
||||
output = [super(UserSelectAdminWidget, self).render(name, value, attrs, choices)]
|
||||
if hasattr(self, 'user') and (self.user.is_superuser or \
|
||||
self.user.has_perm(PageUser._meta.app_label + '.' + PageUser._meta.get_add_permission())):
|
||||
self.user.has_perm(PageUser._meta.app_label + '.' + get_permission_codename('add', PageUser._meta))):
|
||||
# append + icon
|
||||
add_url = '../../../cms/pageuser/add/'
|
||||
output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
|
||||
@@ -250,7 +253,7 @@ class AppHookSelect(Select):
|
||||
selected_html = ''
|
||||
|
||||
if option_value in self.app_namespaces:
|
||||
data_html = mark_safe(' data-namespace="%s"' % self.app_namespaces[option_value])
|
||||
data_html = mark_safe(' data-namespace="%s"' % escape(self.app_namespaces[option_value]))
|
||||
else:
|
||||
data_html = ''
|
||||
|
||||
@@ -289,11 +292,11 @@ class ApplicationConfigSelect(Select):
|
||||
super(ApplicationConfigSelect, self).__init__(attrs, choices)
|
||||
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
output = [super(ApplicationConfigSelect, self).render(name, value, attrs, choices)]
|
||||
output = list(super(ApplicationConfigSelect, self).render(name, value, attrs, choices))
|
||||
output.append('<script>\n')
|
||||
output.append('var apphooks_configuration = {\n')
|
||||
for application, cms_app in self.app_configs.items():
|
||||
output.append("'%s': [%s]," % (application, ",".join(["['%s', '%s']" % (config.pk, force_text(config)) for config in cms_app.get_configs()])))
|
||||
output.append("'%s': [%s]," % (application, ",".join(["['%s', '%s']" % (config.pk, escapejs(escape(config))) for config in cms_app.get_configs()]))) # noqa
|
||||
output.append('\n};\n')
|
||||
output.append('var apphooks_configuration_url = {\n')
|
||||
for application, cms_app in self.app_configs.items():
|
||||
|
||||
Binary file not shown.
+861
-592
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -7,16 +7,17 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-cms\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-12 21:31+0100\n"
|
||||
"PO-Revision-Date: 2013-12-12 08:53+0000\n"
|
||||
"Last-Translator: Patrick Lauber <patrick.lauber@divio.ch>\n"
|
||||
"Language-Team: Afrikaans (http://www.transifex.com/projects/p/django-cms/language/af/)\n"
|
||||
"POT-Creation-Date: 2015-11-05 10:16-0500\n"
|
||||
"PO-Revision-Date: 2015-10-31 12:28+0000\n"
|
||||
"Last-Translator: yakky <i.spalletti@nephila.it>\n"
|
||||
"Language-Team: Afrikaans (http://www.transifex.com/divio/django-cms/language/af/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: af\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/cms/js/modules/cms.changeform.js:60
|
||||
#: static/cms/js/dist/bundle.admin.changeform.min.js:1
|
||||
#: static/cms/js/modules/cms.changeform.js:75
|
||||
msgid "Are you sure you want to change tabs without saving the page first?"
|
||||
msgstr ""
|
||||
|
||||
Binary file not shown.
+888
-614
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -3,21 +3,23 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
# Translators:
|
||||
# Abdelkader Maarouf <dep2g2@gmail.com>, 2015
|
||||
# Ahmed H <info@draco-003.com>, 2013
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-cms\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-12 21:31+0100\n"
|
||||
"PO-Revision-Date: 2013-12-12 08:53+0000\n"
|
||||
"Last-Translator: Patrick Lauber <patrick.lauber@divio.ch>\n"
|
||||
"Language-Team: Arabic (http://www.transifex.com/projects/p/django-cms/language/ar/)\n"
|
||||
"POT-Creation-Date: 2015-11-05 10:16-0500\n"
|
||||
"PO-Revision-Date: 2015-10-31 12:28+0000\n"
|
||||
"Last-Translator: yakky <i.spalletti@nephila.it>\n"
|
||||
"Language-Team: Arabic (http://www.transifex.com/divio/django-cms/language/ar/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ar\n"
|
||||
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
|
||||
|
||||
#: static/cms/js/modules/cms.changeform.js:60
|
||||
#: static/cms/js/dist/bundle.admin.changeform.min.js:1
|
||||
#: static/cms/js/modules/cms.changeform.js:75
|
||||
msgid "Are you sure you want to change tabs without saving the page first?"
|
||||
msgstr "هل أنت متأكد من الإنتقال الى مكان آخر دون حفظ الصفحة أولاً؟"
|
||||
msgstr "هل أنت متأكد من الإنتقال الى jfويب أخر دون حفظ الصفحة أولاً؟"
|
||||
|
||||
Binary file not shown.
+864
-593
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -7,16 +7,17 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-cms\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-12 21:31+0100\n"
|
||||
"PO-Revision-Date: 2013-12-12 08:53+0000\n"
|
||||
"Last-Translator: Patrick Lauber <patrick.lauber@divio.ch>\n"
|
||||
"Language-Team: Belarusian (http://www.transifex.com/projects/p/django-cms/language/be/)\n"
|
||||
"POT-Creation-Date: 2015-11-05 10:16-0500\n"
|
||||
"PO-Revision-Date: 2015-10-31 12:28+0000\n"
|
||||
"Last-Translator: yakky <i.spalletti@nephila.it>\n"
|
||||
"Language-Team: Belarusian (http://www.transifex.com/divio/django-cms/language/be/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: be\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
|
||||
|
||||
#: static/cms/js/modules/cms.changeform.js:60
|
||||
#: static/cms/js/dist/bundle.admin.changeform.min.js:1
|
||||
#: static/cms/js/modules/cms.changeform.js:75
|
||||
msgid "Are you sure you want to change tabs without saving the page first?"
|
||||
msgstr ""
|
||||
|
||||
Binary file not shown.
+908
-638
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -10,16 +10,17 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-cms\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-12 21:31+0100\n"
|
||||
"PO-Revision-Date: 2013-12-12 08:53+0000\n"
|
||||
"Last-Translator: Patrick Lauber <patrick.lauber@divio.ch>\n"
|
||||
"Language-Team: Bulgarian (http://www.transifex.com/projects/p/django-cms/language/bg/)\n"
|
||||
"POT-Creation-Date: 2015-11-05 10:16-0500\n"
|
||||
"PO-Revision-Date: 2015-10-31 12:28+0000\n"
|
||||
"Last-Translator: yakky <i.spalletti@nephila.it>\n"
|
||||
"Language-Team: Bulgarian (http://www.transifex.com/divio/django-cms/language/bg/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: bg\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/cms/js/modules/cms.changeform.js:60
|
||||
#: static/cms/js/dist/bundle.admin.changeform.min.js:1
|
||||
#: static/cms/js/modules/cms.changeform.js:75
|
||||
msgid "Are you sure you want to change tabs without saving the page first?"
|
||||
msgstr "Наистина ли искате да промените раздели без първо да запазите страницата?"
|
||||
|
||||
Binary file not shown.
+868
-599
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -8,16 +8,17 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-cms\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-12 21:31+0100\n"
|
||||
"PO-Revision-Date: 2013-12-12 08:53+0000\n"
|
||||
"Last-Translator: Patrick Lauber <patrick.lauber@divio.ch>\n"
|
||||
"Language-Team: Bengali (http://www.transifex.com/projects/p/django-cms/language/bn/)\n"
|
||||
"POT-Creation-Date: 2015-11-05 10:16-0500\n"
|
||||
"PO-Revision-Date: 2015-10-31 12:28+0000\n"
|
||||
"Last-Translator: yakky <i.spalletti@nephila.it>\n"
|
||||
"Language-Team: Bengali (http://www.transifex.com/divio/django-cms/language/bn/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: bn\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/cms/js/modules/cms.changeform.js:60
|
||||
#: static/cms/js/dist/bundle.admin.changeform.min.js:1
|
||||
#: static/cms/js/modules/cms.changeform.js:75
|
||||
msgid "Are you sure you want to change tabs without saving the page first?"
|
||||
msgstr "আপনি কি নিশ্চিত যে, আপনি পৃষ্ঠাটি সংরক্ষণ না করেই ট্যাব পরিবর্তন করতে চান?"
|
||||
|
||||
Binary file not shown.
+886
-616
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -3,21 +3,22 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
# Translators:
|
||||
# Roger Pons <rogerpons@gmail.com>, 2012
|
||||
# Roger Pons <rogerpons@gmail.com>, 2012,2015
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-cms\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-12 21:31+0100\n"
|
||||
"PO-Revision-Date: 2013-12-12 08:53+0000\n"
|
||||
"Last-Translator: Patrick Lauber <patrick.lauber@divio.ch>\n"
|
||||
"Language-Team: Catalan (http://www.transifex.com/projects/p/django-cms/language/ca/)\n"
|
||||
"POT-Creation-Date: 2015-11-05 10:16-0500\n"
|
||||
"PO-Revision-Date: 2015-10-31 12:28+0000\n"
|
||||
"Last-Translator: yakky <i.spalletti@nephila.it>\n"
|
||||
"Language-Team: Catalan (http://www.transifex.com/divio/django-cms/language/ca/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ca\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/cms/js/modules/cms.changeform.js:60
|
||||
#: static/cms/js/dist/bundle.admin.changeform.min.js:1
|
||||
#: static/cms/js/modules/cms.changeform.js:75
|
||||
msgid "Are you sure you want to change tabs without saving the page first?"
|
||||
msgstr "Estàs segur que voleu canviar de secció sense guardar la primera pàgina?"
|
||||
msgstr "Esteu segur que voleu canviar de secció sense guardar la primera pàgina?"
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -7,16 +7,17 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-cms\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-12 21:31+0100\n"
|
||||
"PO-Revision-Date: 2013-12-12 08:53+0000\n"
|
||||
"Last-Translator: Patrick Lauber <patrick.lauber@divio.ch>\n"
|
||||
"Language-Team: Chinese (Mandarin) (http://www.transifex.com/projects/p/django-cms/language/cmn/)\n"
|
||||
"POT-Creation-Date: 2015-11-05 10:16-0500\n"
|
||||
"PO-Revision-Date: 2016-01-19 20:20+0000\n"
|
||||
"Last-Translator: Martin Koistinen <mkoistinen@gmail.com>\n"
|
||||
"Language-Team: Chinese (Mandarin) (http://www.transifex.com/divio/django-cms/language/cmn/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: cmn\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#: static/cms/js/modules/cms.changeform.js:60
|
||||
#: static/cms/js/dist/bundle.admin.changeform.min.js:1
|
||||
#: static/cms/js/modules/cms.changeform.js:75
|
||||
msgid "Are you sure you want to change tabs without saving the page first?"
|
||||
msgstr ""
|
||||
|
||||
Binary file not shown.
+924
-649
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -5,20 +5,22 @@
|
||||
# Translators:
|
||||
# Jakub Dorňák <jdornak@redhat.com>, 2013
|
||||
# xlu <xlu@seznam.cz>, 2013
|
||||
# xlu <xlu@seznam.cz>, 2013
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-cms\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-12 21:31+0100\n"
|
||||
"PO-Revision-Date: 2013-12-12 08:53+0000\n"
|
||||
"Last-Translator: Patrick Lauber <patrick.lauber@divio.ch>\n"
|
||||
"Language-Team: Czech (http://www.transifex.com/projects/p/django-cms/language/cs/)\n"
|
||||
"POT-Creation-Date: 2015-11-05 10:16-0500\n"
|
||||
"PO-Revision-Date: 2015-10-31 12:28+0000\n"
|
||||
"Last-Translator: yakky <i.spalletti@nephila.it>\n"
|
||||
"Language-Team: Czech (http://www.transifex.com/divio/django-cms/language/cs/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: cs\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
|
||||
|
||||
#: static/cms/js/modules/cms.changeform.js:60
|
||||
#: static/cms/js/dist/bundle.admin.changeform.min.js:1
|
||||
#: static/cms/js/modules/cms.changeform.js:75
|
||||
msgid "Are you sure you want to change tabs without saving the page first?"
|
||||
msgstr "Určitě chcete změnit záložku bez uložení změn?"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-cms\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-11-05 10:16-0500\n"
|
||||
"PO-Revision-Date: 2010-11-30 06:49+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Czech (Czech Republic) (http://www.transifex.com/divio/django-cms/language/cs_CZ/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: cs_CZ\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
|
||||
|
||||
#: static/cms/js/dist/bundle.admin.changeform.min.js:1
|
||||
#: static/cms/js/modules/cms.changeform.js:75
|
||||
msgid "Are you sure you want to change tabs without saving the page first?"
|
||||
msgstr ""
|
||||
Binary file not shown.
+863
-592
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -7,16 +7,17 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-cms\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-12 21:31+0100\n"
|
||||
"PO-Revision-Date: 2013-12-12 08:53+0000\n"
|
||||
"Last-Translator: Patrick Lauber <patrick.lauber@divio.ch>\n"
|
||||
"Language-Team: Welsh (http://www.transifex.com/projects/p/django-cms/language/cy/)\n"
|
||||
"POT-Creation-Date: 2015-11-05 10:16-0500\n"
|
||||
"PO-Revision-Date: 2015-10-31 12:28+0000\n"
|
||||
"Last-Translator: yakky <i.spalletti@nephila.it>\n"
|
||||
"Language-Team: Welsh (http://www.transifex.com/divio/django-cms/language/cy/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: cy\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3;\n"
|
||||
|
||||
#: static/cms/js/modules/cms.changeform.js:60
|
||||
#: static/cms/js/dist/bundle.admin.changeform.min.js:1
|
||||
#: static/cms/js/modules/cms.changeform.js:75
|
||||
msgid "Are you sure you want to change tabs without saving the page first?"
|
||||
msgstr ""
|
||||
|
||||
Binary file not shown.
+881
-610
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -5,20 +5,22 @@
|
||||
# Translators:
|
||||
# Kristian Øllegaard <kristian@oellegaard.com>, 2011
|
||||
# Kristian Øllegaard <kristian@oellegaard.com>, 2011
|
||||
# Kristian Øllegaard <kristian@oellegaard.com>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-cms\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-12 21:31+0100\n"
|
||||
"PO-Revision-Date: 2013-12-12 08:53+0000\n"
|
||||
"Last-Translator: Patrick Lauber <patrick.lauber@divio.ch>\n"
|
||||
"Language-Team: Danish (http://www.transifex.com/projects/p/django-cms/language/da/)\n"
|
||||
"POT-Creation-Date: 2015-11-05 10:16-0500\n"
|
||||
"PO-Revision-Date: 2015-10-31 12:28+0000\n"
|
||||
"Last-Translator: yakky <i.spalletti@nephila.it>\n"
|
||||
"Language-Team: Danish (http://www.transifex.com/divio/django-cms/language/da/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: da\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/cms/js/modules/cms.changeform.js:60
|
||||
#: static/cms/js/dist/bundle.admin.changeform.min.js:1
|
||||
#: static/cms/js/modules/cms.changeform.js:75
|
||||
msgid "Are you sure you want to change tabs without saving the page first?"
|
||||
msgstr "Er du sikker på du vil ændre tabs uden at gemme siden først?"
|
||||
|
||||
Binary file not shown.
+914
-636
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -5,20 +5,22 @@
|
||||
# Translators:
|
||||
# Jonas Obrist <me@ojii.ch>, 2011
|
||||
# Michael P. Jung <michael.jung@terreon.de>, 2013
|
||||
# Stefan T. Oertel <me@schneck.cc>, 2015
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-cms\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-12 21:31+0100\n"
|
||||
"PO-Revision-Date: 2013-12-12 08:53+0000\n"
|
||||
"Last-Translator: Patrick Lauber <patrick.lauber@divio.ch>\n"
|
||||
"Language-Team: German (http://www.transifex.com/projects/p/django-cms/language/de/)\n"
|
||||
"POT-Creation-Date: 2015-11-05 10:16-0500\n"
|
||||
"PO-Revision-Date: 2015-10-31 12:28+0000\n"
|
||||
"Last-Translator: yakky <i.spalletti@nephila.it>\n"
|
||||
"Language-Team: German (http://www.transifex.com/divio/django-cms/language/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: de\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/cms/js/modules/cms.changeform.js:60
|
||||
#: static/cms/js/dist/bundle.admin.changeform.min.js:1
|
||||
#: static/cms/js/modules/cms.changeform.js:75
|
||||
msgid "Are you sure you want to change tabs without saving the page first?"
|
||||
msgstr "Sind Sie sicher Sie wollen die Sprache ändern ohne vorher zu speichern?"
|
||||
|
||||
Binary file not shown.
+879
-608
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -8,16 +8,17 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-cms\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-12 21:31+0100\n"
|
||||
"PO-Revision-Date: 2013-12-12 08:53+0000\n"
|
||||
"Last-Translator: Patrick Lauber <patrick.lauber@divio.ch>\n"
|
||||
"Language-Team: Greek (http://www.transifex.com/projects/p/django-cms/language/el/)\n"
|
||||
"POT-Creation-Date: 2015-11-05 10:16-0500\n"
|
||||
"PO-Revision-Date: 2015-10-31 12:28+0000\n"
|
||||
"Last-Translator: yakky <i.spalletti@nephila.it>\n"
|
||||
"Language-Team: Greek (http://www.transifex.com/divio/django-cms/language/el/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: el\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/cms/js/modules/cms.changeform.js:60
|
||||
#: static/cms/js/dist/bundle.admin.changeform.min.js:1
|
||||
#: static/cms/js/modules/cms.changeform.js:75
|
||||
msgid "Are you sure you want to change tabs without saving the page first?"
|
||||
msgstr "Είσαι σίγουρος πως θες να αλλάξεις καρτέλες χωρίς να έχεις αποθηκεύσει πρώτα τη σελίδα?"
|
||||
|
||||
Binary file not shown.
+1258
-1000
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,22 +1,24 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
# Translators:
|
||||
# Jonas Obrist <ojiidotch@gmail.com>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Project-Id-Version: django-cms\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-02-22 19:10+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"POT-Creation-Date: 2016-04-26 12:22-0400\n"
|
||||
"PO-Revision-Date: 2015-10-31 12:28+0000\n"
|
||||
"Last-Translator: yakky <i.spalletti@nephila.it>\n"
|
||||
"Language-Team: English (http://www.transifex.com/digi604/django-cms/language/"
|
||||
"en/)\n"
|
||||
"Language: en\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/cms/js/modules/cms.changeform.js:60
|
||||
msgid "Are you sure you want to change tabs without saving the page first?"
|
||||
msgstr ""
|
||||
msgstr "Are you sure you want to change tabs without saving the page first?"
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,23 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django-cms\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-11-05 10:16-0500\n"
|
||||
"PO-Revision-Date: 2010-11-30 06:49+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: English (United Kingdom) (http://www.transifex.com/divio/django-cms/language/en_GB/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: en_GB\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: static/cms/js/dist/bundle.admin.changeform.min.js:1
|
||||
#: static/cms/js/modules/cms.changeform.js:75
|
||||
msgid "Are you sure you want to change tabs without saving the page first?"
|
||||
msgstr ""
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user