Compare commits
1002 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f671b1b35 | ||
|
|
d56882cc20 | ||
|
|
888734dcdf | ||
|
|
e81da57f61 | ||
|
|
05d1b141b2 | ||
|
|
b7e5828adc | ||
|
|
2ee4c7ad72 | ||
|
|
77ab11bce8 | ||
|
|
cd8edbaa1f | ||
|
|
5e21048457 | ||
|
|
c0c093d16c | ||
|
|
117153fad7 | ||
|
|
5ae666b0e0 | ||
|
|
afa03789b8 | ||
|
|
44b0073834 | ||
|
|
254636167d | ||
|
|
29e82ec845 | ||
|
|
c3a0bd8625 | ||
|
|
32a4e8e9b9 | ||
|
|
482cbe71c7 | ||
|
|
7dbcd9ca89 | ||
|
|
a1f2dcab8f | ||
|
|
21417c6598 | ||
|
|
f1a272f407 | ||
|
|
064f797023 | ||
|
|
26c2020b12 | ||
|
|
af1a483e64 | ||
|
|
f517cb5e93 | ||
|
|
c8a8914ea7 | ||
|
|
4f834c6bf8 | ||
|
|
d588843121 | ||
|
|
51c7770b10 | ||
|
|
18ff5d29b0 | ||
|
|
e31098eaa5 | ||
|
|
0873050b32 | ||
|
|
8df9ee7b2d | ||
|
|
a9ef557ca4 | ||
|
|
689fa008fb | ||
|
|
1a4ce65e6c | ||
|
|
f6dff04cfe | ||
|
|
a73c391c2c | ||
|
|
e88c7ca4b2 | ||
|
|
0cd8446735 | ||
|
|
6a3cfced9a | ||
|
|
0586d7aa7d | ||
|
|
3736755a12 | ||
|
|
d727fc8e24 | ||
|
|
50b0303ab3 | ||
|
|
6bc6e7bb61 | ||
|
|
cf2eb3e482 | ||
|
|
4aa5500593 | ||
|
|
c7432fb6e6 | ||
|
|
f895443e33 | ||
|
|
32cebb56ff | ||
|
|
4a6b01d094 | ||
|
|
f8295f7577 | ||
|
|
a2fa99d06f | ||
|
|
c508ac6dee | ||
|
|
4900fa799f | ||
|
|
135b1bcdd7 | ||
|
|
25f5d68b74 | ||
|
|
1ed867ce0c | ||
|
|
5ef5f09a5c | ||
|
|
bc97d3d6f2 | ||
|
|
b21a56fdf7 | ||
|
|
97f2a9b484 | ||
|
|
4ef6801230 | ||
|
|
8e21a91b2c | ||
|
|
38a1a38c0c | ||
|
|
2b7785d101 | ||
|
|
2cf9d35fed | ||
|
|
4e1f9c7280 | ||
|
|
f45d5a0421 | ||
|
|
39586d3cdb | ||
|
|
5e66e3ee78 | ||
|
|
a284f6c066 | ||
|
|
fdc0ad8a3c | ||
|
|
fa01b353ef | ||
|
|
dac3308441 | ||
|
|
11d8fc71e8 | ||
|
|
771bbaedbc | ||
|
|
040833ead8 | ||
|
|
7c2acb4324 | ||
|
|
88fe797007 | ||
|
|
5825d7e64f | ||
|
|
97ff49f2bf | ||
|
|
ccada2e8c9 | ||
|
|
1d8bcd6e16 | ||
|
|
1b58094e6f | ||
|
|
1437bfe0c9 | ||
|
|
171da32b00 | ||
|
|
a3edbf4fef | ||
|
|
8ab4ec8d54 | ||
|
|
f8365abf1b | ||
|
|
078e8dcc51 | ||
|
|
b1a1575122 | ||
|
|
201a8e1053 | ||
|
|
ff4c1a1c01 | ||
|
|
7c449e0d30 | ||
|
|
2ca526bd22 | ||
|
|
48f929f3ab | ||
|
|
f2bec90663 | ||
|
|
1c7445dc7f | ||
|
|
78630cafa2 | ||
|
|
0a40dd43cb | ||
|
|
32fb72a5bb | ||
|
|
cbb1c84be7 | ||
|
|
145e1db05d | ||
|
|
4115045c15 | ||
|
|
d0744f3702 | ||
|
|
e7caaf57a9 | ||
|
|
fba0847f0f | ||
|
|
566c6ba3a9 | ||
|
|
b860959fea | ||
|
|
9e4e4b231c | ||
|
|
b8421ddc0c | ||
|
|
9165b25fd6 | ||
|
|
f615abc9b1 | ||
|
|
9237dd67da | ||
|
|
e1347d5a39 | ||
|
|
ca852e8cb2 | ||
|
|
d0da3d7962 | ||
|
|
103966d36e | ||
|
|
1ce2dc8e6d | ||
|
|
2f4f075441 | ||
|
|
50972b98a1 | ||
|
|
874c001d3b | ||
|
|
63519a8f54 | ||
|
|
63a0336e98 | ||
|
|
ae51836187 | ||
|
|
888e756c6e | ||
|
|
a8d2e09952 | ||
|
|
23d2ef5027 | ||
|
|
3735a28b0c | ||
|
|
112defe846 | ||
|
|
804f3ced95 | ||
|
|
209b02b069 | ||
|
|
a5918059a8 | ||
|
|
c32dfea356 | ||
|
|
3b2061e54a | ||
|
|
61673907ca | ||
|
|
1e72301cdf | ||
|
|
055d1b9ea1 | ||
|
|
8d968b7690 | ||
|
|
2989ec5100 | ||
|
|
c4d7df8bba | ||
|
|
d6cd5b1f0d | ||
|
|
e9b3f3f060 | ||
|
|
ed80c30c74 | ||
|
|
3343adb952 | ||
|
|
a8aaa6deea | ||
|
|
3c8fa72dc8 | ||
|
|
91c4f4f980 | ||
|
|
b2406b3696 | ||
|
|
b8504ffb25 | ||
|
|
0294c02aed | ||
|
|
31680dcdea | ||
|
|
d91b69c465 | ||
|
|
9c0c606b20 | ||
|
|
6791352ce8 | ||
|
|
d17d227fad | ||
|
|
c175476e74 | ||
|
|
f0ff1c7c99 | ||
|
|
d0d498e9c7 | ||
|
|
da2a262208 | ||
|
|
2f86a39e38 | ||
|
|
408b66590a | ||
|
|
663cae4788 | ||
|
|
3a66860ab2 | ||
|
|
9abb74abf7 | ||
|
|
bf47f69258 | ||
|
|
0b57e45ee4 | ||
|
|
bf942bf015 | ||
|
|
b2ae5da62a | ||
|
|
858b1afa54 | ||
|
|
bc83153b60 | ||
|
|
1997153549 | ||
|
|
b479013f00 | ||
|
|
211b960440 | ||
|
|
7157f70790 | ||
|
|
84611aff2c | ||
|
|
bd4dd45f8e | ||
|
|
f190ad3576 | ||
|
|
9813216ea9 | ||
|
|
d5e88cc1a9 | ||
|
|
5dc7caf356 | ||
|
|
a8a21e119b | ||
|
|
2c9d45c59c | ||
|
|
d176633804 | ||
|
|
f65594be43 | ||
|
|
f0466f885f | ||
|
|
309a8cde95 | ||
|
|
61f0e8ea9f | ||
|
|
af00eb9cc4 | ||
|
|
61e8a4d6ee | ||
|
|
55a273d982 | ||
|
|
0993223e5c | ||
|
|
08a38b03b9 | ||
|
|
b79ae00603 | ||
|
|
c96985be0c | ||
|
|
36e9acc637 | ||
|
|
e541713282 | ||
|
|
5d57bfa438 | ||
|
|
c5bcd1e341 | ||
|
|
cfb5540e64 | ||
|
|
38d1794ee2 | ||
|
|
8feed39fb9 | ||
|
|
3140f9c3b8 | ||
|
|
e13c6fa955 | ||
|
|
b201168bd0 | ||
|
|
e5e675c14a | ||
|
|
9589f0798f | ||
|
|
e1c35b5220 | ||
|
|
f22f56e73c | ||
|
|
61280e36c4 | ||
|
|
3b4d5afd41 | ||
|
|
3a3026cd63 | ||
|
|
2a1f387bcc | ||
|
|
6f8b8d0fb8 | ||
|
|
8a81d0b274 | ||
|
|
e2c366f536 | ||
|
|
4415f9db48 | ||
|
|
f6ff308dc4 | ||
|
|
546011d8a9 | ||
|
|
60c0f0fb08 | ||
|
|
4b6f451022 | ||
|
|
3fb2bcfac7 | ||
|
|
66c820c863 | ||
|
|
09f57e9a4c | ||
|
|
6f748f1adb | ||
|
|
893772ebd8 | ||
|
|
b987cb8dc4 | ||
|
|
aa322b2cf2 | ||
|
|
4c68f64829 | ||
|
|
cc360191b5 | ||
|
|
1d06117619 | ||
|
|
446a4e1d8a | ||
|
|
428edb05c4 | ||
|
|
21708bcc1c | ||
|
|
23d605c5aa | ||
|
|
b4ffa22885 | ||
|
|
d2bc4b72a0 | ||
|
|
27c352c2ff | ||
|
|
19a354863f | ||
|
|
33f4a1322d | ||
|
|
563f98e2fb | ||
|
|
1e4b94c508 | ||
|
|
cd8d1c7ace | ||
|
|
2ea545f0cf | ||
|
|
e8a1cdafba | ||
|
|
1f8b5d8e1a | ||
|
|
793dfe96b9 | ||
|
|
6533af9c0e | ||
|
|
87ead2f89b | ||
|
|
28e3928094 | ||
|
|
dc41372acb | ||
|
|
6a9ea638d5 | ||
|
|
2a7651eb9c | ||
|
|
9021d5352d | ||
|
|
dc5c9ee3a0 | ||
|
|
079623f40a | ||
|
|
b59ba230de | ||
|
|
6826e44072 | ||
|
|
878fd4ab92 | ||
|
|
abfb1868ab | ||
|
|
7319a8e90d | ||
|
|
d2be893a5a | ||
|
|
cc8b636248 | ||
|
|
a103665bd9 | ||
|
|
0d1603b467 | ||
|
|
d6a7b8c319 | ||
|
|
f5ad7a163a | ||
|
|
d433351dbd | ||
|
|
3601ef90bc | ||
|
|
76b6c736e1 | ||
|
|
a194a64845 | ||
|
|
b00d8584f3 | ||
|
|
39d075b999 | ||
|
|
e57a5741e5 | ||
|
|
a6a9ab15b7 | ||
|
|
4280a59c77 | ||
|
|
3259e34e27 | ||
|
|
3132d35e27 | ||
|
|
7376246592 | ||
|
|
69ce6d78da | ||
|
|
b43e8c5db0 | ||
|
|
e348bdcc21 | ||
|
|
039c3ec110 | ||
|
|
f98ef62ad3 | ||
|
|
17ebcc6925 | ||
|
|
6347a2febf | ||
|
|
2ef8a1e35a | ||
|
|
df11478905 | ||
|
|
e7d2bdb45a | ||
|
|
1564fcaeb3 | ||
|
|
cf38e8d520 | ||
|
|
bb9f28fb08 | ||
|
|
d8a28acfa8 | ||
|
|
b0eccdd12e | ||
|
|
1427fb4078 | ||
|
|
1812be2e16 | ||
|
|
db87179576 | ||
|
|
0d8d41ce7e | ||
|
|
a2f95f6cea | ||
|
|
fa9bd6191c | ||
|
|
bc9de859c4 | ||
|
|
73d04262ad | ||
|
|
6e7d547da5 | ||
|
|
6060e4cb27 | ||
|
|
b5ce54c7ae | ||
|
|
492a1308cc | ||
|
|
147a4f8624 | ||
|
|
eac982398e | ||
|
|
9afd51b7dd | ||
|
|
dd810a3593 | ||
|
|
75a4c52071 | ||
|
|
2707af1d45 | ||
|
|
b7d49db536 | ||
|
|
3a98c6ad53 | ||
|
|
79f3de8042 | ||
|
|
81024e4753 | ||
|
|
e2c333ac02 | ||
|
|
1bdacb9bda | ||
|
|
2fa680cdf8 | ||
|
|
3a17a8a001 | ||
|
|
afbb014cbd | ||
|
|
c75c67ec86 | ||
|
|
e8b72f186f | ||
|
|
a777032894 | ||
|
|
624e25c077 | ||
|
|
34d0b89376 | ||
|
|
fcd3b0d2cb | ||
|
|
5512022a10 | ||
|
|
28e31fee72 | ||
|
|
779a60a42c | ||
|
|
ba29898d97 | ||
|
|
a636c65e34 | ||
|
|
ff3e137c78 | ||
|
|
8c4e0fa7b2 | ||
|
|
3ae1c26a07 | ||
|
|
7a6f2392fa | ||
|
|
07949525ef | ||
|
|
3b9fdf3ffd | ||
|
|
2694c0d5bd | ||
|
|
4f8b5b9f85 | ||
|
|
e75cca4512 | ||
|
|
68e7c41fdc | ||
|
|
735e385537 | ||
|
|
c995f797fe | ||
|
|
54162b52cc | ||
|
|
66fc06895a | ||
|
|
85b7a490eb | ||
|
|
4b0a257246 | ||
|
|
29f1af7eb4 | ||
|
|
ba79b4446c | ||
|
|
40b6afff2d | ||
|
|
a664b627f6 | ||
|
|
3a4657e368 | ||
|
|
4cb565f1cc | ||
|
|
31ca363982 | ||
|
|
4a3588e965 | ||
|
|
11600930b7 | ||
|
|
4bb0d3994f | ||
|
|
5fc4f3adad | ||
|
|
41a0ba5780 | ||
|
|
f7a81d70ac | ||
|
|
baf4664073 | ||
|
|
48700a93e3 | ||
|
|
49d09a349f | ||
|
|
31bc4c6532 | ||
|
|
aa89251a3b | ||
|
|
057846e250 | ||
|
|
5a5e3b589c | ||
|
|
270dd22f08 | ||
|
|
beec04a1bc | ||
|
|
60bfb1c3b8 | ||
|
|
98676bea69 | ||
|
|
8589fdefda | ||
|
|
8ba25e8602 | ||
|
|
de43e21ebc | ||
|
|
90ba15f9bd | ||
|
|
b9f9f15d77 | ||
|
|
cac5266c79 | ||
|
|
420afc6838 | ||
|
|
4e3c4e9c0c | ||
|
|
2c27812ba1 | ||
|
|
2f57a983cd | ||
|
|
91c4ef226b | ||
|
|
38b52bf67b | ||
|
|
281985970d | ||
|
|
03f28a4804 | ||
|
|
c0acba94c6 | ||
|
|
8d550d411e | ||
|
|
e8064371b0 | ||
|
|
166bd31506 | ||
|
|
4055908bc8 | ||
|
|
d68bb8c292 | ||
|
|
c8be171f24 | ||
|
|
43eafd4b9f | ||
|
|
ce1c99b01c | ||
|
|
4b2bc36f89 | ||
|
|
58587525e5 | ||
|
|
df76f60e7f | ||
|
|
2c66ad8689 | ||
|
|
6016e2346c | ||
|
|
56cdd943fd | ||
|
|
6d02f7d3ac | ||
|
|
18f8790062 | ||
|
|
854b63ec34 | ||
|
|
beccbbd3e8 | ||
|
|
91d0d84c65 | ||
|
|
7dd67a8d39 | ||
|
|
ad43cbc808 | ||
|
|
2b2a562d83 | ||
|
|
d9e7696384 | ||
|
|
01bc1fef1e | ||
|
|
567d8c19bf | ||
|
|
b722bed24f | ||
|
|
0af66d68a7 | ||
|
|
f3b7dcff57 | ||
|
|
67331eb975 | ||
|
|
d9ef803d20 | ||
|
|
9fd171b26d | ||
|
|
91e33d1c1c | ||
|
|
601e69ab0d | ||
|
|
81aa74bbbe | ||
|
|
e84b7a9c58 | ||
|
|
b6b0ffb674 | ||
|
|
62bd2db5f9 | ||
|
|
74342a4dea | ||
|
|
c78d77446a | ||
|
|
6eb4fc04b1 | ||
|
|
f1b00e85fc | ||
|
|
7c73f2c1d9 | ||
|
|
704e543c7e | ||
|
|
fcaa1454dc | ||
|
|
9d388f7a89 | ||
|
|
918062de2f | ||
|
|
375738d99c | ||
|
|
1c37ec3902 | ||
|
|
7eb804daf9 | ||
|
|
d99a67ca95 | ||
|
|
3d5ed93fce | ||
|
|
b80006be45 | ||
|
|
70edb0ba38 | ||
|
|
a1fe17d05d | ||
|
|
7083225abd | ||
|
|
af6479bf34 | ||
|
|
44c12325b4 | ||
|
|
f2bf2399fa | ||
|
|
7fe2fece6c | ||
|
|
bf0fe090ab | ||
|
|
397dba4a30 | ||
|
|
593d6a596a | ||
|
|
82a6db8b4f | ||
|
|
28aa3dd17b | ||
|
|
3d4d807370 | ||
|
|
c18cc23c71 | ||
|
|
7ee374ea1a | ||
|
|
20bbda95c6 | ||
|
|
67fecf6c2c | ||
|
|
20d251fd5d | ||
|
|
982b3f0582 | ||
|
|
200aa2e1c2 | ||
|
|
734e905c18 | ||
|
|
5ce223e5f4 | ||
|
|
6a17d3db98 | ||
|
|
f27c5ca6f0 | ||
|
|
ac355b58d4 | ||
|
|
e1a5547fea | ||
|
|
dd8c1d7a9e | ||
|
|
4913441017 | ||
|
|
16b7edbc97 | ||
|
|
5bfde9da91 | ||
|
|
52acfcb0e3 | ||
|
|
8813bc7695 | ||
|
|
1dfe2b8929 | ||
|
|
afb158fb50 | ||
|
|
78edac684e | ||
|
|
46f4bc2a07 | ||
|
|
f361a44cca | ||
|
|
47c37e0153 | ||
|
|
5df3b35189 | ||
|
|
1e4a68f76e | ||
|
|
b3832e21c9 | ||
|
|
18489faceb | ||
|
|
69d328d15e | ||
|
|
738e8ab9b6 | ||
|
|
e7349fea9a | ||
|
|
51a6790f26 | ||
|
|
515f20372e | ||
|
|
12fbe8368f | ||
|
|
55b4dfb309 | ||
|
|
fb7bb76674 | ||
|
|
e46b942259 | ||
|
|
e4affd4bf9 | ||
|
|
16398d3438 | ||
|
|
e8503232ba | ||
|
|
bf48fdf189 | ||
|
|
834f4c2700 | ||
|
|
e297eb9090 | ||
|
|
1f394bef72 | ||
|
|
1cdd5b96b3 | ||
|
|
2d14ab1217 | ||
|
|
8bdbd0dd41 | ||
|
|
800be4c9a0 | ||
|
|
b99ec060ca | ||
|
|
d5f4c030b4 | ||
|
|
4de92e3924 | ||
|
|
3b70560e25 | ||
|
|
d88c89fb05 | ||
|
|
24d6d8e5f9 | ||
|
|
9a9b28113a | ||
|
|
ec84ffbee2 | ||
|
|
3c1dfb88df | ||
|
|
4a6b79b231 | ||
|
|
9e2ed56ef0 | ||
|
|
9e324be057 | ||
|
|
b7f47c9bb1 | ||
|
|
8a997c82be | ||
|
|
3dcd8a24cb | ||
|
|
203ce2c2f9 | ||
|
|
42e42175db | ||
|
|
6f84c0a869 | ||
|
|
a4f2c5abd4 | ||
|
|
6b06fe4481 | ||
|
|
1fc856faf5 | ||
|
|
6e45fd67ce | ||
|
|
39415d4eed | ||
|
|
801b20ae75 | ||
|
|
f564402565 | ||
|
|
0156812af3 | ||
|
|
cf54b336d1 | ||
|
|
096303a93d | ||
|
|
e1c6d9dd7d | ||
|
|
ed80d398ea | ||
|
|
b4ce6ff4b0 | ||
|
|
ec91757257 | ||
|
|
b99f6b16af | ||
|
|
9dfb84656d | ||
|
|
629e63e123 | ||
|
|
b4131800c9 | ||
|
|
aee95eb041 | ||
|
|
0461c2109b | ||
|
|
9e9aa6485c | ||
|
|
0651936856 | ||
|
|
1b4b5707bf | ||
|
|
89d56a0886 | ||
|
|
1482fded9a | ||
|
|
6620a4f682 | ||
|
|
df934dfeff | ||
|
|
485991bd48 | ||
|
|
1d4d30ce8f | ||
|
|
6c00c2a113 | ||
|
|
57b5f17bf4 | ||
|
|
bb3080aef7 | ||
|
|
f4a2dff892 | ||
|
|
fa7f234b48 | ||
|
|
6f05fe80ae | ||
|
|
a703e0582d | ||
|
|
ab432a43dc | ||
|
|
839e3f9dae | ||
|
|
9c6af860d4 | ||
|
|
845b0b3709 | ||
|
|
85596bbba6 | ||
|
|
9ea2777555 | ||
|
|
c7eb929176 | ||
|
|
42baf2e27e | ||
|
|
dade960615 | ||
|
|
80de7a720f | ||
|
|
175642c07b | ||
|
|
29405bbc0e | ||
|
|
60f295ba9f | ||
|
|
2b1a33efba | ||
|
|
f5191dc7c8 | ||
|
|
e597b4c66b | ||
|
|
50cf34ac2e | ||
|
|
b3a7c16f8e | ||
|
|
647cac3612 | ||
|
|
5a7e8a980b | ||
|
|
45f70114e6 | ||
|
|
f442552858 | ||
|
|
483e8934c9 | ||
|
|
dc9898e355 | ||
|
|
2b71144015 | ||
|
|
9b17b5c08c | ||
|
|
647db5122e | ||
|
|
ad98b29398 | ||
|
|
ba1a8284ac | ||
|
|
26dbebb380 | ||
|
|
f14e187545 | ||
|
|
8fc4f7f806 | ||
|
|
24aa475640 | ||
|
|
0bfc589d47 | ||
|
|
97f3178005 | ||
|
|
73f418bb5c | ||
|
|
7e5eeef122 | ||
|
|
aee86c6136 | ||
|
|
c9795133b3 | ||
|
|
561c43c564 | ||
|
|
f5535f613a | ||
|
|
2e9a4c7d71 | ||
|
|
966c3c6471 | ||
|
|
c75054f562 | ||
|
|
fd0629e932 | ||
|
|
bddb65a7a1 | ||
|
|
8e23a24f34 | ||
|
|
82f0beffce | ||
|
|
f225ad349d | ||
|
|
7e69fcbc7d | ||
|
|
cbf43fa0d8 | ||
|
|
bac2a0a0bf | ||
|
|
08924a1934 | ||
|
|
cfca6d67f1 | ||
|
|
a9b83c8798 | ||
|
|
96a9b75558 | ||
|
|
d03bda9e87 | ||
|
|
08b3e8f18f | ||
|
|
40487347e3 | ||
|
|
56c16d5c16 | ||
|
|
0e5cff1923 | ||
|
|
99b9afc81c | ||
|
|
4072e7ee06 | ||
|
|
85221e6109 | ||
|
|
7e7b0589ef | ||
|
|
c1e4515562 | ||
|
|
c9fda13e61 | ||
|
|
2db80d2af8 | ||
|
|
76077c539d | ||
|
|
a5c8ea4e2e | ||
|
|
fa8a6b769b | ||
|
|
8422d0dfda | ||
|
|
fc24485455 | ||
|
|
407bc35a31 | ||
|
|
26b13a81c3 | ||
|
|
d65e856ada | ||
|
|
c0150ae846 | ||
|
|
cc4dc59aa9 | ||
|
|
dca90c4aa9 | ||
|
|
500fb1c5c4 | ||
|
|
6fab8bfbef | ||
|
|
b159e148db | ||
|
|
7cfb2f7f02 | ||
|
|
ee58471aed | ||
|
|
48aa27ce70 | ||
|
|
13318ff360 | ||
|
|
964cbe1899 | ||
|
|
d15c48429b | ||
|
|
6e57040aaf | ||
|
|
756981172f | ||
|
|
6898d6151b | ||
|
|
a9b2f7f3f3 | ||
|
|
e81ba84ca7 | ||
|
|
e2814b5404 | ||
|
|
eaa4858580 | ||
|
|
652bd4ab23 | ||
|
|
e2662336b1 | ||
|
|
d7b669d404 | ||
|
|
760dee370f | ||
|
|
f710b8c4f3 | ||
|
|
4e773c7152 | ||
|
|
160ab54b85 | ||
|
|
7841f2a86b | ||
|
|
ee9cd24e10 | ||
|
|
eb4f9572b8 | ||
|
|
a59b14bd8c | ||
|
|
86890a2de4 | ||
|
|
03c8445a6d | ||
|
|
c5854afb37 | ||
|
|
0ecd9ff0cb | ||
|
|
7397d63073 | ||
|
|
4656381205 | ||
|
|
c286e757b3 | ||
|
|
3ca3f533d0 | ||
|
|
c38470c7b2 | ||
|
|
6dffeeb06f | ||
|
|
c8ecc168e2 | ||
|
|
ff33453736 | ||
|
|
a657c584fc | ||
|
|
414679d86a | ||
|
|
ac4e5985d9 | ||
|
|
99fea79710 | ||
|
|
417e83cf71 | ||
|
|
1a05fe448c | ||
|
|
28846547af | ||
|
|
7fd4b22180 | ||
|
|
5681a2793d | ||
|
|
3f538cb818 | ||
|
|
9dc11b2b83 | ||
|
|
bef02062e7 | ||
|
|
9a393fa974 | ||
|
|
72d1099085 | ||
|
|
5591eeafca | ||
|
|
0a6349e323 | ||
|
|
dbb3c50222 | ||
|
|
c97cbeb6fd | ||
|
|
43fb317812 | ||
|
|
89fca20a44 | ||
|
|
8327b5aae5 | ||
|
|
8451233f95 | ||
|
|
39438b8460 | ||
|
|
d887b6bdc5 | ||
|
|
671077669e | ||
|
|
a0efa25616 | ||
|
|
bf142ff746 | ||
|
|
c8b00206d9 | ||
|
|
0e4eb906f2 | ||
|
|
d54d511133 | ||
|
|
586cd4d6af | ||
|
|
1e6295a788 | ||
|
|
42af22c2a1 | ||
|
|
e76f7c88a5 | ||
|
|
f8af99b058 | ||
|
|
3adf44a241 | ||
|
|
d07bb932ca | ||
|
|
609b871fa2 | ||
|
|
7c7b546812 | ||
|
|
20e5dfbb4a | ||
|
|
d7c9d9f55b | ||
|
|
5e42a835a1 | ||
|
|
7df50f9bf9 | ||
|
|
c5de01bfc4 | ||
|
|
2deed8b146 | ||
|
|
baa6258bba | ||
|
|
ef4e6d32a9 | ||
|
|
df3a83634f | ||
|
|
c67a68f9d8 | ||
|
|
8591d97fa5 | ||
|
|
d4f3bbd91c | ||
|
|
dd5ed218a5 | ||
|
|
d720509849 | ||
|
|
5549926d2c | ||
|
|
50dd65ef92 | ||
|
|
ae44727fb9 | ||
|
|
07c3f757e6 | ||
|
|
65b0ad7f08 | ||
|
|
433a52232b | ||
|
|
0ba508a87e | ||
|
|
e835cbe0b1 | ||
|
|
ed8abd94e6 | ||
|
|
6bf7d82047 | ||
|
|
01d07bcb9a | ||
|
|
1fb1eaab50 | ||
|
|
a7ecadaa52 | ||
|
|
c7c34188e1 | ||
|
|
5afabee1f2 | ||
|
|
c62707cd51 | ||
|
|
1e093e1eee | ||
|
|
e4ee0f89eb | ||
|
|
b00517ec20 | ||
|
|
a2024b4f64 | ||
|
|
0463982b5b | ||
|
|
4efc2cf71c | ||
|
|
cd0e172708 | ||
|
|
71c49bc5f2 | ||
|
|
f1840a5f6c | ||
|
|
b80d263e7f | ||
|
|
8f30c3bfef | ||
|
|
7220ff7a8a | ||
|
|
e910d8938f | ||
|
|
be1da8507a | ||
|
|
13add5885d | ||
|
|
aaab8b036c | ||
|
|
fd4319de06 | ||
|
|
e5d9335596 | ||
|
|
d70d33fb27 | ||
|
|
932116e953 | ||
|
|
5c3cfa4c93 | ||
|
|
2e6af97506 | ||
|
|
e2c5d93751 | ||
|
|
2a8f564500 | ||
|
|
a09570c78d | ||
|
|
546e6e56f1 | ||
|
|
961787d681 | ||
|
|
ec6453bb5f | ||
|
|
9e091af67e | ||
|
|
4119eec796 | ||
|
|
14e5d0977f | ||
|
|
de48f4417a | ||
|
|
b5fd917dda | ||
|
|
4fbddeeb46 | ||
|
|
3a71afbd37 | ||
|
|
fdc6aeb47a | ||
|
|
c54da719a1 | ||
|
|
ba79fd42db | ||
|
|
ffe8e17ac0 | ||
|
|
da7c0c984c | ||
|
|
b59bb0bbc2 | ||
|
|
066b593d8f | ||
|
|
23f4555ff6 | ||
|
|
05288ee08c | ||
|
|
7bd60b5a3d | ||
|
|
0e093bf15e | ||
|
|
210488ba4e | ||
|
|
189bc21e48 | ||
|
|
30ec146298 | ||
|
|
2e946a0aac | ||
|
|
8764a11b1d | ||
|
|
5eea398e43 | ||
|
|
35f35e6f42 | ||
|
|
5eb03b675e | ||
|
|
140a3aa9ea | ||
|
|
a47977084f | ||
|
|
bddaefdae7 | ||
|
|
242584fd49 | ||
|
|
35bf092813 | ||
|
|
17b7ee29ac | ||
|
|
e337c62ba1 | ||
|
|
52edb5b77f | ||
|
|
01ae76ec29 | ||
|
|
9765fdb0ae | ||
|
|
3e40944e19 | ||
|
|
97388738de | ||
|
|
a16b99b0c8 | ||
|
|
2f47e39a9f | ||
|
|
245e12e8b6 | ||
|
|
f57223024a | ||
|
|
740d6b15e5 | ||
|
|
40f93e9d64 | ||
|
|
da5684df27 | ||
|
|
1920a937b2 | ||
|
|
b31f43f838 | ||
|
|
37f5fc3895 | ||
|
|
1d25c7fca7 | ||
|
|
925a33e560 | ||
|
|
6a33abbec2 | ||
|
|
ecabf130fd | ||
|
|
be2fdd1488 | ||
|
|
e4d03490a3 | ||
|
|
80c81ecfc6 | ||
|
|
f0fd859347 | ||
|
|
74f7101524 | ||
|
|
7e2b259816 | ||
|
|
0b13eda1e5 | ||
|
|
c77bf3aa42 | ||
|
|
86480b7482 | ||
|
|
f234f68019 | ||
|
|
fa671378c7 | ||
|
|
615e3695f2 | ||
|
|
2faade7a03 | ||
|
|
89682cf034 | ||
|
|
82c69a17b9 | ||
|
|
f0f4dc12e5 | ||
|
|
727fbeceb4 | ||
|
|
5127826de0 | ||
|
|
ded891e390 | ||
|
|
34f416aace | ||
|
|
a589877698 | ||
|
|
2176ce0cf7 | ||
|
|
72537106a3 | ||
|
|
974fdd0bfd | ||
|
|
f56bbe814e | ||
|
|
4a445f03e8 | ||
|
|
ec82b923f3 | ||
|
|
3d5be91f6c | ||
|
|
bc753951a0 | ||
|
|
d6d76d4088 | ||
|
|
2a7459baf2 | ||
|
|
b13615f2bf | ||
|
|
c3f743af03 | ||
|
|
f246252a42 | ||
|
|
8df767e9a2 | ||
|
|
8ef1cc5373 | ||
|
|
70b58e2826 | ||
|
|
43a25195b7 | ||
|
|
e01173dd51 | ||
|
|
77a208fff7 | ||
|
|
cef06e3c79 | ||
|
|
a0fb48c9a3 | ||
|
|
6b150e01d3 | ||
|
|
820b6ad4c7 | ||
|
|
bb18e8250c | ||
|
|
b99eb8ba73 | ||
|
|
f258c5904e | ||
|
|
a3171c73d0 | ||
|
|
57cab22387 | ||
|
|
7050b5285e | ||
|
|
38ee73ba2e | ||
|
|
6d31aa8d86 | ||
|
|
96421b3d59 | ||
|
|
0245747020 | ||
|
|
4affdc3a93 | ||
|
|
ccbe9f5137 | ||
|
|
23f7efe7d1 | ||
|
|
051b7d45be | ||
|
|
3540a188a4 | ||
|
|
da6e1df293 | ||
|
|
01429b6570 | ||
|
|
cdd55a1c6b | ||
|
|
40ca3dae61 | ||
|
|
07c8ac08e2 | ||
|
|
5d43262f42 | ||
|
|
bd373598b5 | ||
|
|
26aaef8851 | ||
|
|
6fd5ac2c08 | ||
|
|
763f32e22f | ||
|
|
21a810c38c | ||
|
|
27fabf79bd | ||
|
|
91fae55d90 | ||
|
|
d9e1b5ede3 | ||
|
|
99a3386dd6 | ||
|
|
c49c9b0328 | ||
|
|
d151003eb6 | ||
|
|
3436e6173f | ||
|
|
b2fe27dda5 | ||
|
|
ed5a5ebe7e | ||
|
|
2ca76ba8ce | ||
|
|
6dd1b0e033 | ||
|
|
5a965d2263 | ||
|
|
3ab9d9e740 | ||
|
|
438b6b3e51 | ||
|
|
40899c211b | ||
|
|
b2c5b7e575 | ||
|
|
291c193ad4 | ||
|
|
7c6424ff35 | ||
|
|
a7525e2931 | ||
|
|
e7b1adf4ed | ||
|
|
f67286be7b | ||
|
|
e3e41c3621 | ||
|
|
72fc413764 | ||
|
|
463c4e60de | ||
|
|
e06df905c5 | ||
|
|
8987ce1f69 | ||
|
|
7337169342 | ||
|
|
bee18a5e99 | ||
|
|
abf61d0734 | ||
|
|
20d4e72a95 | ||
|
|
056f078615 | ||
|
|
94c2b8d80b | ||
|
|
0eceb8d76c | ||
|
|
37b21b0762 | ||
|
|
40b33d9f5e | ||
|
|
7303312142 | ||
|
|
57c0346a00 | ||
|
|
6b14984352 | ||
|
|
4c85747849 | ||
|
|
a870cc7036 | ||
|
|
b4c15ed3f3 | ||
|
|
a0b38f6832 | ||
|
|
ac53bac2f4 | ||
|
|
fbbe590ea2 | ||
|
|
6ed11a5563 | ||
|
|
d3f88a1bd9 | ||
|
|
06861261fe | ||
|
|
83f41df82f | ||
|
|
5b36bd7b43 | ||
|
|
d443ea582c | ||
|
|
881952e1cc | ||
|
|
9d7feb9796 | ||
|
|
bc6c892e0a | ||
|
|
48315b0e67 | ||
|
|
c35009f14c | ||
|
|
935a483eaa | ||
|
|
badbdca351 | ||
|
|
c02e8f14c7 | ||
|
|
92cb30e921 | ||
|
|
edb723a4fb | ||
|
|
dbac0724ad | ||
|
|
328585edbd | ||
|
|
914cca6b92 | ||
|
|
e3b05f9076 | ||
|
|
86e88d998f | ||
|
|
6d50afd864 | ||
|
|
4527dda08c | ||
|
|
cc4d1fd045 | ||
|
|
3b99deda45 | ||
|
|
900f05eefd | ||
|
|
716c05f5d8 | ||
|
|
b22c8963cb | ||
|
|
eb05b47c54 | ||
|
|
ca91f47d32 | ||
|
|
5040ee629f | ||
|
|
5be09ffc78 | ||
|
|
4c87123514 | ||
|
|
f57f81a3cb | ||
|
|
423ca01ab1 | ||
|
|
9eedce7345 | ||
|
|
a2df67eccd | ||
|
|
3908c012f9 | ||
|
|
ecda271258 | ||
|
|
84f6a9d659 | ||
|
|
7d49302ffa | ||
|
|
f3455aafe9 | ||
|
|
fcd9c28871 | ||
|
|
a6fc1fdc58 | ||
|
|
630fa68714 | ||
|
|
ef4f284fb0 | ||
|
|
5a63f11ed7 | ||
|
|
6807637e25 | ||
|
|
d88ce2d342 | ||
|
|
b257e01c8d | ||
|
|
c132f2ff10 | ||
|
|
d0259ceecd | ||
|
|
fd4fbe8c8b | ||
|
|
4432031341 | ||
|
|
932628bc14 | ||
|
|
27117292f3 | ||
|
|
3715e011c9 | ||
|
|
2eb81546c3 | ||
|
|
bbdeebd1d4 | ||
|
|
5056e18734 | ||
|
|
e3229c55f3 |
48
.eslintrc.js
48
.eslintrc.js
@@ -21,6 +21,27 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['test/**/int.spec.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/consistent-type-imports': 'warn',
|
||||
'jest/prefer-strict-equal': 'off',
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['test/**/e2e.spec.ts'],
|
||||
extends: [
|
||||
'plugin:playwright/playwright-test'
|
||||
],
|
||||
rules: {
|
||||
'jest/consistent-test-it': 'off',
|
||||
'jest/require-top-level-describe': 'off',
|
||||
'jest/no-test-callback': 'off',
|
||||
'jest/prefer-strict-equal': 'off',
|
||||
'jest/expect-expect': 'off',
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
@@ -28,8 +49,8 @@ module.exports = {
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
rules: {
|
||||
"no-shadow": "off",
|
||||
"@typescript-eslint/no-shadow": ["error"],
|
||||
'no-shadow': 'off',
|
||||
'@typescript-eslint/no-shadow': ['error'],
|
||||
'import/no-unresolved': [
|
||||
2,
|
||||
{
|
||||
@@ -38,18 +59,35 @@ module.exports = {
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.spec.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.e2e.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'jest/expect-expect': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
'no-sparse-arrays': 'off',
|
||||
'import/no-extraneous-dependencies': ["error", { "packageDir": "./" }],
|
||||
'import/no-extraneous-dependencies': ['error', { packageDir: './' }],
|
||||
'react/jsx-filename-extension': [2, { extensions: ['.js', '.jsx', '.ts', '.tsx'] }],
|
||||
'import/prefer-default-export': 'off',
|
||||
'react/prop-types': 'off',
|
||||
'react/require-default-props': 'off',
|
||||
'react/no-unused-prop-types': 'off',
|
||||
'no-console': 'warn',
|
||||
'no-sparse-arrays': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'arrow-body-style': 0,
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'import/extensions': [
|
||||
'error',
|
||||
'ignorePackages',
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Security Vulnerability
|
||||
url: https://github.com/payloadcms/payload/blob/master/SECURITY.md
|
||||
about: See instructions to privately disclose any security concerns
|
||||
- name: Feature Request
|
||||
url: https://github.com/payloadcms/payload/discussions
|
||||
about: Suggest an idea to improve Payload in our GitHub Discussions
|
||||
|
||||
32
.github/workflows/tests.yml
vendored
32
.github/workflows/tests.yml
vendored
@@ -1,13 +1,17 @@
|
||||
name: build
|
||||
|
||||
on: [push]
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, edited, synchronize]
|
||||
push:
|
||||
branches: ['master']
|
||||
|
||||
jobs:
|
||||
build_yarn:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x, 14.x, 15.x, 16.x]
|
||||
node-version: [14.x, 16.x, 18.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -31,10 +35,26 @@ jobs:
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
- run: yarn build
|
||||
- run: yarn test:client
|
||||
- run: yarn test:int # In-memory db + api tests
|
||||
env:
|
||||
CI: true
|
||||
|
||||
- name: Component Tests
|
||||
run: yarn test:components
|
||||
- name: Integration Tests
|
||||
run: yarn test:int
|
||||
|
||||
- name: Generate Payload Types
|
||||
run: yarn dev:generate-types fields
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install --with-deps
|
||||
- name: E2E Tests
|
||||
run: yarn test:e2e --bail
|
||||
|
||||
# - uses: actions/upload-artifact@v2
|
||||
# if: always()
|
||||
# with:
|
||||
# name: playwright-report
|
||||
# path: playwright-report/
|
||||
# retention-days: 30
|
||||
install_npm:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
|
||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -86,6 +86,15 @@ typings/
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# Yarn Berry
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
.pnp.*
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
@@ -228,3 +237,7 @@ build
|
||||
# Ignore built components
|
||||
components/index.js
|
||||
components/styles.css
|
||||
|
||||
# Ignore generated
|
||||
demo/generated-types.ts
|
||||
demo/generated-schema.graphql
|
||||
|
||||
32
.vscode/launch.json
vendored
32
.vscode/launch.json
vendored
@@ -29,42 +29,18 @@
|
||||
"request": "launch",
|
||||
"name": "Launch Program",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "demo/payload.config.ts",
|
||||
"BABEL_ENV": "development"
|
||||
},
|
||||
"program": "${workspaceFolder}/demo/index.js",
|
||||
"program": "${workspaceFolder}/test/dev.js",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/babel-node",
|
||||
"runtimeArgs": [
|
||||
"--nolazy"
|
||||
],
|
||||
"args": [
|
||||
"fields"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Program - Production",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "demo/payload.config.ts",
|
||||
"NODE_ENV": "production",
|
||||
"BABEL_ENV": "development"
|
||||
},
|
||||
"program": "${workspaceFolder}/demo/index.js",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/babel-node",
|
||||
"runtimeArgs": [
|
||||
"--nolazy"
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome against Localhost",
|
||||
"url": "http://localhost:3000/admin",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
1187
CHANGELOG.md
1187
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
60
README.md
60
README.md
@@ -1,15 +1,22 @@
|
||||
<h1 align="center">Payload</h1>
|
||||
<p align="center">A self-hosted, JavaScript headless CMS & application framework built with Express, MongoDB and React.</p>
|
||||
<p align="center">A free and open-source TypeScript headless CMS & application framework built with Express, MongoDB and React.</p>
|
||||
<p align="center">
|
||||
<a href="https://opensource.org/licenses/MIT">
|
||||
<img src="https://img.shields.io/badge/License-MIT-blue.svg" />
|
||||
</a>
|
||||
<a href="https://github.com/payloadcms/payload/actions">
|
||||
<img src="https://github.com/payloadcms/payload/workflows/build/badge.svg">
|
||||
<img src="https://github.com/payloadcms/payload/workflows/build/badge.svg" />
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/payload">
|
||||
<img alt="npm" src="https://img.shields.io/npm/v/payload">
|
||||
<img alt="npm" src="https://img.shields.io/npm/v/payload" />
|
||||
</a>
|
||||
|
||||
<a href="https://twitter.com/intent/tweet?text=Payload%20-%20A%20self-hosted%2C%20headless%20JavaScript%20CMS%20%26%20application%20framework&url=https%3A%2F%2Fgithub.com%2Fpayloadcms%2Fpayload">
|
||||
<img alt="Tweet Payload" src="https://img.shields.io/twitter/url/http/shields.io.svg?style=social">
|
||||
<img alt="Tweet Payload" src="https://img.shields.io/twitter/url/http/shields.io.svg?style=social" />
|
||||
</a>
|
||||
|
||||
<a href="https://discord.com/invite/r6sCXqVk3v">
|
||||
<img alt="Discord" src="https://img.shields.io/discord/967097582721572934?label=Discord" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -17,25 +24,15 @@
|
||||
<img src="https://payloadcms.com/images/og-image.jpg" alt="Payload headless CMS Admin panel built with React" />
|
||||
</a>
|
||||
|
||||
### Quick Start
|
||||
|
||||
```
|
||||
npx create-payload-app
|
||||
```
|
||||
|
||||
Alternatively, it only takes about five minutes to [create an app from scratch](https://payloadcms.com/docs/getting-started/installation#from-scratch).
|
||||
|
||||
### Documentation
|
||||
|
||||
Check out the [Payload website](https://payloadcms.com/docs/getting-started/what-is-payload) to find in-depth documentation for everything that Payload offers.
|
||||
|
||||
### Features
|
||||
|
||||
- Completely free and open-source
|
||||
- [GraphQL](https://payloadcms.com/docs/graphql/overview), [REST](https://payloadcms.com/docs/rest-api/overview), and [Local](https://payloadcms.com/docs/local-api/overview) APIs
|
||||
- [Easily customizable ReactJS Admin](https://payloadcms.com/docs/admin/overview)
|
||||
- [Fully self-hosted](https://payloadcms.com/docs/production/deployment)
|
||||
- [Extensible Authentication](https://payloadcms.com/docs/authentication/overview)
|
||||
- [Local file storage & upload](https://payloadcms.com/docs/upload/overview)
|
||||
- [Version History and Drafts](https://payloadcms.com/docs/versions/overview)
|
||||
- [Field-based Localization](https://payloadcms.com/docs/configuration/localization)
|
||||
- [Block-based Layout Builder](https://payloadcms.com/docs/fields/blocks)
|
||||
- [Extensible SlateJS rich text editor](https://payloadcms.com/docs/fields/rich-text)
|
||||
@@ -49,18 +46,14 @@ Check out the [Payload website](https://payloadcms.com/docs/getting-started/what
|
||||
|
||||
### Code-first
|
||||
|
||||
If you know JavaScript, you know Payload. Payload is a _code-first_ CMS, which allows us to do a lot of things right:
|
||||
Payload is a CMS that has been designed for developers from the ground up to deliver them what they need to build great digital products. If you know JavaScript, you know Payload. It's a _code-first_ CMS, which allows us to do a lot of things right:
|
||||
|
||||
- Payload gives you everything you need, but then steps back and lets you build what you want in JavaScript or TypeScript - with no unnecessary complexity brought by GUIs. You'll understand how your CMS works, because you will have written it exactly how you want it.
|
||||
- Payload gives you everything you need, but then steps back and lets you build what you want in JavaScript or TypeScript - with no unnecessary complexity brought by GUIs. You'll understand how your CMS works because you will have written it exactly how you want it.
|
||||
- Bring your own Express server and do whatever you need on top of Payload. Payload doesn't impose anything on you or your app.
|
||||
- Completely control the Admin panel by using your own React components. Swap out fields or even entire views with ease.
|
||||
- Use your data however and wherever you need thanks to auto-generated, yet fully extensible REST, GraphQL and Local Node APIs.
|
||||
- Use your data however and wherever you need thanks to auto-generated, yet fully extensible REST, GraphQL, and Local Node APIs.
|
||||
|
||||
### Free forever for personal use and small projects
|
||||
|
||||
Payload is 100% free for personal projects or small use cases where only one admin user is required. You can also get started without an account whatsoever while running on `localhost`.
|
||||
|
||||
## Installation
|
||||
### Quick Start
|
||||
|
||||
Before beginning to work with Payload, make sure you have all of the [required software](https://payloadcms.com/docs/getting-started/installation).
|
||||
|
||||
@@ -70,8 +63,21 @@ From there, the easiest way to get started with Payload is to use the `create-pa
|
||||
npx create-payload-app
|
||||
```
|
||||
|
||||
Alternatively, it only takes about five minutes to [write out your own app from scratch](https://payloadcms.com/docs/getting-started/installation#from-scratch).
|
||||
Alternatively, it only takes about five minutes to [create an app from scratch](https://payloadcms.com/docs/getting-started/installation#from-scratch).
|
||||
|
||||
## License
|
||||
### Documentation
|
||||
|
||||
Find the Payload license [here](https://github.com/payloadcms/payload/blob/master/license.md).
|
||||
Check out the [Payload website](https://payloadcms.com/docs/getting-started/what-is-payload) to find in-depth documentation for everything that Payload offers.
|
||||
### Contributing
|
||||
|
||||
If you want to add contributions to this repository, please follow the instructions in [contributing.md](./contributing.md).
|
||||
|
||||
### Other Resources
|
||||
|
||||
##### Discussions
|
||||
|
||||
There are lots of good conversations and resources in our [GitHub Discussions board](https://github.com/payloadcms/payload/discussions). If you're struggling with something, chances are, someone's already solved what you're up against. Searching Discussions will often provide very helpful tips and tricks.
|
||||
|
||||
##### Discord
|
||||
|
||||
Join [Payload's Discord channel](https://discord.com/invite/r6sCXqVk3v) to interact with Payload developers in realtime.
|
||||
|
||||
5
SECURITY.md
Normal file
5
SECURITY.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report any security issues or concerns to [info@payloadcms.com](mailto:info@payloadcms.com).
|
||||
5
components/elements.ts
Normal file
5
components/elements.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { default as Button } from '../dist/admin/components/elements/Button';
|
||||
export { default as Card } from '../dist/admin/components/elements/Card';
|
||||
export { default as Eyebrow } from '../dist/admin/components/elements/Eyebrow';
|
||||
export { default as Nav } from '../dist/admin/components/elements/Nav';
|
||||
export { default as Gutter } from '../dist/admin/components/elements/Gutter';
|
||||
@@ -6,15 +6,23 @@ export {
|
||||
useFormModified,
|
||||
} from '../dist/admin/components/forms/Form/context';
|
||||
|
||||
export { default as useFieldType } from '../dist/admin/components/forms/useFieldType';
|
||||
export { default as useField } from '../dist/admin/components/forms/useField';
|
||||
export { default as useFieldType } from '../dist/admin/components/forms/useField';
|
||||
|
||||
export { default as Form } from '../dist/admin/components/forms/Form';
|
||||
|
||||
export { default as Text } from '../dist/admin/components/forms/field-types/Text';
|
||||
export { default as TextInput } from '../dist/admin/components/forms/field-types/Text/Input';
|
||||
|
||||
export { default as Group } from '../dist/admin/components/forms/field-types/Group';
|
||||
|
||||
export { default as Select } from '../dist/admin/components/forms/field-types/Select';
|
||||
export { default as SelectInput } from '../dist/admin/components/forms/field-types/Select/Input';
|
||||
|
||||
export { default as Checkbox } from '../dist/admin/components/forms/field-types/Checkbox';
|
||||
export { default as Submit } from '../dist/admin/components/forms/Submit';
|
||||
export { default as Label } from '../dist/admin/components/forms/Label';
|
||||
|
||||
export { default as reduceFieldsToValues } from '../dist/admin/components/forms/Form/reduceFieldsToValues';
|
||||
|
||||
export { default as withCondition } from '../dist/admin/components/forms/withCondition';
|
||||
|
||||
1
components/hooks.ts
Normal file
1
components/hooks.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { useStepNav } from '../dist/admin/components/elements/StepNav';
|
||||
2
components/icons.ts
Normal file
2
components/icons.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as Chevron } from '../src/admin/components/icons/Chevron';
|
||||
export { default as X } from '../src/admin/components/icons/X';
|
||||
2
components/templates.ts
Normal file
2
components/templates.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as DefaultTemplate } from '../dist/admin/components/templates/Default';
|
||||
export { default as MinimalTemplate } from '../dist/admin/components/templates/Minimal';
|
||||
5
components/utilities.ts
Normal file
5
components/utilities.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { default as Meta } from '../dist/admin/components/utilities/Meta';
|
||||
export { useLocale } from '../dist/admin/components/utilities/Locale';
|
||||
export { useDocumentInfo } from '../dist/admin/components/utilities/DocumentInfo';
|
||||
export { useConfig } from '../dist/admin/components/utilities/Config';
|
||||
export { useAuth } from '../dist/admin/components/utilities/Auth';
|
||||
3
components/views/Dashboard.ts
Normal file
3
components/views/Dashboard.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as Dashboard } from '../../dist/admin/components/views/Dashboard/Default';
|
||||
|
||||
export type { Props } from '../../dist/admin/components/views/Dashboard/types';
|
||||
@@ -2,10 +2,6 @@
|
||||
|
||||
Below you'll find a set of guidelines for how to contribute to Payload CMS.
|
||||
|
||||
## Payload is proprietary software
|
||||
|
||||
Even though you can read Payload's source code, it's technically not "open source". Payload requires an active license to be used in all production purposes. That said, we do not expect PRs from the public, but we still welcome pull requests of any kind.
|
||||
|
||||
## Opening issues
|
||||
|
||||
Before you submit an issue, please check all existing [open and closed issues](https://github.com/payloadcms/payload/issues) to see if your issue has previously been resolved or is already known. If there is already an issue logged, feel free to upvote it by adding a :thumbsup: [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). If you would like to submit a new issue, please fill out our Issue Template to the best of your ability so we can accurately understand your report.
|
||||
@@ -22,12 +18,39 @@ Payload documentation can be found directly within its codebase and you can feel
|
||||
|
||||
## Building additional features
|
||||
|
||||
If you're an incredibly awesome person and want to help us make Payload even better through new features or additions, we would be thrilled to work with you. If your proposed feature is accepted by our team and is significant enough, pending our discretion, we'd be happy to hook you up with a pro-bono license.
|
||||
If you're an incredibly awesome person and want to help us make Payload even better through new features or additions, we would be thrilled to work with you.
|
||||
|
||||
To help us work on new features, you can reach out to our Development team at [`dev@payloadcms.com`](mailto:dev@payloadcms.com). Be as complete and descriptive as possible regarding your vision and we'll go from there!
|
||||
### Before Starting
|
||||
|
||||
To help us work on new features, you can create a new feature request post in [GitHub Discussion](https://github.com/payloadcms/payload/discussions) or discuss it in our [Discord](https://discord.com/invite/r6sCXqVk3v). New functionality often has large implications across the entire Payload repo, so it is best to discuss the architecture and approach before starting work on a pull request.
|
||||
|
||||
### Code
|
||||
|
||||
Most new functionality should keep testing in mind. With 1.0, testability of new features has been vastly improved. All top-level directories within the `test/` directory are for testing a specific category: `fields`, `collections`, etc.
|
||||
|
||||
If it makes sense to add your feature to an existing test directory, please do so.
|
||||
|
||||
A typical directory with `test/` will be structured like this:
|
||||
|
||||
```text
|
||||
.
|
||||
├── config.ts
|
||||
├── int.spec.ts
|
||||
├── e2e.spec.ts
|
||||
└── payload-types.ts
|
||||
```
|
||||
|
||||
- `config.ts` - This is the _granular_ Payload config for testing. It should be as lightweight as possible. Reference existing configs for an example
|
||||
- `int.spec.ts` - This is the test file run by jest. Any test file must have a `*int.spec.ts` suffix.
|
||||
- `e2e.spec.ts` - This is the end-to-end test file that will load up the admin UI using the above config and run Playwright tests. These tests are typically only needed if a large change is being made to the Admin UI.
|
||||
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `yarn dev:generate-types my-test-dir`.
|
||||
|
||||
The directory split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config.
|
||||
|
||||
The following command will start Payload with your config: `yarn dev my-test-dir`. This command will start up Payload using your config and refresh a test database on every restart.
|
||||
|
||||
NOTE: It is recommended to add the test credentials to your autofill for `localhost:3000/admin` as this will be required on every nodemon restart.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
For all Pull Requests, you should be extremely descriptive about both your problem and proposed solution. If there are any affected open or closed issues, please leave the issue number in your PR message.
|
||||
|
||||
By opening a Pull Request against Payload's codebase, you automatically give the entirety of the contribution within your PR to Payload CMS, LLC and retain no personal ownership whatsoever afterward. For more information, please read the full [Payload license](https://github.com/payloadcms/payload/blob/master/license.md).
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* authorize a request by comparing the current user with one or more roles
|
||||
* @param allRoles
|
||||
* @param user
|
||||
* @returns {Function}
|
||||
*/
|
||||
const checkRole = (allRoles, user) => {
|
||||
if (user) {
|
||||
if (allRoles.some((role) => user.roles && user.roles.some((individualRole) => individualRole === role))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export default checkRole;
|
||||
@@ -1,7 +0,0 @@
|
||||
export default [
|
||||
'admin',
|
||||
'editor',
|
||||
'moderator',
|
||||
'user',
|
||||
'viewer',
|
||||
];
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Block } from '../../src/fields/config/types';
|
||||
|
||||
const CTA: Block = {
|
||||
slug: 'cta',
|
||||
labels: {
|
||||
singular: 'Call to Action',
|
||||
plural: 'Calls to Action',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'label',
|
||||
label: 'Label',
|
||||
type: 'text',
|
||||
maxLength: 100,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'url',
|
||||
label: 'URL',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default CTA;
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Block } from '../../src/fields/config/types';
|
||||
|
||||
const Email: Block = {
|
||||
slug: 'email',
|
||||
labels: {
|
||||
singular: 'Email',
|
||||
plural: 'Emails',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'testEmail',
|
||||
label: 'Test Email Field',
|
||||
type: 'email',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Email;
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Block } from '../../src/fields/config/types';
|
||||
|
||||
const NumberBlock: Block = {
|
||||
slug: 'number',
|
||||
labels: {
|
||||
singular: 'Number',
|
||||
plural: 'Numbers',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'testNumber',
|
||||
label: 'Test Number Field',
|
||||
type: 'number',
|
||||
max: 100,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default NumberBlock;
|
||||
@@ -1,34 +0,0 @@
|
||||
import { Block } from '../../src/fields/config/types';
|
||||
|
||||
const Quote: Block = {
|
||||
imageURL: '/static/assets/images/generic-block-image.svg',
|
||||
slug: 'quote',
|
||||
labels: {
|
||||
singular: 'Quote',
|
||||
plural: 'Quotes',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'author',
|
||||
label: 'Author',
|
||||
type: 'relationship',
|
||||
relationTo: 'public-users',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'quote',
|
||||
label: 'Quote',
|
||||
type: 'textarea',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'color',
|
||||
label: 'Color',
|
||||
type: 'text',
|
||||
maxLength: 7,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Quote;
|
||||
@@ -1,14 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<div id="portal"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,9 +0,0 @@
|
||||
<svg width="82" height="53" viewBox="0 0 82 53" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<rect x="0.713013" width="80.574" height="52.7791" fill="url(#pattern0)"/>
|
||||
<defs>
|
||||
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image0" transform="scale(0.00387597 0.00591716)"/>
|
||||
</pattern>
|
||||
<image id="image0" width="258" height="169" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQIAAACpCAYAAADA4zPJAAALpklEQVR4Ae2dSY/UOhSF+///DCSGHQuEgAVI7BBITAI2zCCGZuhmpqEBP53W8yOvK1UdO3Zyr/NFilJdXalyTo4/X984zlZgQQEUWLwCW4tXAAFQAAUCIMAEKIACgAAPoAAKBECACVAABQABHkABFAiAABOgAAoAAjyAAiggBbhqgA9QAAUAAR5AARQgIsADKIACdA3wAAqggBQgR4APUAAFAAEeQAEUICLAAyiAAnQN8AAKoIAUIEeAD1AABQABHkABFCAiwAMogAJ0DfAACqCAFCBHgA9QAAUAAR5AARQgIsADKIACdA3wAAqggBQgR4APUAAFAAEeQAEUICLAAyiAAnQN8AAKoIAUIEeAD1AABQABHkABFCAiwAMogAJ0DfAACqCAFCBHgA9QAAUAAR5AARQgIsADKIACdA3wAAqggBQgR4APUAAFAAEeQAEUICLAAyiAAnQN8AAKoIAUIEeAD1AABQABHkABFCAiwAMLVmB/fz98+/YtfPr0KXz48CHs7u6GnZ2dg+3Hjx/D169fw48fP8KfP3+aV4muQfOnmAPsKqDKr4r/9u3bsL29PXgVKPb29pqFAiDouoTXzSrw8+fPg5Y+pfL3ffbdu3fh+/fvzekECJo7pRxQVwGF9YoA+ir1mPfUhVB00coCCFo5kxzHigKqqGrBx1T4Tfu+efOmmegAEKzYhzdaUEBdAVXUTRW51P+UVPS+AALvZ5DyryigSGAqCESYeIcBIFixEW94VuDXr1/JVwRiZR671VUFrwsg8HrmKPeKAkoMKok3tkLn7q8oRCDyuAACj2eNMvcqoMFBuZW41H4alORxAQQezxplXlHg9+/fk+cF1sHDYxcBEKxYijc8KvDly5fZo4EIhvfv37uTEBC4O2UU+LACyg2kDhmOlbbWVpcvPS2AwNPZoqy9CigUr1Whc79Xoxk9LYDA09mirL0K1BhCnAuAuJ8iFE8LIPB0tihrrwI1hxHHip2z9XQpERD0Wos3vSig/EBOJZ1iH09XDwCBF8dTzl4FNJx4ikqd8xu6kuFlAQRezhTl7FVAMwjlVNIp9vn8+XNvmS2+aR4E6md56mtZPMktl8niFYMIGU9XDkyDQCdZ47e1eupvtVzxrB0bEUGZM2ISBEoAafLISNa4FWH1PxYUiApo4E70h7UtOYJ4ljK2OrGbLgfpfy1NEZUhEbt0FNA9BtYAEMvjaW5DMxGBWnpN7hBFPGrrfSKIjpd5OVIBa8OLo3c9DTM2AQJRXbdvRgGHbrWP9mVZtgKaanyoZ6b6nPJanrqxs4MgJgRzTxCJxGVDQEevEDzXP7X2E5w8LbOBQLQsOUZ8TCLx9evX4fHjx+HRo0esTjW4efNmKL3eu3cvyBs5sPB2lWsWENSaZlr3gacmEi9fvhyOHTvGiga9Hjh79mwyDJSz8NQtUOQyOQhSEoI5JNY+mrJqyPLy5cvekw8YAGPXA7du3UqKCob6b4hHp/rMZCDITQjmwmBIIvH+/fuAgEjgSA9cuXJlMAh0edtbNCDYTAICjf6a4xKPflO/vW4BBLT83ZZ/3esUEHi6ZNitF1VBIDLqxovcVr3UfipDH6UBASBYV/m77w8FgaeRhF0I6HU1EChpp+Rdqco89nv6EomAABB0K/y610NAoCHxfY3N4Qpn9e8qILAwv3wfODTmoJvIef78+ZH9w3Xm4P3lQOTGjRsbGzSNGfAMgeIRgRKCFkd5HYaCyqiyaj1//jwwIGG41gOnT58Ourp02EPx7zHjVyxFB8UigrkSgvGEpG5jIlEw0ECiO3fusDaowe3bt8PVq1eDwvvU9fr162sh0NqI1tEgsJIQTAVB/Py6RKIlWlOWcQrIo+oSlrpypShADUhLyygQaOYgSwnBWLlTtzoGZkFqydb9xxKBkONZQUSNRqs+yQaBbvRQeJRa6ax+Xsfi6f7xfqvz7lAFVKEVJSjbLzCookc/a6uBQcol6ZKgxgZ4TwYepUsyCBQSeUgI5gJHxmgt7DvKBPwfBZJAIDKW6mflVtQp9tMxeh0hhqVRIEeBQSBQWKQQaYpKaOk3dMyth4Q5pmGf9hQ4EgTqS+3s7CwOAhFIOvZWE0Tt2ZkjylVgIwhaSwjGyp26JZGYay/286JALwiULOubTjy1ArX2ee/jyb2YknJOr8AKCJQk06WT1ipxqeORNiQSpzcqv1hXgf9AsNSEYC4gSCTWNSbfPq0CByBYekIwFwYkEqc1K79WT4GtsdOJ51aiVvZTItHbjLX17MQ3e1Vgq5UKOfdxtHI7ajSy7iYlFxLVaH8LCLa3iyVGlUhMnU7dmsWUK+pOL6dZpxlUZe0slS8PICgIghiV6F4M3ct+6dIlV+vFixcPJmo5d+5c6K4XLlwI+p/l47l27RpdtBF8AAQVQHDmzJm1M94wxVm9Kc6kOzeM5dEAEBQGgR6dRmWvV9mP0lbdM5Z0BQBBYRA8ePAAEMw4B6KeVciSrgAgAARNgQsQpENAewACQAAI8upOU3sBgsIgePjwYVMV66g+ubX/ExHk8QkQFAaB5sA/ceIEMJghT3D8+HHmnczjAF2DeO2/5Pbu3bvh1KlTwGBCGJw8eTI8efIksxqwGxFB4YggAuXVq1fh2bNn4enTp0euL168OJj/QaP4Sq26O1JzLw75/Rqf0czApY5lyPcwfmAczABBJRBEIAzd6ual7nMZx5xWK3eTxkfLjTkW9p1GAUBgBAQRGGMrj7W7SRWV6AYmFtsKAAJjIBAQciqPbgyyPL0cj5YDBMXu7out5lK2QyuPl+nleLScXRgQERiMCLqg2zQLkqIAJdK6n7f+mhmhbcIAEBgHgSp2X+VRlnx3d9cVBLqQGpsLsVmd/JYKEDgAQaxA8bmM1hKCsXypWx4tZwccgMARCFTRFB2kVjjrn2dG6PmBAAicgcB6pc4t36ZcyPzVpP0SAAJAYCbC6MuFtF8FbRwhIAAEZkAQowkeLTc9HAABIDAHAgGBR8tNCwNAAAhMgiBGByQSpwECIAAEpkEgIJBIrA8DQAAIzINAMFAikUfL1QMCIAAELkAQuwqtPVquXtVO+2ZAAAhcgUBAaOHRcmnVtP6nAQEgcAeCGB3ohiuWMgoAAkDgFgQCgm68Ypqy8TAABIDANQgEAxKJgMC9iWOYy3b84+lJJOYDgYiAiKApmGoWpP39/fwasdA9AQEgaAoEMbIqNSP0UrgACABBkyAQEEgkDscYIAAEzYJAMMiZEXp49Wnnk4AAEDQNgthVGDojdDtVO+1IAAEgWAQIBAQSievhAAgAwWJAIBhozAGJxFUgAAJAsCgQxK4C06n/HwaAABAsEgQCAonEvzAABIBgsSCI0QGJxBAAASBYPAgEhKU/lxEQAAJA8K8HljydOiAABIDgkAfio+X+9qDbfwUIDpkg9hvZjr8b0LOGS3suIyAABEQEGzywlOnUAcEGE3hu0Sh7uYhmCdOpAwJAQEQwwAOtJxIBwQAT0LqWa129a9nqcxkBASAgIkj0QIvPZQQEiSbw3qJR/nLRTUuJREAACIgIRniglUQiIBhhAlrXcq2rZy1bmE4dEAACIoJCHvA8nTogKGQCzy0aZS8X2Xh9LiMgAAREBBU84O25jICgggloYcu1sJ619DSdOiAABEQEFT3gJZEICCqawHNrRtnLRjXWE4mAABAQEUzkAcvTqQOCiUxAC1u2hfWsp8Xp1AEBICAimMED1hKJgGAGE3huzSh7ucjG0nTqgAAQEBHM7AEL06kDgplNQAtbroX1rOXciURAAAiICIx4QGMO5kokAgIjJvDcmlH2slHNHM9lBASAgIjAoAemTiQCAoMmoIUt28J61nOqRCIgAAREBMY9MMUsSIDAuAk8t2aUvVxkU3s6dUAACIgIHHmg1nMZAYEjE9DClmthPWtZYzp1QAAIiAiceqDkdOqAwKkJPLdolL1cZFMqkbilkUysaIAH/Hpgb28vjF3+AbSb48mcXO9tAAAAAElFTkSuQmCC"/>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.4 KiB |
@@ -1,67 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import roles from '../access/roles';
|
||||
import checkRole from '../access/checkRole';
|
||||
|
||||
const access = ({ req: { user } }) => {
|
||||
const result = checkRole(['admin'], user);
|
||||
return result;
|
||||
};
|
||||
|
||||
const Admin: CollectionConfig = {
|
||||
slug: 'admins',
|
||||
labels: {
|
||||
singular: 'Admin',
|
||||
plural: 'Admins',
|
||||
},
|
||||
access: {
|
||||
create: access,
|
||||
read: access,
|
||||
update: access,
|
||||
delete: access,
|
||||
admin: () => true,
|
||||
},
|
||||
auth: {
|
||||
tokenExpiration: 7200, // 2 hours
|
||||
verify: false,
|
||||
maxLoginAttempts: 5,
|
||||
lockTime: 600 * 1000, // lock time in ms
|
||||
useAPIKey: true,
|
||||
depth: 0,
|
||||
cookies: {
|
||||
secure: false,
|
||||
sameSite: 'lax',
|
||||
domain: undefined,
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'roles',
|
||||
label: 'Role',
|
||||
type: 'select',
|
||||
options: roles,
|
||||
defaultValue: 'user',
|
||||
required: true,
|
||||
saveToJWT: true,
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
name: 'publicUser',
|
||||
type: 'relationship',
|
||||
hasMany: true,
|
||||
relationTo: 'public-users',
|
||||
},
|
||||
{
|
||||
name: 'apiKey',
|
||||
type: 'text',
|
||||
access: {
|
||||
read: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
},
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
admin: {
|
||||
useAsTitle: 'email',
|
||||
},
|
||||
};
|
||||
|
||||
export default Admin;
|
||||
@@ -1,339 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import checkRole from '../access/checkRole';
|
||||
import Email from '../blocks/Email';
|
||||
import Quote from '../blocks/Quote';
|
||||
import NumberBlock from '../blocks/Number';
|
||||
import CallToAction from '../blocks/CallToAction';
|
||||
import CollectionDescription from '../customComponents/CollectionDescription';
|
||||
|
||||
const AllFields: CollectionConfig = {
|
||||
slug: 'all-fields',
|
||||
labels: {
|
||||
singular: 'All Fields',
|
||||
plural: 'All Fields',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'text',
|
||||
preview: (doc, { token }) => {
|
||||
const { text } = doc;
|
||||
|
||||
if (doc && text) {
|
||||
return `http://localhost:3000/previewable-posts/${text}?preview=true&token=${token}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
description: CollectionDescription,
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
label: 'Text',
|
||||
required: true,
|
||||
defaultValue: 'Default Value',
|
||||
unique: true,
|
||||
access: {
|
||||
create: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
update: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
read: ({ req: { user } }) => Boolean(user),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'descriptionText',
|
||||
type: 'text',
|
||||
label: 'Text with text description',
|
||||
defaultValue: 'Default Value',
|
||||
admin: {
|
||||
description: 'This text describes the field',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'descriptionFunction',
|
||||
type: 'text',
|
||||
label: 'Text with function description',
|
||||
defaultValue: 'Default Value',
|
||||
maxLength: 20,
|
||||
admin: {
|
||||
description: ({ value }) => (typeof value === 'string' ? `${20 - value.length} characters left` : ''),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
type: 'upload',
|
||||
label: 'Image',
|
||||
relationTo: 'media',
|
||||
admin: {
|
||||
description: 'No selfies',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'select',
|
||||
label: 'Select',
|
||||
type: 'select',
|
||||
options: [{
|
||||
value: 'option-1',
|
||||
label: 'Option 1 Label',
|
||||
}, {
|
||||
value: 'option-2',
|
||||
label: 'Option 2 Label',
|
||||
}, {
|
||||
value: 'option-3',
|
||||
label: 'Option 3 Label',
|
||||
}, {
|
||||
value: 'option-4',
|
||||
label: 'Option 4 Label',
|
||||
}],
|
||||
defaultValue: 'option-1',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'selectMany',
|
||||
label: 'Select w/ hasMany',
|
||||
type: 'select',
|
||||
options: [{
|
||||
value: 'option-1',
|
||||
label: 'Option 1 Label',
|
||||
}, {
|
||||
value: 'option-2',
|
||||
label: 'Option 2 Label',
|
||||
}, {
|
||||
value: 'option-3',
|
||||
label: 'Option 3 Label',
|
||||
}, {
|
||||
value: 'option-4',
|
||||
label: 'Option 4 Label',
|
||||
}],
|
||||
defaultValue: 'option-1',
|
||||
required: true,
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
name: 'dayOnlyDateFieldExample',
|
||||
label: 'Day Only',
|
||||
type: 'date',
|
||||
required: true,
|
||||
admin: {
|
||||
date: {
|
||||
pickerAppearance: 'dayOnly',
|
||||
monthsToShow: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'timeOnlyDateFieldExample',
|
||||
label: 'Time Only',
|
||||
type: 'date',
|
||||
admin: {
|
||||
date: {
|
||||
pickerAppearance: 'timeOnly',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'radioGroupExample',
|
||||
label: 'Radio Group Example',
|
||||
type: 'radio',
|
||||
options: [{
|
||||
value: 'option-1',
|
||||
label: 'Options 1 Label',
|
||||
}, {
|
||||
value: 'option-2',
|
||||
label: 'Option 2 Label',
|
||||
}, {
|
||||
value: 'option-3',
|
||||
label: 'Option 3 Label',
|
||||
}],
|
||||
defaultValue: 'option-2',
|
||||
required: true,
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'email',
|
||||
label: 'Email',
|
||||
type: 'email',
|
||||
}, {
|
||||
name: 'number',
|
||||
label: 'Number',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
label: 'Group',
|
||||
name: 'group',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'nestedText1',
|
||||
label: 'Nested Text 1',
|
||||
type: 'text',
|
||||
}, {
|
||||
name: 'nestedText2',
|
||||
label: 'Nested Text 2',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
label: 'Array',
|
||||
name: 'array',
|
||||
minRows: 2,
|
||||
maxRows: 4,
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'arrayText1',
|
||||
label: 'Array Text 1',
|
||||
type: 'text',
|
||||
required: true,
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'arrayText2',
|
||||
label: 'Array Text 2',
|
||||
type: 'text',
|
||||
required: true,
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
access: {
|
||||
read: ({ req: { user } }) => Boolean(user),
|
||||
update: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'arrayText3',
|
||||
label: 'Array Text 3',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'checkbox',
|
||||
label: 'Checkbox',
|
||||
type: 'checkbox',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'blocks',
|
||||
label: 'Blocks Content',
|
||||
name: 'blocks',
|
||||
minRows: 2,
|
||||
blocks: [Email, NumberBlock, Quote, CallToAction],
|
||||
localized: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
label: 'Relationship to One Collection',
|
||||
name: 'relationship',
|
||||
relationTo: 'conditions',
|
||||
admin: {
|
||||
description: 'Relates to description',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
label: 'Relationship hasMany',
|
||||
name: 'relationshipHasMany',
|
||||
relationTo: 'localized-posts',
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
label: 'Relationship to Multiple Collections',
|
||||
name: 'relationshipMultipleCollections',
|
||||
relationTo: ['localized-posts', 'conditions'],
|
||||
},
|
||||
{
|
||||
type: 'textarea',
|
||||
label: 'Textarea',
|
||||
name: 'textarea',
|
||||
admin: {
|
||||
description: 'Hello textarea description',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
label: 'Rich Text',
|
||||
required: true,
|
||||
admin: {
|
||||
elements: [
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'blockquote',
|
||||
'ul',
|
||||
'ol',
|
||||
'link',
|
||||
],
|
||||
leaves: [
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'strikethrough',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
label: 'Slug',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
localized: true,
|
||||
unique: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'checkbox',
|
||||
type: 'checkbox',
|
||||
label: 'Checkbox',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'dateFieldExample',
|
||||
label: 'Day and Time',
|
||||
type: 'date',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
date: {
|
||||
timeIntervals: 30,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default AllFields;
|
||||
@@ -1,102 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const AutoLabel: CollectionConfig = {
|
||||
slug: 'auto-label',
|
||||
admin: {
|
||||
useAsTitle: 'autoLabelField',
|
||||
enableRichTextRelationship: true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'autoLabelField',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'noLabel',
|
||||
type: 'text',
|
||||
label: false,
|
||||
},
|
||||
{
|
||||
name: 'labelOverride',
|
||||
type: 'text',
|
||||
label: 'Custom Label',
|
||||
},
|
||||
{
|
||||
name: 'testRelationship',
|
||||
type: 'relationship',
|
||||
relationTo: 'all-fields',
|
||||
},
|
||||
{
|
||||
name: 'specialBlock',
|
||||
type: 'blocks',
|
||||
minRows: 1,
|
||||
maxRows: 20,
|
||||
// Will auto-label
|
||||
// labels: {
|
||||
// singular: 'Special Block',
|
||||
// plural: 'Special Blocks',
|
||||
// },
|
||||
blocks: [
|
||||
{
|
||||
slug: 'number',
|
||||
// Will auto-label
|
||||
// labels: {
|
||||
// singular: 'Number',
|
||||
// plural: 'Numbers',
|
||||
// },
|
||||
fields: [
|
||||
{
|
||||
name: 'testNumber',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'noLabelBlock',
|
||||
type: 'blocks',
|
||||
label: false,
|
||||
minRows: 1,
|
||||
maxRows: 20,
|
||||
blocks: [
|
||||
{
|
||||
slug: 'number',
|
||||
// labels: {
|
||||
// singular: 'Number',
|
||||
// plural: 'Numbers',
|
||||
// },
|
||||
fields: [
|
||||
{
|
||||
name: 'testNumber',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'items',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'itemName',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'noLabelArray',
|
||||
type: 'array',
|
||||
label: false,
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'textField',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default AutoLabel;
|
||||
@@ -1,43 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import Email from '../blocks/Email';
|
||||
import Quote from '../blocks/Quote';
|
||||
import NumberBlock from '../blocks/Number';
|
||||
import CallToAction from '../blocks/CallToAction';
|
||||
|
||||
const Blocks: CollectionConfig = {
|
||||
slug: 'blocks',
|
||||
labels: {
|
||||
singular: 'Blocks',
|
||||
plural: 'Blocks',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'layout',
|
||||
label: 'Layout Blocks',
|
||||
labels: {
|
||||
singular: 'Block',
|
||||
plural: 'Blocks',
|
||||
},
|
||||
type: 'blocks',
|
||||
blocks: [Email, NumberBlock, Quote, CallToAction],
|
||||
localized: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'nonLocalizedLayout',
|
||||
label: 'Non Localized Layout',
|
||||
labels: {
|
||||
singular: 'Layout',
|
||||
plural: 'Layouts',
|
||||
},
|
||||
type: 'blocks',
|
||||
blocks: [Email, NumberBlock, Quote, CallToAction],
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Blocks;
|
||||
@@ -1,23 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const Code: CollectionConfig = {
|
||||
slug: 'code',
|
||||
labels: {
|
||||
singular: 'Code',
|
||||
plural: 'Codes',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'code',
|
||||
type: 'code',
|
||||
label: 'Code',
|
||||
required: true,
|
||||
admin: {
|
||||
language: 'js',
|
||||
description: 'javascript example',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Code;
|
||||
@@ -1,73 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import Email from '../blocks/Email';
|
||||
import Quote from '../blocks/Quote';
|
||||
import NumberBlock from '../blocks/Number';
|
||||
import CallToAction from '../blocks/CallToAction';
|
||||
|
||||
const Conditions: CollectionConfig = {
|
||||
slug: 'conditions',
|
||||
labels: {
|
||||
singular: 'Conditions',
|
||||
plural: 'Conditions',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
label: 'Title',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'enableTest',
|
||||
type: 'checkbox',
|
||||
label: 'Enable Test',
|
||||
},
|
||||
{
|
||||
name: 'number',
|
||||
type: 'number',
|
||||
label: 'Number Field',
|
||||
},
|
||||
{
|
||||
name: 'simpleCondition',
|
||||
type: 'text',
|
||||
label: 'Enable Test is checked',
|
||||
required: true,
|
||||
admin: {
|
||||
condition: (_, siblings) => siblings.enableTest === true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'orCondition',
|
||||
type: 'text',
|
||||
label: 'Number is greater than 20 OR enableTest is checked',
|
||||
required: true,
|
||||
admin: {
|
||||
condition: (_, siblings) => siblings.number > 20 || siblings.enableTest === true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'nestedConditions',
|
||||
type: 'text',
|
||||
label: 'Number is either greater than 20 AND enableTest is checked, OR number is less than 20 and enableTest is NOT checked',
|
||||
admin: {
|
||||
condition: (_, siblings) => (siblings.number > 20 && siblings.enableTest === true) || (siblings.number < 20 && siblings.enableTest === false),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'blocks',
|
||||
label: 'Blocks',
|
||||
labels: {
|
||||
singular: 'Block',
|
||||
plural: 'Blocks',
|
||||
},
|
||||
type: 'blocks',
|
||||
blocks: [Email, NumberBlock, Quote, CallToAction],
|
||||
required: true,
|
||||
admin: {
|
||||
condition: (_, siblings) => siblings?.enableTest === true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Conditions;
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const Cell: React.FC = () => <div className="description">fake description cell</div>;
|
||||
|
||||
export default Cell;
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const Description: React.FC = () => <div className="description">fake description field</div>;
|
||||
|
||||
export default Description;
|
||||
@@ -1,3 +0,0 @@
|
||||
.custom-description-filter {
|
||||
background: lightgray;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Props } from './types';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const Filter: React.FC<Props> = ({ onChange, value }) => (
|
||||
<input
|
||||
className="custom-description-filter"
|
||||
type="text"
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
value={value}
|
||||
/>
|
||||
);
|
||||
|
||||
export default Filter;
|
||||
@@ -1,4 +0,0 @@
|
||||
export type Props = {
|
||||
value?: string
|
||||
onChange?: (value: string) => void
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Group } from '../../../../../../../components/forms';
|
||||
|
||||
const CustomGroup: React.FC = (props) => (
|
||||
<div className="custom-group">
|
||||
<Group {...props} />
|
||||
</div>
|
||||
);
|
||||
|
||||
CustomGroup.defaultProps = {
|
||||
value: '',
|
||||
};
|
||||
|
||||
CustomGroup.propTypes = {
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
export default CustomGroup;
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const NestedArrayCustomField = () => <div className="nested-array-custom-field">Nested array custom field</div>;
|
||||
|
||||
export default NestedArrayCustomField;
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const NestedGroupCustomField = () => <div className="nested-group-custom-field">Nested group custom field</div>;
|
||||
|
||||
export default NestedGroupCustomField;
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const NestedText1 = () => <div className="nested-text-1">Nested Text 1</div>;
|
||||
|
||||
export default NestedText1;
|
||||
@@ -1,5 +0,0 @@
|
||||
$color: purple;
|
||||
|
||||
.custom-list {
|
||||
color: $color;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import React from 'react';
|
||||
import DefaultList from '../../../../../../src/admin/components/views/collections/List/Default';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const CustomListView: React.FC = (props) => (
|
||||
<div className="custom-list">
|
||||
<p>This is a custom Pages list view</p>
|
||||
<p>Sup</p>
|
||||
<DefaultList {...props} />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default CustomListView;
|
||||
@@ -1,120 +0,0 @@
|
||||
import { CollectionConfig } from '../../../src/collections/config/types';
|
||||
import DescriptionField from './components/fields/Description/Field';
|
||||
import DescriptionCell from './components/fields/Description/Cell';
|
||||
import DescriptionFilter from './components/fields/Description/Filter';
|
||||
import NestedArrayField from './components/fields/NestedArrayCustomField/Field';
|
||||
import GroupField from './components/fields/Group/Field';
|
||||
import NestedGroupField from './components/fields/NestedGroupCustomField/Field';
|
||||
import NestedText1Field from './components/fields/NestedText1/Field';
|
||||
import ListView from './components/views/List';
|
||||
import CustomDescriptionComponent from '../../customComponents/Description';
|
||||
|
||||
const CustomComponents: CollectionConfig = {
|
||||
slug: 'custom-components',
|
||||
labels: {
|
||||
singular: 'Custom Component',
|
||||
plural: 'Custom Components',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
maxLength: 100,
|
||||
required: true,
|
||||
unique: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
type: 'textarea',
|
||||
required: true,
|
||||
localized: true,
|
||||
admin: {
|
||||
components: {
|
||||
Field: DescriptionField,
|
||||
Cell: DescriptionCell,
|
||||
Filter: DescriptionFilter,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'componentDescription',
|
||||
label: 'Component ViewDescription',
|
||||
type: 'text',
|
||||
admin: {
|
||||
description: CustomDescriptionComponent,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'array',
|
||||
label: 'Array',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'nestedArrayCustomField',
|
||||
label: 'Nested Array Custom Field',
|
||||
admin: {
|
||||
components: {
|
||||
Field: NestedArrayField,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'group',
|
||||
label: 'Group',
|
||||
type: 'group',
|
||||
admin: {
|
||||
components: {
|
||||
Field: GroupField,
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'nestedGroupCustomField',
|
||||
label: 'Nested Group Custom Field',
|
||||
admin: {
|
||||
components: {
|
||||
Field: NestedGroupField,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'nestedText1',
|
||||
label: 'Nested Text 1',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
Field: NestedText1Field,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
name: 'nestedText2',
|
||||
label: 'Nested Text 2',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
components: {
|
||||
views: {
|
||||
List: ListView,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default CustomComponents;
|
||||
@@ -1,22 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const CustomID: CollectionConfig = {
|
||||
slug: 'custom-id',
|
||||
labels: {
|
||||
singular: 'CustomID',
|
||||
plural: 'CustomIDs',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default CustomID;
|
||||
@@ -1,287 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import checkRole from '../access/checkRole';
|
||||
import Email from '../blocks/Email';
|
||||
import Quote from '../blocks/Quote';
|
||||
import NumberBlock from '../blocks/Number';
|
||||
import CallToAction from '../blocks/CallToAction';
|
||||
|
||||
const DefaultValues: CollectionConfig = {
|
||||
slug: 'default-values',
|
||||
labels: {
|
||||
singular: 'Default Value Test',
|
||||
plural: 'Default Value Tests',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'text',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
label: 'Text',
|
||||
defaultValue: 'Default Value',
|
||||
unique: true,
|
||||
access: {
|
||||
create: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
update: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
read: ({ req: { user } }) => Boolean(user),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
type: 'upload',
|
||||
label: 'Image',
|
||||
relationTo: 'media',
|
||||
},
|
||||
{
|
||||
name: 'select',
|
||||
label: 'Select',
|
||||
type: 'select',
|
||||
options: [{
|
||||
value: 'option-1',
|
||||
label: 'Option 1 Label',
|
||||
}, {
|
||||
value: 'option-2',
|
||||
label: 'Option 2 Label',
|
||||
}, {
|
||||
value: 'option-3',
|
||||
label: 'Option 3 Label',
|
||||
}, {
|
||||
value: 'option-4',
|
||||
label: 'Option 4 Label',
|
||||
}],
|
||||
defaultValue: 'option-1',
|
||||
},
|
||||
{
|
||||
name: 'selectMany',
|
||||
label: 'Select w/ hasMany',
|
||||
type: 'select',
|
||||
options: [{
|
||||
value: 'option-1',
|
||||
label: 'Option 1 Label',
|
||||
}, {
|
||||
value: 'option-2',
|
||||
label: 'Option 2 Label',
|
||||
}, {
|
||||
value: 'option-3',
|
||||
label: 'Option 3 Label',
|
||||
}, {
|
||||
value: 'option-4',
|
||||
label: 'Option 4 Label',
|
||||
}],
|
||||
defaultValue: ['option-1', 'option-4'],
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
name: 'radioGroupExample',
|
||||
label: 'Radio Group Example',
|
||||
type: 'radio',
|
||||
options: [{
|
||||
value: 'option-1',
|
||||
label: 'Options 1 Label',
|
||||
}, {
|
||||
value: 'option-2',
|
||||
label: 'Option 2 Label',
|
||||
}, {
|
||||
value: 'option-3',
|
||||
label: 'Option 3 Label',
|
||||
}],
|
||||
defaultValue: 'option-2',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'email',
|
||||
label: 'Email',
|
||||
type: 'email',
|
||||
defaultValue: 'some@email.com',
|
||||
}, {
|
||||
name: 'number',
|
||||
label: 'Number',
|
||||
type: 'number',
|
||||
defaultValue: 5,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
label: 'Group',
|
||||
name: 'group',
|
||||
defaultValue: {
|
||||
nestedText1: 'neat',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'nestedText1',
|
||||
label: 'Nested Text 1',
|
||||
type: 'text',
|
||||
defaultValue: 'nested default text 1',
|
||||
}, {
|
||||
name: 'nestedText2',
|
||||
label: 'Nested Text 2',
|
||||
type: 'text',
|
||||
defaultValue: 'nested default text 2',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
label: 'Array',
|
||||
name: 'array',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
defaultValue: [
|
||||
{
|
||||
arrayText1: 'Get out',
|
||||
},
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'arrayText1',
|
||||
label: 'Array Text 1',
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
defaultValue: 'default array text',
|
||||
},
|
||||
{
|
||||
name: 'arrayText2',
|
||||
label: 'Array Text 2',
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
access: {
|
||||
read: ({ req: { user } }) => Boolean(user),
|
||||
update: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'arrayText3',
|
||||
label: 'Array Text 3',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'checkbox',
|
||||
label: 'Checkbox',
|
||||
type: 'checkbox',
|
||||
defaultValue: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'blocks',
|
||||
label: 'Blocks Content',
|
||||
name: 'blocks',
|
||||
labels: {
|
||||
singular: 'Block',
|
||||
plural: 'Blocks',
|
||||
},
|
||||
blocks: [Email, NumberBlock, Quote, CallToAction],
|
||||
localized: true,
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
defaultValue: [
|
||||
{
|
||||
blockType: 'email',
|
||||
testEmail: 'dev@payloadcms.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
label: 'Relationship to One Collection',
|
||||
name: 'relationship',
|
||||
relationTo: 'conditions',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
label: 'Relationship hasMany',
|
||||
name: 'relationshipHasMany',
|
||||
relationTo: 'localized-posts',
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
label: 'Relationship to Multiple Collections',
|
||||
name: 'relationshipMultipleCollections',
|
||||
relationTo: ['localized-posts', 'conditions'],
|
||||
},
|
||||
{
|
||||
type: 'textarea',
|
||||
label: 'Textarea',
|
||||
name: 'textarea',
|
||||
defaultValue: 'my textarea text',
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
label: 'Slug',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
localized: true,
|
||||
unique: true,
|
||||
defaultValue: 'my-slug',
|
||||
},
|
||||
{
|
||||
name: 'checkbox',
|
||||
type: 'checkbox',
|
||||
label: 'Checkbox',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
defaultValue: true,
|
||||
},
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
label: 'Rich Text',
|
||||
admin: {
|
||||
elements: [
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'blockquote',
|
||||
'ul',
|
||||
'ol',
|
||||
'link',
|
||||
],
|
||||
leaves: [
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'strikethrough',
|
||||
],
|
||||
},
|
||||
defaultValue: [{
|
||||
children: [{ text: 'Cookin now' }],
|
||||
}],
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
export default DefaultValues;
|
||||
@@ -1,70 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import checkRole from '../access/checkRole';
|
||||
|
||||
const access = ({ req: { user } }) => {
|
||||
const isAdmin = checkRole(['admin'], user);
|
||||
|
||||
if (isAdmin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (user) {
|
||||
return {
|
||||
owner: {
|
||||
equals: user.id,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const Files: CollectionConfig = {
|
||||
slug: 'files',
|
||||
labels: {
|
||||
singular: 'File',
|
||||
plural: 'Files',
|
||||
},
|
||||
upload: {
|
||||
staticURL: '/files',
|
||||
staticDir: './files',
|
||||
},
|
||||
access: {
|
||||
create: () => true,
|
||||
read: access,
|
||||
update: access,
|
||||
delete: access,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'type',
|
||||
label: 'Type',
|
||||
type: 'select',
|
||||
options: [{
|
||||
value: 'Type 1',
|
||||
label: 'Type 1 Label',
|
||||
}, {
|
||||
value: 'Type 2',
|
||||
label: 'Type 2 Label',
|
||||
}, {
|
||||
value: 'Type 3',
|
||||
label: 'Type 3 Label',
|
||||
}],
|
||||
defaultValue: 'Type 1',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'owner',
|
||||
label: 'Owner',
|
||||
type: 'relationship',
|
||||
relationTo: 'admins',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
admin: {
|
||||
useAsTitle: 'filename',
|
||||
},
|
||||
};
|
||||
|
||||
export default Files;
|
||||
@@ -1,35 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const HiddenFields: CollectionConfig = {
|
||||
slug: 'hidden-fields',
|
||||
labels: {
|
||||
singular: 'Hidden Fields',
|
||||
plural: 'Hidden Fields',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
label: 'Title - Not Hidden',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'hiddenAdmin',
|
||||
type: 'text',
|
||||
label: 'Hidden on Admin',
|
||||
admin: {
|
||||
hidden: true,
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'hiddenAPI',
|
||||
type: 'text',
|
||||
label: 'Hidden on API',
|
||||
hidden: true,
|
||||
required: true, // this should not matter
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default HiddenFields;
|
||||
@@ -1,95 +0,0 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const Hooks: CollectionConfig = {
|
||||
slug: 'hooks',
|
||||
labels: {
|
||||
singular: 'Hook',
|
||||
plural: 'Hooks',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
},
|
||||
access: {
|
||||
create: () => true,
|
||||
read: () => true,
|
||||
update: () => true,
|
||||
delete: () => true,
|
||||
},
|
||||
hooks: {
|
||||
beforeRead: [
|
||||
(operation) => {
|
||||
if (operation.req.headers.hook === 'beforeRead') {
|
||||
console.log('before reading Hooks document');
|
||||
}
|
||||
},
|
||||
],
|
||||
beforeChange: [
|
||||
(operation) => {
|
||||
if (operation.req.headers.hook === 'beforeChange') {
|
||||
operation.data.description += '-beforeChangeSuffix';
|
||||
}
|
||||
return operation.data;
|
||||
},
|
||||
],
|
||||
beforeDelete: [
|
||||
(operation) => {
|
||||
if (operation.req.headers.hook === 'beforeDelete') {
|
||||
// TODO: Find a better hook operation to assert against in tests
|
||||
operation.req.headers.hook = 'afterDelete';
|
||||
}
|
||||
},
|
||||
],
|
||||
afterRead: [
|
||||
(operation) => {
|
||||
const { doc } = operation;
|
||||
doc.afterReadHook = true;
|
||||
|
||||
return doc;
|
||||
},
|
||||
],
|
||||
afterChange: [
|
||||
(operation) => {
|
||||
if (operation.req.headers.hook === 'afterChange') {
|
||||
operation.doc.afterChangeHook = true;
|
||||
}
|
||||
return operation.doc;
|
||||
},
|
||||
],
|
||||
afterDelete: [
|
||||
(operation) => {
|
||||
if (operation.req.headers.hook === 'afterDelete') {
|
||||
operation.doc.afterDeleteHook = true;
|
||||
}
|
||||
return operation.doc;
|
||||
},
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
maxLength: 100,
|
||||
required: true,
|
||||
unique: true,
|
||||
localized: true,
|
||||
hooks: {
|
||||
afterRead: [
|
||||
({ value }) => (value ? value.toUpperCase() : null),
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
type: 'textarea',
|
||||
required: true,
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default Hooks;
|
||||
@@ -1,38 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const LocalOperations: CollectionConfig = {
|
||||
slug: 'local-operations',
|
||||
labels: {
|
||||
singular: 'Local Operation',
|
||||
plural: 'Local Operations',
|
||||
},
|
||||
hooks: {
|
||||
afterRead: [
|
||||
async ({ req, doc }) => {
|
||||
const formattedData = { ...doc };
|
||||
const localizedPosts = await req.payload.find({
|
||||
collection: 'localized-posts',
|
||||
});
|
||||
|
||||
const blocksGlobal = await req.payload.findGlobal({
|
||||
slug: 'blocks-global',
|
||||
});
|
||||
|
||||
formattedData.localizedPosts = localizedPosts;
|
||||
formattedData.blocksGlobal = blocksGlobal;
|
||||
|
||||
return formattedData;
|
||||
},
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
label: 'title',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default LocalOperations;
|
||||
@@ -1,142 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import { PayloadRequest } from '../../src/express/types';
|
||||
import { Block } from '../../src/fields/config/types';
|
||||
|
||||
const validateLocalizationTransform = (hook: string, value, req: PayloadRequest) => {
|
||||
if (req.locale !== 'all' && value !== undefined && typeof value !== 'string') {
|
||||
console.error(hook, value);
|
||||
throw new Error('Locale transformation should happen before hook is called');
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const RichTextBlock: Block = {
|
||||
slug: 'richTextBlock',
|
||||
labels: {
|
||||
singular: 'Rich Text Block',
|
||||
plural: 'Rich Text Blocks',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'content',
|
||||
localized: true,
|
||||
type: 'richText',
|
||||
admin: {
|
||||
hideGutter: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const LocalizedPosts: CollectionConfig = {
|
||||
slug: 'localized-posts',
|
||||
labels: {
|
||||
singular: 'Localized Post',
|
||||
plural: 'Localized Posts',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
defaultColumns: [
|
||||
'title',
|
||||
'priority',
|
||||
'createdAt',
|
||||
],
|
||||
enableRichTextRelationship: true,
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
maxLength: 100,
|
||||
required: true,
|
||||
unique: true,
|
||||
localized: true,
|
||||
hooks: {
|
||||
beforeValidate: [({ value, req }) => validateLocalizationTransform('beforeValidate', value, req)],
|
||||
beforeChange: [({ value, req }) => validateLocalizationTransform('beforeChange', value, req)],
|
||||
afterChange: [({ value, req }) => validateLocalizationTransform('afterChange', value, req)],
|
||||
afterRead: [({ value, req }) => validateLocalizationTransform('afterRead', value, req)],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'summary',
|
||||
label: 'Summary',
|
||||
type: 'text',
|
||||
index: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
type: 'textarea',
|
||||
required: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
type: 'richText',
|
||||
name: 'richText',
|
||||
label: 'Rich Text',
|
||||
},
|
||||
{
|
||||
name: 'priority',
|
||||
label: 'Priority',
|
||||
type: 'number',
|
||||
required: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'localizedGroup',
|
||||
label: 'Localized Group',
|
||||
type: 'group',
|
||||
localized: true,
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'text',
|
||||
label: 'Text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'nonLocalizedGroup',
|
||||
label: 'Non-Localized Group',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'text',
|
||||
label: 'Text',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
label: 'Non-Localized Array',
|
||||
name: 'nonLocalizedArray',
|
||||
maxRows: 3,
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'localizedEmbeddedText',
|
||||
label: 'Localized Embedded Text',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Blocks',
|
||||
name: 'richTextBlocks',
|
||||
type: 'blocks',
|
||||
blocks: [
|
||||
RichTextBlock,
|
||||
],
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default LocalizedPosts;
|
||||
@@ -1,79 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import { FieldAccess } from '../../src/fields/config/types';
|
||||
import checkRole from '../access/checkRole';
|
||||
|
||||
const PublicReadabilityAccess: FieldAccess = ({ req: { user }, siblingData }) => {
|
||||
if (checkRole(['admin'], user)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (siblingData?.allowPublicReadability) return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const LocalizedArrays: CollectionConfig = {
|
||||
slug: 'localized-arrays',
|
||||
labels: {
|
||||
singular: 'Localized Array',
|
||||
plural: 'Localized Arrays',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'array',
|
||||
label: false,
|
||||
name: 'array',
|
||||
localized: true,
|
||||
required: true,
|
||||
minRows: 2,
|
||||
maxRows: 4,
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'allowPublicReadability',
|
||||
label: 'Allow Public Readability',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'arrayText1',
|
||||
label: 'Array Text 1',
|
||||
type: 'text',
|
||||
required: true,
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
access: {
|
||||
read: PublicReadabilityAccess,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'arrayText2',
|
||||
label: 'Array Text 2',
|
||||
type: 'text',
|
||||
required: true,
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'arrayText3',
|
||||
label: 'Array Text 3',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default LocalizedArrays;
|
||||
@@ -1,78 +0,0 @@
|
||||
import { CollectionConfig, BeforeChangeHook } from '../../src/collections/config/types';
|
||||
|
||||
const checkForUploadSizesHook: BeforeChangeHook = ({ req: { payloadUploadSizes }, data }) => {
|
||||
if (typeof payloadUploadSizes === 'object') {
|
||||
return {
|
||||
...data,
|
||||
foundUploadSizes: true,
|
||||
};
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
const Media: CollectionConfig = {
|
||||
slug: 'media',
|
||||
labels: {
|
||||
singular: 'Media',
|
||||
plural: 'Media',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
admin: {
|
||||
enableRichTextRelationship: true,
|
||||
description: 'No selfies please',
|
||||
},
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
checkForUploadSizesHook,
|
||||
],
|
||||
},
|
||||
upload: {
|
||||
staticURL: '/media',
|
||||
staticDir: './media',
|
||||
adminThumbnail: ({ doc }) => `/media/${doc.filename}`,
|
||||
imageSizes: [
|
||||
{
|
||||
name: 'maintainedAspectRatio',
|
||||
width: 1024,
|
||||
height: null,
|
||||
crop: 'center',
|
||||
},
|
||||
{
|
||||
name: 'tablet',
|
||||
width: 640,
|
||||
height: 480,
|
||||
crop: 'left top',
|
||||
},
|
||||
{
|
||||
name: 'mobile',
|
||||
width: 320,
|
||||
height: 240,
|
||||
crop: 'left top',
|
||||
},
|
||||
{
|
||||
name: 'icon',
|
||||
width: 16,
|
||||
height: 16,
|
||||
},
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'alt',
|
||||
label: 'Alt Text',
|
||||
type: 'text',
|
||||
required: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'foundUploadSizes',
|
||||
type: 'checkbox',
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default Media;
|
||||
@@ -1,71 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const NestedArray: CollectionConfig = {
|
||||
slug: 'nested-arrays',
|
||||
labels: {
|
||||
singular: 'Nested Array',
|
||||
plural: 'Nested Arrays',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'array',
|
||||
label: 'Array',
|
||||
name: 'array',
|
||||
labels: {
|
||||
singular: 'Parent Row',
|
||||
plural: 'Parent Rows',
|
||||
},
|
||||
required: true,
|
||||
minRows: 2,
|
||||
maxRows: 4,
|
||||
fields: [
|
||||
{
|
||||
name: 'parentIdentifier',
|
||||
label: 'Parent Identifier',
|
||||
defaultValue: ' ',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
name: 'nestedArray',
|
||||
labels: {
|
||||
singular: 'Child Row',
|
||||
plural: 'Child Rows',
|
||||
},
|
||||
required: true,
|
||||
fields: [
|
||||
{
|
||||
name: 'childIdentifier',
|
||||
label: 'Child Identifier',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
name: 'deeplyNestedArray',
|
||||
labels: {
|
||||
singular: 'Grandchild Row',
|
||||
plural: 'Grandchild Rows',
|
||||
},
|
||||
required: true,
|
||||
fields: [
|
||||
{
|
||||
name: 'grandchildIdentifier',
|
||||
label: 'Grandchild Identifier',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default NestedArray;
|
||||
@@ -1,34 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const Preview: CollectionConfig = {
|
||||
slug: 'previewable-post',
|
||||
labels: {
|
||||
singular: 'Previewable Post',
|
||||
plural: 'Previewable Posts',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
preview: (doc, { token }) => {
|
||||
const { title } = doc;
|
||||
if (title) {
|
||||
return `http://localhost:3000/previewable-posts/${title}?preview=true&token=${token}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
maxLength: 100,
|
||||
required: true,
|
||||
unique: true,
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default Preview;
|
||||
@@ -1,61 +0,0 @@
|
||||
import checkRole from '../access/checkRole';
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const access = ({ req: { user } }) => checkRole(['admin'], user);
|
||||
|
||||
const PublicUsers: CollectionConfig = {
|
||||
slug: 'public-users',
|
||||
labels: {
|
||||
singular: 'Public User',
|
||||
plural: 'Public Users',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'email',
|
||||
},
|
||||
access: {
|
||||
admin: () => false,
|
||||
create: () => true,
|
||||
read: () => true,
|
||||
update: ({ req: { user } }) => {
|
||||
if (checkRole(['admin'], user)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (user) {
|
||||
return {
|
||||
id: user.id,
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
delete: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
},
|
||||
auth: {
|
||||
tokenExpiration: 300,
|
||||
verify: true,
|
||||
maxLoginAttempts: 5,
|
||||
lockTime: 600 * 1000, // lock time in ms
|
||||
cookies: {
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
sameSite: 'lax',
|
||||
domain: undefined,
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'adminOnly',
|
||||
label: 'This field should only be readable and editable by Admins with "admin" role',
|
||||
type: 'text',
|
||||
defaultValue: 'test',
|
||||
access: {
|
||||
create: access,
|
||||
read: access,
|
||||
update: access,
|
||||
},
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default PublicUsers;
|
||||
@@ -1,64 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const RelationshipA: CollectionConfig = {
|
||||
slug: 'relationship-a',
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
labels: {
|
||||
singular: 'Relationship A',
|
||||
plural: 'Relationship A',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'post',
|
||||
label: 'Post',
|
||||
type: 'relationship',
|
||||
relationTo: 'relationship-b',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'LocalizedPost',
|
||||
label: 'Localized Post',
|
||||
type: 'relationship',
|
||||
relationTo: 'localized-posts',
|
||||
hasMany: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'postLocalizedMultiple',
|
||||
label: 'Localized Post Multiple',
|
||||
type: 'relationship',
|
||||
relationTo: ['localized-posts', 'all-fields', 'custom-id'],
|
||||
hasMany: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'postManyRelationships',
|
||||
label: 'Post Many Relationships',
|
||||
type: 'relationship',
|
||||
relationTo: ['relationship-b'],
|
||||
localized: true,
|
||||
hasMany: false,
|
||||
},
|
||||
{
|
||||
name: 'postMaxDepth',
|
||||
maxDepth: 0,
|
||||
label: 'Post With MaxDepth',
|
||||
type: 'relationship',
|
||||
relationTo: 'relationship-b',
|
||||
hasMany: false,
|
||||
},
|
||||
{
|
||||
name: 'customID',
|
||||
label: 'CustomID Relation',
|
||||
type: 'relationship',
|
||||
relationTo: 'custom-id',
|
||||
hasMany: true,
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default RelationshipA;
|
||||
@@ -1,45 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const RelationshipB: CollectionConfig = {
|
||||
slug: 'relationship-b',
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
labels: {
|
||||
singular: 'Relationship B',
|
||||
plural: 'Relationship B',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'post',
|
||||
label: 'Post',
|
||||
type: 'relationship',
|
||||
relationTo: 'relationship-a',
|
||||
localized: false,
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
name: 'postManyRelationships',
|
||||
label: 'Post Many Relationships',
|
||||
type: 'relationship',
|
||||
relationTo: ['relationship-a', 'media'],
|
||||
localized: true,
|
||||
hasMany: false,
|
||||
},
|
||||
{
|
||||
name: 'localizedPosts',
|
||||
label: 'Localized Posts',
|
||||
type: 'relationship',
|
||||
hasMany: true,
|
||||
relationTo: ['localized-posts', 'previewable-post'],
|
||||
},
|
||||
{
|
||||
name: 'strictAccess',
|
||||
type: 'relationship',
|
||||
relationTo: 'strict-access',
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default RelationshipB;
|
||||
@@ -1,41 +0,0 @@
|
||||
import Button from '../client/components/richText/elements/Button';
|
||||
import PurpleBackground from '../client/components/richText/leaves/PurpleBackground';
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const RichText: CollectionConfig = {
|
||||
slug: 'rich-text',
|
||||
labels: {
|
||||
singular: 'Rich Text',
|
||||
plural: 'Rich Texts',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'defaultRichText',
|
||||
type: 'richText',
|
||||
label: 'Default Rich Text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'customRichText',
|
||||
type: 'richText',
|
||||
label: 'Customized Rich Text',
|
||||
required: true,
|
||||
admin: {
|
||||
elements: [
|
||||
'h2',
|
||||
'h3',
|
||||
Button,
|
||||
],
|
||||
leaves: [
|
||||
'bold',
|
||||
PurpleBackground,
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default RichText;
|
||||
@@ -1,70 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const Select: CollectionConfig = {
|
||||
slug: 'select',
|
||||
labels: {
|
||||
singular: 'Select',
|
||||
plural: 'Selects',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'Select',
|
||||
type: 'select',
|
||||
options: [{
|
||||
value: 'one',
|
||||
label: 'One',
|
||||
}, {
|
||||
value: 'two',
|
||||
label: 'Two',
|
||||
}, {
|
||||
value: 'three',
|
||||
label: 'Three',
|
||||
}],
|
||||
label: 'Select From',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'SelectHasMany',
|
||||
type: 'select',
|
||||
options: [{
|
||||
value: 'one',
|
||||
label: 'One',
|
||||
}, {
|
||||
value: 'two',
|
||||
label: 'Two',
|
||||
}, {
|
||||
value: 'three',
|
||||
label: 'Three',
|
||||
}],
|
||||
label: 'Select HasMany',
|
||||
required: true,
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
name: 'SelectJustStrings',
|
||||
type: 'select',
|
||||
options: ['blue', 'green', 'yellow'],
|
||||
label: 'Select Just Strings',
|
||||
required: true,
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
name: 'Radio',
|
||||
type: 'radio',
|
||||
options: [{
|
||||
value: 'one',
|
||||
label: 'One',
|
||||
}, {
|
||||
value: 'two',
|
||||
label: 'Two',
|
||||
}, {
|
||||
value: 'three',
|
||||
label: 'Three',
|
||||
}],
|
||||
label: 'Choose From',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Select;
|
||||
@@ -1,76 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import checkRole from '../access/checkRole';
|
||||
|
||||
const StrictAccess: CollectionConfig = {
|
||||
slug: 'strict-access',
|
||||
labels: {
|
||||
singular: 'Strict Access',
|
||||
plural: 'Strict Access',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'address',
|
||||
},
|
||||
access: {
|
||||
create: () => true,
|
||||
read: ({ req: { user } }) => {
|
||||
if (checkRole(['admin'], user)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (user) {
|
||||
return {
|
||||
owner: {
|
||||
equals: user.id,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
update: ({ req: { user } }) => {
|
||||
if (checkRole(['admin'], user)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (user) {
|
||||
return {
|
||||
owner: {
|
||||
equals: user.id,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
delete: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'address',
|
||||
type: 'text',
|
||||
label: 'Address',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'city',
|
||||
type: 'text',
|
||||
label: 'City',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'state',
|
||||
type: 'text',
|
||||
label: 'State',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'zip',
|
||||
type: 'number',
|
||||
label: 'ZIP Code',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default StrictAccess;
|
||||
@@ -1,25 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const Uniques: CollectionConfig = {
|
||||
slug: 'uniques',
|
||||
labels: {
|
||||
singular: 'Unique',
|
||||
plural: 'Uniques',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
label: 'Title',
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
label: 'Description',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Uniques;
|
||||
@@ -1,35 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const UnstoredMedia: CollectionConfig = {
|
||||
slug: 'unstored-media',
|
||||
labels: {
|
||||
singular: 'Unstored Media',
|
||||
plural: 'Unstored Media',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
upload: {
|
||||
staticURL: '/unstored-media',
|
||||
disableLocalStorage: true,
|
||||
imageSizes: [
|
||||
{
|
||||
name: 'tablet',
|
||||
width: 640,
|
||||
height: 480,
|
||||
crop: 'left top',
|
||||
},
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'alt',
|
||||
label: 'Alt Text',
|
||||
type: 'text',
|
||||
required: true,
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default UnstoredMedia;
|
||||
@@ -1,113 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const Validations: CollectionConfig = {
|
||||
slug: 'validations',
|
||||
labels: {
|
||||
singular: 'Validation',
|
||||
plural: 'Validations',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
label: 'Text',
|
||||
required: true,
|
||||
validate: (value) => {
|
||||
const result = value === 'test';
|
||||
|
||||
if (!result) {
|
||||
return 'The only accepted value of this field is "test".';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'lessThan10',
|
||||
label: 'Less than 10',
|
||||
type: 'number',
|
||||
required: true,
|
||||
validate: (value) => {
|
||||
const result = parseInt(value, 10) < 10;
|
||||
|
||||
if (!result) {
|
||||
return 'The value of this field needs to be less than 10.';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
}, {
|
||||
name: 'greaterThan10LessThan50',
|
||||
label: 'Greater than 10, Less than 50',
|
||||
type: 'number',
|
||||
required: true,
|
||||
min: 10,
|
||||
max: 50,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
label: 'Should have at least 3 rows',
|
||||
name: 'atLeast3Rows',
|
||||
required: true,
|
||||
validate: (value) => {
|
||||
const result = value && value.length >= 3;
|
||||
|
||||
if (!result) {
|
||||
return 'This array needs to have at least 3 rows.';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'number',
|
||||
name: 'greaterThan30',
|
||||
label: 'Number should be greater than 30',
|
||||
required: true,
|
||||
validate: (value) => {
|
||||
const result = value > 30;
|
||||
|
||||
if (!result) {
|
||||
return 'This value of this field needs to be greater than 30.';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
label: 'Default array validation',
|
||||
name: 'array',
|
||||
required: true,
|
||||
fields: [
|
||||
{
|
||||
type: 'number',
|
||||
name: 'lessThan20',
|
||||
label: 'Number should be less than 20',
|
||||
required: true,
|
||||
validate: (value) => {
|
||||
const result = value < 30;
|
||||
|
||||
if (!result) {
|
||||
return 'This value of this field needs to be less than 20.';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Validations;
|
||||
@@ -1,15 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="application-name" content="My Payload Application" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<div id="portal"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,9 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const CollectionDescription: React.FC = () => (
|
||||
<div>
|
||||
Collection description
|
||||
</div>
|
||||
);
|
||||
|
||||
export default CollectionDescription;
|
||||
@@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const CustomDescriptionComponent: React.FC = ({ value }) => (
|
||||
<div>
|
||||
Character count:
|
||||
{' '}
|
||||
{ value?.length || 0 }
|
||||
</div>
|
||||
);
|
||||
|
||||
export default CustomDescriptionComponent;
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const CustomAccountView: React.FC = () => <div>fake account view</div>;
|
||||
|
||||
export default CustomAccountView;
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const CustomDashboardView: React.FC = () => <div>fake dashboard view</div>;
|
||||
|
||||
export default CustomDashboardView;
|
||||
@@ -1,22 +0,0 @@
|
||||
import checkRole from '../access/checkRole';
|
||||
import Quote from '../blocks/Quote';
|
||||
import CallToAction from '../blocks/CallToAction';
|
||||
import { GlobalConfig } from '../../src/globals/config/types';
|
||||
|
||||
export default {
|
||||
slug: 'blocks-global',
|
||||
label: 'Blocks Global',
|
||||
access: {
|
||||
update: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'blocks',
|
||||
label: 'Blocks',
|
||||
type: 'blocks',
|
||||
blocks: [Quote, CallToAction],
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
} as GlobalConfig;
|
||||
@@ -1,35 +0,0 @@
|
||||
import { GlobalConfig } from '../../src/globals/config/types';
|
||||
import checkRole from '../access/checkRole';
|
||||
|
||||
export default {
|
||||
slug: 'global-with-access',
|
||||
label: 'Global with Strict Access',
|
||||
access: {
|
||||
update: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
read: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Site Title',
|
||||
type: 'text',
|
||||
maxLength: 100,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'relationship',
|
||||
label: 'Test Relationship',
|
||||
type: 'relationship',
|
||||
relationTo: 'localized-posts',
|
||||
hasMany: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'singleRelationship',
|
||||
label: 'Test Single Relationship',
|
||||
type: 'relationship',
|
||||
relationTo: 'localized-posts',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
} as GlobalConfig;
|
||||
@@ -1,30 +0,0 @@
|
||||
import { GlobalConfig } from '../../src/globals/config/types';
|
||||
import checkRole from '../access/checkRole';
|
||||
|
||||
export default {
|
||||
slug: 'navigation-array',
|
||||
access: {
|
||||
update: ({ req: { user } }) => checkRole(['admin', 'user'], user),
|
||||
read: () => true,
|
||||
},
|
||||
admin: {
|
||||
description: 'A description for the editor',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'array',
|
||||
label: 'Array',
|
||||
type: 'array',
|
||||
localized: true,
|
||||
fields: [{
|
||||
name: 'text',
|
||||
label: 'Text',
|
||||
type: 'text',
|
||||
}, {
|
||||
name: 'textarea',
|
||||
label: 'Textarea',
|
||||
type: 'textarea',
|
||||
}],
|
||||
},
|
||||
],
|
||||
} as GlobalConfig;
|
||||
@@ -1,14 +0,0 @@
|
||||
const babelConfig = require('../babel.config');
|
||||
|
||||
require('@babel/register')({
|
||||
...babelConfig,
|
||||
extensions: ['.ts', '.tsx', '.js', '.jsx'],
|
||||
env: {
|
||||
development: {
|
||||
sourceMaps: 'inline',
|
||||
retainLines: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
require('./server.ts');
|
||||
@@ -1,144 +0,0 @@
|
||||
import path from 'path';
|
||||
import { buildConfig } from '../src/config/build';
|
||||
|
||||
import Admin from './collections/Admin';
|
||||
import AllFields from './collections/AllFields';
|
||||
import AutoLabel from './collections/AutoLabel';
|
||||
import Code from './collections/Code';
|
||||
import Conditions from './collections/Conditions';
|
||||
import CustomComponents from './collections/CustomComponents';
|
||||
import File from './collections/File';
|
||||
import Blocks from './collections/Blocks';
|
||||
import CustomID from './collections/CustomID';
|
||||
import DefaultValues from './collections/DefaultValues';
|
||||
import HiddenFields from './collections/HiddenFields';
|
||||
import Hooks from './collections/Hooks';
|
||||
import Localized from './collections/Localized';
|
||||
import LocalizedArray from './collections/LocalizedArray';
|
||||
import LocalOperations from './collections/LocalOperations';
|
||||
import Media from './collections/Media';
|
||||
import NestedArrays from './collections/NestedArrays';
|
||||
import Preview from './collections/Preview';
|
||||
import PublicUsers from './collections/PublicUsers';
|
||||
import RelationshipA from './collections/RelationshipA';
|
||||
import RelationshipB from './collections/RelationshipB';
|
||||
import RichText from './collections/RichText';
|
||||
import Select from './collections/Select';
|
||||
import StrictAccess from './collections/StrictAccess';
|
||||
import Validations from './collections/Validations';
|
||||
import Uniques from './collections/Uniques';
|
||||
import Geolocation from './collections/Geolocation';
|
||||
|
||||
import BlocksGlobal from './globals/BlocksGlobal';
|
||||
import NavigationArray from './globals/NavigationArray';
|
||||
import GlobalWithStrictAccess from './globals/GlobalWithStrictAccess';
|
||||
import UnstoredMedia from './collections/UnstoredMedia';
|
||||
|
||||
export default buildConfig({
|
||||
cookiePrefix: 'payload',
|
||||
serverURL: 'http://localhost:3000',
|
||||
admin: {
|
||||
user: 'admins',
|
||||
indexHTML: path.resolve(__dirname, './client/index.html'),
|
||||
// meta: {
|
||||
// titleSuffix: '- Payload Demo',
|
||||
// // ogImage: '/static/find-image-here.jpg',
|
||||
// // favicon: '/img/whatever.png',
|
||||
// },
|
||||
disable: false,
|
||||
scss: path.resolve(__dirname, './client/scss/overrides.scss'),
|
||||
components: {
|
||||
// Nav: () => (
|
||||
// <div>Hello</div>
|
||||
// ),
|
||||
views: {
|
||||
// Dashboard: CustomDashboardView,
|
||||
// Account: CustomAccountView,
|
||||
},
|
||||
},
|
||||
webpack: (config) => config,
|
||||
},
|
||||
collections: [
|
||||
Admin,
|
||||
AllFields,
|
||||
AutoLabel,
|
||||
Code,
|
||||
Conditions,
|
||||
CustomComponents,
|
||||
CustomID,
|
||||
File,
|
||||
DefaultValues,
|
||||
Blocks,
|
||||
HiddenFields,
|
||||
Hooks,
|
||||
Localized,
|
||||
LocalizedArray,
|
||||
LocalOperations,
|
||||
Media,
|
||||
NestedArrays,
|
||||
Preview,
|
||||
PublicUsers,
|
||||
RelationshipA,
|
||||
RelationshipB,
|
||||
RichText,
|
||||
Select,
|
||||
StrictAccess,
|
||||
Validations,
|
||||
Uniques,
|
||||
UnstoredMedia,
|
||||
Geolocation,
|
||||
],
|
||||
globals: [
|
||||
NavigationArray,
|
||||
GlobalWithStrictAccess,
|
||||
BlocksGlobal,
|
||||
],
|
||||
cors: [
|
||||
'http://localhost',
|
||||
'http://localhost:3000',
|
||||
'http://localhost:8080',
|
||||
'http://localhost:8081',
|
||||
],
|
||||
csrf: [
|
||||
'http://localhost:3000',
|
||||
'https://other-app-here.com',
|
||||
],
|
||||
routes: {
|
||||
api: '/api',
|
||||
admin: '/admin',
|
||||
graphQL: '/graphql',
|
||||
graphQLPlayground: '/graphql-playground',
|
||||
},
|
||||
defaultDepth: 2,
|
||||
graphQL: {
|
||||
maxComplexity: 1000,
|
||||
disablePlaygroundInProduction: false,
|
||||
disable: false,
|
||||
},
|
||||
// rateLimit: {
|
||||
// window: 15 * 60 * 100,
|
||||
// max: 100,
|
||||
// trustProxy: true,
|
||||
// skip: (req) => req.ip === '127.0.0.1',
|
||||
// },
|
||||
maxDepth: 10,
|
||||
localization: {
|
||||
locales: [
|
||||
'en',
|
||||
'es',
|
||||
],
|
||||
defaultLocale: 'en',
|
||||
fallback: true,
|
||||
},
|
||||
// indexSortableFields: true,
|
||||
hooks: {
|
||||
afterError: (err) => {
|
||||
console.error('global error config handler', err);
|
||||
},
|
||||
},
|
||||
upload: {
|
||||
limits: {
|
||||
fileSize: 10000000, // 10MB
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,40 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
import express from 'express';
|
||||
import path from 'path';
|
||||
import payload from '../src';
|
||||
|
||||
const expressApp = express();
|
||||
|
||||
expressApp.use('/static', express.static(path.resolve(__dirname, 'client/static')));
|
||||
|
||||
payload.init({
|
||||
secret: 'SECRET_KEY',
|
||||
mongoURL: 'mongodb://localhost/payload',
|
||||
express: expressApp,
|
||||
email: {
|
||||
fromName: 'Payload',
|
||||
fromAddress: 'hello@payloadcms.com',
|
||||
},
|
||||
onInit: (app) => {
|
||||
app.logger.info('Payload Demo Initialized');
|
||||
},
|
||||
});
|
||||
|
||||
const externalRouter = express.Router();
|
||||
|
||||
externalRouter.use(payload.authenticate);
|
||||
|
||||
externalRouter.get('/', (req, res) => {
|
||||
if (req.user) {
|
||||
return res.send(`Authenticated successfully as ${req.user.email}.`);
|
||||
}
|
||||
|
||||
return res.send('Not authenticated');
|
||||
});
|
||||
|
||||
expressApp.use('/external-route', externalRouter);
|
||||
|
||||
expressApp.listen(3000, async () => {
|
||||
payload.logger.info(`Admin URL on ${payload.getAdminURL()}`);
|
||||
payload.logger.info(`API URL on ${payload.getAPIURL()}`);
|
||||
});
|
||||
@@ -27,8 +27,10 @@ If a Collection supports [`Authentication`](/docs/authentication/overview), the
|
||||
| **[`unlock`](#unlock)** | Used to restrict which users can access the `unlock` operation |
|
||||
|
||||
**Example Collection config:**
|
||||
```js
|
||||
export default {
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const Posts: CollectionConfig = {
|
||||
slug: "posts",
|
||||
// highlight-start
|
||||
access: {
|
||||
@@ -40,56 +42,135 @@ export default {
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
|
||||
export default Categories;
|
||||
```
|
||||
|
||||
### Create
|
||||
|
||||
Returns a boolean which allows/denies access to the `create` request.
|
||||
|
||||
**Available argument properties :**
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| Option | Description |
|
||||
| ---------- | ----------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`data`** | The data passed to create the document with. |
|
||||
|
||||
**Example:**
|
||||
|
||||
```ts
|
||||
const PublicUsers = {
|
||||
slug: 'public-users',
|
||||
access: {
|
||||
// highlight-start
|
||||
// allow guest users to self-registration
|
||||
create: () => true,
|
||||
// highlight-end
|
||||
...
|
||||
},
|
||||
fields: [ ... ],
|
||||
}
|
||||
```
|
||||
|
||||
### Read
|
||||
|
||||
Read access functions can return a boolean result or optionally return a [query constraint](/docs/queries/overview) which limits the documents that are returned to only those that match the constraint you provide. This can be helpful to restrict users' access to only certain documents however you specify.
|
||||
|
||||
**Available argument properties :**
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`id`** | `id` of document requested, if within `findByID`. Otherwise, `id` is undefined. |
|
||||
| **`id`** | `id` of document requested, if within `findByID` |
|
||||
|
||||
**Example:**
|
||||
|
||||
```ts
|
||||
import { Access } from 'payload/config';
|
||||
|
||||
const canReadPage: Access = ({ req: { user } }) => {
|
||||
// allow authenticated users
|
||||
if (user) {
|
||||
return true;
|
||||
}
|
||||
// using a query constraint, guest users can access when a field named 'isPublic' is set to true
|
||||
return {
|
||||
// assumes we have a checkbox field named 'isPublic'
|
||||
isPublic: {
|
||||
equals: true,
|
||||
},
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Update
|
||||
|
||||
Update access functions can return a boolean result or optionally return a [query constraint](/docs/queries/overview) to limit the document(s) that can be updated by the currently authenticated user. For example, returning a `query` from the `update` Access Control is helpful in cases where you would like to restrict a user to only being able to update the documents containing a `createdBy` relationship field equal to the user's ID.
|
||||
|
||||
**Available argument properties :**
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`id`** | `id` of document requested to update |
|
||||
| Option | Description |
|
||||
| ---------- | ----------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`id`** | `id` of document requested to update |
|
||||
| **`data`** | The data passed to update the document with |
|
||||
|
||||
**Example:**
|
||||
|
||||
```ts
|
||||
import { Access } from 'payload/config';
|
||||
|
||||
const canUpdateUser: Access = ({ req: { user }, id }) => {
|
||||
// allow users with a role of 'admin'
|
||||
if (user.roles && user.roles.some(role => role === 'admin')) {
|
||||
return true;
|
||||
}
|
||||
// allow any other users to update only oneself
|
||||
return user.id === id;
|
||||
};
|
||||
```
|
||||
|
||||
### Delete
|
||||
|
||||
Similarly to the Update function, returns a boolean or a [query constraint](/docs/queries/overview) to limit which documents can be deleted by which users.
|
||||
|
||||
**Available argument properties :**
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------- |
|
||||
| **`req`** | The Express `request` object with additional `user` property, which is the currently logged in user |
|
||||
| **`id`** | `id` of document requested to delete |
|
||||
|
||||
**Example:**
|
||||
|
||||
```ts
|
||||
import { Access } from 'payload/config'
|
||||
|
||||
const canDeleteCustomer: Access = async ({ req, id }) => {
|
||||
if (!id) {
|
||||
// allow the admin UI to show controls to delete since it is indeterminate without the id
|
||||
return true;
|
||||
}
|
||||
// query another collection using the id
|
||||
const result = await req.payload.find({
|
||||
collection: 'contracts',
|
||||
limit: 0,
|
||||
depth: 0,
|
||||
where: {
|
||||
customer: { equals: id },
|
||||
},
|
||||
});
|
||||
|
||||
return result.totalDocs === 0;
|
||||
};
|
||||
```
|
||||
|
||||
### Admin
|
||||
|
||||
If the Collection is [used to access the Payload Admin panel](/docs/admin/overview#the-admin-user-collection), the `Admin` Access Control function determines whether or not the currently logged in user can access the admin UI.
|
||||
|
||||
**Available argument properties :**
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------- |
|
||||
@@ -99,7 +180,7 @@ If the Collection is [used to access the Payload Admin panel](/docs/admin/overvi
|
||||
|
||||
Determines which users can [unlock](/docs/authentication/operations#unlock) other users who may be blocked from authenticating successfully due to [failing too many login attempts](/docs/authentication/config#options).
|
||||
|
||||
**Available argument properties :**
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------- |
|
||||
|
||||
@@ -17,8 +17,10 @@ Field Access Control is specified with functions inside a field's config. All fi
|
||||
| **[`update`](#update)** | Allows or denies the ability to update a field's value |
|
||||
|
||||
**Example Collection config:**
|
||||
```js
|
||||
export default {
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const Posts: CollectionConfig = {
|
||||
slug: 'posts',
|
||||
fields: [
|
||||
{
|
||||
@@ -33,7 +35,7 @@ export default {
|
||||
// highlight-end
|
||||
};
|
||||
],
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Create
|
||||
@@ -58,7 +60,7 @@ Returns a boolean which allows or denies the ability to read a field's value. If
|
||||
| ----------------- | ----------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`id`** | `id` of the document being read |
|
||||
| **`data`** | The full data of the document being read. |
|
||||
| **`doc`** | The full document data. |
|
||||
| **`siblingData`** | Immediately adjacent field data of the document being read. |
|
||||
|
||||
### Update
|
||||
@@ -73,3 +75,4 @@ Returns a boolean which allows or denies the ability to update a field's value.
|
||||
| **`id`** | `id` of the document being updated |
|
||||
| **`data`** | The full data passed to update the document. |
|
||||
| **`siblingData`** | Immediately adjacent field data passed to update the document with. |
|
||||
| **`doc`** | The full document data, before the update is applied. |
|
||||
|
||||
@@ -18,21 +18,25 @@ You can define Global-level Access Control within each Global's `access` propert
|
||||
| **[`update`](#update)** | Used in the `update` Global operation |
|
||||
|
||||
**Example Global config:**
|
||||
```js
|
||||
export default {
|
||||
```ts
|
||||
import { GlobalConfig } from 'payload/types';
|
||||
|
||||
const Header: GlobalConfig = {
|
||||
slug: "header",
|
||||
// highlight-start
|
||||
access: {
|
||||
read: ({ req: { user } }) => { ... },
|
||||
update: ({ req: { user } }) => { ... },
|
||||
read: ({ req: { user } }) => { /* */ },
|
||||
update: ({ req: { user } }) => { /* */ },
|
||||
},
|
||||
// highlight-end
|
||||
};
|
||||
|
||||
export default Header;
|
||||
```
|
||||
|
||||
### Read
|
||||
|
||||
Returns a boolean result to allow or deny a user's ability to read the Global.
|
||||
Returns a boolean result or optionally a [query constraint](/docs/queries/overview) which limits who can read this global based on its current properties.
|
||||
|
||||
**Available argument properties:**
|
||||
|
||||
@@ -42,10 +46,11 @@ Returns a boolean result to allow or deny a user's ability to read the Global.
|
||||
|
||||
### Update
|
||||
|
||||
Returns a boolean result to allow or deny a user's ability to update the Global.
|
||||
Returns a boolean result or optionally a [query constraint](/docs/queries/overview) which limits who can update this global based on its current properties.
|
||||
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| --------- | ----------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| Option | Description |
|
||||
| ---------- | ----------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`data`** | The data passed to update the global with. |
|
||||
|
||||
@@ -23,7 +23,7 @@ Access control within Payload is extremely powerful while remaining easy and int
|
||||
|
||||
**Default Access function:**
|
||||
|
||||
```js
|
||||
```ts
|
||||
const defaultPayloadAccess = ({ req: { user } }) => {
|
||||
// Return `true` if a user is found
|
||||
// and `false` if it is undefined or null
|
||||
@@ -43,3 +43,30 @@ You can manage access within Payload on three different levels:
|
||||
- [Collections](/docs/access-control/collections)
|
||||
- [Fields](/docs/access-control/fields)
|
||||
- [Globals](/docs/access-control/globals)
|
||||
|
||||
|
||||
### When Access Control is Executed
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong><br/>
|
||||
Access control functions are utilized in two places. It's important to understand how and when your access control is executed.
|
||||
</Banner>
|
||||
|
||||
#### As you execute operations
|
||||
|
||||
When you perform Payload operations like `create`, `read`, `update`, and `delete`, your access control functions will be executed before any changes or operations are completed.
|
||||
|
||||
#### Within the Admin UI
|
||||
|
||||
The Payload Admin UI responds dynamically to the access control that you define. For example, if you restrict editing a `ExampleCollection` to only users that feature a `role` of `admin`, the Payload Admin UI will **hide** the `ExampleCollection` from the Admin UI entirely. This is super powerful and allows you to control who can do what with your Admin UI.
|
||||
|
||||
To accomplish this, Payload ships with an `Access` operation, which is executed when a user logs into the Admin UI. Payload will execute each one of your access control functions, across all collections, globals, and fields, at the top level and return a response that contains a reflection of what the currently authenticated user can do with your application.
|
||||
|
||||
### Argument Availability
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong><br/>
|
||||
When your access control functions are executed via the <strong>access</strong> operation, the <strong>id</strong> and <strong>data</strong> arguments will be <strong>undefined</strong>, because Payload is executing your functions without referencing a specific document.
|
||||
</Banner>
|
||||
|
||||
If you use `id` or `data` within your access control functions, make sure to check that they are defined first. If they are not, then you can assume that your access control is being executed via the `access` operation, to determine solely what the user can do within the Admin UI.
|
||||
|
||||
@@ -22,20 +22,34 @@ You can override a set of admin panel-wide components by providing a component t
|
||||
| Path | Description |
|
||||
| --------------------- | -------------|
|
||||
| **`Nav`** | Contains the sidebar and mobile Nav in its entirety. |
|
||||
| **`BeforeDashboard`** | Array of components to inject into the built-in Dashboard, _before_ the default dashboard contents. |
|
||||
| **`AfterDashboard`** | Array of components to inject into the built-in Dashboard, _after_ the default dashboard contents. [Demo](https://github.com/payloadcms/payload/tree/master/test/admin/components/AfterDashboard/index.tsx)|
|
||||
| **`BeforeLogin`** | Array of components to inject into the built-in Login, _before_ the default login form. |
|
||||
| **`AfterLogin`** | Array of components to inject into the built-in Login, _after_ the default login form. |
|
||||
| **`BeforeNavLinks`** | Array of components to inject into the built-in Nav, _before_ the links themselves. |
|
||||
| **`AfterNavLinks`** | Array of components to inject into the built-in Nav, _after_ the links. |
|
||||
| **`views.Account`** | The Account view is used to show the currently logged in user's Account page. |
|
||||
| **`views.Dashboard`** | The main landing page of the Admin panel. |
|
||||
| **`graphics.Icon`** | Used as a graphic within the `Nav` component. Often represents a condensed version of a full logo. |
|
||||
| **`graphics.Logo`** | The full logo to be used in contexts like the `Login` view. |
|
||||
| **`routes`** | Define your own routes to add to the Payload Admin UI. [More](#custom-routes) |
|
||||
| **`providers`** | Define your own provider components that will wrap the Payload Admin UI. [More](#custom-providers) |
|
||||
|
||||
#### Full example:
|
||||
|
||||
`payload.config.js`
|
||||
```js
|
||||
import { buildConfig } from 'payload/config';
|
||||
import { MyCustomNav, MyCustomLogo, MyCustomIcon, MyCustomAccount, MyCustomDashboard } from './customComponents.js';
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config'
|
||||
import {
|
||||
MyCustomNav,
|
||||
MyCustomLogo,
|
||||
MyCustomIcon,
|
||||
MyCustomAccount,
|
||||
MyCustomDashboard,
|
||||
MyProvider,
|
||||
} from './customComponents';
|
||||
|
||||
export default buildConfig({
|
||||
serverURL: 'http://localhost:3000',
|
||||
admin: {
|
||||
components: {
|
||||
Nav: MyCustomNav,
|
||||
@@ -46,13 +60,14 @@ export default buildConfig({
|
||||
views: {
|
||||
Account: MyCustomAccount,
|
||||
Dashboard: MyCustomDashboard,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
providers: [MyProvider],
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
*For more examples regarding how to customize components, look at the [demo app](https://github.com/payloadcms/payload/tree/master/demo).*
|
||||
*For more examples regarding how to customize components, look at the following [examples](https://github.com/payloadcms/payload/tree/master/test/admin/components).*
|
||||
|
||||
### Collections
|
||||
|
||||
@@ -88,31 +103,144 @@ All Payload fields support the ability to swap in your own React components. So,
|
||||
| **`Cell`** | Used in the `List` view's table to represent a table-based preview of the data stored in the field. |
|
||||
| **`Field`** | Swap out the field itself within all `Edit` views. |
|
||||
|
||||
#### Sending and receiving values from the form
|
||||
### Sending and receiving values from the form
|
||||
|
||||
When swapping out the `Field` component, you'll be responsible for sending and receiving the field's `value` from the form itself. To do so, import the `useFieldType` hook as follows:
|
||||
When swapping out the `Field` component, you'll be responsible for sending and receiving the field's `value` from the form itself. To do so, import the `useField` hook as follows:
|
||||
|
||||
```js
|
||||
import { useFieldType } from 'payload/components/forms';
|
||||
```tsx
|
||||
import { useField } from 'payload/components/forms'
|
||||
|
||||
const CustomTextField = ({ path }) => {
|
||||
const { value, setValue } = useFieldType({ path });
|
||||
type Props = { path: string }
|
||||
|
||||
return (
|
||||
<input
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
value={value}
|
||||
/>
|
||||
)
|
||||
const CustomTextField: React.FC<Props> = ({ path }) => {
|
||||
// highlight-start
|
||||
const { value, setValue } = useField<Props>({ path })
|
||||
// highlight-end
|
||||
|
||||
return <input onChange={e => setValue(e.target.value)} value={value.path} />
|
||||
}
|
||||
```
|
||||
|
||||
### Getting other field values from the form
|
||||
|
||||
There are times when a custom field component needs to have access to data from other fields. This can be done using `getDataByPath` from `useWatchForm` as follows:
|
||||
|
||||
```tsx
|
||||
import { useWatchForm } from 'payload/components/forms';
|
||||
|
||||
const DisplayFee: React.FC = () => {
|
||||
const { getDataByPath } = useWatchForm();
|
||||
|
||||
const amount = getDataByPath('amount');
|
||||
const feePercentage = getDataByPath('feePercentage');
|
||||
|
||||
if (amount && feePercentage) {
|
||||
return (
|
||||
<span>The fee is ${(amount * feePercentage) / 100}</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Getting the document ID
|
||||
|
||||
The document ID can be very useful for certain custom components. You can get the `id` from the `useDocumentInfo` hook. Here is an example of a `UI` field using `id` to link to related collections:
|
||||
|
||||
```tsx
|
||||
import { useDocumentInfo } from 'payload/components/utilities';
|
||||
|
||||
const LinkFromCategoryToPosts: React.FC = () => {
|
||||
// highlight-start
|
||||
const { id } = useDocumentInfo();
|
||||
// highlight-end
|
||||
|
||||
// id will be undefined on the create form
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<a href={`/admin/collections/posts?where[or][0][and][0][category][in][0]=[${id}]`} >
|
||||
View posts
|
||||
</a>
|
||||
)
|
||||
};
|
||||
```
|
||||
|
||||
## Custom routes
|
||||
|
||||
You can easily add your own custom routes to the Payload Admin panel using the `admin.components.routes` property. Payload currently uses the extremely powerful React Router v5.x and custom routes support all the properties of the React Router `<Route />` component.
|
||||
|
||||
**Custom routes support the following properties:**
|
||||
|
||||
| Property | Description |
|
||||
| ----------------- | -------------|
|
||||
| **`Component`** * | Pass in the component that should be rendered when a user navigates to this route. |
|
||||
| **`path`** * | React Router `path`. [See the React Router docs](https://v5.reactrouter.com/web/api/Route/path-string-string) for more info. |
|
||||
| **`exact`** | React Router `exact` property. [More](https://v5.reactrouter.com/web/api/Route/exact-bool) |
|
||||
| **`strict`** | React Router `strict` property. [More](https://v5.reactrouter.com/web/api/Route/strict-bool) |
|
||||
| **`sensitive`** | React Router `sensitive` property. [More](https://v5.reactrouter.com/web/api/Route/sensitive-bool) |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
|
||||
#### Custom route components
|
||||
|
||||
Your custom route components will be given all the props that a React Router `<Route />` typically would receive, as well as two props from Payload:
|
||||
|
||||
| Prop | Description |
|
||||
| ---------------------- | -------------|
|
||||
| **`user`** | The currently logged in user. Will be `null` if no user is logged in. |
|
||||
| **`canAccessAdmin`** * | If the currently logged in user is allowed to access the admin panel or not. |
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong><br/>
|
||||
It's up to you to secure your custom routes. If your route requires a user to be logged in or to have certain access rights, you should handle that within your route component yourself.
|
||||
</Banner>
|
||||
|
||||
#### Example
|
||||
|
||||
You can find examples of custom route views in the [Payload source code `/test/admin/components/views` folder](https://github.com/payloadcms/payload/tree/master/test/admin/components/views). There, you'll find two custom routes:
|
||||
|
||||
1. A custom view that uses the `DefaultTemplate`, which is the built-in Payload template that displays the sidebar and "eyebrow nav"
|
||||
1. A custom view that uses the `MinimalTemplate` - which is just a centered template used for things like logging in or out
|
||||
|
||||
To see how to pass in your custom views to create custom routes of your own, take a look at the `admin.components.routes` property of the [Payload test admin config](https://github.com/payloadcms/payload/blob/master/test/admin/config.ts).
|
||||
|
||||
## Custom providers
|
||||
|
||||
As your admin customizations gets more complex you may want to share state between fields or other components. You can add custom providers to do add your own context to any Payload app for use in other custom components within the admin panel. Within your config add `admin.components.providers`, these can be used to share context or provide other custom functionality. Read the [React context](https://reactjs.org/docs/context.html) docs to learn more.
|
||||
|
||||
<Banner type="warning"><strong>Reminder:</strong> Don't forget to pass the **children** prop through the provider component for the admin UI to show</Banner>
|
||||
|
||||
### Styling Custom Components
|
||||
|
||||
Payload exports its SCSS variables and mixins for reuse in your own custom components. This is helpful in cases where you might want to style a custom input similarly to Payload's built-ini styling so it blends more thoroughly into the existing admin UI.
|
||||
Payload exports its SCSS variables and mixins for reuse in your own custom components. This is helpful in cases where you might want to style a custom input similarly to Payload's built-ini styling, so it blends more thoroughly into the existing admin UI.
|
||||
|
||||
To make use of Payload SCSS variables / mixins to use directly in your own components, you can import them as follows:
|
||||
|
||||
```
|
||||
@import '~payload/scss';
|
||||
```
|
||||
|
||||
### Getting the current locale
|
||||
|
||||
In any custom component you can get the selected locale with the `useLocale` hook. Here is a simple example:
|
||||
|
||||
```tsx
|
||||
import { useLocale } from 'payload/components/utilities';
|
||||
|
||||
const Greeting: React.FC = () => {
|
||||
// highlight-start
|
||||
const locale = useLocale();
|
||||
// highlight-end
|
||||
|
||||
const trans = {
|
||||
en: 'Hello',
|
||||
es: 'Hola',
|
||||
};
|
||||
|
||||
return (
|
||||
<span> { trans[locale] } </span>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
@@ -13,51 +13,37 @@ You can add your own CSS by providing your base Payload config with a path to yo
|
||||
To do so, provide your base Payload config with a path to your own stylesheet. It can be either a CSS or SCSS file.
|
||||
|
||||
**Example in payload.config.js:**
|
||||
```js
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config';
|
||||
import path from 'path';
|
||||
|
||||
const config = buildConfig({
|
||||
serverURL: 'http://localhost:3000',
|
||||
admin: {
|
||||
css: path.resolve(__dirname, 'relative/path/to/stylesheet.scss'),
|
||||
},
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
### Overriding SCSS variables
|
||||
### Overriding built-in styles
|
||||
|
||||
You can specify your own SCSS variable stylesheet that will allow for the override of Payload's base theme. This unlocks a ton of powerful theming and design options such as:
|
||||
To make it as easy as possible for you to override our styles, Payload uses [BEM naming conventions](http://getbem.com/) for all CSS within the Admin UI. If you provide your own CSS, you can override any built-in styles easily.
|
||||
|
||||
- Changing dashboard font families
|
||||
- Modifying color palette
|
||||
- Creating a dark theme
|
||||
- Etc.
|
||||
In addition to adding your own style definitions, you can also override Payload's built-in CSS variables. We use as much as possible behind the scenes, and you can override any of them that you'd like to.
|
||||
|
||||
To do so, provide your base Payload config with a path to your own SCSS variable sheet.
|
||||
You can find the built-in Payload CSS variables within [`./src/admin/scss/app.scss`](https://github.com/payloadcms/payload/blob/master/src/admin/scss/app.scss) and [`./src/admin/scss/colors.scss`](https://github.com/payloadcms/payload/blob/master/src/admin/scss/colors.scss). The following variables are defined and can be overridden:
|
||||
|
||||
**Example in payload.config.js:**
|
||||
```js
|
||||
import { buildConfig } from 'payload/config';
|
||||
import path from 'path';
|
||||
- Breakpoints
|
||||
- Base color shades (white to black by default)
|
||||
- Success / warning / error color shades
|
||||
- Theme-specific colors (background, input background, text color, etc.)
|
||||
- Elevation colors (used to determine how "bright" something should be when compared to the background)
|
||||
- Fonts
|
||||
- Horizontal gutter
|
||||
|
||||
const config = buildConfig({
|
||||
serverURL: 'http://localhost:3000',
|
||||
admin: {
|
||||
scss: path.resolve(__dirname, 'relative/path/to/vars.scss'),
|
||||
},
|
||||
})
|
||||
```
|
||||
#### Dark mode
|
||||
|
||||
**Example stylesheet override:**
|
||||
```scss
|
||||
$font-body: 'Papyrus';
|
||||
$style-radius-m: 10px;
|
||||
```
|
||||
|
||||
To reference all Sass variables that you can override, look at the default [SCSS variable stylesheet](https://github.com/payloadcms/payload/blob/master/src/admin/scss/vars.scss) within the Payload source code.
|
||||
|
||||
<Banner type="error">
|
||||
<strong>Warning:</strong><br />
|
||||
Only SCSS variables, mixins, functions, and extends are allowed in <strong>your SCSS overrides</strong>. Do not attempt to add any CSS declarations to this file, as this variable stylesheet is imported by many components throughout the Payload Admin panel and will result in your CSS definition(s) being duplicated many times. If you need to add real CSS definitions, see "Adding your own CSS / SCSS" the top of this page.
|
||||
<Banner type="warning">
|
||||
If you're overriding colors or theme elevations, make sure to consider how your changes will affect dark mode.
|
||||
</Banner>
|
||||
|
||||
By default, Payload automatically overrides all `--theme-elevation`s and inverts all success / warning / error shades to suit dark mode. We also update some base theme variables like `--theme-bg`, `--theme-text`, etc.
|
||||
|
||||
@@ -45,15 +45,14 @@ All options for the Admin panel are defined in your base Payload config file.
|
||||
To specify which Collection to use to log in to the Admin panel, pass the `admin` options a `user` key equal to the slug of the Collection that you'd like to use.
|
||||
|
||||
`payload.config.js`:
|
||||
```js
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config';
|
||||
|
||||
const config = buildConfig({
|
||||
serverURL: 'http://localhost:3000',
|
||||
admin: {
|
||||
user: 'admins', // highlight-line
|
||||
},
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
By default, if you have not specified a Collection, Payload will automatically provide you with a `User` Collection which will be used to access the Admin panel. You can customize or override the fields and settings of the default `User` Collection by passing your own collection using `users` as its `slug` to Payload. When this is done, Payload will use your provided `User` Collection instead of its default version.
|
||||
@@ -67,10 +66,10 @@ For example, you may wish to have two Collections that both support `Authenticat
|
||||
|
||||
This is totally possible. For the above scenario, by specifying `admin: { user: 'admins' }`, your Payload Admin panel will use `admins`. Any users logged in as `customers` will not be able to log in via the Admin panel.
|
||||
|
||||
### Light and dark modes
|
||||
|
||||
Users in the admin panel have access to choosing between light mode and dark mode for their editing experience. The setting is managed while logged into the admin UI within the user account page and will be stored with the browser. By default, the operating system preference is detected and used.
|
||||
|
||||
### Restricting user access
|
||||
|
||||
If you would like to restrict which users from a single Collection can access the Admin panel, you can use the `admin` access control function. [Click here](/docs/access-control/overview#admin) to learn more.
|
||||
|
||||
## License enforcement
|
||||
|
||||
Payload requires a valid license key to be used on production domains. You can use it as much as you'd like locally and on staging / UAT domains, but when you deploy to production, you'll need a license key to activate Payload's Admin panel. For more information, [click here](/docs/production/licensing).
|
||||
|
||||
@@ -10,12 +10,11 @@ Payload uses Webpack 5 to build the Admin panel. It comes with support for many
|
||||
|
||||
To extend the Webpack config, add the `webpack` key to your base Payload config, and provide a function that accepts the default Webpack config as its only argument:
|
||||
|
||||
`payload.config.js`
|
||||
```js
|
||||
`payload.config.ts`
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config';
|
||||
|
||||
export default buildConfig({
|
||||
serverURL: 'http://localhost:3000',
|
||||
admin: {
|
||||
// highlight-start
|
||||
webpack: (config) => {
|
||||
@@ -25,7 +24,7 @@ export default buildConfig({
|
||||
}
|
||||
// highlight-end
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
### Aliasing server-only modules
|
||||
@@ -53,16 +52,17 @@ You may rely on server-only packages such as the above to perform logic in acces
|
||||
<br/><br/>
|
||||
|
||||
`collections/Subscriptions/index.js`
|
||||
```js
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import createStripeSubscription from './hooks/createStripeSubscription';
|
||||
|
||||
const Subscription = {
|
||||
const Subscription: CollectionConfig = {
|
||||
slug: 'subscriptions',
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
createStripeSubscription,
|
||||
]
|
||||
}
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'stripeSubscriptionID',
|
||||
@@ -70,7 +70,7 @@ const Subscription = {
|
||||
required: true,
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export default Subscription;
|
||||
```
|
||||
@@ -128,7 +128,6 @@ const createStripeSubscriptionPath = path.resolve(__dirname, 'collections/Subscr
|
||||
const mockModulePath = path.resolve(__dirname, 'mocks/emptyObject.js');
|
||||
|
||||
export default buildConfig({
|
||||
serverURL: 'http://localhost:3000',
|
||||
collections: [
|
||||
Subscription
|
||||
],
|
||||
|
||||
@@ -12,16 +12,18 @@ To enable Authentication on a collection, define an `auth` property and set it t
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | -------------|
|
||||
| **`useAPIKey`** | Payload Authentication provides for API keys to be set on each user within an Authentication-enabled Collection. [More](/docs/authentication/config#api-keys) |
|
||||
| **`tokenExpiration`** | How long (in seconds) to keep the user logged in. JWTs and HTTP-only cookies will both expire at the same time. |
|
||||
| **`maxLoginAttempts`** | Only allow a user to attempt logging in X amount of times. Automatically locks out a user from authenticating if this limit is passed. Set to `0` to disable. |
|
||||
| **`lockTime`** | Set the time that a user should be locked out if they fail authentication more times than `maxLoginAttempts` allows for. |
|
||||
| **`depth`** | How many levels deep a `user` document should be populated when creating the JWT and binding the `user` to the express `req`. Defaults to `0` and should only be modified if absolutely necessary, as this will affect performance. |
|
||||
| **`cookies`** | Set cookie options, including `secure`, `sameSite`, and `domain`. For advanced users. |
|
||||
| **`forgotPassword`** | Customize the way that the `forgotPassword` operation functions. [More](/docs/authentication/config#forgot-password) |
|
||||
| **`verify`** | Set to `true` or pass an object with verification options to require users to verify by email before they are allowed to log into your app. [More](/docs/authentication/config#email-verification) |
|
||||
| Option | Description |
|
||||
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`useAPIKey`** | Payload Authentication provides for API keys to be set on each user within an Authentication-enabled Collection. [More](/docs/authentication/config#api-keys) |
|
||||
| **`tokenExpiration`** | How long (in seconds) to keep the user logged in. JWTs and HTTP-only cookies will both expire at the same time. |
|
||||
| **`maxLoginAttempts`** | Only allow a user to attempt logging in X amount of times. Automatically locks out a user from authenticating if this limit is passed. Set to `0` to disable. |
|
||||
| **`lockTime`** | Set the time that a user should be locked out if they fail authentication more times than `maxLoginAttempts` allows for. |
|
||||
| **`depth`** | How many levels deep a `user` document should be populated when creating the JWT and binding the `user` to the express `req`. Defaults to `0` and should only be modified if absolutely necessary, as this will affect performance. |
|
||||
| **`cookies`** | Set cookie options, including `secure`, `sameSite`, and `domain`. For advanced users. |
|
||||
| **`forgotPassword`** | Customize the way that the `forgotPassword` operation functions. [More](/docs/authentication/config#forgot-password) |
|
||||
| **`verify`** | Set to `true` or pass an object with verification options to require users to verify by email before they are allowed to log into your app. [More](/docs/authentication/config#email-verification) |
|
||||
| **`disableLocalStrategy`** | Advanced - disable Payload's built-in local auth strategy. Only use this property if you have replaced Payload's auth mechanisms with your own. |
|
||||
| **`strategies`** | Advanced - an array of PassportJS authentication strategies to extend this collection's authentication with. [More](/docs/authentication/config#strategies) |
|
||||
|
||||
### API keys
|
||||
|
||||
@@ -37,7 +39,8 @@ Technically, both of these options will work for third-party integrations but th
|
||||
To enable API keys on a collection, set the `useAPIKey` auth option to `true`. From there, a new interface will appear in the Admin panel for each document within the collection that allows you to generate an API key for each user in the Collection.
|
||||
|
||||
<Banner type="success">
|
||||
User API keys are encrypted within the database, meaning that if your database is compromised, your API keys will not be.
|
||||
User API keys are encrypted within the database, meaning that if your database
|
||||
is compromised, your API keys will not be.
|
||||
</Banner>
|
||||
|
||||
##### Authenticating via API Key
|
||||
@@ -45,8 +48,9 @@ To enable API keys on a collection, set the `useAPIKey` auth option to `true`. F
|
||||
To utilize your API key while interacting with the REST or GraphQL API, add the `Authorization` header.
|
||||
|
||||
**For example, using Fetch:**
|
||||
```js
|
||||
const response = await fetch('http://localhost:3000/api/pages', {
|
||||
|
||||
```ts
|
||||
const response = await fetch("http://localhost:3000/api/pages", {
|
||||
headers: {
|
||||
Authorization: `${collection.labels.singular} API-Key ${YOUR_API_KEY}`,
|
||||
},
|
||||
@@ -62,14 +66,21 @@ You can customize how the Forgot Password workflow operates with the following o
|
||||
Function that accepts one argument, containing `{ req, token, user }`, that allows for overriding the HTML within emails that are sent to users attempting to reset their password. The function should return a string that supports HTML, which can be a full HTML email.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong><br />
|
||||
HTML templating can be used to create custom email templates, inline CSS automatically, and more. You can make a reusable function that standardizes all email sent from Payload, which makes sending custom emails more DRY. Payload doesn't ship with an HTML templating engine, so you are free to choose your own.
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
HTML templating can be used to create custom email templates, inline CSS
|
||||
automatically, and more. You can make a reusable function that standardizes
|
||||
all email sent from Payload, which makes sending custom emails more DRY.
|
||||
Payload doesn't ship with an HTML templating engine, so you are free to choose
|
||||
your own.
|
||||
</Banner>
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
{
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const Customers: CollectionConfig = {
|
||||
slug: 'customers',
|
||||
auth: {
|
||||
forgotPassword: {
|
||||
@@ -95,12 +106,17 @@ Example:
|
||||
// highlight-end
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong><br />
|
||||
If you specify a different URL to send your users to for resetting their password, such as a page on the frontend of your app or similar, you need to handle making the call to the Payload REST or GraphQL reset-password operation yourself on your frontend, using the token that was provided for you. Above, it was passed via query parameter.
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
If you specify a different URL to send your users to for resetting their
|
||||
password, such as a page on the frontend of your app or similar, you need to
|
||||
handle making the call to the Payload REST or GraphQL reset-password operation
|
||||
yourself on your frontend, using the token that was provided for you. Above,
|
||||
it was passed via query parameter.
|
||||
</Banner>
|
||||
|
||||
**`generateEmailSubject`**
|
||||
@@ -109,7 +125,7 @@ Similarly to the above `generateEmailHTML`, you can also customize the subject o
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
```ts
|
||||
{
|
||||
slug: 'customers',
|
||||
auth: {
|
||||
@@ -134,8 +150,11 @@ Function that accepts one argument, containing `{ req, token, user }`, that allo
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
{
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
|
||||
const Customers: CollectionConfig = {
|
||||
slug: 'customers',
|
||||
auth: {
|
||||
verify: {
|
||||
@@ -149,12 +168,17 @@ Example:
|
||||
// highlight-end
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong><br />
|
||||
If you specify a different URL to send your users to for email verification, such as a page on the frontend of your app or similar, you need to handle making the call to the Payload REST or GraphQL verification operation yourself on your frontend, using the token that was provided for you. Above, it was passed via query parameter.
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
If you specify a different URL to send your users to for email verification,
|
||||
such as a page on the frontend of your app or similar, you need to handle
|
||||
making the call to the Payload REST or GraphQL verification operation yourself
|
||||
on your frontend, using the token that was provided for you. Above, it was
|
||||
passed via query parameter.
|
||||
</Banner>
|
||||
|
||||
**`generateEmailSubject`**
|
||||
@@ -163,7 +187,7 @@ Similarly to the above `generateEmailHTML`, you can also customize the subject o
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
```ts
|
||||
{
|
||||
slug: 'customers',
|
||||
auth: {
|
||||
@@ -177,3 +201,29 @@ Example:
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Strategies
|
||||
|
||||
As of Payload `1.0.0`, you can add additional authentication strategies to Payload easily by passing them to your collection's `auth.strategies` array.
|
||||
|
||||
Behind the scenes, Payload uses PassportJS to power its local authentication strategy, so most strategies listed on the PassportJS website will work seamlessly. Combined with adding custom components to the admin panel's `Login` view, you can create advanced authentication strategies directly within Payload.
|
||||
|
||||
<Banner type="warning">
|
||||
This is an advanced feature, so only attempt this if you are an experienced
|
||||
developer. Otherwise, just let Payload's built-in authentication handle user
|
||||
auth for you.
|
||||
</Banner>
|
||||
|
||||
The `strategies` property is an array that takes objects with the following properties:
|
||||
|
||||
**`strategy`**
|
||||
|
||||
This property can accept a Passport strategy directly, or you can pass a function that takes a `payload` argument, and returns a Passport strategy.
|
||||
|
||||
**`name`**
|
||||
|
||||
If you pass a strategy to the `strategy` property directly, the `name` property is optional and allows you to override the strategy's built-in name.
|
||||
|
||||
However, if you pass a function to `strategy`, `name` is a required property.
|
||||
|
||||
In either case, Payload will prefix the strategy name with the collection `slug` that the strategy is passed to.
|
||||
|
||||
@@ -17,10 +17,9 @@ The Access operation returns what a logged in user can and can't do with the col
|
||||
`GET http://localhost:3000/api/access`
|
||||
|
||||
Example response:
|
||||
```js
|
||||
```ts
|
||||
{
|
||||
canAccessAdmin: true,
|
||||
license: 'LICENSE_KEY_HERE',
|
||||
collections: {
|
||||
pages: {
|
||||
create: {
|
||||
@@ -55,7 +54,7 @@ Example response:
|
||||
|
||||
**Example GraphQL Query**:
|
||||
|
||||
```
|
||||
```graphql
|
||||
query {
|
||||
Access {
|
||||
pages {
|
||||
@@ -76,7 +75,7 @@ Returns either a logged in user with token or null when there is no logged in us
|
||||
`GET http://localhost:3000/api/[collection-slug]/me`
|
||||
|
||||
Example response:
|
||||
```js
|
||||
```ts
|
||||
{
|
||||
user: { // The JWT "payload" ;) from the logged in user
|
||||
email: 'dev@payloadcms.com',
|
||||
@@ -91,7 +90,7 @@ Example response:
|
||||
|
||||
**Example GraphQL Query**:
|
||||
|
||||
```
|
||||
```graphql
|
||||
query {
|
||||
Me[collection-singular-label] {
|
||||
user {
|
||||
@@ -107,7 +106,7 @@ query {
|
||||
Accepts an `email` and `password`. On success, it will return the logged in user as well as a token that can be used to authenticate. In the GraphQL and REST APIs, this operation also automatically sets an HTTP-only cookie including the user's token. If you pass an Express `res` to the Local API operation, Payload will set a cookie there as well.
|
||||
|
||||
**Example REST API login**:
|
||||
```js
|
||||
```ts
|
||||
const res = await fetch('http://localhost:3000/api/[collection-slug]/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -138,7 +137,7 @@ const json = await res.json();
|
||||
|
||||
**Example GraphQL Mutation**:
|
||||
|
||||
```
|
||||
```graphql
|
||||
mutation {
|
||||
login[collection-singular-label](email: "dev@payloadcms.com", password: "yikes") {
|
||||
user {
|
||||
@@ -152,7 +151,7 @@ mutation {
|
||||
|
||||
**Example Local API login**:
|
||||
|
||||
```js
|
||||
```ts
|
||||
const result = await payload.login({
|
||||
collection: '[collection-slug]',
|
||||
data: {
|
||||
@@ -167,7 +166,7 @@ const result = await payload.login({
|
||||
As Payload sets HTTP-only cookies, logging out cannot be done by just removing a cookie in JavaScript, as HTTP-only cookies are inaccessible by JS within the browser. So, Payload exposes a `logout` operation to delete the token in a safe way.
|
||||
|
||||
**Example REST API logout**:
|
||||
```js
|
||||
```ts
|
||||
const res = await fetch('http://localhost:3000/api/[collection-slug]/logout', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -193,7 +192,7 @@ This operation requires a non-expired token to send back a new one. If the user'
|
||||
If successful, this operation will automatically renew the user's HTTP-only cookie and will send back the updated token in JSON.
|
||||
|
||||
**Example REST API token refresh**:
|
||||
```js
|
||||
```ts
|
||||
const res = await fetch('http://localhost:3000/api/[collection-slug]/refresh-token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -240,18 +239,18 @@ mutation {
|
||||
If your collection supports email verification, the Verify operation will be exposed which accepts a verification token and sets the user's `_verified` property to `true`, thereby allowing the user to authenticate with the Payload API.
|
||||
|
||||
**Example REST API user verification**:
|
||||
```js
|
||||
```ts
|
||||
const res = await fetch(`http://localhost:3000/api/[collection-slug]/verify/${TOKEN_HERE}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
**Example GraphQL Mutation**:
|
||||
|
||||
```
|
||||
```graphql
|
||||
mutation {
|
||||
verifyEmail[collection-singular-label](token: "TOKEN_HERE")
|
||||
}
|
||||
@@ -259,7 +258,7 @@ mutation {
|
||||
|
||||
**Example Local API verification**:
|
||||
|
||||
```js
|
||||
```ts
|
||||
const result = await payload.verifyEmail({
|
||||
collection: '[collection-slug]',
|
||||
token: 'TOKEN_HERE',
|
||||
@@ -273,7 +272,7 @@ If a user locks themselves out and you wish to deliberately unlock them, you can
|
||||
To restrict who is allowed to unlock users, you can utilize the [`unlock`](/docs/access-control/overview#unlock) access control function.
|
||||
|
||||
**Example REST API unlock**:
|
||||
```js
|
||||
```ts
|
||||
const res = await fetch(`http://localhost:3000/api/[collection-slug]/unlock`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -292,7 +291,7 @@ mutation {
|
||||
|
||||
**Example Local API unlock**:
|
||||
|
||||
```js
|
||||
```ts
|
||||
const result = await payload.unlock({
|
||||
collection: '[collection-slug]',
|
||||
})
|
||||
@@ -307,7 +306,7 @@ The link to reset the user's password contains a token which is what allows the
|
||||
By default, the Forgot Password operations send users to the Payload Admin panel to reset their password, but you can customize the generated email to send users to the frontend of your app instead by [overriding the email HTML](/docs/authentication/config#forgot-password).
|
||||
|
||||
**Example REST API Forgot Password**:
|
||||
```js
|
||||
```ts
|
||||
const res = await fetch(`http://localhost:3000/api/[collection-slug]/forgot-password`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -316,7 +315,7 @@ const res = await fetch(`http://localhost:3000/api/[collection-slug]/forgot-pass
|
||||
body: JSON.stringify({
|
||||
email: 'dev@payloadcms.com',
|
||||
}),
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
**Example GraphQL Mutation**:
|
||||
@@ -329,14 +328,14 @@ mutation {
|
||||
|
||||
**Example Local API forgot password**:
|
||||
|
||||
```js
|
||||
```ts
|
||||
const token = await payload.forgotPassword({
|
||||
collection: '[collection-slug]',
|
||||
data: {
|
||||
email: 'dev@payloadcms.com',
|
||||
},
|
||||
disableEmail: false // you can disable the auto-generation of email via local API
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
@@ -349,7 +348,7 @@ const token = await payload.forgotPassword({
|
||||
After a user has "forgotten" their password and a token is generated, that token can be used to send to the reset password operation along with a new password which will allow the user to reset their password securely.
|
||||
|
||||
**Example REST API Reset Password**:
|
||||
```js
|
||||
```ts
|
||||
const res = await fetch(`http://localhost:3000/api/[collection-slug]/reset-password`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -359,7 +358,7 @@ const res = await fetch(`http://localhost:3000/api/[collection-slug]/reset-passw
|
||||
token: 'TOKEN_GOES_HERE'
|
||||
password: 'not-today',
|
||||
}),
|
||||
})
|
||||
});
|
||||
|
||||
const json = await res.json();
|
||||
|
||||
@@ -380,7 +379,7 @@ const json = await res.json();
|
||||
|
||||
**Example GraphQL Mutation**:
|
||||
|
||||
```
|
||||
```graphql
|
||||
mutation {
|
||||
resetPassword[collection-singular-label](token: "TOKEN_GOES_HERE", password: "not-today")
|
||||
}
|
||||
|
||||
@@ -32,8 +32,10 @@ Every Payload Collection can opt-in to supporting Authentication by specifying t
|
||||
|
||||
Simple example collection:
|
||||
|
||||
```js
|
||||
const Admins = {
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const Admins: CollectionConfig = {
|
||||
slug:
|
||||
// highlight-start
|
||||
auth: {
|
||||
@@ -60,7 +62,7 @@ const Admins = {
|
||||
}
|
||||
```
|
||||
|
||||
**By enabling Authetication on a config, the following modifications will automatically be made to your Collection:**
|
||||
**By enabling Authentication on a config, the following modifications will automatically be made to your Collection:**
|
||||
|
||||
1. `email` as well as password `salt` & `hash` fields will be added to your Collection's schema
|
||||
1. The Admin panel will feature a new set of corresponding UI to allow for changing password and editing email
|
||||
@@ -95,7 +97,7 @@ However, if you use `fetch` or similar APIs to retrieve Payload resources from i
|
||||
|
||||
Fetch example, including credentials:
|
||||
|
||||
```js
|
||||
```ts
|
||||
const response = await fetch('http://localhost:3000/api/pages', {
|
||||
credentials: 'include',
|
||||
});
|
||||
@@ -124,12 +126,11 @@ So, if a user of coolsite.com is logged in and just browsing around on the inter
|
||||
|
||||
To define domains that should allow users to identify themselves via the Payload HTTP-only cookie, use the `csrf` option on the base Payload config to whitelist domains that you trust.
|
||||
|
||||
`payload.config.js`:
|
||||
```js
|
||||
`payload.config.ts`:
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config';
|
||||
|
||||
const config = buildConfig({
|
||||
serverURL: 'http://localhost:3000',
|
||||
collections: [
|
||||
// collections here
|
||||
],
|
||||
@@ -149,7 +150,7 @@ export default config;
|
||||
In addition to authenticating via an HTTP-only cookie, you can also identify users via the `Authorization` header on an HTTP request.
|
||||
|
||||
Example:
|
||||
```js
|
||||
```ts
|
||||
const request = await fetch('http://localhost:3000', {
|
||||
headers: {
|
||||
Authorization: `JWT ${token}`
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Make full use of Payload's built-in authentication with your own custom Ex
|
||||
keywords: authentication, middleware, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
Because Payload uses your existing Express server, you are free to add whatever logic you need to your app through endpoints of your own. However, Payload does not add its middleware to your Express app itself—instead, ist scopes all of its middleware to Payload-specific routers.
|
||||
Because Payload uses your existing Express server, you are free to add whatever logic you need to your app through endpoints of your own. However, Payload does not add its middleware to your Express app itself—instead, it scopes all of its middleware to Payload-specific routers.
|
||||
|
||||
This approach has a ton of benefits - it's great for isolation of concerns and limiting scope, but it also means that your additional routes won't have access to Payload's user authentication.
|
||||
|
||||
@@ -15,7 +15,7 @@ This approach has a ton of benefits - it's great for isolation of concerns and l
|
||||
</Banner>
|
||||
|
||||
Example in `server.js`:
|
||||
```js
|
||||
```ts
|
||||
import express from 'express';
|
||||
import payload from 'payload';
|
||||
|
||||
|
||||
@@ -17,20 +17,23 @@ It's often best practice to write your Collections in separate files and then im
|
||||
| **`slug`** * | Unique, URL-friendly string that will act as an identifier for this Collection. |
|
||||
| **`fields`** * | Array of field types that will determine the structure and functionality of the data stored within this Collection. [Click here](/docs/fields/overview) for a full list of field types as well as how to configure them. |
|
||||
| **`labels`** | Singular and plural labels for use in identifying this Collection throughout Payload. Auto-generated from slug if not defined. |
|
||||
| **`description`**| Text or React component to display below the Collection label in the List view to give editors more information. |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-options). |
|
||||
| **`hooks`** | Entry points to "tie in" to Collection actions at specific points. [More](/docs/hooks/overview#collection-hooks) |
|
||||
| **`access`** | Provide access control functions to define exactly who should be able to do what with Documents in this Collection. [More](/docs/access-control/overview/#collections) |
|
||||
| **`auth`** | Specify options if you would like this Collection to feature authentication. For more, consult the [Authentication](/docs/authentication/config) documentation. |
|
||||
| **`upload`** | Specify options if you would like this Collection to support file uploads. For more, consult the [Uploads](/docs/upload/overview) documentation. |
|
||||
| **`timestamps`** | Set to false to disable documents' automatically generated `createdAt` and `updatedAt` timestamps. |
|
||||
| **`versions`** | Set to true to enable default options, or configure with object properties. [More](/docs/versions/overview#collection-config)|
|
||||
| **`endpoints`** | Add custom routes to the REST API. [More](/docs/rest-api/overview#custom-endpoints) |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
|
||||
#### Simple collection example
|
||||
|
||||
```js
|
||||
const Orders = {
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const Orders: CollectionConfig = {
|
||||
slug: 'orders',
|
||||
fields: [
|
||||
{
|
||||
@@ -45,12 +48,12 @@ const Orders = {
|
||||
required: true,
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
#### More collection config examples
|
||||
|
||||
You can find an assortment of [example collection configs](https://github.com/payloadcms/payload/blob/master/demo/collections) in the Payload source code on GitHub.
|
||||
You can find an assortment of [example collection configs](https://github.com/payloadcms/public-demo/tree/master/src/collections) in the Public Demo source code on GitHub.
|
||||
|
||||
### Admin options
|
||||
|
||||
@@ -59,6 +62,7 @@ You can customize the way that the Admin panel behaves on a collection-by-collec
|
||||
| Option | Description |
|
||||
| ---------------------------- | -------------|
|
||||
| `useAsTitle` | Specify a top-level field to use for a document title throughout the Admin panel. If no field is defined, the ID of the document is used as the title. |
|
||||
| `description` | Text or React component to display below the Collection label in the List view to give editors more information. |
|
||||
| `defaultColumns` | Array of field names that correspond to which columns to show by default in this collection's List view. |
|
||||
| `disableDuplicate ` | Disables the "Duplicate" button while editing documents within this collection. |
|
||||
| `enableRichTextRelationship` | The [Rich Text](/docs/fields/rich-text) field features a `Relationship` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
|
||||
@@ -78,26 +82,28 @@ If the function is specified, a Preview button will automatically appear in the
|
||||
|
||||
**Example collection with preview function:**
|
||||
|
||||
```js
|
||||
{
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const Posts: CollectionConfig = {
|
||||
slug: 'posts',
|
||||
fields: [
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
required: true,
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
admin: {
|
||||
preview: (doc, { locale }) => {
|
||||
if (doc?.slug) {
|
||||
return `https://bigbird.com/preview/posts/${doc.slug}?locale=${locale}`,
|
||||
return `https://bigbird.com/preview/posts/${doc.slug}?locale=${locale}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Access control
|
||||
@@ -116,14 +122,14 @@ Collections support all field types that Payload has to offer—including simple
|
||||
|
||||
You can import collection types as follows:
|
||||
|
||||
```js
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
// This is the type used for incoming collection configs.
|
||||
// Only the bare minimum properties are marked as required.
|
||||
```
|
||||
|
||||
```js
|
||||
```ts
|
||||
import { SanitizedCollectionConfig } from 'payload/types';
|
||||
|
||||
// This is the type used after an incoming collection config is fully sanitized.
|
||||
|
||||
@@ -16,7 +16,6 @@ Payload utilizes a few Express-specific middleware packages within its own route
|
||||
|
||||
```js
|
||||
{
|
||||
serverURL: 'http://localhost:3000',
|
||||
express: {
|
||||
json: {
|
||||
limit: '4mb',
|
||||
@@ -37,7 +36,6 @@ To customize compression options, pass an object to the Payload config's `expres
|
||||
|
||||
```js
|
||||
{
|
||||
serverURL: 'http://localhost:3000',
|
||||
express: {
|
||||
compression: {
|
||||
// settings go here
|
||||
|
||||
@@ -21,13 +21,17 @@ As with Collection configs, it's often best practice to write your Globals in se
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](/docs/configuration/globals#admin-options). |
|
||||
| **`hooks`** | Entry points to "tie in" to collection actions at specific points. [More](/docs/hooks/overview#global-hooks) |
|
||||
| **`access`** | Provide access control functions to define exactly who should be able to do what with this Global. [More](/docs/access-control/overview/#globals) |
|
||||
| **`versions`** | Set to true to enable default options, or configure with object properties. [More](/docs/versions/overview#globals-config)|
|
||||
| **`endpoints`** | Add custom routes to the REST API. [More](/docs/rest-api/overview#custom-endpoints)|
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
|
||||
#### Simple Global example
|
||||
|
||||
```js
|
||||
const Nav = {
|
||||
```ts
|
||||
import { GlobalConfig } from 'payload/types';
|
||||
|
||||
const Nav: GlobalConfig = {
|
||||
slug: 'nav',
|
||||
fields: [
|
||||
{
|
||||
@@ -45,12 +49,14 @@ const Nav = {
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export default Nav;
|
||||
```
|
||||
|
||||
#### More Global config examples
|
||||
#### Global config example
|
||||
|
||||
You can find an assortment of [example Global configs](https://github.com/payloadcms/payload/blob/master/demo/globals) in the Payload source code on GitHub.
|
||||
You can find an [example Global config](https://github.com/payloadcms/public-demo/blob/master/src/globals/MainMenu.ts) in the Public Demo source code on GitHub.
|
||||
|
||||
### Admin options
|
||||
|
||||
@@ -76,14 +82,14 @@ Globals support all field types that Payload has to offer—including simple fie
|
||||
|
||||
You can import global types as follows:
|
||||
|
||||
```js
|
||||
```ts
|
||||
import { GlobalConfig } from 'payload/types';
|
||||
|
||||
// This is the type used for incoming global configs.
|
||||
// Only the bare minimum properties are marked as required.
|
||||
```
|
||||
|
||||
```js
|
||||
```ts
|
||||
import { SanitizedGlobalConfig } from 'payload/types';
|
||||
|
||||
// This is the type used after an incoming global config is fully sanitized.
|
||||
|
||||
@@ -14,22 +14,23 @@ Add the `localization` property to your Payload config to enable localization pr
|
||||
|
||||
**Example Payload config set up for localization:**
|
||||
|
||||
```js
|
||||
{
|
||||
serverURL: 'http://localhost:3000',
|
||||
collections: [
|
||||
... // collections go here
|
||||
],
|
||||
localization: {
|
||||
locales: [
|
||||
'en',
|
||||
'es',
|
||||
'de',
|
||||
],
|
||||
defaultLocale: 'en',
|
||||
fallback: true,
|
||||
},
|
||||
}
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
export default buildConfig({
|
||||
collections: [
|
||||
// collections go here
|
||||
],
|
||||
localization: {
|
||||
locales: [
|
||||
'en',
|
||||
'es',
|
||||
'de',
|
||||
],
|
||||
defaultLocale: 'en',
|
||||
fallback: true,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**Here is a brief explanation of each of the options available within the `localization` property:**
|
||||
@@ -54,11 +55,11 @@ Payload localization works on a **field** level—not a document level. In addit
|
||||
|
||||
```js
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
// highlight-start
|
||||
localized: true,
|
||||
// highlight-end
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
// highlight-start
|
||||
localized: true,
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
@@ -67,8 +68,8 @@ With the above configuration, the `title` field will now be saved in the databas
|
||||
All field types with a `name` property support the `localized` property—even the more complex field types like `array`s and `block`s.
|
||||
|
||||
<Banner>
|
||||
<strong>Note:</strong><br/>
|
||||
Enabling localization for field types that support nested fields will automatically create localized "sets" of all fields contained within the field. For example, if you have a page layout using a blocks field type, you have the choice of either localizing the full layout, by enabling localization on the top-level blocks field, or only certain fields within the layout.
|
||||
<strong>Note:</strong><br/>
|
||||
Enabling localization for field types that support nested fields will automatically create localized "sets" of all fields contained within the field. For example, if you have a page layout using a blocks field type, you have the choice of either localizing the full layout, by enabling localization on the top-level blocks field, or only certain fields within the layout.
|
||||
</Banner>
|
||||
|
||||
### Retrieving localized docs
|
||||
@@ -105,16 +106,16 @@ The `fallbackLocale` arg will accept valid locales as well as `none` to disable
|
||||
|
||||
```graphql
|
||||
query {
|
||||
Posts(locale: de, fallbackLocale: none) {
|
||||
docs {
|
||||
title
|
||||
}
|
||||
}
|
||||
Posts(locale: de, fallbackLocale: none) {
|
||||
docs {
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Banner>
|
||||
In GraphQL, specifying the locale at the top level of a query will automatically apply it throughout all nested relationship fields. You can override this behavior by re-specifying locale arguments in nested related document queries.
|
||||
In GraphQL, specifying the locale at the top level of a query will automatically apply it throughout all nested relationship fields. You can override this behavior by re-specifying locale arguments in nested related document queries.
|
||||
</Banner>
|
||||
|
||||
##### Local API
|
||||
@@ -125,9 +126,9 @@ You can specify `locale` as well as `fallbackLocale` within the Local API as wel
|
||||
|
||||
```js
|
||||
const posts = await payload.find({
|
||||
collection: 'posts',
|
||||
locale: 'es',
|
||||
fallbackLocale: false,
|
||||
collection: 'posts',
|
||||
locale: 'es',
|
||||
fallbackLocale: false,
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
@@ -18,14 +18,14 @@ Payload is a *config-based*, code-first CMS and application framework. The Paylo
|
||||
|
||||
| Option | Description |
|
||||
| -------------------- | -------------|
|
||||
| `serverURL` | A _required_ string used to define the absolute URL of your app including the protocol, for example `https://'example.com`. No paths allowed, only protocol, domain and (optionally) port |
|
||||
| `serverURL` | A string used to define the absolute URL of your app including the protocol, for example `https://example.com`. No paths allowed, only protocol, domain and (optionally) port |
|
||||
| `collections` | An array of all Collections that Payload will manage. To read more about how to define your collection configs, [click here](/docs/configuration/collections). |
|
||||
| `cors` | Either a whitelist array of URLS to allow CORS requests from, or a wildcard string (`'*'`) to accept incoming requests from any domain. |
|
||||
| `globals` | An array of all Globals that Payload will manage. For more on Globals and their configs, [click here](/docs/configuration/globals). |
|
||||
| `admin` | Base Payload admin configuration. Specify custom components, control metadata, set the Admin user collection, and [more](/docs/admin/overview#admin-options). |
|
||||
| `localization` | Opt-in and control how Payload handles the translation of your content into multiple locales. [More](/docs/configuration/localization) |
|
||||
| `graphQL` | Manage GraphQL-specific functionality here. Define your own queries and mutations, manage query complexity limits, and [more](/docs/graphql/overview). |
|
||||
| `graphQL` | Manage GraphQL-specific functionality here. Define your own queries and mutations, manage query complexity limits, and [more](/docs/graphql/overview#graphql-options). |
|
||||
| `cookiePrefix` | A string that will be prefixed to all cookies that Payload sets. |
|
||||
| `cors` | Either a whitelist array of URLS to allow CORS requests from, or a wildcard string (`'*'`) to accept incoming requests from any domain. |
|
||||
| `csrf` | A whitelist array of URLs to allow Payload cookies to be accepted from as a form of CSRF protection. [More](/docs/authentication/overview#csrf-protection) |
|
||||
| `defaultDepth` | If a user does not specify `depth` while requesting a resource, this depth will be used. [More](/docs/getting-started/concepts#depth) |
|
||||
| `maxDepth` | The maximum allowed depth to be permitted application-wide. This setting helps prevent against malicious queries. Defaults to `10`. |
|
||||
@@ -33,19 +33,20 @@ Payload is a *config-based*, code-first CMS and application framework. The Paylo
|
||||
| `upload` | Base Payload upload configuration. [More](/docs/upload/overview#payload-wide-upload-options). |
|
||||
| `routes` | Control the routing structure that Payload binds itself to. Specify `admin`, `api`, `graphQL`, and `graphQLPlayground`. |
|
||||
| `email` | Base email settings to allow Payload to generate email such as Forgot Password requests and other requirements. [More](/docs/email/overview#configuration) |
|
||||
| `express` | Express-specific middleware options such as compression and JSON parsing. [More](/docs/configuration/express). |
|
||||
| `express` | Express-specific middleware options such as compression and JSON parsing. [More](/docs/configuration/express) |
|
||||
| `debug` | Enable to expose more detailed error information. |
|
||||
| `telemetry` | Disable Payload telemetry by passing `false`. [More](/docs/configuration/overview#telemetry) |
|
||||
| `rateLimit` | Control IP-based rate limiting for all Payload resources. Used to prevent DDoS attacks and [more](/docs/production/preventing-abuse#rate-limiting-requests). |
|
||||
| `hooks` | Tap into Payload-wide hooks. [More](/docs/hooks/overview) |
|
||||
| `plugins` | An array of Payload plugins. [More](/docs/plugins/overview) |
|
||||
| `endpoints` | An array of custom API endpoints added to the Payload router. [More](/docs/rest-api/overview#custom-endpoints) |
|
||||
|
||||
#### Simple example
|
||||
|
||||
```js
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config';
|
||||
|
||||
const config = buildConfig({
|
||||
serverURL: 'http://localhost:3000',
|
||||
export default buildConfig({
|
||||
collections: [
|
||||
{
|
||||
slug: 'pages',
|
||||
@@ -82,14 +83,11 @@ const config = buildConfig({
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
export default config;
|
||||
|
||||
```
|
||||
|
||||
#### Full example config
|
||||
|
||||
You can see a full [example config](https://github.com/payloadcms/payload/blob/master/demo/payload.config.ts) in the Payload source code on GitHub.
|
||||
You can see a full [example config](https://github.com/payloadcms/public-demo/blob/master/src/payload.config.ts) in the Public Demo source code on GitHub.
|
||||
|
||||
### Using environment variables in your config
|
||||
|
||||
@@ -139,7 +137,9 @@ The Payload config itself, as well as all files that it requires or imports, are
|
||||
|
||||
Payload comes with `isomorphic-fetch` installed which means that even in Node, you can use the `fetch` API just as you would within the browser. No need to import `axios` or similar, unless you want to!
|
||||
|
||||
#### Re-using the Payload `babel.config.js`
|
||||
#### Payload Config and Babel
|
||||
|
||||
The entire Payload config is transpiled automatically by Payload via `babel`.
|
||||
|
||||
If for any reason you need to re-use the built-in Payload `babel.config.js`, you can do so by importing it as follows:
|
||||
|
||||
@@ -147,20 +147,46 @@ If for any reason you need to re-use the built-in Payload `babel.config.js`, you
|
||||
import { config } from 'payload/babel';
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong><br/>
|
||||
Because the Payload config is transpiled internally, if you want to import it to share or reuse any of its properties within your own Node server's code, you need to make sure that <em>you manually transpile it</em> using <strong>babel-register</strong> or similar. For example, if you try to import your config directly into your server, your Node process will likely crash because the Payload config supports React components, TypeScript, and new ES6+ features.
|
||||
</Banner>
|
||||
|
||||
However, you can share code, like for example your config's `serverURL` property by "hoisting" your shared properties above your config and writing any "shared" code in a module that is compatible with your Node environment.
|
||||
|
||||
For example, to share your `serverURL`, you could create a file like the following:
|
||||
|
||||
`serverURL.js`:
|
||||
|
||||
```js
|
||||
const serverURL = 'http://localhost:3000';
|
||||
|
||||
module.exports = serverURL;
|
||||
```
|
||||
|
||||
Then, you could import this file into both your Payload config and your server, in an effort to avoid importing your full Payload config directly into your server.
|
||||
|
||||
|
||||
### TypeScript
|
||||
|
||||
You can import config types as follows:
|
||||
|
||||
```js
|
||||
```ts
|
||||
import { Config } from 'payload/config';
|
||||
|
||||
// This is the type used for an incoming Payload config.
|
||||
// Only the bare minimum properties are marked as required.
|
||||
```
|
||||
|
||||
```js
|
||||
```ts
|
||||
import { SanitizedConfig } from 'payload/config';
|
||||
|
||||
// This is the type used after an incoming Payload config is fully sanitized.
|
||||
// Generally, this is only used internally by Payload.
|
||||
```
|
||||
|
||||
### Telemetry
|
||||
|
||||
Payload collects **completely anonymous** telemetry data about general usage. This data is super important to us and helps us accurately understand how we're growing and what we can do to build the software into everything that it can possibly be. The telemetry that we collect also help us demonstrate our growth in an accurate manner, which helps us as we seek investment to build and scale our team. If we can accurately demonstrate our growth, we can more effectively continue to support Payload as free and open-source software. To opt out of telemetry, you can pass `telemetry: false` within your Payload config.
|
||||
|
||||
For more information about what we track, take a look at our [privacy policy](/privacy).
|
||||
|
||||
@@ -27,10 +27,11 @@ The following options are configurable in the `email` property object as part of
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------------- | -------------|
|
||||
| **`transport`** | The NodeMailer transport object for when you want to do it yourself, not needed when transportOptions is set |
|
||||
| **`transportOptions`** | An object that configures the transporter that Payload will create. For all the available options see the [NodeMailer documentation](https://nodemailer.com/smtp/) or see the examples below |
|
||||
| **`fromName`** * | The name part of the From field that will be seen on the delivered email |
|
||||
| **`fromAddress`** * | The email address part of the From field that will be used when delivering email |
|
||||
| **`transport`** | The NodeMailer transport object for when you want to do it yourself, not needed when transportOptions is set |
|
||||
| **`transportOptions`** | An object that configures the transporter that Payload will create. For all the available options see the [NodeMailer documentation](https://nodemailer.com/smtp/) or see the examples below |
|
||||
| **`logMockCredentials`** | If set to true and no transport/transportOptions, ethereal credentials will be logged to console on startup |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
|
||||
@@ -39,7 +40,7 @@ The following options are configurable in the `email` property object as part of
|
||||
Simple Mail Transfer Protocol, also known as SMTP can be passed in using the `transportOptions` object on the `email` options.
|
||||
|
||||
**Example email part using SMTP:**
|
||||
```js
|
||||
```ts
|
||||
payload.init({
|
||||
email: {
|
||||
transportOptions: {
|
||||
@@ -59,6 +60,7 @@ payload.init({
|
||||
fromAddress: 'hello@example.com'
|
||||
}
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
@@ -69,9 +71,9 @@ payload.init({
|
||||
|
||||
Many third party mail providers are available and offer benefits beyond basic SMTP. As an example your payload init could look this if you wanted to use SendGrid.com though the same approach would work for any other [NodeMailer transports](https://nodemailer.com/transports/) shown here or provided by another third party.
|
||||
|
||||
```js
|
||||
const nodemailerSendgrid = require('nodemailer-sendgrid');
|
||||
const payload = require('payload');
|
||||
```ts
|
||||
import payload from 'payload'
|
||||
import nodemailerSendgrid from 'nodemailer-sendgrid'
|
||||
|
||||
const sendGridAPIKey = process.env.SENDGRID_API_KEY;
|
||||
|
||||
@@ -91,7 +93,10 @@ payload.init({
|
||||
### Use a custom NodeMailer transport
|
||||
To take full control of the mail transport you may wish to use `nodemailer.createTransport()` on your server and provide it to Payload init.
|
||||
|
||||
```js
|
||||
```ts
|
||||
import payload from 'payload'
|
||||
import nodemailer from 'nodemailer'
|
||||
|
||||
const payload = require('payload');
|
||||
const nodemailer = require('nodemailer');
|
||||
|
||||
@@ -111,6 +116,7 @@ payload.init({
|
||||
transport
|
||||
},
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
### Sending Mail
|
||||
@@ -119,7 +125,20 @@ With a working transport you can call it anywhere you have access to payload by
|
||||
### Mock transport
|
||||
By default, Payload uses a mock implementation that only sends mail to the [ethereal](https://ethereal.email) capture service that will never reach a user's inbox. While in development you may wish to make use of the captured messages which is why the payload output during server output helpfully logs this out on the server console.
|
||||
|
||||
**Console output when starting payload with a mock email instance**
|
||||
To see ethereal credentials, add `logMockCredentials: true` to the email options. This will cause them to be logged to console on startup.
|
||||
|
||||
```ts
|
||||
payload.init({
|
||||
email: {
|
||||
fromName: 'Admin',
|
||||
fromAddress: 'admin@example.com',
|
||||
logMockCredentials: true, // Optional
|
||||
},
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
**Console output when starting payload with a mock email instance and logMockCredentials: true**
|
||||
```
|
||||
[06:37:21] INFO (payload): Starting Payload...
|
||||
[06:37:22] INFO (payload): Payload Demo Initialized
|
||||
|
||||
@@ -31,7 +31,7 @@ keywords: array, fields, config, configuration, documentation, Content Managemen
|
||||
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
|
||||
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin panel. |
|
||||
| **`defaultValue`** | Provide an array of row data to be used for this field's default value. |
|
||||
| **`defaultValue`** | Provide an array of row data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. If enabled, a separate, localized set of all data within this Array will be kept, so there is no need to specify each nested field as `localized`. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`labels`** | Customize the row labels appearing in the Admin dashboard. |
|
||||
@@ -41,9 +41,11 @@ keywords: array, fields, config, configuration, documentation, Content Managemen
|
||||
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.js`
|
||||
```js
|
||||
{
|
||||
`collections/ExampleCollection.ts`
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
@@ -70,6 +72,5 @@ keywords: array, fields, config, configuration, documentation, Content Managemen
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
@@ -32,7 +32,7 @@ keywords: blocks, fields, config, configuration, documentation, Content Manageme
|
||||
| **`hooks`** | Provide field-level hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
|
||||
| **`access`** | Provide field-level access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API response or the Admin panel. |
|
||||
| **`defaultValue`** | Provide an array of block data to be used for this field's default value. |
|
||||
| **`defaultValue`** | Provide an array of block data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. If enabled, a separate, localized set of all data within this field will be kept, so there is no need to specify each nested field as `localized`. || **`required`** | Require this field to have a value. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`labels`** | Customize the block row labels appearing in the Admin dashboard. |
|
||||
@@ -72,8 +72,10 @@ The Admin panel provides each block with a `blockName` field which optionally al
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.js`
|
||||
```js
|
||||
const QuoteBlock = {
|
||||
```ts
|
||||
import { Block, CollectionConfig } from 'payload/types';
|
||||
|
||||
const QuoteBlock: Block = {
|
||||
slug: 'Quote', // required
|
||||
imageURL: 'https://google.com/path/to/image.jpg',
|
||||
imageAltText: 'A nice thumbnail image to show what this block looks like',
|
||||
@@ -90,7 +92,7 @@ const QuoteBlock = {
|
||||
]
|
||||
};
|
||||
|
||||
const ExampleCollection = {
|
||||
const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
@@ -106,3 +108,12 @@ const ExampleCollection = {
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
As you build your own Block configs, you might want to store them in separate files but retain typing accordingly. To do so, you can import and use Payload's `Block` type:
|
||||
|
||||
```ts
|
||||
import type { Block } from 'payload/types';
|
||||
|
||||
```
|
||||
|
||||
@@ -22,7 +22,7 @@ keywords: checkbox, fields, config, configuration, documentation, Content Manage
|
||||
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
|
||||
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value, will default to false if field is also `required`. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. See the [default field admin config](/docs/fields/overview#admin-config) for more details. |
|
||||
@@ -31,9 +31,11 @@ keywords: checkbox, fields, config, configuration, documentation, Content Manage
|
||||
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.js`
|
||||
```js
|
||||
{
|
||||
`collections/ExampleCollection.ts`
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
@@ -43,6 +45,5 @@ keywords: checkbox, fields, config, configuration, documentation, Content Manage
|
||||
defaultValue: false,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
@@ -28,7 +28,7 @@ This field uses `prismjs` for syntax highlighting and `react-simple-code-editor`
|
||||
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
|
||||
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
@@ -39,13 +39,27 @@ This field uses `prismjs` for syntax highlighting and `react-simple-code-editor`
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), the Code field type also allows for the customization of a `language` property.
|
||||
|
||||
Currently, the `language` property only supports JavaScript syntax but more support will be added as requested.
|
||||
The following `prismjs` plugins are imported, enabling the `language` property to accept the following values:
|
||||
|
||||
| Plugin | Language |
|
||||
| ---------------------------- | ----------- |
|
||||
| **`prism-css`** | `css` |
|
||||
| **`prism-clike`** | `clike` |
|
||||
| **`prism-markup`** | `markup`, `html`, `xml`, `svg`, `mathml`, `ssml`, `atom`, `rss` |
|
||||
| **`prism-javascript`** | `javascript`, `js` |
|
||||
| **`prism-json`** | `json` |
|
||||
| **`prism-jsx`** | `jsx` |
|
||||
| **`prism-typescript`** | `typescript`, `ts` |
|
||||
| **`prism-tsx`** | `tsx` |
|
||||
| **`prism-yaml`** | `yaml`, `yml` |
|
||||
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.js`
|
||||
```js
|
||||
{
|
||||
`collections/ExampleCollection.ts
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
@@ -57,6 +71,5 @@ Currently, the `language` property only supports JavaScript syntax but more supp
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
45
docs/fields/collapsible.mdx
Normal file
45
docs/fields/collapsible.mdx
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: Collapsible Field
|
||||
label: Collapsible
|
||||
order: 60
|
||||
desc: With the Collapsible field, you can place fields within a collapsible layout component that can be collapsed / expanded.
|
||||
keywords: row, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
<Banner >
|
||||
The Collapsible field is presentational-only and only affects the Admin panel. By using it, you can place fields within a nice layout component that can be collapsed / expanded.
|
||||
</Banner>
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
| ---------------- | ----------- |
|
||||
| **`label`** * | A label to render within the header of the collapsible component. |
|
||||
| **`fields`** * | Array of field types to nest within this Collapsible. |
|
||||
| **`admin`** | Admin-specific configuration. See the [default field admin config](/docs/fields/overview#admin-config) for more details. |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.ts`
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
label: 'Header of collapsible goes here',
|
||||
type: 'collapsible', // required
|
||||
fields: [ // required
|
||||
{
|
||||
name: 'someTextField',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
};
|
||||
```
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Date Field
|
||||
label: Date
|
||||
order: 60
|
||||
order: 70
|
||||
desc: The Date field type stores a Date in the database. Learn how to use and customize the Date field, see examples and options.
|
||||
keywords: date, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
@@ -25,21 +25,21 @@ This field uses [`react-datepicker`](https://www.npmjs.com/package/react-datepic
|
||||
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
|
||||
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin). |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Admin Date Config
|
||||
### Admin Config
|
||||
|
||||
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can customize the following fields that will adjust how the component displays in the admin panel via the `date` property.
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`pickerAppearance`** | Determines the appearance of the datepicker: `dayAndTime` `timeOnly` `dayOnly`. Defaults to `dayAndTime`. |
|
||||
| **`displayFormat`** | Determines how the date is presented. dayAndTime default to `MMM d, yyy h:mm a` timeOnly defaults to `h:mm a` and dayOnly defaults to `MMM d, yyy`. |
|
||||
| **`pickerAppearance`** | Determines the appearance of the datepicker: `dayAndTime` `timeOnly` `dayOnly` `monthOnly`. Defaults to `dayAndTime`. |
|
||||
| **`displayFormat`** | Determines how the date is presented. dayAndTime default to `MMM d, yyy h:mm a` timeOnly defaults to `h:mm a` dayOnly defaults to `MMM d, yyy` and monthOnly defaults to `MM/yyyy`. |
|
||||
| **`placeholder`** | Placeholder text for the field. |
|
||||
| **`monthsToShow`** | Number of months to display max is 2. Defaults to 1. |
|
||||
| **`minDate`** | Passed directly to [react-datepicker](https://github.com/Hacker0x01/react-datepicker/blob/master/docs/datepicker.md). |
|
||||
@@ -55,10 +55,12 @@ Common use cases for customizing the `date` property are to restrict your field
|
||||
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.js`
|
||||
`collections/ExampleCollection.ts`
|
||||
|
||||
```js
|
||||
{
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
@@ -74,6 +76,5 @@ Common use cases for customizing the `date` property are to restrict your field
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Email Field
|
||||
label: Email
|
||||
order: 70
|
||||
order: 80
|
||||
desc: The Email field enforces that the value provided is a valid email address. Learn how to use Email fields, see examples and options.
|
||||
keywords: email, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
@@ -23,10 +23,10 @@ keywords: email, fields, config, configuration, documentation, Content Managemen
|
||||
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
|
||||
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin). |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
|
||||
@@ -44,9 +44,11 @@ Set this property to a string that will be used for browser autocomplete.
|
||||
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.js`
|
||||
```js
|
||||
{
|
||||
`collections/ExampleCollection.ts`
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
@@ -56,6 +58,5 @@ Set this property to a string that will be used for browser autocomplete.
|
||||
required: true,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Group Field
|
||||
label: Group
|
||||
order: 80
|
||||
order: 90
|
||||
desc: The Group field allows other fields to be nested under a common property. Learn how to use Group fields, see examples and options.
|
||||
keywords: group, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
@@ -22,7 +22,7 @@ keywords: group, fields, config, configuration, documentation, Content Managemen
|
||||
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
|
||||
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin panel. |
|
||||
| **`defaultValue`** | Provide an object of data to be used for this field's default value. |
|
||||
| **`defaultValue`** | Provide an object of data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. If enabled, a separate, localized set of all data within this Group will be kept, so there is no need to specify each nested field as `localized`. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
@@ -39,9 +39,11 @@ Set this property to `true` to hide this field's gutter within the admin panel.
|
||||
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.js`
|
||||
```js
|
||||
{
|
||||
`collections/ExampleCollection.ts`
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
@@ -65,6 +67,5 @@ Set this property to `true` to hide this field's gutter within the admin panel.
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Number Field
|
||||
label: Number
|
||||
order: 90
|
||||
order: 100
|
||||
desc: Number fields store and validate numeric data. Learn how to use and format Number fields, see examples and Number field options.
|
||||
keywords: number, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
@@ -25,10 +25,10 @@ keywords: number, fields, config, configuration, documentation, Content Manageme
|
||||
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
|
||||
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin). |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
|
||||
*\* An asterisk denotes that a property is required.*
|
||||
|
||||
@@ -50,9 +50,11 @@ Set this property to a string that will be used for browser autocomplete.
|
||||
|
||||
### Example
|
||||
|
||||
`collections/ExampleCollection.js`
|
||||
```js
|
||||
{
|
||||
`collections/ExampleCollection.ts`
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
fields: [
|
||||
{
|
||||
@@ -64,6 +66,5 @@ Set this property to a string that will be used for browser autocomplete.
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user