Compare commits
1104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b725a9db9 | ||
|
|
989ebbb444 | ||
|
|
0a6efc1e0f | ||
|
|
d577a0d286 | ||
|
|
6b9b379ce2 | ||
|
|
13234b6fd5 | ||
|
|
2fa0067663 | ||
|
|
4e37fa5778 | ||
|
|
bfb74448b1 | ||
|
|
a255082b07 | ||
|
|
14df35bd90 | ||
|
|
bd0ba7baa5 | ||
|
|
9aa220b95b | ||
|
|
3ed4be63fe | ||
|
|
23f4b0b62f | ||
|
|
4b9acb64da | ||
|
|
ebba0ee0cb | ||
|
|
335ce48d7e | ||
|
|
d9a0c8c523 | ||
|
|
a297bd1944 | ||
|
|
953ea26984 | ||
|
|
e4f80f7660 | ||
|
|
128a185957 | ||
|
|
0bdd14b47a | ||
|
|
3b84b181ad | ||
|
|
c9b0626324 | ||
|
|
dc9a82cade | ||
|
|
8266733e34 | ||
|
|
246987955b | ||
|
|
b93e7fcb60 | ||
|
|
b1cebdbd99 | ||
|
|
d04047abd5 | ||
|
|
efca46945a | ||
|
|
0f9755e36f | ||
|
|
478d8e4116 | ||
|
|
81693e042c | ||
|
|
47b7d7b36c | ||
|
|
ba15c34ce1 | ||
|
|
94f2ad9325 | ||
|
|
d8070ba8a3 | ||
|
|
b1019672b0 | ||
|
|
631307a4d5 | ||
|
|
180a26ee1d | ||
|
|
7eab1982fe | ||
|
|
ca59237ebf | ||
|
|
cc92210dc2 | ||
|
|
6602afdd6c | ||
|
|
c7a04bc08a | ||
|
|
2cc5b7f4e8 | ||
|
|
453f16af03 | ||
|
|
0f3398ae13 | ||
|
|
f1b65c8695 | ||
|
|
2c4c89c8c2 | ||
|
|
4042b603b7 | ||
|
|
63b0288383 | ||
|
|
7c01fee70b | ||
|
|
8127c32ef5 | ||
|
|
563decdfba | ||
|
|
a205b01d70 | ||
|
|
b4290384e1 | ||
|
|
0f76779fb1 | ||
|
|
f34c528cba | ||
|
|
cf01e04101 | ||
|
|
a3a63def55 | ||
|
|
a3489eea04 | ||
|
|
c6cb98c30a | ||
|
|
332c58c82f | ||
|
|
beb0ded6dc | ||
|
|
b49b2035bd | ||
|
|
106c8d373d | ||
|
|
aee44a3284 | ||
|
|
d4c1fcf838 | ||
|
|
832f57c9d7 | ||
|
|
ac2a9b207d | ||
|
|
f1e5d60a14 | ||
|
|
7b1a1dc754 | ||
|
|
c93f804992 | ||
|
|
1cba4b1d45 | ||
|
|
22369a5559 | ||
|
|
a8223ad354 | ||
|
|
c9d3cf7cac | ||
|
|
bbdbc94f6e | ||
|
|
5c8d9c4dca | ||
|
|
546ff6e42f | ||
|
|
7b7d45ce2e | ||
|
|
be3ca7c561 | ||
|
|
abdb6e2d52 | ||
|
|
138ddcdcd7 | ||
|
|
8ffc6550da | ||
|
|
0734715bab | ||
|
|
7528bfb10b | ||
|
|
2798fb3468 | ||
|
|
4e6f4716ec | ||
|
|
e523a4e610 | ||
|
|
31cec76809 | ||
|
|
fdfd9f9275 | ||
|
|
b658c73c19 | ||
|
|
ebd3e6f31a | ||
|
|
ccec114653 | ||
|
|
f0716dc482 | ||
|
|
513778b2c4 | ||
|
|
742e403ae2 | ||
|
|
09a9d610f8 | ||
|
|
b9534f23f5 | ||
|
|
b053f61001 | ||
|
|
21042f2111 | ||
|
|
e953474138 | ||
|
|
0d438ad07c | ||
|
|
e285b7cff0 | ||
|
|
2bb2f30e66 | ||
|
|
9a8d23f582 | ||
|
|
f37d12e056 | ||
|
|
334ffc0be7 | ||
|
|
03f0da4ee6 | ||
|
|
fbbd6eebc0 | ||
|
|
584ced87db | ||
|
|
901953d988 | ||
|
|
8c34a47138 | ||
|
|
0fe3db634c | ||
|
|
d8d838fc4f | ||
|
|
9b94a1b3b2 | ||
|
|
479abc1a65 | ||
|
|
1a17ba13ca | ||
|
|
371c42b738 | ||
|
|
ed85394845 | ||
|
|
a9a684a456 | ||
|
|
d7d7792a4a | ||
|
|
c09587f5d3 | ||
|
|
23f719381c | ||
|
|
d74d39d6e9 | ||
|
|
5f2cf8d3ef | ||
|
|
1843799345 | ||
|
|
bd838b3b7c | ||
|
|
c2d03f5e6b | ||
|
|
74e8e73877 | ||
|
|
8830dc8f78 | ||
|
|
ac877a7c0d | ||
|
|
0a442e712b | ||
|
|
4477f8001e | ||
|
|
152b94428f | ||
|
|
5390b0b191 | ||
|
|
97de8cea08 | ||
|
|
cd465c1aad | ||
|
|
449dea41a8 | ||
|
|
0b1a6e4745 | ||
|
|
e49061e28c | ||
|
|
18cb29b425 | ||
|
|
994ff23719 | ||
|
|
15d077df6e | ||
|
|
b490aa7f5d | ||
|
|
ca6b3badde | ||
|
|
1f200271af | ||
|
|
894a60d016 | ||
|
|
4a2219134b | ||
|
|
7d38fc5c03 | ||
|
|
ef5de187b9 | ||
|
|
a1c424266b | ||
|
|
557b4b7b6f | ||
|
|
98be21253d | ||
|
|
e5a04ada94 | ||
|
|
9b8b3090e6 | ||
|
|
e622c3948d | ||
|
|
94be46ffdb | ||
|
|
7039374588 | ||
|
|
0a5347c08b | ||
|
|
87f3318431 | ||
|
|
2557a8e4ec | ||
|
|
aff7094cb0 | ||
|
|
5a29b4bf70 | ||
|
|
e618183b49 | ||
|
|
a18236b12d | ||
|
|
b5da4e89a6 | ||
|
|
1da2737427 | ||
|
|
032fdadc3c | ||
|
|
8ae3ff3fe6 | ||
|
|
b8669503fa | ||
|
|
863165caaa | ||
|
|
b885f30789 | ||
|
|
461b62bd51 | ||
|
|
23776db3b6 | ||
|
|
19e91a6c7c | ||
|
|
6f40325d3f | ||
|
|
1987bff4b1 | ||
|
|
5aa0d55d47 | ||
|
|
a28196e930 | ||
|
|
c55387819d | ||
|
|
c8cc527aee | ||
|
|
a39b207ad5 | ||
|
|
ea63b50f2e | ||
|
|
b101251aa4 | ||
|
|
c9ba72ebc5 | ||
|
|
4a1c3088a9 | ||
|
|
a480ca1142 | ||
|
|
a928fbfafe | ||
|
|
3bf3ff1ee2 | ||
|
|
9647cc6cf2 | ||
|
|
df2d8925ed | ||
|
|
7a945daefc | ||
|
|
409e77cf2f | ||
|
|
552f99a63b | ||
|
|
0842311451 | ||
|
|
4d4b498636 | ||
|
|
d08cc12240 | ||
|
|
237442872e | ||
|
|
16983826fb | ||
|
|
e60ff6b777 | ||
|
|
3a0ef3760c | ||
|
|
bc0bc78219 | ||
|
|
d3137505a1 | ||
|
|
a2acd336eb | ||
|
|
6e4750336b | ||
|
|
ddefeeaf02 | ||
|
|
250e0a930d | ||
|
|
51c6d60760 | ||
|
|
db513b21f8 | ||
|
|
ab336678ce | ||
|
|
3eea4d6945 | ||
|
|
d091d3fd17 | ||
|
|
fc71f484ad | ||
|
|
bd772bf900 | ||
|
|
14db654681 | ||
|
|
a85b96ea89 | ||
|
|
c2b5e876bc | ||
|
|
91c02dc0b3 | ||
|
|
f78ec830b5 | ||
|
|
9f0e508ab3 | ||
|
|
4ca50d750b | ||
|
|
07c1b1b7f3 | ||
|
|
3e95dd52cf | ||
|
|
80ef2f6b0e | ||
|
|
53a8cda310 | ||
|
|
63de49104c | ||
|
|
8aa80bcb84 | ||
|
|
95115a7c5e | ||
|
|
ce2967fd02 | ||
|
|
399fb87d20 | ||
|
|
c4bd5ac5df | ||
|
|
123c2d6c02 | ||
|
|
6954e9c984 | ||
|
|
fc573e4e48 | ||
|
|
0dbcfdc5ac | ||
|
|
4b8d4b4792 | ||
|
|
d798da33ef | ||
|
|
d99517c8d1 | ||
|
|
0787adcb8e | ||
|
|
f848561d25 | ||
|
|
efbddc2486 | ||
|
|
e6a138d8f2 | ||
|
|
5b7a578307 | ||
|
|
737738de93 | ||
|
|
eb3951ce13 | ||
|
|
c2b7d9a257 | ||
|
|
4738aa2771 | ||
|
|
29ac0af55e | ||
|
|
96bc64c456 | ||
|
|
0369deb72d | ||
|
|
6e53990845 | ||
|
|
feb262644e | ||
|
|
abd679820f | ||
|
|
cd3ce848d1 | ||
|
|
63ba393c12 | ||
|
|
23fdf8c457 | ||
|
|
304ad4e3db | ||
|
|
ec58ab07b6 | ||
|
|
1ba4047b1b | ||
|
|
0bab8adc41 | ||
|
|
17e09c601e | ||
|
|
1aca5fb6ff | ||
|
|
7860d690fa | ||
|
|
6d01c99d38 | ||
|
|
ddb645aeea | ||
|
|
f08e4b41c4 | ||
|
|
1e23624955 | ||
|
|
ee951a7448 | ||
|
|
9935ba370d | ||
|
|
e815cce143 | ||
|
|
cea1032180 | ||
|
|
5695e1d9c8 | ||
|
|
fd317afd01 | ||
|
|
ccddd2a96f | ||
|
|
513d3034d8 | ||
|
|
51495187fa | ||
|
|
2bd53f7b9f | ||
|
|
06d9c48ed4 | ||
|
|
1155d18b7f | ||
|
|
6e14592c78 | ||
|
|
55feaf2d2c | ||
|
|
c487036c8b | ||
|
|
853ebf8c70 | ||
|
|
1c695c1cf9 | ||
|
|
bd5687d169 | ||
|
|
b384f71b64 | ||
|
|
10dd5278e7 | ||
|
|
befa6527e4 | ||
|
|
00497630cb | ||
|
|
95cd457de1 | ||
|
|
7518c9e3e0 | ||
|
|
6a999835e2 | ||
|
|
41d099c1be | ||
|
|
ff306ce2c5 | ||
|
|
c7abc82055 | ||
|
|
041d91dd3c | ||
|
|
387f56ed9b | ||
|
|
3181323c1f | ||
|
|
ecf84150c1 | ||
|
|
5b5025c776 | ||
|
|
e47dd3058b | ||
|
|
71f1dcd475 | ||
|
|
941856932c | ||
|
|
c51fde52e7 | ||
|
|
c5362e3bde | ||
|
|
a113703451 | ||
|
|
55ecb918e9 | ||
|
|
3a870e2f8b | ||
|
|
734231a4f1 | ||
|
|
223d6b29f4 | ||
|
|
4f41ec0a97 | ||
|
|
347a53297d | ||
|
|
820766abcb | ||
|
|
4974fa1fed | ||
|
|
7e829fa204 | ||
|
|
f6c7caa48d | ||
|
|
0dd9d252fd | ||
|
|
39f67a241c | ||
|
|
5706b08366 | ||
|
|
81de9695e2 | ||
|
|
589fb25fe3 | ||
|
|
61e5c6b468 | ||
|
|
087ceb3687 | ||
|
|
0a2cd208b2 | ||
|
|
678a936897 | ||
|
|
7c72ca089b | ||
|
|
21530f315f | ||
|
|
7274905a92 | ||
|
|
6c5cff6162 | ||
|
|
cf6b6c129a | ||
|
|
74491d16ae | ||
|
|
c1ab6e4eb4 | ||
|
|
18c9ae235a | ||
|
|
5c69d5fb88 | ||
|
|
90f0bda879 | ||
|
|
1b5c4a21bb | ||
|
|
08ee37112f | ||
|
|
cfbc88d3d6 | ||
|
|
79f5529a5a | ||
|
|
11ed0abd18 | ||
|
|
01830d9910 | ||
|
|
0f573805f2 | ||
|
|
93b1d81a48 | ||
|
|
e28d13b910 | ||
|
|
8731e343c4 | ||
|
|
605eca8cd7 | ||
|
|
5a8ddf5e4a | ||
|
|
f6d5d575fc | ||
|
|
d5c344e3ac | ||
|
|
18ba326cea | ||
|
|
1a1473d3ba | ||
|
|
72804a09ec | ||
|
|
c1ce0a514c | ||
|
|
bd479312b5 | ||
|
|
469da540d2 | ||
|
|
69edaa974f | ||
|
|
ff56963040 | ||
|
|
266aeaef50 | ||
|
|
fc660cfb1f | ||
|
|
27d343bdea | ||
|
|
a04b0da54a | ||
|
|
b15a6bfa98 | ||
|
|
dcc638c12f | ||
|
|
84ea96a5ad | ||
|
|
ae1bf85740 | ||
|
|
1612d713c9 | ||
|
|
6a4a8af731 | ||
|
|
e18375ca6d | ||
|
|
e537e4538a | ||
|
|
1ae97f5477 | ||
|
|
cc0083c6e5 | ||
|
|
43e6ed2da9 | ||
|
|
27bb3a948b | ||
|
|
7c155d307b | ||
|
|
d789beddd0 | ||
|
|
f790148ad3 | ||
|
|
a643abe293 | ||
|
|
099b08f009 | ||
|
|
35ddf6790e | ||
|
|
6502fdb1f5 | ||
|
|
b5cd3bf0af | ||
|
|
8183648902 | ||
|
|
0e1159b01e | ||
|
|
625ef3da8a | ||
|
|
10c7d9a6e1 | ||
|
|
85952ce6b7 | ||
|
|
bf9ce68d8b | ||
|
|
08c5992447 | ||
|
|
dfc7f7c827 | ||
|
|
efdbbc6098 | ||
|
|
185cf90d4c | ||
|
|
4db4790270 | ||
|
|
be3b890e2f | ||
|
|
4536f96493 | ||
|
|
a598c3e7a8 | ||
|
|
d9f5ee9d76 | ||
|
|
a4ced609cd | ||
|
|
673a4e6805 | ||
|
|
d017ccfbd4 | ||
|
|
1f52ed2e83 | ||
|
|
08e83f616c | ||
|
|
51edc4652e | ||
|
|
a3c6f38642 | ||
|
|
a1db53f50b | ||
|
|
9e1046fde3 | ||
|
|
17173f72e0 | ||
|
|
f60a99c357 | ||
|
|
1d763f1bc9 | ||
|
|
248b94c296 | ||
|
|
f52447ff58 | ||
|
|
0cbacbb959 | ||
|
|
a01edecaef | ||
|
|
779756f1ab | ||
|
|
723fedc066 | ||
|
|
a83bb23540 | ||
|
|
5d68a5133e | ||
|
|
8ca629151d | ||
|
|
693965af28 | ||
|
|
e645a350f2 | ||
|
|
85e9808550 | ||
|
|
0ce1c4565e | ||
|
|
478964ad30 | ||
|
|
74a04e3b35 | ||
|
|
a48992ed9d | ||
|
|
9a6ea8c9bb | ||
|
|
51b05cb128 | ||
|
|
de33d6d44c | ||
|
|
3d5cc98df5 | ||
|
|
13f3b54393 | ||
|
|
f17f7b2272 | ||
|
|
f61dc7197a | ||
|
|
0534508bc3 | ||
|
|
446c7ffd6a | ||
|
|
79e6216669 | ||
|
|
5047e48de5 | ||
|
|
bd48112bf9 | ||
|
|
5dc100d900 | ||
|
|
9f2ecb67d4 | ||
|
|
5e4f45826e | ||
|
|
be6ff21184 | ||
|
|
5c660fbe7f | ||
|
|
108718f275 | ||
|
|
ab53a0b403 | ||
|
|
49b815bc98 | ||
|
|
c702814203 | ||
|
|
0c0172a0b6 | ||
|
|
a8266c22f6 | ||
|
|
532c7fbc8f | ||
|
|
23ed381859 | ||
|
|
1ad11b0c58 | ||
|
|
18cca916a0 | ||
|
|
97012082de | ||
|
|
423810cf61 | ||
|
|
a5159ce8e1 | ||
|
|
4dd3952c19 | ||
|
|
1e26b5c5f1 | ||
|
|
67897dfcc0 | ||
|
|
0100604798 | ||
|
|
47afe01721 | ||
|
|
a2e12b795f | ||
|
|
806ab3438e | ||
|
|
f4be90fdd0 | ||
|
|
dd46767ee3 | ||
|
|
a2c712e5b3 | ||
|
|
35f3a0077a | ||
|
|
bc4195942a | ||
|
|
03baca2ed7 | ||
|
|
54a9c31a1a | ||
|
|
db5073223d | ||
|
|
afd766999c | ||
|
|
0637490216 | ||
|
|
6a3ba87b22 | ||
|
|
20b287da52 | ||
|
|
18a378976b | ||
|
|
8e7af49206 | ||
|
|
edeab082d4 | ||
|
|
7b76baaacf | ||
|
|
053365cb67 | ||
|
|
8301120a95 | ||
|
|
f15f0a6226 | ||
|
|
0cfcadf5fa | ||
|
|
435c4acba6 | ||
|
|
edb913855d | ||
|
|
24739e1638 | ||
|
|
54b906addb | ||
|
|
4a7a8df8a4 | ||
|
|
f1dd62c936 | ||
|
|
80cc7b0d64 | ||
|
|
eb4fbf3c0b | ||
|
|
c1cf1206fc | ||
|
|
efebc02d24 | ||
|
|
21dca8c17f | ||
|
|
4eb9839f77 | ||
|
|
3b7906ea04 | ||
|
|
9d17858500 | ||
|
|
d5ceb5f465 | ||
|
|
7dd2a0bbb4 | ||
|
|
13284fb3b9 | ||
|
|
f42c5ec0ce | ||
|
|
6b269839cb | ||
|
|
2eb3e0a278 | ||
|
|
183a437678 | ||
|
|
116b8171f8 | ||
|
|
c8c723bf4a | ||
|
|
d01cf018ce | ||
|
|
c701ab0776 | ||
|
|
180269d6b0 | ||
|
|
645c604fd4 | ||
|
|
de210db90d | ||
|
|
beddf1c772 | ||
|
|
75e618ee4a | ||
|
|
d2a3ba182b | ||
|
|
427f78b14d | ||
|
|
febcf237ca | ||
|
|
5e158c3bd7 | ||
|
|
b4c9c86ba6 | ||
|
|
7c00853f5d | ||
|
|
a0fcb116f5 | ||
|
|
e46b33544d | ||
|
|
6b9c3ad4e7 | ||
|
|
dc12b9a197 | ||
|
|
d473f56c3a | ||
|
|
4138ab3d7d | ||
|
|
e18d1a451d | ||
|
|
a3048cd393 | ||
|
|
dd8fdc6c0a | ||
|
|
9099e4b709 | ||
|
|
52b176b9eb | ||
|
|
69fd70787c | ||
|
|
ff37aea9c8 | ||
|
|
85f73977bf | ||
|
|
2c04ed48c2 | ||
|
|
1228754280 | ||
|
|
a43ee054ad | ||
|
|
83bc714739 | ||
|
|
a08390c84a | ||
|
|
8b6eacecfe | ||
|
|
fb96787697 | ||
|
|
9cff77be62 | ||
|
|
0d1643da66 | ||
|
|
5e7027647a | ||
|
|
28f6f09e8f | ||
|
|
332af5d21b | ||
|
|
e187005130 | ||
|
|
0357386f7c | ||
|
|
47f8e5b8c6 | ||
|
|
e95c9d73a1 | ||
|
|
b7174070fe | ||
|
|
dd06a7b62c | ||
|
|
ff9d480b6e | ||
|
|
229ad9108b | ||
|
|
0e332d291a | ||
|
|
180904cdc2 | ||
|
|
0e83f7d807 | ||
|
|
5d7931fcaf | ||
|
|
2e906b0bf5 | ||
|
|
33ae6f12de | ||
|
|
f302c2e154 | ||
|
|
3ee2492382 | ||
|
|
4caed50018 | ||
|
|
aadb19a792 | ||
|
|
9f8211a873 | ||
|
|
d45fc05e5d | ||
|
|
955a3a054e | ||
|
|
60f265a5fa | ||
|
|
a2d82a1a7b | ||
|
|
0875d728e8 | ||
|
|
f3cf6b8b38 | ||
|
|
e4465cffb0 | ||
|
|
ca35d714dc | ||
|
|
c06e7348c4 | ||
|
|
60ac8a6ebd | ||
|
|
e3450baeb3 | ||
|
|
72661623f3 | ||
|
|
b4d97d9432 | ||
|
|
b40100f78b | ||
|
|
a343d2b42c | ||
|
|
d3d7e54cff | ||
|
|
6535bc3d5e | ||
|
|
f966fc8d84 | ||
|
|
8a20bbd943 | ||
|
|
cd0f6d85ba | ||
|
|
d51edbb3bb | ||
|
|
553e475cfb | ||
|
|
b9367446d9 | ||
|
|
82d9fccec8 | ||
|
|
cbbcfb7a3a | ||
|
|
1f862b27c1 | ||
|
|
883b03349e | ||
|
|
f740a6ba61 | ||
|
|
fb3e761a37 | ||
|
|
3c7411328d | ||
|
|
9c2bfdfead | ||
|
|
4f3bd1ff4a | ||
|
|
69d10489b8 | ||
|
|
df031b2222 | ||
|
|
850b9e5e3d | ||
|
|
a95a208e1b | ||
|
|
50ff3628f7 | ||
|
|
14d203055b | ||
|
|
4628e28592 | ||
|
|
7fb3d13733 | ||
|
|
11ff81f852 | ||
|
|
0f5af4b990 | ||
|
|
85420602e8 | ||
|
|
6ccf55b601 | ||
|
|
42c9e21d04 | ||
|
|
3030c300f2 | ||
|
|
48b969f3c3 | ||
|
|
bbb78aa5e6 | ||
|
|
31380bbef2 | ||
|
|
479a7d9162 | ||
|
|
6fe02f156a | ||
|
|
c4ed210fed | ||
|
|
ae686fab38 | ||
|
|
8edca9ed5d | ||
|
|
05bafd0db5 | ||
|
|
341d699240 | ||
|
|
552093d962 | ||
|
|
eb6063cc2d | ||
|
|
550ff4ff18 | ||
|
|
5383a8b77c | ||
|
|
86117091fe | ||
|
|
b113028a5f | ||
|
|
60a3f21857 | ||
|
|
65a2ea3935 | ||
|
|
6ecddfc6c0 | ||
|
|
d65d48db48 | ||
|
|
f509b26800 | ||
|
|
43fb6fe6e5 | ||
|
|
9d2d8684b6 | ||
|
|
1689925508 | ||
|
|
4d249553bf | ||
|
|
43ea1044cd | ||
|
|
cc4a301dc1 | ||
|
|
ab67eea36e | ||
|
|
fa326eba6f | ||
|
|
c30ebdf287 | ||
|
|
835bcb7207 | ||
|
|
777424ad18 | ||
|
|
4985e7e96d | ||
|
|
ca1e64ec10 | ||
|
|
26029508c6 | ||
|
|
118259a96b | ||
|
|
35e8dcf2bc | ||
|
|
359a5d01e6 | ||
|
|
1c2acbb57f | ||
|
|
01a702c529 | ||
|
|
1ee584c5a1 | ||
|
|
fc10bd7749 | ||
|
|
f2568092a7 | ||
|
|
6b5d5a6334 | ||
|
|
195ed57025 | ||
|
|
008b4a134b | ||
|
|
1b9bfb5b62 | ||
|
|
edeaa1333b | ||
|
|
e678b52a7e | ||
|
|
b549db58e4 | ||
|
|
c14059f66a | ||
|
|
11f69daaec | ||
|
|
c0120c0f17 | ||
|
|
c1a5f9adf1 | ||
|
|
5087f27546 | ||
|
|
efbff9e217 | ||
|
|
20ea83ae93 | ||
|
|
05daeb561c | ||
|
|
bfff001752 | ||
|
|
c3a45a1584 | ||
|
|
b09a92a264 | ||
|
|
44a792583c | ||
|
|
71c8267dea | ||
|
|
b6688f56b5 | ||
|
|
f703164098 | ||
|
|
6a6b27e905 | ||
|
|
731a46c612 | ||
|
|
92a8078322 | ||
|
|
ba2d77f0bb | ||
|
|
3d21c15281 | ||
|
|
cb4b20c057 | ||
|
|
2af2767699 | ||
|
|
e4bb19b98a | ||
|
|
7e784c9509 | ||
|
|
3dd27797dc | ||
|
|
5e059272dc | ||
|
|
0a9aeca3bc | ||
|
|
11d42e0f93 | ||
|
|
85d8658037 | ||
|
|
dfa29950ef | ||
|
|
b7366a8704 | ||
|
|
57416103c3 | ||
|
|
72bd3731de | ||
|
|
9fab20ca6c | ||
|
|
8b4453f32d | ||
|
|
f4b77e6b03 | ||
|
|
c3da2fca9b | ||
|
|
c0d68c5740 | ||
|
|
5398564aec | ||
|
|
904dc80aab | ||
|
|
516de20148 | ||
|
|
be088709af | ||
|
|
fd4f5057b3 | ||
|
|
686d5e8b03 | ||
|
|
c371ff5504 | ||
|
|
9862dca4aa | ||
|
|
716321b37b | ||
|
|
b3ed8bad9c | ||
|
|
0a170f5c29 | ||
|
|
ec0fba7913 | ||
|
|
2630c2baf1 | ||
|
|
a01865b19b | ||
|
|
00f6115a93 | ||
|
|
a466202bac | ||
|
|
bb12ef24f8 | ||
|
|
c862c6de0f | ||
|
|
a1cb3ec8d5 | ||
|
|
187d4cd02d | ||
|
|
20a0f9b026 | ||
|
|
f9c0ed6ad4 | ||
|
|
27652e7191 | ||
|
|
828665cb29 | ||
|
|
fa784c83bf | ||
|
|
2d83176892 | ||
|
|
776758c7e8 | ||
|
|
d72afe9b92 | ||
|
|
dee61b5499 | ||
|
|
9f73d0a7fb | ||
|
|
bc804c9e56 | ||
|
|
35f450aee7 | ||
|
|
5803b4ca27 | ||
|
|
7bccd62a4f | ||
|
|
335838f2b2 | ||
|
|
204d8cc7eb | ||
|
|
61f5d4b172 | ||
|
|
3d829c6ce8 | ||
|
|
5d9852b72c | ||
|
|
f561ece9d1 | ||
|
|
66eabd3bd6 | ||
|
|
b2f92acbf6 | ||
|
|
6f30ecb365 | ||
|
|
32a89d3895 | ||
|
|
97bf958b74 | ||
|
|
30f8afca85 | ||
|
|
ed88a8e3e3 | ||
|
|
421f690f42 | ||
|
|
a330e8afb2 | ||
|
|
d8e5c9f033 | ||
|
|
209646e012 | ||
|
|
7d518df13c | ||
|
|
ca603f41db | ||
|
|
7bb18f6fad | ||
|
|
1a0e2031d2 | ||
|
|
4f83d69205 | ||
|
|
cfafd90f15 | ||
|
|
a94f416b3c | ||
|
|
fd47e2de29 | ||
|
|
abbc403f73 | ||
|
|
b41c536865 | ||
|
|
bee7314dd7 | ||
|
|
d25407e3b4 | ||
|
|
ad697369ef | ||
|
|
edbdb17a2f | ||
|
|
9d2e2a1ea2 | ||
|
|
6df0597c5e | ||
|
|
093eb28463 | ||
|
|
7d0c279f5b | ||
|
|
d98a6a09bb | ||
|
|
02cf7b9d66 | ||
|
|
9253b783dd | ||
|
|
2ed82be809 | ||
|
|
1c1499dec8 | ||
|
|
f7f151d2a9 | ||
|
|
13f29ee3ce | ||
|
|
ce68f52ca0 | ||
|
|
33172767a6 | ||
|
|
649b3839d2 | ||
|
|
666fb4c194 | ||
|
|
9301497a4a | ||
|
|
6956e21caf | ||
|
|
71dec5746e | ||
|
|
0ea8f4c259 | ||
|
|
8602814dc3 | ||
|
|
20e60edbc6 | ||
|
|
88f59ad1eb | ||
|
|
668a899260 | ||
|
|
75ae85a5d4 | ||
|
|
abc1b4e1b2 | ||
|
|
fa194f0cef | ||
|
|
b3fbd89456 | ||
|
|
5334a4cbe0 | ||
|
|
8f2adf0a50 | ||
|
|
62dfd7cef0 | ||
|
|
a8321e8cd3 | ||
|
|
0119552336 | ||
|
|
033abc64c8 | ||
|
|
ef8014bc6d | ||
|
|
96a880b5ae | ||
|
|
bfedcde978 | ||
|
|
badad70984 | ||
|
|
7611188535 | ||
|
|
31f2cc1fdc | ||
|
|
187e646fa0 | ||
|
|
b2721db8e0 | ||
|
|
fd9f521c60 | ||
|
|
edd6fbe35f | ||
|
|
839c9c9884 | ||
|
|
3a1fe992d6 | ||
|
|
9899f6d1f8 | ||
|
|
446a464b3d | ||
|
|
6a347799c7 | ||
|
|
2dae89e41c | ||
|
|
e8119ba80d | ||
|
|
a5ecad8fae | ||
|
|
1708a4c831 | ||
|
|
a237078b68 | ||
|
|
4ef63d026e | ||
|
|
b8ae3cdd3f | ||
|
|
46d855ce0f | ||
|
|
a3306bbb5a | ||
|
|
1428a5e7e2 | ||
|
|
427940b3be | ||
|
|
7a3e7dc631 | ||
|
|
b38bb40a5d | ||
|
|
b2e1e2e89a | ||
|
|
02fcc42395 | ||
|
|
3accc406a7 | ||
|
|
1c238b7ce4 | ||
|
|
45770173c4 | ||
|
|
3e5f6abdad | ||
|
|
4117c6127e | ||
|
|
aae1fad7ab | ||
|
|
ada65b5ce2 | ||
|
|
14c0c65e17 | ||
|
|
0201aa9bd1 | ||
|
|
dca530f2f2 | ||
|
|
c9f9668e52 | ||
|
|
4f636b7cfb | ||
|
|
34a04c0059 | ||
|
|
00ee58d3fd | ||
|
|
ecb3c4f4f3 | ||
|
|
9dace592c0 | ||
|
|
5d73221b06 | ||
|
|
d50958c9ee | ||
|
|
52bb005792 | ||
|
|
3121aa7164 | ||
|
|
87c54f07c6 | ||
|
|
f1d4a686b1 | ||
|
|
56ac037128 | ||
|
|
e977045d5f | ||
|
|
3301b106ab | ||
|
|
e645f55191 | ||
|
|
278d25c803 | ||
|
|
ac14154b5d | ||
|
|
3352e743f7 | ||
|
|
79d8c17aaa | ||
|
|
ef7ce21ff3 | ||
|
|
a8bcc6206f | ||
|
|
ddfeafc5e2 | ||
|
|
85cdd40102 | ||
|
|
d412d8536c | ||
|
|
bf1a314076 | ||
|
|
e2b2ff7f6f | ||
|
|
a589c9aa69 | ||
|
|
d5b05e391a | ||
|
|
dc7a20280f | ||
|
|
d36fc45c99 | ||
|
|
990d92e569 | ||
|
|
7d4ef4f9a1 | ||
|
|
7baabcef96 | ||
|
|
ded15ecc3f | ||
|
|
0ad3ec444c | ||
|
|
7939503a11 | ||
|
|
8564f93706 | ||
|
|
f3e550d003 | ||
|
|
6c525b5dcd | ||
|
|
bb10d25561 | ||
|
|
7ec5adb6b4 | ||
|
|
ffb73d61fc | ||
|
|
3ee6c34d08 | ||
|
|
2c26ccbc72 | ||
|
|
cfbde151fa | ||
|
|
e278978ad9 | ||
|
|
a284e0c2f7 | ||
|
|
558c920181 | ||
|
|
7622fe9fc5 | ||
|
|
b32aec682c | ||
|
|
d54d25a432 | ||
|
|
ba19bdb90a | ||
|
|
58d10fac84 | ||
|
|
eecc1def2a | ||
|
|
f75fbc3744 | ||
|
|
07750c1f8c | ||
|
|
9ae0d9b0a1 | ||
|
|
c9f5828eb9 | ||
|
|
35a6a1883c | ||
|
|
080c48327e | ||
|
|
28506538a3 | ||
|
|
d578dedd0c | ||
|
|
c7aa105517 | ||
|
|
3c140c3e4d | ||
|
|
8d94b67aaa | ||
|
|
887cca109f | ||
|
|
239061b688 | ||
|
|
6d067428e0 | ||
|
|
b11f13181e | ||
|
|
e52b8e9a13 | ||
|
|
aa567ab078 | ||
|
|
50daf49cf0 | ||
|
|
a49fb65fac | ||
|
|
adfd1e614d | ||
|
|
a3eef04342 | ||
|
|
cf5e660951 | ||
|
|
671d6acfff | ||
|
|
125e759120 | ||
|
|
e81832e48f | ||
|
|
0b17da3b87 | ||
|
|
8379902adb | ||
|
|
00c4ffc154 | ||
|
|
3473337e7d | ||
|
|
4493178693 | ||
|
|
40452dcefe | ||
|
|
082afadb5b | ||
|
|
9ca61f9ef5 | ||
|
|
28a628ec93 | ||
|
|
12b5e21314 | ||
|
|
95aaccb35e | ||
|
|
ac053b00e8 | ||
|
|
938c7df28a | ||
|
|
6e22ea178b | ||
|
|
253f336509 | ||
|
|
3a7e0da80b | ||
|
|
073860cd5b | ||
|
|
18be4db320 | ||
|
|
5cbcbe6d7e | ||
|
|
1ef3f83e46 | ||
|
|
6ab0a839b1 | ||
|
|
e329753939 | ||
|
|
843751b53f | ||
|
|
1f083a52eb | ||
|
|
879eb6ee9f | ||
|
|
2db1e6b596 | ||
|
|
94f5ba7d1a | ||
|
|
e7458f3032 | ||
|
|
840cee206a | ||
|
|
511cdbbfe2 | ||
|
|
35f1999b3a | ||
|
|
3f55c694b8 | ||
|
|
6df0147fe9 | ||
|
|
5e3b4b126e | ||
|
|
b564fe8a0d | ||
|
|
1dc3a7202a | ||
|
|
cfa01d3c15 | ||
|
|
c4cac468ff | ||
|
|
a034bf9710 | ||
|
|
0336b0a15c | ||
|
|
94a2cfe7fc | ||
|
|
d22d0fdab5 | ||
|
|
6f9f47bfe3 | ||
|
|
b2402cdd39 | ||
|
|
289e1ee315 | ||
|
|
c1d1cbda70 | ||
|
|
17bac3714d | ||
|
|
9699fb8894 | ||
|
|
16809c0136 | ||
|
|
e0408510b8 | ||
|
|
14782c184f | ||
|
|
527f5c5ae6 | ||
|
|
b9aa4f1482 | ||
|
|
5ea6234cfb | ||
|
|
d056e1de1e | ||
|
|
2feb0246ff | ||
|
|
f4be045f08 | ||
|
|
a42ca225e3 | ||
|
|
5fb1bed9d2 | ||
|
|
c0c903d81a | ||
|
|
60b56d61ed | ||
|
|
fe1f334e2e | ||
|
|
fe4d70a9f9 | ||
|
|
93fded58f0 | ||
|
|
9a7b8ca27d | ||
|
|
aa7d75fae4 | ||
|
|
be345ffcc1 | ||
|
|
b3589312f8 | ||
|
|
08bd45d07e | ||
|
|
9af60c4192 | ||
|
|
6c48a94ab3 | ||
|
|
2897be7a10 | ||
|
|
69c0b04be6 | ||
|
|
837e309781 | ||
|
|
dca369ba30 | ||
|
|
b49d66aa68 | ||
|
|
4ce8c82244 | ||
|
|
ab9a530403 | ||
|
|
f9d91178b7 | ||
|
|
d0b8232a36 | ||
|
|
8f21e2368f | ||
|
|
87ec1a9fc5 | ||
|
|
cb1dcab37d | ||
|
|
91980277e1 | ||
|
|
c18b259b27 | ||
|
|
dffc82781b | ||
|
|
aef77965e7 | ||
|
|
f21da0cc2b | ||
|
|
234e0ee764 | ||
|
|
4262bb801e | ||
|
|
093941f8ba | ||
|
|
1cb1c35e2a | ||
|
|
432535e238 | ||
|
|
b40dc9d96d | ||
|
|
cb12e1208b | ||
|
|
b8225bd206 | ||
|
|
880c22eef9 | ||
|
|
b379c8380d | ||
|
|
6a61a113b0 | ||
|
|
4373eae1fe | ||
|
|
d12e4305bd | ||
|
|
7c8a45fd4c | ||
|
|
c28f8f763a | ||
|
|
096de6cddf | ||
|
|
ad52476159 | ||
|
|
e3d11ab681 | ||
|
|
162f37e00f | ||
|
|
d879634810 | ||
|
|
4634f853f1 | ||
|
|
ae861f080b | ||
|
|
d058721243 | ||
|
|
9fdef5eb5d | ||
|
|
b4488bf1e7 | ||
|
|
fa6d6b5438 | ||
|
|
02f53a55cc | ||
|
|
fdf0b6263a | ||
|
|
59fa5112fc | ||
|
|
a8033248ae | ||
|
|
9a6f299b41 | ||
|
|
2f1ee93e86 | ||
|
|
34fa5d6bfc | ||
|
|
357f728043 | ||
|
|
9522ee93dc | ||
|
|
c68b6116a2 | ||
|
|
f0db879c9c | ||
|
|
07d8a3d765 | ||
|
|
e35e264d81 | ||
|
|
d537e6a869 | ||
|
|
d4dd1861a9 | ||
|
|
3019a31fbb | ||
|
|
303b9912ff | ||
|
|
0259b2e5b9 | ||
|
|
5c7e8029f4 | ||
|
|
08e3fd3141 | ||
|
|
30123fd6ff | ||
|
|
3955299983 | ||
|
|
b5d0df3ca7 | ||
|
|
22c65da9d1 | ||
|
|
578c1ecfaf | ||
|
|
d8d00a7e26 | ||
|
|
37f0f7a138 | ||
|
|
f61e9367ec | ||
|
|
3c3e59e932 | ||
|
|
29e22a0c6c | ||
|
|
0d1f424425 | ||
|
|
1c01e23867 | ||
|
|
f763a8694b | ||
|
|
675b853b29 | ||
|
|
2434bf14d5 | ||
|
|
70fbbfe2a0 | ||
|
|
e096898a05 | ||
|
|
3fbccf3f64 | ||
|
|
36585395f1 | ||
|
|
e4b0a1613f | ||
|
|
1192e474c5 | ||
|
|
e48ea99e48 | ||
|
|
072f2a0ee9 | ||
|
|
aecb536a34 | ||
|
|
a68686cb06 | ||
|
|
ba8cf3e01e | ||
|
|
b0c5189c4b | ||
|
|
d44eb67dec | ||
|
|
58d36b08e2 | ||
|
|
98906731e3 | ||
|
|
035a4b0928 | ||
|
|
85fbe666ea | ||
|
|
741d0bc686 | ||
|
|
ded539ce7a | ||
|
|
c53fd25d1c | ||
|
|
da32621c55 | ||
|
|
4ccf33af03 | ||
|
|
a5af7a70f3 | ||
|
|
16ab0d29d6 | ||
|
|
05ad9022c0 | ||
|
|
fef211b220 | ||
|
|
6aee1ee41f | ||
|
|
bab7f9b1f3 | ||
|
|
340e7afd06 | ||
|
|
cb83c9cff2 | ||
|
|
911a8fed06 | ||
|
|
eb8b43fe36 | ||
|
|
2a15dc57d8 | ||
|
|
67678e35bb | ||
|
|
2f00db8081 |
26
.gitattributes
vendored
@@ -1,15 +1,17 @@
|
|||||||
src/static/fontawesome/* linguist-vendored
|
src/pretix/static/fontawesome/* linguist-vendored
|
||||||
src/static/lightbox/* linguist-vendored
|
src/pretix/static/lightbox/* linguist-vendored
|
||||||
src/static/typeahead/* linguist-vendored
|
src/pretix/static/typeahead/* linguist-vendored
|
||||||
src/static/moment/* linguist-vendored
|
src/pretix/static/moment/* linguist-vendored
|
||||||
src/static/datetimepicker/* linguist-vendored
|
src/pretix/static/datetimepicker/* linguist-vendored
|
||||||
src/static/colorpicker/* linguist-vendored
|
src/pretix/static/colorpicker/* linguist-vendored
|
||||||
src/static/fileupload/* linguist-vendored
|
src/pretix/static/fileupload/* linguist-vendored
|
||||||
src/static/vuejs/* linguist-vendored
|
src/pretix/static/vuejs/* linguist-vendored
|
||||||
src/static/select2/* linguist-vendored
|
src/pretix/static/select2/* linguist-vendored
|
||||||
src/static/charts/* linguist-vendored
|
src/pretix/static/charts/* linguist-vendored
|
||||||
src/pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/fabric.* linguist-vendored
|
src/pretix/static/rrule/* linguist-vendored
|
||||||
src/pretix/plugins/ticketoutputpdf/static/pretixplugins/ticketoutputpdf/pdf.* linguist-vendored
|
src/pretix/static/iframeresizer/* linguist-vendored
|
||||||
|
src/pretix/static/pdfjs/* linguist-vendored
|
||||||
|
src/pretix/static/fabric/* linguist-vendored
|
||||||
|
|
||||||
# Denote all files that are truly binary and should not be modified.
|
# Denote all files that are truly binary and should not be modified.
|
||||||
*.eot binary
|
*.eot binary
|
||||||
|
|||||||
@@ -17,8 +17,12 @@ pypi:
|
|||||||
- virtualenv env
|
- virtualenv env
|
||||||
- source env/bin/activate
|
- source env/bin/activate
|
||||||
- pip install -U pip wheel setuptools
|
- pip install -U pip wheel setuptools
|
||||||
- XDG_CACHE_HOME=/cache pip3 install -Ur src/requirements.txt -r src/requirements/dev.txt -r src/requirements/py34.txt
|
- XDG_CACHE_HOME=/cache pip3 install -Ur src/requirements.txt -r src/requirements/dev.txt
|
||||||
- cd src
|
- cd src
|
||||||
|
- python setup.py sdist
|
||||||
|
- pip install dist/pretix-*.tar.gz
|
||||||
|
- python -m pretix migrate
|
||||||
|
- python -m pretix check
|
||||||
- python setup.py sdist upload
|
- python setup.py sdist upload
|
||||||
- python setup.py bdist_wheel upload
|
- python setup.py bdist_wheel upload
|
||||||
tags:
|
tags:
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
-r src/requirements/py34.txt
|
|
||||||
-r doc/requirements.txt
|
-r doc/requirements.txt
|
||||||
|
|||||||
20
.travis.sh
@@ -11,21 +11,20 @@ fi
|
|||||||
|
|
||||||
if [ "$PRETIX_CONFIG_FILE" == "tests/travis_postgres.cfg" ]; then
|
if [ "$PRETIX_CONFIG_FILE" == "tests/travis_postgres.cfg" ]; then
|
||||||
psql -c 'create database travis_ci_test;' -U postgres
|
psql -c 'create database travis_ci_test;' -U postgres
|
||||||
pip3 install -Ur src/requirements/postgres.txt
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$1" == "style" ]; then
|
if [ "$1" == "style" ]; then
|
||||||
XDG_CACHE_HOME=/cache pip3 install -Ur src/requirements.txt -r src/requirements/dev.txt -r src/requirements/py34.txt
|
XDG_CACHE_HOME=/cache pip3 install -Ur src/requirements.txt -r src/requirements/dev.txt
|
||||||
cd src
|
cd src
|
||||||
flake8 .
|
flake8 .
|
||||||
isort -c -rc -df .
|
isort -c -rc -df .
|
||||||
fi
|
fi
|
||||||
if [ "$1" == "doctests" ]; then
|
if [ "$1" == "doctests" ]; then
|
||||||
XDG_CACHE_HOME=/cache pip3 install -Ur doc/requirements.txt -r src/requirements/py34.txt
|
XDG_CACHE_HOME=/cache pip3 install -Ur doc/requirements.txt
|
||||||
cd doc
|
cd doc
|
||||||
make doctest
|
make doctest
|
||||||
fi
|
fi
|
||||||
if [ "$1" == "spelling" ]; then
|
if [ "$1" == "doc-spelling" ]; then
|
||||||
XDG_CACHE_HOME=/cache pip3 install -Ur doc/requirements.txt
|
XDG_CACHE_HOME=/cache pip3 install -Ur doc/requirements.txt
|
||||||
cd doc
|
cd doc
|
||||||
make spelling
|
make spelling
|
||||||
@@ -33,22 +32,27 @@ if [ "$1" == "spelling" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
if [ "$1" == "translation-spelling" ]; then
|
||||||
|
XDG_CACHE_HOME=/cache pip3 install -Ur src/requirements/dev.txt
|
||||||
|
cd src
|
||||||
|
potypo
|
||||||
|
fi
|
||||||
if [ "$1" == "tests" ]; then
|
if [ "$1" == "tests" ]; then
|
||||||
pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt -r src/requirements/py34.txt
|
pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt pytest-xdist
|
||||||
cd src
|
cd src
|
||||||
python manage.py check
|
python manage.py check
|
||||||
make all compress
|
make all compress
|
||||||
py.test --reruns 5 tests
|
py.test --reruns 5 -n 3 tests
|
||||||
fi
|
fi
|
||||||
if [ "$1" == "tests-cov" ]; then
|
if [ "$1" == "tests-cov" ]; then
|
||||||
pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt -r src/requirements/py34.txt
|
pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt
|
||||||
cd src
|
cd src
|
||||||
python manage.py check
|
python manage.py check
|
||||||
make all compress
|
make all compress
|
||||||
coverage run -m py.test --reruns 5 tests && codecov
|
coverage run -m py.test --reruns 5 tests && codecov
|
||||||
fi
|
fi
|
||||||
if [ "$1" == "plugins" ]; then
|
if [ "$1" == "plugins" ]; then
|
||||||
pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt -r src/requirements/py34.txt
|
pip3 install -r src/requirements.txt -Ur src/requirements/dev.txt
|
||||||
cd src
|
cd src
|
||||||
python setup.py develop
|
python setup.py develop
|
||||||
make all compress
|
make all compress
|
||||||
|
|||||||
29
.travis.yml
@@ -1,7 +1,7 @@
|
|||||||
language: python
|
language: python
|
||||||
sudo: false
|
sudo: false
|
||||||
install:
|
install:
|
||||||
- pip install -U pip wheel setuptools==28.6.1
|
- pip install -U pip wheel setuptools
|
||||||
script:
|
script:
|
||||||
- bash .travis.sh $JOB
|
- bash .travis.sh $JOB
|
||||||
cache:
|
cache:
|
||||||
@@ -15,31 +15,32 @@ matrix:
|
|||||||
- python: 3.6
|
- python: 3.6
|
||||||
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_sqlite.cfg
|
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_sqlite.cfg
|
||||||
- python: 3.6
|
- python: 3.6
|
||||||
env: JOB=tests-cov
|
env: JOB=tests-cov PRETIX_CONFIG_FILE=tests/travis_postgres.cfg
|
||||||
- python: 3.6
|
- python: 3.6
|
||||||
env: JOB=style
|
env: JOB=style
|
||||||
- python: 3.4
|
|
||||||
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_sqlite.cfg
|
|
||||||
- python: 3.5
|
|
||||||
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_sqlite.cfg
|
|
||||||
- python: 3.4
|
|
||||||
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_mysql.cfg
|
|
||||||
- python: 3.5
|
|
||||||
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_mysql.cfg
|
|
||||||
- python: 3.6
|
- python: 3.6
|
||||||
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_mysql.cfg
|
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_mysql.cfg
|
||||||
- python: 3.4
|
- python: 3.6
|
||||||
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_postgres.cfg
|
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_postgres.cfg
|
||||||
- python: 3.5
|
- python: 3.5
|
||||||
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_postgres.cfg
|
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_postgres.cfg
|
||||||
- python: 3.6
|
|
||||||
env: JOB=tests PRETIX_CONFIG_FILE=tests/travis_postgres.cfg
|
|
||||||
- python: 3.6
|
- python: 3.6
|
||||||
env: JOB=plugins
|
env: JOB=plugins
|
||||||
- python: 3.6
|
- python: 3.6
|
||||||
env: JOB=spelling
|
env: JOB=doc-spelling
|
||||||
|
- python: 3.6
|
||||||
|
env: JOB=translation-spelling
|
||||||
addons:
|
addons:
|
||||||
postgresql: "9.4"
|
postgresql: "9.4"
|
||||||
|
mariadb: '10.3'
|
||||||
apt:
|
apt:
|
||||||
packages:
|
packages:
|
||||||
- enchant
|
- enchant
|
||||||
|
- myspell-de-de
|
||||||
|
- aspell-en
|
||||||
|
- sqlite3
|
||||||
|
sources:
|
||||||
|
- travis-ci/sqlite3
|
||||||
|
branches:
|
||||||
|
except:
|
||||||
|
- /^weblate-.*/
|
||||||
|
|||||||
43
Dockerfile
@@ -1,10 +1,26 @@
|
|||||||
FROM python:3.6
|
FROM python:3.6
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y git libxml2-dev libxslt1-dev python-dev python-virtualenv locales \
|
apt-get install -y --no-install-recommends \
|
||||||
libffi-dev build-essential python3-dev zlib1g-dev libssl-dev gettext libpq-dev \
|
build-essential \
|
||||||
libmysqlclient-dev libmemcached-dev libjpeg-dev supervisor nginx sudo \
|
default-libmysqlclient-dev \
|
||||||
--no-install-recommends && \
|
gettext \
|
||||||
|
git \
|
||||||
|
libffi-dev \
|
||||||
|
libjpeg-dev \
|
||||||
|
libmemcached-dev \
|
||||||
|
libpq-dev \
|
||||||
|
libssl-dev \
|
||||||
|
libxml2-dev \
|
||||||
|
libxslt1-dev \
|
||||||
|
locales \
|
||||||
|
nginx \
|
||||||
|
python-dev \
|
||||||
|
python-virtualenv \
|
||||||
|
python3-dev \
|
||||||
|
sudo \
|
||||||
|
supervisor \
|
||||||
|
zlib1g-dev && \
|
||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
rm -rf /var/lib/apt/lists/* && \
|
rm -rf /var/lib/apt/lists/* && \
|
||||||
dpkg-reconfigure locales && \
|
dpkg-reconfigure locales && \
|
||||||
@@ -19,6 +35,22 @@ RUN apt-get update && \
|
|||||||
ENV LC_ALL=C.UTF-8 \
|
ENV LC_ALL=C.UTF-8 \
|
||||||
DJANGO_SETTINGS_MODULE=production_settings
|
DJANGO_SETTINGS_MODULE=production_settings
|
||||||
|
|
||||||
|
# To copy only the requirements files needed to install from PIP
|
||||||
|
COPY src/requirements /pretix/src/requirements
|
||||||
|
COPY src/requirements.txt /pretix/src
|
||||||
|
RUN pip3 install -U \
|
||||||
|
pip \
|
||||||
|
setuptools \
|
||||||
|
wheel && \
|
||||||
|
cd /pretix/src && \
|
||||||
|
pip3 install \
|
||||||
|
-r requirements.txt \
|
||||||
|
-r requirements/memcached.txt \
|
||||||
|
-r requirements/mysql.txt \
|
||||||
|
-r requirements/redis.txt \
|
||||||
|
gunicorn && \
|
||||||
|
rm -rf ~/.cache/pip
|
||||||
|
|
||||||
COPY deployment/docker/pretix.bash /usr/local/bin/pretix
|
COPY deployment/docker/pretix.bash /usr/local/bin/pretix
|
||||||
COPY deployment/docker/supervisord.conf /etc/supervisord.conf
|
COPY deployment/docker/supervisord.conf /etc/supervisord.conf
|
||||||
COPY deployment/docker/nginx.conf /etc/nginx/nginx.conf
|
COPY deployment/docker/nginx.conf /etc/nginx/nginx.conf
|
||||||
@@ -27,11 +59,8 @@ COPY src /pretix/src
|
|||||||
|
|
||||||
RUN chmod +x /usr/local/bin/pretix && \
|
RUN chmod +x /usr/local/bin/pretix && \
|
||||||
rm /etc/nginx/sites-enabled/default && \
|
rm /etc/nginx/sites-enabled/default && \
|
||||||
pip3 install -U pip wheel setuptools && \
|
|
||||||
cd /pretix/src && \
|
cd /pretix/src && \
|
||||||
rm -f pretix.cfg && \
|
rm -f pretix.cfg && \
|
||||||
pip3 install -r requirements.txt -r requirements/mysql.txt -r requirements/postgres.txt \
|
|
||||||
-r requirements/memcached.txt -r requirements/redis.txt gunicorn && \
|
|
||||||
mkdir -p data && \
|
mkdir -p data && \
|
||||||
chown -R pretixuser:pretixuser /pretix /data data && \
|
chown -R pretixuser:pretixuser /pretix /data data && \
|
||||||
sudo -u pretixuser make production
|
sudo -u pretixuser make production
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ Contributing
|
|||||||
If you want to contribute to pretix, please read the `developer documentation`_
|
If you want to contribute to pretix, please read the `developer documentation`_
|
||||||
in our documentation. If you have any further questions, please do not hesitate to ask!
|
in our documentation. If you have any further questions, please do not hesitate to ask!
|
||||||
|
|
||||||
|
.. image:: https://translate.pretix.eu/widgets/pretix/-/pretix/multi-blue.svg
|
||||||
|
:target: https://translate.pretix.eu/engage/pretix/
|
||||||
|
|
||||||
Code of Conduct
|
Code of Conduct
|
||||||
---------------
|
---------------
|
||||||
We have a `Code of Conduct`_ in place that applies to all project contributions,
|
We have a `Code of Conduct`_ in place that applies to all project contributions,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ cd /pretix/src
|
|||||||
export DJANGO_SETTINGS_MODULE=production_settings
|
export DJANGO_SETTINGS_MODULE=production_settings
|
||||||
export DATA_DIR=/data/
|
export DATA_DIR=/data/
|
||||||
export HOME=/pretix
|
export HOME=/pretix
|
||||||
NUM_WORKERS=10
|
export NUM_WORKERS=$((2 * $(nproc --all)))
|
||||||
|
|
||||||
if [ ! -d /data/logs ]; then
|
if [ ! -d /data/logs ]; then
|
||||||
mkdir /data/logs;
|
mkdir /data/logs;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ at the following locations. It will try to read the file from the specified path
|
|||||||
the following order. The file that is found *last* will override the settings from
|
the following order. The file that is found *last* will override the settings from
|
||||||
the files found before.
|
the files found before.
|
||||||
|
|
||||||
1. ``PREFIX_CONFIG_FILE`` environment variable
|
1. ``PRETIX_CONFIG_FILE`` environment variable
|
||||||
2. ``/etc/pretix/pretix.cfg``
|
2. ``/etc/pretix/pretix.cfg``
|
||||||
3. ``~/.pretix.cfg``
|
3. ``~/.pretix.cfg``
|
||||||
4. ``pretix.cfg`` in the current working directory
|
4. ``pretix.cfg`` in the current working directory
|
||||||
@@ -53,6 +53,10 @@ Example::
|
|||||||
A comma-separated list of plugins that are enabled by default for all new events.
|
A comma-separated list of plugins that are enabled by default for all new events.
|
||||||
Defaults to ``pretix.plugins.sendmail,pretix.plugins.statistics``.
|
Defaults to ``pretix.plugins.sendmail,pretix.plugins.statistics``.
|
||||||
|
|
||||||
|
``plugins_exclude``
|
||||||
|
A comma-separated list of plugins that are not available even though they are installed.
|
||||||
|
Defaults to an empty string.
|
||||||
|
|
||||||
``cookie_domain``
|
``cookie_domain``
|
||||||
The cookie domain to be set. Defaults to ``None``.
|
The cookie domain to be set. Defaults to ``None``.
|
||||||
|
|
||||||
@@ -70,6 +74,10 @@ Example::
|
|||||||
that are used to print tax amounts in the customer currency on invoices for some currencies. Set to ``off`` to
|
that are used to print tax amounts in the customer currency on invoices for some currencies. Set to ``off`` to
|
||||||
disable this feature. Defaults to ``on``.
|
disable this feature. Defaults to ``on``.
|
||||||
|
|
||||||
|
``audit_comments``
|
||||||
|
Enables or disables nagging staff users for leaving comments on their sessions for auditability.
|
||||||
|
Defaults to ``off``.
|
||||||
|
|
||||||
|
|
||||||
Locale settings
|
Locale settings
|
||||||
---------------
|
---------------
|
||||||
@@ -287,5 +295,13 @@ various places like order codes, secrets in the ticket QR codes, etc. Example::
|
|||||||
; Voucher code needs to be < 255 characters, default is 16
|
; Voucher code needs to be < 255 characters, default is 16
|
||||||
voucher_code=16
|
voucher_code=16
|
||||||
|
|
||||||
|
External tools
|
||||||
|
--------------
|
||||||
|
|
||||||
|
pretix can make use of some external tools if they are installed. Currently, they are all optional. Example::
|
||||||
|
|
||||||
|
[tools]
|
||||||
|
pdftk=/usr/bin/pdftk
|
||||||
|
|
||||||
.. _Python documentation: https://docs.python.org/3/library/configparser.html?highlight=configparser#supported-ini-file-structure
|
.. _Python documentation: https://docs.python.org/3/library/configparser.html?highlight=configparser#supported-ini-file-structure
|
||||||
.. _Celery documentation: http://docs.celeryproject.org/en/latest/configuration.html
|
.. _Celery documentation: http://docs.celeryproject.org/en/latest/userguide/configuration.html
|
||||||
|
|||||||
37
doc/admin/installation/dev_version.rst
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
.. highlight:: none
|
||||||
|
|
||||||
|
Installing a development version
|
||||||
|
================================
|
||||||
|
|
||||||
|
If you want to use a feature of pretix that is not yet contained in the last monthly release, you can also
|
||||||
|
install a development version with pretix.
|
||||||
|
|
||||||
|
.. warning:: When in production, we strongly recommend only installing released versions. Development versions might
|
||||||
|
be broken, incompatible to plugins, or in rare cases incompatible to upgrade later on.
|
||||||
|
|
||||||
|
|
||||||
|
Manual installation
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
You can use ``pip`` to update pretix directly to the development branch. Then, upgrade as usual::
|
||||||
|
|
||||||
|
$ source /var/pretix/venv/bin/activate
|
||||||
|
(venv)$ pip3 install -U "git+https://github.com/pretix/pretix.git#egg=pretix&subdirectory=src"
|
||||||
|
(venv)$ python -m pretix migrate
|
||||||
|
(venv)$ python -m pretix rebuild
|
||||||
|
(venv)$ python -m pretix updatestyles
|
||||||
|
# systemctl restart pretix-web pretix-worker
|
||||||
|
|
||||||
|
Docker installation
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
To use the latest development version with Docker, first pull it from Docker Hub::
|
||||||
|
|
||||||
|
$ docker pull pretix/standalone:latest
|
||||||
|
|
||||||
|
|
||||||
|
Then change your ``/etc/systemd/system/pretix.service`` file to use the ``:latest`` tag instead of ``:stable`` as well
|
||||||
|
and upgrade as usual::
|
||||||
|
|
||||||
|
$ systemctl restart pretix.service
|
||||||
|
$ docker exec -it pretix.service pretix upgrade
|
||||||
@@ -26,7 +26,7 @@ installation guides):
|
|||||||
* `Docker`_
|
* `Docker`_
|
||||||
* A SMTP server to send out mails, e.g. `Postfix`_ on your machine or some third-party server you have credentials for
|
* A SMTP server to send out mails, e.g. `Postfix`_ on your machine or some third-party server you have credentials for
|
||||||
* A HTTP reverse proxy, e.g. `nginx`_ or Apache to allow HTTPS connections
|
* A HTTP reverse proxy, e.g. `nginx`_ or Apache to allow HTTPS connections
|
||||||
* A `MySQL`_ or `PostgreSQL`_ database server
|
* A `PostgreSQL`_, `MySQL`_ 5.7+, or MariaDB 10.2.7+ database server
|
||||||
* A `redis`_ server
|
* A `redis`_ server
|
||||||
|
|
||||||
We also recommend that you use a firewall, although this is not a pretix-specific recommendation. If you're new to
|
We also recommend that you use a firewall, although this is not a pretix-specific recommendation. If you're new to
|
||||||
@@ -36,6 +36,9 @@ Linux and firewalls, we recommend that you start with `ufw`_.
|
|||||||
SSL certificates can be obtained for free these days. We also *do not* provide support for HTTP-only
|
SSL certificates can be obtained for free these days. We also *do not* provide support for HTTP-only
|
||||||
installations except for evaluation purposes.
|
installations except for evaluation purposes.
|
||||||
|
|
||||||
|
.. warning:: We recommend **PostgreSQL**. If you go for MySQL, make sure you run **MySQL 5.7 or newer** or
|
||||||
|
**MariaDB 10.2.7 or newer**.
|
||||||
|
|
||||||
On this guide
|
On this guide
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
@@ -58,7 +61,7 @@ Next, we need a database and a database user. We can create these with any kind
|
|||||||
our database's shell, e.g. for MySQL::
|
our database's shell, e.g. for MySQL::
|
||||||
|
|
||||||
$ mysql -u root -p
|
$ mysql -u root -p
|
||||||
mysql> CREATE DATABASE pretix DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
|
mysql> CREATE DATABASE pretix DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;
|
||||||
mysql> GRANT ALL PRIVILEGES ON pretix.* TO pretix@'localhost' IDENTIFIED BY '*********';
|
mysql> GRANT ALL PRIVILEGES ON pretix.* TO pretix@'localhost' IDENTIFIED BY '*********';
|
||||||
mysql> FLUSH PRIVILEGES;
|
mysql> FLUSH PRIVILEGES;
|
||||||
|
|
||||||
@@ -268,8 +271,8 @@ to re-build your custom image after you pulled ``pretix/standalone`` if you want
|
|||||||
.. _pretix.eu: https://pretix.eu/
|
.. _pretix.eu: https://pretix.eu/
|
||||||
.. _MySQL: https://dev.mysql.com/doc/refman/5.7/en/linux-installation-apt-repo.html
|
.. _MySQL: https://dev.mysql.com/doc/refman/5.7/en/linux-installation-apt-repo.html
|
||||||
.. _PostgreSQL: https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-9-4-on-debian-8
|
.. _PostgreSQL: https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-9-4-on-debian-8
|
||||||
.. _redis: http://blog.programster.org/debian-8-install-redis-server/
|
.. _redis: https://blog.programster.org/debian-8-install-redis-server/
|
||||||
.. _ufw: https://en.wikipedia.org/wiki/Uncomplicated_Firewall
|
.. _ufw: https://en.wikipedia.org/wiki/Uncomplicated_Firewall
|
||||||
.. _redis website: http://redis.io/topics/security
|
.. _redis website: https://redis.io/topics/security
|
||||||
.. _redis in docker: https://hub.docker.com/r/_/redis/
|
.. _redis in docker: https://hub.docker.com/r/_/redis/
|
||||||
.. _strong encryption settings: https://mozilla.github.io/server-side-tls/ssl-config-generator/
|
.. _strong encryption settings: https://mozilla.github.io/server-side-tls/ssl-config-generator/
|
||||||
|
|||||||
84
doc/admin/installation/enterprise.rst
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
.. highlight:: none
|
||||||
|
|
||||||
|
Installing pretix Enterprise plugins
|
||||||
|
====================================
|
||||||
|
|
||||||
|
If you want to use a feature of pretix that is part of our commercial offering pretix Enterprise, you need to follow
|
||||||
|
some extra steps. Installation works similar to normal pretix plugins, but involves a few extra steps.
|
||||||
|
|
||||||
|
Buying the license
|
||||||
|
------------------
|
||||||
|
|
||||||
|
To obtain a license, please get in touch at sales@pretix.eu. Please let us know how many tickets you roughly intend
|
||||||
|
to sell per year and how many servers you want to use the plugin on. We recommend having a look at our `price list`_
|
||||||
|
first.
|
||||||
|
|
||||||
|
|
||||||
|
Manual installation
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
First, generate an SSH key for the system user that you install pretix as. In our tutorial, that would be the user
|
||||||
|
``pretix``. Choose an empty passphrase::
|
||||||
|
|
||||||
|
# su pretix
|
||||||
|
$ ssh-keygen
|
||||||
|
Generating public/private rsa key pair.
|
||||||
|
Enter file in which to save the key (/var/pretix/.ssh/id_rsa):
|
||||||
|
Enter passphrase (empty for no passphrase):
|
||||||
|
Enter same passphrase again:
|
||||||
|
Your identification has been saved in /var/pretix/.ssh/id_rsa.
|
||||||
|
Your public key has been saved in /var/pretix/.ssh/id_rsa.pub.
|
||||||
|
|
||||||
|
Next, send the content of the *public* key to your sales representative at pretix::
|
||||||
|
|
||||||
|
$ cat /var/pretix/.ssh/id_rsa.pub
|
||||||
|
ssh-rsa AAAAB3N...744HZawHlD pretix@foo
|
||||||
|
|
||||||
|
After we configured your key in our system, you can install the plugin directly using ``pip`` from the URL we told
|
||||||
|
you, for example::
|
||||||
|
|
||||||
|
$ source /var/pretix/venv/bin/activate
|
||||||
|
(venv)$ pip3 install -U "git+ssh://git@code.rami.io:10022/pretix/pretix-slack.git@stable#egg=pretix-slack"
|
||||||
|
(venv)$ python -m pretix migrate
|
||||||
|
(venv)$ python -m pretix rebuild
|
||||||
|
# systemctl restart pretix-web pretix-worker
|
||||||
|
|
||||||
|
Docker installation
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
To install a plugin, you need to build your own docker image. To do so, create a new directory to work in. As a first
|
||||||
|
step, generate a new SSH key in that directory to use for authentication with us::
|
||||||
|
|
||||||
|
$ cd /home/me/mypretixdocker
|
||||||
|
$ ssh-keygen -N "" -f id_pretix_enterprise
|
||||||
|
|
||||||
|
Next, send the content of the *public* key to your sales representative at pretix::
|
||||||
|
|
||||||
|
$ cat id_pretix_enterprise.pub
|
||||||
|
ssh-rsa AAAAB3N...744HZawHlD pretix@foo
|
||||||
|
|
||||||
|
After we configured your key in our system, you can add a ``Dockerfile`` in your directory that includes the newly
|
||||||
|
generated key and installs the plugin from the URL we told you::
|
||||||
|
|
||||||
|
FROM pretix/standalone:stable
|
||||||
|
USER root
|
||||||
|
COPY id_pretix_enterprise /root/.ssh/id_rsa
|
||||||
|
COPY id_pretix_enterprise.pub /root/.ssh/id_rsa.pub
|
||||||
|
RUN chmod -R 0600 /root/.ssh && \
|
||||||
|
mkdir -p /etc/ssh && \
|
||||||
|
ssh-keyscan -t rsa -p 10022 code.rami.io >> /root/.ssh/known_hosts && \
|
||||||
|
echo StrictHostKeyChecking=no >> /root/.ssh/config && \
|
||||||
|
pip3 install -Ue "git+ssh://git@code.rami.io:10022/pretix/pretix-slack.git@stable#egg=pretix-slack" && \
|
||||||
|
cd /pretix/src && \
|
||||||
|
sudo -u pretixuser make production
|
||||||
|
USER pretixuser
|
||||||
|
|
||||||
|
Then, build the image for docker::
|
||||||
|
|
||||||
|
$ docker build -t mypretix
|
||||||
|
|
||||||
|
You can now use that image ``mypretix`` instead of ``pretix/standalone:stable`` in your ``/etc/systemd/system/pretix.service``
|
||||||
|
service file. Be sure to re-build your custom image after you pulled ``pretix/standalone`` if you want to perform an
|
||||||
|
update to a new version of pretix.
|
||||||
|
|
||||||
|
.. _price list: https://pretix.eu/about/en/pricing
|
||||||
@@ -21,6 +21,9 @@ To use pretix, you will need the following things:
|
|||||||
|
|
||||||
.. warning:: Do not ever use SQLite in production. It will break.
|
.. warning:: Do not ever use SQLite in production. It will break.
|
||||||
|
|
||||||
|
.. warning:: We recommend **PostgreSQL**. If you go for MySQL, make sure you run **MySQL 5.7 or newer** or
|
||||||
|
**MariaDB 10.2.7 or newer**.
|
||||||
|
|
||||||
* A **reverse proxy**. pretix needs to deliver some static content to your users (e.g. CSS, images, ...). While pretix
|
* A **reverse proxy**. pretix needs to deliver some static content to your users (e.g. CSS, images, ...). While pretix
|
||||||
is capable of doing this, having this handled by a proper web server like **nginx** or **Apache** will be much
|
is capable of doing this, having this handled by a proper web server like **nginx** or **Apache** will be much
|
||||||
faster. Also, you need a proxying web server in front to provide SSL encryption.
|
faster. Also, you need a proxying web server in front to provide SSL encryption.
|
||||||
|
|||||||
@@ -10,3 +10,5 @@ for your needs.
|
|||||||
general
|
general
|
||||||
docker_smallscale
|
docker_smallscale
|
||||||
manual_smallscale
|
manual_smallscale
|
||||||
|
dev_version
|
||||||
|
enterprise
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ installation guides):
|
|||||||
|
|
||||||
* A SMTP server to send out mails, e.g. `Postfix`_ on your machine or some third-party server you have credentials for
|
* A SMTP server to send out mails, e.g. `Postfix`_ on your machine or some third-party server you have credentials for
|
||||||
* A HTTP reverse proxy, e.g. `nginx`_ or Apache to allow HTTPS connections
|
* A HTTP reverse proxy, e.g. `nginx`_ or Apache to allow HTTPS connections
|
||||||
* A `MySQL`_ or `PostgreSQL`_ database server
|
* A `PostgreSQL`_, `MySQL`_ 5.7+, or MariaDB 10.2.7+ database server
|
||||||
* A `redis`_ server
|
* A `redis`_ server
|
||||||
|
|
||||||
We also recommend that you use a firewall, although this is not a pretix-specific recommendation. If you're new to
|
We also recommend that you use a firewall, although this is not a pretix-specific recommendation. If you're new to
|
||||||
@@ -33,6 +33,9 @@ Linux and firewalls, we recommend that you start with `ufw`_.
|
|||||||
SSL certificates can be obtained for free these days. We also *do not* provide support for HTTP-only
|
SSL certificates can be obtained for free these days. We also *do not* provide support for HTTP-only
|
||||||
installations except for evaluation purposes.
|
installations except for evaluation purposes.
|
||||||
|
|
||||||
|
.. warning:: We recommend **PostgreSQL**. If you go for MySQL, make sure you run **MySQL 5.7 or newer** or
|
||||||
|
**MariaDB 10.2.7 or newer**.
|
||||||
|
|
||||||
Unix user
|
Unix user
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@@ -50,7 +53,7 @@ Having the database server installed, we still need a database and a database us
|
|||||||
of database managing tool or directly on our database's shell, e.g. for MySQL::
|
of database managing tool or directly on our database's shell, e.g. for MySQL::
|
||||||
|
|
||||||
$ mysql -u root -p
|
$ mysql -u root -p
|
||||||
mysql> CREATE DATABASE pretix DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
|
mysql> CREATE DATABASE pretix DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;
|
||||||
mysql> GRANT ALL PRIVILEGES ON pretix.* TO pretix@'localhost' IDENTIFIED BY '*********';
|
mysql> GRANT ALL PRIVILEGES ON pretix.* TO pretix@'localhost' IDENTIFIED BY '*********';
|
||||||
mysql> FLUSH PRIVILEGES;
|
mysql> FLUSH PRIVILEGES;
|
||||||
|
|
||||||
@@ -121,8 +124,7 @@ command if you're running PostgreSQL::
|
|||||||
|
|
||||||
(venv)$ pip3 install "pretix[mysql]" gunicorn
|
(venv)$ pip3 install "pretix[mysql]" gunicorn
|
||||||
|
|
||||||
If you are running Python 3.4, you also need to ``pip3 install typing``. This is not required on 3.5 or newer.
|
Note that you need Python 3.5 or newer. You can find out your Python version using ``python -V``.
|
||||||
You can find out your Python version using ``python -V``.
|
|
||||||
|
|
||||||
We also need to create a data directory::
|
We also need to create a data directory::
|
||||||
|
|
||||||
@@ -298,6 +300,6 @@ example::
|
|||||||
.. _pretix.eu: https://pretix.eu/
|
.. _pretix.eu: https://pretix.eu/
|
||||||
.. _MySQL: https://dev.mysql.com/doc/refman/5.7/en/linux-installation-apt-repo.html
|
.. _MySQL: https://dev.mysql.com/doc/refman/5.7/en/linux-installation-apt-repo.html
|
||||||
.. _PostgreSQL: https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-9-4-on-debian-8
|
.. _PostgreSQL: https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-9-4-on-debian-8
|
||||||
.. _redis: http://blog.programster.org/debian-8-install-redis-server/
|
.. _redis: https://blog.programster.org/debian-8-install-redis-server/
|
||||||
.. _ufw: https://en.wikipedia.org/wiki/Uncomplicated_Firewall
|
.. _ufw: https://en.wikipedia.org/wiki/Uncomplicated_Firewall
|
||||||
.. _strong encryption settings: https://mozilla.github.io/server-side-tls/ssl-config-generator/
|
.. _strong encryption settings: https://mozilla.github.io/server-side-tls/ssl-config-generator/
|
||||||
|
|||||||
9
doc/api/auth.rst
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Authentication
|
||||||
|
==============
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
tokenauth
|
||||||
|
oauth
|
||||||
|
deviceauth
|
||||||
137
doc/api/deviceauth.rst
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
.. _`rest-deviceauth`:
|
||||||
|
|
||||||
|
Device authentication
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Initializing a new device
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Users can create new devices in the "Device" section of their organizer settings. When creating
|
||||||
|
a new device, users can specify a list of events the device is allowed to access. After a new
|
||||||
|
device is created, users will be presented initialization instructions, consisting of an URL
|
||||||
|
and an initialization token. They will also be shown as a QR code with the following contents::
|
||||||
|
|
||||||
|
{"handshake_version": 1, "url": "https://pretix.eu", "token": "kpp4jn8g2ynzonp6"}
|
||||||
|
|
||||||
|
Your application should be able to scan a QR code of this type, or allow to enter the URL and the
|
||||||
|
initialization token manually. The handshake version is not used for manual initialization. When a
|
||||||
|
QR code is scanned with a higher handshake version than you support, you should reject the request
|
||||||
|
and prompt the user to update the client application.
|
||||||
|
|
||||||
|
After your application received the token, you need to call the initialization endpoint to obtain
|
||||||
|
a proper API token. At this point, you need to identify the name and version of your application,
|
||||||
|
as well as the type of underlying hardware. Example:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/device/initialize HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"token": "kpp4jn8g2ynzonp6",
|
||||||
|
"hardware_brand": "Samsung",
|
||||||
|
"hardware_model": "Galaxy S",
|
||||||
|
"software_brand": "pretixdroid",
|
||||||
|
"software_version": "4.0.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
Every initialization token can only be used once. On success, you will receive a response containing
|
||||||
|
information on your device as well as your API token:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"organizer": "foo",
|
||||||
|
"device_id": 5,
|
||||||
|
"unique_serial": "HHZ9LW9JWP390VFZ",
|
||||||
|
"api_token": "1kcsh572fonm3hawalrncam4l1gktr2rzx25a22l8g9hx108o9oi0rztpcvwnfnd",
|
||||||
|
"name": "Bar"
|
||||||
|
}
|
||||||
|
|
||||||
|
Please make sure that you store this ``api_token`` value. We also recommend storing your device ID, your assigned
|
||||||
|
``unique_serial``, and the ``organizer`` you have access to, but that's up to you.
|
||||||
|
|
||||||
|
In case of an error, the response will look like this:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 400 Bad Request
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{"token":["This initialization token has already been used."]}
|
||||||
|
|
||||||
|
|
||||||
|
Performing API requests
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
You need to include the API token with every request to pretix' API in the ``Authorization`` header
|
||||||
|
like the following:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
:emphasize-lines: 3
|
||||||
|
|
||||||
|
GET /api/v1/organizers/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Authorization: Device 1kcsh572fonm3hawalrncam4l1gktr2rzx25a22l8g9hx108o9oi0rztpcvwnfnd
|
||||||
|
|
||||||
|
Updating the software version
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
If your application is updated, we ask you to tell the server about the new version in use. You can do this at the
|
||||||
|
following endpoint:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/device/update HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Device 1kcsh572fonm3hawalrncam4l1gktr2rzx25a22l8g9hx108o9oi0rztpcvwnfnd
|
||||||
|
|
||||||
|
{
|
||||||
|
"hardware_brand": "Samsung",
|
||||||
|
"hardware_model": "Galaxy S",
|
||||||
|
"software_brand": "pretixdroid",
|
||||||
|
"software_version": "4.1.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
Creating a new API key
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
If you think your API key might have leaked or just want to be extra cautious, the API allows you to create a new key.
|
||||||
|
The old API key will be invalid immediately. A request for a new key looks like this:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/device/roll HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Authorization: Device 1kcsh572fonm3hawalrncam4l1gktr2rzx25a22l8g9hx108o9oi0rztpcvwnfnd
|
||||||
|
|
||||||
|
The response will look like the response to the initialization request.
|
||||||
|
|
||||||
|
Removing a device
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
If you want implement a way to to deprovision a device in your software, you can call the ``revoke`` endpoint to
|
||||||
|
invalidate your API key. There is no way to reverse this operation.
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/device/revoke HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Authorization: Device 1kcsh572fonm3hawalrncam4l1gktr2rzx25a22l8g9hx108o9oi0rztpcvwnfnd
|
||||||
|
|
||||||
|
This can also be done by the user through the web interface.
|
||||||
|
|
||||||
|
Permissions
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Device authentication is currently hardcoded to grant the following permissions:
|
||||||
|
|
||||||
|
* View event meta data and products etc.
|
||||||
|
* View and change orders
|
||||||
|
|
||||||
|
Devices cannot change events or products and cannot access vouchers.
|
||||||
@@ -6,43 +6,42 @@ with pretix' REST API, such as authentication, pagination and similar definition
|
|||||||
|
|
||||||
.. _`rest-auth`:
|
.. _`rest-auth`:
|
||||||
|
|
||||||
Obtaining an API token
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
To authenticate your API requests, you need to obtain an API token. You can create a
|
|
||||||
token in the pretix web interface on the level of organizer teams. Create a new team
|
|
||||||
or choose an existing team that has the level of permissions the token should have and
|
|
||||||
create a new token using the form below the list of team members:
|
|
||||||
|
|
||||||
.. image:: img/token_form.png
|
|
||||||
:class: screenshot
|
|
||||||
|
|
||||||
You can enter a description for the token to distinguish from other tokens later on.
|
|
||||||
Once you click "Add", you will be provided with an API token in the success message.
|
|
||||||
Copy this token, as you won't be able to retrieve it again.
|
|
||||||
|
|
||||||
.. image:: img/token_success.png
|
|
||||||
:class: screenshot
|
|
||||||
|
|
||||||
Authentication
|
Authentication
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
You need to include the API token with every request to pretix' API in the ``Authorization`` header
|
To access the API, you need to present valid authentication credentials. pretix currently
|
||||||
like the following:
|
supports the following authorization schemes:
|
||||||
|
|
||||||
.. sourcecode:: http
|
* :ref:`rest-tokenauth`: This is the simplest way and recommended for server-side applications
|
||||||
:emphasize-lines: 3
|
that interact with pretix without user interaction.
|
||||||
|
* :ref:`rest-oauth`: This is the recommended way to use if you write a third-party application
|
||||||
|
that users can connect with their pretix account. It provides the best user experience, but
|
||||||
|
requires user interaction and slightly more implementation effort.
|
||||||
|
* :ref:`rest-deviceauth`: This is the recommended way if you build apps or hardware devices that can
|
||||||
|
connect to pretix, e.g. for processing check-ins or to sell tickets offline. It provides a way
|
||||||
|
to uniquely identify devices and allows for a quick configuration flow inside your software.
|
||||||
|
* Authentication using browser sessions: This is used by the pretix web interface and it is *not*
|
||||||
|
officially supported for use by third-party applications. It might change or be removed at any
|
||||||
|
time without prior notice. If you use it, you need to comply with Django's `CSRF policies`_.
|
||||||
|
|
||||||
GET /api/v1/organizers/ HTTP/1.1
|
Permissions
|
||||||
Host: pretix.eu
|
-----------
|
||||||
Authorization: Token e1l6gq2ye72thbwkacj7jbri7a7tvxe614ojv8ybureain92ocub46t5gab5966k
|
|
||||||
|
|
||||||
.. note:: The API currently also supports authentication via browser sessions, i.e. the
|
The API follows pretix team based permissions model. Each organizer can have several teams
|
||||||
same way that you authenticate with pretix when using the browser interface.
|
each with it's own set of permissions. Each team can have any number of API keys attached.
|
||||||
Using this type of authentication is *not* officially supported for use by
|
|
||||||
third-party clients and might change or be removed at any time. We plan on
|
To access a given endpoint the team the API key belongs to needs to have the corresponding
|
||||||
adding OAuth2 support in the future for user-level authentication. If you want
|
permission for the organizer/event being accessed.
|
||||||
to use session authentication, be sure to comply with Django's `CSRF policies`_.
|
|
||||||
|
Possible permissions are:
|
||||||
|
|
||||||
|
* Can create events
|
||||||
|
* Can change event settings
|
||||||
|
* Can change product settings
|
||||||
|
* Can view orders
|
||||||
|
* Can change orders
|
||||||
|
* Can view vouchers
|
||||||
|
* Can change vouchers
|
||||||
|
|
||||||
Compatibility
|
Compatibility
|
||||||
-------------
|
-------------
|
||||||
@@ -90,6 +89,41 @@ respective page.
|
|||||||
The field ``results`` contains a list of objects representing the first results. For most
|
The field ``results`` contains a list of objects representing the first results. For most
|
||||||
objects, every page contains 50 results.
|
objects, every page contains 50 results.
|
||||||
|
|
||||||
|
Conditional fetching
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
If you pull object lists from pretix' APIs regularly, we ask you to implement conditional fetching
|
||||||
|
to avoid unnecessary data traffic. This is not supported on all resources and we currently implement
|
||||||
|
two different mechanisms for different resources, which is necessary because we can only obtain best
|
||||||
|
efficiency for resources that do not support deletion operations.
|
||||||
|
|
||||||
|
Object-level conditional fetching
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The :ref:`rest-orders` resource list contains an HTTP header called ``X-Page-Generated`` containing the
|
||||||
|
current time on the server in ISO 8601 format. On your next request, you can pass this header
|
||||||
|
(as is, without any modifications necessary) as the ``modified_since`` query parameter and you will receive
|
||||||
|
a list containing only objects that have changed in the time since your last request.
|
||||||
|
|
||||||
|
List-level conditional fetching
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
If modification checks are not possible with this granularity, you can instead check for the full list.
|
||||||
|
In this case, the list of objects may contain a regular HTTP header ``Last-Modified`` with the date of the
|
||||||
|
last modification to any item of that resource. You can then pass this date back in your next request in the
|
||||||
|
``If-Modified-Since`` header. If the any object has changed in the meantime, you will receive back a full list
|
||||||
|
(if something it missing, this means the object has been deleted). If nothing happened, we'll send back a
|
||||||
|
``304 Not Modified`` return code.
|
||||||
|
|
||||||
|
This is currently implemented on the following resources:
|
||||||
|
|
||||||
|
* :ref:`rest-categories`
|
||||||
|
* :ref:`rest-items`
|
||||||
|
* :ref:`rest-questions`
|
||||||
|
* :ref:`rest-quotas`
|
||||||
|
* :ref:`rest-subevents`
|
||||||
|
* :ref:`rest-taxrules`
|
||||||
|
|
||||||
Errors
|
Errors
|
||||||
------
|
------
|
||||||
|
|
||||||
@@ -114,6 +148,7 @@ Field specific input errors include the name of the offending fields as keys in
|
|||||||
|
|
||||||
{"amount": ["A valid integer is required."], "description": ["This field may not be blank."]}
|
{"amount": ["A valid integer is required."], "description": ["This field may not be blank."]}
|
||||||
|
|
||||||
|
If you see errors of type ``429 Too Many Requests``, you should read our documentation on :ref:`rest-ratelimit`.
|
||||||
|
|
||||||
Data types
|
Data types
|
||||||
----------
|
----------
|
||||||
@@ -146,4 +181,4 @@ as the string values ``true`` and ``false``.
|
|||||||
If the ``ordering`` parameter is documented for a resource, you can use it to sort the result set by one of the allowed
|
If the ``ordering`` parameter is documented for a resource, you can use it to sort the result set by one of the allowed
|
||||||
fields. Prepend a ``-`` to the field name to reverse the sort order.
|
fields. Prepend a ``-`` to the field name to reverse the sort order.
|
||||||
|
|
||||||
.. _CSRF policies: https://docs.djangoproject.com/en/1.11/ref/csrf/#ajax
|
.. _CSRF policies: https://docs.djangoproject.com/en/1.11/ref/csrf/#ajax
|
||||||
|
|||||||
@@ -14,4 +14,7 @@ in functionality over time.
|
|||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
fundamentals
|
fundamentals
|
||||||
|
auth
|
||||||
resources/index
|
resources/index
|
||||||
|
ratelimit
|
||||||
|
webhooks
|
||||||
|
|||||||
207
doc/api/oauth.rst
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
.. _`rest-oauth`:
|
||||||
|
|
||||||
|
OAuth authentication / "Connect with pretix"
|
||||||
|
============================================
|
||||||
|
|
||||||
|
In addition to static tokens, pretix supports `OAuth2`_-based authentication starting with
|
||||||
|
pretix 1.16. This allows you to put a "Connect with pretix" button into your website or tool
|
||||||
|
that allows the user to easily set up a connection between the two systems.
|
||||||
|
|
||||||
|
If you haven't worked with OAuth before, have a look at the `OAuth2 Simplified`_ tutorial.
|
||||||
|
|
||||||
|
Registering an application
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
To use OAuth, you need to register your application with the pretix instance you want to connect to.
|
||||||
|
In order to do this, log in to your pretix account and go to your user settings. Click on "Authorized applications"
|
||||||
|
first and then on "Manage your own apps". From there, you can "Create a new application".
|
||||||
|
|
||||||
|
You should fill in a descriptive name of your application that allows users to recognize who you are. You also need to
|
||||||
|
give a list of fully-qualified URLs that users will be redirected to after a successful authorization. After you pressed
|
||||||
|
"Save", you will be presented with a client ID and a client secret. Please note them down and treat the client secret
|
||||||
|
like a password; it should not become available to your users.
|
||||||
|
|
||||||
|
Obtaining an authorization grant
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
To authorize a new user, link or redirect them to the ``authorize`` endpoint, passing your client ID as a query
|
||||||
|
parameter. Additionally, you can pass a scope (currently either ``read``, ``write``, or ``read write``)
|
||||||
|
and an URL the user should be redirected to after successful or failed authorization. You also need to pass the
|
||||||
|
``response_type`` parameter with a value of ``code``. Example::
|
||||||
|
|
||||||
|
https://pretix.eu/api/v1/oauth/authorize?client_id=lsLi0hNL0vk53mEdYjNJxHUn1PcO1R6wVg81dLNT&response_type=code&scope=read+write&redirect_uri=https://pretalx.com
|
||||||
|
|
||||||
|
To prevent CSRF attacks, you can also optionally pass a ``state`` parameter with a random string. Later, when
|
||||||
|
redirecting back to your application, we will pass the same ``state`` parameter back to you, so you can compare if they
|
||||||
|
match.
|
||||||
|
|
||||||
|
After the user granted or denied access, they will be redirected back either to the ``redirect_url`` you passed in the
|
||||||
|
query or to the first redirect URL configured in your application settings.
|
||||||
|
|
||||||
|
On successful registration, we will append the query parameter ``code`` to the URL containing an authorization code.
|
||||||
|
For example, we might redirect the user to this URL::
|
||||||
|
|
||||||
|
https://pretalx.com/?code=eYBBf8gmeD4E01HLoj0XflqO4Lg3Cw&state=e3KCh9mfx07qxU4bRpXk
|
||||||
|
|
||||||
|
You will need this ``code`` parameter to perform the next step.
|
||||||
|
|
||||||
|
On a failed registration, a query string like ``?error=access_denied`` will be appended to the redirection URL.
|
||||||
|
|
||||||
|
.. note:: In this step, the user is allowed to restrict your access to certain organizer accounts. If you try to
|
||||||
|
re-authenticate the user later, the user might be instantly redirected back to you if authorization is already
|
||||||
|
given and would therefore be unable to review their organizer restriction settings. You can append the
|
||||||
|
``approval_prompt=force`` query parameter if you want to make sure the user actively needs to confirm the
|
||||||
|
authorization.
|
||||||
|
|
||||||
|
Getting an access token
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Using the ``code`` value you obtained above and your client ID, you can now request an access token that actually gives
|
||||||
|
access to the API. The ``token`` endpoint expects you to authenticate using `HTTP Basic authentication`_ using your client
|
||||||
|
ID as a username and your client secret as a password. You are also required to again supply the same ``redirect_uri``
|
||||||
|
parameter that you used for the authorization.
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/oauth/token
|
||||||
|
|
||||||
|
Request a new access token
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/oauth/token HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Authorization: Basic bHNMaTBoTkwwdms1M21FZFlqTkp4SFVuMVBjTzFSNndWZzgxZExOVDplSmpzZVA0UjJMN0hMcjBiS0p1b3BmbnJtT2cyY3NDeTdYaFVVZ0FoalhUU0NhZHhRTjk3cVNvMkpPaXlWTFpQOEozaTVQd1FVdFIwNUNycG5ac2Z0bXJjdmNTbkZ1SkFmb2ZsUTdZUDRpSjZNTWFYTHIwQ0FpNlhIRFJjV1Awcg==
|
||||||
|
|
||||||
|
grant_type=authorization_code&code=eYBBf8gmeD4E01HLoj0XflqO4Lg3Cw&redirect_uri=https://pretalx.com
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"access_token": "i3ytqTSRWsKp16fqjekHXa4tdM4qNC",
|
||||||
|
"expires_in": 86400,
|
||||||
|
"token_type": "Bearer",
|
||||||
|
"scope": "read write",
|
||||||
|
"refresh_token": "XBK0r8z4A4TTeR9LyMUyU2AM5rqpXp"
|
||||||
|
}
|
||||||
|
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
|
||||||
|
|
||||||
|
As you can see, you receive two types of tokens: One "access token", and one "refresh token". The access token is valid
|
||||||
|
for a day and can be used to actually access the API. The refresh token does not have an expiration date and can be used
|
||||||
|
to obtain a new access_token after a day, so you should make sure to store the access token safely if you need long-term
|
||||||
|
access.
|
||||||
|
|
||||||
|
Using the API with an access token
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
You can supply a valid access token as a ``Bearer``-type token in the ``Authorization`` header to get API access.
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
:emphasize-lines: 3
|
||||||
|
|
||||||
|
GET /api/v1/organizers/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Authorization: Bearer i3ytqTSRWsKp16fqjekHXa4tdM4qNC
|
||||||
|
|
||||||
|
Refreshing an access token
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
You can obtain a new access token using your refresh token any time. This can be done using the same ``token`` endpoint
|
||||||
|
used to obtain the first access token above, but with a different set of parameters:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/oauth/token HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Authorization: Basic bHNMaTBoTkwwdms1M21FZFlqTkp4SFVuMVBjTzFSNndWZzgxZExOVDplSmpzZVA0UjJMN0hMcjBiS0p1b3BmbnJtT2cyY3NDeTdYaFVVZ0FoalhUU0NhZHhRTjk3cVNvMkpPaXlWTFpQOEozaTVQd1FVdFIwNUNycG5ac2Z0bXJjdmNTbkZ1SkFmb2ZsUTdZUDRpSjZNTWFYTHIwQ0FpNlhIRFJjV1Awcg==
|
||||||
|
|
||||||
|
grant_type=refresh_token&refresh_token=XBK0r8z4A4TTeR9LyMUyU2AM5rqpXp
|
||||||
|
|
||||||
|
The previous access token will instantly become invalid.
|
||||||
|
|
||||||
|
Revoking a token
|
||||||
|
----------------
|
||||||
|
|
||||||
|
If you don't need a token any more or if you believe it may have been compromised, you can use the ``revoke_token``
|
||||||
|
endpoint to revoke it.
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/oauth/revoke_token
|
||||||
|
|
||||||
|
Revoke an access or refresh token. If you revoke an access token, you can still create a new one using the refresh token. If you
|
||||||
|
revoke a refresh token, the connected access token will also be revoked.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/oauth/revoke_token HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Authorization: Basic bHNMaTBoTkwwdms1M21FZFlqTkp4SFVuMVBjTzFSNndWZzgxZExOVDplSmpzZVA0UjJMN0hMcjBiS0p1b3BmbnJtT2cyY3NDeTdYaFVVZ0FoalhUU0NhZHhRTjk3cVNvMkpPaXlWTFpQOEozaTVQd1FVdFIwNUNycG5ac2Z0bXJjdmNTbkZ1SkFmb2ZsUTdZUDRpSjZNTWFYTHIwQ0FpNlhIRFJjV1Awcg==
|
||||||
|
|
||||||
|
token=XBK0r8z4A4TTeR9LyMUyU2AM5rqpXp
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
|
||||||
|
If you want to revoke your client secret, you can generate a new one in the list of your managed applications in the
|
||||||
|
pretix user interface.
|
||||||
|
|
||||||
|
Fetching the user profile
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
If you need the user's meta data, you can fetch it here:
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/me
|
||||||
|
|
||||||
|
Returns the profile of the authenticated user
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/me HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Authorization: Bearer i3ytqTSRWsKp16fqjekHXa4tdM4qNC
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
email: "admin@localhost",
|
||||||
|
fullname: "John Doe",
|
||||||
|
locale: "de",
|
||||||
|
timezone: "Europe/Berlin"
|
||||||
|
}
|
||||||
|
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
|
||||||
|
.. _OAuth2: https://en.wikipedia.org/wiki/OAuth
|
||||||
|
.. _OAuth2 Simplified: https://aaronparecki.com/oauth-2-simplified/
|
||||||
|
.. _HTTP Basic authentication: https://en.wikipedia.org/wiki/Basic_access_authentication
|
||||||
31
doc/api/ratelimit.rst
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
.. _`rest-ratelimit`:
|
||||||
|
|
||||||
|
Rate limiting
|
||||||
|
=============
|
||||||
|
|
||||||
|
.. note:: This page only applies to the pretix Hosted service at pretix.eu. APIs of custom pretix installations do not
|
||||||
|
enforce any rate limiting by default.
|
||||||
|
|
||||||
|
All authenticated requests to pretix' API are rate limited. If you exceed the limits, you will receive a response
|
||||||
|
with HTTP status code ``429 Too Many Requests``. This response will have a ``Retry-After`` header, containing the number
|
||||||
|
of seconds you are supposed to wait until you try again. We expect that all API clients respect this. If you continue
|
||||||
|
to burst requests after a ``429`` status code, we might get in touch with you or, in extreme cases, disable your API
|
||||||
|
access.
|
||||||
|
|
||||||
|
Currently, the following rate limits apply:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. rst-class:: rest-resource-table
|
||||||
|
|
||||||
|
===================================== =================================================================================
|
||||||
|
Authentication method Rate limit
|
||||||
|
===================================== =================================================================================
|
||||||
|
:ref:`rest-deviceauth` 360 requests per minute per device
|
||||||
|
:ref:`rest-tokenauth` 360 requests per minute per organizer account
|
||||||
|
:ref:`rest-oauth` 360 requests per minute per combination of accessed organizer and OAuth application
|
||||||
|
Session authentication *Not an officially supported authentication method for external access*
|
||||||
|
===================================== =================================================================================
|
||||||
|
|
||||||
|
If you require a higher rate limit, please get in touch at support@pretix.eu and tell us about your use case, we are
|
||||||
|
sure we can work something out.
|
||||||
264
doc/api/resources/carts.rst
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
.. _rest-carts:
|
||||||
|
|
||||||
|
Cart positions
|
||||||
|
==============
|
||||||
|
|
||||||
|
The API provides limited access to the cart position data model. This API currently only allows creating and deleting
|
||||||
|
cart positions to reserve quota.
|
||||||
|
|
||||||
|
Cart position resource
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
The cart position resource contains the following public fields:
|
||||||
|
|
||||||
|
.. rst-class:: rest-resource-table
|
||||||
|
|
||||||
|
===================================== ========================== =======================================================
|
||||||
|
Field Type Description
|
||||||
|
===================================== ========================== =======================================================
|
||||||
|
id integer Internal ID of the cart position
|
||||||
|
cart_id string Identifier of the cart this belongs to. Needs to end
|
||||||
|
in "@api" for API-created positions.
|
||||||
|
datetime datetime Time of creation
|
||||||
|
expires datetime The cart position will expire at this time and no longer block quota
|
||||||
|
item integer ID of the item
|
||||||
|
variation integer ID of the variation (or ``null``)
|
||||||
|
price money (string) Price of this position
|
||||||
|
attendee_name string Specified attendee name for this position (or ``null``)
|
||||||
|
attendee_name_parts object of strings Composition of attendee name (i.e. first name, last name, …)
|
||||||
|
attendee_email string Specified attendee email address for this position (or ``null``)
|
||||||
|
voucher integer Internal ID of the voucher used for this position (or ``null``)
|
||||||
|
addon_to integer Internal ID of the position this position is an add-on for (or ``null``)
|
||||||
|
subevent integer ID of the date inside an event series this position belongs to (or ``null``).
|
||||||
|
answers list of objects Answers to user-defined questions
|
||||||
|
├ question integer Internal ID of the answered question
|
||||||
|
├ answer string Text representation of the answer
|
||||||
|
├ question_identifier string The question's ``identifier`` field
|
||||||
|
├ options list of integers Internal IDs of selected option(s)s (only for choice types)
|
||||||
|
└ option_identifiers list of strings The ``identifier`` fields of the selected option(s)s
|
||||||
|
===================================== ========================== =======================================================
|
||||||
|
|
||||||
|
.. versionchanged:: 1.17
|
||||||
|
|
||||||
|
This resource has been added.
|
||||||
|
|
||||||
|
|
||||||
|
Cart position endpoints
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/cartpositions/
|
||||||
|
|
||||||
|
Returns a list of API-created cart positions.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/organizers/bigevents/events/sampleconf/cartpositions/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
X-Page-Generated: 2017-12-01T10:00:00Z
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 1,
|
||||||
|
"next": null,
|
||||||
|
"previous": null,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"cart_id": "XwokV8FojQviD9jhtDzKvHFdlLRNMhlfo3cNjGbuK6MUTQDT@api",
|
||||||
|
"item": 1,
|
||||||
|
"variation": null,
|
||||||
|
"price": "23.00",
|
||||||
|
"attendee_name": null,
|
||||||
|
"attendee_name_parts": {},
|
||||||
|
"attendee_email": null,
|
||||||
|
"voucher": null,
|
||||||
|
"addon_to": null,
|
||||||
|
"subevent": null,
|
||||||
|
"datetime": "2018-06-11T10:00:00Z",
|
||||||
|
"expires": "2018-06-11T10:00:00Z",
|
||||||
|
"includes_tax": true,
|
||||||
|
"answers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
:query integer page: The page number in case of a multi-page result set, default is 1
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/cartpositions/(id)/
|
||||||
|
|
||||||
|
Returns information on one cart position, identified by its internal ID.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/organizers/bigevents/events/sampleconf/cartpositions/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"cart_id": "XwokV8FojQviD9jhtDzKvHFdlLRNMhlfo3cNjGbuK6MUTQDT@api",
|
||||||
|
"item": 1,
|
||||||
|
"variation": null,
|
||||||
|
"price": "23.00",
|
||||||
|
"attendee_name": null,
|
||||||
|
"attendee_name_parts": {},
|
||||||
|
"attendee_email": null,
|
||||||
|
"voucher": null,
|
||||||
|
"addon_to": null,
|
||||||
|
"subevent": null,
|
||||||
|
"datetime": "2018-06-11T10:00:00Z",
|
||||||
|
"expires": "2018-06-11T10:00:00Z",
|
||||||
|
"includes_tax": true,
|
||||||
|
"answers": []
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to fetch
|
||||||
|
:param event: The ``slug`` field of the event to fetch
|
||||||
|
:param id: The ``id`` field of the position to fetch
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||||
|
:statuscode 404: The requested cart position does not exist.
|
||||||
|
|
||||||
|
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/cartpositions/
|
||||||
|
|
||||||
|
Creates a new cart position.
|
||||||
|
|
||||||
|
.. warning:: This endpoint is considered **experimental**. It might change at any time without prior notice.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This endpoint is intended for advanced users. It is not designed to be used to build your own shop frontend.
|
||||||
|
There is a lot that it does not or can not do, and you will need to be careful using it.
|
||||||
|
It allows to bypass many of the restrictions imposed when creating a cart through the
|
||||||
|
regular shop.
|
||||||
|
|
||||||
|
Specifically, this endpoint currently
|
||||||
|
|
||||||
|
* does not validate if products are only to be sold in a specific time frame
|
||||||
|
|
||||||
|
* does not validate if the event's ticket sales are already over or haven't started
|
||||||
|
|
||||||
|
* does not support add-on products at the moment
|
||||||
|
|
||||||
|
* does not check or calculate prices but believes any prices you send
|
||||||
|
|
||||||
|
* does not support the redemption of vouchers
|
||||||
|
|
||||||
|
* does not prevent you from buying items that can only be bought with a voucher
|
||||||
|
|
||||||
|
* does not support file upload questions
|
||||||
|
|
||||||
|
You can supply the following fields of the resource:
|
||||||
|
|
||||||
|
* ``cart_id`` (optional, needs to end in ``@api``)
|
||||||
|
* ``item``
|
||||||
|
* ``variation`` (optional)
|
||||||
|
* ``price``
|
||||||
|
* ``attendee_name`` **or** ``attendee_name_parts`` (optional)
|
||||||
|
* ``attendee_email`` (optional)
|
||||||
|
* ``subevent`` (optional)
|
||||||
|
* ``expires`` (optional)
|
||||||
|
* ``includes_tax`` (optional)
|
||||||
|
* ``answers``
|
||||||
|
|
||||||
|
* ``question``
|
||||||
|
* ``answer``
|
||||||
|
* ``options``
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/organizers/bigevents/events/sampleconf/cartpositions/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"item": 1,
|
||||||
|
"variation": null,
|
||||||
|
"price": "23.00",
|
||||||
|
"attendee_name_parts": {
|
||||||
|
"given_name": "Peter",
|
||||||
|
"family_name": "Miller"
|
||||||
|
},
|
||||||
|
"attendee_email": null,
|
||||||
|
"answers": [
|
||||||
|
{
|
||||||
|
"question": 1,
|
||||||
|
"answer": "23",
|
||||||
|
"options": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subevent": null
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 201 Created
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
(Full cart position resource, see above.)
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer of the event to create a position for
|
||||||
|
:param event: The ``slug`` field of the event to create a position for
|
||||||
|
:statuscode 201: no error
|
||||||
|
:statuscode 400: The item could not be created due to invalid submitted data or lack of quota.
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this
|
||||||
|
order.
|
||||||
|
|
||||||
|
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/cartpositions/(id)/
|
||||||
|
|
||||||
|
Deletes a cart position, identified by its internal ID.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
DELETE /api/v1/organizers/bigevents/events/sampleconf/cartpositions/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 204 No Content
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to fetch
|
||||||
|
:param event: The ``slug`` field of the event to fetch
|
||||||
|
:param id: The ``id`` field of the position to delete
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||||
|
:statuscode 404: The requested cart position does not exist.
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
.. _`rest-categories`:
|
||||||
|
|
||||||
Item categories
|
Item categories
|
||||||
===============
|
===============
|
||||||
|
|
||||||
@@ -14,6 +16,7 @@ Field Type Description
|
|||||||
===================================== ========================== =======================================================
|
===================================== ========================== =======================================================
|
||||||
id integer Internal ID of the category
|
id integer Internal ID of the category
|
||||||
name multi-lingual string The category's visible name
|
name multi-lingual string The category's visible name
|
||||||
|
internal_name string An optional name that is only used in the backend
|
||||||
description multi-lingual string A public description (might include markdown, can
|
description multi-lingual string A public description (might include markdown, can
|
||||||
be ``null``)
|
be ``null``)
|
||||||
position integer An integer, used for sorting the categories
|
position integer An integer, used for sorting the categories
|
||||||
@@ -22,6 +25,14 @@ is_addon boolean If ``True``, it
|
|||||||
defining add-ons for other products.
|
defining add-ons for other products.
|
||||||
===================================== ========================== =======================================================
|
===================================== ========================== =======================================================
|
||||||
|
|
||||||
|
.. versionchanged:: 1.14
|
||||||
|
|
||||||
|
The operations POST, PATCH, PUT and DELETE have been added.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.16
|
||||||
|
|
||||||
|
The field ``internal_name`` has been added.
|
||||||
|
|
||||||
|
|
||||||
Endpoints
|
Endpoints
|
||||||
---------
|
---------
|
||||||
@@ -54,6 +65,7 @@ Endpoints
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": {"en": "Tickets"},
|
"name": {"en": "Tickets"},
|
||||||
|
"internal_name": "",
|
||||||
"description": {"en": "Tickets are what you need to get in."},
|
"description": {"en": "Tickets are what you need to get in."},
|
||||||
"position": 1,
|
"position": 1,
|
||||||
"is_addon": false
|
"is_addon": false
|
||||||
@@ -95,6 +107,7 @@ Endpoints
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": {"en": "Tickets"},
|
"name": {"en": "Tickets"},
|
||||||
|
"internal_name": "",
|
||||||
"description": {"en": "Tickets are what you need to get in."},
|
"description": {"en": "Tickets are what you need to get in."},
|
||||||
"position": 1,
|
"position": 1,
|
||||||
"is_addon": false
|
"is_addon": false
|
||||||
@@ -106,3 +119,121 @@ Endpoints
|
|||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
:statuscode 401: Authentication failure
|
:statuscode 401: Authentication failure
|
||||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||||
|
|
||||||
|
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/categories/
|
||||||
|
|
||||||
|
Creates a new category
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/organizers/bigevents/events/sampleconf/categories/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": {"en": "Tickets"},
|
||||||
|
"internal_name": "",
|
||||||
|
"description": {"en": "Tickets are what you need to get in."},
|
||||||
|
"position": 1,
|
||||||
|
"is_addon": false
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 201 Created
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": {"en": "Tickets"},
|
||||||
|
"internal_name": "",
|
||||||
|
"description": {"en": "Tickets are what you need to get in."},
|
||||||
|
"position": 1,
|
||||||
|
"is_addon": false
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer of the event to create a category for
|
||||||
|
:param event: The ``slug`` field of the event to create a category for
|
||||||
|
:statuscode 201: no error
|
||||||
|
:statuscode 400: The category could not be created due to invalid submitted data.
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
|
||||||
|
|
||||||
|
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/categories/(id)/
|
||||||
|
|
||||||
|
Update a category. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
|
||||||
|
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
|
||||||
|
want to change.
|
||||||
|
|
||||||
|
You can change all fields of the resource except the ``id`` field.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
PATCH /api/v1/organizers/bigevents/events/sampleconf/categories/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content-Type: application/json
|
||||||
|
Content-Length: 94
|
||||||
|
|
||||||
|
{
|
||||||
|
"is_addon": true
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": {"en": "Tickets"},
|
||||||
|
"internal_name": "",
|
||||||
|
"description": {"en": "Tickets are what you need to get in."},
|
||||||
|
"position": 1,
|
||||||
|
"is_addon": true
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to modify
|
||||||
|
:param event: The ``slug`` field of the event to modify
|
||||||
|
:param id: The ``id`` field of the category to modify
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 400: The category could not be modified due to invalid submitted data
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
|
||||||
|
|
||||||
|
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/category/(id)/
|
||||||
|
|
||||||
|
Delete a category.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
DELETE /api/v1/organizers/bigevents/events/sampleconf/categories/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 204 No Content
|
||||||
|
Vary: Accept
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to modify
|
||||||
|
:param event: The ``slug`` field of the event to modify
|
||||||
|
:param id: The ``id`` field of the category to delete
|
||||||
|
:statuscode 204: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.
|
||||||
|
|||||||
@@ -21,11 +21,12 @@ Field Type Description
|
|||||||
===================================== ========================== =======================================================
|
===================================== ========================== =======================================================
|
||||||
id integer Internal ID of the check-in list
|
id integer Internal ID of the check-in list
|
||||||
name string The internal name of the check-in list
|
name string The internal name of the check-in list
|
||||||
all_products boolean If ``True``, the check-in lists contains tickets of all products in this event. The ``limit_products`` field is ignored in this case.
|
all_products boolean If ``true``, the check-in lists contains tickets of all products in this event. The ``limit_products`` field is ignored in this case.
|
||||||
limit_products list of integers List of item IDs to include in this list.
|
limit_products list of integers List of item IDs to include in this list.
|
||||||
subevent integer ID of the date inside an event series this list belongs to (or ``null``).
|
subevent integer ID of the date inside an event series this list belongs to (or ``null``).
|
||||||
position_count integer Number of tickets that match this list (read-only).
|
position_count integer Number of tickets that match this list (read-only).
|
||||||
checkin_count integer Number of check-ins performed on this list (read-only).
|
checkin_count integer Number of check-ins performed on this list (read-only).
|
||||||
|
include_pending boolean If ``true``, the check-in list also contains tickets from orders in pending state.
|
||||||
===================================== ========================== =======================================================
|
===================================== ========================== =======================================================
|
||||||
|
|
||||||
.. versionchanged:: 1.10
|
.. versionchanged:: 1.10
|
||||||
@@ -36,9 +37,17 @@ checkin_count integer Number of check
|
|||||||
|
|
||||||
The ``positions`` endpoints have been added.
|
The ``positions`` endpoints have been added.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.13
|
||||||
|
|
||||||
|
The ``include_pending`` field has been added.
|
||||||
|
|
||||||
Endpoints
|
Endpoints
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
.. versionchanged:: 1.15
|
||||||
|
|
||||||
|
The ``../status/`` detail endpoint has been added.
|
||||||
|
|
||||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/
|
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/
|
||||||
|
|
||||||
Returns a list of all check-in lists within a given event.
|
Returns a list of all check-in lists within a given event.
|
||||||
@@ -71,6 +80,7 @@ Endpoints
|
|||||||
"position_count": 456,
|
"position_count": 456,
|
||||||
"all_products": true,
|
"all_products": true,
|
||||||
"limit_products": [],
|
"limit_products": [],
|
||||||
|
"include_pending": false,
|
||||||
"subevent": null
|
"subevent": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -111,6 +121,7 @@ Endpoints
|
|||||||
"position_count": 456,
|
"position_count": 456,
|
||||||
"all_products": true,
|
"all_products": true,
|
||||||
"limit_products": [],
|
"limit_products": [],
|
||||||
|
"include_pending": false,
|
||||||
"subevent": null
|
"subevent": null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +132,72 @@ Endpoints
|
|||||||
:statuscode 401: Authentication failure
|
:statuscode 401: Authentication failure
|
||||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(id)/status/
|
||||||
|
|
||||||
|
Returns detailed status information on a check-in list, identified by its ID.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/organizers/bigevents/events/sampleconf/checkinlists/1/status/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"checkin_count": 17,
|
||||||
|
"position_count": 42,
|
||||||
|
"event": {
|
||||||
|
"name": "Demo Converence",
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "T-Shirt",
|
||||||
|
"id": 1,
|
||||||
|
"checkin_count": 1,
|
||||||
|
"admission": False,
|
||||||
|
"position_count": 1,
|
||||||
|
"variations": [
|
||||||
|
{
|
||||||
|
"value": "Red",
|
||||||
|
"id": 1,
|
||||||
|
"checkin_count": 1,
|
||||||
|
"position_count": 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "Blue",
|
||||||
|
"id": 2,
|
||||||
|
"checkin_count": 4,
|
||||||
|
"position_count": 8
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ticket",
|
||||||
|
"id": 2,
|
||||||
|
"checkin_count": 15,
|
||||||
|
"admission": True,
|
||||||
|
"position_count": 22,
|
||||||
|
"variations": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to fetch
|
||||||
|
:param event: The ``slug`` field of the event to fetch
|
||||||
|
:param id: The ``id`` field of the check-in list to fetch
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||||
|
|
||||||
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/
|
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/
|
||||||
|
|
||||||
Creates a new check-in list.
|
Creates a new check-in list.
|
||||||
@@ -156,6 +233,7 @@ Endpoints
|
|||||||
"position_count": 0,
|
"position_count": 0,
|
||||||
"all_products": false,
|
"all_products": false,
|
||||||
"limit_products": [1, 2],
|
"limit_products": [1, 2],
|
||||||
|
"include_pending": false,
|
||||||
"subevent": null
|
"subevent": null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,6 +282,7 @@ Endpoints
|
|||||||
"position_count": 42,
|
"position_count": 42,
|
||||||
"all_products": false,
|
"all_products": false,
|
||||||
"limit_products": [1, 2],
|
"limit_products": [1, 2],
|
||||||
|
"include_pending": false,
|
||||||
"subevent": null
|
"subevent": null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,6 +324,18 @@ Endpoints
|
|||||||
Order position endpoints
|
Order position endpoints
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
.. versionchanged:: 1.15
|
||||||
|
|
||||||
|
The order positions endpoint has been extended by the filter queries ``item__in``, ``variation__in``,
|
||||||
|
``order__status__in``, ``subevent__in``, ``addon_to__in``, and ``search``. The search for attendee names and order
|
||||||
|
codes is now case-insensitive.
|
||||||
|
|
||||||
|
The ``.../redeem/`` endpoint has been added.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.0
|
||||||
|
|
||||||
|
The order positions endpoint has been extended by the filter queries ``voucher`` and ``voucher__code``.
|
||||||
|
|
||||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(list)/positions/
|
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(list)/positions/
|
||||||
|
|
||||||
Returns a list of all order positions within a given event. The result is the same as
|
Returns a list of all order positions within a given event. The result is the same as
|
||||||
@@ -280,6 +371,9 @@ Order position endpoints
|
|||||||
"variation": null,
|
"variation": null,
|
||||||
"price": "23.00",
|
"price": "23.00",
|
||||||
"attendee_name": "Peter",
|
"attendee_name": "Peter",
|
||||||
|
"attendee_name_parts": {
|
||||||
|
"full_name": "Peter",
|
||||||
|
},
|
||||||
"attendee_email": null,
|
"attendee_email": null,
|
||||||
"voucher": null,
|
"voucher": null,
|
||||||
"tax_rate": "0.00",
|
"tax_rate": "0.00",
|
||||||
@@ -288,6 +382,7 @@ Order position endpoints
|
|||||||
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
|
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
|
||||||
"addon_to": null,
|
"addon_to": null,
|
||||||
"subevent": null,
|
"subevent": null,
|
||||||
|
"pseudonymization_id": "MQLJvANO3B",
|
||||||
"checkins": [
|
"checkins": [
|
||||||
{
|
{
|
||||||
"list": 1,
|
"list": 1,
|
||||||
@@ -316,15 +411,26 @@ Order position endpoints
|
|||||||
``order__datetime``, ``positionid``, ``attendee_name``, ``last_checked_in`` and ``order__email``. Default:
|
``order__datetime``, ``positionid``, ``attendee_name``, ``last_checked_in`` and ``order__email``. Default:
|
||||||
``attendee_name,positionid``
|
``attendee_name,positionid``
|
||||||
:query string order: Only return positions of the order with the given order code
|
:query string order: Only return positions of the order with the given order code
|
||||||
|
:query string search: Fuzzy search matching the attendee name, order code, invoice address name as well as to the beginning of the secret.
|
||||||
:query integer item: Only return positions with the purchased item matching the given ID.
|
:query integer item: Only return positions with the purchased item matching the given ID.
|
||||||
|
:query integer item__in: Only return positions with the purchased item matching one of the given comma-separated IDs.
|
||||||
:query integer variation: Only return positions with the purchased item variation matching the given ID.
|
:query integer variation: Only return positions with the purchased item variation matching the given ID.
|
||||||
|
:query integer variation__in: Only return positions with one of the purchased item variation matching the given
|
||||||
|
comma-separated IDs.
|
||||||
:query string attendee_name: Only return positions with the given value in the attendee_name field. Also, add-on
|
:query string attendee_name: Only return positions with the given value in the attendee_name field. Also, add-on
|
||||||
products positions are shown if they refer to an attendee with the given name.
|
products positions are shown if they refer to an attendee with the given name.
|
||||||
:query string secret: Only return positions with the given ticket secret.
|
:query string secret: Only return positions with the given ticket secret.
|
||||||
:query bollean has_checkin: If set to ``true`` or ``false``, only return positions that have or have not been
|
:query string order__status: Only return positions with the given order status.
|
||||||
checked in already on this list.
|
:query string order__status__in: Only return positions with one the given comma-separated order status.
|
||||||
|
:query boolean has_checkin: If set to ``true`` or ``false``, only return positions that have or have not been
|
||||||
|
checked in already.
|
||||||
:query integer subevent: Only return positions of the sub-event with the given ID
|
:query integer subevent: Only return positions of the sub-event with the given ID
|
||||||
|
:query integer subevent__in: Only return positions of one of the sub-events with the given comma-separated IDs
|
||||||
:query integer addon_to: Only return positions that are add-ons to the position with the given ID.
|
:query integer addon_to: Only return positions that are add-ons to the position with the given ID.
|
||||||
|
:query integer addon_to__in: Only return positions that are add-ons to one of the positions with the given
|
||||||
|
comma-separated IDs.
|
||||||
|
:query string voucher: Only return positions with a specific voucher.
|
||||||
|
:query string voucher__code: Only return positions with a specific voucher code.
|
||||||
:param organizer: The ``slug`` field of the organizer to fetch
|
:param organizer: The ``slug`` field of the organizer to fetch
|
||||||
:param event: The ``slug`` field of the event to fetch
|
:param event: The ``slug`` field of the event to fetch
|
||||||
:param list: The ID of the check-in list to look for
|
:param list: The ID of the check-in list to look for
|
||||||
@@ -333,7 +439,7 @@ Order position endpoints
|
|||||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||||
:statuscode 404: The requested check-in list does not exist.
|
:statuscode 404: The requested check-in list does not exist.
|
||||||
|
|
||||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(list)/positions/(id)
|
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(list)/positions/(id)/
|
||||||
|
|
||||||
Returns information on one order position, identified by its internal ID.
|
Returns information on one order position, identified by its internal ID.
|
||||||
The result format is the same as the :ref:`order-position-resource`, with one important difference: the
|
The result format is the same as the :ref:`order-position-resource`, with one important difference: the
|
||||||
@@ -343,7 +449,7 @@ Order position endpoints
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /api/v1/organizers/bigevents/events/sampleconf/checkinlists/1/positions/ HTTP/1.1
|
GET /api/v1/organizers/bigevents/events/sampleconf/checkinlists/1/positions/23442/ HTTP/1.1
|
||||||
Host: pretix.eu
|
Host: pretix.eu
|
||||||
Accept: application/json, text/javascript
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
@@ -363,6 +469,9 @@ Order position endpoints
|
|||||||
"variation": null,
|
"variation": null,
|
||||||
"price": "23.00",
|
"price": "23.00",
|
||||||
"attendee_name": "Peter",
|
"attendee_name": "Peter",
|
||||||
|
"attendee_name_parts": {
|
||||||
|
"full_name": "Peter",
|
||||||
|
},
|
||||||
"attendee_email": null,
|
"attendee_email": null,
|
||||||
"voucher": null,
|
"voucher": null,
|
||||||
"tax_rate": "0.00",
|
"tax_rate": "0.00",
|
||||||
@@ -371,6 +480,7 @@ Order position endpoints
|
|||||||
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
|
"secret": "z3fsn8jyufm5kpk768q69gkbyr5f4h6w",
|
||||||
"addon_to": null,
|
"addon_to": null,
|
||||||
"subevent": null,
|
"subevent": null,
|
||||||
|
"pseudonymization_id": "MQLJvANO3B",
|
||||||
"checkins": [
|
"checkins": [
|
||||||
{
|
{
|
||||||
"list": 1,
|
"list": 1,
|
||||||
@@ -400,3 +510,127 @@ Order position endpoints
|
|||||||
:statuscode 401: Authentication failure
|
:statuscode 401: Authentication failure
|
||||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||||
:statuscode 404: The requested order position or check-in list does not exist.
|
:statuscode 404: The requested order position or check-in list does not exist.
|
||||||
|
|
||||||
|
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(list)/positions/(id)/redeem/
|
||||||
|
|
||||||
|
Tries to redeem an order position, identified by its internal ID, i.e. checks the attendee in. This endpoint
|
||||||
|
accepts a number of optional requests in the body.
|
||||||
|
|
||||||
|
:<json boolean questions_supported: When this parameter is set to ``true``, handling of questions is supported. If
|
||||||
|
you do not implement question handling in your user interface, you **must**
|
||||||
|
set this to ``false``. In that case, questions will just be ignored. Defaults
|
||||||
|
to ``true``.
|
||||||
|
:<json datetime datetime: Specifies the datetime of the check-in. If not supplied, the current time will be used.
|
||||||
|
:<json boolean force: Specifies that the check-in should succeed regardless of previous check-ins or required
|
||||||
|
questions that have not been filled. Defaults to ``false``.
|
||||||
|
:<json boolean ignore_unpaid: Specifies that the check-in should succeed even if the order is in pending state.
|
||||||
|
Defaults to ``false``.
|
||||||
|
:<json string nonce: You can set this parameter to a unique random value to identify this check-in. If you're sending
|
||||||
|
this request twice with the same nonce, the second request will also succeed but will always
|
||||||
|
create only one check-in object even when the previous request was successful as well. This
|
||||||
|
allows for a certain level of idempotency and enables you to re-try after a connection failure.
|
||||||
|
:<json object answers: If questions are supported/required, you may/must supply a mapping of question IDs to their
|
||||||
|
respective answers. The answers should always be strings. In case of (multiple-)choice-type
|
||||||
|
answers, the string should contain the (comma-separated) IDs of the selected options.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/organizers/bigevents/events/sampleconf/checkinlists/1/positions/234/redeem/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
{
|
||||||
|
"force": false,
|
||||||
|
"ignore_unpaid": false,
|
||||||
|
"nonce": "Pvrk50vUzQd0DhdpNRL4I4OcXsvg70uA",
|
||||||
|
"datetime": null,
|
||||||
|
"questions_supported": true,
|
||||||
|
"answers": {
|
||||||
|
"4": "XS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example successful response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 201 Created
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example response with required questions**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 400 Bad Request
|
||||||
|
Content-Type: text/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"status": "incomplete"
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"question": {"en": "T-Shirt size"},
|
||||||
|
"type": "C",
|
||||||
|
"required": false,
|
||||||
|
"items": [1, 2],
|
||||||
|
"position": 1,
|
||||||
|
"identifier": "WY3TP9SL",
|
||||||
|
"ask_during_checkin": true,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"identifier": "LVETRWVU",
|
||||||
|
"position": 0,
|
||||||
|
"answer": {"en": "S"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"identifier": "DFEMJWMJ",
|
||||||
|
"position": 1,
|
||||||
|
"answer": {"en": "M"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"identifier": "W9AH7RDE",
|
||||||
|
"position": 2,
|
||||||
|
"answer": {"en": "L"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example error response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Type: text/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"status": "error",
|
||||||
|
"reason": "unpaid",
|
||||||
|
}
|
||||||
|
|
||||||
|
Possible error reasons:
|
||||||
|
|
||||||
|
* ``unpaid`` - Ticket is not paid for or has been refunded
|
||||||
|
* ``already_redeemed`` - Ticket already has been redeemed
|
||||||
|
* ``product`` - Tickets with this product may not be scanned at this device
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to fetch
|
||||||
|
:param event: The ``slug`` field of the event to fetch
|
||||||
|
:param list: The ID of the check-in list to look for
|
||||||
|
:param id: The ``id`` field of the order position to fetch
|
||||||
|
:statuscode 201: no error
|
||||||
|
:statuscode 400: Invalid or incomplete request, see above
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||||
|
:statuscode 404: The requested order position or check-in list does not exist.
|
||||||
|
|||||||
@@ -25,14 +25,26 @@ presale_start datetime The date at whi
|
|||||||
presale_end datetime The date at which the ticket shop closes (or ``null``)
|
presale_end datetime The date at which the ticket shop closes (or ``null``)
|
||||||
location multi-lingual string The event location (or ``null``)
|
location multi-lingual string The event location (or ``null``)
|
||||||
has_subevents boolean ``True`` if the event series feature is active for this
|
has_subevents boolean ``True`` if the event series feature is active for this
|
||||||
event
|
event. Cannot change after event is created.
|
||||||
meta_data dict Values set for organizer-specific meta data parameters.
|
meta_data dict Values set for organizer-specific meta data parameters.
|
||||||
|
plugins list A list of package names of the enabled plugins for this
|
||||||
|
event.
|
||||||
===================================== ========================== =======================================================
|
===================================== ========================== =======================================================
|
||||||
|
|
||||||
|
|
||||||
.. versionchanged:: 1.7
|
.. versionchanged:: 1.7
|
||||||
|
|
||||||
The ``meta_data`` field has been added.
|
The ``meta_data`` field has been added.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.15
|
||||||
|
|
||||||
|
The ``plugins`` field has been added.
|
||||||
|
The operations POST, PATCH, PUT and DELETE have been added.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.1
|
||||||
|
|
||||||
|
Filters have been added to the list of events.
|
||||||
|
|
||||||
Endpoints
|
Endpoints
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@@ -40,6 +52,8 @@ Endpoints
|
|||||||
|
|
||||||
Returns a list of all events within a given organizer the authenticated user/token has access to.
|
Returns a list of all events within a given organizer the authenticated user/token has access to.
|
||||||
|
|
||||||
|
Permission required: "Can change event settings"
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
@@ -74,12 +88,24 @@ Endpoints
|
|||||||
"presale_end": null,
|
"presale_end": null,
|
||||||
"location": null,
|
"location": null,
|
||||||
"has_subevents": false,
|
"has_subevents": false,
|
||||||
"meta_data": {}
|
"meta_data": {},
|
||||||
|
"plugins": [
|
||||||
|
"pretix.plugins.banktransfer"
|
||||||
|
"pretix.plugins.stripe"
|
||||||
|
"pretix.plugins.paypal"
|
||||||
|
"pretix.plugins.ticketoutputpdf"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
:query page: The page number in case of a multi-page result set, default is 1
|
:query page: The page number in case of a multi-page result set, default is 1
|
||||||
|
:query is_public: If set to ``true``/``false``, only events with a matching value of ``is_public`` are returned.
|
||||||
|
:query live: If set to ``true``/``false``, only events with a matching value of ``live`` are returned.
|
||||||
|
:query has_subevents: If set to ``true``/``false``, only events with a matching value of ``has_subevents`` are returned.
|
||||||
|
:query is_future: If set to ``true`` (``false``), only events that happen currently or in the future are (not) returned. Event series are never (always) returned.
|
||||||
|
:query is_past: If set to ``true`` (``false``), only events that are over are (not) returned. Event series are never (always) returned.
|
||||||
|
:query ends_after: If set to a date and time, only events that happen during of after the given time are returned. Event series are never returned.
|
||||||
:param organizer: The ``slug`` field of a valid organizer
|
:param organizer: The ``slug`` field of a valid organizer
|
||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
:statuscode 401: Authentication failure
|
:statuscode 401: Authentication failure
|
||||||
@@ -89,6 +115,8 @@ Endpoints
|
|||||||
|
|
||||||
Returns information on one event, identified by its slug.
|
Returns information on one event, identified by its slug.
|
||||||
|
|
||||||
|
Permission required: "Can change event settings"
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
@@ -118,7 +146,13 @@ Endpoints
|
|||||||
"presale_end": null,
|
"presale_end": null,
|
||||||
"location": null,
|
"location": null,
|
||||||
"has_subevents": false,
|
"has_subevents": false,
|
||||||
"meta_data": {}
|
"meta_data": {},
|
||||||
|
"plugins": [
|
||||||
|
"pretix.plugins.banktransfer"
|
||||||
|
"pretix.plugins.stripe"
|
||||||
|
"pretix.plugins.paypal"
|
||||||
|
"pretix.plugins.ticketoutputpdf"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
:param organizer: The ``slug`` field of the organizer to fetch
|
:param organizer: The ``slug`` field of the organizer to fetch
|
||||||
@@ -126,3 +160,242 @@ Endpoints
|
|||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
:statuscode 401: Authentication failure
|
:statuscode 401: Authentication failure
|
||||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view it.
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view it.
|
||||||
|
|
||||||
|
.. http:post:: /api/v1/organizers/(organizer)/events/
|
||||||
|
|
||||||
|
Creates a new event
|
||||||
|
|
||||||
|
Please note that events cannot be created as 'live' using this endpoint. Quotas and payment must be added to the
|
||||||
|
event before sales can go live.
|
||||||
|
|
||||||
|
Permission required: "Can create events"
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/organizers/bigevents/events/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": {"en": "Sample Conference"},
|
||||||
|
"slug": "sampleconf",
|
||||||
|
"live": false,
|
||||||
|
"currency": "EUR",
|
||||||
|
"date_from": "2017-12-27T10:00:00Z",
|
||||||
|
"date_to": null,
|
||||||
|
"date_admission": null,
|
||||||
|
"is_public": false,
|
||||||
|
"presale_start": null,
|
||||||
|
"presale_end": null,
|
||||||
|
"location": null,
|
||||||
|
"has_subevents": false,
|
||||||
|
"meta_data": {},
|
||||||
|
"plugins": [
|
||||||
|
"pretix.plugins.stripe",
|
||||||
|
"pretix.plugins.paypal"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 201 Created
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": {"en": "Sample Conference"},
|
||||||
|
"slug": "sampleconf",
|
||||||
|
"live": false,
|
||||||
|
"currency": "EUR",
|
||||||
|
"date_from": "2017-12-27T10:00:00Z",
|
||||||
|
"date_to": null,
|
||||||
|
"date_admission": null,
|
||||||
|
"is_public": false,
|
||||||
|
"presale_start": null,
|
||||||
|
"presale_end": null,
|
||||||
|
"location": null,
|
||||||
|
"has_subevents": false,
|
||||||
|
"meta_data": {},
|
||||||
|
"plugins": [
|
||||||
|
"pretix.plugins.stripe",
|
||||||
|
"pretix.plugins.paypal"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer of the event to create.
|
||||||
|
:statuscode 201: no error
|
||||||
|
:statuscode 400: The event could not be created due to invalid submitted data.
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
|
||||||
|
|
||||||
|
|
||||||
|
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/clone/
|
||||||
|
|
||||||
|
Creates a new event with properties as set in the request body. The properties that are copied are: 'is_public',
|
||||||
|
settings, plugin settings, items, variations, add-ons, quotas, categories, tax rules, questions.
|
||||||
|
|
||||||
|
If the 'plugins' and/or 'is_public' fields are present in the post body this will determine their value. Otherwise
|
||||||
|
their value will be copied from the existing event.
|
||||||
|
|
||||||
|
Please note that you can only copy from events under the same organizer.
|
||||||
|
|
||||||
|
Permission required: "Can create events"
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/organizers/bigevents/events/sampleconf/clone/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": {"en": "Sample Conference"},
|
||||||
|
"slug": "sampleconf",
|
||||||
|
"live": false,
|
||||||
|
"currency": "EUR",
|
||||||
|
"date_from": "2017-12-27T10:00:00Z",
|
||||||
|
"date_to": null,
|
||||||
|
"date_admission": null,
|
||||||
|
"is_public": false,
|
||||||
|
"presale_start": null,
|
||||||
|
"presale_end": null,
|
||||||
|
"location": null,
|
||||||
|
"has_subevents": false,
|
||||||
|
"meta_data": {},
|
||||||
|
"plugins": [
|
||||||
|
"pretix.plugins.stripe",
|
||||||
|
"pretix.plugins.paypal"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 201 Created
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": {"en": "Sample Conference"},
|
||||||
|
"slug": "sampleconf",
|
||||||
|
"live": false,
|
||||||
|
"currency": "EUR",
|
||||||
|
"date_from": "2017-12-27T10:00:00Z",
|
||||||
|
"date_to": null,
|
||||||
|
"date_admission": null,
|
||||||
|
"is_public": false,
|
||||||
|
"presale_start": null,
|
||||||
|
"presale_end": null,
|
||||||
|
"location": null,
|
||||||
|
"has_subevents": false,
|
||||||
|
"meta_data": {},
|
||||||
|
"plugins": [
|
||||||
|
"pretix.plugins.stripe",
|
||||||
|
"pretix.plugins.paypal"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer of the event to create.
|
||||||
|
:param event: The ``slug`` field of the event to copy settings and items from.
|
||||||
|
:statuscode 201: no error
|
||||||
|
:statuscode 400: The event could not be created due to invalid submitted data.
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
|
||||||
|
|
||||||
|
|
||||||
|
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/
|
||||||
|
|
||||||
|
Updates an event
|
||||||
|
|
||||||
|
Permission required: "Can change event settings"
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
PATCH /api/v1/organizers/bigevents/events/sampleconf/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"pretix.plugins.banktransfer",
|
||||||
|
"pretix.plugins.stripe",
|
||||||
|
"pretix.plugins.paypal",
|
||||||
|
"pretix.plugins.pretixdroid"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": {"en": "Sample Conference"},
|
||||||
|
"slug": "sampleconf",
|
||||||
|
"live": false,
|
||||||
|
"currency": "EUR",
|
||||||
|
"date_from": "2017-12-27T10:00:00Z",
|
||||||
|
"date_to": null,
|
||||||
|
"date_admission": null,
|
||||||
|
"is_public": false,
|
||||||
|
"presale_start": null,
|
||||||
|
"presale_end": null,
|
||||||
|
"location": null,
|
||||||
|
"has_subevents": false,
|
||||||
|
"meta_data": {},
|
||||||
|
"plugins": [
|
||||||
|
"pretix.plugins.banktransfer",
|
||||||
|
"pretix.plugins.stripe",
|
||||||
|
"pretix.plugins.paypal",
|
||||||
|
"pretix.plugins.pretixdroid"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer of the event to update
|
||||||
|
:param event: The ``slug`` field of the event to update
|
||||||
|
:statuscode 201: no error
|
||||||
|
:statuscode 400: The event could not be created due to invalid submitted data.
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
|
||||||
|
|
||||||
|
|
||||||
|
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/items/(id)/
|
||||||
|
|
||||||
|
Delete an event. Note that events with orders cannot be deleted to ensure data integrity.
|
||||||
|
|
||||||
|
Permission required: "Can change event settings"
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
DELETE /api/v1/organizers/bigevents/events/sampleconf/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 204 No Content
|
||||||
|
Vary: Accept
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to modify
|
||||||
|
:param event: The ``slug`` field of the event to delete
|
||||||
|
:statuscode 204: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.
|
||||||
|
|||||||
@@ -13,9 +13,12 @@ Resources and endpoints
|
|||||||
item_variations
|
item_variations
|
||||||
item_add-ons
|
item_add-ons
|
||||||
questions
|
questions
|
||||||
|
question_options
|
||||||
quotas
|
quotas
|
||||||
orders
|
orders
|
||||||
invoices
|
invoices
|
||||||
vouchers
|
vouchers
|
||||||
checkinlists
|
checkinlists
|
||||||
waitinglist
|
waitinglist
|
||||||
|
carts
|
||||||
|
webhooks
|
||||||
|
|||||||
@@ -223,3 +223,59 @@ Endpoints
|
|||||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||||
:statuscode 409: The file is not yet ready and will now be prepared. Retry the request after waiting for a few
|
:statuscode 409: The file is not yet ready and will now be prepared. Retry the request after waiting for a few
|
||||||
seconds.
|
seconds.
|
||||||
|
|
||||||
|
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/invoices/(invoice_no)/reissue/
|
||||||
|
|
||||||
|
Cancels the invoice and creates a new one.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/organizers/bigevents/events/sampleconf/invoices/00001/reissue/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 204 No Content
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/pdf
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to fetch
|
||||||
|
:param event: The ``slug`` field of the event to fetch
|
||||||
|
:param invoice_no: The ``invoice_no`` field of the invoice to reissue
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 400: The invoice has already been canceled
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
|
||||||
|
|
||||||
|
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/invoices/(invoice_no)/regenerate/
|
||||||
|
|
||||||
|
Re-generates the invoice from order data.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/organizers/bigevents/events/sampleconf/invoices/00001/regenerate/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 204 No Content
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/pdf
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to fetch
|
||||||
|
:param event: The ``slug`` field of the event to fetch
|
||||||
|
:param invoice_no: The ``invoice_no`` field of the invoice to regenerate
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 400: The invoice has already been canceled
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ Endpoints
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 201 Created
|
||||||
Vary: Accept
|
Vary: Accept
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Resource description
|
|||||||
|
|
||||||
Variations of items can be use for products (items) that are available in different sizes, colors or other variations
|
Variations of items can be use for products (items) that are available in different sizes, colors or other variations
|
||||||
of the same product.
|
of the same product.
|
||||||
The addons resource contains the following public fields:
|
The variations resource contains the following public fields:
|
||||||
|
|
||||||
.. rst-class:: rest-resource-table
|
.. rst-class:: rest-resource-table
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ Endpoints
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 201 Created
|
||||||
Vary: Accept
|
Vary: Accept
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
.. _rest-items:
|
||||||
|
|
||||||
Items
|
Items
|
||||||
=====
|
=====
|
||||||
|
|
||||||
@@ -14,6 +16,7 @@ Field Type Description
|
|||||||
===================================== ========================== =======================================================
|
===================================== ========================== =======================================================
|
||||||
id integer Internal ID of the item
|
id integer Internal ID of the item
|
||||||
name multi-lingual string The item's visible name
|
name multi-lingual string The item's visible name
|
||||||
|
internal_name string An optional name that is only used in the backend
|
||||||
default_price money (string) The item price that is applied if the price is not
|
default_price money (string) The item price that is applied if the price is not
|
||||||
overwritten by variations or other options.
|
overwritten by variations or other options.
|
||||||
category integer The ID of the category this item belongs to
|
category integer The ID of the category this item belongs to
|
||||||
@@ -34,6 +37,8 @@ admission boolean ``True`` for it
|
|||||||
position integer An integer, used for sorting
|
position integer An integer, used for sorting
|
||||||
picture string A product picture to be displayed in the shop
|
picture string A product picture to be displayed in the shop
|
||||||
(read-only).
|
(read-only).
|
||||||
|
sales_channels list of strings Sales channels this product is available on, such as
|
||||||
|
``"web"`` or ``"resellers"``. Defaults to ``["web"]``.
|
||||||
available_from datetime The first date time at which this item can be bought
|
available_from datetime The first date time at which this item can be bought
|
||||||
(or ``null``).
|
(or ``null``).
|
||||||
available_until datetime The last date time at which this item can be bought
|
available_until datetime The last date time at which this item can be bought
|
||||||
@@ -54,10 +59,17 @@ max_per_order integer This product ca
|
|||||||
checkin_attention boolean If ``True``, the check-in app should show a warning
|
checkin_attention boolean If ``True``, the check-in app should show a warning
|
||||||
that this ticket requires special attention if such
|
that this ticket requires special attention if such
|
||||||
a product is being scanned.
|
a product is being scanned.
|
||||||
|
original_price money (string) An original price, shown for comparison, not used
|
||||||
|
for price calculations.
|
||||||
|
require_approval boolean If ``True``, orders with this product will need to be
|
||||||
|
approved by the event organizer before they can be
|
||||||
|
paid.
|
||||||
has_variations boolean Shows whether or not this item has variations.
|
has_variations boolean Shows whether or not this item has variations.
|
||||||
variations list of objects A list with one object for each variation of this item.
|
variations list of objects A list with one object for each variation of this item.
|
||||||
Can be empty. Only writable on POST.
|
Can be empty. Only writable during creation,
|
||||||
|
use separate endpoint to modify this later.
|
||||||
├ id integer Internal ID of the variation
|
├ id integer Internal ID of the variation
|
||||||
|
├ value multi-lingual string The "name" of the variation
|
||||||
├ default_price money (string) The price set directly for this variation or ``null``
|
├ default_price money (string) The price set directly for this variation or ``null``
|
||||||
├ price money (string) The price used for this variation. This is either the
|
├ price money (string) The price used for this variation. This is either the
|
||||||
same as ``default_price`` if that value is set or equal
|
same as ``default_price`` if that value is set or equal
|
||||||
@@ -67,7 +79,8 @@ variations list of objects A list with one
|
|||||||
Markdown syntax or can be ``null``.
|
Markdown syntax or can be ``null``.
|
||||||
└ position integer An integer, used for sorting
|
└ position integer An integer, used for sorting
|
||||||
addons list of objects Definition of add-ons that can be chosen for this item.
|
addons list of objects Definition of add-ons that can be chosen for this item.
|
||||||
Only writable on POST.
|
Only writable during creation,
|
||||||
|
use separate endpoint to modify this later.
|
||||||
├ addon_category integer Internal ID of the item category the add-on can be
|
├ addon_category integer Internal ID of the item category the add-on can be
|
||||||
chosen from.
|
chosen from.
|
||||||
├ min_count integer The minimal number of add-ons that need to be chosen.
|
├ min_count integer The minimal number of add-ons that need to be chosen.
|
||||||
@@ -86,6 +99,18 @@ addons list of objects Definition of a
|
|||||||
The write operations ``POST``, ``PATCH``, ``PUT``, and ``DELETE`` have been added.
|
The write operations ``POST``, ``PATCH``, ``PUT``, and ``DELETE`` have been added.
|
||||||
The attribute ``price_included`` has been added to ``addons``.
|
The attribute ``price_included`` has been added to ``addons``.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.16
|
||||||
|
|
||||||
|
The ``internal_name`` and ``original_price`` fields have been added.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.0
|
||||||
|
|
||||||
|
The field ``require_approval`` has been added.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.3
|
||||||
|
|
||||||
|
The ``sales_channels`` attribute has been added.
|
||||||
|
|
||||||
Notes
|
Notes
|
||||||
-----
|
-----
|
||||||
Please note that an item either always has variations or never has. Once created with variations the item can never
|
Please note that an item either always has variations or never has. Once created with variations the item can never
|
||||||
@@ -127,7 +152,10 @@ Endpoints
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": {"en": "Standard ticket"},
|
"name": {"en": "Standard ticket"},
|
||||||
|
"internal_name": "",
|
||||||
|
"sales_channels": ["web"],
|
||||||
"default_price": "23.00",
|
"default_price": "23.00",
|
||||||
|
"original_price": null,
|
||||||
"category": null,
|
"category": null,
|
||||||
"active": true,
|
"active": true,
|
||||||
"description": null,
|
"description": null,
|
||||||
@@ -146,6 +174,7 @@ Endpoints
|
|||||||
"max_per_order": null,
|
"max_per_order": null,
|
||||||
"checkin_attention": false,
|
"checkin_attention": false,
|
||||||
"has_variations": false,
|
"has_variations": false,
|
||||||
|
"require_approval": false,
|
||||||
"variations": [
|
"variations": [
|
||||||
{
|
{
|
||||||
"value": {"en": "Student"},
|
"value": {"en": "Student"},
|
||||||
@@ -209,7 +238,10 @@ Endpoints
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": {"en": "Standard ticket"},
|
"name": {"en": "Standard ticket"},
|
||||||
|
"internal_name": "",
|
||||||
|
"sales_channels": ["web"],
|
||||||
"default_price": "23.00",
|
"default_price": "23.00",
|
||||||
|
"original_price": null,
|
||||||
"category": null,
|
"category": null,
|
||||||
"active": true,
|
"active": true,
|
||||||
"description": null,
|
"description": null,
|
||||||
@@ -228,6 +260,7 @@ Endpoints
|
|||||||
"max_per_order": null,
|
"max_per_order": null,
|
||||||
"checkin_attention": false,
|
"checkin_attention": false,
|
||||||
"has_variations": false,
|
"has_variations": false,
|
||||||
|
"require_approval": false,
|
||||||
"variations": [
|
"variations": [
|
||||||
{
|
{
|
||||||
"value": {"en": "Student"},
|
"value": {"en": "Student"},
|
||||||
@@ -256,7 +289,7 @@ Endpoints
|
|||||||
:statuscode 401: Authentication failure
|
:statuscode 401: Authentication failure
|
||||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||||
|
|
||||||
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/items/(item)/
|
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/items/
|
||||||
|
|
||||||
Creates a new item
|
Creates a new item
|
||||||
|
|
||||||
@@ -272,7 +305,10 @@ Endpoints
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": {"en": "Standard ticket"},
|
"name": {"en": "Standard ticket"},
|
||||||
|
"internal_name": "",
|
||||||
|
"sales_channels": ["web"],
|
||||||
"default_price": "23.00",
|
"default_price": "23.00",
|
||||||
|
"original_price": null,
|
||||||
"category": null,
|
"category": null,
|
||||||
"active": true,
|
"active": true,
|
||||||
"description": null,
|
"description": null,
|
||||||
@@ -290,6 +326,7 @@ Endpoints
|
|||||||
"min_per_order": null,
|
"min_per_order": null,
|
||||||
"max_per_order": null,
|
"max_per_order": null,
|
||||||
"checkin_attention": false,
|
"checkin_attention": false,
|
||||||
|
"require_approval": false,
|
||||||
"variations": [
|
"variations": [
|
||||||
{
|
{
|
||||||
"value": {"en": "Student"},
|
"value": {"en": "Student"},
|
||||||
@@ -315,14 +352,17 @@ Endpoints
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 201 Created
|
||||||
Vary: Accept
|
Vary: Accept
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": {"en": "Standard ticket"},
|
"name": {"en": "Standard ticket"},
|
||||||
|
"internal_name": "",
|
||||||
|
"sales_channels": ["web"],
|
||||||
"default_price": "23.00",
|
"default_price": "23.00",
|
||||||
|
"original_price": null,
|
||||||
"category": null,
|
"category": null,
|
||||||
"active": true,
|
"active": true,
|
||||||
"description": null,
|
"description": null,
|
||||||
@@ -341,6 +381,7 @@ Endpoints
|
|||||||
"max_per_order": null,
|
"max_per_order": null,
|
||||||
"checkin_attention": false,
|
"checkin_attention": false,
|
||||||
"has_variations": true,
|
"has_variations": true,
|
||||||
|
"require_approval": false,
|
||||||
"variations": [
|
"variations": [
|
||||||
{
|
{
|
||||||
"value": {"en": "Student"},
|
"value": {"en": "Student"},
|
||||||
@@ -369,7 +410,7 @@ Endpoints
|
|||||||
:statuscode 401: Authentication failure
|
:statuscode 401: Authentication failure
|
||||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
|
||||||
|
|
||||||
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/items/(item)/
|
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/items/(id)/
|
||||||
|
|
||||||
Update an item. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
|
Update an item. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
|
||||||
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
|
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
|
||||||
@@ -404,7 +445,10 @@ Endpoints
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": {"en": "Ticket"},
|
"name": {"en": "Ticket"},
|
||||||
|
"internal_name": "",
|
||||||
|
"sales_channels": ["web"],
|
||||||
"default_price": "25.00",
|
"default_price": "25.00",
|
||||||
|
"original_price": null,
|
||||||
"category": null,
|
"category": null,
|
||||||
"active": true,
|
"active": true,
|
||||||
"description": null,
|
"description": null,
|
||||||
@@ -423,6 +467,7 @@ Endpoints
|
|||||||
"max_per_order": null,
|
"max_per_order": null,
|
||||||
"checkin_attention": false,
|
"checkin_attention": false,
|
||||||
"has_variations": true,
|
"has_variations": true,
|
||||||
|
"require_approval": false,
|
||||||
"variations": [
|
"variations": [
|
||||||
{
|
{
|
||||||
"value": {"en": "Student"},
|
"value": {"en": "Student"},
|
||||||
|
|||||||
233
doc/api/resources/question_options.rst
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
Question options
|
||||||
|
================
|
||||||
|
|
||||||
|
Resource description
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Questions of type "choice" or "multiple choice" can have different options attached.
|
||||||
|
The options resource contains the following public fields:
|
||||||
|
|
||||||
|
.. rst-class:: rest-resource-table
|
||||||
|
|
||||||
|
===================================== ========================== =======================================================
|
||||||
|
Field Type Description
|
||||||
|
===================================== ========================== =======================================================
|
||||||
|
id integer Internal ID of the option
|
||||||
|
position integer An integer, used for sorting
|
||||||
|
identifier string An arbitrary string that can be used for matching with
|
||||||
|
other sources.
|
||||||
|
answer multi-lingual string The displayed value of this option
|
||||||
|
===================================== ========================== =======================================================
|
||||||
|
|
||||||
|
.. versionchanged:: 1.12
|
||||||
|
|
||||||
|
This resource has been added.
|
||||||
|
|
||||||
|
Endpoints
|
||||||
|
---------
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/questions/(question)/options/
|
||||||
|
|
||||||
|
Returns a list of all options for a given question.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/organizers/bigevents/events/sampleconf/questions/11/options/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 2,
|
||||||
|
"next": null,
|
||||||
|
"previous": null,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"identifier": "LVETRWVU",
|
||||||
|
"position": 1,
|
||||||
|
"answer": {"en": "S"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"identifier": "DFEMJWMJ",
|
||||||
|
"position": 2,
|
||||||
|
"answer": {"en": "M"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"identifier": "W9AH7RDE",
|
||||||
|
"position": 3,
|
||||||
|
"answer": {"en": "L"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
:query integer page: The page number in case of a multi-page result set, default is 1
|
||||||
|
:query boolean active: If set to ``true`` or ``false``, only questions with this value for the field ``active`` will be
|
||||||
|
returned.
|
||||||
|
:param organizer: The ``slug`` field of the organizer to fetch
|
||||||
|
:param event: The ``slug`` field of the event to fetch
|
||||||
|
:param question: The ``id`` field of the question to fetch
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event/question does not exist **or** you have no permission to view this resource.
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/questions/(question)/options/(id)/
|
||||||
|
|
||||||
|
Returns information on one option, identified by its ID.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/organizers/bigevents/events/sampleconf/questions/1/options/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"identifier": "LVETRWVU",
|
||||||
|
"position": 1,
|
||||||
|
"answer": {"en": "S"}
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to fetch
|
||||||
|
:param event: The ``slug`` field of the event to fetch
|
||||||
|
:param question: The ``id`` field of the question to fetch
|
||||||
|
:param id: The ``id`` field of the option to fetch
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||||
|
|
||||||
|
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/questions/(question)/options/
|
||||||
|
|
||||||
|
Creates a new option
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/organizers/bigevents/events/sampleconf/questions/1/options/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"identifier": "LVETRWVU",
|
||||||
|
"position": 1,
|
||||||
|
"answer": {"en": "S"}
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 201 Created
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"identifier": "LVETRWVU",
|
||||||
|
"position": 1,
|
||||||
|
"answer": {"en": "S"}
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer of the event/question to create a option for
|
||||||
|
:param event: The ``slug`` field of the event to create a option for
|
||||||
|
:param question: The ``id`` field of the question to create a option for
|
||||||
|
:statuscode 201: no error
|
||||||
|
:statuscode 400: The option could not be created due to invalid submitted data.
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
|
||||||
|
|
||||||
|
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/questions/(question)/options/(id)/
|
||||||
|
|
||||||
|
Update an option. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
|
||||||
|
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
|
||||||
|
want to change.
|
||||||
|
|
||||||
|
You can change all fields of the resource except the ``id`` field.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
PATCH /api/v1/organizers/bigevents/events/sampleconf/questions/1/options/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content-Type: application/json
|
||||||
|
Content-Length: 94
|
||||||
|
|
||||||
|
{
|
||||||
|
"position": 3
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"identifier": "LVETRWVU",
|
||||||
|
"position": 1,
|
||||||
|
"answer": {"en": "S"}
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to modify
|
||||||
|
:param event: The ``slug`` field of the event to modify
|
||||||
|
:param id: The ``id`` field of the question to modify
|
||||||
|
:param id: The ``id`` field of the option to modify
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 400: The option could not be modified due to invalid submitted data
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
|
||||||
|
|
||||||
|
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/questions/(id)/options/(id)/
|
||||||
|
|
||||||
|
Delete an option.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
DELETE /api/v1/organizers/bigevents/events/sampleconf/questions/1/options/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 204 No Content
|
||||||
|
Vary: Accept
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to modify
|
||||||
|
:param event: The ``slug`` field of the event to modify
|
||||||
|
:param id: The ``id`` field of the question to modify
|
||||||
|
:param id: The ``id`` field of the option to delete
|
||||||
|
:statuscode 204: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
.. spelling:: checkin
|
.. spelling:: checkin
|
||||||
|
|
||||||
|
.. _rest-questions:
|
||||||
|
|
||||||
Questions
|
Questions
|
||||||
=========
|
=========
|
||||||
|
|
||||||
@@ -31,12 +33,18 @@ type string The expected ty
|
|||||||
required boolean If ``True``, the question needs to be filled out.
|
required boolean If ``True``, the question needs to be filled out.
|
||||||
position integer An integer, used for sorting
|
position integer An integer, used for sorting
|
||||||
items list of integers List of item IDs this question is assigned to.
|
items list of integers List of item IDs this question is assigned to.
|
||||||
|
identifier string An arbitrary string that can be used for matching with
|
||||||
|
other sources.
|
||||||
ask_during_checkin boolean If ``True``, this question will not be asked while
|
ask_during_checkin boolean If ``True``, this question will not be asked while
|
||||||
buying the ticket, but will show up when redeeming
|
buying the ticket, but will show up when redeeming
|
||||||
the ticket instead.
|
the ticket instead.
|
||||||
options list of objects In case of question type ``C`` or ``M``, this lists the
|
options list of objects In case of question type ``C`` or ``M``, this lists the
|
||||||
available objects.
|
available objects. Only writable during creation,
|
||||||
|
use separate endpoint to modify this later.
|
||||||
├ id integer Internal ID of the option
|
├ id integer Internal ID of the option
|
||||||
|
├ position integer An integer, used for sorting
|
||||||
|
├ identifier string An arbitrary string that can be used for matching with
|
||||||
|
other sources.
|
||||||
└ answer multi-lingual string The displayed value of this option
|
└ answer multi-lingual string The displayed value of this option
|
||||||
===================================== ========================== =======================================================
|
===================================== ========================== =======================================================
|
||||||
|
|
||||||
@@ -45,9 +53,19 @@ options list of objects In case of ques
|
|||||||
The values ``D``, ``H``, and ``W`` for the field ``type`` are now allowed and the ``ask_during_checkin`` field has
|
The values ``D``, ``H``, and ``W`` for the field ``type`` are now allowed and the ``ask_during_checkin`` field has
|
||||||
been added.
|
been added.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.14
|
||||||
|
|
||||||
|
Write methods have been added. The attribute ``identifier`` has been added to both the resource itself and the
|
||||||
|
options resource. The ``position`` attribute has been added to the options resource.
|
||||||
|
|
||||||
Endpoints
|
Endpoints
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
.. versionchanged:: 1.15
|
||||||
|
|
||||||
|
The questions endpoint has been extended by the filter queries ``ask_during_checkin``, ``requred``, and
|
||||||
|
``identifier``.
|
||||||
|
|
||||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/questions/
|
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/questions/
|
||||||
|
|
||||||
Returns a list of all questions within a given event.
|
Returns a list of all questions within a given event.
|
||||||
@@ -80,18 +98,25 @@ Endpoints
|
|||||||
"required": false,
|
"required": false,
|
||||||
"items": [1, 2],
|
"items": [1, 2],
|
||||||
"position": 1,
|
"position": 1,
|
||||||
|
"identifier": "WY3TP9SL",
|
||||||
"ask_during_checkin": false,
|
"ask_during_checkin": false,
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
|
"identifier": "LVETRWVU",
|
||||||
|
"position": 0,
|
||||||
"answer": {"en": "S"}
|
"answer": {"en": "S"}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
|
"identifier": "DFEMJWMJ",
|
||||||
|
"position": 1,
|
||||||
"answer": {"en": "M"}
|
"answer": {"en": "M"}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
|
"identifier": "W9AH7RDE",
|
||||||
|
"position": 2,
|
||||||
"answer": {"en": "L"}
|
"answer": {"en": "L"}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -102,6 +127,9 @@ Endpoints
|
|||||||
:query integer page: The page number in case of a multi-page result set, default is 1
|
:query integer page: The page number in case of a multi-page result set, default is 1
|
||||||
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``id`` and ``position``.
|
:query string ordering: Manually set the ordering of results. Valid fields to be used are ``id`` and ``position``.
|
||||||
Default: ``position``
|
Default: ``position``
|
||||||
|
:query string identifier: Only return questions with the given identifier string
|
||||||
|
:query boolean ask_during_checkin: Only return questions that are or are not to be asked during check-in
|
||||||
|
:query boolean required: Only return questions that are or are not required to fill in
|
||||||
:param organizer: The ``slug`` field of the organizer to fetch
|
:param organizer: The ``slug`` field of the organizer to fetch
|
||||||
:param event: The ``slug`` field of the event to fetch
|
:param event: The ``slug`` field of the event to fetch
|
||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
@@ -134,19 +162,26 @@ Endpoints
|
|||||||
"type": "C",
|
"type": "C",
|
||||||
"required": false,
|
"required": false,
|
||||||
"items": [1, 2],
|
"items": [1, 2],
|
||||||
"ask_during_checkin": false,
|
|
||||||
"position": 1,
|
"position": 1,
|
||||||
|
"identifier": "WY3TP9SL",
|
||||||
|
"ask_during_checkin": false,
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
|
"identifier": "LVETRWVU",
|
||||||
|
"position": 1,
|
||||||
"answer": {"en": "S"}
|
"answer": {"en": "S"}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
|
"identifier": "DFEMJWMJ",
|
||||||
|
"position": 2,
|
||||||
"answer": {"en": "M"}
|
"answer": {"en": "M"}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
|
"identifier": "W9AH7RDE",
|
||||||
|
"position": 3,
|
||||||
"answer": {"en": "L"}
|
"answer": {"en": "L"}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -158,3 +193,179 @@ Endpoints
|
|||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
:statuscode 401: Authentication failure
|
:statuscode 401: Authentication failure
|
||||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||||
|
|
||||||
|
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/questions/
|
||||||
|
|
||||||
|
Creates a new question
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/organizers/bigevents/events/sampleconf/questions/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"question": {"en": "T-Shirt size"},
|
||||||
|
"type": "C",
|
||||||
|
"required": false,
|
||||||
|
"items": [1, 2],
|
||||||
|
"position": 1,
|
||||||
|
"ask_during_checkin": false,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"answer": {"en": "S"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": {"en": "M"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": {"en": "L"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 201 Created
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"question": {"en": "T-Shirt size"},
|
||||||
|
"type": "C",
|
||||||
|
"required": false,
|
||||||
|
"items": [1, 2],
|
||||||
|
"position": 1,
|
||||||
|
"identifier": "WY3TP9SL",
|
||||||
|
"ask_during_checkin": false,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"identifier": "LVETRWVU",
|
||||||
|
"position": 1,
|
||||||
|
"answer": {"en": "S"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"identifier": "DFEMJWMJ",
|
||||||
|
"position": 2,
|
||||||
|
"answer": {"en": "M"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"identifier": "W9AH7RDE",
|
||||||
|
"position": 3,
|
||||||
|
"answer": {"en": "L"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer of the event to create an item for
|
||||||
|
:param event: The ``slug`` field of the event to create an item for
|
||||||
|
:statuscode 201: no error
|
||||||
|
:statuscode 400: The item could not be created due to invalid submitted data.
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
|
||||||
|
|
||||||
|
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/questions/(id)/
|
||||||
|
|
||||||
|
Update a question. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
|
||||||
|
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
|
||||||
|
want to change.
|
||||||
|
|
||||||
|
You can change all fields of the resource except the ``options`` field. If
|
||||||
|
you need to update/delete options please use the nested dedicated endpoints.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
PATCH /api/v1/organizers/bigevents/events/sampleconf/items/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content-Type: application/json
|
||||||
|
Content-Length: 94
|
||||||
|
|
||||||
|
{
|
||||||
|
"position": 2
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"question": {"en": "T-Shirt size"},
|
||||||
|
"type": "C",
|
||||||
|
"required": false,
|
||||||
|
"items": [1, 2],
|
||||||
|
"position": 2,
|
||||||
|
"identifier": "WY3TP9SL",
|
||||||
|
"ask_during_checkin": false,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"identifier": "LVETRWVU",
|
||||||
|
"position": 1,
|
||||||
|
"answer": {"en": "S"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"identifier": "DFEMJWMJ",
|
||||||
|
"position": 2,
|
||||||
|
"answer": {"en": "M"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"identifier": "W9AH7RDE",
|
||||||
|
"position": 3,
|
||||||
|
"answer": {"en": "L"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to modify
|
||||||
|
:param event: The ``slug`` field of the event to modify
|
||||||
|
:param id: The ``id`` field of the question to modify
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 400: The item could not be modified due to invalid submitted data
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
|
||||||
|
|
||||||
|
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/questions/(id)/
|
||||||
|
|
||||||
|
Delete a question.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
DELETE /api/v1/organizers/bigevents/events/sampleconf/items/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 204 No Content
|
||||||
|
Vary: Accept
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to modify
|
||||||
|
:param event: The ``slug`` field of the event to modify
|
||||||
|
:param id: The ``id`` field of the item to delete
|
||||||
|
:statuscode 204: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
.. _rest-quotas:
|
||||||
|
|
||||||
Quotas
|
Quotas
|
||||||
======
|
======
|
||||||
|
|
||||||
@@ -135,7 +137,7 @@ Endpoints
|
|||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 201 Created
|
||||||
Vary: Accept
|
Vary: Accept
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
.. _rest-subevents:
|
||||||
|
|
||||||
Event series dates / Sub-events
|
Event series dates / Sub-events
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
@@ -15,6 +17,7 @@ Field Type Description
|
|||||||
===================================== ========================== =======================================================
|
===================================== ========================== =======================================================
|
||||||
id integer Internal ID of the sub-event
|
id integer Internal ID of the sub-event
|
||||||
name multi-lingual string The sub-event's full name
|
name multi-lingual string The sub-event's full name
|
||||||
|
event string The slug of the parent event
|
||||||
active boolean If ``true``, the sub-event ticket shop is publicly
|
active boolean If ``true``, the sub-event ticket shop is publicly
|
||||||
available.
|
available.
|
||||||
date_from datetime The sub-event's start date
|
date_from datetime The sub-event's start date
|
||||||
@@ -38,6 +41,10 @@ meta_data dict Values set for
|
|||||||
|
|
||||||
The ``meta_data`` field has been added.
|
The ``meta_data`` field has been added.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.1
|
||||||
|
|
||||||
|
The ``event`` field has been added, together with filters on the list of dates and an organizer-level list.
|
||||||
|
|
||||||
|
|
||||||
Endpoints
|
Endpoints
|
||||||
---------
|
---------
|
||||||
@@ -70,6 +77,7 @@ Endpoints
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": {"en": "First Sample Conference"},
|
"name": {"en": "First Sample Conference"},
|
||||||
|
"event": "sampleconf",
|
||||||
"active": false,
|
"active": false,
|
||||||
"date_from": "2017-12-27T10:00:00Z",
|
"date_from": "2017-12-27T10:00:00Z",
|
||||||
"date_to": null,
|
"date_to": null,
|
||||||
@@ -90,6 +98,10 @@ Endpoints
|
|||||||
}
|
}
|
||||||
|
|
||||||
:query page: The page number in case of a multi-page result set, default is 1
|
:query page: The page number in case of a multi-page result set, default is 1
|
||||||
|
:query active: If set to ``true``/``false``, only events with a matching value of ``active`` are returned.
|
||||||
|
:query is_future: If set to ``true`` (``false``), only events that happen currently or in the future are (not) returned.
|
||||||
|
:query is_past: If set to ``true`` (``false``), only events that are over are (not) returned.
|
||||||
|
:query ends_after: If set to a date and time, only events that happen during of after the given time are returned.
|
||||||
:param organizer: The ``slug`` field of a valid organizer
|
:param organizer: The ``slug`` field of a valid organizer
|
||||||
:param event: The ``slug`` field of the event to fetch
|
:param event: The ``slug`` field of the event to fetch
|
||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
@@ -119,6 +131,7 @@ Endpoints
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": {"en": "First Sample Conference"},
|
"name": {"en": "First Sample Conference"},
|
||||||
|
"event": "sampleconf",
|
||||||
"active": false,
|
"active": false,
|
||||||
"date_from": "2017-12-27T10:00:00Z",
|
"date_from": "2017-12-27T10:00:00Z",
|
||||||
"date_to": null,
|
"date_to": null,
|
||||||
@@ -142,3 +155,63 @@ Endpoints
|
|||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
:statuscode 401: Authentication failure
|
:statuscode 401: Authentication failure
|
||||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view it.
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view it.
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/organizers/(organizer)/subevents/
|
||||||
|
|
||||||
|
Returns a list of all sub-events of any event series you have access to within an organizer account.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/organizers/bigevents/subevents/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 1,
|
||||||
|
"next": null,
|
||||||
|
"previous": null,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": {"en": "First Sample Conference"},
|
||||||
|
"event": "sampleconf",
|
||||||
|
"active": false,
|
||||||
|
"date_from": "2017-12-27T10:00:00Z",
|
||||||
|
"date_to": null,
|
||||||
|
"date_admission": null,
|
||||||
|
"presale_start": null,
|
||||||
|
"presale_end": null,
|
||||||
|
"location": null,
|
||||||
|
"item_price_overrides": [
|
||||||
|
{
|
||||||
|
"item": 2,
|
||||||
|
"price": "12.00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"variation_price_overrides": [],
|
||||||
|
"meta_data": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
:query page: The page number in case of a multi-page result set, default is 1
|
||||||
|
:query active: If set to ``true``/``false``, only events with a matching value of ``active`` are returned.
|
||||||
|
:query event__live: If set to ``true``/``false``, only events with a matching value of ``live`` on the parent event are returned.
|
||||||
|
:query is_future: If set to ``true`` (``false``), only events that happen currently or in the future are (not) returned.
|
||||||
|
:query is_past: If set to ``true`` (``false``), only events that are over are (not) returned.
|
||||||
|
:query ends_after: If set to a date and time, only events that happen during of after the given time are returned.
|
||||||
|
:param organizer: The ``slug`` field of a valid organizer
|
||||||
|
:param event: The ``slug`` field of the event to fetch
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer does not exist **or** you have no permission to view it.
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
|
.. _rest-taxrules:
|
||||||
|
|
||||||
Tax rules
|
Tax rules
|
||||||
=========
|
=========
|
||||||
|
|
||||||
Resource description
|
Resource description
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
Tax rules specify how tax should be calculated for specific products.
|
Tax rules specify how tax should be calculated for specific products. Custom taxation rule sets are currently to
|
||||||
|
available via the API.
|
||||||
|
|
||||||
.. rst-class:: rest-resource-table
|
.. rst-class:: rest-resource-table
|
||||||
|
|
||||||
|
|||||||
@@ -231,6 +231,76 @@ Endpoints
|
|||||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
|
||||||
:statuscode 409: The server was unable to acquire a lock and could not process your request. You can try again after a short waiting period.
|
:statuscode 409: The server was unable to acquire a lock and could not process your request. You can try again after a short waiting period.
|
||||||
|
|
||||||
|
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/vouchers/batch_create/
|
||||||
|
|
||||||
|
Creates multiple new vouchers atomically.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/organizers/bigevents/events/sampleconf/vouchers/batch_create/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content-Type: application/json
|
||||||
|
Content-Length: 408
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"code": "43K6LKM37FBVR2YG",
|
||||||
|
"max_usages": 1,
|
||||||
|
"valid_until": null,
|
||||||
|
"block_quota": false,
|
||||||
|
"allow_ignore_quota": false,
|
||||||
|
"price_mode": "set",
|
||||||
|
"value": "12.00",
|
||||||
|
"item": 1,
|
||||||
|
"variation": null,
|
||||||
|
"quota": null,
|
||||||
|
"tag": "testvoucher",
|
||||||
|
"comment": "",
|
||||||
|
"subevent": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "ASDKLJCYXCASDASD",
|
||||||
|
"max_usages": 1,
|
||||||
|
"valid_until": null,
|
||||||
|
"block_quota": false,
|
||||||
|
"allow_ignore_quota": false,
|
||||||
|
"price_mode": "set",
|
||||||
|
"value": "12.00",
|
||||||
|
"item": 1,
|
||||||
|
"variation": null,
|
||||||
|
"quota": null,
|
||||||
|
"tag": "testvoucher",
|
||||||
|
"comment": "",
|
||||||
|
"subevent": null
|
||||||
|
},
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 201 Created
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"code": "43K6LKM37FBVR2YG",
|
||||||
|
…
|
||||||
|
}, …
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to create a vouchers for
|
||||||
|
:param event: The ``slug`` field of the event to create a vouchers for
|
||||||
|
:statuscode 201: no error
|
||||||
|
:statuscode 400: The vouchers could not be created due to invalid submitted data.
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this resource.
|
||||||
|
:statuscode 409: The server was unable to acquire a lock and could not process your request. You can try again after a short waiting period.
|
||||||
|
|
||||||
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/vouchers/(id)/
|
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/vouchers/(id)/
|
||||||
|
|
||||||
Update a voucher. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
|
Update a voucher. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
|
||||||
@@ -251,7 +321,7 @@ Endpoints
|
|||||||
|
|
||||||
{
|
{
|
||||||
"price_mode": "set",
|
"price_mode": "set",
|
||||||
"value": "24.00",
|
"value": "24.00"
|
||||||
}
|
}
|
||||||
|
|
||||||
**Example response**:
|
**Example response**:
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ subevent integer ID of the date
|
|||||||
===================================== ========================== =======================================================
|
===================================== ========================== =======================================================
|
||||||
|
|
||||||
|
|
||||||
|
.. versionchanged:: 1.15
|
||||||
|
|
||||||
|
The write operations ``POST``, ``PATCH``, ``PUT``, and ``DELETE`` have been added as well as a method to send out
|
||||||
|
vouchers.
|
||||||
|
|
||||||
|
|
||||||
Endpoints
|
Endpoints
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@@ -121,3 +127,161 @@ Endpoints
|
|||||||
:statuscode 200: no error
|
:statuscode 200: no error
|
||||||
:statuscode 401: Authentication failure
|
:statuscode 401: Authentication failure
|
||||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||||
|
|
||||||
|
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/waitinglistentries/
|
||||||
|
|
||||||
|
Create a new entry.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/organizers/bigevents/events/sampleconf/waitinglistentries/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content-Type: application/json
|
||||||
|
Content-Length: 408
|
||||||
|
|
||||||
|
{
|
||||||
|
"email": "waiting@example.org",
|
||||||
|
"item": 3,
|
||||||
|
"variation": null,
|
||||||
|
"locale": "de",
|
||||||
|
"subevent": null
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 201 Created
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"created": "2017-12-01T10:00:00Z",
|
||||||
|
"email": "waiting@example.org",
|
||||||
|
"voucher": null,
|
||||||
|
"item": 3,
|
||||||
|
"variation": null,
|
||||||
|
"locale": "de",
|
||||||
|
"subevent": null
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to create an entry for
|
||||||
|
:param event: The ``slug`` field of the event to create an entry for
|
||||||
|
:statuscode 201: no error
|
||||||
|
:statuscode 400: The voucher could not be created due to invalid submitted data.
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this
|
||||||
|
resource **or** entries cannot be created for this item at this time.
|
||||||
|
|
||||||
|
.. http:patch:: /api/v1/organizers/(organizer)/events/(event)/waitinglistentries/(id)/
|
||||||
|
|
||||||
|
Update an entry. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
|
||||||
|
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
|
||||||
|
want to change.
|
||||||
|
|
||||||
|
You can change all fields of the resource except the ``id``, ``voucher`` and ``created`` fields. You can only change
|
||||||
|
an entry as long as no ``voucher`` is set.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
PATCH /api/v1/organizers/bigevents/events/sampleconf/waitinglistentries/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content-Type: application/json
|
||||||
|
Content-Length: 408
|
||||||
|
|
||||||
|
{
|
||||||
|
"item": 4
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"created": "2017-12-01T10:00:00Z",
|
||||||
|
"email": "waiting@example.org",
|
||||||
|
"voucher": null,
|
||||||
|
"item": 4,
|
||||||
|
"variation": null,
|
||||||
|
"locale": "de",
|
||||||
|
"subevent": null
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to modify
|
||||||
|
:param event: The ``slug`` field of the event to modify
|
||||||
|
:param id: The ``id`` field of the entry to modify
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 400: The entry could not be modified due to invalid submitted data
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to create this
|
||||||
|
resource **or** entries cannot be created for this item at this time **or** this entry already
|
||||||
|
has a voucher assigned
|
||||||
|
|
||||||
|
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/waitinglistentries/(id)/send_voucher/
|
||||||
|
|
||||||
|
Manually sends a voucher to someone on the waiting list
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/organizers/bigevents/events/sampleconf/waitinglistentries/1/send_voucher/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content-Type: application/json
|
||||||
|
Content-Length: 0
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 204 No Content
|
||||||
|
Vary: Accept
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to modify
|
||||||
|
:param event: The ``slug`` field of the event to modify
|
||||||
|
:param id: The ``id`` field of the entry to modify
|
||||||
|
:statuscode 204: no error
|
||||||
|
:statuscode 400: The voucher could not be sent out, see body for details (e.g. voucher has already been sent or
|
||||||
|
item is not available).
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to do this
|
||||||
|
|
||||||
|
.. http:delete:: /api/v1/organizers/(organizer)/events/(event)/waitinglistentries/(id)/
|
||||||
|
|
||||||
|
Delete an entry. Note that you cannot delete an entry once it is assigned a voucher.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
DELETE /api/v1/organizers/bigevents/events/sampleconf/waitinglistentries/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 204 No Content
|
||||||
|
Vary: Accept
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to modify
|
||||||
|
:param event: The ``slug`` field of the event to modify
|
||||||
|
:param id: The ``id`` field of the entry to delete
|
||||||
|
:statuscode 204: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this
|
||||||
|
resource **or** this entry already has a voucher assigned.
|
||||||
|
|||||||
243
doc/api/resources/webhooks.rst
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
.. _`rest-webhooks`:
|
||||||
|
|
||||||
|
Webhooks
|
||||||
|
========
|
||||||
|
|
||||||
|
.. note:: This page is about how to modify webhook settings themselves through the REST API. If you just want to know
|
||||||
|
how webhooks work, go here: :ref:`webhooks`
|
||||||
|
|
||||||
|
Resource description
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The webhook resource contains the following public fields:
|
||||||
|
|
||||||
|
.. rst-class:: rest-resource-table
|
||||||
|
|
||||||
|
===================================== ========================== =======================================================
|
||||||
|
Field Type Description
|
||||||
|
===================================== ========================== =======================================================
|
||||||
|
id integer Internal ID of the webhook
|
||||||
|
enabled boolean If ``False``, this webhook will not receive any notifications
|
||||||
|
target_url string The URL to call
|
||||||
|
all_events boolean If ``True``, this webhook will receive notifications
|
||||||
|
on all events of this organizer
|
||||||
|
limit_events list of strings If ``all_events`` is ``False``, this is a list of
|
||||||
|
event slugs this webhook is active for
|
||||||
|
action_types list of strings A list of action type filters that limit the
|
||||||
|
notifications sent to this webhook. See below for
|
||||||
|
valid values
|
||||||
|
===================================== ========================== =======================================================
|
||||||
|
|
||||||
|
The following values for ``action_types`` are valid with pretix core:
|
||||||
|
|
||||||
|
* ``pretix.event.order.placed``
|
||||||
|
* ``pretix.event.order.paid``
|
||||||
|
* ``pretix.event.order.canceled``
|
||||||
|
* ``pretix.event.order.expired``
|
||||||
|
* ``pretix.event.order.modified``
|
||||||
|
* ``pretix.event.order.contact.changed``
|
||||||
|
* ``pretix.event.order.changed.*``
|
||||||
|
* ``pretix.event.order.refund.created.externally``
|
||||||
|
* ``pretix.event.order.refunded``
|
||||||
|
* ``pretix.event.order.approved``
|
||||||
|
* ``pretix.event.order.denied``
|
||||||
|
* ``pretix.event.checkin``
|
||||||
|
* ``pretix.event.checkin.reverted``
|
||||||
|
|
||||||
|
Installed plugins might register more valid values.
|
||||||
|
|
||||||
|
|
||||||
|
Endpoints
|
||||||
|
---------
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/organizers/(organizer)/webhooks/
|
||||||
|
|
||||||
|
Returns a list of all webhooks within a given organizer.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/organizers/bigevents/webhooks/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 1,
|
||||||
|
"next": null,
|
||||||
|
"previous": null,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"enabled": true,
|
||||||
|
"target_url": "https://httpstat.us/200",
|
||||||
|
"all_events": false,
|
||||||
|
"limit_events": ["democon"],
|
||||||
|
"action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
:query integer page: The page number in case of a multi-page result set, default is 1
|
||||||
|
:param organizer: The ``slug`` field of the organizer to fetch
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/organizers/(organizer)/webhooks/(id)/
|
||||||
|
|
||||||
|
Returns information on one webhook, identified by its ID.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/organizers/bigevents/webhooks/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"enabled": true,
|
||||||
|
"target_url": "https://httpstat.us/200",
|
||||||
|
"all_events": false,
|
||||||
|
"limit_events": ["democon"],
|
||||||
|
"action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"]
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to fetch
|
||||||
|
:param id: The ``id`` field of the webhook to fetch
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource.
|
||||||
|
|
||||||
|
.. http:post:: /api/v1/organizers/(organizer)/webhooks/
|
||||||
|
|
||||||
|
Creates a new webhook
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
POST /api/v1/organizers/bigevents/webhooks/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"enabled": true,
|
||||||
|
"target_url": "https://httpstat.us/200",
|
||||||
|
"all_events": false,
|
||||||
|
"limit_events": ["democon"],
|
||||||
|
"action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"]
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 201 Created
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"enabled": true,
|
||||||
|
"target_url": "https://httpstat.us/200",
|
||||||
|
"all_events": false,
|
||||||
|
"limit_events": ["democon"],
|
||||||
|
"action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"]
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to create a webhook for
|
||||||
|
:statuscode 201: no error
|
||||||
|
:statuscode 400: The webhook could not be created due to invalid submitted data.
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer does not exist **or** you have no permission to create this resource.
|
||||||
|
|
||||||
|
.. http:patch:: /api/v1/organizers/(organizer)/webhooks/(id)/
|
||||||
|
|
||||||
|
Update a webhook. You can also use ``PUT`` instead of ``PATCH``. With ``PUT``, you have to provide all fields of
|
||||||
|
the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you
|
||||||
|
want to change.
|
||||||
|
|
||||||
|
You can change all fields of the resource except the ``id`` field.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
PATCH /api/v1/organizers/bigevents/webhooks/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
Content-Type: application/json
|
||||||
|
Content-Length: 94
|
||||||
|
|
||||||
|
{
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"enabled": false,
|
||||||
|
"target_url": "https://httpstat.us/200",
|
||||||
|
"all_events": false,
|
||||||
|
"limit_events": ["democon"],
|
||||||
|
"action_types": ["pretix.event.order.modified", "pretix.event.order.changed.*"]
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to modify
|
||||||
|
:param id: The ``id`` field of the webhook to modify
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 400: The webhook could not be modified due to invalid submitted data
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer does not exist **or** you have no permission to change this resource.
|
||||||
|
|
||||||
|
.. http:delete:: /api/v1/organizers/(organizer)/webhook/(id)/
|
||||||
|
|
||||||
|
Delete a webhook. Currently, this will not delete but just disable the webhook.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
DELETE /api/v1/organizers/bigevents/webhooks/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 204 No Content
|
||||||
|
Vary: Accept
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to modify
|
||||||
|
:param id: The ``id`` field of the webhook to delete
|
||||||
|
:statuscode 204: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer does not exist **or** you have no permission to delete this resource.
|
||||||
36
doc/api/tokenauth.rst
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
.. _`rest-tokenauth`:
|
||||||
|
|
||||||
|
Token-based authentication
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Obtaining an API token
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
To authenticate your API requests with Tokens, you need to obtain a team-level API token.
|
||||||
|
You can create a token in the pretix web interface on the level of organizer teams. Create
|
||||||
|
a new team or choose an existing team that has the level of permissions the token should
|
||||||
|
have and create a new token using the form below the list of team members:
|
||||||
|
|
||||||
|
.. image:: img/token_form.png
|
||||||
|
:class: screenshot
|
||||||
|
|
||||||
|
You can enter a description for the token to distinguish from other tokens later on.
|
||||||
|
Once you click "Add", you will be provided with an API token in the success message.
|
||||||
|
Copy this token, as you won't be able to retrieve it again.
|
||||||
|
|
||||||
|
.. image:: img/token_success.png
|
||||||
|
:class: screenshot
|
||||||
|
|
||||||
|
Using an API token
|
||||||
|
------------------
|
||||||
|
|
||||||
|
You need to include the API token with every request to pretix' API in the ``Authorization`` header
|
||||||
|
like the following:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
:emphasize-lines: 3
|
||||||
|
|
||||||
|
GET /api/v1/organizers/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Authorization: Token e1l6gq2ye72thbwkacj7jbri7a7tvxe614ojv8ybureain92ocub46t5gab5966k
|
||||||
|
|
||||||
108
doc/api/webhooks.rst
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
.. _`webhooks`:
|
||||||
|
|
||||||
|
Webhooks
|
||||||
|
========
|
||||||
|
|
||||||
|
pretix can send webhook calls to notify your application of any changes that happen inside pretix. This is especially
|
||||||
|
useful for everything triggered by an actual user, such as a new ticket sale or the arrival of a payment.
|
||||||
|
|
||||||
|
You can register any number of webhook URLs that pretix will notify any time one of the supported events occurs inside
|
||||||
|
your organizer account. A great example use case of webhooks would be to add the buyer to your mailing list every time
|
||||||
|
a new order comes in.
|
||||||
|
|
||||||
|
Configuring webhooks
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
You can find the list of your active webhooks in the "Webhook" section of your organizer account:
|
||||||
|
|
||||||
|
.. thumbnail:: ../screens/organizer/webhook_list.png
|
||||||
|
:align: center
|
||||||
|
:class: screenshot
|
||||||
|
|
||||||
|
Click "Create webhook" if you want to add a new URL. You will then be able to enter the URL pretix shall call for
|
||||||
|
notifications. You need to select any number of notification types that you want to receive and you can optionally
|
||||||
|
filter the events you want to receive notifications for.
|
||||||
|
|
||||||
|
.. thumbnail:: ../screens/organizer/webhook_edit.png
|
||||||
|
:align: center
|
||||||
|
:class: screenshot
|
||||||
|
|
||||||
|
You can also configure webhooks :ref:`through the API itself <rest-webhooks>`.
|
||||||
|
|
||||||
|
Receiving webhooks
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Creating a webhook endpoint on your server is no different from creating any other page on your website. If your
|
||||||
|
website is written in PHP, you might just create a new ``.php`` file on your server; if you use a web framework like
|
||||||
|
Symfony or Django, you would just create a new route with the desired URL.
|
||||||
|
|
||||||
|
We will call your URL with a HTTP ``POST`` request with a ``JSON`` body. In PHP, you can parse this like this::
|
||||||
|
|
||||||
|
$input = @file_get_contents('php://input');
|
||||||
|
$event_json = json_decode($input);
|
||||||
|
// Do something with $event_json
|
||||||
|
|
||||||
|
In Django, you would create a view like this::
|
||||||
|
|
||||||
|
def my_webhook_view(request):
|
||||||
|
event_json = json.loads(request.body)
|
||||||
|
# Do something with event_json
|
||||||
|
return HttpResponse(status=200)
|
||||||
|
|
||||||
|
More samples for the language of your choice are easy to find online.
|
||||||
|
|
||||||
|
The exact body of the request varies by notification type, but for the main types included with pretix core, such as
|
||||||
|
those related to changes of an order, it will look like this::
|
||||||
|
|
||||||
|
{
|
||||||
|
"notification_id": 123455,
|
||||||
|
"organizer": "acmecorp",
|
||||||
|
"event": "democon",
|
||||||
|
"code": "ABC23",
|
||||||
|
"action": "pretix.event.order.placed"
|
||||||
|
}
|
||||||
|
|
||||||
|
Notifications regarding a check-in will contain more details like ``orderposition_id``
|
||||||
|
and ``checkin_list``.
|
||||||
|
|
||||||
|
.. warning:: You should not trust data supplied to your webhook, but only use it as a trigger to fetch updated data.
|
||||||
|
Anyone could send data there if they guess the correct URL and you won't be able to tell. Therefore, we
|
||||||
|
only include the minimum amount of data necessary for you to fetch the changed objects from our
|
||||||
|
:ref:`rest-api` in an authenticated way.
|
||||||
|
|
||||||
|
If you want to further prevent others from accessing your webhook URL, you can also use `Basic authentication`_ and
|
||||||
|
supply the URL to us in the format of ``https://username:password@domain.com/path/``.
|
||||||
|
We recommend that you use HTTPS for your webhook URL and might require it in the future. If HTTPS is used, we require
|
||||||
|
that a valid certificate is in use.
|
||||||
|
|
||||||
|
.. note:: If you use a web framework that makes use of automatic CSRF protection, this protection might prevent us
|
||||||
|
from calling your webhook URL. In this case, we recommend that you turn of CSRF protection selectively
|
||||||
|
for that route. In Django, you can do this by putting the ``@csrf_exempt`` decorator on your view. In
|
||||||
|
Rails, you can pass an ``except`` parameter to ``protect_from_forgery``.
|
||||||
|
|
||||||
|
|
||||||
|
Responding to a webhook
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
If you successfully received a webhook call, your endpoint should return a HTTP status code between ``200`` and ``299``.
|
||||||
|
If any other status code is returned, we will assume you did not receive the call. This does mean that any redirection
|
||||||
|
or ``304 Not Modified`` response will be treated as a failure. pretix will not follow any ``301`` or ``302`` redirect
|
||||||
|
headers and pretix will ignore all other information in your response headers or body.
|
||||||
|
|
||||||
|
If we do not receive a status code in the range of ``200`` and ``299``, pretix will retry to deliver for up to three
|
||||||
|
days with an exponential back off. Therefore, we recommend that you implement your endpoint in a way where calling it
|
||||||
|
multiple times for the same event due to a perceived error does not do any harm.
|
||||||
|
|
||||||
|
There is only one exception: If status code ``410 Gone`` is returned, we will assume the
|
||||||
|
endpoint does not exist any more and automatically disable the webhook.
|
||||||
|
|
||||||
|
.. note:: If you use a self-hosted version of pretix (i.e. not our SaaS offering at pretix.eu) and you did not
|
||||||
|
configure a background task queue, failed webhooks will not be retried.
|
||||||
|
|
||||||
|
Debugging webhooks
|
||||||
|
------------------
|
||||||
|
|
||||||
|
If you want to debug your webhooks, you can view a log of all sent notifications and the responses of your server for
|
||||||
|
30 days right next to your configuration.
|
||||||
|
|
||||||
|
.. _Basic authentication: https://en.wikipedia.org/wiki/Basic_access_authentication
|
||||||
42
doc/conf.py
@@ -31,6 +31,13 @@ import django
|
|||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pretix.testutils.settings")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pretix.testutils.settings")
|
||||||
django.setup()
|
django.setup()
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
import enchant
|
||||||
|
HAS_PYENCHANT = True
|
||||||
|
except:
|
||||||
|
HAS_PYENCHANT = False
|
||||||
|
|
||||||
# -- General configuration ------------------------------------------------
|
# -- General configuration ------------------------------------------------
|
||||||
|
|
||||||
# If your documentation needs a minimal Sphinx version, state it here.
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
@@ -45,8 +52,9 @@ extensions = [
|
|||||||
'sphinx.ext.coverage',
|
'sphinx.ext.coverage',
|
||||||
'sphinxcontrib.httpdomain',
|
'sphinxcontrib.httpdomain',
|
||||||
'sphinxcontrib.images',
|
'sphinxcontrib.images',
|
||||||
'sphinxcontrib.spelling',
|
|
||||||
]
|
]
|
||||||
|
if HAS_PYENCHANT:
|
||||||
|
extensions.append('sphinxcontrib.spelling')
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ['_templates']
|
||||||
@@ -292,21 +300,25 @@ images_config = {
|
|||||||
'default_image_width': '250px'
|
'default_image_width': '250px'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linkcheck_ignore = [
|
||||||
|
r'http://localhost.*', r'.*yourdomain.*', r'https://en.wikipedia.org', 'https://pretix.eu/',
|
||||||
|
]
|
||||||
|
|
||||||
# -- Options for Spelling output ------------------------------------------
|
# -- Options for Spelling output ------------------------------------------
|
||||||
|
if HAS_PYENCHANT:
|
||||||
|
# String specifying the language, as understood by PyEnchant and enchant.
|
||||||
|
# Defaults to en_US for US English.
|
||||||
|
spelling_lang = 'en_US'
|
||||||
|
|
||||||
# String specifying the language, as understood by PyEnchant and enchant.
|
# String specifying a file containing a list of words known to be spelled
|
||||||
# Defaults to en_US for US English.
|
# correctly but that do not appear in the language dictionary selected by
|
||||||
spelling_lang = 'en_US'
|
# spelling_lang. The file should contain one word per line.
|
||||||
|
spelling_word_list_filename='spelling_wordlist.txt'
|
||||||
|
|
||||||
# String specifying a file containing a list of words known to be spelled
|
# Boolean controlling whether suggestions for misspelled words are printed.
|
||||||
# correctly but that do not appear in the language dictionary selected by
|
# Defaults to False.
|
||||||
# spelling_lang. The file should contain one word per line.
|
spelling_show_suggestions=True
|
||||||
spelling_word_list_filename='spelling_wordlist.txt'
|
|
||||||
|
|
||||||
# Boolean controlling whether suggestions for misspelled words are printed.
|
# List of filter classes to be added to the tokenizer that produces words to be checked.
|
||||||
# Defaults to False.
|
from checkin_filter import CheckinFilter
|
||||||
spelling_show_suggestions=True
|
spelling_filters=[CheckinFilter]
|
||||||
|
|
||||||
# List of filter classes to be added to the tokenizer that produces words to be checked.
|
|
||||||
from checkin_filter import CheckinFilter
|
|
||||||
spelling_filters=[CheckinFilter]
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ Similarly, there is ``organizer_permission_required`` and ``OrganizerPermissionR
|
|||||||
event-related views, there is also a signal that allows you to add the view to the event navigation like this::
|
event-related views, there is also a signal that allows you to add the view to the event navigation like this::
|
||||||
|
|
||||||
|
|
||||||
from django.core.urlresolvers import resolve, reverse
|
from django.urls import resolve, reverse
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from pretix.control.signals import nav_event
|
from pretix.control.signals import nav_event
|
||||||
|
|||||||
109
doc/development/api/email.rst
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
.. highlight:: python
|
||||||
|
:linenothreshold: 5
|
||||||
|
|
||||||
|
Writing an HTML e-mail renderer plugin
|
||||||
|
======================================
|
||||||
|
|
||||||
|
An email renderer class controls how the HTML part of e-mails sent by pretix is built.
|
||||||
|
The creation of such a plugin is very similar to creating an export output.
|
||||||
|
|
||||||
|
Please read :ref:`Creating a plugin <pluginsetup>` first, if you haven't already.
|
||||||
|
|
||||||
|
Output registration
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
The email HTML renderer API does not make a lot of usage from signals, however, it
|
||||||
|
does use a signal to get a list of all available email renderers. Your plugin
|
||||||
|
should listen for this signal and return the subclass of ``pretix.base.email.BaseHTMLMailRenderer``
|
||||||
|
that we'll provide in this plugin::
|
||||||
|
|
||||||
|
from django.dispatch import receiver
|
||||||
|
|
||||||
|
from pretix.base.signals import register_html_mail_renderers
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(register_html_mail_renderers, dispatch_uid="renderer_custom")
|
||||||
|
def register_mail_renderers(sender, **kwargs):
|
||||||
|
from .email import MyMailRenderer
|
||||||
|
return MyMailRenderer
|
||||||
|
|
||||||
|
|
||||||
|
The renderer class
|
||||||
|
------------------
|
||||||
|
|
||||||
|
.. class:: pretix.base.email.BaseHTMLMailRenderer
|
||||||
|
|
||||||
|
The central object of each email renderer is the subclass of ``BaseHTMLMailRenderer``.
|
||||||
|
|
||||||
|
.. py:attribute:: BaseHTMLMailRenderer.event
|
||||||
|
|
||||||
|
The default constructor sets this property to the event we are currently
|
||||||
|
working for.
|
||||||
|
|
||||||
|
.. autoattribute:: identifier
|
||||||
|
|
||||||
|
This is an abstract attribute, you **must** override this!
|
||||||
|
|
||||||
|
.. autoattribute:: verbose_name
|
||||||
|
|
||||||
|
This is an abstract attribute, you **must** override this!
|
||||||
|
|
||||||
|
.. autoattribute:: thumbnail_filename
|
||||||
|
|
||||||
|
This is an abstract attribute, you **must** override this!
|
||||||
|
|
||||||
|
.. autoattribute:: is_available
|
||||||
|
|
||||||
|
.. automethod:: render
|
||||||
|
|
||||||
|
This is an abstract method, you **must** implement this!
|
||||||
|
|
||||||
|
Helper class for template-base renderers
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
The email renderer that ships with pretix is based on Django templates to generate HTML.
|
||||||
|
In case you also want to render emails based on a template, we provided a ready-made base
|
||||||
|
class ``TemplateBasedMailRenderer`` that you can re-use to perform the following steps:
|
||||||
|
|
||||||
|
* Convert the body text and the signature to HTML using our markdown renderer
|
||||||
|
|
||||||
|
* Render the template
|
||||||
|
|
||||||
|
* Call `inlinestyler`_ to convert all ``<style>`` style sheets to inline ``style=""``
|
||||||
|
attributes for better compatibility
|
||||||
|
|
||||||
|
To use it, you just need to implement some variables::
|
||||||
|
|
||||||
|
class ClassicMailRenderer(TemplateBasedMailRenderer):
|
||||||
|
verbose_name = _('pretix default')
|
||||||
|
identifier = 'classic'
|
||||||
|
thumbnail_filename = 'pretixbase/email/thumb.png'
|
||||||
|
template_name = 'pretixbase/email/plainwrapper.html'
|
||||||
|
|
||||||
|
The template is passed the following context variables:
|
||||||
|
|
||||||
|
``site``
|
||||||
|
Name of the pretix installation (``settings.PRETIX_INSTANCE_NAME``)
|
||||||
|
|
||||||
|
``site_url``
|
||||||
|
Root URL of the pretix installation (``settings.SITE_URL``)
|
||||||
|
|
||||||
|
``body``
|
||||||
|
The body as markdown (render with ``{{ body|safe }}``)
|
||||||
|
|
||||||
|
``subject``
|
||||||
|
The email subject
|
||||||
|
|
||||||
|
``color``
|
||||||
|
The primary color of the event
|
||||||
|
|
||||||
|
``event``
|
||||||
|
The ``Event`` object
|
||||||
|
|
||||||
|
``signature`` (optional, only if configured)
|
||||||
|
The body as markdown (render with ``{{ signature|safe }}``)
|
||||||
|
|
||||||
|
``order`` (optional, only if applicable)
|
||||||
|
The ``Order`` object
|
||||||
|
|
||||||
|
.. _inlinestyler: https://pypi.org/project/inlinestyler/
|
||||||
@@ -11,7 +11,8 @@ Core
|
|||||||
----
|
----
|
||||||
|
|
||||||
.. automodule:: pretix.base.signals
|
.. automodule:: pretix.base.signals
|
||||||
:members: periodic_task, event_live_issues, event_copy_data, email_filter, register_notification_types
|
:members: periodic_task, event_live_issues, event_copy_data, email_filter, register_notification_types,
|
||||||
|
item_copy_data, register_sales_channels
|
||||||
|
|
||||||
Order events
|
Order events
|
||||||
""""""""""""
|
""""""""""""
|
||||||
@@ -47,7 +48,8 @@ Backend
|
|||||||
-------
|
-------
|
||||||
|
|
||||||
.. automodule:: pretix.control.signals
|
.. automodule:: pretix.control.signals
|
||||||
:members: nav_event, html_head, quota_detail_html, nav_topbar, nav_global, nav_organizer, nav_event_settings, order_info, event_settings_widget
|
:members: nav_event, html_head, html_page_start, quota_detail_html, nav_topbar, nav_global, nav_organizer, nav_event_settings,
|
||||||
|
order_info, event_settings_widget, oauth_application_registered
|
||||||
|
|
||||||
|
|
||||||
.. automodule:: pretix.base.signals
|
.. automodule:: pretix.base.signals
|
||||||
@@ -56,6 +58,12 @@ Backend
|
|||||||
Vouchers
|
Vouchers
|
||||||
""""""""
|
""""""""
|
||||||
|
|
||||||
|
.. automodule:: pretix.control.signals
|
||||||
|
:members: item_forms
|
||||||
|
|
||||||
|
Vouchers
|
||||||
|
""""""""
|
||||||
|
|
||||||
.. automodule:: pretix.control.signals
|
.. automodule:: pretix.control.signals
|
||||||
:members: voucher_form_class, voucher_form_html, voucher_form_validation
|
:members: voucher_form_class, voucher_form_html, voucher_form_validation
|
||||||
|
|
||||||
@@ -68,5 +76,5 @@ Dashboards
|
|||||||
Ticket designs
|
Ticket designs
|
||||||
""""""""""""""
|
""""""""""""""
|
||||||
|
|
||||||
.. automodule:: pretix.plugins.ticketoutputpdf.signals
|
.. automodule:: pretix.base.signals
|
||||||
:members: layout_text_variables
|
:members: layout_text_variables
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ Contents:
|
|||||||
exporter
|
exporter
|
||||||
ticketoutput
|
ticketoutput
|
||||||
payment
|
payment
|
||||||
|
payment_2.0
|
||||||
|
email
|
||||||
invoice
|
invoice
|
||||||
|
shredder
|
||||||
customview
|
customview
|
||||||
general
|
general
|
||||||
|
quality
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Output registration
|
|||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
The invoice renderer API does not make a lot of usage from signals, however, it
|
The invoice renderer API does not make a lot of usage from signals, however, it
|
||||||
does use a signal to get a list of all available ticket outputs. Your plugin
|
does use a signal to get a list of all available invoice renderers. Your plugin
|
||||||
should listen for this signal and return the subclass of ``pretix.base.invoice.BaseInvoiceRenderer``
|
should listen for this signal and return the subclass of ``pretix.base.invoice.BaseInvoiceRenderer``
|
||||||
that we'll provide in this plugin::
|
that we'll provide in this plugin::
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ is very similar to creating an export output.
|
|||||||
|
|
||||||
Please read :ref:`Creating a plugin <pluginsetup>` first, if you haven't already.
|
Please read :ref:`Creating a plugin <pluginsetup>` first, if you haven't already.
|
||||||
|
|
||||||
|
.. warning:: We changed our payment provider API a lot in pretix 2.x. Our documentation page on :ref:`payment2.0`
|
||||||
|
might be insightful even if you do not have a payment provider to port, as it outlines the rationale
|
||||||
|
behind the current design.
|
||||||
|
|
||||||
Provider registration
|
Provider registration
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
@@ -31,7 +35,7 @@ that the plugin will provide::
|
|||||||
The provider class
|
The provider class
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
.. class:: pretix.base.payment.BasePaymentProvider
|
.. py:class:: pretix.base.payment.BasePaymentProvider
|
||||||
|
|
||||||
The central object of each payment provider is the subclass of ``BasePaymentProvider``.
|
The central object of each payment provider is the subclass of ``BasePaymentProvider``.
|
||||||
|
|
||||||
@@ -54,53 +58,61 @@ The provider class
|
|||||||
|
|
||||||
This is an abstract attribute, you **must** override this!
|
This is an abstract attribute, you **must** override this!
|
||||||
|
|
||||||
.. autoattribute:: is_enabled
|
.. autoattribute:: public_name
|
||||||
|
|
||||||
.. automethod:: calculate_fee
|
.. autoattribute:: is_enabled
|
||||||
|
|
||||||
.. autoattribute:: settings_form_fields
|
.. autoattribute:: settings_form_fields
|
||||||
|
|
||||||
|
.. automethod:: settings_form_clean
|
||||||
|
|
||||||
.. automethod:: settings_content_render
|
.. automethod:: settings_content_render
|
||||||
|
|
||||||
.. automethod:: render_invoice_text
|
.. automethod:: is_allowed
|
||||||
|
|
||||||
.. automethod:: payment_form_render
|
.. automethod:: payment_form_render
|
||||||
|
|
||||||
.. automethod:: payment_form
|
.. automethod:: payment_form
|
||||||
|
|
||||||
.. automethod:: is_allowed
|
|
||||||
|
|
||||||
.. autoattribute:: payment_form_fields
|
.. autoattribute:: payment_form_fields
|
||||||
|
|
||||||
.. automethod:: checkout_prepare
|
|
||||||
|
|
||||||
.. automethod:: payment_is_valid_session
|
.. automethod:: payment_is_valid_session
|
||||||
|
|
||||||
|
.. automethod:: checkout_prepare
|
||||||
|
|
||||||
.. automethod:: checkout_confirm_render
|
.. automethod:: checkout_confirm_render
|
||||||
|
|
||||||
This is an abstract method, you **must** override this!
|
This is an abstract method, you **must** override this!
|
||||||
|
|
||||||
.. automethod:: payment_perform
|
.. automethod:: execute_payment
|
||||||
|
|
||||||
|
.. automethod:: calculate_fee
|
||||||
|
|
||||||
.. automethod:: order_pending_mail_render
|
.. automethod:: order_pending_mail_render
|
||||||
|
|
||||||
.. automethod:: order_pending_render
|
.. automethod:: payment_pending_render
|
||||||
|
|
||||||
This is an abstract method, you **must** override this!
|
.. autoattribute:: abort_pending_allowed
|
||||||
|
|
||||||
|
.. automethod:: render_invoice_text
|
||||||
|
|
||||||
.. automethod:: order_change_allowed
|
.. automethod:: order_change_allowed
|
||||||
|
|
||||||
.. automethod:: order_can_retry
|
.. automethod:: payment_prepare
|
||||||
|
|
||||||
.. automethod:: order_prepare
|
.. automethod:: payment_control_render
|
||||||
|
|
||||||
.. automethod:: order_paid_render
|
.. automethod:: payment_refund_supported
|
||||||
|
|
||||||
.. automethod:: order_control_render
|
.. automethod:: payment_partial_refund_supported
|
||||||
|
|
||||||
.. automethod:: order_control_refund_render
|
.. automethod:: execute_refund
|
||||||
|
|
||||||
.. automethod:: order_control_refund_perform
|
.. automethod:: shred_payment_info
|
||||||
|
|
||||||
|
.. autoattribute:: is_implicit
|
||||||
|
|
||||||
|
.. autoattribute:: is_meta
|
||||||
|
|
||||||
|
|
||||||
Additional views
|
Additional views
|
||||||
|
|||||||
129
doc/development/api/payment_2.0.rst
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
.. highlight:: python
|
||||||
|
:linenothreshold: 5
|
||||||
|
|
||||||
|
.. _`payment2.0`:
|
||||||
|
|
||||||
|
Porting a payment provider from pretix 1.x to pretix 2.x
|
||||||
|
========================================================
|
||||||
|
|
||||||
|
In pretix 2.x, we changed large parts of the payment provider API. This documentation details the changes we made
|
||||||
|
and shows you how you can make an existing pretix 1.x payment provider compatible with pretix 2.x
|
||||||
|
|
||||||
|
Conceptual overview
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
In pretix 1.x, an order was always directly connected to a payment provider for the full life of an order. As long as
|
||||||
|
an order was unpaid, this could still be changed in some cases, but once an order was paid, no changes to the payment
|
||||||
|
provider were possible any more. Additionally, the internal state of orders allowed orders only to be fully paid or
|
||||||
|
not paid at all. This leads to a couple of consequences:
|
||||||
|
|
||||||
|
* Payment-related functions (like "execute payment" or "do a refund") always operated on full orders.
|
||||||
|
|
||||||
|
* Changing the total of an order was basically impossible once an order was paid, since there was no concept of
|
||||||
|
partial payments or partial refunds.
|
||||||
|
|
||||||
|
* Payment provider plugins needed to take complicated steps to detect cases that require human intervention, like e.g.
|
||||||
|
|
||||||
|
* An order has expired, no quota is left to revive it, but a payment has been received
|
||||||
|
|
||||||
|
* A payment has been received for a canceled order
|
||||||
|
|
||||||
|
* A payment has been received for an order that has already been paid with a different payment method
|
||||||
|
|
||||||
|
* An external payment service notified us of a refund/dispute
|
||||||
|
|
||||||
|
We noticed that we copied and repeated large portions of code in all our official payment provider plugins, just
|
||||||
|
to deal with some of these cases.
|
||||||
|
|
||||||
|
* Sometimes, there is the need to mark an order as refunded within pretix, without automatically triggering a refund
|
||||||
|
with an external API. Every payment method needed to implement a user interface for this independently.
|
||||||
|
|
||||||
|
* If a refund was not possible automatically, there was no way user to track which payments actually have been refunded
|
||||||
|
manually and which are still left to do.
|
||||||
|
|
||||||
|
* When the payment with one payment provider failed and the user changed to a different payment provider, all
|
||||||
|
information about the first payment was lost from the order object and could only be retrieved from order log data,
|
||||||
|
which also made it hard to design a data shredder API to get rid of this data.
|
||||||
|
|
||||||
|
In pretix 2.x, we introduced two new models, :py:class:`OrderPayment <pretix.base.models.OrderPayment>` and
|
||||||
|
:py:class:`OrderRefund <pretix.base.models.OrderRefund>`. Each instance of these is connected to an order and
|
||||||
|
represents one single attempt to pay or refund a specific amount of money. Each one of these has an individual state,
|
||||||
|
can individually fail or succeed, and carries an amount variable that can differ from the order total.
|
||||||
|
|
||||||
|
This has the following advantages:
|
||||||
|
|
||||||
|
* The system can now detect orders that are over- or underpaid, independent of the payment providers in use.
|
||||||
|
|
||||||
|
* Therefore, we can now allow partial payments, partial refunds, and changing paid orders, and automatically detect
|
||||||
|
the cases listed above and notify the user.
|
||||||
|
|
||||||
|
Payment providers now interact with those payment and refund objects more than with orders.
|
||||||
|
|
||||||
|
Your to-do list
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Payment processing
|
||||||
|
""""""""""""""""""
|
||||||
|
|
||||||
|
* The method ``BasePaymentProvider.order_pending_render`` has been removed and replaced by a new
|
||||||
|
``BasePaymentProvider.payment_pending_render(request, payment)`` method that is passed an ``OrderPayment``
|
||||||
|
object instead of an ``Order``.
|
||||||
|
|
||||||
|
* The method ``BasePaymentProvider.payment_form_render`` now receives a new ``total`` parameter.
|
||||||
|
|
||||||
|
* The method ``BasePaymentProvider.payment_perform`` has been removed and replaced by a new method
|
||||||
|
``BasePaymentProvider.execute_payment(request, payment)`` that is passed an ``OrderPayment``
|
||||||
|
object instead of an ``Order``.
|
||||||
|
|
||||||
|
* The function ``pretix.base.services.mark_order_paid`` has been removed, instead call ``payment.confirm()``
|
||||||
|
on a pending ``OrderPayment`` object. If no further payments are required for this order, this will also
|
||||||
|
mark the order as paid automatically. Note that ``payment.confirm()`` can still throw a ``QuotaExceededException``,
|
||||||
|
however it will still mark the payment as complete (not the order!), so you should catch this exception and
|
||||||
|
inform the user, but not abort the transaction.
|
||||||
|
|
||||||
|
* A new property ``BasePaymentProvider.abort_pending_allowed`` has been introduced. Only if set, the user will
|
||||||
|
be able to retry a payment or switch the payment method when the order currently has a payment object in
|
||||||
|
state ``"pending"``. This replaces ``BasePaymentProvider.order_can_retry``, which no longer exists.
|
||||||
|
|
||||||
|
* The methods ``BasePaymentProvider.retry_prepare`` and ``BasePaymentProvider.order_prepare`` have both been
|
||||||
|
replaced by a new method ``BasePaymentProvider.payment_prepare(request, payment)`` that is passed an ``OrderPayment``
|
||||||
|
object instead of an ``Order``. **Keep in mind that this payment object might have an amount property that
|
||||||
|
differs from the order total, if the order is already partially paid.**
|
||||||
|
|
||||||
|
* The method ``BasePaymentProvider.order_paid_render`` has been removed.
|
||||||
|
|
||||||
|
* The method ``BasePaymentProvider.order_control_render`` has been removed and replaced by a new method
|
||||||
|
``BasePaymentProvider.payment_control_render(request, payment)`` that is passed an ``OrderPayment``
|
||||||
|
object instead of an ``Order``.
|
||||||
|
|
||||||
|
* There's no need to manually deal with excess payments or duplicate payments anymore, just setting the ``OrderPayment``
|
||||||
|
methods to the correct state will do the job.
|
||||||
|
|
||||||
|
Creating refunds
|
||||||
|
""""""""""""""""
|
||||||
|
|
||||||
|
* The methods ``BasePaymentProvider.order_control_refund_render`` and ``BasePaymentProvider.order_control_refund_perform``
|
||||||
|
have been removed.
|
||||||
|
|
||||||
|
* Two new boolean methods ``BasePaymentProvider.payment_refund_supported(payment)`` and ``BasePaymentProvider.payment_partial_refund_supported(payment)``
|
||||||
|
have been introduced. They should be set to return ``True`` if and only if the payment API allows to *automatically*
|
||||||
|
transfer the money back to the customer.
|
||||||
|
|
||||||
|
* A new method ``BasePaymentProvider.execute_refund(refund)`` has been introduced. This method is called using a
|
||||||
|
``OrderRefund`` object in ``"created"`` state and is expected to transfer the money back and confirm success with
|
||||||
|
calling ``refund.done()``. This will only ever be called if either ``BasePaymentProvider.payment_refund_supported(payment)``
|
||||||
|
or ``BasePaymentProvider.payment_partial_refund_supported(payment)`` return ``True``.
|
||||||
|
|
||||||
|
Processing external refunds
|
||||||
|
"""""""""""""""""""""""""""
|
||||||
|
|
||||||
|
* If e.g. a webhook API notifies you that a payment has been disputed or refunded with the external API, you are
|
||||||
|
expected to call ``OrderPayment.create_external_refund(self, amount, execution_date, info='{}')`` on this payment.
|
||||||
|
This will create and return an appropriate ``OrderRefund`` object and send out a notification. However, it will not
|
||||||
|
mark the order as refunded, but will ask the event organizer for a decision.
|
||||||
|
|
||||||
|
Data shredders
|
||||||
|
""""""""""""""
|
||||||
|
|
||||||
|
* The method ``BasePaymentProvider.shred_payment_info`` is no longer passed an order, but instead **either**
|
||||||
|
an ``OrderPayment`` **or** an ``OrderRefund``.
|
||||||
@@ -142,5 +142,5 @@ your Django app label.
|
|||||||
.. _Django app: https://docs.djangoproject.com/en/1.7/ref/applications/
|
.. _Django app: https://docs.djangoproject.com/en/1.7/ref/applications/
|
||||||
.. _signal dispatcher: https://docs.djangoproject.com/en/1.7/topics/signals/
|
.. _signal dispatcher: https://docs.djangoproject.com/en/1.7/topics/signals/
|
||||||
.. _namespace packages: http://legacy.python.org/dev/peps/pep-0420/
|
.. _namespace packages: http://legacy.python.org/dev/peps/pep-0420/
|
||||||
.. _entry point: https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins
|
.. _entry point: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#locating-plugins
|
||||||
.. _cookiecutter: https://cookiecutter.readthedocs.io/en/latest/
|
.. _cookiecutter: https://cookiecutter.readthedocs.io/en/latest/
|
||||||
|
|||||||
125
doc/development/api/quality.rst
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
.. highlight:: python
|
||||||
|
:linenothreshold: 5
|
||||||
|
|
||||||
|
.. _`pluginquality`:
|
||||||
|
|
||||||
|
Plugin quality checklist
|
||||||
|
========================
|
||||||
|
|
||||||
|
If you want to write a high-quality pretix plugin, this is a list of things you should check before
|
||||||
|
you publish it. This is also a list of things that we check, if we consider installing an externally
|
||||||
|
developed plugin on our hosted infrastructure.
|
||||||
|
|
||||||
|
A. Meta
|
||||||
|
-------
|
||||||
|
|
||||||
|
#. The plugin is clearly licensed under an appropriate license.
|
||||||
|
|
||||||
|
#. The plugin has an unambiguous name, description, and author metadata.
|
||||||
|
|
||||||
|
#. The plugin has a clear versioning scheme and the latest version of the plugin is kept compatible to the latest
|
||||||
|
stable version of pretix.
|
||||||
|
|
||||||
|
#. The plugin is properly packaged using standard Python packaging tools.
|
||||||
|
|
||||||
|
#. The plugin correctly declares its external dependencies.
|
||||||
|
|
||||||
|
#. A contact address is provided in case of security issues.
|
||||||
|
|
||||||
|
B. Isolation
|
||||||
|
------------
|
||||||
|
|
||||||
|
#. If any signal receivers use the `dispatch_uid`_ feature, the UIDs are prefixed by the plugin's name and do not
|
||||||
|
clash with other plugins.
|
||||||
|
|
||||||
|
#. If any templates or static files are shipped, they are located in subdirectories with the name of the plugin and do
|
||||||
|
not clash with other plugins or core files.
|
||||||
|
|
||||||
|
#. Any keys stored to the settings store are prefixed with the plugin's name and do not clash with other plugins or
|
||||||
|
core.
|
||||||
|
|
||||||
|
#. Any keys stored to the user session are prefixed with the plugin's name and do not clash with other plugins or
|
||||||
|
core.
|
||||||
|
|
||||||
|
#. Any registered URLs are unlikely to clash with other plugins or future core URLs.
|
||||||
|
|
||||||
|
C. Security
|
||||||
|
-----------
|
||||||
|
|
||||||
|
#. All important actions are logged to the :ref:`shared log storage <logging>` and a signal receiver is registered to
|
||||||
|
provide a human-readable representation of the log entry.
|
||||||
|
|
||||||
|
#. All views require appropriate permissions and use the ``event_urls`` mechanism if appropriate.
|
||||||
|
:ref:`Read more <customview>`
|
||||||
|
|
||||||
|
#. Any session data for customers is stored in the cart session system if appropriate.
|
||||||
|
|
||||||
|
#. If the plugin is a payment provider:
|
||||||
|
|
||||||
|
#. No credit card numbers may be stored within pretix.
|
||||||
|
|
||||||
|
#. A notification/webhook system is implemented to notify pretix of any refunds.
|
||||||
|
|
||||||
|
#. If such a webhook system is implemented, contents of incoming webhooks are either verified using a cryptographic
|
||||||
|
signature or are not being trusted and all data is fetched from an API instead.
|
||||||
|
|
||||||
|
D. Privacy
|
||||||
|
----------
|
||||||
|
|
||||||
|
#. No personal data is stored that is not required for the plugin's functionality.
|
||||||
|
|
||||||
|
#. For any personal data that is saved to the database, an appropriate :ref:`data shredder <shredder>` is provided
|
||||||
|
that offers the data for download and then removes it from the database (including log entries).
|
||||||
|
|
||||||
|
E. Internationalization
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
#. All user-facing strings in templates, Python code, and templates are wrapped in `gettext calls`_.
|
||||||
|
|
||||||
|
#. No languages, time zones, date formats, or time formats are hardcoded.
|
||||||
|
|
||||||
|
#. Installing the plugin automatically compiles ``.po`` files to ``.mo`` files. This is fulfilled automatically if
|
||||||
|
you use the ``setup.py`` file form our plugin cookiecutter.
|
||||||
|
|
||||||
|
F. Functionality
|
||||||
|
----------------
|
||||||
|
|
||||||
|
#. If the plugin adds any database models or relationships from the settings storage to database models, it registers
|
||||||
|
a receiver to the :py:attr:`pretix.base.signals.event_copy_data` or :py:attr:`pretix.base.signals.item_copy_data`
|
||||||
|
signals.
|
||||||
|
|
||||||
|
#. If the plugin is a payment provider:
|
||||||
|
|
||||||
|
#. A webhook-like system is implemented if payment confirmations are not sent instantly.
|
||||||
|
|
||||||
|
#. Refunds are implemented, if possible.
|
||||||
|
|
||||||
|
#. In case of overpayment or external refunds, a "required action" is created to notify the event organizer.
|
||||||
|
|
||||||
|
#. If the plugin adds steps to the checkout process, it has been tested in combination with the pretix widget.
|
||||||
|
|
||||||
|
G. Code quality
|
||||||
|
---------------
|
||||||
|
|
||||||
|
#. `isort`_ and `flake8`_ are used to ensure consistent code styling.
|
||||||
|
|
||||||
|
#. Unit tests are provided for important pieces of business logic.
|
||||||
|
|
||||||
|
#. Functional tests are provided for important interface parts.
|
||||||
|
|
||||||
|
#. Tests are provided to check that permission checks are working.
|
||||||
|
|
||||||
|
#. Continuous Integration is set up to check that tests are passing and styling is consistent.
|
||||||
|
|
||||||
|
H. Specific to pretix.eu
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
#. pretix.eu integrates the data stored by this plugin with its data report features.
|
||||||
|
|
||||||
|
#. pretix.eu integrates this plugin in its generated privacy statements, if necessary.
|
||||||
|
|
||||||
|
|
||||||
|
.. _isort: https://www.google.de/search?q=isort&oq=isort&aqs=chrome..69i57j0j69i59j69i60l2j69i59.599j0j4&sourceid=chrome&ie=UTF-8
|
||||||
|
.. _flake8: http://flake8.pycqa.org/en/latest/
|
||||||
|
.. _gettext calls: https://docs.djangoproject.com/en/2.0/topics/i18n/translation/
|
||||||
|
.. _dispatch_uid: https://docs.djangoproject.com/en/2.0/topics/signals/#django.dispatch.Signal.connect
|
||||||
94
doc/development/api/shredder.rst
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
.. highlight:: python
|
||||||
|
:linenothreshold: 5
|
||||||
|
|
||||||
|
.. _`shredder`:
|
||||||
|
|
||||||
|
Writing a data shredder
|
||||||
|
=======================
|
||||||
|
|
||||||
|
If your plugin adds the ability to store personal data within pretix, you should also implement a "data shredder"
|
||||||
|
to anonymize or pseudonymize the data later.
|
||||||
|
|
||||||
|
Shredder registration
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The data shredder API does not make a lot of usage from signals, however, it
|
||||||
|
does use a signal to get a list of all available data shredders. Your plugin
|
||||||
|
should listen for this signal and return the subclass of ``pretix.base.shredder.BaseDataShredder``
|
||||||
|
that we'll provide in this plugin:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
from django.dispatch import receiver
|
||||||
|
|
||||||
|
from pretix.base.signals import register_data_shredders
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(register_data_shredders, dispatch_uid="custom_data_shredders")
|
||||||
|
def register_shredder(sender, **kwargs):
|
||||||
|
return [
|
||||||
|
PluginDataShredder,
|
||||||
|
]
|
||||||
|
|
||||||
|
The shredder class
|
||||||
|
------------------
|
||||||
|
|
||||||
|
.. class:: pretix.base.shredder.BaseDataShredder
|
||||||
|
|
||||||
|
The central object of each invoice renderer is the subclass of ``BaseInvoiceRenderer``.
|
||||||
|
|
||||||
|
.. py:attribute:: BaseInvoiceRenderer.event
|
||||||
|
|
||||||
|
The default constructor sets this property to the event we are currently
|
||||||
|
working for.
|
||||||
|
|
||||||
|
.. autoattribute:: identifier
|
||||||
|
|
||||||
|
This is an abstract attribute, you **must** override this!
|
||||||
|
|
||||||
|
.. autoattribute:: verbose_name
|
||||||
|
|
||||||
|
This is an abstract attribute, you **must** override this!
|
||||||
|
|
||||||
|
.. autoattribute:: description
|
||||||
|
|
||||||
|
This is an abstract attribute, you **must** override this!
|
||||||
|
|
||||||
|
.. automethod:: generate_files
|
||||||
|
|
||||||
|
.. automethod:: shred_data
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
|
||||||
|
For example, the core data shredder responsible for removing invoice address information including their history
|
||||||
|
looks like this:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
class InvoiceAddressShredder(BaseDataShredder):
|
||||||
|
verbose_name = _('Invoice addresses')
|
||||||
|
identifier = 'invoice_addresses'
|
||||||
|
description = _('This will remove all invoice addresses from orders, '
|
||||||
|
'as well as logged changes to them.')
|
||||||
|
|
||||||
|
def generate_files(self) -> List[Tuple[str, str, str]]:
|
||||||
|
yield 'invoice-addresses.json', 'application/json', json.dumps({
|
||||||
|
ia.order.code: InvoiceAdddressSerializer(ia).data
|
||||||
|
for ia in InvoiceAddress.objects.filter(order__event=self.event)
|
||||||
|
}, indent=4)
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def shred_data(self):
|
||||||
|
InvoiceAddress.objects.filter(order__event=self.event).delete()
|
||||||
|
|
||||||
|
for le in self.event.logentry_set.filter(action_type="pretix.event.order.modified"):
|
||||||
|
d = le.parsed_data
|
||||||
|
if 'invoice_data' in d and not isinstance(d['invoice_data'], bool):
|
||||||
|
for field in d['invoice_data']:
|
||||||
|
if d['invoice_data'][field]:
|
||||||
|
d['invoice_data'][field] = '█'
|
||||||
|
le.data = json.dumps(d)
|
||||||
|
le.shredded = True
|
||||||
|
le.save(update_fields=['data', 'shredded'])
|
||||||
|
|
||||||
@@ -77,6 +77,6 @@ Attribution
|
|||||||
-----------
|
-----------
|
||||||
|
|
||||||
This Code of Conduct is adapted from the `Contributor Covenant`_, version 1.4,
|
This Code of Conduct is adapted from the `Contributor Covenant`_, version 1.4,
|
||||||
available at http://contributor-covenant.org/version/1/4/
|
available at https://www.contributor-covenant.org/version/1/4/
|
||||||
|
|
||||||
.. _Contributor Covenant: http://contributor-covenant.org
|
.. _Contributor Covenant: https://www.contributor-covenant.org
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ Coding style and quality
|
|||||||
``Fix #123 -- Problems with order creation`` or ``Refs #123 -- Fix this part of that bug``.
|
``Fix #123 -- Problems with order creation`` or ``Refs #123 -- Fix this part of that bug``.
|
||||||
|
|
||||||
|
|
||||||
.. _PEP 8: http://legacy.python.org/dev/peps/pep-0008/
|
.. _PEP 8: https://legacy.python.org/dev/peps/pep-0008/
|
||||||
.. _flake8: https://pypi.python.org/pypi/flake8
|
.. _flake8: https://pypi.python.org/pypi/flake8
|
||||||
.. _Django Coding Style: https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/
|
.. _Django Coding Style: https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/
|
||||||
.. _translation: https://docs.djangoproject.com/en/1.11/topics/i18n/translation/
|
.. _translation: https://docs.djangoproject.com/en/1.11/topics/i18n/translation/
|
||||||
|
|||||||
@@ -16,4 +16,5 @@ Contents:
|
|||||||
settings
|
settings
|
||||||
background
|
background
|
||||||
email
|
email
|
||||||
|
permissions
|
||||||
logging
|
logging
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ Logging and notifications
|
|||||||
As pretix is handling monetary transactions, we are very careful to make it possible to review all changes
|
As pretix is handling monetary transactions, we are very careful to make it possible to review all changes
|
||||||
in the system that lead to the current state.
|
in the system that lead to the current state.
|
||||||
|
|
||||||
|
.. _`logging`:
|
||||||
|
|
||||||
Logging changes
|
Logging changes
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ Organizers and events
|
|||||||
.. autoclass:: pretix.base.models.Team
|
.. autoclass:: pretix.base.models.Team
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pretix.base.models.TeamAPIToken
|
||||||
|
:members:
|
||||||
|
|
||||||
.. autoclass:: pretix.base.models.RequiredAction
|
.. autoclass:: pretix.base.models.RequiredAction
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
@@ -83,6 +86,15 @@ Carts and Orders
|
|||||||
.. autoclass:: pretix.base.models.OrderPosition
|
.. autoclass:: pretix.base.models.OrderPosition
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pretix.base.models.OrderFee
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pretix.base.models.OrderPayment
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pretix.base.models.OrderRefund
|
||||||
|
:members:
|
||||||
|
|
||||||
.. autoclass:: pretix.base.models.CartPosition
|
.. autoclass:: pretix.base.models.CartPosition
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|||||||
194
doc/development/implementation/permissions.rst
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
Permissions
|
||||||
|
===========
|
||||||
|
|
||||||
|
pretix uses a fine-grained permission system to control who is allowed to control what parts of the system.
|
||||||
|
The central concept here is the concept of *Teams*. You can read more on `configuring teams and permissions <user-teams>`_
|
||||||
|
and the :class:`pretix.base.models.Team` model in the respective parts of the documentation. The basic digest is:
|
||||||
|
An organizer account can have any number of teams, and any number of users can be part of a team. A team can be
|
||||||
|
assigned a set of permissions and connected to some or all of the events of the organizer.
|
||||||
|
|
||||||
|
A second way to access pretix is via the REST API, which allows authentication via tokens that are bound to a team,
|
||||||
|
but not to a user. You can read more at :class:`pretix.base.models.TeamAPIToken`. This page will show you how to
|
||||||
|
work with permissions in plugins and within the pretix code base.
|
||||||
|
|
||||||
|
Requiring permissions for a view
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
pretix provides a number of useful mixins and decorators that allow you to specify that a user needs a certain
|
||||||
|
permission level to access a view::
|
||||||
|
|
||||||
|
from pretix.control.permissions import (
|
||||||
|
OrganizerPermissionRequiredMixin, organizer_permission_required
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MyOrgaView(OrganizerPermissionRequiredMixin, View):
|
||||||
|
permission = 'can_change_organizer_settings'
|
||||||
|
# Only users with the permission ``can_change_organizer_settings`` on
|
||||||
|
# this organizer can access this
|
||||||
|
|
||||||
|
|
||||||
|
class MyOtherOrgaView(OrganizerPermissionRequiredMixin, View):
|
||||||
|
permission = None
|
||||||
|
# Only users with *any* permission on this organizer can access this
|
||||||
|
|
||||||
|
|
||||||
|
@organizer_permission_required('can_change_organizer_settings')
|
||||||
|
def my_orga_view(request, organizer, **kwargs):
|
||||||
|
# Only users with the permission ``can_change_organizer_settings`` on
|
||||||
|
# this organizer can access this
|
||||||
|
|
||||||
|
|
||||||
|
@organizer_permission_required()
|
||||||
|
def my_other_orga_view(request, organizer, **kwargs):
|
||||||
|
# Only users with *any* permission on this organizer can access this
|
||||||
|
|
||||||
|
|
||||||
|
Of course, the same is available on event level::
|
||||||
|
|
||||||
|
from pretix.control.permissions import (
|
||||||
|
EventPermissionRequiredMixin, event_permission_required
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MyEventView(EventPermissionRequiredMixin, View):
|
||||||
|
permission = 'can_change_event_settings'
|
||||||
|
# Only users with the permission ``can_change_event_settings`` on
|
||||||
|
# this event can access this
|
||||||
|
|
||||||
|
|
||||||
|
class MyOtherEventView(EventPermissionRequiredMixin, View):
|
||||||
|
permission = None
|
||||||
|
# Only users with *any* permission on this event can access this
|
||||||
|
|
||||||
|
|
||||||
|
@event_permission_required('can_change_event_settings')
|
||||||
|
def my_event_view(request, organizer, **kwargs):
|
||||||
|
# Only users with the permission ``can_change_event_settings`` on
|
||||||
|
# this event can access this
|
||||||
|
|
||||||
|
|
||||||
|
@event_permission_required()
|
||||||
|
def my_other_event_view(request, organizer, **kwargs):
|
||||||
|
# Only users with *any* permission on this event can access this
|
||||||
|
|
||||||
|
You can also require that this view is only accessible by system administrators with an active "admin session"
|
||||||
|
(see below for what this means)::
|
||||||
|
|
||||||
|
from pretix.control.permissions import (
|
||||||
|
AdministratorPermissionRequiredMixin, administrator_permission_required
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MyGlobalView(AdministratorPermissionRequiredMixin, View):
|
||||||
|
# ...
|
||||||
|
|
||||||
|
|
||||||
|
@administrator_permission_required
|
||||||
|
def my_global_view(request, organizer, **kwargs):
|
||||||
|
# ...
|
||||||
|
|
||||||
|
In rare cases it might also be useful to expose a feature only to people who have a staff account but do not
|
||||||
|
necessarily have an active admin session::
|
||||||
|
|
||||||
|
from pretix.control.permissions import (
|
||||||
|
StaffMemberRequiredMixin, staff_member_required
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MyGlobalView(StaffMemberRequiredMixin, View):
|
||||||
|
# ...
|
||||||
|
|
||||||
|
|
||||||
|
@staff_member_required
|
||||||
|
def my_global_view(request, organizer, **kwargs):
|
||||||
|
# ...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Requiring permissions in the REST API
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
When creating your own ``viewset`` using Django REST framework, you just need to set the ``permission`` attribute
|
||||||
|
and pretix will check it automatically for you::
|
||||||
|
|
||||||
|
class MyModelViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
|
permission = 'can_view_orders'
|
||||||
|
|
||||||
|
Checking permission in code
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
If you need to work with permissions manually, there are a couple of useful helper methods on the :class:`pretix.base.models.Event`,
|
||||||
|
:class:`pretix.base.models.User` and :class:`pretix.base.models.TeamAPIToken` classes. Here's a quick overview.
|
||||||
|
|
||||||
|
Return all users that are in any team that is connected to this event::
|
||||||
|
|
||||||
|
>>> event.get_users_with_any_permission()
|
||||||
|
<QuerySet: …>
|
||||||
|
|
||||||
|
Return all users that are in a team with a specific permission for this event::
|
||||||
|
|
||||||
|
>>> event.get_users_with_permission('can_change_event_settings')
|
||||||
|
<QuerySet: …>
|
||||||
|
|
||||||
|
Determine if a user has a certain permission for a specific event::
|
||||||
|
|
||||||
|
>>> user.has_event_permission(organizer, event, 'can_change_event_settings', request=request)
|
||||||
|
True
|
||||||
|
|
||||||
|
Determine if a user has any permission for a specific event::
|
||||||
|
|
||||||
|
>>> user.has_event_permission(organizer, event, request=request)
|
||||||
|
True
|
||||||
|
|
||||||
|
In the two previous commands, the ``request`` argument is optional, but required to support staff sessions (see below).
|
||||||
|
|
||||||
|
The same method exists for organizer-level permissions::
|
||||||
|
|
||||||
|
>>> user.has_organizer_permission(organizer, 'can_change_event_settings', request=request)
|
||||||
|
True
|
||||||
|
|
||||||
|
Sometimes, it might be more useful to get the set of permissions at once::
|
||||||
|
|
||||||
|
>>> user.get_event_permission_set(organizer, event)
|
||||||
|
{'can_change_event_settings', 'can_view_orders', 'can_change_orders'}
|
||||||
|
|
||||||
|
>>> user.get_organizer_permission_set(organizer, event)
|
||||||
|
{'can_change_organizer_settings', 'can_create_events'}
|
||||||
|
|
||||||
|
Within a view on the ``/control`` subpath, the results of these two methods are already available in the
|
||||||
|
``request.eventpermset`` and ``request.orgapermset`` properties. This makes it convenient to query them in templates::
|
||||||
|
|
||||||
|
{% if "can_change_orders" in request.eventpermset %}
|
||||||
|
…
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
You can also do the reverse to get any events a user has access to::
|
||||||
|
|
||||||
|
>>> user.get_events_with_permission('can_change_event_settings', request=request)
|
||||||
|
<QuerySet: …>
|
||||||
|
|
||||||
|
>>> user.get_events_with_any_permission(request=request)
|
||||||
|
<QuerySet: …>
|
||||||
|
|
||||||
|
Most of these methods work identically on :class:`pretix.base.models.TeamAPIToken`.
|
||||||
|
|
||||||
|
Staff sessions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
.. versionchanged:: 1.14
|
||||||
|
|
||||||
|
In 1.14, the ``User.is_superuser`` attribute has been deprecated and statically set to return ``False``. Staff
|
||||||
|
sessions have been newly introduced.
|
||||||
|
|
||||||
|
System administrators of a pretix instance are identified by the ``is_staff`` attribute on the user model. By default,
|
||||||
|
the regular permission rules apply for users with ``is_staff = True``. The only difference is that such users can
|
||||||
|
temporarily turn on "staff mode" via a button in the user interface that grants them **all permissions** as long as
|
||||||
|
staff mode is active. You can check if a user is in staff mode using their session key:
|
||||||
|
|
||||||
|
>>> user.has_active_staff_session(request.session.session_key)
|
||||||
|
False
|
||||||
|
|
||||||
|
Staff mode has a hard time limit and during staff mode, a middleware will log all requests made by that user. Later,
|
||||||
|
the user is able to also save a message to comment on what they did in their administrative session. This feature is
|
||||||
|
intended to help compliance with data protection rules as imposed e.g. by GDPR.
|
||||||
@@ -8,5 +8,6 @@ Developer documentation
|
|||||||
setup
|
setup
|
||||||
contribution/index
|
contribution/index
|
||||||
implementation/index
|
implementation/index
|
||||||
|
translation/index
|
||||||
api/index
|
api/index
|
||||||
structure
|
structure
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ External Dependencies
|
|||||||
---------------------
|
---------------------
|
||||||
Your should install the following on your system:
|
Your should install the following on your system:
|
||||||
|
|
||||||
* Python 3.4 or newer
|
* Python 3.5 or newer
|
||||||
* ``pip`` for Python 3 (Debian package: ``python3-pip``)
|
* ``pip`` for Python 3 (Debian package: ``python3-pip``)
|
||||||
* ``python-dev`` for Python 3 (Debian package: ``python3-dev``)
|
* ``python-dev`` for Python 3 (Debian package: ``python3-dev``)
|
||||||
* ``libffi`` (Debian package: ``libffi-dev``)
|
* ``libffi`` (Debian package: ``libffi-dev``)
|
||||||
@@ -54,10 +54,6 @@ The first thing you need are all the main application's dependencies::
|
|||||||
cd src/
|
cd src/
|
||||||
pip3 install -r requirements.txt -r requirements/dev.txt
|
pip3 install -r requirements.txt -r requirements/dev.txt
|
||||||
|
|
||||||
If you are working with Python 3.4, you will also need (you can skip this for Python 3.5+)::
|
|
||||||
|
|
||||||
pip3 install -r requirements/py34.txt
|
|
||||||
|
|
||||||
Next, you need to copy the SCSS files from the source folder to the STATIC_ROOT directory::
|
Next, you need to copy the SCSS files from the source folder to the STATIC_ROOT directory::
|
||||||
|
|
||||||
python manage.py collectstatic --noinput
|
python manage.py collectstatic --noinput
|
||||||
@@ -115,12 +111,21 @@ Execute the following command to run pretix' test suite (might take a couple of
|
|||||||
``NUM`` being the number of threads you want to use.
|
``NUM`` being the number of threads you want to use.
|
||||||
|
|
||||||
It is a good idea to put this command into your git hook ``.git/hooks/pre-commit``,
|
It is a good idea to put this command into your git hook ``.git/hooks/pre-commit``,
|
||||||
for example::
|
for example, to check for any errors in any staged files when committing::
|
||||||
|
|
||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
cd $GIT_DIR/../src
|
cd $GIT_DIR/../src
|
||||||
flake8 . || exit 1
|
export GIT_WORK_TREE=../
|
||||||
isort -q -rc -c . || exit 1
|
export GIT_DIR=../.git
|
||||||
|
source ../env/bin/activate # Adjust to however you activate your virtual environment
|
||||||
|
for file in $(git diff --cached --name-only | grep -E '\.py$' | grep -Ev "migrations|mt940\.py|pretix/settings\.py|make_testdata\.py|testutils/settings\.py|tests/settings\.py|pretix/base/models/__init__\.py")
|
||||||
|
do
|
||||||
|
echo $file
|
||||||
|
git show ":$file" | flake8 - --stdin-display-name="$file" || exit 1 # we only want to lint the staged changes, not any un-staged changes
|
||||||
|
git show ":$file" | isort -df --check-only - | grep ERROR && exit 1 || true
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
This keeps you from accidentally creating commits violating the style guide.
|
This keeps you from accidentally creating commits violating the style guide.
|
||||||
|
|
||||||
@@ -145,6 +150,10 @@ and update the ``*.po`` files accordingly::
|
|||||||
|
|
||||||
make localegen
|
make localegen
|
||||||
|
|
||||||
|
However, most of the time you don't need to care about this. Just create your pull request
|
||||||
|
with functionality and English strings only, and we'll push the new translation strings
|
||||||
|
to our translation platform after the merge.
|
||||||
|
|
||||||
To actually see pretix in your language, you have to compile the ``*.po`` files to their
|
To actually see pretix in your language, you have to compile the ``*.po`` files to their
|
||||||
optimized binary ``*.mo`` counterparts::
|
optimized binary ``*.mo`` counterparts::
|
||||||
|
|
||||||
|
|||||||
BIN
doc/development/translation/img/weblate1.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
doc/development/translation/img/weblate2.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
doc/development/translation/img/weblate3.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
doc/development/translation/img/weblate4.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
doc/development/translation/img/weblate5.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
doc/development/translation/img/weblate6.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
88
doc/development/translation/index.rst
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
Translating pretix
|
||||||
|
==================
|
||||||
|
|
||||||
|
pretix has been designed for multi-language capabilities from its start. Organizers can enter their event information
|
||||||
|
in multiple languages at the same time. However, the software interface of pretix also needs to be translated for
|
||||||
|
this to be useful.
|
||||||
|
|
||||||
|
Since we (the developers of pretix) only speak a very limited number of languages, we need help from the community
|
||||||
|
to achieve this goal. To make translating pretix easy not only for software developers, we set up a translation
|
||||||
|
platform at `translate.pretix.eu`_.
|
||||||
|
|
||||||
|
Official and inofficial languages
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
In the pretix project, there are three types of languages:
|
||||||
|
|
||||||
|
Official languages
|
||||||
|
are translated and maintained by the core team behind pretix or as part of long-term partnerships. We are
|
||||||
|
committed to keeping these translations up-to-date with new features or changes in pretix and try to offer
|
||||||
|
support in this language.
|
||||||
|
|
||||||
|
Inofficial languages
|
||||||
|
are contributed and maintained by the Community. We ship them with pretix so you can use them, but we can not
|
||||||
|
guarantee that new or changed features in pretix will be translated in time.
|
||||||
|
|
||||||
|
Incubating languages
|
||||||
|
are currently in the process of being translated. They can not yet be selected in pretix by end users on
|
||||||
|
production installations and are only available in development mode for testing.
|
||||||
|
|
||||||
|
Please contact translate@pretix.eu if you think an incubated language should be promoted to an inofficial one or if
|
||||||
|
you are interested in a partnership to make your language official.
|
||||||
|
|
||||||
|
The current translation status of various languages is:
|
||||||
|
|
||||||
|
.. image:: https://translate.pretix.eu/widgets/pretix/-/multi-blue.svg
|
||||||
|
:target: https://translate.pretix.eu/engage/pretix/?utm_source=widget
|
||||||
|
|
||||||
|
|
||||||
|
Using our translation platform
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
If you visit `translate.pretix.eu`_ for the first time, it admittedly looks pretty bare.
|
||||||
|
|
||||||
|
.. image:: img/weblate1.png
|
||||||
|
:class: screenshot
|
||||||
|
|
||||||
|
It gets better if you create an account, which you will need to contribute translations. Click on "Register" in the
|
||||||
|
top-right corner to get started:
|
||||||
|
|
||||||
|
.. image:: img/weblate2.png
|
||||||
|
:class: screenshot
|
||||||
|
|
||||||
|
You can either create an account or choose to log in with your GitHub account, whichever you like more.
|
||||||
|
After creating and activating your account, we recommend that you change your profile and select which languages you
|
||||||
|
can translate to and which languages you understand. You can find your profile settings by clicking on your name in
|
||||||
|
the top-right corner.
|
||||||
|
|
||||||
|
.. image:: img/weblate3.png
|
||||||
|
:class: screenshot
|
||||||
|
|
||||||
|
Going back to the dashboard by clicking on the logo in the top-left corner, you can select between different lists
|
||||||
|
of translation projects. You can either filter by projects that already have a translation in your language, or you
|
||||||
|
go to the `pretix project page`_ where you can select specific components.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If you want to translate pretix to a new language that is not yet listed here, you are very welcome to do so!
|
||||||
|
While you technically can add the language to the portal yourself, we ask you to drop us a short mail to
|
||||||
|
translate@pretix.eu so we can add it to all components at once and also make it selectable in pretix itself.
|
||||||
|
|
||||||
|
.. image:: img/weblate4.png
|
||||||
|
:class: screenshot
|
||||||
|
|
||||||
|
Once you selected a component of a language, you can start going through strings to translate. You can start of by
|
||||||
|
clicking the "Strings needing action" line in this view:
|
||||||
|
|
||||||
|
.. image:: img/weblate5.png
|
||||||
|
:class: screenshot
|
||||||
|
|
||||||
|
In the translate view, you can input your translation for a given source string. If you're unsure about your
|
||||||
|
translation, you can also just "Suggest" it or mark it as "Needs editing". If you have no idea, just "Skip". If you
|
||||||
|
scroll down, there is also a "Comments" section to discuss any questions with fellow translators or us developers.
|
||||||
|
|
||||||
|
.. image:: img/weblate6.png
|
||||||
|
:class: screenshot
|
||||||
|
|
||||||
|
.. _translate.pretix.eu: https://translate.pretix.eu
|
||||||
|
.. _pretix project page: https://translate.pretix.eu/projects/pretix/
|
||||||
0
doc/doc_warnings
Normal file
110
doc/plugins/badges.rst
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
Badges
|
||||||
|
======
|
||||||
|
|
||||||
|
The badges plugin provides a HTTP API that exposes the various layouts used to generate PDF badges.
|
||||||
|
|
||||||
|
Resource description
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The badge layout resource contains the following public fields:
|
||||||
|
|
||||||
|
.. rst-class:: rest-resource-table
|
||||||
|
|
||||||
|
===================================== ========================== =======================================================
|
||||||
|
Field Type Description
|
||||||
|
===================================== ========================== =======================================================
|
||||||
|
id integer Internal layout ID
|
||||||
|
name string Internal layout description
|
||||||
|
default boolean ``true`` if this is the default layout
|
||||||
|
layout object Layout specification for libpretixprint
|
||||||
|
background URL Background PDF file
|
||||||
|
item_assignments list of objects Products this layout is assigned to
|
||||||
|
└ item integer Item ID
|
||||||
|
===================================== ========================== =======================================================
|
||||||
|
|
||||||
|
.. versionchanged:: 1.16
|
||||||
|
|
||||||
|
This resource has been added.
|
||||||
|
|
||||||
|
|
||||||
|
Endpoints
|
||||||
|
---------
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/badgelayouts/
|
||||||
|
|
||||||
|
Returns a list of all badge layouts
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/organizers/bigevents/events/democon/badgelayouts/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: text/javascript
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 1,
|
||||||
|
"next": null,
|
||||||
|
"previous": null,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Default layout",
|
||||||
|
"default": true,
|
||||||
|
"layout": {…},
|
||||||
|
"background": {},
|
||||||
|
"item_assignments": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
:query page: The page number in case of a multi-page result set, default is 1
|
||||||
|
:param organizer: The ``slug`` field of a valid organizer
|
||||||
|
:param event: The ``slug`` field of a valid event
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer does not exist **or** you have no permission to view it.
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/badgelayouts/(id)/
|
||||||
|
|
||||||
|
Returns information on layout.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/organizers/bigevents/events/democon/layoutsbadge/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: text/javascript
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Default layout",
|
||||||
|
"default": true,
|
||||||
|
"layout": {…},
|
||||||
|
"background": {},
|
||||||
|
"item_assignments": []
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to fetch
|
||||||
|
:param event: The ``slug`` field of the event to fetch
|
||||||
|
:param id: The ``id`` field of the layout to fetch
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view it.
|
||||||
@@ -12,3 +12,5 @@ If you want to **create** a plugin, please go to the
|
|||||||
list
|
list
|
||||||
pretixdroid
|
pretixdroid
|
||||||
banktransfer
|
banktransfer
|
||||||
|
ticketoutputpdf
|
||||||
|
badges
|
||||||
|
|||||||
@@ -4,56 +4,7 @@
|
|||||||
List of plugins
|
List of plugins
|
||||||
===============
|
===============
|
||||||
|
|
||||||
The following plugins are shipped with pretix and are supported in the same
|
A detailed list of plugins that are available for pretix can be found on the
|
||||||
ways that pretix itself is:
|
`project website`_.
|
||||||
|
|
||||||
* Bank transfer
|
.. _project website: https://pretix.eu/about/en/plugins
|
||||||
* PayPal
|
|
||||||
* Stripe
|
|
||||||
* Check-in lists
|
|
||||||
* pretixdroid
|
|
||||||
* Report exporter
|
|
||||||
* Send out emails
|
|
||||||
* Statistics
|
|
||||||
* PDF ticket output
|
|
||||||
|
|
||||||
The following plugins are not shipped with pretix but are maintained by the
|
|
||||||
same team. We update them regularly to make them compatible with the latest
|
|
||||||
pretix releases:
|
|
||||||
|
|
||||||
* `SEPA direct debit`_
|
|
||||||
* `Wirecard payment`_
|
|
||||||
* `Pages`_
|
|
||||||
* `Passbook/Wallet ticket output`_
|
|
||||||
* `Cartshare`_
|
|
||||||
* `Fontpack Free fonts`_
|
|
||||||
* `Mailing list subscription`_
|
|
||||||
|
|
||||||
The following closed-source plugins are available to customers of the hosted pretix.eu platform.
|
|
||||||
Please get in touch with the pretix team if you want to have them for your self-hosted
|
|
||||||
pretix installation:
|
|
||||||
|
|
||||||
* Campaign tracking
|
|
||||||
* Integration with Google Analytics and Facebook Pixel
|
|
||||||
* Integration with Slack
|
|
||||||
* Integration with MailChimp
|
|
||||||
|
|
||||||
The following plugins are from independent third-party authors, so we can make
|
|
||||||
no statements about their functionality, security, stability or compatibility:
|
|
||||||
|
|
||||||
* `esPass ticket output`_
|
|
||||||
* `IcePay integration`_
|
|
||||||
* `Average price chart`_
|
|
||||||
* `Pay in cash upon arrival`_
|
|
||||||
|
|
||||||
.. _SEPA direct debit: https://github.com/pretix/pretix-sepadebit
|
|
||||||
.. _Passbook/Wallet ticket output: https://github.com/pretix/pretix-passbook
|
|
||||||
.. _Cartshare: https://github.com/pretix/pretix-cartshare
|
|
||||||
.. _Pages: https://github.com/pretix/pretix-pages
|
|
||||||
.. _esPass ticket output: https://github.com/esPass/pretix-espass
|
|
||||||
.. _IcePay integration: https://github.com/chotee/pretix-icepay
|
|
||||||
.. _Fontpack Free fonts: https://github.com/pretix/pretix-fontpack-free
|
|
||||||
.. _Wirecard payment: https://github.com/pretix/pretix-wirecard
|
|
||||||
.. _Mailing list subscription: https://github.com/pretix/pretix-newsletter-ml
|
|
||||||
.. _Average price chart: https://github.com/rixx/pretix-avgchart
|
|
||||||
.. _Pay in cash upon arrival: https://github.com/pc-coholic/pretix-cashpayment
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ pretixdroid HTTP API
|
|||||||
The pretixdroid plugin provides a HTTP API that the `pretixdroid Android app`_
|
The pretixdroid plugin provides a HTTP API that the `pretixdroid Android app`_
|
||||||
uses to communicate with the pretix server.
|
uses to communicate with the pretix server.
|
||||||
|
|
||||||
.. warning:: This API is intended **only** to serve the pretixdroid Android app. There are no backwards compatibility
|
.. warning:: This API is **DEPRECATED** and will probably go away soon. It is used **only** to serve the pretixdroid
|
||||||
guarantees on this API. We will not add features that are not required for the Android App. There is a
|
Android app. There are no backwards compatibility guarantees on this API. We will not add features that
|
||||||
general-purpose :ref:`rest-api` that not yet provides all features that this API provides, but will do
|
are not required for the Android App. There is a general-purpose :ref:`rest-api` that provides all
|
||||||
so in the future.
|
features that you need to check in.
|
||||||
|
|
||||||
.. versionchanged:: 1.12
|
.. versionchanged:: 1.12
|
||||||
|
|
||||||
@@ -15,6 +15,10 @@ uses to communicate with the pretix server.
|
|||||||
negotiated live, so clients which do not need this feature can ignore the change. For this reason, the API version
|
negotiated live, so clients which do not need this feature can ignore the change. For this reason, the API version
|
||||||
has not been increased and is still set to 3.
|
has not been increased and is still set to 3.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.13
|
||||||
|
|
||||||
|
Support for checking in unpaid tickets has been added.
|
||||||
|
|
||||||
|
|
||||||
.. http:post:: /pretixdroid/api/(organizer)/(event)/redeem/
|
.. http:post:: /pretixdroid/api/(organizer)/(event)/redeem/
|
||||||
|
|
||||||
@@ -49,6 +53,9 @@ uses to communicate with the pretix server.
|
|||||||
check-in. This is meant to be used to prevent duplicate check-ins when you are just retrying after a connection
|
check-in. This is meant to be used to prevent duplicate check-ins when you are just retrying after a connection
|
||||||
failure.
|
failure.
|
||||||
|
|
||||||
|
You **may** set the additional parameter ``ignore_unpaid`` to indicate that the check-in should be performed even
|
||||||
|
if the order is in pending state.
|
||||||
|
|
||||||
If questions are supported and required, you will receive a dictionary ``questions`` containing details on the
|
If questions are supported and required, you will receive a dictionary ``questions`` containing details on the
|
||||||
particular questions to ask. To answer them, just re-send your redemption request with additional parameters of
|
particular questions to ask. To answer them, just re-send your redemption request with additional parameters of
|
||||||
the form ``answer_<question>=<answer>``, e.g. ``answer_12=24``.
|
the form ``answer_<question>=<answer>``, e.g. ``answer_12=24``.
|
||||||
@@ -73,6 +80,8 @@ uses to communicate with the pretix server.
|
|||||||
"attendee_name": "Peter Higgs",
|
"attendee_name": "Peter Higgs",
|
||||||
"attention": false,
|
"attention": false,
|
||||||
"redeemed": true,
|
"redeemed": true,
|
||||||
|
"checkin_allowed": true,
|
||||||
|
"addons_text": "Parking spot",
|
||||||
"paid": true
|
"paid": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,6 +106,8 @@ uses to communicate with the pretix server.
|
|||||||
"attendee_name": "Peter Higgs",
|
"attendee_name": "Peter Higgs",
|
||||||
"attention": false,
|
"attention": false,
|
||||||
"redeemed": true,
|
"redeemed": true,
|
||||||
|
"checkin_allowed": true,
|
||||||
|
"addons_text": "Parking spot",
|
||||||
"paid": true
|
"paid": true
|
||||||
},
|
},
|
||||||
"questions": [
|
"questions": [
|
||||||
@@ -142,6 +153,8 @@ uses to communicate with the pretix server.
|
|||||||
"attendee_name": "Peter Higgs",
|
"attendee_name": "Peter Higgs",
|
||||||
"attention": false,
|
"attention": false,
|
||||||
"redeemed": true,
|
"redeemed": true,
|
||||||
|
"checkin_allowed": true,
|
||||||
|
"addons_text": "Parking spot",
|
||||||
"paid": true
|
"paid": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,6 +214,8 @@ uses to communicate with the pretix server.
|
|||||||
"attendee_name": "Peter Higgs",
|
"attendee_name": "Peter Higgs",
|
||||||
"redeemed": false,
|
"redeemed": false,
|
||||||
"attention": false,
|
"attention": false,
|
||||||
|
"checkin_allowed": true,
|
||||||
|
"addons_text": "Parking spot",
|
||||||
"paid": true
|
"paid": true
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
@@ -244,6 +259,7 @@ uses to communicate with the pretix server.
|
|||||||
"attendee_name": "Peter Higgs",
|
"attendee_name": "Peter Higgs",
|
||||||
"redeemed": false,
|
"redeemed": false,
|
||||||
"attention": false,
|
"attention": false,
|
||||||
|
"checkin_allowed": true,
|
||||||
"paid": true
|
"paid": true
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
|
|||||||
116
doc/plugins/ticketoutputpdf.rst
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
PDF ticket output
|
||||||
|
=================
|
||||||
|
|
||||||
|
The PDF ticket output plugin provides a HTTP API that exposes the various layouts used
|
||||||
|
to generate PDF tickets.
|
||||||
|
|
||||||
|
Resource description
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The ticket layout resource contains the following public fields:
|
||||||
|
|
||||||
|
.. rst-class:: rest-resource-table
|
||||||
|
|
||||||
|
===================================== ========================== =======================================================
|
||||||
|
Field Type Description
|
||||||
|
===================================== ========================== =======================================================
|
||||||
|
id integer Internal layout ID
|
||||||
|
name string Internal layout description
|
||||||
|
default boolean ``true`` if this is the default layout
|
||||||
|
layout object Layout specification for libpretixprint
|
||||||
|
background URL Background PDF file
|
||||||
|
item_assignments list of objects Products this layout is assigned to
|
||||||
|
├ sales_channel string Sales channel (defaults to ``web``).
|
||||||
|
└ item integer Item ID
|
||||||
|
===================================== ========================== =======================================================
|
||||||
|
|
||||||
|
.. versionchanged:: 1.16
|
||||||
|
|
||||||
|
This resource has been added.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.3
|
||||||
|
|
||||||
|
The ``item_assignments.sales_channel`` field has been added.
|
||||||
|
|
||||||
|
|
||||||
|
Endpoints
|
||||||
|
---------
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/ticketlayouts/
|
||||||
|
|
||||||
|
Returns a list of all ticket layouts
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/organizers/bigevents/events/democon/ticketlayouts/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: text/javascript
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 1,
|
||||||
|
"next": null,
|
||||||
|
"previous": null,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Default layout",
|
||||||
|
"default": true,
|
||||||
|
"layout": {…},
|
||||||
|
"background": {},
|
||||||
|
"item_assignments": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
:query page: The page number in case of a multi-page result set, default is 1
|
||||||
|
:param organizer: The ``slug`` field of a valid organizer
|
||||||
|
:param event: The ``slug`` field of a valid event
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer does not exist **or** you have no permission to view it.
|
||||||
|
|
||||||
|
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/ticketlayouts/(id)/
|
||||||
|
|
||||||
|
Returns information on layout.
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /api/v1/organizers/bigevents/events/democon/ticketlayouts/1/ HTTP/1.1
|
||||||
|
Host: pretix.eu
|
||||||
|
Accept: application/json, text/javascript
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Vary: Accept
|
||||||
|
Content-Type: text/javascript
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Default layout",
|
||||||
|
"default": true,
|
||||||
|
"layout": {…},
|
||||||
|
"background": {},
|
||||||
|
"item_assignments": []
|
||||||
|
}
|
||||||
|
|
||||||
|
:param organizer: The ``slug`` field of the organizer to fetch
|
||||||
|
:param event: The ``slug`` field of the event to fetch
|
||||||
|
:param id: The ``id`` field of the layout to fetch
|
||||||
|
:statuscode 200: no error
|
||||||
|
:statuscode 401: Authentication failure
|
||||||
|
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view it.
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
-r ../src/requirements.txt
|
-r ../src/requirements.txt
|
||||||
sphinx
|
sphinx==1.6.*
|
||||||
sphinx-rtd-theme
|
sphinx-rtd-theme
|
||||||
sphinxcontrib-httpdomain
|
sphinxcontrib-httpdomain
|
||||||
sphinxcontrib-images
|
sphinxcontrib-images
|
||||||
sphinxcontrib-spelling
|
sphinxcontrib-spelling
|
||||||
pyenchant
|
# See https://github.com/rfk/pyenchant/pull/130
|
||||||
|
git+https://github.com/raphaelm/pyenchant.git@patch-1#egg=pyenchant
|
||||||
|
|||||||
BIN
doc/screens/organizer/webhook_edit.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
doc/screens/organizer/webhook_list.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
@@ -1,6 +1,9 @@
|
|||||||
addon
|
addon
|
||||||
addons
|
addons
|
||||||
|
Analytics
|
||||||
|
anonymize
|
||||||
api
|
api
|
||||||
|
auditability
|
||||||
auth
|
auth
|
||||||
autobuild
|
autobuild
|
||||||
backend
|
backend
|
||||||
@@ -15,10 +18,13 @@ checksum
|
|||||||
config
|
config
|
||||||
contenttypes
|
contenttypes
|
||||||
contextmanager
|
contextmanager
|
||||||
|
cookiecutter
|
||||||
cron
|
cron
|
||||||
cronjob
|
cronjob
|
||||||
|
cryptographic
|
||||||
debian
|
debian
|
||||||
deduplication
|
deduplication
|
||||||
|
deprovision
|
||||||
discoverable
|
discoverable
|
||||||
django
|
django
|
||||||
dockerfile
|
dockerfile
|
||||||
@@ -33,8 +39,13 @@ gettext
|
|||||||
gunicorn
|
gunicorn
|
||||||
hardcoded
|
hardcoded
|
||||||
hostname
|
hostname
|
||||||
|
idempotency
|
||||||
|
incrementing
|
||||||
|
inofficial
|
||||||
invalidations
|
invalidations
|
||||||
iterable
|
iterable
|
||||||
|
Jimdo
|
||||||
|
libpretixprint
|
||||||
libsass
|
libsass
|
||||||
linters
|
linters
|
||||||
memcached
|
memcached
|
||||||
@@ -53,7 +64,9 @@ nginx
|
|||||||
NotificationType
|
NotificationType
|
||||||
ons
|
ons
|
||||||
optimizations
|
optimizations
|
||||||
|
overpayment
|
||||||
param
|
param
|
||||||
|
passphrase
|
||||||
percental
|
percental
|
||||||
positionid
|
positionid
|
||||||
pre
|
pre
|
||||||
@@ -68,6 +81,8 @@ pretixpresale
|
|||||||
prometheus
|
prometheus
|
||||||
proxied
|
proxied
|
||||||
proxying
|
proxying
|
||||||
|
pseudonymize
|
||||||
|
pseudonymization
|
||||||
queryset
|
queryset
|
||||||
redemptions
|
redemptions
|
||||||
redis
|
redis
|
||||||
@@ -76,7 +91,9 @@ regex
|
|||||||
renderer
|
renderer
|
||||||
renderers
|
renderers
|
||||||
reportlab
|
reportlab
|
||||||
|
SaaS
|
||||||
screenshot
|
screenshot
|
||||||
|
selectable
|
||||||
serializers
|
serializers
|
||||||
serializers
|
serializers
|
||||||
sexualized
|
sexualized
|
||||||
@@ -91,9 +108,11 @@ subevent
|
|||||||
subevents
|
subevents
|
||||||
submodule
|
submodule
|
||||||
subpath
|
subpath
|
||||||
|
Symfony
|
||||||
systemd
|
systemd
|
||||||
testutils
|
testutils
|
||||||
timestamp
|
timestamp
|
||||||
|
tuples
|
||||||
un
|
un
|
||||||
unconfigured
|
unconfigured
|
||||||
unix
|
unix
|
||||||
@@ -102,6 +121,7 @@ untrusted
|
|||||||
username
|
username
|
||||||
url
|
url
|
||||||
versa
|
versa
|
||||||
|
versioning
|
||||||
viewset
|
viewset
|
||||||
viewsets
|
viewsets
|
||||||
webhook
|
webhook
|
||||||
|
|||||||
@@ -126,4 +126,29 @@ With the checkbox "Use custom SMTP server" you can turn using your SMTP server o
|
|||||||
button "Save and test custom SMTP connection", you can test if the connection and authentication to your SMTP server
|
button "Save and test custom SMTP connection", you can test if the connection and authentication to your SMTP server
|
||||||
succeeds, even before turning that checkbox on.
|
succeeds, even before turning that checkbox on.
|
||||||
|
|
||||||
|
Spam issues
|
||||||
|
-----------
|
||||||
|
|
||||||
|
If you use an email address of your own domain as a sender address and do not use a custom SMTP server, it is very
|
||||||
|
likely that at least some of your emails will go to the spam folders of their recipients. We **strongly recommend**
|
||||||
|
to use your organization's SMTP server in this case, making your email really come from your organization. If you don't
|
||||||
|
want that or cannot do that, you should add the pretix application server to your SPF record.
|
||||||
|
|
||||||
|
If you are using our hosted service at pretix.eu, you can add the following to your SPF record::
|
||||||
|
|
||||||
|
include:_spf.pretix.eu
|
||||||
|
|
||||||
|
A complete record could look like this::
|
||||||
|
|
||||||
|
v=spf1 a mx include:_spf.pretix.eu ~all
|
||||||
|
|
||||||
|
Make sure to read up on the `SPF specification`_. If you want to authenticate your emails with DKIM, set up a DNS TXT
|
||||||
|
record for the subdomain ``pretix._domainkey`` with the following contents::
|
||||||
|
|
||||||
|
v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXrDk6lwOWX00e2MbiiJac6huI+gnzLf9N4G1FnBv3PXq8fz3i2q1szH72OF5mAlKm3zXO4cl/uxx+lfidS1ERbX6Bn9BRstBTQUKWC4JFj8Yk9+fwT7LWehDURazLdTzfsIjJFudLLvxtOKSaOCtMhbPX05DIhziaqVCBqgz/NQIDAQAB
|
||||||
|
|
||||||
|
Then, please contact support@pretix.eu and we will enable DKIM for your domain on our mail servers.
|
||||||
|
|
||||||
|
|
||||||
.. _Sender Policy Framework: https://en.wikipedia.org/wiki/Sender_Policy_Framework
|
.. _Sender Policy Framework: https://en.wikipedia.org/wiki/Sender_Policy_Framework
|
||||||
|
.. _SPF specification: http://www.openspf.org/SPF_Record_Syntax
|
||||||
|
|||||||
@@ -100,6 +100,16 @@ taxes" at the end of the page.
|
|||||||
errors of usually up to one cent from the intended price. This is unavoidable due to the
|
errors of usually up to one cent from the intended price. This is unavoidable due to the
|
||||||
flexible nature in which prices are being calculated.
|
flexible nature in which prices are being calculated.
|
||||||
|
|
||||||
|
Custom tax rules
|
||||||
|
----------------
|
||||||
|
|
||||||
|
If you have very special requirements for the conditions in which VAT will or will not be charged, you can use the
|
||||||
|
"Custom tax rules" section instead of the options listed above. Here, you can create a set of rules consisting of
|
||||||
|
conditions (i.e. a country or a type of customer) and actions (i.e. do or do not charge VAT).
|
||||||
|
|
||||||
|
The rules will then be checked from top to bottom and the first matching rule will be used to decide if VAT will be
|
||||||
|
charged to the user.
|
||||||
|
|
||||||
Taxation of payment fees
|
Taxation of payment fees
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,12 @@ The second snippet should be embedded at the position where the widget should sh
|
|||||||
You can of course embed multiple widgets of multiple events on your page. In this case, please add the first
|
You can of course embed multiple widgets of multiple events on your page. In this case, please add the first
|
||||||
snippet only *once* and the second snippets once *for each event*.
|
snippet only *once* and the second snippets once *for each event*.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Some website builders like Jimdo have trouble with our custom HTML tag. In that case, you can use
|
||||||
|
``<div class="pretix-widget-compat" …></div>`` instead of ``<pretix-widget …></pretix-widget>`` starting with
|
||||||
|
pretix 1.14.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@@ -101,4 +107,143 @@ voucher's settings.
|
|||||||
</div>
|
</div>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
|
Disabling the voucher input
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
If you want to disable voucher input in the widget, you can pass the ``disable-vouchers`` attribute::
|
||||||
|
|
||||||
|
<pretix-widget event="https://pretix.eu/demo/democon/" disable-vouchers></pretix-widget>
|
||||||
|
|
||||||
|
pretix Button
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Instead of a product list, you can also display just a single button. When pressed, the button will add a number of
|
||||||
|
products associated with the button to the cart and will immediately proceed to checkout if the operation succeeded.
|
||||||
|
You can try out this behavior here:
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<pretix-button event="https://pretix.eu/demo/democon/" items="item_6424=1">Buy ticket!</pretix-button>
|
||||||
|
<noscript>
|
||||||
|
<div class="pretix-widget">
|
||||||
|
<div class="pretix-widget-info-message">
|
||||||
|
JavaScript is disabled in your browser. To access our ticket shop without javascript, please <a target="_blank" href="https://pretix.eu/demo/democon/">click here</a>.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</noscript>
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
You can embed the pretix Button just like the pretix Widget. Just like above, first embed the CSS and JavaScript
|
||||||
|
resources. Then, instead of the ``pretix-widget`` tag, use the ``pretix-button`` tag::
|
||||||
|
|
||||||
|
<pretix-button event="https://pretix.eu/demo/democon/" items="item_6424=1">
|
||||||
|
Buy ticket!
|
||||||
|
</pretix-button>
|
||||||
|
|
||||||
|
As you can see, the ``pretix-button`` element takes an additional ``items`` attribute that specifies the items that
|
||||||
|
should be added to the cart. The syntax of this attribute is ``item_ITEMID=1,item_ITEMID=2,variation_ITEMID_VARID=4``
|
||||||
|
where ``ITEMID`` are the internal IDs of items to be added and ``VARID`` are the internal IDs of variations of those
|
||||||
|
items, if the items have variations. If you omit the ``items`` attribute, the general start page will be presented.
|
||||||
|
|
||||||
|
Just as the widget, the button supports the optional attributes ``voucher`` and ``skip-ssl-check``.
|
||||||
|
|
||||||
|
You can style the button using the ``pretix-button`` CSS class.
|
||||||
|
|
||||||
|
Dynamically loading the widget
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
If you need to control the way or timing the widget loads, for example because you want to modify user data (see
|
||||||
|
below) dynamically via JavaScript, you can register a listener that we will call before creating the widget::
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.pretixWidgetCallback = function () {
|
||||||
|
// Will be run before we create the widget.
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
If you want, you can suppress us loading the widget and/or modify the user data passed to the widget::
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.pretixWidgetCallback = function () {
|
||||||
|
window.PretixWidget.build_widgets = false;
|
||||||
|
window.PretixWidget.widget_data["email"] = "test@example.org";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
If you then later want to trigger loading the widgets, just call ``window.PretixWidget.buildWidgets()``.
|
||||||
|
|
||||||
|
|
||||||
|
Passing user data to the widget
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
If you display the widget in a restricted area of your website and you want to pre-fill fields in the checkout process
|
||||||
|
with known user data to save your users some typing and increase conversions, you can pass additional data attributes
|
||||||
|
with that information::
|
||||||
|
|
||||||
|
<pretix-widget event="https://pretix.eu/demo/democon/"
|
||||||
|
data-attendee-name-given-name="John"
|
||||||
|
data-attendee-name-family-name="Doe"
|
||||||
|
data-invoice-address-name-given-name="John"
|
||||||
|
data-invoice-address-name-family-name="Doe"
|
||||||
|
data-email="test@example.org"
|
||||||
|
data-question-L9G8NG9M="Foobar">
|
||||||
|
</pretix-widget>
|
||||||
|
|
||||||
|
This works for the pretix Button as well. Currently, the following attributes are understood by pretix itself:
|
||||||
|
|
||||||
|
* ``data-email`` will pre-fill the order email field as well as the attendee email field (if enabled).
|
||||||
|
|
||||||
|
* ``data-question-IDENTIFIER`` will pre-fill the answer for the question with the given identifier. You can view and set
|
||||||
|
identifiers in the *Questions* section of the backend.
|
||||||
|
|
||||||
|
* Depending on the person name scheme configured in your event settings, you can pass one or more of
|
||||||
|
``data-attendee-name-full-name``, ``data-attendee-name-given-name``, ``data-attendee-name-family-name``,
|
||||||
|
``data-attendee-name-middle-name``, ``data-attendee-name-title``, ``data-attendee-name-calling-name``,
|
||||||
|
``data-attendee-name-latin-transcription``. If you don't know or don't care, you can also just pass a string as
|
||||||
|
``data-attendee-name``, which will pre-fill the last part of the name, whatever that is.
|
||||||
|
|
||||||
|
* ``data-invoice-address-FIELD`` will pre-fill the corresponding field of the invoice address. Possible values for
|
||||||
|
``FIELD`` are ``company``, ``street``, ``zipcode``, ``city`` and ``country``, as well as fields specified by the
|
||||||
|
naming scheme such as ``name-title`` or ``name-given-name`` (see above). ``country`` expects a two-character
|
||||||
|
country code.
|
||||||
|
|
||||||
|
Any configured pretix plugins might understand more data fields. For example, if the appropriate plugins on pretix
|
||||||
|
Hosted or pretix Enterprise are active, you can pass the following fields:
|
||||||
|
|
||||||
|
* If you use the campaigns plugin, you can pass a campaign ID as a value to ``data-campaign``. This way, all orders
|
||||||
|
made through this widget will be counted towards this campaign.
|
||||||
|
|
||||||
|
* If you use the tracking plugin, you can pass a Google Analytics User ID to enable cross-domain tracking. This will
|
||||||
|
require you to dynamically load the widget, like this::
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||||
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||||
|
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||||
|
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||||
|
|
||||||
|
ga('create', 'UA-XXXXXX-1', 'auto');
|
||||||
|
ga('send', 'pageview');
|
||||||
|
|
||||||
|
window.pretixWidgetCallback = function () {
|
||||||
|
window.PretixWidget.build_widgets = false;
|
||||||
|
window.addEventListener('load', function() { // Wait for GA to be loaded
|
||||||
|
if(window.ga && ga.create) {
|
||||||
|
ga(function(tracker) {
|
||||||
|
window.PretixWidget.widget_data["tracking-ga-id"] = tracker.get('clientId');
|
||||||
|
window.PretixWidget.buildWidgets()
|
||||||
|
});
|
||||||
|
} else { // Tracking is probably blocked
|
||||||
|
window.PretixWidget.buildWidgets()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
.. versionchanged:: 2.3
|
||||||
|
|
||||||
|
Data passing options have been added in pretix 2.3. If you use a self-hosted version of pretix, they only work
|
||||||
|
fully if you configured a redis server.
|
||||||
|
|
||||||
.. _Let's Encrypt: https://letsencrypt.org/
|
.. _Let's Encrypt: https://letsencrypt.org/
|
||||||
|
|||||||
@@ -51,3 +51,25 @@ If you created a product and it doesn't show up, please follow the following ste
|
|||||||
quota that is assigned to the series date that you access the shop for.
|
quota that is assigned to the series date that you access the shop for.
|
||||||
6. If the sale period has not started yet or is already over, check the "Show items outside presale period" setting of
|
6. If the sale period has not started yet or is already over, check the "Show items outside presale period" setting of
|
||||||
your event.
|
your event.
|
||||||
|
|
||||||
|
How can I revert a check-in?
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Neither our apps nor our web interface can currently undo the check-in of a tickets. We know that this is
|
||||||
|
inconvenient for some of you, but we have a good reason for it:
|
||||||
|
|
||||||
|
Our Desktop and Android apps both support an asynchronous mode in which they can scan tickets while staying
|
||||||
|
independent of their internet connection. When scanning with multiple devices, it can of course happen that two
|
||||||
|
devices scan the same ticket without knowing of the other scan. As soon as one of the devices regains connectivity, it
|
||||||
|
will upload its activity and the server marks the ticket as checked in -- regardless of the order in which the two
|
||||||
|
scans were made and uploaded (which could be two different orders).
|
||||||
|
|
||||||
|
If we'd provide a "check out" feature, it would not only be used to fix an accidental scan, but scan at entry and
|
||||||
|
exit to count the current number of people inside etc. In this case, the order of operations matters very much for them
|
||||||
|
to make sense and provide useful results. This makes implementing an asynchronous mode much more complicated.
|
||||||
|
|
||||||
|
In this trade off, we chose offline-capabilities over the check out feature. We plan on solving this problem in the
|
||||||
|
future, but we're not there yet.
|
||||||
|
|
||||||
|
If you're just *testing* the check-in capabilities and want to clean out everything for the real process, you can just
|
||||||
|
delete and re-create the check-in list.
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
.. _user-teams:
|
||||||
|
|
||||||
Teams
|
Teams
|
||||||
=====
|
=====
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ If you look into pretix' settings, you are required to fill in two keys:
|
|||||||
|
|
||||||
Unfortunately, it is not straightforward how to get those keys from PayPal's website. In order to do so, you
|
Unfortunately, it is not straightforward how to get those keys from PayPal's website. In order to do so, you
|
||||||
need to go to `developer.paypal.com`_ to link the account to your pretix event.
|
need to go to `developer.paypal.com`_ to link the account to your pretix event.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
Unfortunately, PayPal tries to confuse you by having multiple APIs with different keys. You really need to
|
||||||
|
go to https://developer.paypal.com for the API we use, not to your normal account settings!
|
||||||
|
|
||||||
Click on "Log In" in the top-right corner and log in with your PayPal account.
|
Click on "Log In" in the top-right corner and log in with your PayPal account.
|
||||||
|
|
||||||
.. image:: img/paypal2.png
|
.. image:: img/paypal2.png
|
||||||
@@ -46,8 +52,8 @@ webhooks. To create one, scroll a bit down and click "Add Webhook".
|
|||||||
.. image:: img/paypal7.png
|
.. image:: img/paypal7.png
|
||||||
:class: screenshot
|
:class: screenshot
|
||||||
|
|
||||||
Then, enter the webhook URL that you find on the pretix settings page. It should look similar to the one in the
|
Then, enter the webhook URL that you find on the pretix settings page. If you use pretix Hosted, this is always ``https://pretix.eu/_paypal/webhook/``.
|
||||||
screenshot but contain your event name. Tick the box "All events" and save.
|
Tick the box "All events" and save.
|
||||||
|
|
||||||
.. image:: img/paypal8.png
|
.. image:: img/paypal8.png
|
||||||
:class: screenshot
|
:class: screenshot
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
General settings
|
General settings
|
||||||
================
|
================
|
||||||
|
|
||||||
At "Settings" → "Pages", you can configure every aspect related to the payments you want to accept. The upper part
|
At "Settings" → "Payment", you can configure every aspect related to the payments you want to accept. The upper part
|
||||||
of the page shows a number of general settings that affect all payment methods:
|
of the page shows a number of general settings that affect all payment methods:
|
||||||
|
|
||||||
.. thumbnail:: ../../screens/event/settings_payment.png
|
.. thumbnail:: ../../screens/event/settings_payment.png
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
Stripe
|
Stripe
|
||||||
======
|
======
|
||||||
|
|
||||||
|
.. note:: If you use the Hosted version of pretix at pretix.eu, you do not need to copy API keys and create webhooks
|
||||||
|
any more. Instead, you can just click "Connect with Stripe" in pretix and everything will connect
|
||||||
|
automatically.
|
||||||
|
|
||||||
To integrate Stripe with pretix, you first need to have an active Stripe merchant account. If you do not already have a
|
To integrate Stripe with pretix, you first need to have an active Stripe merchant account. If you do not already have a
|
||||||
Stripe account, you can create one on `stripe.com`_. Then, click on "API" in the left navigation of the Stripe
|
Stripe account, you can create one on `stripe.com`_. Then, click on "API" in the left navigation of the Stripe
|
||||||
Dashboard. As you can see in the following screenshot, you will be presented with two sets of API keys, one for test
|
Dashboard. As you can see in the following screenshot, you will be presented with two sets of API keys, one for test
|
||||||
|
|||||||
6
readthedocs.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
build:
|
||||||
|
image: latest
|
||||||
|
|
||||||
|
python:
|
||||||
|
version: 3.6
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
[run]
|
|
||||||
source = pretix
|
|
||||||
omit = */migrations/*,*/urls.py,*/tests/*,*/testdummy/*,*/admin.py,pretix/wsgi.py,pretix/settings.py
|
|
||||||
|
|
||||||
[report]
|
|
||||||
exclude_lines =
|
|
||||||
pragma: no cover
|
|
||||||
def __str__
|
|
||||||
der __repr__
|
|
||||||
if settings.DEBUG
|
|
||||||
NOQA
|
|
||||||
NotImplementedError
|
|
||||||
37
src/.update-locales
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
COMPONENTS="pretix/pretix pretix/pretix-js"
|
||||||
|
DIR=pretix/locale
|
||||||
|
# Renerates .po files used for translating the plugin
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Lock Weblate
|
||||||
|
for c in $COMPONENTS; do
|
||||||
|
wlc lock $c;
|
||||||
|
done
|
||||||
|
|
||||||
|
# Push changes from Weblate to GitHub
|
||||||
|
for c in $COMPONENTS; do
|
||||||
|
wlc commit $c;
|
||||||
|
done
|
||||||
|
|
||||||
|
# Pull changes from GitHub
|
||||||
|
git pull --rebase
|
||||||
|
|
||||||
|
# Update po files itself
|
||||||
|
make localegen
|
||||||
|
|
||||||
|
# Commit changes
|
||||||
|
git add $DIR/*/*/*.po
|
||||||
|
git add $DIR/*.pot
|
||||||
|
|
||||||
|
git commit -s -m "Update po files
|
||||||
|
[CI skip]"
|
||||||
|
|
||||||
|
# Push changes
|
||||||
|
git push
|
||||||
|
|
||||||
|
# Unlock Weblate
|
||||||
|
for c in $COMPONENTS; do
|
||||||
|
wlc unlock $c;
|
||||||
|
done
|
||||||
@@ -8,6 +8,8 @@ recursive-include pretix/control/templates *
|
|||||||
recursive-include pretix/presale/templates *
|
recursive-include pretix/presale/templates *
|
||||||
recursive-include pretix/plugins/banktransfer/templates *
|
recursive-include pretix/plugins/banktransfer/templates *
|
||||||
recursive-include pretix/plugins/banktransfer/static *
|
recursive-include pretix/plugins/banktransfer/static *
|
||||||
|
recursive-include pretix/plugins/manualpayment/templates *
|
||||||
|
recursive-include pretix/plugins/manualpayment/static *
|
||||||
recursive-include pretix/plugins/paypal/templates *
|
recursive-include pretix/plugins/paypal/templates *
|
||||||
recursive-include pretix/plugins/pretixdroid/templates *
|
recursive-include pretix/plugins/pretixdroid/templates *
|
||||||
recursive-include pretix/plugins/pretixdroid/static *
|
recursive-include pretix/plugins/pretixdroid/static *
|
||||||
@@ -18,3 +20,5 @@ recursive-include pretix/plugins/stripe/templates *
|
|||||||
recursive-include pretix/plugins/stripe/static *
|
recursive-include pretix/plugins/stripe/static *
|
||||||
recursive-include pretix/plugins/ticketoutputpdf/templates *
|
recursive-include pretix/plugins/ticketoutputpdf/templates *
|
||||||
recursive-include pretix/plugins/ticketoutputpdf/static *
|
recursive-include pretix/plugins/ticketoutputpdf/static *
|
||||||
|
recursive-include pretix/plugins/badges/templates *
|
||||||
|
recursive-include pretix/plugins/badges/static *
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
all: localecompile staticfiles
|
all: localecompile staticfiles
|
||||||
production: localecompile staticfiles compress
|
production: localecompile staticfiles compress
|
||||||
|
LNGS:=`find pretix/locale/ -mindepth 1 -maxdepth 1 -type d -printf "-l %f "`
|
||||||
|
|
||||||
localecompile:
|
localecompile:
|
||||||
./manage.py compilemessages
|
./manage.py compilemessages
|
||||||
|
|
||||||
localegen:
|
localegen:
|
||||||
./manage.py makemessages --all --ignore "pretix/helpers/*"
|
./manage.py makemessages --keep-pot --ignore "pretix/helpers/*" $(LNGS)
|
||||||
./manage.py makemessages --all -d djangojs --ignore "pretix/helpers/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static.dist/*" --ignore "data/*" --ignore "build/*"
|
./manage.py makemessages --keep-pot -d djangojs --ignore "pretix/helpers/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static.dist/*" --ignore "data/*" --ignore "build/*" $(LNGS)
|
||||||
|
|
||||||
staticfiles: jsi18n
|
staticfiles: jsi18n
|
||||||
./manage.py collectstatic --noinput
|
./manage.py collectstatic --noinput
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "1.12.0"
|
__version__ = "2.3.0"
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class PretixApiConfig(AppConfig):
|
||||||
|
name = 'pretix.api'
|
||||||
|
label = 'pretixapi'
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
from . import signals, webhooks # noqa
|
||||||
|
|
||||||
|
|
||||||
|
default_app_config = 'pretix.api.PretixApiConfig'
|
||||||
|
|||||||
25
src/pretix/api/auth/device.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
from django.contrib.auth.models import AnonymousUser
|
||||||
|
from rest_framework import exceptions
|
||||||
|
from rest_framework.authentication import TokenAuthentication
|
||||||
|
|
||||||
|
from pretix.base.models import Device
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceTokenAuthentication(TokenAuthentication):
|
||||||
|
model = Device
|
||||||
|
keyword = 'Device'
|
||||||
|
|
||||||
|
def authenticate_credentials(self, key):
|
||||||
|
model = self.get_model()
|
||||||
|
try:
|
||||||
|
device = model.objects.select_related('organizer').get(api_token=key)
|
||||||
|
except model.DoesNotExist:
|
||||||
|
raise exceptions.AuthenticationFailed('Invalid token.')
|
||||||
|
|
||||||
|
if not device.initialized:
|
||||||
|
raise exceptions.AuthenticationFailed('Device has not been initialized.')
|
||||||
|
|
||||||
|
if not device.api_token:
|
||||||
|
raise exceptions.AuthenticationFailed('Device access has been revoked.')
|
||||||
|
|
||||||
|
return AnonymousUser(), device
|
||||||
@@ -1,19 +1,17 @@
|
|||||||
import time
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.auth import logout
|
|
||||||
from rest_framework.exceptions import PermissionDenied
|
|
||||||
from rest_framework.permissions import SAFE_METHODS, BasePermission
|
from rest_framework.permissions import SAFE_METHODS, BasePermission
|
||||||
|
|
||||||
from pretix.base.models import Event
|
from pretix.api.models import OAuthAccessToken
|
||||||
|
from pretix.base.models import Device, Event
|
||||||
from pretix.base.models.organizer import Organizer, TeamAPIToken
|
from pretix.base.models.organizer import Organizer, TeamAPIToken
|
||||||
|
from pretix.helpers.security import (
|
||||||
|
SessionInvalid, SessionReauthRequired, assert_session_valid,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EventPermission(BasePermission):
|
class EventPermission(BasePermission):
|
||||||
model = TeamAPIToken
|
|
||||||
|
|
||||||
def has_permission(self, request, view):
|
def has_permission(self, request, view):
|
||||||
if not request.user.is_authenticated and not isinstance(request.auth, TeamAPIToken):
|
if not request.user.is_authenticated and not isinstance(request.auth, (Device, TeamAPIToken)):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if request.method not in SAFE_METHODS and hasattr(view, 'write_permission'):
|
if request.method not in SAFE_METHODS and hasattr(view, 'write_permission'):
|
||||||
@@ -24,18 +22,15 @@ class EventPermission(BasePermission):
|
|||||||
required_permission = None
|
required_permission = None
|
||||||
|
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
# If this logic is updated, make sure to also update the logic in pretix/control/middleware.py
|
try:
|
||||||
if not settings.PRETIX_LONG_SESSIONS or not request.session.get('pretix_auth_long_session', False):
|
# If this logic is updated, make sure to also update the logic in pretix/control/middleware.py
|
||||||
last_used = request.session.get('pretix_auth_last_used', time.time())
|
assert_session_valid(request)
|
||||||
if time.time() - request.session.get('pretix_auth_login_time', time.time()) > settings.PRETIX_SESSION_TIMEOUT_ABSOLUTE:
|
except SessionInvalid:
|
||||||
logout(request)
|
return False
|
||||||
request.session['pretix_auth_login_time'] = 0
|
except SessionReauthRequired:
|
||||||
return False
|
return False
|
||||||
if time.time() - last_used > settings.PRETIX_SESSION_TIMEOUT_RELATIVE:
|
|
||||||
return False
|
|
||||||
request.session['pretix_auth_last_used'] = int(time.time())
|
|
||||||
|
|
||||||
perm_holder = (request.auth if isinstance(request.auth, TeamAPIToken)
|
perm_holder = (request.auth if isinstance(request.auth, (Device, TeamAPIToken))
|
||||||
else request.user)
|
else request.user)
|
||||||
if 'event' in request.resolver_match.kwargs and 'organizer' in request.resolver_match.kwargs:
|
if 'event' in request.resolver_match.kwargs and 'organizer' in request.resolver_match.kwargs:
|
||||||
request.event = Event.objects.filter(
|
request.event = Event.objects.filter(
|
||||||
@@ -60,19 +55,28 @@ class EventPermission(BasePermission):
|
|||||||
|
|
||||||
if required_permission and required_permission not in request.orgapermset:
|
if required_permission and required_permission not in request.orgapermset:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if isinstance(request.auth, OAuthAccessToken):
|
||||||
|
if not request.auth.allow_scopes(['write']) and request.method not in SAFE_METHODS:
|
||||||
|
return False
|
||||||
|
if not request.auth.allow_scopes(['read']) and request.method in SAFE_METHODS:
|
||||||
|
return False
|
||||||
|
if isinstance(request.auth, OAuthAccessToken) and hasattr(request, 'organizer'):
|
||||||
|
if not request.auth.organizers.filter(pk=request.organizer.pk).exists():
|
||||||
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def permission_required(required_permission):
|
class EventCRUDPermission(EventPermission):
|
||||||
def decorator(function):
|
def has_permission(self, request, view):
|
||||||
def wrapper(self, request, *args, **kw):
|
if not super(EventCRUDPermission, self).has_permission(request, view):
|
||||||
if 'event' in request.resolver_match.kwargs and 'organizer' in request.resolver_match.kwargs:
|
return False
|
||||||
if required_permission and required_permission not in request.eventpermset:
|
elif view.action == 'create' and 'can_create_events' not in request.orgapermset:
|
||||||
raise PermissionDenied('You do not have permission to perform this operation.')
|
return False
|
||||||
elif 'organizer' in request.resolver_match.kwargs:
|
elif view.action == 'destroy' and 'can_change_event_settings' not in request.eventpermset:
|
||||||
if required_permission and required_permission not in request.orgapermset:
|
return False
|
||||||
raise PermissionDenied('You do not have permission to perform this operation.')
|
elif view.action in ['update', 'partial_update'] \
|
||||||
|
and 'can_change_event_settings' not in request.eventpermset:
|
||||||
|
return False
|
||||||
|
|
||||||
return function(self, request, *args, **kw)
|
return True
|
||||||
return wrapper
|
|
||||||
return decorator
|
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ def custom_exception_handler(exc, context):
|
|||||||
if isinstance(exc, LockTimeoutException):
|
if isinstance(exc, LockTimeoutException):
|
||||||
response = Response(
|
response = Response(
|
||||||
{'detail': 'The server was too busy to process your request. Please try again.'},
|
{'detail': 'The server was too busy to process your request. Please try again.'},
|
||||||
status=status.HTTP_409_CONFLICT
|
status=status.HTTP_409_CONFLICT,
|
||||||
|
headers={
|
||||||
|
'Retry-After': 5
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|||||||
128
src/pretix/api/migrations/0001_initial.py
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.13 on 2018-06-04 11:19
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import oauth2_provider.generators
|
||||||
|
import oauth2_provider.validators
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OAuthAccessToken',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
|
('token', models.CharField(max_length=255, unique=True)),
|
||||||
|
('expires', models.DateTimeField()),
|
||||||
|
('scope', models.TextField(blank=True)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OAuthApplication',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
|
('client_type',
|
||||||
|
models.CharField(choices=[('confidential', 'Confidential'), ('public', 'Public')], max_length=32)),
|
||||||
|
('authorization_grant_type', models.CharField(
|
||||||
|
choices=[('authorization-code', 'Authorization code'), ('implicit', 'Implicit'),
|
||||||
|
('password', 'Resource owner password-based'),
|
||||||
|
('client-credentials', 'Client credentials')], max_length=32)),
|
||||||
|
('skip_authorization', models.BooleanField(default=False)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('name', models.CharField(max_length=255, verbose_name='Application name')),
|
||||||
|
('redirect_uris', models.TextField(help_text='Allowed URIs list, space separated',
|
||||||
|
validators=[oauth2_provider.validators.URIValidator],
|
||||||
|
verbose_name='Redirection URIs')),
|
||||||
|
('client_id',
|
||||||
|
models.CharField(db_index=True, default=oauth2_provider.generators.generate_client_id, max_length=100,
|
||||||
|
unique=True, verbose_name='Client ID')),
|
||||||
|
('client_secret',
|
||||||
|
models.CharField(db_index=True, default=oauth2_provider.generators.generate_client_secret,
|
||||||
|
max_length=255, verbose_name='Client secret')),
|
||||||
|
('active', models.BooleanField(default=True)),
|
||||||
|
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name='pretixapi_oauthapplication', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OAuthGrant',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
|
('code', models.CharField(max_length=255, unique=True)),
|
||||||
|
('expires', models.DateTimeField()),
|
||||||
|
('redirect_uri', models.CharField(max_length=255)),
|
||||||
|
('scope', models.TextField(blank=True)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)),
|
||||||
|
('user',
|
||||||
|
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pretixapi_oauthgrant',
|
||||||
|
to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OAuthRefreshToken',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
|
('token', models.CharField(max_length=255)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('revoked', models.DateTimeField(null=True)),
|
||||||
|
('access_token',
|
||||||
|
models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name='refresh_token', to=settings.OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL)),
|
||||||
|
('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name='pretixapi_oauthrefreshtoken', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='oauthaccesstoken',
|
||||||
|
name='application',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='oauthaccesstoken',
|
||||||
|
name='source_refresh_token',
|
||||||
|
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name='refreshed_access_token',
|
||||||
|
to=settings.OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='oauthaccesstoken',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name='pretixapi_oauthaccesstoken', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='oauthrefreshtoken',
|
||||||
|
unique_together=set([('token', 'revoked')]),
|
||||||
|
),
|
||||||
|
]
|
||||||
26
src/pretix/api/migrations/0002_auto_20180604_1120.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.13 on 2018-06-04 11:20
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pretixbase', '0001_initial'),
|
||||||
|
('pretixapi', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='oauthaccesstoken',
|
||||||
|
name='organizers',
|
||||||
|
field=models.ManyToManyField(to='pretixbase.Organizer'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='oauthgrant',
|
||||||
|
name='organizers',
|
||||||
|
field=models.ManyToManyField(to='pretixbase.Organizer'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
# Generated by Django 2.1.1 on 2018-11-07 10:46
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pretixbase', '0102_auto_20181017_0024'),
|
||||||
|
('pretixapi', '0002_auto_20180604_1120'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='WebHook',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('enabled', models.BooleanField(default=True, verbose_name='Enable webhook')),
|
||||||
|
('target_url', models.URLField(verbose_name='Target URL')),
|
||||||
|
('all_events', models.BooleanField(default=False, verbose_name='All events (including newly created ones)')),
|
||||||
|
('limit_events', models.ManyToManyField(blank=True, to='pretixbase.Event', verbose_name='Limit to events')),
|
||||||
|
('organizer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Organizer')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='WebHookCall',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('datetime', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('target_url', models.URLField()),
|
||||||
|
('is_retry', models.BooleanField(default=False)),
|
||||||
|
('execution_time', models.FloatField(null=True)),
|
||||||
|
('return_code', models.PositiveIntegerField(default=0)),
|
||||||
|
('payload', models.TextField()),
|
||||||
|
('response_body', models.TextField()),
|
||||||
|
('webhook', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pretixapi.WebHook')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='WebHookEventListener',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('action_type', models.CharField(max_length=255)),
|
||||||
|
('webhook', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pretixapi.WebHook')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='webhookcall',
|
||||||
|
name='success',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='webhook',
|
||||||
|
name='all_events',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='All events (including newly created ones)'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='webhook',
|
||||||
|
name='organizer',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='webhooks', to='pretixbase.Organizer'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='webhookcall',
|
||||||
|
name='webhook',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='calls', to='pretixapi.WebHook'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='webhookeventlistener',
|
||||||
|
name='webhook',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='listeners', to='pretixapi.WebHook'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='webhookcall',
|
||||||
|
name='action_type',
|
||||||
|
field=models.CharField(default='', max_length=255),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
||||||
0
src/pretix/api/migrations/__init__.py
Normal file
108
src/pretix/api/models.py
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.timezone import now
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from oauth2_provider.generators import (
|
||||||
|
generate_client_id, generate_client_secret,
|
||||||
|
)
|
||||||
|
from oauth2_provider.models import (
|
||||||
|
AbstractAccessToken, AbstractApplication, AbstractGrant,
|
||||||
|
AbstractRefreshToken,
|
||||||
|
)
|
||||||
|
from oauth2_provider.validators import URIValidator
|
||||||
|
|
||||||
|
|
||||||
|
class OAuthApplication(AbstractApplication):
|
||||||
|
name = models.CharField(verbose_name=_("Application name"), max_length=255, blank=False)
|
||||||
|
redirect_uris = models.TextField(
|
||||||
|
blank=False, validators=[URIValidator],
|
||||||
|
verbose_name=_("Redirection URIs"),
|
||||||
|
help_text=_("Allowed URIs list, space separated")
|
||||||
|
)
|
||||||
|
client_id = models.CharField(
|
||||||
|
verbose_name=_("Client ID"),
|
||||||
|
max_length=100, unique=True, default=generate_client_id, db_index=True
|
||||||
|
)
|
||||||
|
client_secret = models.CharField(
|
||||||
|
verbose_name=_("Client secret"),
|
||||||
|
max_length=255, blank=False, default=generate_client_secret, db_index=True
|
||||||
|
)
|
||||||
|
active = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse("control:user.settings.oauth.app", kwargs={'pk': self.id})
|
||||||
|
|
||||||
|
def is_usable(self, request):
|
||||||
|
return self.active and super().is_usable(request)
|
||||||
|
|
||||||
|
|
||||||
|
class OAuthGrant(AbstractGrant):
|
||||||
|
application = models.ForeignKey(
|
||||||
|
OAuthApplication, on_delete=models.CASCADE
|
||||||
|
)
|
||||||
|
organizers = models.ManyToManyField('pretixbase.Organizer')
|
||||||
|
|
||||||
|
|
||||||
|
class OAuthAccessToken(AbstractAccessToken):
|
||||||
|
source_refresh_token = models.OneToOneField(
|
||||||
|
# unique=True implied by the OneToOneField
|
||||||
|
'OAuthRefreshToken', on_delete=models.SET_NULL, blank=True, null=True,
|
||||||
|
related_name="refreshed_access_token"
|
||||||
|
)
|
||||||
|
application = models.ForeignKey(
|
||||||
|
OAuthApplication, on_delete=models.CASCADE, blank=True, null=True,
|
||||||
|
)
|
||||||
|
organizers = models.ManyToManyField('pretixbase.Organizer')
|
||||||
|
|
||||||
|
def revoke(self):
|
||||||
|
self.expires = now() - timedelta(hours=1)
|
||||||
|
self.save(update_fields=['expires'])
|
||||||
|
|
||||||
|
|
||||||
|
class OAuthRefreshToken(AbstractRefreshToken):
|
||||||
|
application = models.ForeignKey(
|
||||||
|
OAuthApplication, on_delete=models.CASCADE)
|
||||||
|
access_token = models.OneToOneField(
|
||||||
|
OAuthAccessToken, on_delete=models.SET_NULL, blank=True, null=True,
|
||||||
|
related_name="refresh_token"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WebHook(models.Model):
|
||||||
|
organizer = models.ForeignKey('pretixbase.Organizer', on_delete=models.CASCADE, related_name='webhooks')
|
||||||
|
enabled = models.BooleanField(default=True, verbose_name=_("Enable webhook"))
|
||||||
|
target_url = models.URLField(verbose_name=_("Target URL"))
|
||||||
|
all_events = models.BooleanField(default=True, verbose_name=_("All events (including newly created ones)"))
|
||||||
|
limit_events = models.ManyToManyField('pretixbase.Event', verbose_name=_("Limit to events"), blank=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def action_types(self):
|
||||||
|
return [
|
||||||
|
l.action_type for l in self.listeners.all()
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class WebHookEventListener(models.Model):
|
||||||
|
webhook = models.ForeignKey('WebHook', on_delete=models.CASCADE, related_name='listeners')
|
||||||
|
action_type = models.CharField(max_length=255)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ("action_type",)
|
||||||
|
|
||||||
|
|
||||||
|
class WebHookCall(models.Model):
|
||||||
|
webhook = models.ForeignKey('WebHook', on_delete=models.CASCADE, related_name='calls')
|
||||||
|
datetime = models.DateTimeField(auto_now_add=True)
|
||||||
|
target_url = models.URLField()
|
||||||
|
action_type = models.CharField(max_length=255)
|
||||||
|
is_retry = models.BooleanField(default=False)
|
||||||
|
execution_time = models.FloatField(null=True)
|
||||||
|
return_code = models.PositiveIntegerField(default=0)
|
||||||
|
success = models.BooleanField(default=False)
|
||||||
|
payload = models.TextField()
|
||||||
|
response_body = models.TextField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ("-datetime",)
|
||||||