Compare commits
809 Commits
v3.0.0-bet
...
plugin-str
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34ce21c1f8 | ||
|
|
ca6725b4ec | ||
|
|
a54fc12189 | ||
|
|
cfe0fdf3f6 | ||
|
|
2e2013602d | ||
|
|
9f17391475 | ||
|
|
cd12963296 | ||
|
|
165442028c | ||
|
|
3c4c32fecd | ||
|
|
7648b32669 | ||
|
|
c835a21707 | ||
|
|
a5e6ea5737 | ||
|
|
106d7dde23 | ||
|
|
276a6dde06 | ||
|
|
f12cb6298a | ||
|
|
2e352552d1 | ||
|
|
aae52181b0 | ||
|
|
07ff181ccf | ||
|
|
f430db8bc5 | ||
|
|
a2e54db469 | ||
|
|
73bd4370b8 | ||
|
|
95a4443ea1 | ||
|
|
59ee821cec | ||
|
|
4892d96515 | ||
|
|
b0de37ba95 | ||
|
|
0cf96785bc | ||
|
|
e7ae5f0a97 | ||
|
|
7039b1f82e | ||
|
|
67ce92cf2e | ||
|
|
216e4ef077 | ||
|
|
6f453ea05a | ||
|
|
cf6a5df926 | ||
|
|
769bee82cd | ||
|
|
55eb6e7583 | ||
|
|
a907480a94 | ||
|
|
9c25754eed | ||
|
|
7c9ec9c4e0 | ||
|
|
a0bd7060c4 | ||
|
|
169da5c3d8 | ||
|
|
25932c0db6 | ||
|
|
3bce3d4240 | ||
|
|
e2a13deff6 | ||
|
|
5d99adfd38 | ||
|
|
819753a637 | ||
|
|
b7d65ab717 | ||
|
|
e365300bee | ||
|
|
4d9f494a80 | ||
|
|
fa6d4596dc | ||
|
|
08b02ecba2 | ||
|
|
eba777c3a0 | ||
|
|
bfe6918681 | ||
|
|
e5d6cdae38 | ||
|
|
218f2ead03 | ||
|
|
e9c1222182 | ||
|
|
c8ed6454a7 | ||
|
|
4077598777 | ||
|
|
65d7d54ba3 | ||
|
|
6690c37c4e | ||
|
|
0efc610210 | ||
|
|
cc99c3a619 | ||
|
|
24a8dc7aa3 | ||
|
|
90764efa9a | ||
|
|
d05e3b0411 | ||
|
|
e4bc281fc2 | ||
|
|
9d05b82dc6 | ||
|
|
f2284f3d1b | ||
|
|
1347b6cc36 | ||
|
|
0a56d50334 | ||
|
|
02999a5659 | ||
|
|
365127bee4 | ||
|
|
b67e97aa7f | ||
|
|
61e8ce1743 | ||
|
|
034aa68cd4 | ||
|
|
268e6c485e | ||
|
|
4c1a5dca44 | ||
|
|
a12d1f4755 | ||
|
|
d55be73992 | ||
|
|
b9f236ae50 | ||
|
|
1d38e6d5d5 | ||
|
|
2f3c994cea | ||
|
|
0586f236bb | ||
|
|
d582619ead | ||
|
|
17fc2d13d0 | ||
|
|
800ffd2611 | ||
|
|
661ca74364 | ||
|
|
ec73b461a8 | ||
|
|
94885f3c65 | ||
|
|
31d0b309fe | ||
|
|
c86526b5c8 | ||
|
|
28a065072f | ||
|
|
efc0bc9ec9 | ||
|
|
ade1d27c95 | ||
|
|
1040731e32 | ||
|
|
30f28898b6 | ||
|
|
6cb0470906 | ||
|
|
170ea5badc | ||
|
|
cfb56589eb | ||
|
|
f312bac065 | ||
|
|
3dd3f5b135 | ||
|
|
59f4d125ab | ||
|
|
b2b2ee3338 | ||
|
|
7308abaabd | ||
|
|
9b1d0b2d0f | ||
|
|
9014f1fa63 | ||
|
|
ba75d876e3 | ||
|
|
f2b2e5cda9 | ||
|
|
f751f69239 | ||
|
|
f7ac9ff52a | ||
|
|
ba7a043a99 | ||
|
|
b149180db4 | ||
|
|
4efb9dd867 | ||
|
|
7002ca78b9 | ||
|
|
44ca3a4073 | ||
|
|
dc7c952ace | ||
|
|
c8a659cd39 | ||
|
|
6ba293c0f8 | ||
|
|
96a624ad5c | ||
|
|
545949dafc | ||
|
|
d9f61bbdc8 | ||
|
|
be06579b3e | ||
|
|
25e9bc62db | ||
|
|
aca567634b | ||
|
|
1f0934877c | ||
|
|
61da010991 | ||
|
|
ab9074220a | ||
|
|
afa90a4362 | ||
|
|
bc0516da90 | ||
|
|
46daf473c8 | ||
|
|
337b8ccbf3 | ||
|
|
ba2e4c278f | ||
|
|
3196036ae9 | ||
|
|
9bc3ad5159 | ||
|
|
94d18e8d74 | ||
|
|
c624eea0d8 | ||
|
|
f97627092c | ||
|
|
f00183029e | ||
|
|
b6c5aaa966 | ||
|
|
517aaa0665 | ||
|
|
2c2ffe406f | ||
|
|
7f39afa192 | ||
|
|
fc4d24aa88 | ||
|
|
efa56cefc1 | ||
|
|
907d7d1d3a | ||
|
|
eca1517237 | ||
|
|
9865ae998b | ||
|
|
1a0ef4824b | ||
|
|
39e110e633 | ||
|
|
3e780b9815 | ||
|
|
a308d6384f | ||
|
|
492ed30cb8 | ||
|
|
fca5a404db | ||
|
|
b13f7e8843 | ||
|
|
25dfdb66cd | ||
|
|
9c9e6896a5 | ||
|
|
a3085435ef | ||
|
|
1466657e8f | ||
|
|
1348483648 | ||
|
|
5321098d7e | ||
|
|
c57591bc4f | ||
|
|
9750bc217e | ||
|
|
468e5441f1 | ||
|
|
3c5cce4c6f | ||
|
|
9f0f94893d | ||
|
|
03b7892fc9 | ||
|
|
f96cf593ce | ||
|
|
8259611ce6 | ||
|
|
a3ed25a253 | ||
|
|
69e7b7a158 | ||
|
|
c6da99b4d1 | ||
|
|
ebd23caa56 | ||
|
|
faa9b21824 | ||
|
|
1690560f11 | ||
|
|
0058660b3f | ||
|
|
6d7ef919cb | ||
|
|
abffa37d85 | ||
|
|
1b208c7add | ||
|
|
2840632161 | ||
|
|
0841d5a35e | ||
|
|
bd19fcf259 | ||
|
|
18645771c8 | ||
|
|
20377bb22c | ||
|
|
7daaf3d780 | ||
|
|
667d3dc885 | ||
|
|
51474fa661 | ||
|
|
d475b16790 | ||
|
|
4d0befb67a | ||
|
|
84d214f992 | ||
|
|
51cd5942fa | ||
|
|
74105d8ee5 | ||
|
|
1cc61ddab6 | ||
|
|
99397a0bdb | ||
|
|
a5492afad6 | ||
|
|
6c1156e2e4 | ||
|
|
77e8ce980e | ||
|
|
39e34ce94e | ||
|
|
2aa2971fb9 | ||
|
|
c82d2caa29 | ||
|
|
cf52d64d98 | ||
|
|
320dcc0a08 | ||
|
|
ea18735d3b | ||
|
|
4baa0e3221 | ||
|
|
2d35d695ea | ||
|
|
bb911cc7ec | ||
|
|
874774375f | ||
|
|
d337c5b523 | ||
|
|
dab632388e | ||
|
|
6f63e724e3 | ||
|
|
b230da33ce | ||
|
|
58427ffae3 | ||
|
|
9ecc6c8899 | ||
|
|
336438506c | ||
|
|
025306f9e6 | ||
|
|
b1c9dee840 | ||
|
|
9a17d6614c | ||
|
|
28a6d14a9a | ||
|
|
074a3f0e14 | ||
|
|
0a51de7623 | ||
|
|
e2004e525d | ||
|
|
4014b124ca | ||
|
|
ca2ccc6614 | ||
|
|
7fd716188b | ||
|
|
02e0e6fff8 | ||
|
|
dfa0afe7b3 | ||
|
|
e1b8e6fe02 | ||
|
|
c2c5ac5a1c | ||
|
|
f597cdfcfd | ||
|
|
967bd941e5 | ||
|
|
5f1c47130d | ||
|
|
e8165b79c5 | ||
|
|
1c986a9832 | ||
|
|
fb4ef6fc0f | ||
|
|
015aafda75 | ||
|
|
dae56e60ee | ||
|
|
9f0aaf066e | ||
|
|
f6ba3befae | ||
|
|
68ea693a88 | ||
|
|
62fa22cb24 | ||
|
|
3b4bb3065a | ||
|
|
4e0725f7c6 | ||
|
|
ff70fd9813 | ||
|
|
e40570bd0d | ||
|
|
b7e852993b | ||
|
|
ab97590879 | ||
|
|
ed86b15242 | ||
|
|
d58631c12c | ||
|
|
37c8386a51 | ||
|
|
2f9ed34d13 | ||
|
|
921a5c065d | ||
|
|
e3003b443f | ||
|
|
8a622984e7 | ||
|
|
507e0954b2 | ||
|
|
63bc6ae52f | ||
|
|
d016fbd2a5 | ||
|
|
9525511e8b | ||
|
|
1e834e58a4 | ||
|
|
373cb00139 | ||
|
|
558b298bf0 | ||
|
|
cd24e2bb3c | ||
|
|
ac8c2096af | ||
|
|
626be15578 | ||
|
|
67c0b0e6e0 | ||
|
|
11239103a6 | ||
|
|
4998ef8c9b | ||
|
|
e44ce819ce | ||
|
|
b700208b98 | ||
|
|
4a54aa7776 | ||
|
|
4fddea86eb | ||
|
|
547acfe876 | ||
|
|
56c6700cf2 | ||
|
|
a352ebc552 | ||
|
|
312b4a94c3 | ||
|
|
cb3dbed127 | ||
|
|
3a73a9696f | ||
|
|
d587441e80 | ||
|
|
fcfc3c593f | ||
|
|
baf945b1ea | ||
|
|
4f9d78df5e | ||
|
|
f07783a279 | ||
|
|
eeddeceda9 | ||
|
|
4c832ad984 | ||
|
|
81bc777dfe | ||
|
|
a757635bc7 | ||
|
|
80bf0a3067 | ||
|
|
a06c06415c | ||
|
|
2dd7e82fdc | ||
|
|
63e04e2ae0 | ||
|
|
e71888a625 | ||
|
|
c5514f1441 | ||
|
|
23e2fe643e | ||
|
|
bdef2f1bc7 | ||
|
|
8f03cd7c78 | ||
|
|
c0092191a6 | ||
|
|
b9854ed60a | ||
|
|
576ee14976 | ||
|
|
bf77cec7e9 | ||
|
|
ab8b2f3fb8 | ||
|
|
db5f3f3ccd | ||
|
|
cece39957f | ||
|
|
157fff0417 | ||
|
|
88e113a545 | ||
|
|
d33afe48fe | ||
|
|
e76df32f09 | ||
|
|
b068f30f51 | ||
|
|
82bd5c656f | ||
|
|
afe8992ca6 | ||
|
|
48a410e294 | ||
|
|
82b88a315f | ||
|
|
cc94078607 | ||
|
|
1c30ad73b6 | ||
|
|
d9442dcce3 | ||
|
|
de92c50847 | ||
|
|
b7f5f932f6 | ||
|
|
bc9e591e37 | ||
|
|
ad38011348 | ||
|
|
d02b1fb084 | ||
|
|
51efe4f39b | ||
|
|
30e535b5b9 | ||
|
|
4bd3bb9400 | ||
|
|
69c93d3c62 | ||
|
|
78aa957043 | ||
|
|
cd06c022c0 | ||
|
|
8299e9fc33 | ||
|
|
9df5ab8a10 | ||
|
|
6979f5a1b1 | ||
|
|
723791b94f | ||
|
|
aa3833ec83 | ||
|
|
2972af2af1 | ||
|
|
857b9a4ac3 | ||
|
|
f829b084ba | ||
|
|
6ae682698d | ||
|
|
622cdb0440 | ||
|
|
cac52da638 | ||
|
|
3cb3c1aceb | ||
|
|
db4aacebb8 | ||
|
|
b735d6aa16 | ||
|
|
fbec3a33e0 | ||
|
|
034be89bdb | ||
|
|
1f2af0963b | ||
|
|
20f1ece2d7 | ||
|
|
2be5ad0eba | ||
|
|
5c58bd322d | ||
|
|
23f3eb1cf0 | ||
|
|
af67749e49 | ||
|
|
43dab5c705 | ||
|
|
9b7e62dc20 | ||
|
|
6e38cc2bcf | ||
|
|
83551bfcaa | ||
|
|
7b44d9d28a | ||
|
|
182d5db6de | ||
|
|
93109ec84a | ||
|
|
4d9e0f35f0 | ||
|
|
19327c8d6d | ||
|
|
831f1ff5be | ||
|
|
a8ac8b4633 | ||
|
|
36b1f5a763 | ||
|
|
24f697219b | ||
|
|
3fccd34abe | ||
|
|
a38f8e93a6 | ||
|
|
84570e6e3b | ||
|
|
5ad8e0edcb | ||
|
|
91bac9c0aa | ||
|
|
33f6edc9d5 | ||
|
|
e1f91f5170 | ||
|
|
0e75dfb5c1 | ||
|
|
1300e264be | ||
|
|
5600125de7 | ||
|
|
22e270f89c | ||
|
|
593f82bcba | ||
|
|
cbf3da1144 | ||
|
|
1a2aab4126 | ||
|
|
d0ba694c80 | ||
|
|
d78df36d9b | ||
|
|
02572d945a | ||
|
|
f05a433320 | ||
|
|
9ce3b3ab29 | ||
|
|
ade637befb | ||
|
|
c31b8dcaa0 | ||
|
|
0ffdcc685f | ||
|
|
881119ba3a | ||
|
|
60372faf36 | ||
|
|
8bca0b0b86 | ||
|
|
87a1d698b2 | ||
|
|
c11600aac3 | ||
|
|
ad01c6784d | ||
|
|
62601c54a7 | ||
|
|
4a144ddc44 | ||
|
|
9152a238d2 | ||
|
|
fc8b835264 | ||
|
|
28ee5e34c3 | ||
|
|
e25886649f | ||
|
|
985796be54 | ||
|
|
bd8b5123b0 | ||
|
|
c380deee4a | ||
|
|
0b12aac895 | ||
|
|
90d3f178ab | ||
|
|
a8c9625cde | ||
|
|
938d069523 | ||
|
|
1a337ec223 | ||
|
|
08f372e6c2 | ||
|
|
2994269f22 | ||
|
|
8d1a706928 | ||
|
|
fcb29bb1c6 | ||
|
|
2c402cc65c | ||
|
|
ad38f76011 | ||
|
|
b974a2c042 | ||
|
|
cb8d562132 | ||
|
|
1f0036054a | ||
|
|
aaa4397351 | ||
|
|
6185f8a5d8 | ||
|
|
e47e544364 | ||
|
|
2e80350a5a | ||
|
|
4c4f924e90 | ||
|
|
170957c380 | ||
|
|
9061ae05e7 | ||
|
|
fe0028c899 | ||
|
|
ec1ad0b662 | ||
|
|
e5f32562a3 | ||
|
|
ba6100cbe8 | ||
|
|
c08c8b5628 | ||
|
|
9e918831d1 | ||
|
|
25c9a145be | ||
|
|
3b1d331316 | ||
|
|
5aa68b5e5d | ||
|
|
d8e9084db2 | ||
|
|
65690a675c | ||
|
|
9530d28a67 | ||
|
|
509ec677c4 | ||
|
|
a00439ea89 | ||
|
|
0055a8eb36 | ||
|
|
de5d6cc4bd | ||
|
|
51f84a4fcf | ||
|
|
c0ba6cc19a | ||
|
|
5fa99fb060 | ||
|
|
e3c3ddac34 | ||
|
|
6186493246 | ||
|
|
9b44296092 | ||
|
|
cbd03ed2f8 | ||
|
|
cf135fd1e4 | ||
|
|
e7608f5507 | ||
|
|
608d6d0a87 | ||
|
|
9bbacc4fb1 | ||
|
|
7df7bf448b | ||
|
|
44599cbc7b | ||
|
|
373787de31 | ||
|
|
c1c86009a5 | ||
|
|
6cf6ca3ea8 | ||
|
|
6706bdb140 | ||
|
|
5caaa032bb | ||
|
|
4cf8c5bd78 | ||
|
|
d6afee662b | ||
|
|
0fc62446b8 | ||
|
|
823fde07c3 | ||
|
|
fe35b7d32c | ||
|
|
3b45b0d085 | ||
|
|
742a7af93d | ||
|
|
a7e7c92768 | ||
|
|
e10d5df0b2 | ||
|
|
3023118b71 | ||
|
|
80375d1798 | ||
|
|
589b492b2e | ||
|
|
bdeef377ea | ||
|
|
b76c5c8c11 | ||
|
|
58c9fecbae | ||
|
|
e88de5db60 | ||
|
|
ce75fab4fc | ||
|
|
5b57f1411a | ||
|
|
11ff2042f3 | ||
|
|
8f9481f8a7 | ||
|
|
5de32b2534 | ||
|
|
dbcffc688f | ||
|
|
17cf179d16 | ||
|
|
1dc86aa6e6 | ||
|
|
d8e0824547 | ||
|
|
1453547e35 | ||
|
|
4c6874449a | ||
|
|
332c9b3a98 | ||
|
|
73544b5b5e | ||
|
|
e03302160a | ||
|
|
edb530fc64 | ||
|
|
064b4baed8 | ||
|
|
4cb8259b8b | ||
|
|
34b2451b3b | ||
|
|
29ccd06553 | ||
|
|
a5971c12b3 | ||
|
|
0900baf8f9 | ||
|
|
d1a9a3e4d5 | ||
|
|
d27a33dc1d | ||
|
|
d01c586a90 | ||
|
|
caa0d7cf0d | ||
|
|
c40b5a1784 | ||
|
|
b0869ea0ed | ||
|
|
00faebed4b | ||
|
|
638a35a2e7 | ||
|
|
44bca2ed36 | ||
|
|
fa90cec758 | ||
|
|
2164dcc594 | ||
|
|
a3ae416085 | ||
|
|
02d2c51717 | ||
|
|
b3503e3dce | ||
|
|
2e5400fa7a | ||
|
|
48dc116f57 | ||
|
|
9b2ba7cc0e | ||
|
|
916923071a | ||
|
|
3ceb6efd32 | ||
|
|
8184e0005a | ||
|
|
1c0d43c61a | ||
|
|
0eb7b44735 | ||
|
|
c038acb3a4 | ||
|
|
f8c2ccf6b1 | ||
|
|
e6092fd36c | ||
|
|
e0eae77758 | ||
|
|
0780ff22bb | ||
|
|
e7f6bfbe9d | ||
|
|
23df60dba5 | ||
|
|
8919b86571 | ||
|
|
8104fac5ed | ||
|
|
59ef1263a5 | ||
|
|
813e3dbc98 | ||
|
|
12a23092e1 | ||
|
|
4a49beb552 | ||
|
|
80530c666e | ||
|
|
f53e2df951 | ||
|
|
db58e2bab2 | ||
|
|
d88e97e123 | ||
|
|
bb72e506e6 | ||
|
|
b1f727fd6a | ||
|
|
72af18229b | ||
|
|
af52b526c8 | ||
|
|
60525623bf | ||
|
|
0fba582926 | ||
|
|
5d1cad3adb | ||
|
|
e31f72da8e | ||
|
|
7aa058d604 | ||
|
|
64e80d242e | ||
|
|
e8f2ca484e | ||
|
|
ceca5c4e97 | ||
|
|
ee13736288 | ||
|
|
815bdfac0b | ||
|
|
7a7f0ed7e8 | ||
|
|
ad42d541b3 | ||
|
|
32ed95e1ee | ||
|
|
70e57fef18 | ||
|
|
0a07f607b9 | ||
|
|
3918fc7c21 | ||
|
|
13f71ac475 | ||
|
|
07720e777a | ||
|
|
efff47e400 | ||
|
|
453ac218ea | ||
|
|
d4b09bd9cd | ||
|
|
dd67e03fc1 | ||
|
|
548de80bee | ||
|
|
2c05fbbb5e | ||
|
|
9b54659818 | ||
|
|
e9f550406e | ||
|
|
98b87e2278 | ||
|
|
5f3d0169be | ||
|
|
35c2a085ef | ||
|
|
1ac943ed5e | ||
|
|
25cee8bb10 | ||
|
|
419aef452d | ||
|
|
ea52489126 | ||
|
|
e80c70acae | ||
|
|
70b0064d0b | ||
|
|
9636bf6efd | ||
|
|
8f4d0da4e0 | ||
|
|
f0f1dbdcb0 | ||
|
|
a895aee8b1 | ||
|
|
aa1dac08c1 | ||
|
|
b8cd1c6ba4 | ||
|
|
6344464bc6 | ||
|
|
5d4022f144 | ||
|
|
bf942fdfa6 | ||
|
|
d6c25783cf | ||
|
|
82e9d31127 | ||
|
|
399e606b34 | ||
|
|
0d18822062 | ||
|
|
00fc0343da | ||
|
|
6323965c65 | ||
|
|
6d6823c3e5 | ||
|
|
ca70298436 | ||
|
|
4f565759f6 | ||
|
|
df39602758 | ||
|
|
6ea6172afa | ||
|
|
486774796d | ||
|
|
1cd1c38764 | ||
|
|
f6d7da7510 | ||
|
|
cdc4cb971b | ||
|
|
e0191b54e1 | ||
|
|
2315781f18 | ||
|
|
a0a58e7fd2 | ||
|
|
e1813fb884 | ||
|
|
da184d40ec | ||
|
|
ca8675f89d | ||
|
|
e8c6c9338d | ||
|
|
558534aff8 | ||
|
|
29c901ba9b | ||
|
|
f3876c2a39 | ||
|
|
c3a3942969 | ||
|
|
23b135b963 | ||
|
|
e3c8105cc2 | ||
|
|
2c71aaef75 | ||
|
|
922fb9b7fa | ||
|
|
0740d5095e | ||
|
|
b392d656fe | ||
|
|
c0eef90cdc | ||
|
|
db22cbdf21 | ||
|
|
1e8a6b7899 | ||
|
|
5d934ba02d | ||
|
|
f651665f2f | ||
|
|
5d3659d48a | ||
|
|
47106d5a1a | ||
|
|
afa2b942e0 | ||
|
|
20ddd0de5b | ||
|
|
64f705c3c9 | ||
|
|
b30ea8aa6b | ||
|
|
471d2113a7 | ||
|
|
8725d41164 | ||
|
|
0bd81aa25a | ||
|
|
8c09ca9be5 | ||
|
|
90d7ee3e65 | ||
|
|
58bbd8c00f | ||
|
|
003ad065c3 | ||
|
|
70715926a8 | ||
|
|
b3a6bfacf2 | ||
|
|
e1d9accb27 | ||
|
|
f2f55a84cc | ||
|
|
eba53ba60a | ||
|
|
f73d503fec | ||
|
|
6930c4e9f2 | ||
|
|
3eb681e847 | ||
|
|
cb4638cfa1 | ||
|
|
b40e9f85a2 | ||
|
|
e5a7907a72 | ||
|
|
3f25d1ca84 | ||
|
|
d5720bea7b | ||
|
|
8ce15c8b07 | ||
|
|
9f5efef78f | ||
|
|
dfba5222f3 | ||
|
|
b99d24fcfa | ||
|
|
836ed77568 | ||
|
|
1c5d5b07c8 | ||
|
|
da5f1f2240 | ||
|
|
c84c58c7b4 | ||
|
|
1c1b8f3cec | ||
|
|
3f69f83180 | ||
|
|
371353f153 | ||
|
|
a92c6334b6 | ||
|
|
eb9e771a9c | ||
|
|
ee5390aaca | ||
|
|
a861311c5a | ||
|
|
74c3fe1bb2 | ||
|
|
a2be50279e | ||
|
|
403eb06acf | ||
|
|
f5c2cd74cc | ||
|
|
a6a1963ec6 | ||
|
|
0647c870f1 | ||
|
|
3b88adc7d0 | ||
|
|
82383a5b5f | ||
|
|
f9dda628b2 | ||
|
|
93eb0e4a31 | ||
|
|
2e362f44f4 | ||
|
|
775502b161 | ||
|
|
84d75ce6ca | ||
|
|
175cf229c0 | ||
|
|
bb40bd3efb | ||
|
|
3d74c133aa | ||
|
|
0f6b6ca970 | ||
|
|
8e1692ef10 | ||
|
|
1d1ee913fc | ||
|
|
beca43341f | ||
|
|
84818469ea | ||
|
|
0a259d27b5 | ||
|
|
e2e56a4d58 | ||
|
|
d0f7677d5f | ||
|
|
35956eb837 | ||
|
|
d92af295eb | ||
|
|
493fde5ccc | ||
|
|
c6bd20ef33 | ||
|
|
6d5ac1de1e | ||
|
|
fa3b3dd62d | ||
|
|
1115387744 | ||
|
|
53e8690feb | ||
|
|
4319fe1c6e | ||
|
|
2fee0c0d44 | ||
|
|
7923edd7bc | ||
|
|
5702b83e82 | ||
|
|
63e5c43fe6 | ||
|
|
04f2888135 | ||
|
|
0f69b9c2f8 | ||
|
|
f1bb2f8151 | ||
|
|
ea528b8c10 | ||
|
|
0053e40404 | ||
|
|
605c0be43c | ||
|
|
3152b4c4c5 | ||
|
|
a64b80babc | ||
|
|
5cfde542b1 | ||
|
|
772020963e | ||
|
|
5e083689d0 | ||
|
|
911764a490 | ||
|
|
e3b81d913d | ||
|
|
41b3b17911 | ||
|
|
e9860b36f3 | ||
|
|
14b39fbc85 | ||
|
|
d08e85d08c | ||
|
|
abcbf9974d | ||
|
|
d01437d212 | ||
|
|
06729a0a73 | ||
|
|
2bd7822a16 | ||
|
|
bc7daf6b49 | ||
|
|
feab679ef7 | ||
|
|
be39ed4317 | ||
|
|
570e192eb4 | ||
|
|
22f4967dd4 | ||
|
|
4873c36129 | ||
|
|
f0ec21cdda | ||
|
|
da737bdf8e | ||
|
|
40508880c1 | ||
|
|
8f420d841a | ||
|
|
9022e27308 | ||
|
|
acf2e41312 | ||
|
|
6acfae8ee7 | ||
|
|
20bdd91da4 | ||
|
|
50502834c9 | ||
|
|
2b731c1088 | ||
|
|
983733ad74 | ||
|
|
555d02769a | ||
|
|
682eca2186 | ||
|
|
6affa1c304 | ||
|
|
1d14d9f8b8 | ||
|
|
0abaddc2ef | ||
|
|
57dc93da5d | ||
|
|
21b9453cf4 | ||
|
|
136993ec2b | ||
|
|
63bc4cabe1 | ||
|
|
6a8a6e4ef4 | ||
|
|
9828772890 | ||
|
|
6116573164 | ||
|
|
cab6babd60 | ||
|
|
55399424a1 | ||
|
|
28d3f73c2a | ||
|
|
28a30120dd | ||
|
|
40a0921597 | ||
|
|
7eae86bcb3 | ||
|
|
0b80e4a403 | ||
|
|
b378532ddf | ||
|
|
d419275fb5 | ||
|
|
0fb3a9ca89 | ||
|
|
f43cf185d4 | ||
|
|
5d15955f83 | ||
|
|
2d35e06667 | ||
|
|
d2de6db449 | ||
|
|
a3e78161b5 | ||
|
|
d543665995 | ||
|
|
db7dddf1c5 | ||
|
|
3027a03ad1 | ||
|
|
85e38b7cfd | ||
|
|
9090540ece | ||
|
|
46ef284f6b | ||
|
|
0727dcd963 | ||
|
|
52f8d4f9f0 | ||
|
|
f1fa374ed1 | ||
|
|
6b691eee43 | ||
|
|
be3beabb9b | ||
|
|
1fa00cc25c | ||
|
|
f70943524b | ||
|
|
a67080a291 | ||
|
|
69a99445c9 | ||
|
|
00d8480062 | ||
|
|
7424ba9090 | ||
|
|
ec4d2f97cb | ||
|
|
9d9ac0ec28 | ||
|
|
635e7c26e8 | ||
|
|
c4a4678afb | ||
|
|
a5a91c08a9 | ||
|
|
7db58b482b | ||
|
|
1b914083c8 | ||
|
|
657d14c07b | ||
|
|
fbf8ab72a4 | ||
|
|
997f158149 | ||
|
|
c3be5d1d5e | ||
|
|
250bcd8189 | ||
|
|
a71d37b398 | ||
|
|
5c5523195c | ||
|
|
bff4cf518f | ||
|
|
8015e999cd | ||
|
|
0c905f0da7 | ||
|
|
e691a90a4c | ||
|
|
0b2da4fba7 | ||
|
|
1c6d6788a3 | ||
|
|
ecc7978184 | ||
|
|
1b9ee64a67 | ||
|
|
22b02226c3 | ||
|
|
e4102b88d8 | ||
|
|
a099f55a69 | ||
|
|
1f1445c798 | ||
|
|
741a5e3650 | ||
|
|
365047a3fb | ||
|
|
42c06acd18 | ||
|
|
f2c8ac4a9a | ||
|
|
05e8914db7 | ||
|
|
35191bdd66 | ||
|
|
98890eee1f | ||
|
|
ef43629502 | ||
|
|
c703497924 | ||
|
|
5caad706bb | ||
|
|
aa048d5409 | ||
|
|
aafd538cf8 | ||
|
|
1b42bd207d | ||
|
|
9fac2ef24e |
@@ -8,7 +8,3 @@
|
||||
**/dist/**
|
||||
**/node_modules
|
||||
**/temp
|
||||
playwright.config.ts
|
||||
jest.config.js
|
||||
test/live-preview/next-app
|
||||
tsconfig.tsbuildinfo
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/** @type {import('eslint').Linter.Config} */
|
||||
module.exports = {
|
||||
extends: ['@payloadcms'],
|
||||
ignorePatterns: ['README.md', 'packages/**/*.spec.ts'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['packages/**'],
|
||||
plugins: ['payload'],
|
||||
rules: {
|
||||
'payload/no-jsx-import-statements': 'warn',
|
||||
'payload/no-relative-monorepo-imports': 'error',
|
||||
'payload/no-imports-from-exports-dir': 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['scripts/**'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'no-console': 'off',
|
||||
'perfectionist/sort-object-types': 'off',
|
||||
'perfectionist/sort-objects': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
extends: ['plugin:@typescript-eslint/disable-type-checked'],
|
||||
files: ['*.js', '*.cjs', '*.json', '*.md', '*.yml', '*.yaml'],
|
||||
},
|
||||
{
|
||||
files: ['packages/eslint-config-payload/**'],
|
||||
rules: {
|
||||
'perfectionist/sort-objects': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['templates/vercel-postgres/**'],
|
||||
rules: {
|
||||
'no-restricted-exports': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['package.json', 'tsconfig.json'],
|
||||
rules: {
|
||||
'perfectionist/sort-array-includes': 'off',
|
||||
'perfectionist/sort-astro-attributes': 'off',
|
||||
'perfectionist/sort-classes': 'off',
|
||||
'perfectionist/sort-enums': 'off',
|
||||
'perfectionist/sort-exports': 'off',
|
||||
'perfectionist/sort-imports': 'off',
|
||||
'perfectionist/sort-interfaces': 'off',
|
||||
'perfectionist/sort-jsx-props': 'off',
|
||||
'perfectionist/sort-keys': 'off',
|
||||
'perfectionist/sort-maps': 'off',
|
||||
'perfectionist/sort-named-exports': 'off',
|
||||
'perfectionist/sort-named-imports': 'off',
|
||||
'perfectionist/sort-object-types': 'off',
|
||||
'perfectionist/sort-objects': 'off',
|
||||
'perfectionist/sort-svelte-attributes': 'off',
|
||||
'perfectionist/sort-union-types': 'off',
|
||||
'perfectionist/sort-vue-attributes': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true,
|
||||
EXPERIMENTAL_useProjectService: true,
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 'latest',
|
||||
},
|
||||
root: true,
|
||||
}
|
||||
38
.eslintrc.js
Normal file
38
.eslintrc.js
Normal file
@@ -0,0 +1,38 @@
|
||||
module.exports = {
|
||||
extends: ['@payloadcms'],
|
||||
overrides: [
|
||||
{
|
||||
extends: ['plugin:@typescript-eslint/disable-type-checked'],
|
||||
files: ['*.js', '*.cjs', '*.json', '*.md', '*.yml', '*.yaml'],
|
||||
},
|
||||
{
|
||||
files: ['packages/eslint-config-payload/**'],
|
||||
rules: {
|
||||
'perfectionist/sort-objects': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['package.json', 'tsconfig.json'],
|
||||
rules: {
|
||||
'perfectionist/sort-array-includes': 'off',
|
||||
'perfectionist/sort-astro-attributes': 'off',
|
||||
'perfectionist/sort-classes': 'off',
|
||||
'perfectionist/sort-enums': 'off',
|
||||
'perfectionist/sort-exports': 'off',
|
||||
'perfectionist/sort-imports': 'off',
|
||||
'perfectionist/sort-interfaces': 'off',
|
||||
'perfectionist/sort-jsx-props': 'off',
|
||||
'perfectionist/sort-keys': 'off',
|
||||
'perfectionist/sort-maps': 'off',
|
||||
'perfectionist/sort-named-exports': 'off',
|
||||
'perfectionist/sort-named-imports': 'off',
|
||||
'perfectionist/sort-object-types': 'off',
|
||||
'perfectionist/sort-objects': 'off',
|
||||
'perfectionist/sort-svelte-attributes': 'off',
|
||||
'perfectionist/sort-union-types': 'off',
|
||||
'perfectionist/sort-vue-attributes': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
root: true,
|
||||
}
|
||||
@@ -16,6 +16,3 @@ fb7d1be2f3325d076b7c967b1730afcef37922c2
|
||||
|
||||
# lint and format create-payload-app
|
||||
5fd3d430001efe86515262ded5e26f00c1451181
|
||||
|
||||
# 3.0 prettier & lint everywhere
|
||||
6789e61488a1d3de56f472ac3214faf344030005
|
||||
|
||||
16
.github/CODEOWNERS
vendored
16
.github/CODEOWNERS
vendored
@@ -1,21 +1,23 @@
|
||||
# Order matters. The last matching pattern takes precedence.
|
||||
|
||||
### Package Exports ###
|
||||
/**/exports/ @denolfe @jmikrut
|
||||
### Core ###
|
||||
|
||||
### Packages ###
|
||||
### Adapters ###
|
||||
/packages/richtext-*/ @AlessioGr
|
||||
|
||||
### Plugins ###
|
||||
/packages/plugin-cloud*/ @denolfe
|
||||
/packages/email-*/ @denolfe
|
||||
/packages/storage-*/ @denolfe
|
||||
/packages/create-payload-app/ @denolfe
|
||||
/packages/eslint-*/ @denolfe
|
||||
|
||||
### Templates ###
|
||||
/templates/ @jacobsfletch @denolfe
|
||||
|
||||
### Misc ###
|
||||
/packages/create-payload-app/ @denolfe
|
||||
/packages/eslint-*/ @denolfe
|
||||
|
||||
### Build Files ###
|
||||
/**/package.json @denolfe
|
||||
|
||||
/tsconfig.json @denolfe
|
||||
/**/tsconfig*.json @denolfe
|
||||
|
||||
|
||||
47
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
47
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
@@ -1,47 +0,0 @@
|
||||
name: Bug Report
|
||||
description: Create a bug report for Payload
|
||||
labels: ['[possible-bug]']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
*Note:* Feature requests should be opened as [discussions](https://github.com/payloadcms/payload/discussions/new?category=feature-requests-ideas).
|
||||
- type: input
|
||||
id: reproduction-link
|
||||
attributes:
|
||||
label: Link to reproduction
|
||||
description: Want us to look into your issue faster? Follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the Bug
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: To Reproduce
|
||||
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Payload Version
|
||||
description: What version of Payload are you running?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: adapters-plugins
|
||||
attributes:
|
||||
label: Adapters and Plugins
|
||||
description: What adapters and plugins are you using? ie. db-mongodb, db-postgres, bundler-webpack, etc.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Before submitting the issue, go through the steps you've written down to make sure the steps provided are detailed and clear.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Contributors should be able to follow the steps provided in order to reproduce the bug.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance!
|
||||
77
.github/ISSUE_TEMPLATE/1.bug_report_v3.yml
vendored
Normal file
77
.github/ISSUE_TEMPLATE/1.bug_report_v3.yml
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
name: Functionality Bug
|
||||
description: '[REPRODUCTION REQUIRED] - Create a bug report'
|
||||
labels: ['status: needs-triage', 'v3', 'validate-reproduction']
|
||||
body:
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the Bug
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: reproduction-link
|
||||
attributes:
|
||||
label: Link to the code that reproduces this issue
|
||||
description: >-
|
||||
_REQUIRED_: Please provide a link to your reproduction. Note, if the URL is invalid (404 or a private repository), we may close the issue.
|
||||
Either use `npx create-payload-app@beta -t blank` then push to a repo or follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Reproduction Steps
|
||||
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Which area(s) are affected? (Select all that apply)
|
||||
multiple: true
|
||||
options:
|
||||
- 'Not sure'
|
||||
- 'area: core'
|
||||
- 'area: docs'
|
||||
- 'area: templates'
|
||||
- 'area: ui'
|
||||
- 'db-mongodb'
|
||||
- 'db-postgres'
|
||||
- 'db-sqlite'
|
||||
- 'db-vercel-postgres'
|
||||
- 'plugin: cloud'
|
||||
- 'plugin: cloud-storage'
|
||||
- 'plugin: form-builder'
|
||||
- 'plugin: nested-docs'
|
||||
- 'plugin: richtext-lexical'
|
||||
- 'plugin: richtext-slate'
|
||||
- 'plugin: search'
|
||||
- 'plugin: sentry'
|
||||
- 'plugin: seo'
|
||||
- 'plugin: stripe'
|
||||
- 'plugin: other'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment Info
|
||||
description: Paste output from `pnpm payload info` (>= beta.92) _or_ Payload, Node.js, and Next.js versions.
|
||||
render: text
|
||||
placeholder: |
|
||||
Payload:
|
||||
Node.js:
|
||||
Next.js:
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Before submitting the issue, go through the steps you've written down to make sure the steps provided are detailed and clear.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Contributors should be able to follow the steps provided in order to reproduce the bug.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance!
|
||||
41
.github/ISSUE_TEMPLATE/2.design_issue.yml
vendored
Normal file
41
.github/ISSUE_TEMPLATE/2.design_issue.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: Design Issue
|
||||
description: '[SCREENSHOT REQUIRED] - Create a design issue report'
|
||||
labels: ['status: needs-triage', 'v3', 'area: ui']
|
||||
body:
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the Bug.
|
||||
description: >-
|
||||
_REQUIRED:_ Please a screenshot/video of the issue along with a detailed description of the problem.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Reproduction Steps
|
||||
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment Info
|
||||
description: Paste output from `pnpm payload info` (>= beta.92) _or_ Payload, Node.js, and Next.js versions.
|
||||
render: text
|
||||
placeholder: |
|
||||
Payload:
|
||||
Node.js:
|
||||
Next.js:
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Before submitting the issue, go through the steps you've written down to make sure the steps provided are detailed and clear.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Contributors should be able to follow the steps provided in order to reproduce the bug.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance!
|
||||
47
.github/ISSUE_TEMPLATE/3.bug_report_v2.yml
vendored
Normal file
47
.github/ISSUE_TEMPLATE/3.bug_report_v2.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
name: v2 Bug Report
|
||||
description: Report a bug for Payload v2. ONLY CRITICAL bugs will be fixed in v2.
|
||||
labels: ['status: needs-triage', 'v2']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
ONLY CRITICAL bugs will be fixed in v2.
|
||||
- type: input
|
||||
id: reproduction-link
|
||||
attributes:
|
||||
label: Link to reproduction
|
||||
description: Want us to look into your issue faster? Follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the Bug
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: To Reproduce
|
||||
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Payload Version
|
||||
description: What version of Payload are you running?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: adapters-plugins
|
||||
attributes:
|
||||
label: Adapters and Plugins
|
||||
description: What adapters and plugins are you using? ie. db-mongodb, db-postgres, bundler-webpack, etc.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Before submitting the issue, go through the steps you've written down to make sure the steps provided are detailed and clear.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Contributors should be able to follow the steps provided in order to reproduce the bug.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance!
|
||||
32
.github/PULL_REQUEST_TEMPLATE.md
vendored
32
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,23 +1,23 @@
|
||||
## Description
|
||||
<!--
|
||||
|
||||
<!-- Please include a summary of the pull request and any related issues it fixes. Please also include relevant motivation and context. -->
|
||||
Thank you for the PR! Please go through the checklist below and make sure you've completed all the steps.
|
||||
|
||||
- [ ] I have read and understand the [CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md) document in this repository.
|
||||
Please review the [CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md) document in this repository if you haven't already.
|
||||
|
||||
## Type of change
|
||||
The following items will ensure that your PR is handled as smoothly as possible:
|
||||
|
||||
<!-- Please delete options that are not relevant. -->
|
||||
- PR Title must follow conventional commits format. For example, `feat: my new feature`, `fix(plugin-seo): my fix`.
|
||||
- Minimal description explained as if explained to someone not immediately familiar with the code.
|
||||
- Provide before/after screenshots or code diffs if applicable.
|
||||
- Link any related issues/discussions from GitHub or Discord.
|
||||
- Add review comments if necessary to explain to the reviewer the logic behind a change
|
||||
|
||||
- [ ] Chore (non-breaking change which does not add functionality)
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] Change to the [templates](https://github.com/payloadcms/payload/tree/main/templates) directory (does not affect core functionality)
|
||||
- [ ] Change to the [examples](https://github.com/payloadcms/payload/tree/main/examples) directory (does not affect core functionality)
|
||||
- [ ] This change requires a documentation update
|
||||
### What?
|
||||
|
||||
## Checklist:
|
||||
### Why?
|
||||
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||
- [ ] Existing test suite passes locally with my changes
|
||||
- [ ] I have made corresponding changes to the documentation
|
||||
### How?
|
||||
|
||||
Fixes #
|
||||
|
||||
-->
|
||||
|
||||
13
.github/actions/release-commenter/.eslintrc.js
vendored
Normal file
13
.github/actions/release-commenter/.eslintrc.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
es6: true,
|
||||
node: true,
|
||||
},
|
||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/eslint-recommended'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint'],
|
||||
}
|
||||
8
.github/actions/release-commenter/.prettierrc.js
vendored
Normal file
8
.github/actions/release-commenter/.prettierrc.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
printWidth: 100,
|
||||
parser: 'typescript',
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
arrowParens: 'avoid',
|
||||
}
|
||||
74
.github/actions/release-commenter/README.md
vendored
Normal file
74
.github/actions/release-commenter/README.md
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
# Release Commenter
|
||||
|
||||
This GitHub Action automatically comments on and/or labels Issues and PRs when a fix is released for them.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 🔧 Heavily modified version of https://github.com/apexskier/github-release-commenter
|
||||
|
||||
## Fork Modifications
|
||||
|
||||
- Filters to closed PRs only
|
||||
- Adds tag filter to support non-linear releases
|
||||
- Better logging
|
||||
- Moved to pnpm
|
||||
- Uses @vercel/ncc for packaging
|
||||
- Comments on locked issues by unlocking then re-locking
|
||||
|
||||
## How it works
|
||||
|
||||
Use this action in a workflow [triggered by a release](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#release). It will scan commits between that and the prior release, find associated Issues and PRs, and comment on them to let people know a release has been made. Associated Issues and PRs can be directly [linked](https://docs.github.com/en/free-pro-team@latest/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue) to the commit or manually linked from a PR associated with the commit.
|
||||
|
||||
## Inputs
|
||||
|
||||
**GITHUB_TOKEN**
|
||||
|
||||
A GitHub personal access token with repo scope, such as [`secrets.GITHUB_TOKEN`](https://docs.github.com/en/free-pro-team@latest/actions/reference/authentication-in-a-workflow#about-the-github_token-secret).
|
||||
|
||||
**comment-template** (optional)
|
||||
|
||||
Override the comment posted on Issues and PRs. Set to the empty string to disable commenting. Several variables strings will be automatically replaced:
|
||||
|
||||
- `{release_link}` - a markdown link to the release
|
||||
- `{release_name}` - the release's name
|
||||
- `{release_tag}` - the release's tag
|
||||
|
||||
**label-template** (optional)
|
||||
|
||||
Add the given label. Multiple labels can be separated by commas. Several variable strings will be automatically replaced:
|
||||
|
||||
- `{release_name}` - the release's name
|
||||
- `{release_tag}` - the release's tag
|
||||
|
||||
**skip-label** (optional)
|
||||
|
||||
Skip processing if any of the given labels are present. Same processing rules as **label-template**. Default is "dependencies".
|
||||
|
||||
## Example
|
||||
|
||||
```yml
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
release:
|
||||
steps:
|
||||
- uses: apexskier/github-release-commenter@v1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
comment-template: |
|
||||
Release {release_link} addresses this.
|
||||
```
|
||||
|
||||
## Known limitations
|
||||
|
||||
These are some known limitations of this action. I'd like to try to address them in the future.
|
||||
|
||||
- Non-linear releases aren't supported. For example, releasing a patch to a prior major release after a new major release has been bumped.
|
||||
- Non-sequential releases aren't supported. For example, if you release multiple prereleases between two official releases, this will only create a comment for the first prerelease in which a fix is released, not the final release.
|
||||
- The first release for a project will be ignored. This is intentional, as the use case is unlikely. Most projects will either have several alphas that don't need release comments, or won't use issues/PRs for the first commit.
|
||||
- If a large number of things are commented on, you may see the error `Error: You have triggered an abuse detection mechanism. Please wait a few minutes before you try again.`. Consider using the `skip-label` input to reduce your load on the GitHub API.
|
||||
|
||||
## Versions
|
||||
|
||||
Workflows will automatically update the tags `v1` and `latest`, allowing you to reference one of those instead of locking to a specific release.
|
||||
32
.github/actions/release-commenter/action.yml
vendored
Normal file
32
.github/actions/release-commenter/action.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: Release Commenter
|
||||
description: Comment on PRs and Issues when a fix is released
|
||||
branding:
|
||||
icon: message-square
|
||||
color: blue
|
||||
inputs:
|
||||
GITHUB_TOKEN:
|
||||
description: |
|
||||
A GitHub personal access token with repo scope, such as
|
||||
secrets.GITHUB_TOKEN.
|
||||
required: true
|
||||
comment-template:
|
||||
description: |
|
||||
Text template for the comment string.
|
||||
required: false
|
||||
default: |
|
||||
Included in release {release_link}
|
||||
label-template:
|
||||
description: Add the given label. Multiple labels can be separated by commas.
|
||||
required: false
|
||||
skip-label:
|
||||
description: Skip commenting if any of the given label are present. Multiple labels can be separated by commas.
|
||||
required: false
|
||||
default: "dependencies"
|
||||
tag-filter:
|
||||
description: |
|
||||
Filter tags by a regular expression. Must be escaped. e.g. 'v\\d' to isolate tags between major versions.
|
||||
required: false
|
||||
default: null
|
||||
runs:
|
||||
using: node20
|
||||
main: dist/index.js
|
||||
34199
.github/actions/release-commenter/dist/index.js
vendored
Normal file
34199
.github/actions/release-commenter/dist/index.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
.github/actions/release-commenter/jest.config.js
vendored
Normal file
7
.github/actions/release-commenter/jest.config.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
testEnvironment: 'node',
|
||||
testPathIgnorePatterns: ['/node_modules/', '<rootDir>/dist/'],
|
||||
transform: {
|
||||
'^.+\\.(t|j)sx?$': ['@swc/jest'],
|
||||
},
|
||||
}
|
||||
34
.github/actions/release-commenter/package.json
vendored
Normal file
34
.github/actions/release-commenter/package.json
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "release-commenter",
|
||||
"version": "0.0.0",
|
||||
"description": "GitHub Action to automatically comment on PRs and Issues when a fix is released.",
|
||||
"main": "dist/index.js",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"build": "pnpm build:typecheck && pnpm build:ncc",
|
||||
"build:ncc": "ncc build src/index.ts -t -o dist",
|
||||
"build:typecheck": "tsc",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.3.0",
|
||||
"@actions/github": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@octokit/webhooks-types": "^7.5.1",
|
||||
"@swc/jest": "^0.2.36",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^20.16.5",
|
||||
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
||||
"@typescript-eslint/parser": "^4.33.0",
|
||||
"@vercel/ncc": "0.38.1",
|
||||
"concurrently": "^8.2.2",
|
||||
"eslint": "^7.32.0",
|
||||
"jest": "^29.7.0",
|
||||
"prettier": "^3.3.3",
|
||||
"ts-jest": "^26.5.6",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
}
|
||||
5419
.github/actions/release-commenter/pnpm-lock.yaml
generated
vendored
Normal file
5419
.github/actions/release-commenter/pnpm-lock.yaml
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
266
.github/actions/release-commenter/src/__snapshots__/index.test.ts.snap
vendored
Normal file
266
.github/actions/release-commenter/src/__snapshots__/index.test.ts.snap
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`tests feature tests can apply labels 1`] = `
|
||||
[
|
||||
[
|
||||
{
|
||||
"issue_number": 123,
|
||||
"labels": [
|
||||
":dart: landed",
|
||||
"release-current_tag_name",
|
||||
"Release Name",
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
"issue_number": 7,
|
||||
"labels": [
|
||||
":dart: landed",
|
||||
"release-current_tag_name",
|
||||
"Release Name",
|
||||
],
|
||||
},
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`tests main test 1`] = `
|
||||
{
|
||||
"graphql": [MockFunction] {
|
||||
"calls": [
|
||||
[
|
||||
"
|
||||
{
|
||||
resource(url: "http://repository/commit/SHA1") {
|
||||
... on Commit {
|
||||
messageHeadlineHTML
|
||||
messageBodyHTML
|
||||
associatedPullRequests(first: 10) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
bodyHTML
|
||||
number
|
||||
state
|
||||
labels(first: 10) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
nodes {
|
||||
name
|
||||
}
|
||||
}
|
||||
timelineItems(itemTypes: [CONNECTED_EVENT, DISCONNECTED_EVENT], first: 100) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
nodes {
|
||||
... on ConnectedEvent {
|
||||
__typename
|
||||
isCrossRepository
|
||||
subject {
|
||||
... on Issue {
|
||||
number
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DisconnectedEvent {
|
||||
__typename
|
||||
isCrossRepository
|
||||
subject {
|
||||
... on Issue {
|
||||
number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
",
|
||||
],
|
||||
[
|
||||
"
|
||||
{
|
||||
resource(url: "http://repository/commit/SHA2") {
|
||||
... on Commit {
|
||||
messageHeadlineHTML
|
||||
messageBodyHTML
|
||||
associatedPullRequests(first: 10) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
bodyHTML
|
||||
number
|
||||
state
|
||||
labels(first: 10) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
nodes {
|
||||
name
|
||||
}
|
||||
}
|
||||
timelineItems(itemTypes: [CONNECTED_EVENT, DISCONNECTED_EVENT], first: 100) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
nodes {
|
||||
... on ConnectedEvent {
|
||||
__typename
|
||||
isCrossRepository
|
||||
subject {
|
||||
... on Issue {
|
||||
number
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DisconnectedEvent {
|
||||
__typename
|
||||
isCrossRepository
|
||||
subject {
|
||||
... on Issue {
|
||||
number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
",
|
||||
],
|
||||
],
|
||||
"results": [
|
||||
{
|
||||
"type": "return",
|
||||
"value": Promise {},
|
||||
},
|
||||
{
|
||||
"type": "return",
|
||||
"value": Promise {},
|
||||
},
|
||||
],
|
||||
},
|
||||
"rest": {
|
||||
"issues": {
|
||||
"addLabels": [MockFunction],
|
||||
"createComment": [MockFunction] {
|
||||
"calls": [
|
||||
[
|
||||
{
|
||||
"body": "Included in release [current_tag_name](http://current_release). Replacements: current_tag_name, current_tag_name.",
|
||||
"issue_number": 3,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
"body": "Included in release [current_tag_name](http://current_release). Replacements: current_tag_name, current_tag_name.",
|
||||
"issue_number": 123,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
"body": "Included in release [current_tag_name](http://current_release). Replacements: current_tag_name, current_tag_name.",
|
||||
"issue_number": 7,
|
||||
},
|
||||
],
|
||||
],
|
||||
"results": [
|
||||
{
|
||||
"type": "return",
|
||||
"value": Promise {},
|
||||
},
|
||||
{
|
||||
"type": "return",
|
||||
"value": Promise {},
|
||||
},
|
||||
{
|
||||
"type": "return",
|
||||
"value": Promise {},
|
||||
},
|
||||
],
|
||||
},
|
||||
"get": [MockFunction] {
|
||||
"calls": [
|
||||
[
|
||||
{
|
||||
"issue_number": 3,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
"issue_number": 123,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
"issue_number": 7,
|
||||
},
|
||||
],
|
||||
],
|
||||
"results": [
|
||||
{
|
||||
"type": "return",
|
||||
"value": Promise {},
|
||||
},
|
||||
{
|
||||
"type": "return",
|
||||
"value": Promise {},
|
||||
},
|
||||
{
|
||||
"type": "return",
|
||||
"value": Promise {},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
"repos": {
|
||||
"compareCommits": [MockFunction] {
|
||||
"calls": [
|
||||
[
|
||||
{
|
||||
"base": "prior_tag_name",
|
||||
"head": "current_tag_name",
|
||||
},
|
||||
],
|
||||
],
|
||||
"results": [
|
||||
{
|
||||
"type": "return",
|
||||
"value": Promise {},
|
||||
},
|
||||
],
|
||||
},
|
||||
"listReleases": [MockFunction] {
|
||||
"calls": [
|
||||
[
|
||||
{
|
||||
"per_page": 100,
|
||||
},
|
||||
],
|
||||
],
|
||||
"results": [
|
||||
{
|
||||
"type": "return",
|
||||
"value": Promise {},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
399
.github/actions/release-commenter/src/index.test.ts
vendored
Normal file
399
.github/actions/release-commenter/src/index.test.ts
vendored
Normal file
@@ -0,0 +1,399 @@
|
||||
import type * as githubModule from '@actions/github'
|
||||
import type * as coreModule from '@actions/core'
|
||||
import { mock } from 'node:test'
|
||||
|
||||
jest.mock('@actions/core')
|
||||
jest.mock('@actions/github')
|
||||
|
||||
type Mocked<T> = {
|
||||
-readonly [P in keyof T]: T[P] extends Function ? jest.Mock<T[P]> : jest.Mocked<Partial<T[P]>>
|
||||
}
|
||||
|
||||
const github = require('@actions/github') as jest.Mocked<Mocked<typeof githubModule>>
|
||||
const core = require('@actions/core') as jest.Mocked<Mocked<typeof coreModule>>
|
||||
|
||||
describe('tests', () => {
|
||||
let mockOctokit: any = {}
|
||||
let currentTag: string = 'current_tag_name'
|
||||
|
||||
;(core.warning as any) = jest.fn(console.warn.bind(console))
|
||||
;(core.error as any) = jest.fn(console.error.bind(console))
|
||||
|
||||
let commentTempate: string = ''
|
||||
let labelTemplate: string | null = null
|
||||
const skipLabelTemplate: string | null = 'skip,test'
|
||||
let tagFilter: string | RegExp | null = null
|
||||
|
||||
let simpleMockOctokit: any = {}
|
||||
|
||||
beforeEach(() => {
|
||||
tagFilter = null
|
||||
currentTag = 'current_tag_name'
|
||||
;(github.context as any) = {
|
||||
payload: {
|
||||
repo: {
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
},
|
||||
release: {
|
||||
tag_name: currentTag,
|
||||
},
|
||||
repository: { html_url: 'http://repository' },
|
||||
},
|
||||
}
|
||||
|
||||
github.getOctokit.mockReset().mockImplementationOnce(((token: string) => {
|
||||
expect(token).toBe('GITHUB_TOKEN_VALUE')
|
||||
return mockOctokit
|
||||
}) as any)
|
||||
;(core.getInput as any).mockImplementation((key: string) => {
|
||||
if (key == 'GITHUB_TOKEN') {
|
||||
return 'GITHUB_TOKEN_VALUE'
|
||||
}
|
||||
if (key == 'comment-template') {
|
||||
return commentTempate
|
||||
}
|
||||
if (key == 'label-template') {
|
||||
return labelTemplate
|
||||
}
|
||||
if (key == 'skip-label') {
|
||||
return skipLabelTemplate
|
||||
}
|
||||
if (key == 'tag-filter') {
|
||||
return tagFilter
|
||||
}
|
||||
fail(`Unexpected input key ${key}`)
|
||||
})
|
||||
|
||||
commentTempate =
|
||||
'Included in release {release_link}. Replacements: {release_name}, {release_tag}.'
|
||||
labelTemplate = null
|
||||
simpleMockOctokit = {
|
||||
rest: {
|
||||
issues: {
|
||||
get: jest.fn(() => Promise.resolve({ data: { locked: false } })),
|
||||
createComment: jest.fn(() => Promise.resolve()),
|
||||
addLabels: jest.fn(() => Promise.resolve()),
|
||||
},
|
||||
repos: {
|
||||
listReleases: jest.fn(() =>
|
||||
Promise.resolve({
|
||||
data: [
|
||||
{
|
||||
name: 'Release Name',
|
||||
tag_name: 'current_tag_name',
|
||||
html_url: 'http://current_release',
|
||||
},
|
||||
{
|
||||
tag_name: 'prior_tag_name',
|
||||
html_url: 'http://prior_release',
|
||||
},
|
||||
],
|
||||
}),
|
||||
),
|
||||
compareCommits: jest.fn(() =>
|
||||
Promise.resolve({
|
||||
data: { commits: [{ sha: 'SHA1' }] },
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
graphql: jest.fn(() =>
|
||||
Promise.resolve({
|
||||
resource: {
|
||||
messageHeadlineHTML: '',
|
||||
messageBodyHTML:
|
||||
'<span class="issue-keyword tooltipped tooltipped-se" aria-label="This commit closes issue #123.">Closes</span> <p><span class="issue-keyword tooltipped tooltipped-se" aria-label="This pull request closes issue #7.">Closes</span>',
|
||||
associatedPullRequests: {
|
||||
pageInfo: { hasNextPage: false },
|
||||
edges: [],
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
expect(core.error).not.toHaveBeenCalled()
|
||||
expect(core.warning).not.toHaveBeenCalled()
|
||||
expect(core.setFailed).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('main test', async () => {
|
||||
mockOctokit = {
|
||||
...simpleMockOctokit,
|
||||
rest: {
|
||||
issues: {
|
||||
get: jest.fn(() => Promise.resolve({ data: { locked: false } })),
|
||||
createComment: jest.fn(() => Promise.resolve()),
|
||||
addLabels: jest.fn(() => Promise.resolve()),
|
||||
},
|
||||
repos: {
|
||||
listReleases: jest.fn(() =>
|
||||
Promise.resolve({
|
||||
data: [
|
||||
{
|
||||
tag_name: 'current_tag_name',
|
||||
html_url: 'http://current_release',
|
||||
},
|
||||
{
|
||||
tag_name: 'prior_tag_name',
|
||||
html_url: 'http://prior_release',
|
||||
},
|
||||
],
|
||||
}),
|
||||
),
|
||||
compareCommits: jest.fn(() =>
|
||||
Promise.resolve({
|
||||
data: { commits: [{ sha: 'SHA1' }, { sha: 'SHA2' }] },
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
graphql: jest.fn(() =>
|
||||
Promise.resolve({
|
||||
resource: {
|
||||
messageHeadlineHTML:
|
||||
'<span class="issue-keyword tooltipped tooltipped-se" aria-label="This commit closes issue #3.">Closes</span> <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="718013420" data-permission-text="Title is private" data-url="https://github.com/apexskier/github-release-commenter/issues/1" data-hovercard-type="issue" data-hovercard-url="/apexskier/github-release-commenter/issues/1/hovercard" href="https://github.com/apexskier/github-release-commenter/issues/1">#1</a>',
|
||||
messageBodyHTML:
|
||||
'<span class="issue-keyword tooltipped tooltipped-se" aria-label="This commit closes issue #123.">Closes</span> <p><span class="issue-keyword tooltipped tooltipped-se" aria-label="This pull request closes issue #7.">Closes</span>',
|
||||
associatedPullRequests: {
|
||||
pageInfo: { hasNextPage: false },
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
bodyHTML:
|
||||
'<span class="issue-keyword tooltipped tooltipped-se" aria-label="This commit closes issue #4.">Closes</span> <span class="issue-keyword tooltipped tooltipped-se" aria-label="This commit closes issue #5.">Closes</span>',
|
||||
number: 9,
|
||||
labels: {
|
||||
pageInfo: { hasNextPage: false },
|
||||
nodes: [{ name: 'label1' }, { name: 'label2' }],
|
||||
},
|
||||
timelineItems: {
|
||||
pageInfo: { hasNextPage: false },
|
||||
nodes: [
|
||||
{
|
||||
isCrossRepository: true,
|
||||
__typename: 'ConnectedEvent',
|
||||
subject: { number: 1 },
|
||||
},
|
||||
{
|
||||
isCrossRepository: false,
|
||||
__typename: 'ConnectedEvent',
|
||||
subject: { number: 2 },
|
||||
},
|
||||
{
|
||||
isCrossRepository: false,
|
||||
__typename: 'DisconnectedEvent',
|
||||
subject: { number: 2 },
|
||||
},
|
||||
{
|
||||
isCrossRepository: false,
|
||||
__typename: 'ConnectedEvent',
|
||||
subject: { number: 2 },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
bodyHTML: '',
|
||||
number: 42,
|
||||
labels: {
|
||||
pageInfo: { hasNextPage: false },
|
||||
nodes: [{ name: 'label1' }, { name: 'skip' }],
|
||||
},
|
||||
timelineItems: {
|
||||
pageInfo: { hasNextPage: false },
|
||||
nodes: [
|
||||
{
|
||||
isCrossRepository: true,
|
||||
__typename: 'ConnectedEvent',
|
||||
subject: { number: 82 },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
}
|
||||
|
||||
jest.isolateModules(() => {
|
||||
require('./index')
|
||||
})
|
||||
|
||||
await new Promise<void>(setImmediate)
|
||||
|
||||
expect(mockOctokit).toMatchSnapshot()
|
||||
expect(mockOctokit.rest.issues.createComment).toHaveBeenCalledTimes(3)
|
||||
})
|
||||
|
||||
describe('can filter tags', () => {
|
||||
const v3prev = 'v3.0.1'
|
||||
const v3current = 'v3.0.2'
|
||||
const v2prev = 'v2.0.1'
|
||||
const v2current = 'v2.0.2'
|
||||
|
||||
const listReleasesData = [
|
||||
{
|
||||
name: 'Current Release Name',
|
||||
tag_name: v3current,
|
||||
html_url: 'http://v3.0.2',
|
||||
},
|
||||
{
|
||||
name: 'Prev Release Name',
|
||||
tag_name: v3prev,
|
||||
html_url: 'http://v3.0.1',
|
||||
},
|
||||
{
|
||||
name: 'v2 Current Release Name',
|
||||
tag_name: v2current,
|
||||
html_url: 'http://v2.0.2',
|
||||
},
|
||||
{
|
||||
name: 'v2 Prev Release Name',
|
||||
tag_name: v2prev,
|
||||
html_url: 'http://v2.0.1',
|
||||
},
|
||||
]
|
||||
|
||||
it.each`
|
||||
description | prevTag | currentTag | filter
|
||||
${'no filter'} | ${v3prev} | ${v3current} | ${null}
|
||||
${'v3'} | ${v3prev} | ${v3current} | ${'v\\d'}
|
||||
${'v2'} | ${v2prev} | ${v2current} | ${'v\\d'}
|
||||
`('should filter tags with $description', async ({ prevTag, currentTag, filter }) => {
|
||||
// @ts-ignore
|
||||
github.context.payload.release.tag_name = currentTag
|
||||
|
||||
tagFilter = filter
|
||||
|
||||
mockOctokit = {
|
||||
...simpleMockOctokit,
|
||||
rest: {
|
||||
issues: {
|
||||
get: jest.fn(() => Promise.resolve({ data: { locked: false } })),
|
||||
createComment: jest.fn(() => Promise.resolve()),
|
||||
addLabels: jest.fn(() => Promise.resolve()),
|
||||
},
|
||||
repos: {
|
||||
listReleases: jest.fn(() =>
|
||||
Promise.resolve({
|
||||
data: listReleasesData,
|
||||
}),
|
||||
),
|
||||
compareCommits: jest.fn(() =>
|
||||
Promise.resolve({
|
||||
data: { commits: [{ sha: 'SHA1' }] },
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
graphql: jest.fn(() =>
|
||||
Promise.resolve({
|
||||
resource: {
|
||||
messageHeadlineHTML: '',
|
||||
messageBodyHTML:
|
||||
'<span class="issue-keyword tooltipped tooltipped-se" aria-label="This commit closes issue #123.">Closes</span> <p><span class="issue-keyword tooltipped tooltipped-se" aria-label="This pull request closes issue #7.">Closes</span>',
|
||||
associatedPullRequests: {
|
||||
pageInfo: { hasNextPage: false },
|
||||
edges: [],
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
}
|
||||
|
||||
jest.isolateModules(() => {
|
||||
require('./index')
|
||||
})
|
||||
|
||||
await new Promise<void>(resolve => setImmediate(() => resolve()))
|
||||
|
||||
expect(github.getOctokit).toHaveBeenCalled()
|
||||
expect(mockOctokit.rest.repos.compareCommits.mock.calls).toEqual([
|
||||
[{ base: prevTag, head: currentTag }],
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('feature tests', () => {
|
||||
beforeEach(() => {
|
||||
mockOctokit = simpleMockOctokit
|
||||
})
|
||||
|
||||
it('can disable comments', async () => {
|
||||
commentTempate = ''
|
||||
|
||||
jest.isolateModules(() => {
|
||||
require('./index')
|
||||
})
|
||||
|
||||
await new Promise<void>(resolve => setImmediate(() => resolve()))
|
||||
|
||||
expect(github.getOctokit).toHaveBeenCalled()
|
||||
expect(mockOctokit.rest.issues.createComment).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should unlock and comment', async () => {
|
||||
mockOctokit = {
|
||||
...simpleMockOctokit,
|
||||
rest: {
|
||||
...simpleMockOctokit.rest,
|
||||
issues: {
|
||||
// Return locked for both issues to be commented on
|
||||
get: jest.fn(() => Promise.resolve({ data: { locked: true } })),
|
||||
lock: jest.fn(() => Promise.resolve()),
|
||||
unlock: jest.fn(() => Promise.resolve()),
|
||||
createComment: jest.fn(() => Promise.resolve()),
|
||||
},
|
||||
},
|
||||
graphql: jest.fn(() =>
|
||||
Promise.resolve({
|
||||
resource: {
|
||||
messageHeadlineHTML: '',
|
||||
messageBodyHTML:
|
||||
'<span class="issue-keyword tooltipped tooltipped-se" aria-label="This commit closes issue #123.">Closes</span> <p><span class="issue-keyword tooltipped tooltipped-se" aria-label="This pull request closes issue #7.">Closes</span>',
|
||||
associatedPullRequests: {
|
||||
pageInfo: { hasNextPage: false },
|
||||
edges: [],
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
}
|
||||
|
||||
jest.isolateModules(() => {
|
||||
require('./index')
|
||||
})
|
||||
|
||||
await new Promise<void>(resolve => setImmediate(() => resolve()))
|
||||
|
||||
expect(github.getOctokit).toHaveBeenCalled()
|
||||
|
||||
// Should call once for both linked issues
|
||||
expect(mockOctokit.rest.issues.unlock).toHaveBeenCalledTimes(2)
|
||||
expect(mockOctokit.rest.issues.createComment).toHaveBeenCalledTimes(2)
|
||||
expect(mockOctokit.rest.issues.lock).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it.skip('can apply labels', async () => {
|
||||
labelTemplate = ':dart: landed,release-{release_tag},{release_name}'
|
||||
|
||||
jest.isolateModules(() => {
|
||||
require('./index')
|
||||
})
|
||||
|
||||
await new Promise<void>(resolve => setImmediate(() => resolve()))
|
||||
|
||||
expect(github.getOctokit).toHaveBeenCalled()
|
||||
expect(mockOctokit.rest.issues.addLabels.mock.calls).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
})
|
||||
349
.github/actions/release-commenter/src/index.ts
vendored
Normal file
349
.github/actions/release-commenter/src/index.ts
vendored
Normal file
@@ -0,0 +1,349 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as github from '@actions/github'
|
||||
import type * as Webhooks from '@octokit/webhooks-types'
|
||||
|
||||
const closesMatcher = /aria-label="This (?:commit|pull request) closes issue #(\d+)\."/g
|
||||
|
||||
const releaseLinkTemplateRegex = /{release_link}/g
|
||||
const releaseNameTemplateRegex = /{release_name}/g
|
||||
const releaseTagTemplateRegex = /{release_tag}/g
|
||||
|
||||
;(async function main() {
|
||||
try {
|
||||
const payload = github.context.payload as Webhooks.EventPayloadMap['release']
|
||||
|
||||
const githubToken = core.getInput('GITHUB_TOKEN')
|
||||
const tagFilter = core.getInput('tag-filter') || undefined // Accept tag filter as an input
|
||||
const octokit = github.getOctokit(githubToken)
|
||||
|
||||
const commentTemplate = core.getInput('comment-template')
|
||||
const labelTemplate = core.getInput('label-template') || null
|
||||
const skipLabelTemplate = core.getInput('skip-label') || null
|
||||
|
||||
// Fetch the releases with the optional tag filter applied
|
||||
const { data: rawReleases } = await octokit.rest.repos.listReleases({
|
||||
...github.context.repo,
|
||||
per_page: 100,
|
||||
})
|
||||
|
||||
// Get the current release tag or latest tag
|
||||
const currentTag = payload?.release?.tag_name || rawReleases?.[0]?.tag_name
|
||||
|
||||
let releases = rawReleases
|
||||
|
||||
// Filter releases by the tag filter if provided
|
||||
if (tagFilter) {
|
||||
core.info(`Filtering releases by tag filter: ${tagFilter}`)
|
||||
// Get the matching part of the current release tag
|
||||
const regexMatch = currentTag.match(tagFilter)?.[0]
|
||||
if (!regexMatch) {
|
||||
core.error(`Current release tag ${currentTag} does not match the tag filter ${tagFilter}`)
|
||||
return
|
||||
}
|
||||
|
||||
core.info(`Matched string from filter: ${regexMatch}`)
|
||||
|
||||
releases = releases
|
||||
.filter(release => {
|
||||
const match = release.tag_name.match(regexMatch)?.[0]
|
||||
return match
|
||||
})
|
||||
.slice(0, 2)
|
||||
}
|
||||
|
||||
core.info(`Releases: ${JSON.stringify(releases, null, 2)}`)
|
||||
|
||||
if (releases.length < 2) {
|
||||
if (!releases.length) {
|
||||
core.error(`No releases found with the provided tag filter: '${tagFilter}'`)
|
||||
return
|
||||
}
|
||||
|
||||
core.info('first release')
|
||||
return
|
||||
}
|
||||
|
||||
const [currentRelease, priorRelease] = releases
|
||||
|
||||
core.info(`${priorRelease.tag_name}...${currentRelease.tag_name}`)
|
||||
|
||||
const {
|
||||
data: { commits },
|
||||
} = await octokit.rest.repos.compareCommits({
|
||||
...github.context.repo,
|
||||
base: priorRelease.tag_name,
|
||||
head: currentRelease.tag_name,
|
||||
})
|
||||
|
||||
if (!currentRelease.name) {
|
||||
core.info('Current release has no name, will fall back to the tag name.')
|
||||
}
|
||||
const releaseLabel = currentRelease.name || currentRelease.tag_name
|
||||
|
||||
const comment = commentTemplate
|
||||
.trim()
|
||||
.split(releaseLinkTemplateRegex)
|
||||
.join(`[${releaseLabel}](${currentRelease.html_url})`)
|
||||
.split(releaseNameTemplateRegex)
|
||||
.join(releaseLabel)
|
||||
.split(releaseTagTemplateRegex)
|
||||
.join(currentRelease.tag_name)
|
||||
|
||||
const parseLabels = (rawInput: string | null) =>
|
||||
rawInput
|
||||
?.split(releaseNameTemplateRegex)
|
||||
.join(releaseLabel)
|
||||
?.split(releaseTagTemplateRegex)
|
||||
.join(currentRelease.tag_name)
|
||||
?.split(',')
|
||||
?.map(l => l.trim())
|
||||
.filter(l => l)
|
||||
|
||||
const labels = parseLabels(labelTemplate)
|
||||
const skipLabels = parseLabels(skipLabelTemplate)
|
||||
|
||||
const linkedIssuesPrs = new Set<number>()
|
||||
|
||||
await Promise.all(
|
||||
commits.map(commit =>
|
||||
(async () => {
|
||||
const query = `
|
||||
{
|
||||
resource(url: "${payload.repository.html_url}/commit/${commit.sha}") {
|
||||
... on Commit {
|
||||
messageHeadlineHTML
|
||||
messageBodyHTML
|
||||
associatedPullRequests(first: 10) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
bodyHTML
|
||||
number
|
||||
state
|
||||
labels(first: 10) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
nodes {
|
||||
name
|
||||
}
|
||||
}
|
||||
timelineItems(itemTypes: [CONNECTED_EVENT, DISCONNECTED_EVENT], first: 100) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
}
|
||||
nodes {
|
||||
... on ConnectedEvent {
|
||||
__typename
|
||||
isCrossRepository
|
||||
subject {
|
||||
... on Issue {
|
||||
number
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DisconnectedEvent {
|
||||
__typename
|
||||
isCrossRepository
|
||||
subject {
|
||||
... on Issue {
|
||||
number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const response: {
|
||||
resource: null | {
|
||||
messageHeadlineHTML: string
|
||||
messageBodyHTML: string
|
||||
associatedPullRequests: {
|
||||
pageInfo: { hasNextPage: boolean }
|
||||
edges: ReadonlyArray<{
|
||||
node: {
|
||||
bodyHTML: string
|
||||
number: number
|
||||
state: 'OPEN' | 'CLOSED' | 'MERGED'
|
||||
labels: {
|
||||
pageInfo: { hasNextPage: boolean }
|
||||
nodes: ReadonlyArray<{
|
||||
name: string
|
||||
}>
|
||||
}
|
||||
timelineItems: {
|
||||
pageInfo: { hasNextPage: boolean }
|
||||
nodes: ReadonlyArray<{
|
||||
__typename: 'ConnectedEvent' | 'DisconnectedEvent'
|
||||
isCrossRepository: boolean
|
||||
subject: {
|
||||
number: number
|
||||
}
|
||||
}>
|
||||
}
|
||||
}
|
||||
}>
|
||||
}
|
||||
}
|
||||
} = await octokit.graphql(query)
|
||||
|
||||
if (!response.resource) {
|
||||
return
|
||||
}
|
||||
|
||||
// core.info(JSON.stringify(response.resource, null, 2))
|
||||
|
||||
core.info(`Checking commit: ${payload.repository.html_url}/commit/${commit.sha}`)
|
||||
|
||||
const associatedClosedPREdges = response.resource.associatedPullRequests.edges.filter(
|
||||
e => e.node.state === 'MERGED',
|
||||
)
|
||||
|
||||
if (associatedClosedPREdges.length) {
|
||||
core.info(
|
||||
` Associated Merged PRs:\n ${associatedClosedPREdges.map(pr => `${payload.repository.html_url}/pull/${pr.node.number}`).join('\n ')}`,
|
||||
)
|
||||
} else {
|
||||
core.info(' No associated merged PRs')
|
||||
}
|
||||
|
||||
const html = [
|
||||
response.resource.messageHeadlineHTML,
|
||||
response.resource.messageBodyHTML,
|
||||
...associatedClosedPREdges.map(pr => pr.node.bodyHTML),
|
||||
].join(' ')
|
||||
|
||||
for (const match of html.matchAll(closesMatcher)) {
|
||||
const [, num] = match
|
||||
linkedIssuesPrs.add(parseInt(num, 10))
|
||||
core.info(
|
||||
` Linked issue/PR from closesMatcher: ${payload.repository.html_url}/pull/${num}`,
|
||||
)
|
||||
}
|
||||
|
||||
if (response.resource.associatedPullRequests.pageInfo.hasNextPage) {
|
||||
core.warning(`Too many PRs associated with ${commit.sha}`)
|
||||
}
|
||||
|
||||
const seen = new Set<number>()
|
||||
for (const associatedPR of associatedClosedPREdges) {
|
||||
if (associatedPR.node.timelineItems.pageInfo.hasNextPage) {
|
||||
core.warning(`Too many links for #${associatedPR.node.number}`)
|
||||
}
|
||||
if (associatedPR.node.labels.pageInfo.hasNextPage) {
|
||||
core.warning(`Too many labels for #${associatedPR.node.number}`)
|
||||
}
|
||||
// a skip labels is present on this PR
|
||||
if (
|
||||
skipLabels?.some(l => associatedPR.node.labels.nodes.some(({ name }) => name === l))
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
linkedIssuesPrs.add(associatedPR.node.number)
|
||||
core.info(
|
||||
` Linked issue/PR from associated PR: ${payload.repository.html_url}/pull/${associatedPR.node.number}`,
|
||||
)
|
||||
|
||||
// These are sorted by creation date in ascending order. The latest event for a given issue/PR is all we need
|
||||
// ignore links that aren't part of this repo
|
||||
const links = associatedPR.node.timelineItems.nodes
|
||||
.filter(node => !node.isCrossRepository)
|
||||
.reverse()
|
||||
for (const link of links) {
|
||||
if (seen.has(link.subject.number)) {
|
||||
continue
|
||||
}
|
||||
if (link.__typename == 'ConnectedEvent') {
|
||||
linkedIssuesPrs.add(link.subject.number)
|
||||
core.info(
|
||||
`Linked issue/PR from connected event: ${payload.repository.html_url}/pull/${link.subject.number}`,
|
||||
)
|
||||
}
|
||||
seen.add(link.subject.number)
|
||||
}
|
||||
}
|
||||
})(),
|
||||
),
|
||||
)
|
||||
|
||||
core.info(
|
||||
`Final issues/PRs to be commented on: \n${Array.from(linkedIssuesPrs)
|
||||
.map(num => ` ${payload.repository.html_url}/pull/${num}`)
|
||||
.join('\n')}`,
|
||||
)
|
||||
|
||||
const requests: Array<Promise<unknown>> = []
|
||||
for (const issueNumber of linkedIssuesPrs) {
|
||||
const baseRequest = {
|
||||
...github.context.repo,
|
||||
issue_number: issueNumber,
|
||||
}
|
||||
if (comment) {
|
||||
const commentRequest = {
|
||||
...baseRequest,
|
||||
body: comment,
|
||||
}
|
||||
|
||||
// Check if issue is locked or not
|
||||
const { data: issue } = await octokit.rest.issues.get(baseRequest)
|
||||
|
||||
let createCommentPromise: () => Promise<void>
|
||||
if (!issue.locked) {
|
||||
createCommentPromise = async () => {
|
||||
try {
|
||||
await octokit.rest.issues.createComment(commentRequest)
|
||||
} catch (error) {
|
||||
core.error(error as Error)
|
||||
core.error(
|
||||
`Failed to comment on issue/PR: ${issueNumber}. ${payload.repository.html_url}/pull/${issueNumber}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
core.info(
|
||||
`Issue/PR is locked: ${issueNumber}. Unlocking, commenting, and re-locking. ${payload.repository.html_url}/pull/${issueNumber}`,
|
||||
)
|
||||
createCommentPromise = async () => {
|
||||
try {
|
||||
core.debug(`Unlocking issue/PR: ${issueNumber}`)
|
||||
await octokit.rest.issues.unlock(baseRequest)
|
||||
core.debug(`Commenting on issue/PR: ${issueNumber}`)
|
||||
await octokit.rest.issues.createComment(commentRequest)
|
||||
core.debug(`Re-locking issue/PR: ${issueNumber}`)
|
||||
await octokit.rest.issues.lock(baseRequest)
|
||||
} catch (error) {
|
||||
core.error(error as Error)
|
||||
core.error(
|
||||
`Failed to unlock, comment, and re-lock issue/PR: ${issueNumber}. ${payload.repository.html_url}/pull/${issueNumber}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requests.push(createCommentPromise())
|
||||
}
|
||||
if (labels) {
|
||||
const request = {
|
||||
...baseRequest,
|
||||
labels,
|
||||
}
|
||||
// core.info(JSON.stringify(request, null, 2))
|
||||
requests.push(octokit.rest.issues.addLabels(request))
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(requests)
|
||||
} catch (error) {
|
||||
core.error(error as Error)
|
||||
core.setFailed((error as Error).message)
|
||||
}
|
||||
})()
|
||||
15
.github/actions/release-commenter/tsconfig.json
vendored
Normal file
15
.github/actions/release-commenter/tsconfig.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["es2020.string"],
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"downlevelIteration": true,
|
||||
"skipLibCheck": true,
|
||||
},
|
||||
"exclude": ["src/**/*.test.ts"]
|
||||
}
|
||||
48
.github/actions/setup/action.yml
vendored
48
.github/actions/setup/action.yml
vendored
@@ -1,48 +0,0 @@
|
||||
name: Setup node and pnpm
|
||||
description: Configure the Node.js and pnpm versions
|
||||
|
||||
inputs:
|
||||
node-version:
|
||||
description: 'The Node.js version to use'
|
||||
required: true
|
||||
default: 18.20.2
|
||||
pnpm-version:
|
||||
description: 'The pnpm version to use'
|
||||
required: true
|
||||
default: 8.15.7
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
shell: bash
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ inputs.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: ${{ inputs.pnpm-version }}
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
pnpm-store-
|
||||
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- shell: bash
|
||||
run: pnpm install
|
||||
13
.github/actions/triage/.eslintrc.js
vendored
Normal file
13
.github/actions/triage/.eslintrc.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
es6: true,
|
||||
node: true,
|
||||
},
|
||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/eslint-recommended'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint'],
|
||||
}
|
||||
8
.github/actions/triage/.prettierrc.js
vendored
Normal file
8
.github/actions/triage/.prettierrc.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
printWidth: 100,
|
||||
parser: 'typescript',
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
arrowParens: 'avoid',
|
||||
}
|
||||
22
.github/actions/triage/LICENSE
vendored
Normal file
22
.github/actions/triage/LICENSE
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Payload <info@payloadcms.com>. All modification and additions are copyright of Payload.
|
||||
|
||||
---
|
||||
|
||||
Original license:
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2023, Balázs Orbán
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
21
.github/actions/triage/README.md
vendored
Normal file
21
.github/actions/triage/README.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Triage
|
||||
|
||||
Modified version of https://github.com/balazsorban44/nissuer
|
||||
|
||||
## Modifications
|
||||
|
||||
- Port to TypeScript
|
||||
- Remove issue locking
|
||||
- Remove reproduction blocklist
|
||||
- Uses `@vercel/ncc` for packaging
|
||||
|
||||
## Development
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Whenever a modification is made to the action, the action built to `dist` must be committed to the repository.
|
||||
|
||||
This is done by running:
|
||||
|
||||
```sh
|
||||
pnpm build
|
||||
```
|
||||
40
.github/actions/triage/action.yml
vendored
Normal file
40
.github/actions/triage/action.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: Triage
|
||||
description: Initial triage for issues
|
||||
|
||||
inputs:
|
||||
reproduction-comment:
|
||||
description: 'Either a string or a path to a .md file inside the repository. Example: ".github/invalid-reproduction.md"'
|
||||
default: '.github/invalid-reproduction.md'
|
||||
reproduction-hosts:
|
||||
description: 'Comma-separated list of hostnames that are allowed for reproductions. Example: "github.com,codesandbox.io"'
|
||||
default: github.com
|
||||
reproduction-invalid-label:
|
||||
description: 'Label to apply to issues without a valid reproduction. Example: "invalid-reproduction"'
|
||||
default: 'invalid-reproduction'
|
||||
reproduction-issue-labels:
|
||||
description: 'Comma-separated list of issue labels. If configured, only verify reproduction URLs of issues with one of these labels present. Adding a comma at the end will handle non-labeled issues as invalid. Example: "bug,", will consider issues with the label "bug" or no label.'
|
||||
default: ''
|
||||
reproduction-link-section:
|
||||
description: 'A regular expression string with "(.*)" matching a valid URL in the issue body. The result is trimmed. Example: "### Link to reproduction(.*)### To reproduce"'
|
||||
default: '### Link to reproduction(.*)### To reproduce'
|
||||
tag-only:
|
||||
description: Log and tag only. Do not perform closing or commenting actions.
|
||||
default: false
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: actions/checkout@v4
|
||||
- name: Run action
|
||||
run: node ${{ github.action_path }}/dist/index.js
|
||||
shell: sh
|
||||
# https://github.com/actions/runner/issues/665#issuecomment-676581170
|
||||
env:
|
||||
"INPUT_REPRODUCTION_COMMENT": ${{inputs.reproduction-comment}}
|
||||
"INPUT_REPRODUCTION_HOSTS": ${{inputs.reproduction-hosts}}
|
||||
"INPUT_REPRODUCTION_INVALID_LABEL": ${{inputs.reproduction-invalid-label}}
|
||||
"INPUT_REPRODUCTION_ISSUE_LABELS": ${{inputs.reproduction-issue-labels}}
|
||||
"INPUT_REPRODUCTION_LINK_SECTION": ${{inputs.reproduction-link-section}}
|
||||
"INPUT_TAG_ONLY": ${{inputs.tag-only}}
|
||||
34048
.github/actions/triage/dist/index.js
vendored
Normal file
34048
.github/actions/triage/dist/index.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
.github/actions/triage/jest.config.js
vendored
Normal file
7
.github/actions/triage/jest.config.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
testEnvironment: 'node',
|
||||
testPathIgnorePatterns: ['/node_modules/', '<rootDir>/dist/'],
|
||||
transform: {
|
||||
'^.+\\.(t|j)sx?$': ['@swc/jest'],
|
||||
},
|
||||
}
|
||||
34
.github/actions/triage/package.json
vendored
Normal file
34
.github/actions/triage/package.json
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "triage",
|
||||
"version": "0.0.0",
|
||||
"description": "GitHub Action to triage new issues",
|
||||
"main": "dist/index.js",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"build": "pnpm build:typecheck && pnpm build:ncc",
|
||||
"build:ncc": "ncc build src/index.ts -t -o dist",
|
||||
"build:typecheck": "tsc",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.3.0",
|
||||
"@actions/github": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@octokit/webhooks-types": "^7.5.1",
|
||||
"@swc/jest": "^0.2.36",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^20.16.5",
|
||||
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
||||
"@typescript-eslint/parser": "^4.33.0",
|
||||
"@vercel/ncc": "0.38.1",
|
||||
"concurrently": "^8.2.2",
|
||||
"eslint": "^7.32.0",
|
||||
"jest": "^29.7.0",
|
||||
"prettier": "^3.3.3",
|
||||
"ts-jest": "^26.5.6",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
}
|
||||
5419
.github/actions/triage/pnpm-lock.yaml
generated
vendored
Normal file
5419
.github/actions/triage/pnpm-lock.yaml
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
195
.github/actions/triage/src/index.ts
vendored
Normal file
195
.github/actions/triage/src/index.ts
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
import { debug, error, getBooleanInput, getInput, info, setFailed } from '@actions/core'
|
||||
|
||||
import { context, getOctokit } from '@actions/github'
|
||||
import { readFile, access } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
|
||||
// Ensure GITHUB_TOKEN and GITHUB_WORKSPACE are present
|
||||
if (!process.env.GITHUB_TOKEN) throw new TypeError('No GITHUB_TOKEN provided')
|
||||
if (!process.env.GITHUB_WORKSPACE) throw new TypeError('Not a GitHub workspace')
|
||||
|
||||
// Define the configuration object
|
||||
interface Config {
|
||||
invalidLink: {
|
||||
comment: string
|
||||
bugLabels: string[]
|
||||
hosts: string[]
|
||||
label: string
|
||||
linkSection: string
|
||||
}
|
||||
tagOnly: boolean
|
||||
token: string
|
||||
workspace: string
|
||||
}
|
||||
|
||||
const config: Config = {
|
||||
invalidLink: {
|
||||
comment: getInput('reproduction_comment') || '.github/invalid-reproduction.md',
|
||||
bugLabels: getInput('reproduction_issue_labels')
|
||||
.split(',')
|
||||
.map(l => l.trim()),
|
||||
hosts: (getInput('reproduction_hosts') || 'github.com').split(',').map(h => h.trim()),
|
||||
label: getInput('reproduction_invalid_label') || 'invalid-reproduction',
|
||||
linkSection:
|
||||
getInput('reproduction_link_section') || '### Link to reproduction(.*)### To reproduce',
|
||||
},
|
||||
tagOnly: getBooleanOrUndefined('tag_only') || false,
|
||||
token: process.env.GITHUB_TOKEN,
|
||||
workspace: process.env.GITHUB_WORKSPACE,
|
||||
}
|
||||
|
||||
// Attempt to parse JSON, return parsed object or error
|
||||
function tryParse(json: string): Record<string, unknown> {
|
||||
try {
|
||||
return JSON.parse(json)
|
||||
} catch (e) {
|
||||
setFailed(`Could not parse JSON: ${e instanceof Error ? e.message : e}`)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieves a boolean input or undefined based on environment variables
|
||||
function getBooleanOrUndefined(value: string): boolean | undefined {
|
||||
const variable = process.env[`INPUT_${value.toUpperCase()}`]
|
||||
return variable === undefined || variable === '' ? undefined : getBooleanInput(value)
|
||||
}
|
||||
|
||||
// Returns the appropriate label match type
|
||||
function getLabelMatch(value: string | undefined): 'name' | 'description' {
|
||||
return value === 'name' ? 'name' : 'description'
|
||||
}
|
||||
|
||||
// Function to check if an issue contains a valid reproduction link
|
||||
async function checkValidReproduction(): Promise<void> {
|
||||
const { issue, action } = context.payload as {
|
||||
issue: { number: number; body: string; labels: { name: string }[] } | undefined
|
||||
action: string
|
||||
}
|
||||
|
||||
if (action !== 'opened' || !issue?.body) return
|
||||
|
||||
const labels = issue.labels.map(l => l.name)
|
||||
|
||||
const issueMatchingLabel =
|
||||
labels.length &&
|
||||
config.invalidLink.bugLabels.length &&
|
||||
labels.some(l => config.invalidLink.bugLabels.includes(l))
|
||||
|
||||
if (!issueMatchingLabel) {
|
||||
info(
|
||||
`Issue #${issue.number} does not match required labels: ${config.invalidLink.bugLabels.join(', ')}`,
|
||||
)
|
||||
info(`Issue labels: ${labels.join(', ')}`)
|
||||
return
|
||||
}
|
||||
|
||||
info(`Issue #${issue.number} labels: ${labels.join(', ')}`)
|
||||
|
||||
const { rest: client } = getOctokit(config.token)
|
||||
const common = { ...context.repo, issue_number: issue.number }
|
||||
|
||||
const labelsToRemove = labels.filter(l => config.invalidLink.bugLabels.includes(l))
|
||||
|
||||
if (await isValidReproduction(issue.body)) {
|
||||
await Promise.all(
|
||||
labelsToRemove.map(label => client.issues.removeLabel({ ...common, name: label })),
|
||||
)
|
||||
|
||||
return info(`Issue #${issue.number} contains a valid reproduction 💚`)
|
||||
}
|
||||
|
||||
info(`Invalid reproduction, issue will be closed/labeled/commented...`)
|
||||
|
||||
// Adjust labels
|
||||
await Promise.all(
|
||||
labelsToRemove.map(label => client.issues.removeLabel({ ...common, name: label })),
|
||||
)
|
||||
info(`Issue #${issue.number} - validate label removed`)
|
||||
await client.issues.addLabels({ ...common, labels: [config.invalidLink.label] })
|
||||
info(`Issue #${issue.number} - labeled`)
|
||||
|
||||
// If tagOnly, do not close or comment
|
||||
if (config.tagOnly) {
|
||||
info('Tag-only enabled, no closing/commenting actions taken')
|
||||
return
|
||||
}
|
||||
|
||||
// Perform closing and commenting actions
|
||||
await client.issues.update({ ...common, state: 'closed' })
|
||||
info(`Issue #${issue.number} - closed`)
|
||||
|
||||
const comment = join(config.workspace, config.invalidLink.comment)
|
||||
await client.issues.createComment({ ...common, body: await getCommentBody(comment) })
|
||||
info(`Issue #${issue.number} - commented`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an issue contains a valid/accessible link to a reproduction.
|
||||
*
|
||||
* Returns `true` if the link is valid.
|
||||
* @param body - The body content of the issue
|
||||
*/
|
||||
async function isValidReproduction(body: string): Promise<boolean> {
|
||||
const linkSectionRe = new RegExp(config.invalidLink.linkSection, 'is')
|
||||
const link = body.match(linkSectionRe)?.[1]?.trim()
|
||||
|
||||
if (!link) {
|
||||
info('Missing link')
|
||||
info(`Link section regex: ${linkSectionRe}`)
|
||||
info(`Link section: ${body}`)
|
||||
return false
|
||||
}
|
||||
|
||||
info(`Checking validity of link: ${link}`)
|
||||
|
||||
if (!URL.canParse(link)) {
|
||||
info(`Invalid URL: ${link}`)
|
||||
return false
|
||||
}
|
||||
|
||||
const url = new URL(link)
|
||||
|
||||
if (!config.invalidLink.hosts.includes(url.hostname)) {
|
||||
info('Link did not match allowed reproduction hosts')
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
// Verify that the link can be accessed
|
||||
const response = await fetch(link)
|
||||
const isOk = response.status < 400 || response.status >= 500
|
||||
|
||||
info(`Link status: ${response.status}`)
|
||||
if (!isOk) {
|
||||
info(`Link returned status ${response.status}`)
|
||||
}
|
||||
return isOk
|
||||
} catch (error) {
|
||||
info(`Error fetching link: ${(error as Error).message}`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return either a file's content or a string
|
||||
* @param {string} pathOrComment
|
||||
*/
|
||||
async function getCommentBody(pathOrComment: string) {
|
||||
try {
|
||||
await access(pathOrComment)
|
||||
return await readFile(pathOrComment, 'utf8')
|
||||
} catch (error: any) {
|
||||
if (error.code === 'ENOENT') return pathOrComment
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function run() {
|
||||
const { token, workspace, ...safeConfig } = config
|
||||
info('Configuration:')
|
||||
info(JSON.stringify(safeConfig, null, 2))
|
||||
|
||||
await checkValidReproduction()
|
||||
}
|
||||
|
||||
run().catch(setFailed)
|
||||
15
.github/actions/triage/tsconfig.json
vendored
Normal file
15
.github/actions/triage/tsconfig.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["es2020.string"],
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"noUnusedLocals": false, // Undo this
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"downlevelIteration": true,
|
||||
"skipLibCheck": true,
|
||||
},
|
||||
"exclude": ["src/**/*.test.ts"]
|
||||
}
|
||||
18
.github/comments/invalid-reproduction.md
vendored
Normal file
18
.github/comments/invalid-reproduction.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
We cannot recreate the issue with the provided information. **Please add a reproduction in order for us to be able to investigate.**
|
||||
|
||||
### Why was this issue marked with the `invalid-reproduction` label?
|
||||
|
||||
To be able to investigate, we need access to a reproduction to identify what triggered the issue. We prefer a link to a public GitHub repository created with `create-payload-app@beta -t blank` or a forked/branched version of this repository with tests added (more info in the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md)).
|
||||
|
||||
To make sure the issue is resolved as quickly as possible, please make sure that the reproduction is as **minimal** as possible. This means that you should **remove unnecessary code, files, and dependencies** that do not contribute to the issue. Ensure your reproduction does not depend on secrets, 3rd party registries, private dependencies, or any other data that cannot be made public. Avoid a reproduction including a whole monorepo (unless relevant to the issue). The easier it is to reproduce the issue, the quicker we can help.
|
||||
|
||||
Please test your reproduction against the latest version of Payload to make sure your issue has not already been fixed.
|
||||
|
||||
### I added a link, why was it still marked?
|
||||
|
||||
Ensure the link is pointing to a codebase that is accessible (e.g. not a private repository). "[example.com](http://example.com/)", "n/a", "will add later", etc. are not acceptable links -- we need to see a public codebase. See the above section for accepted links.
|
||||
|
||||
### Useful Resources
|
||||
|
||||
- [Reproduction Guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md)
|
||||
- [Contributing to Payload](https://www.youtube.com/watch?v=08Qa3ggR9rw)
|
||||
74
.github/dependabot.yml
vendored
Normal file
74
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
# docs: https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directories:
|
||||
- /
|
||||
- /.github/workflows
|
||||
- /.github/actions/* # Not working until resolved: https://github.com/dependabot/dependabot-core/issues/6345
|
||||
- /.github/actions/setup
|
||||
target-branch: beta
|
||||
schedule:
|
||||
interval: monthly
|
||||
timezone: America/Detroit
|
||||
time: '06:00'
|
||||
groups:
|
||||
github_actions:
|
||||
patterns:
|
||||
- '*'
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
target-branch: beta
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: sunday
|
||||
timezone: America/Detroit
|
||||
time: '06:00'
|
||||
commit-message:
|
||||
prefix: 'chore(deps)'
|
||||
labels:
|
||||
- dependencies
|
||||
groups:
|
||||
production-deps:
|
||||
dependency-type: production
|
||||
update-types:
|
||||
- minor
|
||||
- patch
|
||||
patterns:
|
||||
- '*'
|
||||
exclude-patterns:
|
||||
- 'drizzle*'
|
||||
dev-deps:
|
||||
dependency-type: development
|
||||
update-types:
|
||||
- minor
|
||||
- patch
|
||||
patterns:
|
||||
- '*'
|
||||
exclude-patterns:
|
||||
- 'drizzle*'
|
||||
|
||||
# Only bump patch versions for 2.x
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
target-branch: main
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: sunday
|
||||
timezone: America/Detroit
|
||||
time: '06:00'
|
||||
commit-message:
|
||||
prefix: 'chore(deps)'
|
||||
labels:
|
||||
- dependencies
|
||||
groups:
|
||||
production-deps:
|
||||
dependency-type: production
|
||||
update-types:
|
||||
- patch
|
||||
patterns:
|
||||
- '*'
|
||||
exclude-patterns:
|
||||
- 'drizzle*'
|
||||
4026
.github/pnpm-lock.yaml
generated
vendored
Normal file
4026
.github/pnpm-lock.yaml
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
.github/pnpm-workspace.yaml
vendored
Normal file
2
.github/pnpm-workspace.yaml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
packages:
|
||||
- 'actions/*'
|
||||
50
.github/workflows/label-author.yml
vendored
50
.github/workflows/label-author.yml
vendored
@@ -1,50 +0,0 @@
|
||||
name: label-author
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
debug-context:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: View context attributes
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: console.log(context)
|
||||
|
||||
label-created-by:
|
||||
name: Label pr/issue on opening
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Tag with 'created-by'
|
||||
uses: actions/github-script@v7
|
||||
if: github.event.action == 'opened'
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const type = context.payload.pull_request ? 'pull_request' : 'issue';
|
||||
const association = context.payload[type].author_association;
|
||||
let label = ''
|
||||
if (association === 'MEMBER' || association === 'OWNER') {
|
||||
label = 'created-by: Payload team';
|
||||
} else if (association === 'CONTRIBUTOR') {
|
||||
label = 'created-by: Contributor';
|
||||
}
|
||||
|
||||
if (!label) return;
|
||||
|
||||
github.rest.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: [label],
|
||||
});
|
||||
console.log('Added created-by: Payload team label');
|
||||
116
.github/workflows/label-on-change.yml
vendored
Normal file
116
.github/workflows/label-on-change.yml
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
name: label-on-change
|
||||
|
||||
on:
|
||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
|
||||
issues:
|
||||
types:
|
||||
- assigned
|
||||
- closed
|
||||
- labeled
|
||||
- reopened
|
||||
|
||||
# TODO: Handle labeling on comment
|
||||
|
||||
jobs:
|
||||
on-labeled-ensure-one-status:
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
issues: write
|
||||
# Only run on issue labeled and if label starts with 'status:'
|
||||
if: github.event.action == 'labeled' && startsWith(github.event.label.name, 'status:')
|
||||
steps:
|
||||
- name: Ensure only one status label
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
// Get all labels that start with 'status:' and are not the incoming label
|
||||
const incomingLabelName = context.payload.label.name;
|
||||
const labelNamesToRemove = context.payload.issue.labels
|
||||
.filter(label => label.name.startsWith('status:') && label.name !== incomingLabelName)
|
||||
.map(label => label.name);
|
||||
|
||||
if (!labelNamesToRemove.length) {
|
||||
console.log('No labels to remove');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Labels to remove: '${labelNamesToRemove}'`);
|
||||
|
||||
// If there is more than one status label, remove all but the incoming label
|
||||
for (const labelName of labelNamesToRemove) {
|
||||
await github.rest.issues.removeLabel({
|
||||
issue_number: context.issue.number,
|
||||
name: labelName,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
});
|
||||
console.log(`Removed '${labelName}' label`);
|
||||
}
|
||||
|
||||
on-issue-close:
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
issues: write
|
||||
if: github.event.action == 'closed'
|
||||
steps:
|
||||
- name: Remove all labels on issue close
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
// Get all labels that start with 'status:' and 'stale'
|
||||
const labelNamesToRemove = context.payload.issue.labels
|
||||
.filter(label => label.name.startsWith('status:') || label.name === 'stale')
|
||||
.map(label => label.name);
|
||||
|
||||
if (!labelNamesToRemove.length) {
|
||||
console.log('No labels to remove');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Labels to remove: '${labelNamesToRemove}'`);
|
||||
|
||||
for (const labelName of labelNamesToRemove) {
|
||||
await github.rest.issues.removeLabel({
|
||||
issue_number: context.issue.number,
|
||||
name: labelName,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
});
|
||||
console.log(`Removed '${labelName}' label`);
|
||||
}
|
||||
|
||||
on-issue-reopen:
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
issues: write
|
||||
if: github.event.action == 'reopened'
|
||||
steps:
|
||||
- name: Add needs-triage label on issue reopen
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: 'status: needs-triage'
|
||||
|
||||
on-issue-assigned:
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
issues: write
|
||||
if: >
|
||||
github.event.action == 'assigned' &&
|
||||
contains(github.event.issue.labels.*.name, 'status: needs-triage')
|
||||
steps:
|
||||
- name: Remove needs-triage label on issue assign
|
||||
uses: actions-ecosystem/action-remove-labels@v1
|
||||
with:
|
||||
labels: 'status: needs-triage'
|
||||
|
||||
# on-pr-merge:
|
||||
# runs-on: ubuntu-24.04
|
||||
# if: github.event.pull_request.merged == true
|
||||
# steps:
|
||||
|
||||
# on-pr-close:
|
||||
# runs-on: ubuntu-24.04
|
||||
# if: github.event_name == 'pull_request_target' && github.event.pull_request.merged == false
|
||||
# steps:
|
||||
26
.github/workflows/lock-issues.yml
vendored
Normal file
26
.github/workflows/lock-issues.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: lock-issues
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run nightly at 12am EST
|
||||
- cron: '0 4 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
lock_issues:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Lock issues
|
||||
uses: dessant/lock-threads@v5
|
||||
with:
|
||||
process-only: 'issues'
|
||||
issue-inactive-days: '1'
|
||||
exclude-any-issue-labels: 'status: awaiting-reply'
|
||||
log-output: true
|
||||
issue-comment: >
|
||||
This issue has been automatically locked.
|
||||
|
||||
Please open a new issue if this issue persists with any additional detail.
|
||||
419
.github/workflows/main.yml
vendored
419
.github/workflows/main.yml
vendored
@@ -2,29 +2,13 @@ name: build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
types: [opened, reopened, synchronize]
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- beta
|
||||
|
||||
concurrency:
|
||||
# <workflow_name>-<branch_name>-<true || commit_sha if branch is protected>
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.ref_protected && github.sha || ''}}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
NODE_VERSION: 18.20.2
|
||||
PNPM_VERSION: 8.15.7
|
||||
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
|
||||
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
|
||||
branches: ['main']
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
pull-requests: read
|
||||
outputs:
|
||||
@@ -38,12 +22,12 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@v2
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
needs_build:
|
||||
- '.github/workflows/**'
|
||||
- '.github/workflows/main.yml'
|
||||
- 'packages/**'
|
||||
- 'test/**'
|
||||
- 'pnpm-lock.yaml'
|
||||
@@ -55,55 +39,10 @@ jobs:
|
||||
echo "needs_build: ${{ steps.filter.outputs.needs_build }}"
|
||||
echo "templates: ${{ steps.filter.outputs.templates }}"
|
||||
|
||||
lint:
|
||||
if: >
|
||||
github.event_name == 'pull_request' && !contains(github.event.pull_request.title, 'no-lint') && !contains(github.event.pull_request.title, 'skip-lint')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 720
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
pnpm-store-
|
||||
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- run: pnpm install
|
||||
- name: Lint staged
|
||||
run: |
|
||||
git diff --name-only --diff-filter=d origin/${GITHUB_BASE_REF}...${GITHUB_SHA}
|
||||
npx lint-staged --diff="origin/${GITHUB_BASE_REF}...${GITHUB_SHA}"
|
||||
|
||||
build:
|
||||
core-build:
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.needs_build == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -114,15 +53,15 @@ jobs:
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
- name: Use Node.js 22.6.0
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
node-version: 22.6.0
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
version: 9.7.0
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
@@ -130,72 +69,31 @@ jobs:
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 720
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
pnpm-store-
|
||||
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
${{ runner.os }}-pnpm-store-
|
||||
${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- run: pnpm install
|
||||
- run: pnpm run build:all
|
||||
env:
|
||||
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
|
||||
- run: pnpm run build
|
||||
|
||||
- name: Cache build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
tests-unit:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Unit Tests
|
||||
run: pnpm test:unit
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8096
|
||||
|
||||
tests-int:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
tests:
|
||||
runs-on: ubuntu-24.04
|
||||
needs: core-build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
database:
|
||||
- mongodb
|
||||
- postgres
|
||||
- postgres-custom-schema
|
||||
- postgres-uuid
|
||||
- supabase
|
||||
database: [mongoose, postgres, postgres-custom-schema, postgres-uuid, supabase]
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
@@ -210,20 +108,19 @@ jobs:
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
- name: Use Node.js 22.6.0
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
node-version: 22.6.0
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
version: 9.7.0
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -273,6 +170,9 @@ jobs:
|
||||
echo "POSTGRES_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres" >> $GITHUB_ENV
|
||||
if: matrix.database == 'supabase'
|
||||
|
||||
- name: Component Tests
|
||||
run: pnpm test:components
|
||||
|
||||
- name: Integration Tests
|
||||
run: pnpm test:int
|
||||
env:
|
||||
@@ -281,71 +181,35 @@ jobs:
|
||||
POSTGRES_URL: ${{ env.POSTGRES_URL }}
|
||||
|
||||
tests-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
runs-on: ubuntu-24.04
|
||||
needs: core-build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# find test -type f -name 'e2e.spec.ts' | sort | xargs dirname | xargs -I {} basename {}
|
||||
suite:
|
||||
- _community
|
||||
- access-control
|
||||
- admin__e2e__1
|
||||
- admin__e2e__2
|
||||
- auth
|
||||
- field-error-states
|
||||
- fields-relationship
|
||||
- fields
|
||||
- fields__collections__Blocks
|
||||
- fields__collections__Array
|
||||
- fields__collections__Relationship
|
||||
- fields__collections__RichText
|
||||
- fields__collections__Lexical__e2e__main
|
||||
- fields__collections__Lexical__e2e__blocks
|
||||
- fields__collections__Date
|
||||
- fields__collections__Number
|
||||
- fields__collections__Point
|
||||
- fields__collections__Tabs
|
||||
- fields__collections__Text
|
||||
- fields__collections__Upload
|
||||
- live-preview
|
||||
- localization
|
||||
- i18n
|
||||
- plugin-cloud-storage
|
||||
- plugin-form-builder
|
||||
- plugin-nested-docs
|
||||
- plugin-seo
|
||||
- versions
|
||||
- uploads
|
||||
env:
|
||||
SUITE_NAME: ${{ matrix.suite }}
|
||||
part: [1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8, 8/8]
|
||||
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
- name: Use Node.js 22.6.0
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
node-version: 22.6.0
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
version: 9.7.0
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Start LocalStack
|
||||
run: pnpm docker:start
|
||||
if: ${{ matrix.suite == 'plugin-cloud-storage' }}
|
||||
|
||||
- name: Store Playwright's Version
|
||||
run: |
|
||||
# Extract the version number using a more targeted regex pattern with awk
|
||||
@@ -369,93 +233,42 @@ jobs:
|
||||
run: pnpm exec playwright install-deps chromium
|
||||
|
||||
- name: E2E Tests
|
||||
run: PLAYWRIGHT_JSON_OUTPUT_NAME=results_${{ matrix.suite }}.json pnpm test:e2e ${{ matrix.suite }}
|
||||
env:
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME: results_${{ matrix.suite }}.json
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
uses: nick-fields/retry@v2
|
||||
with:
|
||||
retry_on: error
|
||||
max_attempts: 2
|
||||
timeout_minutes: 15
|
||||
command: pnpm test:e2e --part ${{ matrix.part }} --bail
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: test-results-${{ matrix.suite }}
|
||||
path: test/test-results/
|
||||
if-no-files-found: ignore
|
||||
name: test-results
|
||||
path: test-results/
|
||||
retention-days: 1
|
||||
|
||||
# Disabled until this is fixed: https://github.com/daun/playwright-report-summary/issues/156
|
||||
# - uses: daun/playwright-report-summary@v3
|
||||
# with:
|
||||
# report-file: results_${{ matrix.suite }}.json
|
||||
# report-tag: ${{ matrix.suite }}
|
||||
# job-summary: true
|
||||
|
||||
app-build-with-packed:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Start MongoDB
|
||||
uses: supercharge/mongodb-github-action@1.10.0
|
||||
with:
|
||||
mongodb-version: 6.0
|
||||
|
||||
- name: Pack and build app
|
||||
run: |
|
||||
set -ex
|
||||
pnpm run script:pack --dest templates/blank-3.0
|
||||
cd templates/blank-3.0
|
||||
cp .env.example .env
|
||||
ls -la
|
||||
pnpm add ./*.tgz --ignore-workspace
|
||||
pnpm install --ignore-workspace --no-frozen-lockfile
|
||||
cat package.json
|
||||
pnpm run build
|
||||
|
||||
tests-type-generation:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
runs-on: ubuntu-24.04
|
||||
needs: core-build
|
||||
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
- name: Use Node.js 22.6.0
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
node-version: 22.6.0
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
version: 9.7.0
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -466,10 +279,96 @@ jobs:
|
||||
- name: Generate GraphQL schema file
|
||||
run: pnpm dev:generate-graphql-schema graphql-schema-gen
|
||||
|
||||
build-packages:
|
||||
runs-on: ubuntu-24.04
|
||||
needs: core-build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pkg:
|
||||
- db-mongodb
|
||||
- db-postgres
|
||||
- bundler-webpack
|
||||
- bundler-vite
|
||||
- richtext-slate
|
||||
- richtext-lexical
|
||||
- live-preview
|
||||
- live-preview-react
|
||||
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Use Node.js 22.6.0
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.6.0
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 9.7.0
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Build ${{ matrix.pkg }}
|
||||
run: pnpm turbo run build --filter=${{ matrix.pkg }}
|
||||
|
||||
plugins:
|
||||
runs-on: ubuntu-24.04
|
||||
needs: core-build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pkg:
|
||||
- create-payload-app
|
||||
- plugin-cloud
|
||||
- plugin-cloud-storage
|
||||
- plugin-form-builder
|
||||
- plugin-nested-docs
|
||||
- plugin-search
|
||||
- plugin-sentry
|
||||
- plugin-seo
|
||||
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Use Node.js 22.6.0
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.6.0
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 9.7.0
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Build ${{ matrix.pkg }}
|
||||
run: pnpm turbo run build --filter=${{ matrix.pkg }}
|
||||
|
||||
- name: Test ${{ matrix.pkg }}
|
||||
run: pnpm --filter ${{ matrix.pkg }} run test
|
||||
if: matrix.pkg != 'create-payload-app' # degit doesn't work within GitHub Actions
|
||||
|
||||
templates:
|
||||
needs: changes
|
||||
if: false # Disable until templates are updated for 3.0
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ needs.changes.outputs.templates == 'true' }}
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -479,14 +378,15 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
- name: Use Node.js 22.6.0
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
node-version: 22.6.0
|
||||
|
||||
- name: Start MongoDB
|
||||
uses: supercharge/mongodb-github-action@1.10.0
|
||||
@@ -500,48 +400,3 @@ jobs:
|
||||
yarn install
|
||||
yarn build
|
||||
yarn generate:types
|
||||
|
||||
generated-templates:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Build all generated templates
|
||||
run: pnpm tsx ./scripts/build-generated-templates.ts
|
||||
|
||||
all-green:
|
||||
name: All Green
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- lint
|
||||
- build
|
||||
- tests-unit
|
||||
- tests-int
|
||||
- tests-e2e
|
||||
|
||||
steps:
|
||||
- if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}
|
||||
run: exit 1
|
||||
|
||||
32
.github/workflows/post-release.yml
vendored
Normal file
32
.github/workflows/post-release.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: post-release
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
post_release:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# Only needed if debugging on a branch other than default
|
||||
# ref: ${{ github.event.release.target_commitish || github.ref }}
|
||||
- run: echo "npm_version=$(npm pkg get version | tr -d '"')" >> "$GITHUB_ENV"
|
||||
- uses: ./.github/actions/release-commenter
|
||||
continue-on-error: true
|
||||
env:
|
||||
ACTIONS_STEP_DEBUG: true
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag-filter: 'v\d'
|
||||
|
||||
|
||||
# Change to blank to disable commenting
|
||||
# comment-template: ''
|
||||
|
||||
comment-template: |
|
||||
🚀 This is included in version {release_link}
|
||||
103
.github/workflows/pr-title.yml
vendored
103
.github/workflows/pr-title.yml
vendored
@@ -1,103 +0,0 @@
|
||||
name: pr-title
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- synchronize
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
main:
|
||||
name: lint-pr-title
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@v5
|
||||
id: lint_pr_title
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
types: |
|
||||
build
|
||||
chore
|
||||
ci
|
||||
docs
|
||||
feat
|
||||
fix
|
||||
perf
|
||||
refactor
|
||||
revert
|
||||
style
|
||||
test
|
||||
types
|
||||
scopes: |
|
||||
cpa
|
||||
db-\*
|
||||
db-mongodb
|
||||
db-postgres
|
||||
email-nodemailer
|
||||
eslint
|
||||
graphql
|
||||
live-preview
|
||||
live-preview-react
|
||||
next
|
||||
payload
|
||||
plugin-cloud
|
||||
plugin-cloud-storage
|
||||
plugin-form-builder
|
||||
plugin-nested-docs
|
||||
plugin-redirects
|
||||
plugin-relationship-object-ids
|
||||
plugin-search
|
||||
plugin-sentry
|
||||
plugin-seo
|
||||
plugin-stripe
|
||||
richtext-\*
|
||||
richtext-lexical
|
||||
richtext-slate
|
||||
storage-\*
|
||||
storage-azure
|
||||
storage-gcs
|
||||
storage-vercel-blob
|
||||
storage-s3
|
||||
translations
|
||||
ui
|
||||
templates
|
||||
examples(\/(\w|-)+)?
|
||||
deps
|
||||
|
||||
# Disallow uppercase letters at the beginning of the subject
|
||||
subjectPattern: ^(?![A-Z]).+$
|
||||
subjectPatternError: |
|
||||
The subject "{subject}" found in the pull request title "{title}"
|
||||
didn't match the configured pattern. Please ensure that the subject
|
||||
doesn't start with an uppercase character.
|
||||
|
||||
- uses: marocchino/sticky-pull-request-comment@v2
|
||||
# When the previous steps fails, the workflow would stop. By adding this
|
||||
# condition you can continue the execution with the populated error message.
|
||||
if: always() && (steps.lint_pr_title.outputs.error_message != null)
|
||||
with:
|
||||
header: pr-title-lint-error
|
||||
message: |
|
||||
Pull Request titles must follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and have valid scopes.
|
||||
|
||||
${{ steps.lint_pr_title.outputs.error_message }}
|
||||
|
||||
```
|
||||
feat(ui): add Button component
|
||||
^ ^ ^
|
||||
| | |__ Subject
|
||||
| |_______ Scope
|
||||
|____________ Type
|
||||
```
|
||||
|
||||
# Delete a previous comment when the issue has been resolved
|
||||
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
header: pr-title-lint-error
|
||||
delete: true
|
||||
33
.github/workflows/release-canary.yml
vendored
33
.github/workflows/release-canary.yml
vendored
@@ -2,35 +2,10 @@ name: release-canary
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
branches:
|
||||
- beta
|
||||
|
||||
env:
|
||||
NODE_VERSION: 18.20.2
|
||||
PNPM_VERSION: 8.15.7
|
||||
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
|
||||
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
|
||||
|
||||
jobs:
|
||||
release:
|
||||
permissions:
|
||||
id-token: write
|
||||
runs-on: ubuntu-latest
|
||||
test:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
||||
- name: Load npm token
|
||||
run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
- name: Canary release script
|
||||
# dry run hard-coded to true for testing and no npm token provided
|
||||
run: pnpm tsx ./scripts/publish-canary.ts
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
- name: Echo
|
||||
run: echo "Register release-canary workflow"
|
||||
|
||||
42
.github/workflows/stale.yml
vendored
Normal file
42
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: stale
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
id: stale
|
||||
with:
|
||||
debug-only: true
|
||||
days-before-stale: 90
|
||||
days-before-close: 7
|
||||
ascending: true
|
||||
operations-per-run: 300
|
||||
|
||||
# Ignore all assigned
|
||||
exempt-all-assignees: false
|
||||
|
||||
# Issues
|
||||
stale-issue-label: 'stale'
|
||||
exempt-issue-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
|
||||
stale-issue-message: >
|
||||
This issue has been marked as stale due to lack of activity. To keep the ticket open, please indicate that it is still relevant in a comment below.
|
||||
close-issue-message: >
|
||||
This issue was automatically closed due to lack of activity.
|
||||
|
||||
# Pull Requests
|
||||
stale-pr-label: 'stale'
|
||||
exempt-pr-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
|
||||
stale-pr-message: >
|
||||
This PR is stale due to lack of activity. To keep the PR open, please indicate that it is still relevant in a comment below.
|
||||
close-pr-message: >
|
||||
This pull request was automatically closed due to lack of activity.
|
||||
|
||||
- name: Print outputs
|
||||
run: echo ${{ format('{0},{1}', toJSON(steps.stale.outputs.staled-issues-prs), toJSON(steps.stale.outputs.closed-issues-prs)) }}
|
||||
102
.github/workflows/triage.yml
vendored
Normal file
102
.github/workflows/triage.yml
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
name: triage
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
debug-context:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: View context attributes
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: console.log({ context })
|
||||
|
||||
label-created-by:
|
||||
name: label-on-open
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Tag with 'created-by'
|
||||
uses: actions/github-script@v7
|
||||
if: github.event.action == 'opened'
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const payloadTeamUsernames = [
|
||||
'denolfe',
|
||||
'jmikrut',
|
||||
'DanRibbens',
|
||||
'jacobsfletch',
|
||||
'JarrodMFlesch',
|
||||
'AlessioGr',
|
||||
'JessChowdhury',
|
||||
'kendelljoseph',
|
||||
'PatrikKozak',
|
||||
'tylandavis',
|
||||
'paulpopus',
|
||||
'r1tsuu',
|
||||
'GermanJablo',
|
||||
];
|
||||
|
||||
const type = context.payload.pull_request ? 'pull_request' : 'issue';
|
||||
|
||||
const isTeamMember = payloadTeamUsernames
|
||||
.map(n => n.toLowerCase())
|
||||
.includes(context.payload[type].user.login.toLowerCase());
|
||||
|
||||
if (isTeamMember) {
|
||||
github.rest.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ['created-by: Payload team'],
|
||||
});
|
||||
console.log(`Added 'created-by: Payload team' label`);
|
||||
return;
|
||||
}
|
||||
|
||||
const association = context.payload[type].author_association;
|
||||
let label = ''
|
||||
if (association === 'MEMBER' || association === 'OWNER') {
|
||||
label = 'created-by: Payload team';
|
||||
} else if (association === 'CONTRIBUTOR') {
|
||||
label = 'created-by: Contributor';
|
||||
}
|
||||
|
||||
if (!label) return;
|
||||
|
||||
github.rest.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: [label],
|
||||
});
|
||||
console.log(`Added '${label}' label.`);
|
||||
|
||||
triage:
|
||||
name: initial-triage
|
||||
if: github.event_name == 'issues'
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: ./.github/actions/triage
|
||||
with:
|
||||
reproduction-comment: '.github/comments/invalid-reproduction.md'
|
||||
reproduction-link-section: '### Link to the code that reproduces this issue(.*)### Reproduction Steps'
|
||||
reproduction-issue-labels: 'validate-reproduction'
|
||||
tag-only: 'true'
|
||||
14
.gitignore
vendored
14
.gitignore
vendored
@@ -3,21 +3,15 @@ package-lock.json
|
||||
dist
|
||||
/.idea/*
|
||||
!/.idea/runConfigurations
|
||||
!/.idea/payload.iml
|
||||
|
||||
# Custom actions
|
||||
!.github/actions/**/dist
|
||||
|
||||
test-results
|
||||
.devcontainer
|
||||
.localstack
|
||||
/migrations
|
||||
.localstack
|
||||
.turbo
|
||||
|
||||
.turbo
|
||||
|
||||
# Ignore test directory media folder/files
|
||||
/media
|
||||
test/media
|
||||
/versions
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||
@@ -143,7 +137,6 @@ out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
@@ -290,4 +283,3 @@ $RECYCLE.BIN/
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||
|
||||
/build
|
||||
.swc
|
||||
|
||||
81
.idea/payload.iml
generated
81
.idea/payload.iml
generated
@@ -1,81 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/payload/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/payload/components" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/payload/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.swc" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/examples" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/media" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/create-payload-app/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/create-payload-app/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/db-mongodb/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/db-mongodb/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/db-postgres/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/db-postgres/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/graphql/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/graphql/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/live-preview-react/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/live-preview-react/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/live-preview/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/live-preview/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/next/.swc" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/next/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/payload/fields" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-cloud-storage/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-cloud-storage/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-cloud/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-cloud/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-form-builder/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-nested-docs/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-nested-docs/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-redirects/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-redirects/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-search/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-search/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-sentry/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-seo/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-seo/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-stripe/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/richtext-lexical/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/richtext-lexical/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/templates" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/test/.swc" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/versions" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/richtext-slate/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/richtext-slate/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/email-nodemailer/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/email-nodemailer/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/email-resend/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/email-resend/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/live-preview-vue/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/live-preview-vue/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/payload/.swc" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-form-builder/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-relationship-object-ids/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-relationship-object-ids/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-stripe/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-azure/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-azure/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-gcs/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-gcs/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-s3/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-s3/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-uploadthing/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-uploadthing/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-vercel-blob/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/storage-vercel-blob/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/translations/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/translations/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/ui/.swc" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/ui/.turbo" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
5
.idea/runConfigurations/Run_Dev_Fields.xml
generated
5
.idea/runConfigurations/Run_Dev_Fields.xml
generated
@@ -1,8 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run Dev Fields" type="NodeJSConfigurationType" application-parameters="--no-deprecation fields" path-to-js-file="test/dev.js" working-dir="$PROJECT_DIR$">
|
||||
<envs>
|
||||
<env name="NODE_OPTIONS" value="--no-deprecation" />
|
||||
</envs>
|
||||
<configuration default="false" name="Run Dev Fields" type="NodeJSConfigurationType" application-parameters="fields" path-to-js-file="node_modules/.pnpm/nodemon@3.0.3/node_modules/nodemon/bin/nodemon.js" working-dir="$PROJECT_DIR$">
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
5
.idea/runConfigurations/Run_Dev__community.xml
generated
5
.idea/runConfigurations/Run_Dev__community.xml
generated
@@ -1,8 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run Dev _community" type="NodeJSConfigurationType" application-parameters="--no-deprecation _community" path-to-js-file="test/dev.js" working-dir="$PROJECT_DIR$">
|
||||
<envs>
|
||||
<env name="NODE_OPTIONS" value="--no-deprecation" />
|
||||
</envs>
|
||||
<configuration default="false" name="Run Dev _community" type="NodeJSConfigurationType" application-parameters="_community" path-to-js-file="node_modules/.pnpm/nodemon@3.0.3/node_modules/nodemon/bin/nodemon.js" working-dir="$PROJECT_DIR$">
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,9 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="true" type="JavaScriptTestRunnerJest">
|
||||
<node-interpreter value="project" />
|
||||
<node-options value="--experimental-vm-modules --no-deprecation" />
|
||||
<envs />
|
||||
<scope-kind value="ALL" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1 +1 @@
|
||||
v18.20.2
|
||||
v18.17.1
|
||||
|
||||
@@ -9,8 +9,3 @@
|
||||
**/node_modules
|
||||
**/temp
|
||||
**/docs/**
|
||||
tsconfig.json
|
||||
packages/payload/*.js
|
||||
packages/payload/*.d.ts
|
||||
payload-types.ts
|
||||
tsconfig.tsbuildinfo
|
||||
|
||||
16
.release-it.pre.js
Normal file
16
.release-it.pre.js
Normal file
@@ -0,0 +1,16 @@
|
||||
module.exports = {
|
||||
verbose: true,
|
||||
git: {
|
||||
requireCleanWorkingDir: false,
|
||||
commit: false,
|
||||
push: false,
|
||||
tag: false,
|
||||
},
|
||||
npm: {
|
||||
skipChecks: true,
|
||||
tag: 'beta',
|
||||
},
|
||||
hooks: {
|
||||
'before:init': ['pnpm install', 'pnpm clean', 'pnpm build'],
|
||||
},
|
||||
}
|
||||
24
.swcrc
24
.swcrc
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "es6"
|
||||
}
|
||||
}
|
||||
7
.vscode/extensions.json
vendored
7
.vscode/extensions.json
vendored
@@ -1,8 +1,3 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"firsttris.vscode-jest-runner",
|
||||
"ms-playwright.playwright"
|
||||
]
|
||||
"recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"]
|
||||
}
|
||||
|
||||
93
.vscode/launch.json
vendored
93
.vscode/launch.json
vendored
@@ -3,59 +3,12 @@
|
||||
// Hover to view descriptions of existing attributes.
|
||||
"configurations": [
|
||||
{
|
||||
"command": "pnpm generate:types",
|
||||
"name": "Generate Types CLI",
|
||||
"request": "launch",
|
||||
"type": "node-terminal",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js _community",
|
||||
"command": "pnpm run dev _community",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Community",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js storage-uploadthing",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Uploadthing",
|
||||
"request": "launch",
|
||||
"type": "node-terminal",
|
||||
"envFile": "${workspaceFolder}/test/storage-uploadthing/.env"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js live-preview",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Live Preview",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/loader/init.js",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Loader",
|
||||
"request": "launch",
|
||||
"type": "node-terminal",
|
||||
"env": {
|
||||
"LOADER_TEST_FILE_PATH": "./dependency-test.js"
|
||||
// "LOADER_TEST_FILE_PATH": "../fields/config.ts"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js admin",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Admin",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js auth",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Auth",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev plugin-cloud-storage",
|
||||
"cwd": "${workspaceFolder}",
|
||||
@@ -67,56 +20,63 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js collections-graphql",
|
||||
"command": "pnpm run dev collections-graphql",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev GraphQL",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js fields",
|
||||
"command": "pnpm run dev fields",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Fields",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js versions",
|
||||
"command": "pnpm run dev:postgres fields",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Postgres",
|
||||
"request": "launch",
|
||||
"type": "node-terminal",
|
||||
"env": {
|
||||
"PAYLOAD_DATABASE": "postgres"
|
||||
}
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js versions",
|
||||
"command": "pnpm run dev versions",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Versions",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js localization",
|
||||
"command": "pnpm run dev localization",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Localization",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js uploads",
|
||||
"command": "pnpm run dev field-error-states",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Field Error States",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev uploads",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Uploads",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js field-error-states",
|
||||
"command": "PAYLOAD_BUNDLER=vite pnpm run dev fields",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Field Error States",
|
||||
"name": "Run Dev Fields (Vite)",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
"type": "node-terminal",
|
||||
"env": {
|
||||
"NODE_ENV": "production"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "pnpm run test:int live-preview",
|
||||
@@ -156,6 +116,17 @@
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "ts-node ./packages/payload/src/bin/index.ts generate:types",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "test/_community/config.ts",
|
||||
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
|
||||
},
|
||||
"name": "Generate Types CLI",
|
||||
"outputCapture": "std",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:status",
|
||||
"env": {
|
||||
|
||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -36,12 +36,6 @@
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
// Load .git-blame-ignore-revs file
|
||||
"gitlens.advanced.blame.customArguments": ["--ignore-revs-file", ".git-blame-ignore-revs"],
|
||||
"[javascript][typescript][typescriptreact]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
}
|
||||
},
|
||||
"files.insertFinalNewline": true,
|
||||
"jestrunner.jestCommand": "pnpm exec cross-env NODE_OPTIONS=\"--experimental-vm-modules --no-deprecation\" node 'node_modules/jest/bin/jest.js'",
|
||||
"jestrunner.debugOptions": {
|
||||
"runtimeArgs": ["--experimental-vm-modules", "--no-deprecation"]
|
||||
|
||||
542
CHANGELOG.md
542
CHANGELOG.md
@@ -1,3 +1,545 @@
|
||||
## [2.32.1](https://github.com/payloadcms/payload/compare/v2.32.0...v2.32.1) (2025-03-14)
|
||||
|
||||
Bumps dependencies ([#11702](https://github.com/payloadcms/payload/pull/11702)) ([#11705](https://github.com/payloadcms/payload/pull/11705))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove collapsible label prefix from the FieldSelect dropdown component ([#10760](https://github.com/payloadcms/payload/issues/10760)) ([a5e6ea5](https://github.com/payloadcms/payload/commit/a5e6ea57373591ec90f452ad61b376f72f9ce304)), closes [#10757](https://github.com/payloadcms/payload/issues/10757)
|
||||
|
||||
## [2.32.0](https://github.com/payloadcms/payload/compare/v2.31.0...v2.32.0) (2025-01-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* export CheckboxInput ([#10489](https://github.com/payloadcms/payload/issues/10489)) ([2e35255](https://github.com/payloadcms/payload/commit/2e352552d1905217acdc52f5c4a6255eb24b247c))
|
||||
|
||||
## [2.31.0](https://github.com/payloadcms/payload/compare/v2.30.4...v2.31.0) (2025-01-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adds `forceRenderAllFields` admin prop to force all fields in edit view to render immediately ([#10464](https://github.com/payloadcms/payload/issues/10464)) ([07ff181](https://github.com/payloadcms/payload/commit/07ff181ccf5e455c97a4afa6d72bcbc173b1a76d))
|
||||
* **i18n:** add Slovenian (sl) translation ([#8709](https://github.com/payloadcms/payload/issues/8709)) ([7039b1f](https://github.com/payloadcms/payload/commit/7039b1f82e323358a605f9b2165328403f8da74c))
|
||||
* **v2:** allows filtering on group field types from list view ([#10421](https://github.com/payloadcms/payload/issues/10421)) ([f430db8](https://github.com/payloadcms/payload/commit/f430db8bc5853f5a444d8c2ec6626df0af2e41a4))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **graphql:** 500 error when querying hasMany field data that is filtered by access-control ([#6519](https://github.com/payloadcms/payload/issues/6519)) ([4892d96](https://github.com/payloadcms/payload/commit/4892d965157285964fb024516ddcdf56a74cc68e)), closes [#6518](https://github.com/payloadcms/payload/issues/6518)
|
||||
* when publishing from a draft, only 10 were published. ([#7906](https://github.com/payloadcms/payload/issues/7906)) ([0cf9678](https://github.com/payloadcms/payload/commit/0cf96785bc47e2d2ef107a139d03058e99c8bb79))
|
||||
|
||||
## [2.30.4](https://github.com/payloadcms/payload/compare/v2.30.3...v2.30.4) (2024-11-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **plugin-seo:** add Turkish translation ([#8918](https://github.com/payloadcms/payload/issues/8918)) ([7c9ec9c](https://github.com/payloadcms/payload/commit/7c9ec9c4e0bafd19eb0ef94e9bb4aa07df3e097e))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db-postgres:** sort by localized fields ([#9016](https://github.com/payloadcms/payload/issues/9016)) ([9c25754](https://github.com/payloadcms/payload/commit/9c25754eed87e70b053231693102378150b8b80d))
|
||||
* edit many modal draft action button order and style ([#9046](https://github.com/payloadcms/payload/issues/9046)) ([a907480](https://github.com/payloadcms/payload/commit/a907480a94f07b4afa0e0723a3738c8bd457c51f)), closes [#9045](https://github.com/payloadcms/payload/issues/9045)
|
||||
* error saving after duplicating blocks with nested items ([#8814](https://github.com/payloadcms/payload/issues/8814)) ([eba777c](https://github.com/payloadcms/payload/commit/eba777c3a0b4c489d87d3c6154d835a105fef4a2))
|
||||
* list drawer relationship not displaying ([#9011](https://github.com/payloadcms/payload/issues/9011)) ([169da5c](https://github.com/payloadcms/payload/commit/169da5c3d83bfe3559227504929aa62721ded1d9))
|
||||
* querying relationships by `id` path with REST ([#9014](https://github.com/payloadcms/payload/issues/9014)) ([a0bd706](https://github.com/payloadcms/payload/commit/a0bd7060c45df3037ee074bd83400837c0adb83a))
|
||||
|
||||
## [2.30.3](https://github.com/payloadcms/payload/compare/v2.30.2...v2.30.3) (2024-10-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db-postgres:** migrate:create errors with previous schemas ([#8786](https://github.com/payloadcms/payload/issues/8786)) ([e9c1222](https://github.com/payloadcms/payload/commit/e9c12221824a9a180991722135d22ff91d07ef11))
|
||||
* duplicate with select hasMany fields ([#8734](https://github.com/payloadcms/payload/issues/8734)) ([c8ed645](https://github.com/payloadcms/payload/commit/c8ed6454a733bea09ae620517c4894701999e119))
|
||||
|
||||
## [2.30.2](https://github.com/payloadcms/payload/compare/v2.30.1...v2.30.2) (2024-10-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* applies resize after cropping if `resizeOptions` are defined ([#8535](https://github.com/payloadcms/payload/issues/8535)) ([f2284f3](https://github.com/payloadcms/payload/commit/f2284f3d1b420c543d9eee9929f961db4cbef8a1))
|
||||
* calculates correct aspect ratio dimensions on sharp based files ([#8510](https://github.com/payloadcms/payload/issues/8510)) ([9d05b82](https://github.com/payloadcms/payload/commit/9d05b82dc67b967e55176b92f7b20d4486883201)), closes [#8317](https://github.com/payloadcms/payload/issues/8317)
|
||||
* **db-postgres:** build indexes for relationships ([#8446](https://github.com/payloadcms/payload/issues/8446)) ([d05e3b0](https://github.com/payloadcms/payload/commit/d05e3b0411c2e705bd7a26e93ba55f2b7532e41b))
|
||||
* **db-postgres:** port many various fixes from 3.0 ([#8468](https://github.com/payloadcms/payload/issues/8468)) ([1347b6c](https://github.com/payloadcms/payload/commit/1347b6cc36c33043755b4e31d7731ba19b8c985f))
|
||||
* **db-postgres:** select hasMany nested to array + tab/group ([#8739](https://github.com/payloadcms/payload/issues/8739)) ([0efc610](https://github.com/payloadcms/payload/commit/0efc6102104729a162912157a7e34293ec9764a5))
|
||||
* **richtext-lexical:** add target _blank for new-tab in linkFeature ([#8571](https://github.com/payloadcms/payload/issues/8571)) ([61e8ce1](https://github.com/payloadcms/payload/commit/61e8ce17439301cb16aee92887e97a69cac4e044)), closes [#8569](https://github.com/payloadcms/payload/issues/8569)
|
||||
|
||||
## [2.30.1](https://github.com/payloadcms/payload/compare/v2.30.0...v2.30.1) (2024-10-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db-mongodb:** properly filters out `number` field values with the `exists` operator filter ([#8415](https://github.com/payloadcms/payload/issues/8415)) ([0586f23](https://github.com/payloadcms/payload/commit/0586f236bbf04163a0d9b226772849cb3d977864)), closes [#8181](https://github.com/payloadcms/payload/issues/8181)
|
||||
* sorting by id incorrectly orders by version.id ([#8450](https://github.com/payloadcms/payload/issues/8450)) ([1d38e6d](https://github.com/payloadcms/payload/commit/1d38e6d5d5b56a91aa8f59a461d40f28b1750f8c))
|
||||
|
||||
## [2.30.0](https://github.com/payloadcms/payload/compare/v2.29.0...v2.30.0) (2024-09-27)
|
||||
|
||||
* export toast from react toastify in payload ([#8438](https://github.com/payloadcms/payload/issues/8438)) ([17fc2d1](https://github.com/payloadcms/payload/commit/17fc2d13d06b6de01f839c27fd706bc0d6a185eb))
|
||||
|
||||
## [2.29.0](https://github.com/payloadcms/payload/compare/v2.28.0...v2.29.0) (2024-09-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add new option to disable JOI validation ([#8067](https://github.com/payloadcms/payload/issues/8067)) ([28a0650](https://github.com/payloadcms/payload/commit/28a065072fcad2dc768e44d79609eb5ab8a3fdfd))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db-postgres:** localized items in arrays with versions ([#8334](https://github.com/payloadcms/payload/issues/8334)) ([c86526b](https://github.com/payloadcms/payload/commit/c86526b5c81ff484e66fbe6e7c727fdcc1f93c77))
|
||||
* **db-postgres:** querying on array within a relationship field ([#8153](https://github.com/payloadcms/payload/issues/8153)) ([170ea5b](https://github.com/payloadcms/payload/commit/170ea5badcff154514b8166ac92177d89a3fa5f8))
|
||||
* **db-postgres:** sanitize tab/group path for table name ([#8010](https://github.com/payloadcms/payload/issues/8010)) ([ba7a043](https://github.com/payloadcms/payload/commit/ba7a043a99f58fad39a62ac471eeb7309a39bba0))
|
||||
* treat empty strings as null / undefined for `exists` queries ([#8336](https://github.com/payloadcms/payload/issues/8336)) ([31d0b30](https://github.com/payloadcms/payload/commit/31d0b309fe5df1e37ed2a938959c1ef87834d987)), closes [#7714](https://github.com/payloadcms/payload/issues/7714)
|
||||
|
||||
## [2.28.0](https://github.com/payloadcms/payload/compare/v2.27.0...v2.28.0) (2024-09-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* collections can use custom database operations ([#7675](https://github.com/payloadcms/payload/issues/7675)) ([6ba293c](https://github.com/payloadcms/payload/commit/6ba293c0f84f91bf89cf089a20e47de130013ebb))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db-postgres:** migration exit codes ([#7873](https://github.com/payloadcms/payload/issues/7873)) ([25e9bc6](https://github.com/payloadcms/payload/commit/25e9bc62dbcbabcb3619cf83e3dc0110e0a4cabf)), closes [#7031](https://github.com/payloadcms/payload/issues/7031)
|
||||
* **db-postgres:** query hasMany text/number in array/blocks ([#8033](https://github.com/payloadcms/payload/issues/8033)) ([96a624a](https://github.com/payloadcms/payload/commit/96a624ad5c5259b197b4ca793d8419d1e827de9c))
|
||||
* **plugin-cloud:** better logging on static handler ([#7924](https://github.com/payloadcms/payload/issues/7924)) ([1f09348](https://github.com/payloadcms/payload/commit/1f0934877ce5aabb771c936c3677a26d2ef006ec))
|
||||
|
||||
## [2.27.0](https://github.com/payloadcms/payload/compare/v2.26.0...v2.27.0) (2024-08-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add support for custom image size file names ([#7637](https://github.com/payloadcms/payload/issues/7637)) ([f976270](https://github.com/payloadcms/payload/commit/f97627092cabe4eabbebefa75afc53579188386b))
|
||||
* upgrade react-toastify dependency, and upgrade to pnpm v9 in our monorepo ([#7667](https://github.com/payloadcms/payload/issues/7667)) ([94d18e8](https://github.com/payloadcms/payload/commit/94d18e8d747588efce225cde0b621db9b513e7c1))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update state of field if either `valid` status or `errorMessage` changes ([#7632](https://github.com/payloadcms/payload/issues/7632)) ([c624eea](https://github.com/payloadcms/payload/commit/c624eea0d868938f4603860fa25be3df580ba7fe)), closes [#6413](https://github.com/payloadcms/payload/issues/6413)
|
||||
|
||||
## [2.26.0](https://github.com/payloadcms/payload/compare/v2.25.0...v2.26.0) (2024-08-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adds classnames to edit, list views ([#7595](https://github.com/payloadcms/payload/issues/7595)) ([7f39afa](https://github.com/payloadcms/payload/commit/7f39afa1928b118451138e811ea71a04fce021d5))
|
||||
* adds upload's relationship thumbnail ([#5015](https://github.com/payloadcms/payload/issues/5015)) ([39e110e](https://github.com/payloadcms/payload/commit/39e110e6331efff0ca8ca7174780076243a016de))
|
||||
* **ui:** expose custom errors in delete many ([#7439](https://github.com/payloadcms/payload/issues/7439)) ([3e780b9](https://github.com/payloadcms/payload/commit/3e780b98155550f877021996dd094ba435dff81b))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db-postgres:** localized array inside blocks field ([#7458](https://github.com/payloadcms/payload/issues/7458)) ([a308d63](https://github.com/payloadcms/payload/commit/a308d6384f9724c5ff330382070a5803fbcf167c)), closes [#5240](https://github.com/payloadcms/payload/issues/5240)
|
||||
* deprecated `inflight` package ([#6558](https://github.com/payloadcms/payload/issues/6558)) ([eca1517](https://github.com/payloadcms/payload/commit/eca1517237c78983c192f4bafa92a86d94a0de9e)), closes [#6492](https://github.com/payloadcms/payload/issues/6492)
|
||||
* enable `relationship` & `upload` field population in `versions` ([#7533](https://github.com/payloadcms/payload/issues/7533)) ([9865ae9](https://github.com/payloadcms/payload/commit/9865ae998b9aeb5d72724023976bb203133e19ff))
|
||||
* filtering by non-poly `relationships` with `not_equals` operator ([#7573](https://github.com/payloadcms/payload/issues/7573)) ([efa56ce](https://github.com/payloadcms/payload/commit/efa56cefc15a48cd45b3aaba2eddacca79e1be30)), closes [#5212](https://github.com/payloadcms/payload/issues/5212) [#6278](https://github.com/payloadcms/payload/issues/6278)
|
||||
* filtering by polymorphic `relationships` with `drafts` enabled ([#7565](https://github.com/payloadcms/payload/issues/7565)) ([907d7d1](https://github.com/payloadcms/payload/commit/907d7d1d3a89ed22bb991a1f238bb77d54e3e173)), closes [#6880](https://github.com/payloadcms/payload/issues/6880)
|
||||
* retained date milliseconds ([#7393](https://github.com/payloadcms/payload/issues/7393)) ([9c9e689](https://github.com/payloadcms/payload/commit/9c9e6896a502de209c6cccf63cc5cfc0f0143bf3)), closes [#6108](https://github.com/payloadcms/payload/issues/6108)
|
||||
* prevents `hasMany` text going outside of input boundaries ([#7454](https://github.com/payloadcms/payload/issues/7454)) ([1a0ef48](https://github.com/payloadcms/payload/commit/1a0ef4824b3d6548d36e7f28a2030640361c0655)), closes [#6034](https://github.com/payloadcms/payload/issues/6034)
|
||||
* previousValue missing from ValidateOptions type ([#6931](https://github.com/payloadcms/payload/issues/6931)) ([fca5a40](https://github.com/payloadcms/payload/commit/fca5a404dbf3b440b428e55cf5e03db647f9a453))
|
||||
* render singular label for `ArrayCell` when length is 1 ([#7585](https://github.com/payloadcms/payload/issues/7585)) ([fc4d24a](https://github.com/payloadcms/payload/commit/fc4d24aa8889ac9be76059a92478d5532b142b5c)), closes [#6099](https://github.com/payloadcms/payload/issues/6099)
|
||||
|
||||
## [2.25.0](https://github.com/payloadcms/payload/compare/v2.24.2...v2.25.0) (2024-07-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* allows metadata to be appended to the file of the output media ([#7295](https://github.com/payloadcms/payload/issues/7295)) ([3c5cce4](https://github.com/payloadcms/payload/commit/3c5cce4c6f108f87e87b091bbfec976423de73a2))
|
||||
* **db-mongodb:** adds new optional `collation` feature flag behind mongodb collation option ([#7359](https://github.com/payloadcms/payload/issues/7359)) ([9750bc2](https://github.com/payloadcms/payload/commit/9750bc217ee7d63732a34908c84eb88b88dac0a8)), closes [#7349](https://github.com/payloadcms/payload/issues/7349)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* properly handles `0` value number fields in list view ([#7364](https://github.com/payloadcms/payload/issues/7364)) ([5321098](https://github.com/payloadcms/payload/commit/5321098d7eada43838f6d5c69f3233c150fe0afa)), closes [#5510](https://github.com/payloadcms/payload/issues/5510)
|
||||
* preserves objectids in deepCopyObject ([#7385](https://github.com/payloadcms/payload/issues/7385)) ([1348483](https://github.com/payloadcms/payload/commit/134848364801c72cc773ef7b48854306d1b9bac3))
|
||||
* relaxes equality check for relationship options in filter ([#7344](https://github.com/payloadcms/payload/issues/7344)) ([468e544](https://github.com/payloadcms/payload/commit/468e5441f16775134d915ec7caddb17b817d3408))
|
||||
* supports null values in query strings ([#5241](https://github.com/payloadcms/payload/issues/5241)) ([c57591b](https://github.com/payloadcms/payload/commit/c57591bc4fb8d28b7de16a111faffea7d3e11f8d))
|
||||
|
||||
## [2.24.2](https://github.com/payloadcms/payload/compare/v2.24.1...v2.24.2) (2024-07-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **db-mongodb:** add jsonParse flag to mongooseAdapter that preserves existing, untracked MongoDB data types ([#7338](https://github.com/payloadcms/payload/issues/7338)) ([f96cf59](https://github.com/payloadcms/payload/commit/f96cf593cedcae0d8ed55f9a70e8e4e77917a876))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow autosave relationship drawers to function properly ([#7325](https://github.com/payloadcms/payload/issues/7325)) ([69e7b7a](https://github.com/payloadcms/payload/commit/69e7b7a158c38058ece54a97bfa79e65192774a6))
|
||||
* **db-mongodb:** removes precedence of regular chars over international chars in sort ([#6923](https://github.com/payloadcms/payload/issues/6923)) ([0058660](https://github.com/payloadcms/payload/commit/0058660b3f8bd820abb4494ff53fa67f49f0f6b4)), closes [#6719](https://github.com/payloadcms/payload/issues/6719)
|
||||
* fetches and sets permissions before setting user ([#7337](https://github.com/payloadcms/payload/issues/7337)) ([8259611](https://github.com/payloadcms/payload/commit/8259611ce60e23f6298a07564d5f6dd2966d61ff))
|
||||
* **plugin-stripe:** properly types async webhooks ([#7316](https://github.com/payloadcms/payload/issues/7316)) ([c6da99b](https://github.com/payloadcms/payload/commit/c6da99b4d1b986089bb697486a7825db66323078))
|
||||
|
||||
## [2.24.1](https://github.com/payloadcms/payload/compare/v2.24.0...v2.24.1) (2024-07-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* aliases AfterMe, AfterLogout, and AfterRefresh hook types ([#7146](https://github.com/payloadcms/payload/issues/7146)) ([20377bb](https://github.com/payloadcms/payload/commit/20377bb22c867552e412c1cafd16869399aadd68))
|
||||
* exports fallback hook types to ensure backwards compatibility ([#7217](https://github.com/payloadcms/payload/issues/7217)) ([1864577](https://github.com/payloadcms/payload/commit/18645771c86664f1246f0fb599c8265a4cd1d6c0))
|
||||
* resizes images first before applying focal point ([#7278](https://github.com/payloadcms/payload/issues/7278)) ([1b208c7](https://github.com/payloadcms/payload/commit/1b208c7addf56ae8a1af5e408b001b3e5f080a38))
|
||||
* uploads from drawer and focal point positioning ([#7244](https://github.com/payloadcms/payload/issues/7244)) ([0841d5a](https://github.com/payloadcms/payload/commit/0841d5a35ee00650c703231a08fc9a361861ba67))
|
||||
|
||||
## [2.24.0](https://github.com/payloadcms/payload/compare/v2.23.1...v2.24.0) (2024-07-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adds ability to upload files from a remote url ([#7087](https://github.com/payloadcms/payload/issues/7087)) ([84d214f](https://github.com/payloadcms/payload/commit/84d214f99207ad78a466b8c16eb36e29f57cd0e3))
|
||||
* **db-mongodb:** allows mongoose schemaOptions to be configured ([#7099](https://github.com/payloadcms/payload/issues/7099)) ([51474fa](https://github.com/payloadcms/payload/commit/51474fa661ae24ab8fc0d13001fafc0f35216c1e))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ensures access query runs with locale when present ([#6981](https://github.com/payloadcms/payload/issues/6981)) ([a5492af](https://github.com/payloadcms/payload/commit/a5492afad672e19dd35b1f5370b51f22656f334c))
|
||||
|
||||
## [2.23.1](https://github.com/payloadcms/payload/compare/v2.23.0...v2.23.1) (2024-06-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove unused refresh arg, this affected me/refresh hooks ([#6976](https://github.com/payloadcms/payload/issues/6976)) ([c82d2ca](https://github.com/payloadcms/payload/commit/77e8ce980ef0bcb0380b499dd1ccdfd36199b707))
|
||||
|
||||
## [2.23.0](https://github.com/payloadcms/payload/compare/v2.22.2...v2.23.0) (2024-06-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adds me and refresh hooks ([#6968](https://github.com/payloadcms/payload/issues/6968)) ([c82d2ca](https://github.com/payloadcms/payload/commit/c82d2caa29422083e97affc99a033296d78892d6))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **richtext-lexical:** html converters unnecessarily growing over time ([#6963](https://github.com/payloadcms/payload/issues/6963)) ([cf52d64](https://github.com/payloadcms/payload/commit/cf52d64d984d98ab782ca33f20b43c935ce60683))
|
||||
|
||||
## [2.22.2](https://github.com/payloadcms/payload/compare/v2.22.1...v2.22.2) (2024-06-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* return exp and strategy from auth ([#6943](https://github.com/payloadcms/payload/issues/6943)) ([ea18735](https://github.com/payloadcms/payload/commit/ea18735d3b2d2a96989009130e3724aab487e520))
|
||||
|
||||
## [2.22.1](https://github.com/payloadcms/payload/compare/v2.22.0...v2.22.1) (2024-06-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* graphql query concurrency issues ([#6857](https://github.com/payloadcms/payload/issues/6857)) ([bb911cc](https://github.com/payloadcms/payload/commit/bb911cc7eca1eeef15ade8eb043c0056c281e311))
|
||||
* sends cropped image pixel values to server instead of percent values ([#6852](https://github.com/payloadcms/payload/issues/6852)) ([8747743](https://github.com/payloadcms/payload/commit/874774375f8beada9bac0a8ef3e77f63adc30834))
|
||||
|
||||
## [2.22.0](https://github.com/payloadcms/payload/compare/v2.21.0...v2.22.0) (2024-06-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* passes prev value through to validate functions ([#6805](https://github.com/payloadcms/payload/issues/6805)) ([0a51de7](https://github.com/payloadcms/payload/commit/0a51de7623d7c7790db52f0cc3e431b5ec3436f2))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adjust json field schema for defaultValue ([#6872](https://github.com/payloadcms/payload/issues/6872)) ([58427ff](https://github.com/payloadcms/payload/commit/58427ffae3e0c1159fae817850b4c1770f43b9b9))
|
||||
* array row validation messages ([#6780](https://github.com/payloadcms/payload/issues/6780)) ([68ea693](https://github.com/payloadcms/payload/commit/68ea693a88714951661464917d5df10a416a5ca0))
|
||||
* cannot use empty string in defaultValue on text-like fields ([#6842](https://github.com/payloadcms/payload/issues/6842)) ([3364385](https://github.com/payloadcms/payload/commit/336438506ce5e54065f59a0352531e0255f5313d))
|
||||
* corrects redirect with lonely slash on login ([#6784](https://github.com/payloadcms/payload/issues/6784)) ([dae56e6](https://github.com/payloadcms/payload/commit/dae56e60eef9e6456d9c51e7fc3b995a32e56f5e))
|
||||
* **db-postgres:** cascade delete FKs on hasMany relationships ([#6735](https://github.com/payloadcms/payload/issues/6735)) ([9ecc6c8](https://github.com/payloadcms/payload/commit/9ecc6c889929a07d1163919eac14c9b33b90208a))
|
||||
* **payload, db-mongodb:** querying relationships with `in` & `not_in` ([#6773](https://github.com/payloadcms/payload/issues/6773)) ([f6ba3be](https://github.com/payloadcms/payload/commit/f6ba3befaeb02edca66f04fbc2ea91d2d2549d08)), closes [#6741](https://github.com/payloadcms/payload/issues/6741)
|
||||
* **plugin-cloud-storage:** missing error handling for invalid plugin config, leading to unexpected webpack errors ([#6786](https://github.com/payloadcms/payload/issues/6786)) ([015aafd](https://github.com/payloadcms/payload/commit/015aafda758938028c37a96e9b5fe62902757b09))
|
||||
* **ui:** withinCollapsible always false from useCollapsible provider ([#6783](https://github.com/payloadcms/payload/issues/6783)) ([9f0aaf0](https://github.com/payloadcms/payload/commit/9f0aaf066e76210a85faf43b2b5dc5129ccf3879)), closes [#6760](https://github.com/payloadcms/payload/issues/6760)
|
||||
* unflattening json objects containing keys with periods ([#6834](https://github.com/payloadcms/payload/issues/6834)) ([025306f](https://github.com/payloadcms/payload/commit/025306f9e685c030956dad3b0f0158c1dce49668)), closes [#5378](https://github.com/payloadcms/payload/issues/5378)
|
||||
* upgrade swc, fixing swc issues on linux ([#6809](https://github.com/payloadcms/payload/issues/6809)) ([1c986a9](https://github.com/payloadcms/payload/commit/1c986a98321163c921cb071a160b6fa9fc8bd9ee))
|
||||
|
||||
|
||||
## [2.21.0](https://github.com/payloadcms/payload/compare/v2.20.0...v2.21.0) (2024-06-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* draft validation ([#6746](https://github.com/payloadcms/payload/issues/6746)) ([ff70fd9](https://github.com/payloadcms/payload/commit/ff70fd9813ec7dc14bf54d3457c25e145fe01699))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adjust version status pill when unpublished ([#6744](https://github.com/payloadcms/payload/issues/6744)) ([b7e8529](https://github.com/payloadcms/payload/commit/b7e852993beaaa465e38caa36e75e870819516b5))
|
||||
* use correct time for isLocked check ([#6052](https://github.com/payloadcms/payload/issues/6052)) ([3b4bb30](https://github.com/payloadcms/payload/commit/3b4bb3065a6d2ac2f7d6aafd0fb4a35b580f3662))
|
||||
* unable to save animated file types with undefined image sizes ([#6733](https://github.com/payloadcms/payload/issues/6733)) ([e40570b](https://github.com/payloadcms/payload/commit/e40570bd0d64660bb2ae5b5785b4c85c684ca9ab)), closes [#6727](https://github.com/payloadcms/payload/issues/6727)
|
||||
|
||||
## [2.20.0](https://github.com/payloadcms/payload/compare/v2.19.3...v2.20.0) (2024-06-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **ui:** updates draft/published version pills ([#6732](https://github.com/payloadcms/payload/issues/6732)) ([ed86b15](https://github.com/payloadcms/payload/commit/ed86b15242ccbd737f3fe80103afc7d8c4cc6915))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adds multi select inputs for `number` & `text` fields in where builder ([#6662](https://github.com/payloadcms/payload/issues/6662)) ([e3003b4](https://github.com/payloadcms/payload/commit/e3003b443fd3b472670ade228c174c7f755b1732))
|
||||
* create sharp file for `fileHasAdjustments` files or `fileIsAnimated` files ([#6710](https://github.com/payloadcms/payload/issues/6710)) ([921a5c0](https://github.com/payloadcms/payload/commit/921a5c065d6089f0118ad8be7adbf75614c8db9c))
|
||||
* enable SaveDraft button when creating new documents ([#6672](https://github.com/payloadcms/payload/issues/6672)) ([63bc6ae](https://github.com/payloadcms/payload/commit/63bc6ae52f63adf98f442c2d7992461e7f5f86e4)), closes [#6671](https://github.com/payloadcms/payload/issues/6671) [/github.com/payloadcms/payload/commit/8f03cd7c789eda7613ddced0d45a32afe49b1e01#diff-b7c978f47b1f3beff95c78ad95078e600624cbcd7ac10f9378cc4ad6803db244L75-R79](https://github.com/payloadcms//github.com/payloadcms/payload/commit/8f03cd7c789eda7613ddced0d45a32afe49b1e01/issues/diff-b7c978f47b1f3beff95c78ad95078e600624cbcd7ac10f9378cc4ad6803db244L75-R79)
|
||||
* handles localized nested relationship fields in versions ([#6679](https://github.com/payloadcms/payload/issues/6679)) ([8a62298](https://github.com/payloadcms/payload/commit/8a622984e7ce4a2439d5d63e2689404652f8c96b))
|
||||
* live preview device position when using zoom ([#6667](https://github.com/payloadcms/payload/issues/6667)) ([9525511](https://github.com/payloadcms/payload/commit/9525511e8bc6bc3fbd7e21e36596404604f1c109))
|
||||
* only use `metadata.pages` for height if animated ([#6729](https://github.com/payloadcms/payload/issues/6729)) ([2f9ed34](https://github.com/payloadcms/payload/commit/2f9ed34d13d94070db40b1eabd04655d2e13e094))
|
||||
* removes `array` & `blocks` & `group` fields from sort ([#6574](https://github.com/payloadcms/payload/issues/6574)) ([507e095](https://github.com/payloadcms/payload/commit/507e0954b2743012f0b53c396d49461120a02b1a)), closes [#6469](https://github.com/payloadcms/payload/issues/6469)
|
||||
* withinCollapsible should be undefined by default ([#6666](https://github.com/payloadcms/payload/issues/6666)) ([37c8386](https://github.com/payloadcms/payload/commit/37c8386a51172966057df3477517d160e1c4a9a7)), closes [#6658](https://github.com/payloadcms/payload/issues/6658)
|
||||
|
||||
## [2.19.3](https://github.com/payloadcms/payload/compare/v2.19.2...v2.19.3) (2024-06-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* scopes uploadEdits to documents, hoists action to doc provider ([#6664](https://github.com/payloadcms/payload/issues/6664)) ([373cb00](https://github.com/payloadcms/payload/commit/373cb0013902b52aba455542e10402316da4b2f4))
|
||||
|
||||
## [2.19.2](https://github.com/payloadcms/payload/compare/v2.19.1...v2.19.2) (2024-06-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* cascade draft arg when querying globals with graphql ([#6651](https://github.com/payloadcms/payload/issues/6651)) ([ac8c209](https://github.com/payloadcms/payload/commit/ac8c2096af641a6886e4543ee65c9790e45f080f))
|
||||
* filtered out `disableListColumn` fields reappeared after toggling other fields ([#6636](https://github.com/payloadcms/payload/issues/6636)) ([626be15](https://github.com/payloadcms/payload/commit/626be155784dda181276bb87617433822a0accf3))
|
||||
* resizing animated images ([#6621](https://github.com/payloadcms/payload/issues/6621)) ([67c0b0e](https://github.com/payloadcms/payload/commit/67c0b0e6e0b5b190f6a916b59ba02f8c18479e18)), closes [#2181](https://github.com/payloadcms/payload/issues/2181) [#6146](https://github.com/payloadcms/payload/issues/6146)
|
||||
|
||||
## [2.19.1](https://github.com/payloadcms/payload/compare/v2.19.0...v2.19.1) (2024-06-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* override ajv dependency version to 8.14.0 wherever possible ([#6618](https://github.com/payloadcms/payload/issues/6618)) ([e44ce81](https://github.com/payloadcms/payload/commit/e44ce819cefddeaaf20b2b7ce804e94a9272baf1))
|
||||
|
||||
## [2.19.0](https://github.com/payloadcms/payload/compare/v2.18.3...v2.19.0) (2024-06-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **translations:** update Turkish translations ([#5738](https://github.com/payloadcms/payload/issues/5738)) ([4fddea8](https://github.com/payloadcms/payload/commit/4fddea86ebd5f21705be2310f8b7053d31109189))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adds new `userEmailAlreadyRegistered` translations ([#6549](https://github.com/payloadcms/payload/issues/6549)) ([56c6700](https://github.com/payloadcms/payload/commit/56c6700cf25570cc217e28dc69459a3b81adbced)), closes [#6535](https://github.com/payloadcms/payload/issues/6535)
|
||||
* adjusts sizing of remove/add buttons to be same size ([#6527](https://github.com/payloadcms/payload/issues/6527)) ([a352ebc](https://github.com/payloadcms/payload/commit/a352ebc5520bbd0f6a9caef068825976dba05ded)), closes [#6098](https://github.com/payloadcms/payload/issues/6098)
|
||||
* focalPoint undefined handling ([#6552](https://github.com/payloadcms/payload/issues/6552)) ([fcfc3c5](https://github.com/payloadcms/payload/commit/fcfc3c593f69f63c51f8aa09973fcacbfbe04952))
|
||||
* pagination on polymorphic relationship field requesting entries with page parameter set to NaN ([#5366](https://github.com/payloadcms/payload/issues/5366)) ([547acfe](https://github.com/payloadcms/payload/commit/547acfe876bdf0df2ce808941f72b690c9dbcae3))
|
||||
* safely evaluates `field.admin` in WhereBuilder ([#6534](https://github.com/payloadcms/payload/issues/6534)) ([4f9d78d](https://github.com/payloadcms/payload/commit/4f9d78df5e38f3f70852bb6de47cff619f57c648))
|
||||
* separate sort and search fields when looking up relationship. ([#5964](https://github.com/payloadcms/payload/issues/5964)) ([c009219](https://github.com/payloadcms/payload/commit/c0092191a6ded1098a94d9f48918ab79171e5e32)), closes [#4815](https://github.com/payloadcms/payload/issues/4815) [#5222](https://github.com/payloadcms/payload/issues/5222)
|
||||
* ui field validation error with `admin.disableListColumn` property ([#6530](https://github.com/payloadcms/payload/issues/6530)) ([eeddece](https://github.com/payloadcms/payload/commit/eeddeceda988d7a4ce8ad31d3036a4ee84aceec3)), closes [#6521](https://github.com/payloadcms/payload/issues/6521)
|
||||
* **ui:** blocks browser save dialog from opening when hotkey used with no changes ([#6365](https://github.com/payloadcms/payload/issues/6365)) ([8f03cd7](https://github.com/payloadcms/payload/commit/8f03cd7c789eda7613ddced0d45a32afe49b1e01)), closes [#214](https://github.com/payloadcms/payload/issues/214)
|
||||
|
||||
## [2.18.3](https://github.com/payloadcms/payload/compare/v2.18.2...v2.18.3) (2024-05-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db-postgres:** query with like on id columns ([#6416](https://github.com/payloadcms/payload/issues/6416)) ([bf77cec](https://github.com/payloadcms/payload/commit/bf77cec7e9e7db4988e481d464178636203fca32))
|
||||
* **db-postgres:** uuid custom db name ([#6409](https://github.com/payloadcms/payload/issues/6409)) ([db5f3f3](https://github.com/payloadcms/payload/commit/db5f3f3ccdaedd9e8036c3e39fc20650a309a151))
|
||||
* nested `disableListColumn` in rows ([#6412](https://github.com/payloadcms/payload/issues/6412)) ([ab8b2f3](https://github.com/payloadcms/payload/commit/ab8b2f3fb87864484582b7d819ca307888a9449b)), closes [#6407](https://github.com/payloadcms/payload/issues/6407)
|
||||
|
||||
## [2.18.2](https://github.com/payloadcms/payload/compare/v2.18.1...v2.18.2) (2024-05-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow focal point when no sizes defined ([#6397](https://github.com/payloadcms/payload/issues/6397)) ([88e113a](https://github.com/payloadcms/payload/commit/88e113a5452300434f690186d10ea02ab159ffc3))
|
||||
|
||||
## [2.18.1](https://github.com/payloadcms/payload/compare/v2.18.0...v2.18.1) (2024-05-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add back explicit crop x and y values ([#6391](https://github.com/payloadcms/payload/issues/6391)) ([e76df32](https://github.com/payloadcms/payload/commit/e76df32f0987cc92dc8d9c693950e650c52576bf))
|
||||
|
||||
## [2.18.0](https://github.com/payloadcms/payload/compare/v2.17.0...v2.18.0) (2024-05-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* store focal point on uploads ([#6364](https://github.com/payloadcms/payload/issues/6364)) ([82b88a3](https://github.com/payloadcms/payload/commit/82b88a315ff1d52f0b19a70224d5c600a3a97eb5))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db-postgres:** filter with ID not_in AND queries - postgres ([#6358](https://github.com/payloadcms/payload/issues/6358)) ([cc94078](https://github.com/payloadcms/payload/commit/cc940786072c0065f10fdd2893050bddc4595a21)), closes [#5151](https://github.com/payloadcms/payload/issues/5151)
|
||||
* **richtext-lexical:** upload, relationship and block node insertion fails sometimes ([#6390](https://github.com/payloadcms/payload/issues/6390)) ([48a410e](https://github.com/payloadcms/payload/commit/48a410e294598af9c73577a04f86466248f93da0))
|
||||
|
||||
## [2.17.0](https://github.com/payloadcms/payload/compare/v2.16.1...v2.17.0) (2024-05-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adds misc translations to API view and react select ([#6138](https://github.com/payloadcms/payload/issues/6138)) ([30e535b](https://github.com/payloadcms/payload/commit/30e535b5b929dddead007d8a9adca62808595e2c))
|
||||
|
||||
* **richtext-lexical:** remove LexicalBlock, RichTextFieldRequiredEditor and FieldWithRichTextRequiredEditor types ([#6279](https://github.com/payloadcms/payload/issues/6279)) ([9df5ab8](https://github.com/payloadcms/payload/commit/9df5ab8a10a35ad34615d7e4da024f59ff037e0e))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* appends `editDepth` value to `radio` & `checkbox` IDs when inside drawer ([#6181](https://github.com/payloadcms/payload/issues/6181)) ([69c93d3](https://github.com/payloadcms/payload/commit/69c93d3c62394a5cf995a2eaec9a3ab30e0f77af))
|
||||
* collection labels with locales not working when creating new doc ([#5995](https://github.com/payloadcms/payload/issues/5995)) ([51efe4f](https://github.com/payloadcms/payload/commit/51efe4f39bcaadccb109a2a02a690ca65041ee57))
|
||||
* safely access cookie header for uploads ([#6367](https://github.com/payloadcms/payload/issues/6367)) ([de92c50](https://github.com/payloadcms/payload/commit/de92c50847640661f915455f8db0029873ddc7ab))
|
||||
* step-nav breadcrumbs ellipsis ([#6345](https://github.com/payloadcms/payload/issues/6345)) ([d02b1fb](https://github.com/payloadcms/payload/commit/d02b1fb084e636e49122ad55b25b9c49eb761f1c))
|
||||
*
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **richtext-lexical:** remove LexicalBlock, RichTextFieldRequiredEditor and FieldWithRichTextRequiredEditor types (#6279)
|
||||
|
||||
## [2.16.1](https://github.com/payloadcms/payload/compare/v2.16.0...v2.16.1) (2024-05-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **richtext-lexical:** add maxDepth property to various lexical features ([#6250](https://github.com/payloadcms/payload/issues/6250)) ([857b9a4](https://github.com/payloadcms/payload/commit/857b9a4ac3236c740458750f156a3a4274eda210)), closes [#6242](https://github.com/payloadcms/payload/issues/6242)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **richtext-lexical:** export missing HorizontalRuleFeature ([#6236](https://github.com/payloadcms/payload/issues/6236)) ([f829b08](https://github.com/payloadcms/payload/commit/f829b084ba9649ef596cce4a7bf6ae8c7ccf57e3))
|
||||
|
||||
## [2.16.0](https://github.com/payloadcms/payload/compare/v2.15.0...v2.16.0) (2024-05-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adds disableListColumn, disableListFilter to fields admin props ([#6188](https://github.com/payloadcms/payload/issues/6188)) ([db4aace](https://github.com/payloadcms/payload/commit/db4aacebb801f1cc11ef8732f9f3b78475256641))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* graphql upload relations returning null ([#6233](https://github.com/payloadcms/payload/issues/6233)) ([cac52da](https://github.com/payloadcms/payload/commit/cac52da638a0df4356120a2f61c6aaf25641a5ad))
|
||||
* hide drag handles when `admin.isSortable: false` ([#6225](https://github.com/payloadcms/payload/issues/6225)) ([622cdb0](https://github.com/payloadcms/payload/commit/622cdb044002b2c3182c3b0432b51befbfb9b979))
|
||||
* **plugin-form-builder:** hook overrides not working as intended ([#6203](https://github.com/payloadcms/payload/issues/6203)) ([b735d6a](https://github.com/payloadcms/payload/commit/b735d6aa169acca8cb638859d2c8ba43e315f02c))
|
||||
|
||||
## [2.15.0](https://github.com/payloadcms/payload/compare/v2.14.2...v2.15.0) (2024-05-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add isSortable to arrays and blocks ([#5962](https://github.com/payloadcms/payload/issues/5962)) ([5c58bd3](https://github.com/payloadcms/payload/commit/5c58bd322da966fe610959df13dfd49add35a2ef))
|
||||
* use filterOptions in list relationship filter ([#6156](https://github.com/payloadcms/payload/issues/6156)) ([23f3eb1](https://github.com/payloadcms/payload/commit/23f3eb1cf0b75a4044319d7cd3e5000d5b4e42c4))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* bulk publish from collection list ([#6063](https://github.com/payloadcms/payload/issues/6063)) ([84570e6](https://github.com/payloadcms/payload/commit/84570e6e3bbb81fcae80da92b01bc56d09906072))
|
||||
* cascade draft arg in nested GraphQL relationship queries ([#6141](https://github.com/payloadcms/payload/issues/6141)) ([a8ac8b4](https://github.com/payloadcms/payload/commit/a8ac8b463349664f3188ae77217f037da72f796b))
|
||||
* GraphQL nested relationships not respecting req locale ([#6117](https://github.com/payloadcms/payload/issues/6117)) ([3fccd34](https://github.com/payloadcms/payload/commit/3fccd34abe5a332f88f5e950b755cd1d21441fb6))
|
||||
* hide unusable fields from collection filter select ([#6135](https://github.com/payloadcms/payload/issues/6135)) ([2be5ad0](https://github.com/payloadcms/payload/commit/2be5ad0ebafd1d3c1c0567e2085ccfd593f18271))
|
||||
* incorrect `localesNotSaved` translation ([#5996](https://github.com/payloadcms/payload/issues/5996)) ([af67749](https://github.com/payloadcms/payload/commit/af67749e49db92e675b63b52190e562468894706))
|
||||
* **plugin-cloud:** purge cache for all sizes ([#5301](https://github.com/payloadcms/payload/issues/5301)) ([831f1ff](https://github.com/payloadcms/payload/commit/831f1ff5bed7e083cc076e9eb5ff9a2b2f1ed710))
|
||||
* properly adds `readonly` styles to disabled `radio` fields ([#6176](https://github.com/payloadcms/payload/issues/6176)) ([9b7e62d](https://github.com/payloadcms/payload/commit/9b7e62dc20dca7402c6c68dfb8a5995c211993af))
|
||||
* resets filter state when param state change within route ([#6169](https://github.com/payloadcms/payload/issues/6169)) ([6e38cc2](https://github.com/payloadcms/payload/commit/6e38cc2bcfb08b608abcb6aac4b4c1f6eea63428))
|
||||
* **richtext-lexical:** drag and add block handles disappear too quickly for smaller screen sizes. ([#6145](https://github.com/payloadcms/payload/issues/6145)) ([24f6972](https://github.com/payloadcms/payload/commit/24f697219b5071d91a5c37aafb50e2d823b68d4c))
|
||||
* **richtext-lexical:** floating toolbar caret positioned incorrectly for some line heights ([#6151](https://github.com/payloadcms/payload/issues/6151)) ([36b1f5a](https://github.com/payloadcms/payload/commit/36b1f5a763f782c140e62aa062b4077d6efd0738))
|
||||
* sanitizes fields in default edit view for drawer content ([#6175](https://github.com/payloadcms/payload/issues/6175)) ([43dab5c](https://github.com/payloadcms/payload/commit/43dab5c7053831a0c71f3a6860113f653cab674f))
|
||||
* version restoration ([#6039](https://github.com/payloadcms/payload/issues/6039)) ([91bac9c](https://github.com/payloadcms/payload/commit/91bac9c0aa1ff3da052b9c2ad83fa5ac23a16d1d))
|
||||
|
||||
## [2.14.2](https://github.com/payloadcms/payload/compare/v2.14.1...v2.14.2) (2024-04-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** dedupes react ([#6058](https://github.com/payloadcms/payload/issues/6058)) ([d0ba694](https://github.com/payloadcms/payload/commit/d0ba694c80a1b699c4f2cad98b1f0bde1f0d43ca))
|
||||
|
||||
## [2.14.1](https://github.com/payloadcms/payload/compare/v2.14.0...v2.14.1) (2024-04-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db-postgres:** cumulative updates ([#6033](https://github.com/payloadcms/payload/issues/6033)) ([c31b8dc](https://github.com/payloadcms/payload/commit/c31b8dcaa0c43132d8a01e0cc43094f466cc9168))
|
||||
* disable api key checkbox does not remove api key ([#6017](https://github.com/payloadcms/payload/issues/6017)) ([0ffdcc6](https://github.com/payloadcms/payload/commit/0ffdcc685f4e917a02e62dbaccec7cc8ebbf695d))
|
||||
* **richtext-lexical:** minimize the amount of times sanitizeFields is called ([#6018](https://github.com/payloadcms/payload/issues/6018)) ([60372fa](https://github.com/payloadcms/payload/commit/60372faf36b7f6d92a61ccbaee0f528e50f5a51a))
|
||||
|
||||
## [2.14.0](https://github.com/payloadcms/payload/compare/v2.13.0...v2.14.0) (2024-04-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add count operation to collections ([#5936](https://github.com/payloadcms/payload/issues/5936)) ([c380dee](https://github.com/payloadcms/payload/commit/c380deee4a1db82bce9fea264060000957a53eee))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* bulk publish ([#6006](https://github.com/payloadcms/payload/issues/6006)) ([c11600a](https://github.com/payloadcms/payload/commit/c11600aac38cd67019765faf2a41e62df13e50cc))
|
||||
* **db-postgres:** extra version suffix added to table names ([#5939](https://github.com/payloadcms/payload/issues/5939)) ([bd8b512](https://github.com/payloadcms/payload/commit/bd8b5123b0991e53eb209315897dbca10d14d45e))
|
||||
* **db-postgres:** Fixes nested groups inside nested blocks ([#5882](https://github.com/payloadcms/payload/issues/5882)) ([e258866](https://github.com/payloadcms/payload/commit/e25886649fce414d5d47918f35ba2d4d2ba59174))
|
||||
* **db-postgres:** row table names were not being built properly - v2 ([#5961](https://github.com/payloadcms/payload/issues/5961)) ([9152a23](https://github.com/payloadcms/payload/commit/9152a238d2982503e7f509350651b0ba3f83b1ec))
|
||||
* header filters ([#5997](https://github.com/payloadcms/payload/issues/5997)) ([ad01c67](https://github.com/payloadcms/payload/commit/ad01c6784d283386dc819dfcd47455cad5accfaa))
|
||||
* min/max attributes missing from number input ([#5779](https://github.com/payloadcms/payload/issues/5779)) ([985796b](https://github.com/payloadcms/payload/commit/985796be54b593af0a4934685ab8621b9badda10))
|
||||
* removes `equals` & `not_equals` operators from fields with `hasMany` ([#5885](https://github.com/payloadcms/payload/issues/5885)) ([a8c9625](https://github.com/payloadcms/payload/commit/a8c9625cdec33476a5da87bcd9f010f9d7fb9a94))
|
||||
|
||||
## [2.13.0](https://github.com/payloadcms/payload/compare/v2.12.1...v2.13.0) (2024-04-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* allow configuration for setting headers on external file fetch ([ec1ad0b](https://github.com/payloadcms/payload/commit/ec1ad0b6628d400d7435821c8a72b6746bf87577))
|
||||
* **db-\*:** custom db table and enum names ([#5045](https://github.com/payloadcms/payload/issues/5045)) ([9bbacc4](https://github.com/payloadcms/payload/commit/9bbacc4fb1ad247634f394e95c42ee3adade8048))
|
||||
* json field schemas ([#5726](https://github.com/payloadcms/payload/issues/5726)) ([2c402cc](https://github.com/payloadcms/payload/commit/2c402cc65c9e8f7f33e2fb0ce5e1a8ceff52af1b))
|
||||
* **plugin-seo:** add Chinese translation ([#5429](https://github.com/payloadcms/payload/issues/5429)) ([fcb29bb](https://github.com/payloadcms/payload/commit/fcb29bb1c637867301bbc1070b4a84383bf0e90a))
|
||||
* **richtext-lexical:** add HorizontalRuleFeature ([d8e9084](https://github.com/payloadcms/payload/commit/d8e9084db21828968046ab59775633e409ce5c2a))
|
||||
* **richtext-lexical:** improve floating handle y-positioning by positioning it in the center for smaller elements. ([0055a8e](https://github.com/payloadcms/payload/commit/0055a8eb36b95722cccdc5eb3101a79d3e764f8b))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adds type error validations for `email` and `password` in login operation ([#4852](https://github.com/payloadcms/payload/issues/4852)) ([1f00360](https://github.com/payloadcms/payload/commit/1f0036054a9461535b0992f2449e91e4eaf97d4e))
|
||||
* avoids getting and setting doc preferences when creating new ([#5757](https://github.com/payloadcms/payload/issues/5757)) ([e3c3dda](https://github.com/payloadcms/payload/commit/e3c3ddac34dff8fa085f5b702be2838d513be300))
|
||||
* block field type missing dbName ([#5695](https://github.com/payloadcms/payload/issues/5695)) ([e7608f5](https://github.com/payloadcms/payload/commit/e7608f5507d3b85ea3f44b5cb1f43edf67608b1b))
|
||||
* **db-mongodb:** failing `contains` query with special chars ([#5774](https://github.com/payloadcms/payload/issues/5774)) ([5fa99fb](https://github.com/payloadcms/payload/commit/5fa99fb060cabbb69b5d6688748260e562e6bea3))
|
||||
* **db-mongodb:** ignore end session errors ([#5904](https://github.com/payloadcms/payload/issues/5904)) ([cb8d562](https://github.com/payloadcms/payload/commit/cb8d562132bee437798880e1d7f64dbfdee36949))
|
||||
* **db-mongodb:** version fields indexSortableFields ([#5863](https://github.com/payloadcms/payload/issues/5863)) ([fe0028c](https://github.com/payloadcms/payload/commit/fe0028c89945303a431b48efdae7b6e22304c8a3))
|
||||
* **db-postgres:** hasMany relationship query contains operator ([#4212](https://github.com/payloadcms/payload/issues/4212)) ([608d6d0](https://github.com/payloadcms/payload/commit/608d6d0a872af224ea42c3e6c8a3b4f21678f550))
|
||||
* **db-postgres:** issue querying by localised relationship not respecting locale as constraint ([#5666](https://github.com/payloadcms/payload/issues/5666)) ([44599cb](https://github.com/payloadcms/payload/commit/44599cbc7b8f23d6d8c7a3e05466237406812a6d))
|
||||
* **db-postgres:** query hasMany fields with in ([#5881](https://github.com/payloadcms/payload/issues/5881)) ([6185f8a](https://github.com/payloadcms/payload/commit/6185f8a5d845d12651f5a3ee128eb43d3b9d2449))
|
||||
* **db-postgres:** relationship query pagination ([#5802](https://github.com/payloadcms/payload/issues/5802)) ([65690a6](https://github.com/payloadcms/payload/commit/65690a675c17cfacebe775a327a57741ac09416a))
|
||||
* **db-postgres:** validateExistingBlockIsIdentical localized ([#5839](https://github.com/payloadcms/payload/issues/5839)) ([4c4f924](https://github.com/payloadcms/payload/commit/4c4f924e90ee23a73c9a7cc7e69bbc2caf902b92))
|
||||
* duplicate document multiple times in quick succession ([#5642](https://github.com/payloadcms/payload/issues/5642)) ([373787d](https://github.com/payloadcms/payload/commit/373787de31cbbd33b587aa4be6344948f082f5bb))
|
||||
* missing date locales ([#5656](https://github.com/payloadcms/payload/issues/5656)) ([c1c8600](https://github.com/payloadcms/payload/commit/c1c86009a5e9aad401a05f7c63ad37bd3f88dc84))
|
||||
* number ids were not sanitized to number in rest api ([51f84a4](https://github.com/payloadcms/payload/commit/51f84a4fcfd437eb73c7d83205b66e3620085909))
|
||||
* passes parent id instead of incoming id to saveVersion ([#5831](https://github.com/payloadcms/payload/issues/5831)) ([25c9a14](https://github.com/payloadcms/payload/commit/25c9a145bec9e9566d2bbcba59d5b34394e10bbd))
|
||||
* **plugin-seo:** uses correct key for ukrainian translation ([#5873](https://github.com/payloadcms/payload/issues/5873)) ([e47e544](https://github.com/payloadcms/payload/commit/e47e544364031ac834565a4d86ef6ec9c04e63c0))
|
||||
* properly handle drafts in bulk update ([#5872](https://github.com/payloadcms/payload/issues/5872)) ([ad38f76](https://github.com/payloadcms/payload/commit/ad38f760111abf947c6b0ee4b983ee1224a9bf1b))
|
||||
* req.collection being lost when querying a global inside a collection ([#5727](https://github.com/payloadcms/payload/issues/5727)) ([cbd03ed](https://github.com/payloadcms/payload/commit/cbd03ed2f8819ee8ac20e8739cc03e88ff4caa25))
|
||||
* **richtext-lexical:** catch errors that may occur during HTML generation ([#5754](https://github.com/payloadcms/payload/issues/5754)) ([9b44296](https://github.com/payloadcms/payload/commit/9b442960929d00faa07f1383b1267f71e6f44efe))
|
||||
* **richtext-lexical:** do not allow omitting editor prop for sub-richtext fields within lexical defined in the payload config ([#5766](https://github.com/payloadcms/payload/issues/5766)) ([6186493](https://github.com/payloadcms/payload/commit/6186493246157b4d4b33c8c47378f08581315942))
|
||||
* **richtext-lexical:** incorrect floating handle y-position calculation next to certain kinds of HTML elements like HR ([de5d6cc](https://github.com/payloadcms/payload/commit/de5d6cc4bd591745156f0b8c56795b7bd2eaad7e))
|
||||
* **richtext-lexical:** limit unnecessary floating handle positioning updates ([a00439e](https://github.com/payloadcms/payload/commit/a00439ea893e074d64be83ee6af1e780178a7ee3))
|
||||
* **richtext-lexical:** pass through config for schema generation. Makes it more robust ([#5700](https://github.com/payloadcms/payload/issues/5700)) ([cf135fd](https://github.com/payloadcms/payload/commit/cf135fd1e4aeb30121281399e26be901393ada6d))
|
||||
* **richtext-lexical:** use correct nodeType on HorizontalRule feature HTML converter ([#5805](https://github.com/payloadcms/payload/issues/5805)) ([3b1d331](https://github.com/payloadcms/payload/commit/3b1d3313165499616673f6d363c90ef884994525))
|
||||
* updates type name of `CustomPublishButtonProps` to `CustomPublishButtonType` ([#5644](https://github.com/payloadcms/payload/issues/5644)) ([7df7bf4](https://github.com/payloadcms/payload/commit/7df7bf448bd26e870a1fde8aaa47430904d68366))
|
||||
* updates var ([9530d28](https://github.com/payloadcms/payload/commit/9530d28a6760a667b718027a49ea43ba1accd546))
|
||||
* use isolateObjectProperty function in createLocalReq ([#5748](https://github.com/payloadcms/payload/issues/5748)) ([c0ba6cc](https://github.com/payloadcms/payload/commit/c0ba6cc19a20c043a08ca77caacd47ef7cfb48f4))
|
||||
* uses find instead of fieldIndex for custom ID check ([509ec67](https://github.com/payloadcms/payload/commit/509ec677c42993d9c08facf6928a5ef1e9767508))
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **richtext-lexical:** do not allow omitting editor prop for sub-richtext fields within lexical defined in the payload config ([#5766](https://github.com/payloadcms/payload/issues/5766))
|
||||
|
||||
## [2.12.1](https://github.com/payloadcms/payload/compare/v2.12.0...v2.12.1) (2024-04-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Skip parsing if operator is 'exist' ([#5404](https://github.com/payloadcms/payload/issues/5404)) ([742a7af](https://github.com/payloadcms/payload/commit/742a7af93d2e9ef4d41ee093cef875322792ae72))
|
||||
* updates colors of tooltip in light mode ([#5632](https://github.com/payloadcms/payload/issues/5632)) ([a7e7c92](https://github.com/payloadcms/payload/commit/a7e7c9276835e0a35a18daccb218c901ca2fdd8c))
|
||||
|
||||
## [2.12.0](https://github.com/payloadcms/payload/compare/v2.11.2...v2.12.0) (2024-04-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **plugin-seo:** adds Norwegian translation ([#5621](https://github.com/payloadcms/payload/issues/5621)) ([589b492](https://github.com/payloadcms/payload/commit/589b492b2e6bc578e6f17649ff6d936ffd88b8b5))
|
||||
* **richtext-*:** add ability to provide custom Field and Error components ([#5574](https://github.com/payloadcms/payload/issues/5574)) ([02d2c51](https://github.com/payloadcms/payload/commit/02d2c517176a775c6eeb4b164d9e6b45d8f5e4c1))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db-postgres:** error on delete having joins ([#5459](https://github.com/payloadcms/payload/issues/5459)) ([9169230](https://github.com/payloadcms/payload/commit/916923071a2c28077326bfd2059d1665ef47e94f))
|
||||
* image resize tiff files ([#5415](https://github.com/payloadcms/payload/issues/5415)) ([8184e00](https://github.com/payloadcms/payload/commit/8184e0005af1b6da93d0921c31547515a4ce6343))
|
||||
* number field with hasMany accept defaultValue array ([#5618](https://github.com/payloadcms/payload/issues/5618)) ([a3ae416](https://github.com/payloadcms/payload/commit/a3ae4160850290c194bb228e1fbeb8c7533cead3))
|
||||
* regression of filterOptions using different transaction ([#5169](https://github.com/payloadcms/payload/issues/5169)) ([3ceb6ef](https://github.com/payloadcms/payload/commit/3ceb6efd32cfa9556c6656469fa43d61d7679b1d))
|
||||
* **richtext-lexical:** Blocks: generated output schema is not fully correct ([#5259](https://github.com/payloadcms/payload/issues/5259)) ([e7f6bfb](https://github.com/payloadcms/payload/commit/e7f6bfbe9da4cab42c7ec7019371d5e79af17ec9))
|
||||
* **richtext-lexical:** checklist html converter incorrectly outputting children ([#5570](https://github.com/payloadcms/payload/issues/5570)) ([2e5400f](https://github.com/payloadcms/payload/commit/2e5400fa7a5ac7441e00134a7f4299022228f64f))
|
||||
* **richtext-lexical:** disable instanceof HTMLImageElement check as it causes issues when used on the server ([2164dcc](https://github.com/payloadcms/payload/commit/2164dcc594b5c16d7111b71cdb01f5f7aaa7e127))
|
||||
* **richtext-lexical:** Link: add open-in-new-tab to html converter ([23df60d](https://github.com/payloadcms/payload/commit/23df60dba5682255ce6f60e3ee8e6901f6a70c7b))
|
||||
* **richtext-lexical:** properly center add- and drag-block handles ([#5568](https://github.com/payloadcms/payload/issues/5568)) ([48dc116](https://github.com/payloadcms/payload/commit/48dc116f57113c3124ff93a50fa1189053e21d76))
|
||||
* sets beforeValidateHook req type to required ([#5608](https://github.com/payloadcms/payload/issues/5608)) ([e10d5df](https://github.com/payloadcms/payload/commit/e10d5df0b2dd21b86cce44d0e36b31e9d3411b39))
|
||||
|
||||
## [2.11.2](https://github.com/payloadcms/payload/compare/v2.11.1...v2.11.2) (2024-02-23)
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<hr/>
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 🎉 <strong>Payload 2.0 is now available!</strong> Read more in the <a target="_blank" href="https://payloadcms.com/blog/payload-2-0" rel="dofollow"><strong>announcement post</strong></a>.
|
||||
> 🎉 <strong>Payload 3.0 beta released!</strong> You can now deploy Payload fully in any Next.js app folder. Read more in the <a target="_blank" href="https://payloadcms.com/blog/30-beta-install-payload-into-any-nextjs-app-with-one-line" rel="dofollow"><strong>announcement post</strong></a>.
|
||||
|
||||
<h3>Benefits over a regular CMS</h3>
|
||||
<ul>
|
||||
@@ -99,6 +99,10 @@ If you want to add contributions to this repository, please follow the instructi
|
||||
|
||||
The [Examples Directory](./examples) is a great resource for learning how to setup Payload in a variety of different ways, but you can also find great examples in our blog and throughout our social media.
|
||||
|
||||
If you'd like to run the examples, you can either copy them to a folder outside this repo or run them directly by (1) navigating to the example's subfolder (`cd examples/your-example-folder`) and (2) using the `--ignore-workspace` flag to bypass workspace restrictions (e.g., `pnpm --ignore-workspace install` or `pnpm --ignore-workspace dev`).
|
||||
|
||||
You can see more examples at:
|
||||
|
||||
- [Examples Directory](./examples)
|
||||
- [Payload Blog](https://payloadcms.com/blog)
|
||||
- [Payload YouTube](https://www.youtube.com/@payloadcms)
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
import type { Metadata } from 'next'
|
||||
|
||||
import config from '@payload-config'
|
||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views'
|
||||
|
||||
type Args = {
|
||||
params: {
|
||||
segments: string[]
|
||||
}
|
||||
searchParams: {
|
||||
[key: string]: string | string[]
|
||||
}
|
||||
}
|
||||
|
||||
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
|
||||
generatePageMetadata({ config, params, searchParams })
|
||||
|
||||
const NotFound = ({ params, searchParams }: Args) => NotFoundPage({ config, params, searchParams })
|
||||
|
||||
export default NotFound
|
||||
@@ -1,22 +0,0 @@
|
||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
import type { Metadata } from 'next'
|
||||
|
||||
import config from '@payload-config'
|
||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
|
||||
|
||||
type Args = {
|
||||
params: {
|
||||
segments: string[]
|
||||
}
|
||||
searchParams: {
|
||||
[key: string]: string | string[]
|
||||
}
|
||||
}
|
||||
|
||||
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
|
||||
generatePageMetadata({ config, params, searchParams })
|
||||
|
||||
const Page = ({ params, searchParams }: Args) => RootPage({ config, params, searchParams })
|
||||
|
||||
export default Page
|
||||
@@ -1,10 +0,0 @@
|
||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
/* DO NOT MODIFY it because it could be re-written at any time. */
|
||||
import config from '@payload-config'
|
||||
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST } from '@payloadcms/next/routes'
|
||||
|
||||
export const GET = REST_GET(config)
|
||||
export const POST = REST_POST(config)
|
||||
export const DELETE = REST_DELETE(config)
|
||||
export const PATCH = REST_PATCH(config)
|
||||
export const OPTIONS = REST_OPTIONS(config)
|
||||
@@ -1,6 +0,0 @@
|
||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
/* DO NOT MODIFY it because it could be re-written at any time. */
|
||||
import config from '@payload-config'
|
||||
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'
|
||||
|
||||
export const GET = GRAPHQL_PLAYGROUND_GET(config)
|
||||
@@ -1,6 +0,0 @@
|
||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
/* DO NOT MODIFY it because it could be re-written at any time. */
|
||||
import config from '@payload-config'
|
||||
import { GRAPHQL_POST } from '@payloadcms/next/routes'
|
||||
|
||||
export const POST = GRAPHQL_POST(config)
|
||||
@@ -1,15 +0,0 @@
|
||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
import configPromise from '@payload-config'
|
||||
import { RootLayout } from '@payloadcms/next/layouts'
|
||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||
import React from 'react'
|
||||
|
||||
import './custom.scss'
|
||||
|
||||
type Args = {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const Layout = ({ children }: Args) => <RootLayout config={configPromise}>{children}</RootLayout>
|
||||
|
||||
export default Layout
|
||||
@@ -1,5 +0,0 @@
|
||||
export const GET = () => {
|
||||
return Response.json({
|
||||
hello: 'elliot',
|
||||
})
|
||||
}
|
||||
39
changelog.config.js
Normal file
39
changelog.config.js
Normal file
@@ -0,0 +1,39 @@
|
||||
module.exports = {
|
||||
// gitRawCommitsOpts: {
|
||||
// from: 'v2.0.9',
|
||||
// path: 'packages/payload',
|
||||
// },
|
||||
// infile: 'CHANGELOG.md',
|
||||
options: {
|
||||
preset: {
|
||||
name: 'conventionalcommits',
|
||||
types: [
|
||||
{ section: 'Features', type: 'feat' },
|
||||
{ section: 'Features', type: 'feature' },
|
||||
{ section: 'Bug Fixes', type: 'fix' },
|
||||
{ section: 'Documentation', type: 'docs' },
|
||||
],
|
||||
},
|
||||
},
|
||||
// outfile: 'NEW.md',
|
||||
writerOpts: {
|
||||
commitGroupsSort: (a, b) => {
|
||||
const groupOrder = ['Features', 'Bug Fixes', 'Documentation']
|
||||
return groupOrder.indexOf(a.title) - groupOrder.indexOf(b.title)
|
||||
},
|
||||
|
||||
// Scoped commits at the end, alphabetical sort
|
||||
commitsSort: (a, b) => {
|
||||
if (a.scope || b.scope) {
|
||||
if (!a.scope) return -1
|
||||
if (!b.scope) return 1
|
||||
return a.scope === b.scope
|
||||
? a.subject.localeCompare(b.subject)
|
||||
: a.scope.localeCompare(b.scope)
|
||||
}
|
||||
|
||||
// Alphabetical sort
|
||||
return a.subject.localeCompare(b.subject)
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -3,7 +3,7 @@ title: Collection Access Control
|
||||
label: Collections
|
||||
order: 20
|
||||
desc: With Collection-level Access Control you can define which users can create, read, update or delete Collections.
|
||||
keywords: collections, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: collections, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
You can define Collection-level Access Control within each Collection's `access` property. All Access Control functions accept one `args` argument.
|
||||
@@ -51,10 +51,10 @@ Returns a boolean which allows/denies access to the `create` request.
|
||||
|
||||
**Available argument properties:**
|
||||
|
||||
| Option | Description |
|
||||
| ---------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |
|
||||
| **`data`** | The data passed to create the document with. |
|
||||
| Option | Description |
|
||||
| ---------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`data`** | The data passed to create the document with. |
|
||||
|
||||
**Example:**
|
||||
|
||||
@@ -80,7 +80,7 @@ Read access functions can return a boolean result or optionally return a [query
|
||||
|
||||
| Option | Description |
|
||||
| --------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`id`** | `id` of document requested, if within `findByID` |
|
||||
|
||||
**Example:**
|
||||
@@ -111,7 +111,7 @@ Update access functions can return a boolean result or optionally return a [quer
|
||||
|
||||
| Option | Description |
|
||||
| ---------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |
|
||||
| **`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 |
|
||||
|
||||
@@ -138,7 +138,7 @@ Similarly to the Update function, returns a boolean or a [query constraint](/doc
|
||||
|
||||
| Option | Description |
|
||||
| --------- | --------------------------------------------------------------------------------------------------- |
|
||||
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object with additional `user` property, which is the currently logged in user |
|
||||
| **`req`** | The Express `request` object with additional `user` property, which is the currently logged in user |
|
||||
| **`id`** | `id` of document requested to delete |
|
||||
|
||||
**Example:**
|
||||
@@ -173,7 +173,7 @@ If the Collection is [used to access the Payload Admin panel](/docs/admin/overvi
|
||||
|
||||
| Option | Description |
|
||||
| --------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
|
||||
### Unlock
|
||||
|
||||
@@ -183,4 +183,4 @@ Determines which users can [unlock](/docs/authentication/operations#unlock) othe
|
||||
|
||||
| Option | Description |
|
||||
| --------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
|
||||
@@ -3,7 +3,7 @@ title: Field-level Access Control
|
||||
label: Fields
|
||||
order: 30
|
||||
desc: Field-level Access Control is specified within a field's config, and allows you to define which users can create, read or update Fields.
|
||||
keywords: fields, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: fields, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
Field Access Control is specified with functions inside a field's config. All field-level Controls return a boolean value to allow or deny access for the specified operation. No field-level Access Controls support returning query constraints. All Access Control functions accept one `args` argument.
|
||||
@@ -47,7 +47,7 @@ Returns a boolean which allows or denies the ability to set a field's value when
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`data`** | The full data passed to create the document. |
|
||||
| **`siblingData`** | Immediately adjacent field data passed to create the document. |
|
||||
|
||||
@@ -59,7 +59,7 @@ Returns a boolean which allows or denies the ability to read a field's value. If
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`id`** | `id` of the document being read |
|
||||
| **`doc`** | The full document data. |
|
||||
| **`siblingData`** | Immediately adjacent field data of the document being read. |
|
||||
@@ -74,7 +74,7 @@ If `false` is returned and you attempt to update the field's value, the operatio
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`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. |
|
||||
|
||||
@@ -3,11 +3,13 @@ title: Globals Access Control
|
||||
label: Globals
|
||||
order: 40
|
||||
desc: Global-level Access Control is specified within each Global's `access` property and allows you to define which users can read or update Globals.
|
||||
keywords: globals, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: globals, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
You can define Global-level Access Control within each Global's `access` property. All Access Control functions accept one `args` argument.
|
||||
|
||||
\*\*Available argument properties:
|
||||
|
||||
## Available Controls
|
||||
|
||||
| Function | Allows/Denies Access |
|
||||
@@ -45,7 +47,7 @@ Returns a boolean result or optionally a [query constraint](/docs/queries/overvi
|
||||
|
||||
| Option | Description |
|
||||
| --------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
|
||||
### Update
|
||||
|
||||
@@ -55,5 +57,5 @@ Returns a boolean result or optionally a [query constraint](/docs/queries/overvi
|
||||
|
||||
| Option | Description |
|
||||
| ---------- | -------------------------------------------------------------------------- |
|
||||
| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |
|
||||
| **`req`** | The Express `request` object containing the currently authenticated `user` |
|
||||
| **`data`** | The data passed to update the global with. |
|
||||
|
||||
@@ -3,7 +3,7 @@ title: Access Control
|
||||
label: Overview
|
||||
order: 10
|
||||
desc: Payload makes it simple to define and manage access control. By declaring roles, you can set permissions and restrict what your users can interact with.
|
||||
keywords: overview, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: overview, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
Access control within Payload is extremely powerful while remaining easy and intuitive to manage. Declaring who should have access to what documents is no more complex than writing a simple JavaScript function that either returns a `boolean` or a [`query`](/docs/queries/overview) constraint to restrict which documents users can interact with.
|
||||
@@ -19,9 +19,9 @@ Access control within Payload is extremely powerful while remaining easy and int
|
||||
- Restricting a `User` to only be able to see their own `Order`(s), but no others
|
||||
- Allowing `User`s that belong to a certain `Organization` to access only that `Organization`'s `Resource`s
|
||||
|
||||
## Default Settings
|
||||
### Default Settings
|
||||
|
||||
**By default, all Collections and Globals require that a user is logged in to be able to interact in any way.** The default Access Control function evaluates the `user` from the `req` and returns `true` if a user is logged in, and `false` if not.
|
||||
**By default, all Collections and Globals require that a user is logged in to be able to interact in any way.** The default Access Control function evaluates the `user` from the Express `req` and returns `true` if a user is logged in, and `false` if not.
|
||||
|
||||
**Default Access function:**
|
||||
|
||||
@@ -34,18 +34,17 @@ const defaultPayloadAccess = ({ req: { user } }) => {
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
**Note:**
|
||||
|
||||
In the Local API, all Access Control functions are skipped by default, allowing your server to do
|
||||
whatever it needs. But, you can opt back in by setting the option
|
||||
<strong>
|
||||
overrideAccess
|
||||
</strong>
|
||||
{' '}
|
||||
to <strong>false</strong>.
|
||||
|
||||
**overrideAccess**
|
||||
|
||||
to **false**.
|
||||
</Banner>
|
||||
|
||||
## Access Control Types
|
||||
### Access Control Types
|
||||
|
||||
You can manage access within Payload on three different levels:
|
||||
|
||||
@@ -53,32 +52,32 @@ You can manage access within Payload on three different levels:
|
||||
- [Fields](/docs/access-control/fields)
|
||||
- [Globals](/docs/access-control/globals)
|
||||
|
||||
## When Access Control is Executed
|
||||
### When Access Control is Executed
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
**Note:**
|
||||
|
||||
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
|
||||
#### 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
|
||||
#### 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
|
||||
### 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>,
|
||||
**Important:**
|
||||
|
||||
When your access control functions are executed via the **access** operation, the{' '}
|
||||
**id** and **data** arguments will be **undefined**,
|
||||
because Payload is executing your functions without referencing a specific document.
|
||||
</Banner>
|
||||
|
||||
|
||||
54
docs/admin/bundlers.mdx
Normal file
54
docs/admin/bundlers.mdx
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: Bundlers
|
||||
label: Bundlers
|
||||
order: 60
|
||||
desc: Bundlers are used to bundle the code that serves Payload's Admin Panel.
|
||||
---
|
||||
|
||||
Payload has two official bundlers, the [Webpack Bundler](/docs/admin/webpack) and the [Vite Bundler](/docs/admin/vite). You must install a bundler to use the admin panel.
|
||||
|
||||
##### Install a bundler
|
||||
|
||||
Webpack (recommended):
|
||||
|
||||
```text
|
||||
yarn add @payloadcms/bundler-webpack
|
||||
```
|
||||
|
||||
Vite (beta):
|
||||
|
||||
```text
|
||||
yarn add @payloadcms/bundler-vite
|
||||
```
|
||||
|
||||
##### Configure the bundler
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
|
||||
import { buildConfig } from 'payload/config'
|
||||
import { webpackBundler } from '@payloadcms/bundler-webpack'
|
||||
// import { viteBundler } from '@payloadcms/bundler-vite'
|
||||
|
||||
export default buildConfig({
|
||||
// highlight-start
|
||||
admin: {
|
||||
bundler: webpackBundler() // or viteBundler()
|
||||
},
|
||||
// highlight-end
|
||||
})
|
||||
```
|
||||
|
||||
### What are bundlers?
|
||||
|
||||
At their core, a bundler's main goal is to take a bunch of files and turn them into a few optimized files that you ship to the browser. The admin UI has a root `index.html` entry point, and from there the bundler traverses the dependency tree, bundling all of the files that are required from that point on.
|
||||
|
||||
Since the bundled file is sent to the browser, it can't include any server-only code. You will need to remove any server-only code from your admin UI before bundling it. You can learn more about [excluding server code](/docs/admin/excluding-server-code) section.
|
||||
|
||||
<Banner type="warning">
|
||||
**Using environment variables in the admin UI**
|
||||
|
||||
Bundles should not contain sensitive information. By default, Payload
|
||||
excludes env variables from the bundle. If you need to use env variables in your payload config,
|
||||
you need to prefix them with `PAYLOAD_PUBLIC_` to make them available to the client-side code.
|
||||
</Banner>
|
||||
@@ -3,43 +3,39 @@ title: Swap in your own React components
|
||||
label: Custom Components
|
||||
order: 20
|
||||
desc: Fully customize your Admin Panel by swapping in your own React components. Add fields, remove views, update routes and change functions to sculpt your perfect Dashboard.
|
||||
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
The [Payload Admin](./overview) panel is designed to be as minimal and straightforward as possible to allow easy customization and control. In order for Payload to support this level of customization without introducing versioning or future-proofing issues, Payload provides a pattern for you to supply your own React components via your Payload config.
|
||||
While designing the Payload Admin panel, we determined it should be as minimal and straightforward as possible to allow easy customization and control. There are many times where you may want to completely control how a whole view or a field works. You might even want to add in new views entirely. In order for Payload to support this level of customization without introducing versioning / future-proofing issues, Payload provides for a pattern to supply your own React components via your Payload config.
|
||||
|
||||
<Banner type="warning">
|
||||
All Custom Components in the Admin Panel are [React Server Components](https://react.dev/reference/rsc/server-components). This means they are rendered on the server
|
||||
</Banner>
|
||||
|
||||
To swap in your own React component, first, consult the list of available components below. Determine the scope that corresponds to what you are trying to accomplish, and then author your React component accordingly.
|
||||
To swap in your own React component, first, consult the list of available component overrides below. Determine the scope that corresponds to what you are trying to accomplish, and then author your React component accordingly.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
Custom components will automatically be provided with all props that the default component
|
||||
normally accepts.
|
||||
**Tip:**
|
||||
|
||||
Custom components will automatically be provided with all props that the default component normally
|
||||
accepts.
|
||||
</Banner>
|
||||
|
||||
## Base Component Overrides
|
||||
### Base Component Overrides
|
||||
|
||||
You can override a set of admin panel-wide components by providing a component to your base Payload config's `admin.components` property. The following options are available:
|
||||
|
||||
| Path | Description |
|
||||
|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`Nav`** | Contains the sidebar / mobile menu in its entirety. |
|
||||
| **`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. |
|
||||
| **`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/main/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. |
|
||||
| **`logout.Button`** | A custom React component. |
|
||||
| **`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. |
|
||||
| **`providers`** | Define your own provider components that will wrap the [Admin Panel](./overview). [More](#custom-providers) |
|
||||
| **`actions`** | Array of custom components to be rendered in the [Admin Panel](./overview) header, providing additional interactivity and functionality. |
|
||||
| **`views`** | Override or create new [Views](./views) within the [Admin Panel](./overview). |
|
||||
| Path | Description |
|
||||
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`Nav`** | Contains the sidebar / mobile menu in its entirety. |
|
||||
| **`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. |
|
||||
| **`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/main/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. |
|
||||
| **`logout.Button`** | A custom React component. |
|
||||
| **`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. |
|
||||
| **`providers`** | Define your own provider components that will wrap the Payload Admin UI. [More](#custom-providers) |
|
||||
| **`actions`** | Array of custom components to be rendered in the Payload Admin UI header, providing additional interactivity and functionality. |
|
||||
| **`views`** | Override or create new views within the Payload Admin UI. [More](#views) |
|
||||
|
||||
Here is a full example showing how to swap some of these components for your own.
|
||||
|
||||
@@ -77,7 +73,76 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
## Collections
|
||||
#### Views
|
||||
|
||||
You can easily swap entire views with your own by using the `admin.components.views` property. At the root level, Payload renders the following views by default, all of which can be overridden:
|
||||
|
||||
| Property | Description |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`Account`** | The Account view is used to show the currently logged in user's Account page. |
|
||||
| **`Dashboard`** | The main landing page of the Admin panel. |
|
||||
|
||||
To swap out any of these views, simply pass in your custom component to the `admin.components.views` property of your Payload config. For example:
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Account: MyCustomAccountView,
|
||||
Dashboard: MyCustomDashboardView,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
For more granular control, pass a configuration object instead. Each view corresponds to its own `<Route />` component in [React Router v5](https://v5.reactrouter.com). Payload exposes all of the properties of React Router:
|
||||
|
||||
| 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._
|
||||
|
||||
#### Adding new views
|
||||
|
||||
To add a _new_ view to the Admin Panel, simply add another key to the `views` object with at least a `path` and `Component` property. For example:
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
MyCustomView: {
|
||||
Component: MyCustomView,
|
||||
path: '/my-custom-view',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
**Note:**
|
||||
|
||||
Routes are cascading. This means that unless explicitly given the `exact` property, they will match on URLs that simply _start_ with the route's path. This is helpful when creating catch-all routes in your application. Alternatively, you could define your nested route _before_ your parent route.
|
||||
</Banner>
|
||||
|
||||
_For more examples regarding how to customize components, look at the following [examples](https://github.com/payloadcms/payload/tree/main/test/admin/components)._
|
||||
|
||||
For help on how to build your own custom view components, see [building a custom view component](#building-a-custom-view-component).
|
||||
|
||||
### Collections
|
||||
|
||||
You can override components on a collection-by-collection basis via the `admin.components` property.
|
||||
|
||||
@@ -91,33 +156,25 @@ You can override components on a collection-by-collection basis via the `admin.c
|
||||
| **`edit.SaveDraftButton`** | Replace the default `Save Draft` button with a custom component. Drafts must be enabled and autosave must be disabled. |
|
||||
| **`edit.PublishButton`** | Replace the default `Publish` button with a custom component. Drafts must be enabled. |
|
||||
| **`edit.PreviewButton`** | Replace the default `Preview` button with a custom component. |
|
||||
| **`views`** | Override or create new [Views](./views) within the [Admin Panel](./overview). |
|
||||
| **`views`** | Override or create new views within the Payload Admin UI. [More](#collection-views) |
|
||||
|
||||
Here is a full example showing how to swap some of these components for your own:
|
||||
|
||||
`CustomSaveButton.tsx`
|
||||
`Collection.ts`
|
||||
|
||||
```tsx
|
||||
import { CustomSaveButtonProps } from 'payload/types'
|
||||
import * as React from 'react'
|
||||
|
||||
const CustomSaveButton: CustomSaveButtonProps = ({
|
||||
DefaultButton,
|
||||
label,
|
||||
save
|
||||
}) => {
|
||||
return (
|
||||
<DefaultButton
|
||||
label={label}
|
||||
save={save}
|
||||
/>
|
||||
)
|
||||
import {
|
||||
CustomSaveButtonProps,
|
||||
CustomSaveDraftButtonProps,
|
||||
CustomPublishButtonType,
|
||||
CustomPreviewButtonProps,
|
||||
} from 'payload/types'
|
||||
|
||||
export const CustomSaveButton: CustomSaveButtonProps = ({ DefaultButton, label, save }) => {
|
||||
return <DefaultButton label={label} save={save} />
|
||||
}
|
||||
```
|
||||
|
||||
`CustomSaveDraftButton.tsx`
|
||||
|
||||
```tsx
|
||||
import { CustomSaveDraftButtonProps } from 'payload/types'
|
||||
|
||||
export const CustomSaveDraftButton: CustomSaveDraftButtonProps = ({
|
||||
DefaultButton,
|
||||
@@ -125,41 +182,17 @@ export const CustomSaveDraftButton: CustomSaveDraftButtonProps = ({
|
||||
label,
|
||||
saveDraft,
|
||||
}) => {
|
||||
return (
|
||||
<DefaultButton
|
||||
label={label}
|
||||
disabled={disabled}
|
||||
saveDraft={saveDraft}
|
||||
/>
|
||||
)
|
||||
return <DefaultButton label={label} disabled={disabled} saveDraft={saveDraft} />
|
||||
}
|
||||
```
|
||||
|
||||
`CustomPublishButton.tsx`
|
||||
|
||||
```tsx
|
||||
import { CustomPreviewButtonProps } from 'payload/types'
|
||||
|
||||
export const CustomPublishButton: CustomPublishButtonProps = ({
|
||||
export const CustomPublishButton: CustomPublishButtonType = ({
|
||||
DefaultButton,
|
||||
disabled,
|
||||
label,
|
||||
publish,
|
||||
}) => {
|
||||
return (
|
||||
<DefaultButton
|
||||
label={label}
|
||||
disabled={disabled}
|
||||
publish={publish}
|
||||
/>
|
||||
)
|
||||
return <DefaultButton label={label} disabled={disabled} publish={publish} />
|
||||
}
|
||||
```
|
||||
|
||||
`CustomPreviewButton`
|
||||
|
||||
```tsx
|
||||
import { CustomPreviewButtonProps } from 'payload/types'
|
||||
|
||||
export const CustomPreviewButton: CustomPreviewButtonProps = ({
|
||||
DefaultButton,
|
||||
@@ -167,25 +200,8 @@ export const CustomPreviewButton: CustomPreviewButtonProps = ({
|
||||
label,
|
||||
preview,
|
||||
}) => {
|
||||
return (
|
||||
<DefaultButton
|
||||
label={label}
|
||||
disabled={disabled}
|
||||
preview={preview}
|
||||
/>
|
||||
)
|
||||
return <DefaultButton label={label} disabled={disabled} preview={preview} />
|
||||
}
|
||||
```
|
||||
|
||||
`collection.ts`
|
||||
|
||||
```tsx
|
||||
import * as React from 'react'
|
||||
|
||||
import { CustomSaveButton } from './CustomSaveButton'
|
||||
import { CustomSaveDraftButton } from './CustomSaveDraftButton'
|
||||
import { CustomPublishButton } from './CustomPublishButton'
|
||||
import { CustomPreviewButton } from './CustomPreviewButton'
|
||||
|
||||
export const MyCollection: SanitizedCollectionConfig = {
|
||||
slug: 'my-collection',
|
||||
@@ -198,11 +214,87 @@ export const MyCollection: SanitizedCollectionConfig = {
|
||||
PreviewButton: CustomPreviewButton,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Collection views
|
||||
|
||||
To swap out entire views on collections, you can use the `admin.components.views` property on the collection's config. Payload renders the following views by default, all of which can be overridden:
|
||||
|
||||
| Property | Description |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`Edit`** | The Edit view is used to edit a single document for a given collection. |
|
||||
| **`List`** | The List view is used to show a list of documents for a given collection. |
|
||||
|
||||
To swap out any of these views, simply pass in your custom component to the `admin.components.views` property of your Payload config. This will replace the entire view, including the page breadcrumbs, title, tabs, etc, _as well as all nested routes_.
|
||||
|
||||
```ts
|
||||
// Collection.ts
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Edit: MyCustomEditView,
|
||||
List: MyCustomListView,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Globals
|
||||
_For help on how to build your own custom view components, see [building a custom view component](#building-a-custom-view-component)._
|
||||
|
||||
**Customizing Nested Views within 'Edit' in Collections**
|
||||
|
||||
The `Edit` view in collections consists of several nested views, each serving a unique purpose. You can customize these nested views using the `admin.components.views.Edit` property in the collection's configuration. This approach allows you to replace specific nested views while keeping the overall structure of the `Edit` view intact, including the page breadcrumbs, title, tabs, etc.
|
||||
|
||||
Here's an example of how you can customize nested views within the `Edit` view in collections, including the use of the `actions` property:
|
||||
|
||||
```ts
|
||||
// Collection.ts
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Edit: {
|
||||
Default: {
|
||||
Component: MyCustomDefaultTab,
|
||||
actions: [CollectionEditButton], // Custom actions for the default edit view
|
||||
},
|
||||
API: {
|
||||
Component: MyCustomAPIView,
|
||||
actions: [CollectionAPIButton], // Custom actions for API view
|
||||
},
|
||||
LivePreview: {
|
||||
Component: MyCustomLivePreviewView,
|
||||
actions: [CollectionLivePreviewButton], // Custom actions for Live Preview
|
||||
},
|
||||
Version: {
|
||||
Component: MyCustomVersionView,
|
||||
actions: [CollectionVersionButton], // Custom actions for Version view
|
||||
},
|
||||
Versions: {
|
||||
Component: MyCustomVersionsView,
|
||||
actions: [CollectionVersionsButton], // Custom actions for Versions view
|
||||
},
|
||||
},
|
||||
List: {
|
||||
actions: [CollectionListButton],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Adding New Tabs to 'Edit' View**
|
||||
|
||||
You can also add _new_ tabs to the `Edit` view by adding another key to the `components.views.Edit[key]` object with a `path` and `Component` property. See [Custom Tabs](#custom-tabs) for more information.
|
||||
|
||||
### Globals
|
||||
|
||||
As with Collections, you can override components on a global-by-global basis via the `admin.components` property.
|
||||
|
||||
@@ -212,20 +304,360 @@ As with Collections, you can override components on a global-by-global basis via
|
||||
| **`elements.SaveDraftButton`** | Replace the default `Save Draft` button with a custom component. Drafts must be enabled and autosave must be disabled. |
|
||||
| **`elements.PublishButton`** | Replace the default `Publish` button with a custom component. Drafts must be enabled. |
|
||||
| **`elements.PreviewButton`** | Replace the default `Preview` button with a custom component. |
|
||||
| **`views`** | Override or create new [Views](./views) within the [Admin Panel](./overview). |
|
||||
| **`views`** | Override or create new views within the Payload Admin UI. [More](#global-views) |
|
||||
|
||||
#### Global views
|
||||
|
||||
To swap out views for globals, you can use the `admin.components.views` property on the global's config. Payload renders the following views by default, all of which can be overridden:
|
||||
|
||||
| Property | Description |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`Edit`** | The Edit view is used to edit a single document for a given Global. |
|
||||
|
||||
To swap out any of these views, simply pass in your custom component to the `admin.components.views` property of your Payload config. This will replace the entire view, including the page breadcrumbs, title, and tabs, _as well as all nested views_.
|
||||
|
||||
```ts
|
||||
// Global.ts
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Edit: MyCustomEditView,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
_For help on how to build your own custom view components, see [building a custom view component](#building-a-custom-view-component)._
|
||||
|
||||
**Customizing Nested Views within 'Edit' in Globals**
|
||||
|
||||
Similar to collections, Globals allow for detailed customization within the `Edit` view. This includes the ability to swap specific nested views while maintaining the overall structure of the `Edit` view. You can use the `admin.components.views.Edit` property in the Globals configuration to achieve this, and this will only replace the nested view, leaving the page breadcrumbs, title, and tabs intact.
|
||||
|
||||
Here's how you can customize nested views within the `Edit` view in Globals, including the use of the `actions` property:
|
||||
|
||||
```ts
|
||||
// Global.ts
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Edit: {
|
||||
Default: {
|
||||
Component: MyCustomGlobalDefaultTab,
|
||||
actions: [GlobalEditButton], // Custom actions for the default edit view
|
||||
},
|
||||
API: {
|
||||
Component: MyCustomGlobalAPIView,
|
||||
actions: [GlobalAPIButton], // Custom actions for API view
|
||||
},
|
||||
LivePreview: {
|
||||
Component: MyCustomGlobalLivePreviewView,
|
||||
actions: [GlobalLivePreviewButton], // Custom actions for Live Preview
|
||||
},
|
||||
Version: {
|
||||
Component: MyCustomGlobalVersionView,
|
||||
actions: [GlobalVersionButton], // Custom actions for Version view
|
||||
},
|
||||
Versions: {
|
||||
Component: MyCustomGlobalVersionsView,
|
||||
actions: [GlobalVersionsButton], // Custom actions for Versions view
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
You can also add _new_ tabs to the `Edit` view by adding another key to the `components.views.Edit[key]` object with a `path` and `Component` property. See [Custom Tabs](#custom-tabs) for more information.
|
||||
|
||||
### Custom Tabs
|
||||
|
||||
You can easily swap individual collection or global edit views. To do this, pass an _object_ to the `admin.components.views.Edit` property of the config. Payload renders the following views by default, all of which can be overridden:
|
||||
|
||||
| Property | Description |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`Default`** | The Default view is the primary view in which your document is edited. |
|
||||
| **`Versions`** | The Versions view is used to view the version history of a single document. [More details](../versions) |
|
||||
| **`Version`** | The Version view is used to view a single version of a single document for a given collection. [More details](../versions). |
|
||||
| **`API`** | The API view is used to display the REST API JSON response for a given document. |
|
||||
| **`LivePreview`** | The LivePreview view is used to display the Live Preview interface. [More details](../live-preview) |
|
||||
|
||||
Here is an example:
|
||||
|
||||
```ts
|
||||
// Collection.ts or Global.ts
|
||||
export const MyCollection: SanitizedCollectionConfig = {
|
||||
slug: 'my-collection',
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Edit: { // You can also define `components.views.Edit` as a component, this will override _all_ nested views
|
||||
Default: MyCustomDefaultTab,
|
||||
Versions: MyCustomVersionsTab,
|
||||
Version: MyCustomVersionTab,
|
||||
API: MyCustomAPITab,
|
||||
LivePreview: MyCustomLivePreviewTab,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
To add a _new_ tab to the `Edit` view, simply add another key to `components.views.Edit[key]` with at least a `path` and `Component` property. For example:
|
||||
|
||||
```ts
|
||||
// `Collection.ts` or `Global.ts`
|
||||
export const MyCollection: SanitizedCollectionConfig = {
|
||||
slug: 'my-collection',
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Edit: {
|
||||
MyCustomTab: {
|
||||
Component: MyCustomTab,
|
||||
path: '/my-custom-tab',
|
||||
// You an swap the entire tab component out for your own
|
||||
Tab: MyCustomTab
|
||||
},
|
||||
AnotherCustomView: {
|
||||
Component: AnotherCustomView,
|
||||
path: '/another-custom-view',
|
||||
// Or you can use the default tab component and just pass in your own label and href
|
||||
Tab: {
|
||||
label: 'Another Custom View',
|
||||
href: '/another-custom-view',
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Building a custom view component
|
||||
|
||||
Your custom view 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">
|
||||
**Note:**
|
||||
|
||||
It's up to you to secure your custom views. If your view requires a user to be logged in or to
|
||||
have certain access rights, you should handle that within your view component yourself.
|
||||
</Banner>
|
||||
|
||||
#### Example
|
||||
|
||||
You can find examples of custom views in the [Payload source code `/test/admin/components/views` folder](https://github.com/payloadcms/payload/tree/main/test/admin/components/views). There, you'll find two custom views:
|
||||
|
||||
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 views of your own, take a look at the `admin.components.views` property of the [Payload test admin config](https://github.com/payloadcms/payload/blob/main/test/admin/config.ts).
|
||||
|
||||
### Fields
|
||||
|
||||
All Payload fields support the ability to swap in your own React components. So, for example, instead of rendering a default Text input, you might need to render a color picker that provides the editor with a custom color picker interface to restrict the data entered to colors only.
|
||||
|
||||
<Banner type="success">
|
||||
**Tip:**
|
||||
|
||||
Don't see a built-in field type that you need? Build it! Using a combination of custom validation
|
||||
and custom components, you can override the entirety of how a component functions within the admin
|
||||
panel and effectively create your own field type.
|
||||
</Banner>
|
||||
|
||||
**Fields support the following custom components:**
|
||||
|
||||
| Component | Description |
|
||||
| ------------ | --------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`Filter`** | Override the text input that is presented in the `List` view when a user is filtering documents by the customized field. |
|
||||
| **`Cell`** | Used in the `List` view's table to represent a table-based preview of the data stored in the field. [More](#cell-component) |
|
||||
| **`Field`** | Swap out the field itself within all `Edit` views. [More](#field-component) |
|
||||
|
||||
As an alternative to replacing the entire Field component, you may want to keep the majority of the default Field component and only swap components within. This allows you to replace the **`Label`** or **`Error`** within a field component or add additional components inside the field with **`beforeInput`** or **`afterInput`**. **`beforeInput`** and **`afterInput`** are allowed in any fields that don't contain other fields, except [UI](/docs/fields/ui) and [Rich Text](/docs/fields/rich-text).
|
||||
|
||||
| Component | Description |
|
||||
| ----------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| **`Label`** | Override the default Label in the Field Component. [More](#label-component) |
|
||||
| **`Error`** | Override the default Error in the Field Component. [More](#error-component) |
|
||||
| **`beforeInput`** | An array of elements that will be added before `input`/`textarea` elements. [More](#afterinput-and-beforeinput) |
|
||||
| **`afterInput`** | An array of elements that will be added after `input`/`textarea` elements. [More](#afterinput-and-beforeinput) |
|
||||
|
||||
## Cell Component
|
||||
|
||||
These are the props that will be passed to your custom Cell to use in your own components.
|
||||
|
||||
| Property | Description |
|
||||
| ---------------- | ----------------------------------------------------------------- |
|
||||
| **`field`** | An object that includes the field configuration. |
|
||||
| **`colIndex`** | A unique number for the column in the list. |
|
||||
| **`collection`** | An object with the config of the collection that the field is in. |
|
||||
| **`cellData`** | The data for the field that the cell represents. |
|
||||
| **`rowData`** | An object with all the field values for the row. |
|
||||
|
||||
#### Example
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
import type { Props } from 'payload/components/views/Cell'
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'custom-cell'
|
||||
|
||||
const CustomCell: React.FC<Props> = (props) => {
|
||||
const { field, colIndex, collection, cellData, rowData } = props
|
||||
|
||||
return <span className={baseClass}>{cellData}</span>
|
||||
}
|
||||
```
|
||||
|
||||
## Field Component
|
||||
|
||||
When writing your own custom components you can make use of a number of hooks to set data, get reactive changes to other fields, get the id of the document or interact with a context from a custom provider.
|
||||
|
||||
### 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 `useField` hook as follows:
|
||||
|
||||
```tsx
|
||||
import { useField } from 'payload/components/forms'
|
||||
|
||||
const CustomTextField: React.FC<{ path: string }> = ({ path }) => {
|
||||
// highlight-start
|
||||
const { value, setValue } = useField<string>({ path })
|
||||
// highlight-end
|
||||
|
||||
|
||||
return <input onChange={(e) => setValue(e.target.value)} value={value} />
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
For more information regarding the hooks that are available to you while you build custom
|
||||
components, including the **useField** hook, [click here](/docs/admin/hooks).
|
||||
</Banner>
|
||||
|
||||
## Label Component
|
||||
|
||||
These are the props that will be passed to your custom Label.
|
||||
|
||||
| Property | Description |
|
||||
| ---------------- | ---------------------------------------------------------------- |
|
||||
| **`htmlFor`** | Property used to set `for` attribute for label. |
|
||||
| **`label`** | Label value provided in field, it can be used with i18n. |
|
||||
| **`required`** | A boolean value that represents if the field is required or not. |
|
||||
|
||||
#### Example
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { getTranslation } from 'payload/utilities/getTranslation'
|
||||
|
||||
type Props = {
|
||||
htmlFor?: string
|
||||
label?: Record<string, string> | false | string
|
||||
required?: boolean
|
||||
}
|
||||
|
||||
const CustomLabel: React.FC<Props> = (props) => {
|
||||
const { htmlFor, label, required = false } = props
|
||||
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
if (label) {
|
||||
return (<span>
|
||||
{getTranslation(label, i18n)}
|
||||
{required && <span className="required">*</span>}
|
||||
</span>);
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
```
|
||||
|
||||
## Error Component
|
||||
|
||||
These are the props that will be passed to your custom Error.
|
||||
|
||||
| Property | Description |
|
||||
| ---------------- | ------------------------------------------------------------- |
|
||||
| **`message`** | The error message. |
|
||||
| **`showError`** | A boolean value that represents if the error should be shown. |
|
||||
|
||||
#### Example
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
|
||||
type Props = {
|
||||
message: string
|
||||
showError?: boolean
|
||||
}
|
||||
|
||||
const CustomError: React.FC<Props> = (props) => {
|
||||
const { message, showError } = props
|
||||
|
||||
if (showError) {
|
||||
return <p style={{color: 'red'}}>{message}</p>
|
||||
} else return null;
|
||||
}
|
||||
```
|
||||
|
||||
## afterInput and beforeInput
|
||||
|
||||
With these properties you can add multiple components before and after the input element. For example, you can add an absolutely positioned button to clear the current field value.
|
||||
|
||||
#### Example
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
import { Field } from 'payload/types'
|
||||
|
||||
import './style.scss'
|
||||
|
||||
const ClearButton: React.FC = () => {
|
||||
return <button onClick={() => {/* ... */}}>X</button>
|
||||
}
|
||||
|
||||
const titleField: Field = {
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
afterInput: [ClearButton]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default titleField;
|
||||
```
|
||||
|
||||
## 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](./overview). Within your config add `admin.components.providers`, these can be used to share context or provide other custom functionality. See the [React Context](https://reactjs.org/docs/context.html) docs to learn more.
|
||||
As your admin customizations gets more complex you may want to share state between fields or other components. You can add custom providers to 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
|
||||
**Reminder:** Don't forget to pass the **children** prop through the provider
|
||||
component for the admin UI to show
|
||||
</Banner>
|
||||
|
||||
## Styling Custom Components
|
||||
### 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-in 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:
|
||||
|
||||
@@ -233,23 +665,23 @@ To make use of Payload SCSS variables / mixins to use directly in your own compo
|
||||
@import '~payload/scss';
|
||||
```
|
||||
|
||||
## Getting the current language
|
||||
### Getting the current language
|
||||
|
||||
When developing custom components you can support multiple languages to be consistent with Payload's i18n support. The best way to do this is to add your translation resources to the [i18n configuration](https://payloadcms.com/docs/configuration/i18n) and import `useTranslation` from `@payloadcms/ui/providers/Translation` in your components.
|
||||
When developing custom components you can support multiple languages to be consistent with Payload's i18n support. The best way to do this is to add your translation resources to the [i18n configuration](https://payloadcms.com/docs/configuration/i18n) and import `useTranslation` from `react-i18next` in your components.
|
||||
|
||||
For example:
|
||||
|
||||
```tsx
|
||||
import { useTranslation } from '@payloadcms/ui/providers/Translation'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const CustomComponent: React.FC = () => {
|
||||
// highlight-start
|
||||
const { t, i18n } = useTranslation()
|
||||
const { t, i18n } = useTranslation('namespace1')
|
||||
// highlight-end
|
||||
|
||||
return (
|
||||
<ul>
|
||||
<li>{t('namespace1:key', { variable: 'value' })}</li>
|
||||
<li>{t('key', { variable: 'value' })}</li>
|
||||
<li>{t('namespace2:key', { variable: 'value' })}</li>
|
||||
<li>{i18n.language}</li>
|
||||
</ul>
|
||||
@@ -257,7 +689,7 @@ const CustomComponent: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
## Getting the current locale
|
||||
### Getting the current locale
|
||||
|
||||
In any custom component you can get the selected locale with `useLocale` hook. `useLocale` returns the full locale object, consisting of a `label`, `rtl`(right-to-left) property, and then `code`. Here is a simple example:
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Customizing CSS & SCSS
|
||||
label: Customizing CSS
|
||||
order: 60
|
||||
order: 40
|
||||
desc: Customize your Payload admin panel further by adding your own CSS or SCSS style sheet to the configuration, powerful theme and design options are waiting for you.
|
||||
keywords: admin, css, scss, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: admin, css, scss, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
## Adding your own CSS / SCSS
|
||||
### Adding your own CSS / SCSS
|
||||
|
||||
You can add your own CSS by providing your base Payload config with a path to your own CSS or SCSS. Customize the styling of any part of the Payload dashboard as necessary.
|
||||
|
||||
@@ -25,7 +25,7 @@ const config = buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
## Overriding built-in styles
|
||||
### Overriding built-in styles
|
||||
|
||||
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.
|
||||
|
||||
@@ -41,7 +41,7 @@ You can find the built-in Payload CSS variables within [`./src/admin/scss/app.sc
|
||||
- Fonts
|
||||
- Horizontal gutter
|
||||
|
||||
### Dark mode
|
||||
#### Dark mode
|
||||
|
||||
<Banner type="warning">
|
||||
If you're overriding colors or theme elevations, make sure to consider how your changes will
|
||||
|
||||
@@ -8,8 +8,8 @@ desc: NEEDS TO BE WRITTEN
|
||||
## Admin environment vars
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
**Important:**
|
||||
|
||||
Be careful about what variables you provide to your client-side code. Analyze every single one to
|
||||
make sure that you're not accidentally leaking anything that an attacker could exploit. Only keys
|
||||
that are safe for anyone to read in plain text should be provided to your Admin panel.
|
||||
|
||||
206
docs/admin/excluding-server-code.mdx
Normal file
206
docs/admin/excluding-server-code.mdx
Normal file
@@ -0,0 +1,206 @@
|
||||
---
|
||||
title: Excluding server-only code from admin UI
|
||||
label: Excluding server code
|
||||
order: 70
|
||||
desc: Learn how to exclude server-only code from the Payload Admin UI bundle
|
||||
---
|
||||
|
||||
Because the Admin Panel browser bundle includes your Payload Config file, files using server-only modules need to be excluded.
|
||||
It's common for your config to rely on server only modules to perform logic in access control functions, hooks, and other contexts.
|
||||
|
||||
Any file that imports a server-only module such as `fs`, `stripe`, `authorizenet`, `nodemailer`, etc. **cannot** be included in the browser bundle.
|
||||
|
||||
#### Example Scenario
|
||||
|
||||
Say we have a collection called `Subscriptions` that has a `beforeChange` hook that creates a Stripe subscription whenever a Subscription document is created in Payload.
|
||||
|
||||
**Collection config**:
|
||||
|
||||
```ts
|
||||
// collections/Subscriptions/index.ts
|
||||
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
import createStripeSubscription from './hooks/createStripeSubscription'
|
||||
|
||||
export const Subscription: CollectionConfig = {
|
||||
slug: 'subscriptions',
|
||||
hooks: {
|
||||
beforeChange: [createStripeSubscription],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'stripeSubscriptionID',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
**Collection hook**:
|
||||
|
||||
```ts
|
||||
// collections/Subscriptions/hooks/createStripeSubscription.ts
|
||||
|
||||
// highlight-start
|
||||
import Stripe from 'stripe' // <-- server-only module
|
||||
// highlight-end
|
||||
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)
|
||||
|
||||
export const createStripeSubscription = async ({ data, operation }) => {
|
||||
if (operation === 'create') {
|
||||
const dataWithStripeID = { ...data }
|
||||
|
||||
// use Stripe to create a Stripe subscription
|
||||
const subscription = await stripe.subscriptions.create({
|
||||
// Configure the subscription accordingly
|
||||
})
|
||||
|
||||
// Automatically add the Stripe subscription ID
|
||||
// to the data that will be saved to this Subscription doc
|
||||
dataWithStripeID.stripeSubscriptionID = subscription.id
|
||||
|
||||
return dataWithStripeID
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="error">
|
||||
**Warning:**
|
||||
|
||||
The above code is NOT production-ready and should not be referenced to create Stripe
|
||||
subscriptions. Although creating a beforeChange hook is a completely valid spot to do things like
|
||||
create subscriptions, the code above is incomplete and insecure, meant for explanation purposes
|
||||
only.
|
||||
</Banner>
|
||||
|
||||
**As-is, this collection will prevent your Admin panel from bundling or loading correctly, because Stripe relies on some Node-only packages.**
|
||||
|
||||
#### How to fix this
|
||||
|
||||
You need to make sure that you use `alias`es to tell your bundler to import "safe" files vs. attempting to import any server-side code that you need to get rid of. Depending on your bundler (Webpack, Vite, etc.) the steps involved may be slightly different.
|
||||
|
||||
The basic idea is to create a file that exports an empty object, and then alias import paths of any files that import server-only modules to that empty object file.
|
||||
|
||||
This way when your bundler goes to import a file that contains server-only modules, it will instead import the empty object file, which will not break the browser bundle.
|
||||
|
||||
### Aliasing server-only modules
|
||||
|
||||
To remove files that contain server-only modules from your bundle, you can use an `alias`.
|
||||
|
||||
In the Subscriptions config file above, we are importing the hook like so:
|
||||
|
||||
```ts
|
||||
// collections/Subscriptions/index.ts
|
||||
|
||||
import createStripeSubscription from './hooks/createStripeSubscription'
|
||||
```
|
||||
|
||||
By default the browser bundle will now include all the code from that file and any files down the tree. We know that the file imports `stripe`.
|
||||
|
||||
To fix this, we need to alias the `createStripeSubscription` file to a different file that can safely be included in the browser bundle.
|
||||
|
||||
First, we will create a mock file to replace the server-only file when bundling:
|
||||
```js
|
||||
// mocks/modules.js
|
||||
|
||||
export default {}
|
||||
|
||||
/**
|
||||
* NOTE: if you are destructuring an import
|
||||
* the mock file will need to export matching
|
||||
* variables as the destructured object.
|
||||
*
|
||||
* export const namedExport = {}
|
||||
*/
|
||||
```
|
||||
|
||||
Aliasing with [Webpack](/docs/admin/webpack) can be done by:
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
|
||||
import { buildConfig } from 'payload/config'
|
||||
import { webpackBundler } from '@payloadcms/bundler-webpack'
|
||||
|
||||
import { Subscriptions } from './collections/Subscriptions'
|
||||
|
||||
const mockModulePath = path.resolve(__dirname, 'mocks/emptyObject.js')
|
||||
const fullFilePath = path.resolve(
|
||||
__dirname,
|
||||
'collections/Subscriptions/hooks/createStripeSubscription'
|
||||
)
|
||||
|
||||
export default buildConfig({
|
||||
collections: [Subscriptions],
|
||||
admin: {
|
||||
bundler: webpackBundler(),
|
||||
webpack: (config) => {
|
||||
return {
|
||||
...config,
|
||||
resolve: {
|
||||
...config.resolve,
|
||||
// highlight-start
|
||||
alias: {
|
||||
...config.resolve.alias,
|
||||
[fullFilePath]: mockModulePath,
|
||||
},
|
||||
// highlight-end
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Aliasing with [Vite](/docs/admin/vite) can be done by:
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
|
||||
import { buildConfig } from 'payload/config'
|
||||
import { viteBundler } from '@payloadcms/bundler-vite'
|
||||
|
||||
import { Subscriptions } from './collections/Subscriptions'
|
||||
|
||||
const mockModulePath = path.resolve(__dirname, 'mocks/emptyObject.js')
|
||||
|
||||
export default buildConfig({
|
||||
collections: [Subscriptions],
|
||||
admin: {
|
||||
bundler: viteBundler(),
|
||||
vite: (incomingViteConfig) => {
|
||||
const existingAliases = incomingViteConfig?.resolve?.alias || {};
|
||||
let aliasArray: { find: string | RegExp; replacement: string; }[] = [];
|
||||
|
||||
// Pass the existing Vite aliases
|
||||
if (Array.isArray(existingAliases)) {
|
||||
aliasArray = existingAliases;
|
||||
} else {
|
||||
aliasArray = Object.values(existingAliases);
|
||||
}
|
||||
|
||||
|
||||
// highlight-start
|
||||
// Add your own aliases using the find and replacement keys
|
||||
// remember, vite aliases are exact-match only
|
||||
aliasArray.push({
|
||||
find: '../server-only-module',
|
||||
replacement: path.resolve(__dirname, './path/to/browser-safe-module.js')
|
||||
});
|
||||
// highlight-end
|
||||
|
||||
return {
|
||||
...incomingViteConfig,
|
||||
resolve: {
|
||||
...(incomingViteConfig?.resolve || {}),
|
||||
alias: aliasArray,
|
||||
}
|
||||
};
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
@@ -1,194 +0,0 @@
|
||||
---
|
||||
title: Customizing Fields
|
||||
label: Customizing Fields
|
||||
order: 40
|
||||
desc:
|
||||
keywords:
|
||||
---
|
||||
|
||||
All Payload fields support the ability to swap in your own React components. So, for example, instead of rendering a default Text input, you might need to render a color picker that provides the editor with a custom color picker interface to restrict the data entered to colors only.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
Don't see a built-in field type that you need? Build it! Using a combination of custom validation
|
||||
and custom components, you can override the entirety of how a component functions within the admin
|
||||
panel and effectively create your own field type.
|
||||
</Banner>
|
||||
|
||||
**Fields support the following custom components:**
|
||||
|
||||
| Component | Description |
|
||||
| ------------ | --------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`Filter`** | Override the text input that is presented in the `List` view when a user is filtering documents by the customized field. |
|
||||
| **`Cell`** | Used in the `List` view's table to represent a table-based preview of the data stored in the field. [More](#cell-component) |
|
||||
| **`Field`** | Swap out the field itself within all `Edit` views. [More](#field-component) |
|
||||
|
||||
As an alternative to replacing the entire Field component, you may want to keep the majority of the default Field component and only swap components within. This allows you to replace the **`Label`** or **`Error`** within a field component or add additional components inside the field with **`beforeInput`** or **`afterInput`**. **`beforeInput`** and **`afterInput`** are allowed in any fields that don't contain other fields, except [UI](/docs/fields/ui) and [Rich Text](/docs/fields/rich-text).
|
||||
|
||||
| Component | Description |
|
||||
| ----------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| **`Label`** | Override the default Label in the Field Component. [More](#label-component) |
|
||||
| **`Error`** | Override the default Label in the Field Component. [More](#error-component) |
|
||||
| **`beforeInput`** | An array of elements that will be added before `input`/`textarea` elements. [More](#afterinput-and-beforeinput) |
|
||||
| **`afterInput`** | An array of elements that will be added after `input`/`textarea` elements. [More](#afterinput-and-beforeinput) |
|
||||
|
||||
## Cell Component
|
||||
|
||||
These are the props that will be passed to your custom Cell to use in your own components.
|
||||
|
||||
| Property | Description |
|
||||
| ---------------- | ----------------------------------------------------------------- |
|
||||
| **`field`** | An object that includes the field configuration. |
|
||||
| **`colIndex`** | A unique number for the column in the list. |
|
||||
| **`collection`** | An object with the config of the collection that the field is in. |
|
||||
| **`cellData`** | The data for the field that the cell represents. |
|
||||
| **`rowData`** | An object with all the field values for the row. |
|
||||
|
||||
#### Example
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
import type { Props } from 'payload/components/views/Cell'
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'custom-cell'
|
||||
|
||||
const CustomCell: React.FC<Props> = (props) => {
|
||||
const { field, colIndex, collection, cellData, rowData } = props
|
||||
|
||||
return <span className={baseClass}>{cellData}</span>
|
||||
}
|
||||
```
|
||||
|
||||
## Field Component
|
||||
|
||||
When writing your own custom components you can make use of a number of hooks to set data, get reactive changes to other fields, get the id of the document or interact with a context from a custom provider.
|
||||
|
||||
### 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 `useField` hook as follows:
|
||||
|
||||
```tsx
|
||||
import { useField } from 'payload/components/forms'
|
||||
|
||||
const CustomTextField: React.FC<{ path: string }> = ({ path }) => {
|
||||
// highlight-start
|
||||
const { value, setValue } = useField<string>({ path })
|
||||
// highlight-end
|
||||
|
||||
return <input onChange={(e) => setValue(e.target.value)} value={value} />
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
For more information regarding the hooks that are available to you while you build custom
|
||||
components, including the <strong>useField</strong> hook, [click here](/docs/admin/hooks).
|
||||
</Banner>
|
||||
|
||||
## Label Component
|
||||
|
||||
These are the props that will be passed to your custom Label.
|
||||
|
||||
| Property | Description |
|
||||
| -------------- | ---------------------------------------------------------------- |
|
||||
| **`htmlFor`** | Property used to set `for` attribute for label. |
|
||||
| **`label`** | Label value provided in field, it can be used with i18n. |
|
||||
| **`required`** | A boolean value that represents if the field is required or not. |
|
||||
|
||||
#### Example
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { getTranslation } from 'payload/utilities/getTranslation'
|
||||
|
||||
type Props = {
|
||||
htmlFor?: string
|
||||
label?: Record<string, string> | false | string
|
||||
required?: boolean
|
||||
}
|
||||
|
||||
const CustomLabel: React.FC<Props> = (props) => {
|
||||
const { htmlFor, label, required = false } = props
|
||||
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
if (label) {
|
||||
return (
|
||||
<span>
|
||||
{getTranslation(label, i18n)}
|
||||
{required && <span className="required">*</span>}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
```
|
||||
|
||||
## Error Component
|
||||
|
||||
These are the props that will be passed to your custom Error.
|
||||
|
||||
| Property | Description |
|
||||
| --------------- | ------------------------------------------------------------- |
|
||||
| **`message`** | The error message. |
|
||||
| **`showError`** | A boolean value that represents if the error should be shown. |
|
||||
|
||||
#### Example
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
|
||||
type Props = {
|
||||
message: string
|
||||
showError?: boolean
|
||||
}
|
||||
|
||||
const CustomError: React.FC<Props> = (props) => {
|
||||
const { message, showError } = props
|
||||
|
||||
if (showError) {
|
||||
return <p style={{ color: 'red' }}>{message}</p>
|
||||
} else return null
|
||||
}
|
||||
```
|
||||
|
||||
## afterInput and beforeInput
|
||||
|
||||
With these properties you can add multiple components before and after the input element. For example, you can add an absolutely positioned button to clear the current field value.
|
||||
|
||||
#### Example
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
import { Field } from 'payload/types'
|
||||
|
||||
import './style.scss'
|
||||
|
||||
const ClearButton: React.FC = () => {
|
||||
return (
|
||||
<button
|
||||
onClick={() => {
|
||||
/* ... */
|
||||
}}
|
||||
>
|
||||
X
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
const titleField: Field = {
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
afterInput: [ClearButton],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default titleField
|
||||
```
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
title: React Hooks
|
||||
label: React Hooks
|
||||
order: 50
|
||||
order: 30
|
||||
desc: Make use of all of the powerful React hooks that Payload provides.
|
||||
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
Payload provides a variety of powerful hooks that can be used within your own React components. With them, you can interface with Payload itself and build just about any type of complex customization you can think of—directly in familiar React code.
|
||||
|
||||
## useField
|
||||
### useField
|
||||
|
||||
The `useField` hook is used internally within every applicable Payload field component, and it manages sending and receiving a field's state from its parent form.
|
||||
|
||||
@@ -52,12 +52,12 @@ const {
|
||||
// The rest of your component goes here
|
||||
```
|
||||
|
||||
## useFormFields
|
||||
### useFormFields
|
||||
|
||||
There are times when a custom field component needs to have access to data from other fields, and you have a few options to do so. The `useFormFields` hook is a powerful and highly performant way to retrieve a form's field state, as well as to retrieve the `dispatchFields` method, which can be helpful for setting other fields' form states from anywhere within a form.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>This hook is great for retrieving only certain fields from form state</strong> because it
|
||||
**This hook is great for retrieving only certain fields from form state** because it
|
||||
ensures that it will only cause a rerender when the items that you ask for change.
|
||||
</Banner>
|
||||
|
||||
@@ -81,7 +81,7 @@ const MyComponent: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
## useAllFormFields
|
||||
### useAllFormFields
|
||||
|
||||
**To retrieve more than one field**, you can use the `useAllFormFields` hook. Your component will re-render when _any_ field changes, so use this hook only if you absolutely need to. Unlike the `useFormFields` hook, this hook does not accept a "selector", and it always returns an array with type of `[fields: Fields, dispatch: React.Dispatch<Action>]]`.
|
||||
|
||||
@@ -109,7 +109,7 @@ const ExampleComponent: React.FC = () => {
|
||||
};
|
||||
```
|
||||
|
||||
#### Updating other fields' values
|
||||
##### Updating other fields' values
|
||||
|
||||
If you are building a custom component, then you should use `setValue` which is returned from the `useField` hook to programmatically set your field's value. But if you're looking to update _another_ field's value, you can use `dispatchFields` returned from `useFormFields`.
|
||||
|
||||
@@ -128,13 +128,13 @@ You can send the following actions to the `dispatchFields` function.
|
||||
|
||||
To see types for each action supported within the `dispatchFields` hook, check out the Form types [here](https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/components/forms/Form/types.ts).
|
||||
|
||||
## useForm
|
||||
### useForm
|
||||
|
||||
The `useForm` hook can be used to interact with the form itself, and sends back many methods that can be used to reactively fetch form state without causing rerenders within your components each time a field is changed. This is useful if you have action-based callbacks that your components fire, and need to interact with form state _based on a user action_.
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Warning:</strong>
|
||||
<br />
|
||||
**Warning:**
|
||||
|
||||
This hook is optimized to avoid causing rerenders when fields change, and as such, its `fields`
|
||||
property will be out of date. You should only leverage this hook if you need to perform actions
|
||||
against the form in response to your users' actions. Do not rely on its returned "fields" as being
|
||||
@@ -152,7 +152,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
rows={[
|
||||
[
|
||||
{
|
||||
value: <strong><code>fields</code></strong>,
|
||||
value: "**`fields`**",
|
||||
},
|
||||
{
|
||||
value: "Deprecated. This property cannot be relied on as up-to-date.",
|
||||
@@ -163,7 +163,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>submit</code></strong>,
|
||||
value: "**`submit`**",
|
||||
},
|
||||
{
|
||||
value: "Method to trigger the form to submit",
|
||||
@@ -174,7 +174,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>dispatchFields</code></strong>,
|
||||
value: "**`dispatchFields`**",
|
||||
},
|
||||
{
|
||||
value: "Dispatch actions to the form field state",
|
||||
@@ -185,7 +185,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>validateForm</code></strong>,
|
||||
value: "**`validateForm`**",
|
||||
},
|
||||
{
|
||||
value: "Trigger a validation of the form state",
|
||||
@@ -196,10 +196,10 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>createFormData</code></strong>,
|
||||
value: "**`createFormData`**",
|
||||
},
|
||||
{
|
||||
value: <>Create a <code>multipart/form-data</code> object from the current form's state</>,
|
||||
value: "Create a `multipart/form-data` object from the current form's state",
|
||||
},
|
||||
{
|
||||
value: ''
|
||||
@@ -207,7 +207,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>disabled</code></strong>,
|
||||
value: "**`disabled`**",
|
||||
},
|
||||
{
|
||||
value: "Boolean denoting whether or not the form is disabled",
|
||||
@@ -218,7 +218,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>getFields</code></strong>,
|
||||
value: "**`getFields`**",
|
||||
},
|
||||
{
|
||||
value: 'Gets all fields from state',
|
||||
@@ -229,7 +229,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>getField</code></strong>,
|
||||
value: "**`getField`**",
|
||||
},
|
||||
{
|
||||
value: 'Gets a single field from state by path',
|
||||
@@ -240,7 +240,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>getData</code></strong>,
|
||||
value: "**`getData`**",
|
||||
},
|
||||
{
|
||||
value: 'Returns the data stored in the form',
|
||||
@@ -251,7 +251,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>getSiblingData</code></strong>,
|
||||
value: "**`getSiblingData`**",
|
||||
},
|
||||
{
|
||||
value: 'Returns form sibling data for the given field path',
|
||||
@@ -262,10 +262,10 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>setModified</code></strong>,
|
||||
value: "**`setModified`**",
|
||||
},
|
||||
{
|
||||
value: <>Set the form\'s <code>modified</code> state</>,
|
||||
value: "Set the form\'s `modified` state",
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
@@ -273,10 +273,10 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>setProcessing</code></strong>,
|
||||
value: "**`setProcessing`**",
|
||||
},
|
||||
{
|
||||
value: <>Set the form\'s <code>processing</code> state</>,
|
||||
value: "Set the form\'s `processing` state",
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
@@ -284,10 +284,10 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>setSubmitted</code></strong>,
|
||||
value: "**`setSubmitted`**",
|
||||
},
|
||||
{
|
||||
value: <>Set the form\'s <code>submitted</code> state</>,
|
||||
value: "Set the form\'s `submitted` state",
|
||||
},
|
||||
{
|
||||
value: '',
|
||||
@@ -295,7 +295,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>formRef</code></strong>,
|
||||
value: "**`formRef`**",
|
||||
},
|
||||
{
|
||||
value: 'The ref from the form HTML element',
|
||||
@@ -306,7 +306,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>reset</code></strong>,
|
||||
value: "**`reset`**",
|
||||
},
|
||||
{
|
||||
value: 'Method to reset the form to its initial state',
|
||||
@@ -317,7 +317,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>addFieldRow</code></strong>,
|
||||
value: "**`addFieldRow`**",
|
||||
},
|
||||
{
|
||||
value: "Method to add a row on an array or block field",
|
||||
@@ -326,8 +326,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
drawerTitle: 'addFieldRow',
|
||||
drawerDescription: 'A useful method to programmatically add a row to an array or block field.',
|
||||
drawerSlug: 'addFieldRow',
|
||||
drawerContent: (
|
||||
<>
|
||||
drawerContent: `
|
||||
<TableWithDrawers
|
||||
columns={[
|
||||
'Prop',
|
||||
@@ -336,7 +335,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
rows={[
|
||||
[
|
||||
{
|
||||
value: <strong><code>path</code></strong>,
|
||||
value: "**\\\`path\\\`**",
|
||||
},
|
||||
{
|
||||
value: "The path to the array or block field",
|
||||
@@ -344,7 +343,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>rowIndex</code></strong>,
|
||||
value: "**\\\`rowIndex\\\`**",
|
||||
},
|
||||
{
|
||||
value: "The index of the row to add. If omitted, the row will be added to the end of the array.",
|
||||
@@ -352,7 +351,7 @@ The `useForm` hook returns an object with the following properties: |
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>data</code></strong>,
|
||||
value: "**\\\`data\\\`**",
|
||||
},
|
||||
{
|
||||
value: "The data to add to the row",
|
||||
@@ -361,14 +360,9 @@ The `useForm` hook returns an object with the following properties: |
|
||||
]}
|
||||
/>
|
||||
|
||||
{' '}
|
||||
|
||||
<br />
|
||||
|
||||
{' '}
|
||||
|
||||
<pre>
|
||||
{`import { useForm } from "payload/components/forms";
|
||||
\`\`\`tsx
|
||||
import { useForm } from "payload/components/forms";
|
||||
|
||||
export const CustomArrayManager = () => {
|
||||
const { addFieldRow } = useForm()
|
||||
@@ -391,12 +385,13 @@ export const CustomArrayManager = () => {
|
||||
Add Row
|
||||
</button>
|
||||
)
|
||||
}`}
|
||||
</pre>
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
<p>An example config to go along with the custom component</p>
|
||||
<pre>
|
||||
{`const ExampleCollection = {
|
||||
An example config to go along with the Custom Component
|
||||
|
||||
\`\`\`tsx
|
||||
const ExampleCollection = {
|
||||
slug: "example-collection",
|
||||
fields: [
|
||||
{
|
||||
@@ -414,20 +409,19 @@ export const CustomArrayManager = () => {
|
||||
name: "customArrayManager",
|
||||
admin: {
|
||||
components: {
|
||||
Field: CustomArrayManager,
|
||||
Field: '/path/to/CustomArrayManagerField',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}`}
|
||||
</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>removeFieldRow</code></strong>,
|
||||
value: "**`removeFieldRow`**",
|
||||
},
|
||||
{
|
||||
value: "Method to remove a row from an array or block field",
|
||||
@@ -436,8 +430,7 @@ export const CustomArrayManager = () => {
|
||||
drawerTitle: 'removeFieldRow',
|
||||
drawerDescription: 'A useful method to programmatically remove a row from an array or block field.',
|
||||
drawerSlug: 'removeFieldRow',
|
||||
drawerContent: (
|
||||
<>
|
||||
drawerContent: `
|
||||
<TableWithDrawers
|
||||
columns={[
|
||||
'Prop',
|
||||
@@ -446,7 +439,7 @@ export const CustomArrayManager = () => {
|
||||
rows={[
|
||||
[
|
||||
{
|
||||
value: <strong><code>path</code></strong>,
|
||||
value: "**\\\`path\\\`**",
|
||||
},
|
||||
{
|
||||
value: "The path to the array or block field",
|
||||
@@ -454,7 +447,7 @@ export const CustomArrayManager = () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>rowIndex</code></strong>,
|
||||
value: "**\\\`rowIndex\\\`**",
|
||||
},
|
||||
{
|
||||
value: "The index of the row to remove",
|
||||
@@ -463,14 +456,10 @@ export const CustomArrayManager = () => {
|
||||
]}
|
||||
/>
|
||||
|
||||
{' '}
|
||||
|
||||
<br />
|
||||
|
||||
{' '}
|
||||
|
||||
<pre>
|
||||
{`import { useForm } from "payload/components/forms";
|
||||
\`\`\`tsx
|
||||
import { useForm } from "payload/components/forms";
|
||||
|
||||
export const CustomArrayManager = () => {
|
||||
const { removeFieldRow } = useForm()
|
||||
@@ -488,12 +477,13 @@ export const CustomArrayManager = () => {
|
||||
Remove Row
|
||||
</button>
|
||||
)
|
||||
}`}
|
||||
</pre>
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
<p>An example config to go along with the custom component</p>
|
||||
<pre>
|
||||
{`const ExampleCollection = {
|
||||
An example config to go along with the Custom Component
|
||||
|
||||
\`\`\`tsx
|
||||
const ExampleCollection = {
|
||||
slug: "example-collection",
|
||||
fields: [
|
||||
{
|
||||
@@ -511,20 +501,19 @@ export const CustomArrayManager = () => {
|
||||
name: "customArrayManager",
|
||||
admin: {
|
||||
components: {
|
||||
Field: CustomArrayManager,
|
||||
Field: '/path/to/CustomArrayManagerField',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}`}
|
||||
</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>replaceFieldRow</code></strong>,
|
||||
value: "**`replaceFieldRow`**",
|
||||
},
|
||||
{
|
||||
value: "Method to replace a row from an array or block field",
|
||||
@@ -533,8 +522,7 @@ export const CustomArrayManager = () => {
|
||||
drawerTitle: 'replaceFieldRow',
|
||||
drawerDescription: 'A useful method to programmatically replace a row from an array or block field.',
|
||||
drawerSlug: 'replaceFieldRow',
|
||||
drawerContent: (
|
||||
<>
|
||||
drawerContent: `
|
||||
<TableWithDrawers
|
||||
columns={[
|
||||
'Prop',
|
||||
@@ -543,7 +531,7 @@ export const CustomArrayManager = () => {
|
||||
rows={[
|
||||
[
|
||||
{
|
||||
value: <strong><code>path</code></strong>,
|
||||
value: "**\\\`path\\\`**",
|
||||
},
|
||||
{
|
||||
value: "The path to the array or block field",
|
||||
@@ -551,7 +539,7 @@ export const CustomArrayManager = () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>rowIndex</code></strong>,
|
||||
value: "**\\\`rowIndex\\\`**",
|
||||
},
|
||||
{
|
||||
value: "The index of the row to replace",
|
||||
@@ -559,7 +547,7 @@ export const CustomArrayManager = () => {
|
||||
],
|
||||
[
|
||||
{
|
||||
value: <strong><code>data</code></strong>,
|
||||
value: "**\\\`data\\\`**",
|
||||
},
|
||||
{
|
||||
value: "The data to replace within the row",
|
||||
@@ -568,14 +556,11 @@ export const CustomArrayManager = () => {
|
||||
]}
|
||||
/>
|
||||
|
||||
{' '}
|
||||
|
||||
<br />
|
||||
|
||||
{' '}
|
||||
|
||||
<pre>
|
||||
{`import { useForm } from "payload/components/forms";
|
||||
\`\`\`tsx
|
||||
import { useForm } from "payload/components/forms";
|
||||
|
||||
export const CustomArrayManager = () => {
|
||||
const { replaceFieldRow } = useForm()
|
||||
@@ -598,12 +583,13 @@ export const CustomArrayManager = () => {
|
||||
Replace Row
|
||||
</button>
|
||||
)
|
||||
}`}
|
||||
</pre>
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
<p>An example config to go along with the custom component</p>
|
||||
<pre>
|
||||
{`const ExampleCollection = {
|
||||
An example config to go along with the Custom Component
|
||||
|
||||
\`\`\`tsx
|
||||
const ExampleCollection = {
|
||||
slug: "example-collection",
|
||||
fields: [
|
||||
{
|
||||
@@ -621,30 +607,29 @@ export const CustomArrayManager = () => {
|
||||
name: "customArrayManager",
|
||||
admin: {
|
||||
components: {
|
||||
Field: CustomArrayManager,
|
||||
Field: '/path/to/CustomArrayManagerField',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}`}
|
||||
</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
],
|
||||
]}
|
||||
/>
|
||||
|
||||
## useCollapsible
|
||||
### useCollapsible
|
||||
|
||||
The `useCollapsible` hook allows you to control parent collapsibles:
|
||||
|
||||
| Property | Description |
|
||||
| ------------------------- | ------------------------------------------------------------------------------------------------------------ | --- |
|
||||
| **`isCollapsed`** | State of the collapsible. `true` if open, `false` if collapsed |
|
||||
| **`isVisible`** | If nested, determine if the nearest collapsible is visible. `true` if no parent is closed, `false` otherwise |
|
||||
| **`toggle`** | Toggles the state of the nearest collapsible |
|
||||
| **`isWithinCollapsible`** | Determine when you are within another collaspible | |
|
||||
| Property | Description |
|
||||
|---------------------------|--------------------------------------------------------------------------------------------------------------------|
|
||||
| **`collapsed`** | State of the collapsible. `true` if open, `false` if collapsed |
|
||||
| **`isVisible`** | If nested, determine if the nearest collapsible is visible. `true` if no parent is closed, `false` otherwise |
|
||||
| **`toggle`** | Toggles the state of the nearest collapsible |
|
||||
| **`withinCollapsible`** | Determine when you are within another collaspible | |
|
||||
|
||||
**Example:**
|
||||
|
||||
@@ -654,11 +639,10 @@ import React from 'react'
|
||||
import { useCollapsible } from 'payload/components/utilities'
|
||||
|
||||
const CustomComponent: React.FC = () => {
|
||||
const { isCollapsed, toggle } = useCollapsible()
|
||||
|
||||
const { collapsed, toggle } = useCollapsible()
|
||||
return (
|
||||
<div>
|
||||
<p className="field-type">I am {isCollapsed ? 'closed' : 'open'}</p>
|
||||
<p className="field-type">I am {collapsed ? 'closed' : 'open'}</p>
|
||||
<button onClick={toggle} type="button">
|
||||
Toggle
|
||||
</button>
|
||||
@@ -667,12 +651,12 @@ const CustomComponent: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
## useDocumentInfo
|
||||
### useDocumentInfo
|
||||
|
||||
The `useDocumentInfo` hook provides lots of information about the document currently being edited, including the following:
|
||||
|
||||
| Property | Description |
|
||||
| ------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
|---------------------------|--------------------------------------------------------------------------------------------------------------------|
|
||||
| **`collection`** | If the doc is a collection, its collection config will be returned |
|
||||
| **`global`** | If the doc is a global, its global config will be returned |
|
||||
| **`id`** | If the doc is a collection, its ID will be returned |
|
||||
@@ -707,7 +691,7 @@ const LinkFromCategoryToPosts: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
## useLocale
|
||||
### useLocale
|
||||
|
||||
In any custom component you can get the selected locale object with the `useLocale` hook. `useLocale`gives you the full locale object, consisting of a `label`, `rtl`(right-to-left) property, and then `code`. Here is a simple example:
|
||||
|
||||
@@ -728,7 +712,7 @@ const Greeting: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
## useAuth
|
||||
### useAuth
|
||||
|
||||
Useful to retrieve info about the currently logged in user as well as methods for interacting with it. It sends back an object with the following properties:
|
||||
|
||||
@@ -755,7 +739,7 @@ const Greeting: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
## useConfig
|
||||
### useConfig
|
||||
|
||||
Used to easily fetch the full Payload config.
|
||||
|
||||
@@ -771,7 +755,7 @@ const MyComponent: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
## useEditDepth
|
||||
### useEditDepth
|
||||
|
||||
Sends back how many editing levels "deep" the current component is. Edit depth is relevant while adding new documents / editing documents in modal windows and other cases.
|
||||
|
||||
@@ -787,11 +771,11 @@ const MyComponent: React.FC = () => {
|
||||
}
|
||||
```
|
||||
|
||||
## usePreferences
|
||||
### usePreferences
|
||||
|
||||
Returns methods to set and get user preferences. More info can be found [here](https://payloadcms.com/docs/admin/preferences).
|
||||
|
||||
## useTheme
|
||||
### useTheme
|
||||
|
||||
Returns the currently selected theme (`light`, `dark` or `auto`), a set function to update it and a boolean `autoMode`, used to determine if the theme value should be set automatically based on the user's device preferences.
|
||||
|
||||
@@ -805,21 +789,19 @@ const MyComponent: React.FC = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<span>
|
||||
The current theme is {theme} and autoMode is {autoMode}
|
||||
</span>
|
||||
<span>The current theme is {theme} and autoMode is {autoMode}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setTheme((prev) => (prev === 'light' ? 'dark' : 'light'))}
|
||||
onClick={() => setTheme(prev => prev === "light" ? "dark" : "light")}
|
||||
>
|
||||
Toggle theme
|
||||
</button>
|
||||
</>
|
||||
)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## useTableColumns
|
||||
### useTableColumns
|
||||
|
||||
Returns methods to manipulate table columns
|
||||
|
||||
@@ -836,21 +818,24 @@ const MyComponent: React.FC = () => {
|
||||
// highlight-end
|
||||
|
||||
return (
|
||||
<button type="button" onClick={resetColumns}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={resetColumns}
|
||||
>
|
||||
Reset columns
|
||||
</button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## useDocumentEvents
|
||||
### useDocumentEvents
|
||||
|
||||
The `useDocumentEvents` hook provides a way of subscribing to cross-document events, such as updates made to nested documents within a drawer. This hook will report document events that are outside the scope of the document currently being edited. This hook provides the following:
|
||||
|
||||
| Property | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`mostRecentUpdate`** | An object containing the most recently updated document. It contains the `entitySlug`, `id` (if collection), and `updatedAt` properties |
|
||||
| **`reportUpdate`** | A method used to report updates to documents. It accepts the same arguments as the `mostRecentUpdate` property. |
|
||||
| Property | Description |
|
||||
|---------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`mostRecentUpdate`** | An object containing the most recently updated document. It contains the `entitySlug`, `id` (if collection), and `updatedAt` properties |
|
||||
| **`reportUpdate`** | A method used to report updates to documents. It accepts the same arguments as the `mostRecentUpdate` property. |
|
||||
|
||||
**Example:**
|
||||
|
||||
@@ -860,11 +845,14 @@ import { useDocumentEvents } from 'payload/components/hooks'
|
||||
const ListenForUpdates: React.FC = () => {
|
||||
const { mostRecentUpdate } = useDocumentEvents()
|
||||
|
||||
return <span>{JSON.stringify(mostRecentUpdate)}</span>
|
||||
return (
|
||||
<span>
|
||||
{JSON.stringify(mostRecentUpdate)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="info">
|
||||
Right now the `useDocumentEvents` hook only tracks recently updated documents, but in the future
|
||||
it will track more document-related events as needed, such as document creation, deletion, etc.
|
||||
Right now the `useDocumentEvents` hook only tracks recently updated documents, but in the future it will track more document-related events as needed, such as document creation, deletion, etc.
|
||||
</Banner>
|
||||
|
||||
@@ -3,15 +3,16 @@ title: The Admin Panel
|
||||
label: Overview
|
||||
order: 10
|
||||
desc: Manage your data and customize the Admin Panel by swapping in your own React components. Create, modify or remove views, fields, styles and much more.
|
||||
keywords: admin, components, custom, customize, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: admin, components, custom, customize, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
Payload dynamically generates a beautiful, fully functional Admin Panel to manage your users and data. The Payload Admin Panel is highly performant, even with 100+ fields, and is written fully in TypeScript. It is built with [React](https://react.dev) using the [Next.js App Router](https://nextjs.org/docs/app) and fully supports [React Server Components](https://react.dev/reference/rsc/server-components) which enables the use of the [Local API](/docs/local-api/overview) on the front-end.
|
||||
Payload dynamically generates a beautiful, fully functional React admin panel to manage your data. It's extremely powerful and can be customized / extended upon easily by swapping in your own React components. You can add additional views, modify how built-in views look / work, swap out Payload branding for your client's, build your own field types and much more.
|
||||
|
||||
You can endlessly customize and extend the Admin UI by swapping your own in [Custom Components](./components) for everything from field labels to entire views. You can also modify built-in views, build your own fields, [swap out Payload branding for your own](https://payloadcms.com/blog/white-label-admin-ui), and so much more.
|
||||
The Payload Admin panel can be bundled with our officially supported [Vite](/docs/admin/vite) and [webpack](/docs/admin/webpack) bundlers or you can integrate another bundler following our adapter pattern approach.
|
||||
When bundled, it is code-split, highly performant (even with 100+ fields), and written fully in TypeScript.
|
||||
|
||||
<Banner type="success">
|
||||
The Admin Panel is meant to be simple enough to give you a starting point but not bring too much
|
||||
The Admin panel is meant to be simple enough to give you a starting point but not bring too much
|
||||
complexity, so that you can easily customize it to suit the needs of your application and your
|
||||
editors.
|
||||
</Banner>
|
||||
@@ -19,36 +20,44 @@ You can endlessly customize and extend the Admin UI by swapping your own in [Cus
|
||||
<LightDarkImage
|
||||
srcLight="https://payloadcms.com/images/docs/admin.jpg"
|
||||
srcDark="https://payloadcms.com/images/docs/admin-dark.jpg"
|
||||
alt="Admin Panel with collapsible sidebar"
|
||||
caption="Redesigned Admin Panel with a collapsible sidebar that's open by default, providing greater extensibility and enhanced horizontal real estate."
|
||||
alt="Admin panel with collapsible sidebar"
|
||||
caption="Redesigned admin panel with a collapsible sidebar that's open by default, providing greater extensibility and enhanced horizontal real estate."
|
||||
/>
|
||||
|
||||
## Admin Options
|
||||
|
||||
All high-level options for the Admin Panel are defined in your Payload config under the `admin` key:
|
||||
All options for the Admin panel are defined in your base Payload config file.
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `user` | The `slug` of a Collection that you want to be used to log in to the Admin Panel. [More](/docs/admin/overview#the-admin-user-collection) |
|
||||
| `buildPath` | Specify an absolute path for where to store the built Admin bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
|
||||
| `meta` | Base meta data to use for the Admin Panel. Included properties are `titleSuffix`, `icons`, and `openGraph`. Can be overridden on a per collection or per global basis. |
|
||||
| `disable` | If set to `true`, the entire Admin Panel will be disabled. |
|
||||
| `dateFormat` | Global date format that will be used for all dates in the Admin Panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
|
||||
| `bundler` | The bundler that you would like to use to bundle the admin panel. Officially supported bundlers: [Webpack](/docs/admin/webpack) and [Vite](/docs/admin/vite). |
|
||||
| `user` | The `slug` of a Collection that you want be used to log in to the Admin dashboard. [More](/docs/admin/overview#the-admin-user-collection) |
|
||||
| `buildPath` | Specify an absolute path for where to store the built Admin panel bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
|
||||
| `meta` | Base meta data to use for the Admin panel. Included properties are `titleSuffix`, `ogImage`, and `favicon`. |
|
||||
| `disable` | If set to `true`, the entire Admin panel will be disabled. |
|
||||
| `indexHTML` | Optionally replace the entirety of the `index.html` file used by the Admin panel. Reference the [base index.html file](https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/index.html) to ensure your replacement has the appropriate HTML elements. |
|
||||
| `css` | Absolute path to a stylesheet that you can use to override / customize the Admin panel styling. [More](/docs/admin/customizing-css). |
|
||||
| `scss` | Absolute path to a Sass variables / mixins stylesheet meant to override Payload styles to make for an easy re-skinning of the Admin panel. [More](/docs/admin/customizing-css#overriding-scss-variables). |
|
||||
| `dateFormat` | Global date format that will be used for all dates in the Admin panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
|
||||
| `avatar` | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |
|
||||
| `autoLogin` | Used to automate admin log-in for dev and demonstration convenience. [More](/docs/authentication/config). |
|
||||
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More](/docs/live-preview/overview). |
|
||||
| `components` | Component overrides that affect the entirety of the Admin Panel. [More](/docs/admin/components) |
|
||||
| `routes` | Replace built-in Admin Panel routes with your own custom routes. [More](#custom-admin-panel-routes) |
|
||||
| `components` | Component overrides that affect the entirety of the Admin panel. [More](/docs/admin/components) |
|
||||
| `webpack` | Customize the Webpack config that's used to generate the Admin panel. [More](/docs/admin/webpack) |
|
||||
| `vite` | Customize the Vite config that's used to generate the Admin panel. [More](/docs/admin/vite) |
|
||||
| `logoutRoute` | The route for the `logout` page. |
|
||||
| `inactivityRoute` | The route for the `logout` inactivity page. |
|
||||
|
||||
### The Admin User Collection
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
The Admin Panel can only be used by a single auth-enabled Collection. To enable authentication for a Collection, simply set `auth: true` in the Collection's configuration. See [Authentication](/docs/authentication/overview) for more information.
|
||||
**Important:**
|
||||
|
||||
The Payload Admin panel can only be used by one Collection that supports
|
||||
[Authentication](/docs/authentication/overview).
|
||||
</Banner>
|
||||
|
||||
To specify which Collection to allow to login to the Admin Panel, pass the `admin.user` key equal to the slug of any auth-enabled Collection. See [Authentication](/docs/authentication/overview) for more information.
|
||||
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`:
|
||||
|
||||
@@ -62,67 +71,21 @@ const config = buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
<Banner type="info">
|
||||
By default, if you have not specified a Collection, Payload will automatically provide a `User` Collection with access the Admin Panel. You can customize or override the fields and settings of the default `User` Collection by adding your own Collection with `slug: 'users'`. Doing this will force Payload to use your provided `User` Collection instead of its default version.
|
||||
</Banner>
|
||||
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.
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
You can use whatever Collection you'd like to access the Admin Panel as long as the Collection supports [Authentication](/docs/authentication/overview). It doesn't need to be called `users`. For example, you could use a Collection called `admins` or `editors` instead.
|
||||
</Banner>
|
||||
**Note: you can use whatever Collection you'd like to access the Admin panel as long as the Collection supports Authentication. It doesn't need to be called `users`!**
|
||||
|
||||
For example, you may wish to have two Collections that both support Authentication:
|
||||
For example, you may wish to have two Collections that both support `Authentication`:
|
||||
|
||||
- `admins` - meant to have a higher level of permissions to manage your data and access the Admin Panel
|
||||
- `customers` - meant for end users of your app that should not be allowed to log into the Admin Panel
|
||||
- `admins` - meant to have a higher level of permissions to manage your data and access the Admin panel
|
||||
- `customers` - meant for end users of your app that should not be allowed to log into the Admin panel
|
||||
|
||||
This is totally possible. For the above scenario, by specifying `admin: { user: 'admins' }`, your Admin Panel will use `admins`. Any users logged in as `customers` will not be able to log in via the Admin Panel.
|
||||
|
||||
### Restricting user access
|
||||
|
||||
It is also possible to allow _multiple admin user types_ into the Admin Panel with limited permissions. To do this, add a `roles` or similar field to your auth-enabled Collection and use the `access.admin` property to limit access. See [Access Control](/docs/access-control/overview) for full details. For a working example, check out the [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth/payload).
|
||||
|
||||
### I18n
|
||||
|
||||
The Payload Admin Panel is translated in over 30 languages and counting. Languages are automatically detected based on the user's browser and all text displays in that language. If no language was detected, or if the user's language is not yet supported, English will be chosen. Users can easily specify their language by selecting one from their account page. See [I18n](../configuration/i18n) for more information.
|
||||
|
||||
<Banner>
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
If there is a language that Payload does not yet support, we accept code
|
||||
[contributions](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md).
|
||||
</Banner>
|
||||
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.
|
||||
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.
|
||||
|
||||
### Custom admin panel routes
|
||||
### Restricting user access
|
||||
|
||||
You can configure custom routes in the Admin Panel for the following routes:
|
||||
|
||||
| Option | Default route |
|
||||
| ----------------- | ----------------------- |
|
||||
| `account` | `/account` |
|
||||
| `createFirstUser` | `/create-first-user` |
|
||||
| `forgot` | `/forgot` |
|
||||
| `inactivity` | `/logout-inactivity` |
|
||||
| `login` | `/login` |
|
||||
| `logout` | `/logout` |
|
||||
| `reset` | `/reset` |
|
||||
| `unauthorized` | `/unauthorized` |
|
||||
|
||||
`payload.config.js`:
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
const config = buildConfig({
|
||||
admin: {
|
||||
routes: {
|
||||
admin: '/custom-admin-route'
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
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.
|
||||
|
||||
@@ -3,7 +3,7 @@ title: Managing User Preferences
|
||||
label: Preferences
|
||||
order: 50
|
||||
desc: Store the preferences of your users as they interact with the Admin panel.
|
||||
keywords: admin, preferences, custom, customize, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: admin, preferences, custom, customize, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
As your users interact with your Admin panel, you might want to store their preferences in a persistent manner, so that when they revisit the Admin panel, they can pick right back up where they left off.
|
||||
@@ -15,13 +15,13 @@ Out of the box, Payload handles the persistence of your users' preferences in a
|
||||
1. The "collapsed" state of blocks, on a document level, as users edit or interact with documents
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
**Important:**
|
||||
|
||||
All preferences are stored on an individual user basis. Payload automatically recognizes the user
|
||||
that is reading or setting a preference via all provided authentication methods.
|
||||
</Banner>
|
||||
|
||||
## Use cases
|
||||
### Use cases
|
||||
|
||||
This API is used significantly for internal operations of the Admin panel, as mentioned above. But, if you're building your own React components for use in the Admin panel, you can allow users to set their own preferences in correspondence to their usage of your components. For example:
|
||||
|
||||
@@ -30,7 +30,7 @@ This API is used significantly for internal operations of the Admin panel, as me
|
||||
- You might want to store `recentlyAccessed` documents to give admin editors an easy shortcut back to their recently accessed documents on the `Dashboard` or similar
|
||||
- Many other use cases exist. Invent your own! Give your editors an intelligent and persistent editing experience.
|
||||
|
||||
## Database
|
||||
### Database
|
||||
|
||||
Payload automatically creates an internally used `payload-preferences` collection that stores user preferences. Each document in the `payload-preferences` collection contains the following shape:
|
||||
|
||||
@@ -44,15 +44,15 @@ Payload automatically creates an internally used `payload-preferences` collectio
|
||||
| `createdAt` | A timestamp of when the preference was created. |
|
||||
| `updatedAt` | A timestamp set to the last time the preference was updated. |
|
||||
|
||||
## APIs
|
||||
### APIs
|
||||
|
||||
Preferences are available to both [GraphQL](/docs/graphql/overview#preferences) and [REST](/docs/rest-api/overview#) APIs.
|
||||
|
||||
## Adding or reading Preferences in your own components
|
||||
### Adding or reading Preferences in your own components
|
||||
|
||||
The Payload admin panel offers a `usePreferences` hook. The hook is only meant for use within the admin panel itself. It provides you with two methods:
|
||||
|
||||
#### `getPreference`
|
||||
##### `getPreference`
|
||||
|
||||
This async method provides an easy way to retrieve a user's preferences by `key`. It will return a promise containing the resulting preference value.
|
||||
|
||||
@@ -60,7 +60,7 @@ This async method provides an easy way to retrieve a user's preferences by `key`
|
||||
|
||||
- `key`: the `key` of your preference to retrieve.
|
||||
|
||||
#### `setPreference`
|
||||
##### `setPreference`
|
||||
|
||||
Also async, this method provides you with an easy way to set a user preference. It returns `void`.
|
||||
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
---
|
||||
title: Customizing Views
|
||||
label: Customizing Views
|
||||
order: 30
|
||||
desc:
|
||||
keywords:
|
||||
---
|
||||
|
||||
### Root
|
||||
|
||||
You can easily swap entire views with your own by using the `admin.components.views` property. At the root level, Payload renders the following views by default, all of which can be overridden:
|
||||
|
||||
| Property | Description |
|
||||
| --------------- | ----------------------------------------------------------------------------- |
|
||||
| **`Account`** | The Account view is used to show the currently logged in user's Account page. |
|
||||
| **`Dashboard`** | The main landing page of the [Admin Panel](./overview). |
|
||||
|
||||
To swap out any of these views, simply pass in your custom component to the `admin.components.views` property of your Payload config. For example:
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Account: MyCustomAccountView,
|
||||
Dashboard: MyCustomDashboardView,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
For more granular control, pass a configuration object instead. Payload exposes the following properties for each view:
|
||||
|
||||
| Property | Description |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **`Component`** \* | Pass in the component that should be rendered when a user navigates to this route. |
|
||||
| **`path`** \* | Any valid URL path or array of paths that [`path-to-regexp`](https://www.npmjs.com/package/path-to-regex) understands. |
|
||||
| **`exact`** | Boolean. When true, will only match if the path matches the `usePathname()` exactly. |
|
||||
| **`strict`** | When true, a path that has a trailing slash will only match a location.pathname with a trailing slash. This has no effect when there are additional URL segments in the location.pathname. |
|
||||
| **`sensitive`** | When true, will match if the path is case sensitive. |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
#### Adding new root views
|
||||
|
||||
To add a _new_ view to the [Admin Panel](./overview), simply add another key to the `views` object with at least a `path` and `Component` property. For example:
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
MyCustomView: {
|
||||
Component: MyCustomView,
|
||||
path: '/my-custom-view',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
Routes are cascading. This means that unless explicitly given the `exact` property, they will
|
||||
match on URLs that simply _start_ with the route's path. This is helpful when creating catch-all
|
||||
routes in your application. Alternatively, you could define your nested route _before_ your parent
|
||||
route.
|
||||
</Banner>
|
||||
|
||||
For help on how to build your own custom view components, see [Building Custom View Components](#building-custom-view-components). For more examples regarding how to customize components, [look at the following examples](https://github.com/payloadcms/payload/tree/main/test/admin/components)._
|
||||
|
||||
### Collections
|
||||
|
||||
To swap out entire views on collections, you can use the `admin.components.views` property on the collection's config. Payload renders the following views by default, all of which can be overridden:
|
||||
|
||||
| Property | Description |
|
||||
| ---------- | ------------------------------------------------------------------------- |
|
||||
| **`Edit`** | The Edit view is used to edit a single document for a given collection. |
|
||||
| **`List`** | The List view is used to show a list of documents for a given collection. |
|
||||
|
||||
To swap out any of these views, simply pass in your custom component to the `admin.components.views` property of your Payload config. This will replace the entire view, including the page breadcrumbs, title, tabs, etc, _as well as all nested routes_.
|
||||
|
||||
```ts
|
||||
// Collection.ts
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Edit: MyCustomEditView,
|
||||
List: MyCustomListView,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
For help on how to build your own custom view components, see [Building Custom View Components](#building-custom-view-components).
|
||||
|
||||
**Customizing Nested Views within 'Edit' in Collections**
|
||||
|
||||
The `Edit` view in collections consists of several nested views, each serving a unique purpose. You can customize these nested views using the `admin.components.views.Edit` property in the collection's configuration. This approach allows you to replace specific nested views while keeping the overall structure of the `Edit` view intact, including the page breadcrumbs, title, tabs, etc.
|
||||
|
||||
Here's an example of how you can customize nested views within the `Edit` view in collections, including the use of the `actions` property:
|
||||
|
||||
```ts
|
||||
// Collection.ts
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Edit: {
|
||||
Default: {
|
||||
Component: MyCustomDefaultTab,
|
||||
actions: [CollectionEditButton], // Custom actions for the default edit view
|
||||
},
|
||||
API: {
|
||||
Component: MyCustomAPIView,
|
||||
actions: [CollectionAPIButton], // Custom actions for API view
|
||||
},
|
||||
LivePreview: {
|
||||
Component: MyCustomLivePreviewView,
|
||||
actions: [CollectionLivePreviewButton], // Custom actions for Live Preview
|
||||
},
|
||||
Version: {
|
||||
Component: MyCustomVersionView,
|
||||
actions: [CollectionVersionButton], // Custom actions for Version view
|
||||
},
|
||||
Versions: {
|
||||
Component: MyCustomVersionsView,
|
||||
actions: [CollectionVersionsButton], // Custom actions for Versions view
|
||||
},
|
||||
},
|
||||
List: {
|
||||
actions: [CollectionListButton],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Adding New Tabs to 'Edit' View**
|
||||
|
||||
You can also add _new_ tabs to the `Edit` view by adding another key to the `components.views.Edit[key]` object with a `path` and `Component` property. See [Custom Tabs](#custom-tabs) for more information.
|
||||
|
||||
### Globals
|
||||
|
||||
To swap out views for globals, you can use the `admin.components.views` property on the global's config. Payload renders the following views by default, all of which can be overridden:
|
||||
|
||||
| Property | Description |
|
||||
| ---------- | ------------------------------------------------------------------- |
|
||||
| **`Edit`** | The Edit view is used to edit a single document for a given Global. |
|
||||
|
||||
To swap out any of these views, simply pass in your custom component to the `admin.components.views` property of your Payload config. This will replace the entire view, including the page breadcrumbs, title, and tabs, _as well as all nested views_.
|
||||
|
||||
```ts
|
||||
// Global.ts
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Edit: MyCustomEditView,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
For help on how to build your own custom view components, see [Building Custom View Components](#building-custom-view-components).
|
||||
|
||||
**Customizing Nested Views within 'Edit' in Globals**
|
||||
|
||||
Similar to collections, Globals allow for detailed customization within the `Edit` view. This includes the ability to swap specific nested views while maintaining the overall structure of the `Edit` view. You can use the `admin.components.views.Edit` property in the Globals configuration to achieve this, and this will only replace the nested view, leaving the page breadcrumbs, title, and tabs intact.
|
||||
|
||||
Here's how you can customize nested views within the `Edit` view in Globals, including the use of the `actions` property:
|
||||
|
||||
```ts
|
||||
// Global.ts
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Edit: {
|
||||
Default: {
|
||||
Component: MyCustomGlobalDefaultTab,
|
||||
actions: [GlobalEditButton], // Custom actions for the default edit view
|
||||
},
|
||||
API: {
|
||||
Component: MyCustomGlobalAPIView,
|
||||
actions: [GlobalAPIButton], // Custom actions for API view
|
||||
},
|
||||
LivePreview: {
|
||||
Component: MyCustomGlobalLivePreviewView,
|
||||
actions: [GlobalLivePreviewButton], // Custom actions for Live Preview
|
||||
},
|
||||
Version: {
|
||||
Component: MyCustomGlobalVersionView,
|
||||
actions: [GlobalVersionButton], // Custom actions for Version view
|
||||
},
|
||||
Versions: {
|
||||
Component: MyCustomGlobalVersionsView,
|
||||
actions: [GlobalVersionsButton], // Custom actions for Versions view
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
You can also add _new_ tabs to the `Edit` view by adding another key to the `components.views.Edit[key]` object with a `path` and `Component` property. See [Custom Tabs](#custom-tabs) for more information.
|
||||
|
||||
### Custom Tabs
|
||||
|
||||
You can easily swap individual collection or global edit views. To do this, pass an _object_ to the `admin.components.views.Edit` property of the config. Payload renders the following views by default, all of which can be overridden:
|
||||
|
||||
| Property | Description |
|
||||
| ----------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`Default`** | The Default view is the primary view in which your document is edited. |
|
||||
| **`Versions`** | The Versions view is used to view the version history of a single document. [More details](../versions) |
|
||||
| **`Version`** | The Version view is used to view a single version of a single document for a given collection. [More details](../versions). |
|
||||
| **`API`** | The API view is used to display the REST API JSON response for a given document. |
|
||||
| **`LivePreview`** | The LivePreview view is used to display the Live Preview interface. [More details](../live-preview) |
|
||||
|
||||
Here is an example:
|
||||
|
||||
```ts
|
||||
// Collection.ts or Global.ts
|
||||
export const MyCollection: SanitizedCollectionConfig = {
|
||||
slug: 'my-collection',
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Edit: {
|
||||
// You can also define `components.views.Edit` as a component, this will override _all_ nested views
|
||||
Default: MyCustomDefaultTab,
|
||||
Versions: MyCustomVersionsTab,
|
||||
Version: MyCustomVersionTab,
|
||||
API: MyCustomAPITab,
|
||||
LivePreview: MyCustomLivePreviewTab,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
To add a _new_ tab to the `Edit` view, simply add another key to `components.views.Edit[key]` with at least a `path` and `Component` property. For example:
|
||||
|
||||
```ts
|
||||
// `Collection.ts` or `Global.ts`
|
||||
export const MyCollection: SanitizedCollectionConfig = {
|
||||
slug: 'my-collection',
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Edit: {
|
||||
MyCustomTab: {
|
||||
Component: MyCustomTab,
|
||||
path: '/my-custom-tab',
|
||||
// You an swap the entire tab component out for your own
|
||||
Tab: MyCustomTab,
|
||||
},
|
||||
AnotherCustomView: {
|
||||
Component: AnotherCustomView,
|
||||
path: '/another-custom-view',
|
||||
// Or you can use the default tab component and just pass in your own label and href
|
||||
Tab: {
|
||||
label: 'Another Custom View',
|
||||
href: '/another-custom-view',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Building Custom View Components
|
||||
|
||||
Your custom view components will be provided with the following props:
|
||||
|
||||
| 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](./overview) or not. |
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
It's up to you to secure your custom views. If your view requires a user to be logged in or to
|
||||
have certain access rights, you should handle that within your view component yourself.
|
||||
</Banner>
|
||||
|
||||
### Examples
|
||||
|
||||
You can find examples of custom views in the [Payload source code `/test/admin/components/views` folder](https://github.com/payloadcms/payload/tree/main/test/admin/components/views). There, you'll find two custom views:
|
||||
|
||||
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 views of your own, take a look at the `admin.components.views` property of the [Payload test admin config](https://github.com/payloadcms/payload/blob/main/test/admin/config.ts).
|
||||
|
||||
161
docs/admin/vite.mdx
Normal file
161
docs/admin/vite.mdx
Normal file
@@ -0,0 +1,161 @@
|
||||
---
|
||||
title: Vite
|
||||
label: Vite
|
||||
order: 90
|
||||
desc: NEEDS TO BE WRITTEN
|
||||
---
|
||||
|
||||
<Banner type="info">
|
||||
The Vite bundler is currently in beta. If you would like to help us test this package, we'd love to hear from you if you find any [bugs or issues](https://github.com/payloadcms/payload/issues/)!
|
||||
</Banner>
|
||||
|
||||
Payload has a Vite bundler that you can install and bundle the Admin Panel with. This is an alternative to the [Webpack](/docs/admin/webpack) bundler and might give some performance boosts to your development workflow.
|
||||
|
||||
To use Vite as your bundler, first you need to install the package:
|
||||
|
||||
```bash
|
||||
yarn add @payloadcms/bundler-vite
|
||||
```
|
||||
|
||||
Then you will need to add the [bundler](/docs/admin/bundlers) to your Payload config:
|
||||
|
||||
```ts
|
||||
import { buildConfig } from '@payloadcms/config'
|
||||
import { viteBundler } from '@payloadcms/bundler-vite'
|
||||
|
||||
export default buildConfig({
|
||||
collections: [],
|
||||
admin: {
|
||||
bundler: viteBundler(),
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Vite works fundamentally differently than Webpack. In development mode, it will first pre-bundle any of your dependencies that are CommonJS-only, and then it'll leverage ESM directly in your browser for a better HMR experience.
|
||||
|
||||
It then uses Rollup to create production builds of your admin UI. With Vite, you should see a decent performance boost—especially after your first cold start. However, that first cold start might take a few more seconds.
|
||||
|
||||
<Banner type="warning">
|
||||
In most cases, Vite should work out of the box. But existing Payload plugins may need to make compatibility changes to support Vite.
|
||||
</Banner>
|
||||
|
||||
This is because Vite aliases work fundamentally differently than Webpack aliases, and Payload relies on aliasing server-only code out of the Payload config to ensure that the bundled admin JS works within your browser.
|
||||
|
||||
Here are the main differences between how Vite aliases work and how Webpack aliases work.
|
||||
|
||||
**Vite aliases do not work with absolute paths.**
|
||||
|
||||
In Vite, alias keys must **exactly match** a import paths. If you have 2 files that import the same server-only module, but have different import paths, you would need to add 2 aliases to support both import paths.
|
||||
|
||||
```ts
|
||||
// File A
|
||||
import serverOnlyModule from '../server-only-module'
|
||||
|
||||
// File B
|
||||
import serverOnlyModule from '../../server-only-module'
|
||||
|
||||
// payload.config.ts
|
||||
// You would need to add 2 aliases to support both import paths
|
||||
export const buildConfig({
|
||||
collections: [],
|
||||
admin: {
|
||||
bundler: viteBundler(),
|
||||
vite: (incomingViteConfig) => {
|
||||
const existingAliases = incomingViteConfig?.resolve?.alias || {};
|
||||
let aliasArray: { find: string | RegExp; replacement: string; }[] = [];
|
||||
|
||||
// Pass the existing Vite aliases
|
||||
if (Array.isArray(existingAliases)) {
|
||||
aliasArray = existingAliases;
|
||||
} else {
|
||||
aliasArray = Object.values(existingAliases);
|
||||
}
|
||||
|
||||
// Add your own aliases using the find and replacement keys
|
||||
aliasArray.push({
|
||||
find: '../server-only-module',
|
||||
replacement: path.resolve(__dirname, './path/to/browser-safe-module.js')
|
||||
find: '../../server-only-module',
|
||||
replacement: path.resolve(__dirname, './path/to/browser-safe-module.js')
|
||||
});
|
||||
|
||||
return {
|
||||
...incomingViteConfig,
|
||||
resolve: {
|
||||
...(incomingViteConfig?.resolve || {}),
|
||||
alias: aliasArray,
|
||||
}
|
||||
};
|
||||
},
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Vite aliases do not get applied to pre-bundled dependencies.**
|
||||
|
||||
This especially affects plugins, as plugins will be pre-bundled by Vite using `esbuild`. To get around this and support Vite, plugin authors need to configure an alias to their plugin at the top level, so that the alias will work accordingly.
|
||||
|
||||
Here's an example. Say your plugin is called `payload-plugin-cool`. It's imported as follows:
|
||||
|
||||
```ts
|
||||
import { myCoolPlugin } from 'payload-plugin-cool'
|
||||
```
|
||||
|
||||
That plugin should create an alias to support Vite as follows:
|
||||
|
||||
```ts
|
||||
{
|
||||
// aliases go here
|
||||
find: 'payload-plugin-cool',
|
||||
replacement: path.resolve(__dirname, './my-admin-plugin.js')
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
This will effectively alias the entire plugin and work with Vite. If the plugin requires admin-specific code, then the `./my-admin-plugin.js` alias target file should reflect any changes necessary to the admin UI that the main server-side plugin performs.
|
||||
|
||||
### Extending the Vite config
|
||||
|
||||
The Payload config supports a new property for plugins to be able to extend the Vite config specifically. That property exists on the main Payload config under `admin.vite`. You can check out the [Vite docs](https://vitejs.dev/config/shared-options.html) for more information on what you can do with the Vite config.
|
||||
|
||||
It's a function that takes a Vite config, and returns an updated Vite config. Here's an example:
|
||||
|
||||
```ts
|
||||
export const buildConfig({
|
||||
collections: [],
|
||||
admin: {
|
||||
bundler: viteBundler(),
|
||||
vite: (incomingViteConfig) => {
|
||||
const existingAliases = incomingViteConfig?.resolve?.alias || {};
|
||||
let aliasArray: { find: string | RegExp; replacement: string; }[] = [];
|
||||
|
||||
// Pass the existing Vite aliases
|
||||
if (Array.isArray(existingAliases)) {
|
||||
aliasArray = existingAliases;
|
||||
} else {
|
||||
aliasArray = Object.values(existingAliases);
|
||||
}
|
||||
|
||||
// Add your own aliases using the find and replacement keys
|
||||
aliasArray.push({
|
||||
find: '../server-only-module',
|
||||
replacement: path.resolve(__dirname, './path/to/browser-safe-module.js')
|
||||
});
|
||||
|
||||
return {
|
||||
...incomingViteConfig,
|
||||
resolve: {
|
||||
...(incomingViteConfig?.resolve || {}),
|
||||
alias: aliasArray,
|
||||
}
|
||||
};
|
||||
},
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Learn more about [aliasing server-only modules](https://payloadcms.com/docs/admin/excluding-server-code#aliasing-server-only-modules).
|
||||
|
||||
Even though there is a new property for Vite configs specifically, we have implemented some "compatibility" between Webpack and Vite out-of-the-box.
|
||||
|
||||
If your config specifies Webpack aliases, we attempt to leverage them automatically within the Vite config. They are merged into the Vite alias configuration seamlessly and may work out-of-the-box.
|
||||
67
docs/admin/webpack.mdx
Normal file
67
docs/admin/webpack.mdx
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Webpack
|
||||
label: Webpack
|
||||
order: 80
|
||||
desc: The Payload admin panel uses Webpack 5 and supports many common functionalities such as SCSS and Typescript out of the box to give you more freedom.
|
||||
keywords: admin, webpack, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
Payload has a Webpack (v5) bundler that you can build the Admin panel with. For now, we recommended using it because it is stable. If you are feeling a bit more adventurous you can give the [Vite](/docs/admin/vite) bundler a shot.
|
||||
|
||||
Out of the box, the Webpack bundler supports common functionalities such as SCSS and Typescript, but there are many cases where you may need to add support for additional functionalities.
|
||||
|
||||
#### Installation
|
||||
|
||||
```bash
|
||||
yarn add @payloadcms/bundler-webpack
|
||||
```
|
||||
|
||||
#### Import the bundler
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
|
||||
import { buildConfig } from 'payload/config'
|
||||
import { webpackBundler } from '@payloadcms/bundler-webpack'
|
||||
|
||||
export default buildConfig({
|
||||
// highlight-start
|
||||
admin: {
|
||||
bundler: webpackBundler()
|
||||
},
|
||||
// highlight-end
|
||||
})
|
||||
```
|
||||
|
||||
### Extending Webpack
|
||||
|
||||
If you need to extend the Webpack config, you can do so by passing a function to the `admin.webpack` property on your Payload config.
|
||||
The function will receive the Webpack config as an argument and should return the modified config.
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
|
||||
import { buildConfig } from 'payload/config'
|
||||
import { webpackBundler } from '@payloadcms/bundler-webpack'
|
||||
|
||||
export default buildConfig({
|
||||
admin: {
|
||||
bundler: webpackBundler()
|
||||
// highlight-start
|
||||
webpack: (config) => {
|
||||
// full control of the Webpack config
|
||||
|
||||
return config
|
||||
},
|
||||
// highlight-end
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
**Tip:**
|
||||
|
||||
If changes to your Webpack aliases are not surfacing, they might be
|
||||
[cached](https://webpack.js.org/configuration/cache/) in `node_modules/.cache/webpack`. Try
|
||||
deleting that folder and restarting your server.
|
||||
</Banner>
|
||||
@@ -3,7 +3,7 @@ title: Authentication Config
|
||||
label: Config
|
||||
order: 20
|
||||
desc: Enable and customize options in the Authentication config for features including Forgot Password, Login Attempts, API key usage and more.
|
||||
keywords: authentication, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: authentication, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
Payload's Authentication is extremely powerful and gives you everything you need when you go to build a new app or site in a secure and responsible manner.
|
||||
@@ -13,12 +13,12 @@ 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 (in milliseconds) 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 `req`. Defaults to `0` and should only be modified if absolutely necessary, as this will affect performance. |
|
||||
| **`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) |
|
||||
@@ -46,11 +46,10 @@ To enable API keys on a collection, set the `useAPIKey` auth option to `true`. F
|
||||
</Banner>
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
**Important:**
|
||||
If you change your `PAYLOAD_SECRET`, you will need to regenerate your API keys.
|
||||
<br />
|
||||
The secret key is used to encrypt the API keys, so if you change the secret, existing API keys will
|
||||
no longer be valid.
|
||||
|
||||
The secret key is used to encrypt the API keys, so if you change the secret, existing API keys will no longer be valid.
|
||||
</Banner>
|
||||
|
||||
#### Authenticating via API Key
|
||||
@@ -96,8 +95,8 @@ 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 />
|
||||
**Tip:**
|
||||
|
||||
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
|
||||
@@ -139,8 +138,8 @@ export const Customers: CollectionConfig = {
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
**Important:**
|
||||
|
||||
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
|
||||
@@ -199,8 +198,8 @@ export const Customers: CollectionConfig = {
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
**Important:**
|
||||
|
||||
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.
|
||||
@@ -217,7 +216,7 @@ Example:
|
||||
{
|
||||
slug: 'customers',
|
||||
auth: {
|
||||
forgotPassword: {
|
||||
verify: {
|
||||
// highlight-start
|
||||
generateEmailSubject: ({ req, user }) => {
|
||||
return `Hey ${user.email}, reset your password!`;
|
||||
|
||||
@@ -3,12 +3,12 @@ title: Authentication Operations
|
||||
label: Operations
|
||||
order: 30
|
||||
desc: Enabling Authentication automatically makes key operations available such as Login, Logout, Verify, Unlock, Reset Password and more.
|
||||
keywords: authentication, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: authentication, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
Enabling Authentication on a Collection automatically exposes additional auth-based operations in the Local, REST, and GraphQL APIs.
|
||||
|
||||
## Access
|
||||
### Access
|
||||
|
||||
The Access operation returns what a logged in user can and can't do with the collections and globals that are registered via your config. This data can be immensely helpful if your app needs to show and hide certain features based on access control, as the Payload Admin panel does.
|
||||
|
||||
@@ -69,7 +69,7 @@ query {
|
||||
|
||||
Document access can also be queried on a collection/global basis. Access on a global can queried like `http://localhost:3000/api/global-slug/access`, Collection document access can be queried like `http://localhost:3000/api/collection-slug/access/:id`.
|
||||
|
||||
## Me
|
||||
### Me
|
||||
|
||||
Returns either a logged in user with token or null when there is no logged in user.
|
||||
|
||||
@@ -105,9 +105,9 @@ query {
|
||||
}
|
||||
```
|
||||
|
||||
## Login
|
||||
### Login
|
||||
|
||||
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 a `res` to the Local API operation, Payload will set a cookie there as well.
|
||||
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**:
|
||||
|
||||
@@ -166,7 +166,7 @@ const result = await payload.login({
|
||||
})
|
||||
```
|
||||
|
||||
## Logout
|
||||
### Logout
|
||||
|
||||
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.
|
||||
|
||||
@@ -189,9 +189,9 @@ mutation {
|
||||
}
|
||||
```
|
||||
|
||||
## Refresh
|
||||
### Refresh
|
||||
|
||||
Allows for "refreshing" JWTs. If your user has a token that is about to expire, but the user is still active and using the app, you might want to use the `refresh` operation to receive a new token by sending the operation the token that is about to expire.
|
||||
Allows for "refreshing" JWTs. If your user has a token that is about to expire, but the user is still active and using the app, you might want to use the `refresh` operation to receive a new token by executing this operation via the authenticated user.
|
||||
|
||||
This operation requires a non-expired token to send back a new one. If the user's token has already expired, you will need to allow them to log in again to retrieve a new token.
|
||||
|
||||
@@ -237,14 +237,7 @@ mutation {
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
The Refresh operation will automatically find the user's token in either a JWT header or the
|
||||
HTTP-only cookie. But, you can specify the token you're looking to refresh by providing the REST
|
||||
API with a `token` within the JSON body of the request, or by providing the GraphQL resolver a
|
||||
`token` arg.
|
||||
</Banner>
|
||||
|
||||
## Verify by Email
|
||||
### Verify by Email
|
||||
|
||||
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.
|
||||
|
||||
@@ -276,7 +269,7 @@ const result = await payload.verifyEmail({
|
||||
})
|
||||
```
|
||||
|
||||
## Unlock
|
||||
### Unlock
|
||||
|
||||
If a user locks themselves out and you wish to deliberately unlock them, you can utilize the Unlock operation. The Admin panel features an Unlock control automatically for all collections that feature max login attempts, but you can programmatically unlock users as well by using the Unlock operation.
|
||||
|
||||
@@ -290,6 +283,9 @@ const res = await fetch(`http://localhost:3000/api/[collection-slug]/unlock`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: 'dev@payloadcms.com',
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
@@ -297,7 +293,7 @@ const res = await fetch(`http://localhost:3000/api/[collection-slug]/unlock`, {
|
||||
|
||||
```
|
||||
mutation {
|
||||
unlock[collection-singular-label]
|
||||
unlock[collection-singular-label](email: "dev@payloadcms.com")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -306,10 +302,13 @@ mutation {
|
||||
```ts
|
||||
const result = await payload.unlock({
|
||||
collection: '[collection-slug]',
|
||||
data: {
|
||||
email: 'dev@payloadcms.com',
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Forgot Password
|
||||
### Forgot Password
|
||||
|
||||
Payload comes with built-in forgot password functionality. Submitting an email address to the Forgot Password operation will generate an email and send it to the respective email address with a link to reset their password.
|
||||
|
||||
@@ -352,8 +351,8 @@ const token = await payload.forgotPassword({
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
**Tip:**
|
||||
|
||||
You can stop the reset-password email from being sent via using the local API. This is helpful if
|
||||
you need to create user accounts programmatically, but not set their password for them. This
|
||||
effectively generates a reset password token which you can then use to send to a page you create,
|
||||
@@ -361,7 +360,7 @@ const token = await payload.forgotPassword({
|
||||
use the token to "reset" their password.
|
||||
</Banner>
|
||||
|
||||
## Reset Password
|
||||
### Reset Password
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ title: Authentication Overview
|
||||
label: Overview
|
||||
order: 10
|
||||
desc: Payload provides highly secure user Authentication out of the box, and you can fully customize, override, or remove the default Authentication support.
|
||||
keywords: authentication, config, configuration, overview, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: authentication, config, configuration, overview, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
<YouTube
|
||||
@@ -30,7 +30,7 @@ _Admin panel screenshot depicting an Admins Collection with Auth enabled_
|
||||
|
||||
By default, Payload provides you with a `User` collection that supports Authentication, which is used to access the Admin panel. But, you can add support to one or many Collections of your own. For more information on how to customize, override, or remove the default `User` collection, [click here](/docs/admin/overview#the-admin-user-collection).
|
||||
|
||||
## Enabling Auth on a collection
|
||||
### Enabling Auth on a collection
|
||||
|
||||
Every Payload Collection can opt-in to supporting Authentication by specifying the `auth` property on the Collection's config to either `true` or to an object containing `auth` options.
|
||||
|
||||
@@ -57,7 +57,12 @@ export const Admins: CollectionConfig = {
|
||||
name: 'role',
|
||||
type: 'select',
|
||||
required: true,
|
||||
options: ['user', 'admin', 'editor', 'developer'],
|
||||
options: [
|
||||
'user',
|
||||
'admin',
|
||||
'editor',
|
||||
'developer',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -71,33 +76,33 @@ export const Admins: CollectionConfig = {
|
||||
|
||||
Once enabled, each document that is created within the Collection can be thought of as a `user` - who can make use of commonly required authentication functions such as logging in / out, resetting their password, and more.
|
||||
|
||||
## Logging in / out, resetting password, etc.
|
||||
### Logging in / out, resetting password, etc.
|
||||
|
||||
[Click here](/docs/authentication/operations) for a list of all automatically-enabled Auth operations, including `login`, `logout`, `refresh`, and others.
|
||||
|
||||
## Token-based auth
|
||||
### Token-based auth
|
||||
|
||||
Successfully logging in returns a `JWT` (JSON web token) which is how a user will identify themselves to Payload. By providing this JWT via either an HTTP-only cookie or an `Authorization: JWT` or `Authorization: Bearer` header, Payload will automatically identify the user and add its user JWT data to the `req`, which is available throughout Payload including within access control, hooks, and more.
|
||||
Successfully logging in returns a `JWT` (JSON web token) which is how a user will identify themselves to Payload. By providing this JWT via either an HTTP-only cookie or an `Authorization: JWT` or `Authorization: Bearer` header, Payload will automatically identify the user and add its user JWT data to the Express `req`, which is available throughout Payload including within access control, hooks, and more.
|
||||
|
||||
You can specify what data gets encoded to the JWT token by setting `saveToJWT` to true in your auth collection fields. If you wish to use a different key other than the field `name`, you can provide it to `saveToJWT` as a string. It is also possible to use `saveToJWT` on fields that are nested in inside groups and tabs. If a group has a `saveToJWT` set it will include the object with all sub-fields in the token. You can set `saveToJWT: false` for any fields you wish to omit. If a field inside a group has `saveToJWT` set, but the group does not, the field will be included at the top level of the token.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
You can access the logged-in user from access control functions and hooks via the{' '}
|
||||
<strong>req</strong>. The logged-in user is automatically added as the <strong>user</strong>{' '}
|
||||
**Tip:**
|
||||
|
||||
You can access the logged-in user from access control functions and hooks via the Express{' '}
|
||||
**req**. The logged-in user is automatically added as the **user**
|
||||
property.
|
||||
</Banner>
|
||||
|
||||
## HTTP-only cookies
|
||||
### HTTP-only cookies
|
||||
|
||||
Payload `login`, `logout`, and `refresh` operations make use of HTTP-only cookies for authentication purposes. HTTP-only cookies are a highly secure method of storing identifiable data on a user's device so that Payload can automatically recognize a returning user until their cookie expires. They are totally protected from common XSS attacks and cannot be read at all via JavaScript in the browser.
|
||||
|
||||
#### Automatic browser inclusion
|
||||
##### Automatic browser inclusion
|
||||
|
||||
Modern browsers automatically include `http-only` cookies when making requests directly to URLs—meaning that if you are running your API on http://example.com, and you have logged in and visit http://example.com/test-page, your browser will automatically include the Payload authentication cookie for you.
|
||||
|
||||
#### Using Fetch or other HTTP APIs
|
||||
##### Using Fetch or other HTTP APIs
|
||||
|
||||
However, if you use `fetch` or similar APIs to retrieve Payload resources from its REST or GraphQL API, you need to specify to include credentials (cookies).
|
||||
|
||||
@@ -114,14 +119,14 @@ const pages = await response.json()
|
||||
For more about how to automatically include cookies in requests from your app to your Payload API, [click here](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Sending_a_request_with_credentials_included).
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
**Tip:**
|
||||
|
||||
To make sure you have a Payload cookie set properly in your browser after logging in, you can use
|
||||
Chrome's Developer Tools - Application - Cookies - [your-domain-here]. The Chrome Developer tools
|
||||
will still show HTTP-only cookies, even when JavaScript running on the page can't.
|
||||
</Banner>
|
||||
|
||||
## CSRF Protection
|
||||
### CSRF Protection
|
||||
|
||||
CSRF (cross-site request forgery) attacks are common and dangerous. By using an HTTP-only cookie, Payload removes many XSS vulnerabilities, however, CSRF attacks can still be possible.
|
||||
|
||||
@@ -130,10 +135,10 @@ For example, let's say you have a very popular app running at coolsite.com. This
|
||||
So, if a user of coolsite.com is logged in and just browsing around on the internet, they might stumble onto a page with bad intentions. That bad page might automatically make requests to all sorts of sites to see if they can find one that they can log into - and coolsite.com might be on their list. If your user was logged in while they visited that evil site, the attacker could do whatever they wanted as if they were your coolsite.com user by just sending requests to the coolsite API (which would automatically include the auth cookie). They could send themselves a bunch of money from your user's account, change the user's password, etc. This is what a CSRF attack is.
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>
|
||||
**
|
||||
To protect against CSRF attacks, Payload only accepts cookie-based authentication from domains
|
||||
that you explicitly whitelist.
|
||||
</strong>
|
||||
**
|
||||
</Banner>
|
||||
|
||||
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.
|
||||
@@ -159,7 +164,7 @@ const config = buildConfig({
|
||||
export default config
|
||||
```
|
||||
|
||||
## Identifying users via the Authorization Header
|
||||
### Identifying users via the Authorization Header
|
||||
|
||||
In addition to authenticating via an HTTP-only cookie, you can also identify users via the `Authorization` header on an HTTP request.
|
||||
|
||||
|
||||
56
docs/authentication/using-middleware.mdx
Normal file
56
docs/authentication/using-middleware.mdx
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: Using the Payload Auth Middleware
|
||||
label: Using the Middleware
|
||||
order: 40
|
||||
desc: Make full use of Payload's built-in authentication with your own custom Express endpoints by adding Payload's authentication middleware.
|
||||
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, 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.
|
||||
|
||||
<Banner type="success">
|
||||
You can make full use of Payload's built-in authentication within your own custom Express
|
||||
endpoints by adding Payload's authentication middleware.
|
||||
</Banner>
|
||||
|
||||
<Banner type="warning">
|
||||
Payload must be initialized before the `payload.authenticate` middleware can be used. This is done
|
||||
by calling `payload.init()` prior to adding the middleware.
|
||||
</Banner>
|
||||
|
||||
Example in `server.js`:
|
||||
|
||||
```ts
|
||||
import express from 'express'
|
||||
import payload from 'payload'
|
||||
|
||||
const app = express()
|
||||
|
||||
const start = async () => {
|
||||
await payload.init({
|
||||
secret: 'PAYLOAD_SECRET_KEY',
|
||||
express: app,
|
||||
})
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
// Note: Payload must be initialized before the `payload.authenticate` middleware can be used
|
||||
router.use(payload.authenticate) // highlight-line
|
||||
|
||||
router.get('/', (req, res) => {
|
||||
if (req.user) {
|
||||
return res.send(`Authenticated successfully as ${req.user.email}.`)
|
||||
}
|
||||
|
||||
return res.send('Not authenticated')
|
||||
})
|
||||
|
||||
app.use('/some-route-here', router)
|
||||
|
||||
app.listen(3000)
|
||||
}
|
||||
|
||||
start()
|
||||
```
|
||||
@@ -6,7 +6,7 @@ desc: Quickly configure and deploy your Payload Cloud project in a few simple st
|
||||
keywords: configuration, config, settings, project, cloud, payload cloud, deploy, deployment
|
||||
---
|
||||
|
||||
## Select your plan
|
||||
### Select your plan
|
||||
|
||||
Once you have created a project, you will need to select your plan. This will determine the resources that are allocated to your project and the features that are available to you.
|
||||
|
||||
@@ -17,7 +17,7 @@ Once you have created a project, you will need to select your plan. This will de
|
||||
anytime.
|
||||
</Banner>
|
||||
|
||||
## Project Details
|
||||
### Project Details
|
||||
|
||||
| Option | Description |
|
||||
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -26,7 +26,7 @@ Once you have created a project, you will need to select your plan. This will de
|
||||
| **Project Slug** | Choose a unique slug to identify your project. This needs to be unique for your team and you can change it any time. |
|
||||
| **Team** | Select the team you want to create the project under. If this is your first project, a personal team will be created for you automatically. You can modify your team settings and invite new members at any time from the Team Settings page. |
|
||||
|
||||
## Build Settings
|
||||
### Build Settings
|
||||
|
||||
If you are deploying a new project from a template, the following settings will be automatically configured for you. If you are using your own repository, you need to make sure your build settings are accurate for your project to deploy correctly.
|
||||
|
||||
@@ -39,22 +39,22 @@ If you are deploying a new project from a template, the following settings will
|
||||
| **Branch to Deploy** | Select the branch of your repository that you want to deploy from. This is the branch that will be used to build your project when you commit new changes. |
|
||||
| **Default Domain** | Set a default domain for your project. This must be unique and you will not able to change it. You can always add a custom domain later in your project settings. |
|
||||
|
||||
## Environment Variables
|
||||
### Environment Variables
|
||||
|
||||
Any of the features in Payload Cloud that require environment variables will automatically be provided to your application. If your app requires any custom environment variables, you can set them here.
|
||||
|
||||
<Banner type="warning">
|
||||
Note: For security reasons, any variables you wish to provide to the Admin panel must be prefixed
|
||||
with `PAYLOAD_PUBLIC_`. Learn more
|
||||
[here](https://payloadcms.com/docs/admin/environment-vars).
|
||||
[here](https://payloadcms.com/docs/admin/webpack#admin-environment-vars).
|
||||
</Banner>
|
||||
|
||||
## Payment
|
||||
### Payment
|
||||
|
||||
Payment methods can be set per project and can be updated any time. You can use team’s default payment method, or add a new one. Modify your payment methods in your Project settings / Team settings.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong> All Payload Cloud teams that deploy a project require a card on file. This
|
||||
**Note:** All Payload Cloud teams that deploy a project require a card on file. This
|
||||
helps us prevent fraud and abuse on our platform. If you select a plan with a free trial, you will
|
||||
not be charged until your trial period is over. We’ll remind you 7 days before your trial ends and
|
||||
you can cancel anytime.
|
||||
|
||||
@@ -31,7 +31,7 @@ Next, select your `GitHub Scope`. If you belong to multiple organizations, they
|
||||
After selecting your scope, create a unique `repository name` and select whether you want your repository to be public or private on GitHub.
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong> Public repositories can be accessed by anyone online, while private
|
||||
**Note:** Public repositories can be accessed by anyone online, while private
|
||||
repositories grant access only to you and anyone you explicitly authorize.
|
||||
</Banner>
|
||||
|
||||
@@ -45,7 +45,7 @@ Payload Cloud works for any Node.js + MongoDB app. From the New Project page, se
|
||||
_Creating a new project from an existing repository._
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong> In order to make use of the features of Payload Cloud in your own codebase,
|
||||
**Note:** In order to make use of the features of Payload Cloud in your own codebase,
|
||||
you will need to add the [Cloud Plugin](https://github.com/payloadcms/plugin-cloud) to your
|
||||
Payload app.
|
||||
</Banner>
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Manage your Payload Cloud projects.
|
||||
keywords: cloud, payload cloud, projects, project, overview, database, file storage, build settings, environment variables, custom domains, email, developing locally
|
||||
---
|
||||
|
||||
## Overview
|
||||
### Overview
|
||||
|
||||
<Banner>
|
||||
The overview tab shows your most recent deployment, along with build and deployment logs. From
|
||||
@@ -18,19 +18,19 @@ keywords: cloud, payload cloud, projects, project, overview, database, file stor
|
||||

|
||||
_A screenshot of the Overview page for a Cloud project._
|
||||
|
||||
## Database
|
||||
### Database
|
||||
|
||||
Your Payload Cloud project comes with a MongoDB serverless Atlas DB instance or a Dedicated Atlas cluster, depending on your plan. To interact with your cloud database, you will be provided with a MongoDB connection string. This can be found under the **Database** tab of your project.
|
||||
|
||||
`mongodb+srv://your_connection_string`
|
||||
|
||||
## File Storage
|
||||
### File Storage
|
||||
|
||||
Payload Cloud gives you S3 file storage backed by Cloudflare as a CDN, and this plugin extends Payload so that all of your media will be stored in S3 rather than locally.
|
||||
|
||||
AWS Cognito is used for authentication to your S3 bucket. The [Payload Cloud Plugin](https://github.com/payloadcms/plugin-cloud) will automatically pick up these values. These values are only if you'd like to access your files directly, outside of Payload Cloud.
|
||||
|
||||
### Accessing Files Outside of Payload Cloud
|
||||
#### Accessing Files Outside of Payload Cloud
|
||||
|
||||
If you'd like to access your files outside of Payload Cloud, you'll need to retrieve some values from your project's settings and put them into your environment variables. In Payload Cloud, navigate to the File Storage tab and copy the values using the copy button. Put these values in your .env file. Also copy the Cognito Password value separately and put into your .env file as well.
|
||||
|
||||
@@ -50,21 +50,21 @@ PAYLOAD_CLOUD_COGNITO_PASSWORD=
|
||||
|
||||
The plugin will pick up these values and use them to access your files.
|
||||
|
||||
## Build Settings
|
||||
### Build Settings
|
||||
|
||||
You can update settings from your Project’s Settings tab. Changes to your build settings will trigger a redeployment of your project.
|
||||
|
||||
## Environment Variables
|
||||
### Environment Variables
|
||||
|
||||
From the Environment Variables page of the Settings tab, you can add, update and delete variables for use in your project. Like build settings, these changes will trigger a redeployment of your project.
|
||||
|
||||
<Banner>
|
||||
Note: For security reasons, any variables you wish to provide to the Admin panel must be prefixed
|
||||
with `PAYLOAD_PUBLIC_`. Learn more
|
||||
[here](https://payloadcms.com/docs/admin/environment-vars).
|
||||
[here](https://payloadcms.com/docs/admin/webpack#admin-environment-vars).
|
||||
</Banner>
|
||||
|
||||
## Custom Domains
|
||||
### Custom Domains
|
||||
|
||||
With Payload Cloud, you can add custom domain names to your project. To do so, first go to the Domains page of the Settings tab of your project. Here you can see your default domain. To add a new domain, type in the domain name you wish to use.
|
||||
|
||||
@@ -84,19 +84,19 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
## Email
|
||||
### Email
|
||||
|
||||
Powered by [Resend](https://resend.com), Payload Cloud comes with integrated email support out of the box. No configuration is needed, and you can use `payload.sendEmail()` to send email right from your Payload app. To learn more about sending email with Payload, checkout the [Email Configuration](https://payloadcms.com/docs/email/overview) overview.
|
||||
|
||||
If you are on the Pro or Enterprise plan, you can add your own custom Email domain name. From the Email page of your project’s Settings, add the domain you wish to use for email delivery. This will generate a set of DNS records. Add these records to your DNS provider and click verify to check that your records are resolving properly. Once verified, your emails will now be sent from your custom domain name.
|
||||
|
||||
## Developing Locally
|
||||
### Developing Locally
|
||||
|
||||
To make changes to your project, you will need to clone the repository defined in your project settings to your local machine. In order to run your project locally, you will need configure your local environment first. Refer to your repository’s `README.md` file to see the steps needed for your specific template.
|
||||
|
||||
From there, you are ready to make updates to your project. When you are ready to make your changes live, commit your changes to the branch you specified in your Project settings, and your application will automatically trigger a redeploy and build from your latest commit.
|
||||
|
||||
## Cloud Plugin
|
||||
### Cloud Plugin
|
||||
|
||||
Projects generated from a template will come pre-configured with the official Cloud Plugin, but if you are using your own repository you will need to add this into your project. To do so, add the plugin to your Payload config:
|
||||
|
||||
@@ -117,7 +117,7 @@ export default buildConfig({
|
||||
over Payload Cloud's email service.
|
||||
</Banner>
|
||||
|
||||
#### **Optional configuration**
|
||||
##### **Optional configuration**
|
||||
|
||||
If you wish to opt-out of any Payload cloud features, the plugin also accepts options to do so.
|
||||
|
||||
|
||||
@@ -14,22 +14,22 @@ keywords: team, teams, billing, subscription, payment, plan, plans, cloud, paylo
|
||||

|
||||
_A screenshot of the Team Settings page._
|
||||
|
||||
## Members
|
||||
### Members
|
||||
|
||||
Each team has members that can interact with your projects. You can invite multiple people to your team and each individual can belong to more than one team. You can assign them either `owner` or `user` permissions. Owners are able to make admin-only changes, such as deleting projects, and editing billing information.
|
||||
|
||||
## Adding Members
|
||||
### Adding Members
|
||||
|
||||
To add a new member to your team, visit your Team’s Settings page, and click “Invite Teammate”. You can then add their email address, and assign their role. Press “Save” to send the invitations, which will send an email to the invited team member where they can create a new account.
|
||||
|
||||
## Billing
|
||||
### Billing
|
||||
|
||||
Users can update billing settings and subscriptions for any teams where they are designated as an `owner`. To make updates to the team’s payment methods, visit the Billing page under the Team Settings tab. You can add new cards, delete cards, and set a payment method as a default. The default payment method will be used in the event that another payment method fails.
|
||||
|
||||
## Subscriptions
|
||||
### Subscriptions
|
||||
|
||||
From the Subscriptions page, a team owner can see all current plans for their team. From here, you can see the price of each plan, if there is an active trial, and when you will be billed next.
|
||||
|
||||
## Invoices
|
||||
### Invoices
|
||||
|
||||
The Invoices page will you show you the invoices for your account, as well as the status on their payment.
|
||||
|
||||
@@ -3,7 +3,7 @@ title: Collection Configs
|
||||
label: Collections
|
||||
order: 20
|
||||
desc: Structure your Collections for your needs by defining fields, adding slugs and labels, establishing access control, tying in hooks, setting timestamps and more.
|
||||
keywords: collections, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: collections, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
Payload Collections are defined through configs of their own, and you can define as many as your application needs. Each
|
||||
@@ -13,29 +13,29 @@ It's often best practice to write your Collections in separate files and then im
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Description |
|
||||
|------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`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. |
|
||||
| **`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. Set to `false` to disable routes. [More](/docs/rest-api/overview#custom-endpoints) |
|
||||
| **`graphQL`** | An object with `singularName` and `pluralName` strings used in schema generation. Auto-generated from slug if not defined. Set to `false` to disable GraphQL. |
|
||||
| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
|
||||
| **`disableDuplicate`** | When true, do not show the "Duplicate" button while editing documents within this collection and prevent `duplicate` from all APIs. |
|
||||
| **`defaultSort`** | Pass a top-level field to sort by default in the collection List view. Prefix the name of the field with a minus symbol ("-") to sort in descending order. |
|
||||
| **`dbName`** | Custom table or collection name depending on the database adapter. Auto-generated from slug if not defined.
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| Option | Description |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **`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. |
|
||||
| **`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. Set to `false` to disable routes. [More](/docs/rest-api/overview#custom-endpoints) |
|
||||
| **`graphQL`** | An object with `singularName` and `pluralName` strings used in schema generation. Auto-generated from slug if not defined. Set to `false` to disable GraphQL. |
|
||||
| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
|
||||
| **`defaultSort`** | Pass a top-level field to sort by default in the collection List view. Prefix the name of the field with a minus symbol ("-") to sort in descending order. |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`dbName`** | Custom table or collection name depending on the database adapter. Auto-generated from slug if not defined. |
|
||||
| **`db`** | Set custom database operations for this Collection. [More](/docs/database/overview#collection-operations) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Simple collection example
|
||||
#### Simple collection example
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
@@ -58,36 +58,38 @@ export const Orders: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
### More collection config examples
|
||||
#### More collection config examples
|
||||
|
||||
You can find an assortment
|
||||
of [example collection configs](https://github.com/payloadcms/public-demo/tree/master/src/payload/collections) in the Public
|
||||
of [example collection configs](https://github.com/payloadcms/public-demo/tree/master/src/payload/collections) in the
|
||||
Public
|
||||
Demo source code on GitHub.
|
||||
|
||||
## Admin options
|
||||
### Admin options
|
||||
|
||||
You can customize the way that the Admin panel behaves on a collection-by-collection basis by defining the `admin`
|
||||
property on a collection's config.
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `group` | Text used as a label for grouping collection and global links together in the navigation. |
|
||||
| `hidden` | Set to true or a function, called with the current user, returning true to exclude this collection from navigation and admin routing. |
|
||||
| `hooks` | Admin-specific hooks for this collection. [More](#admin-hooks) |
|
||||
| `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. |
|
||||
| `hideAPIURL` | Hides the "API URL" meta field while editing documents within this collection. |
|
||||
| `enableRichTextLink` | The [Rich Text](/docs/fields/rich-text) field features a `Link` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
|
||||
| `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. |
|
||||
| `meta` | Metadata overrides to apply to the [Admin panel](../admin/overview). Included properties are `description` and `openGraph`. |
|
||||
| `preview` | Function to generate preview URLS within the Admin panel that can point to your app. [More](#preview). |
|
||||
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More](/docs/live-preview/overview). |
|
||||
| `components` | Swap in your own React components to be used within this collection. [More](/docs/admin/components#collections) |
|
||||
| `listSearchableFields` | Specify which fields should be searched in the List search view. [More](#list-searchable-fields) |
|
||||
| **`pagination`** | Set pagination-specific options for this collection. [More](#pagination) |
|
||||
| Option | Description |
|
||||
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `group` | Text used as a label for grouping collection and global links together in the navigation. |
|
||||
| `hidden` | Set to true or a function, called with the current user, returning true to exclude this collection from navigation and admin routing. |
|
||||
| `hooks` | Admin-specific hooks for this collection. [More](#admin-hooks) |
|
||||
| `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. |
|
||||
| `forceRenderAllFields ` | Forces all fields in the Edit view to render immediately, regardless of scroll position. By default, this is set to `false` to improve performance, as fields are progressively rendered to balance load times. Enabling this option can make it easier to locate fields using browser search (e.g., `CMD+F`). |
|
||||
| `hideAPIURL` | Hides the "API URL" meta field while editing documents within this collection. |
|
||||
| `enableRichTextLink` | The [Rich Text](/docs/fields/rich-text) field features a `Link` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
|
||||
| `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. |
|
||||
| `preview` | Function to generate preview URLS within the Admin panel that can point to your app. [More](#preview). |
|
||||
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More](/docs/live-preview/overview). |
|
||||
| `components` | Swap in your own React components to be used within this collection. [More](/docs/admin/components#collections) |
|
||||
| `listSearchableFields` | Specify which fields should be searched in the List search view. [More](#list-searchable-fields) |
|
||||
| **`pagination`** | Set pagination-specific options for this collection. [More](#pagination) |
|
||||
|
||||
## Preview
|
||||
### Preview
|
||||
|
||||
Collection `admin` options can accept a `preview` function that will be used to generate a link pointing to the frontend
|
||||
of your app to preview data.
|
||||
@@ -126,7 +128,7 @@ export const Posts: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
## Pagination
|
||||
### Pagination
|
||||
|
||||
Here are a few options that you can specify options for pagination on a collection-by-collection basis:
|
||||
|
||||
@@ -135,23 +137,23 @@ Here are a few options that you can specify options for pagination on a collecti
|
||||
| `defaultLimit` | Integer that specifies the default per-page limit that should be used. Defaults to 10. |
|
||||
| `limits` | Provide an array of integers to use as per-page options for admins to choose from in the List view. |
|
||||
|
||||
## Access control
|
||||
### Access control
|
||||
|
||||
You can specify extremely granular access control (what users can do with documents in a collection) on a collection by
|
||||
collection basis. To learn more, go to the [Access Control](/docs/access-control/overview) docs.
|
||||
|
||||
## Hooks
|
||||
### Hooks
|
||||
|
||||
Hooks are a powerful way to extend collection functionality and execute your own logic, and can be defined on a
|
||||
collection by collection basis. To learn more, go to the [Hooks](/docs/hooks/overview) documentation.
|
||||
|
||||
## Field types
|
||||
### Field types
|
||||
|
||||
Collections support all field types that Payload has to offer—including simple fields like text and checkboxes all the
|
||||
way to more complicated layout-building field groups like Blocks. [Click here](/docs/fields/overview) to learn more
|
||||
about field types.
|
||||
|
||||
## List Searchable Fields
|
||||
### List Searchable Fields
|
||||
|
||||
In the List view, there is a "search" box that allows you to quickly find a document with a search. By default, it
|
||||
searches on the ID field. If you have `admin.useAsTitle` defined, the list search will use that field. However, you can
|
||||
@@ -163,13 +165,61 @@ add `admin.listSearchableFields: ['title', 'metaDescription', 'tags']` - and the
|
||||
those three fields plus the ID field.
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
If you are adding <strong>listSearchableFields</strong>, make sure you index each of these fields
|
||||
so your admin queries can remain performant.
|
||||
**Note:**
|
||||
|
||||
If you are adding **listSearchableFields**, make sure you index each of these fields
|
||||
so your admin queries can remain performant.
|
||||
|
||||
</Banner>
|
||||
|
||||
## TypeScript
|
||||
### Admin Hooks
|
||||
|
||||
In addition to collection hooks themselves, Payload provides for admin UI-specific hooks that you can leverage.
|
||||
|
||||
**`beforeDuplicate`**
|
||||
|
||||
The `beforeDuplicate` hook is an async function that accepts an object containing the data to duplicate, as well as the
|
||||
locale of the doc to duplicate. Within this hook, you can modify the data to be duplicated, which is useful in cases
|
||||
where you have unique fields that need to be incremented or similar, as well as if you want to automatically modify a
|
||||
document's `title`.
|
||||
|
||||
Example:
|
||||
|
||||
```ts
|
||||
import { BeforeDuplicate, CollectionConfig } from 'payload/types'
|
||||
// Your auto-generated Page type
|
||||
import { Page } from '../payload-types.ts'
|
||||
|
||||
const beforeDuplicate: BeforeDuplicate<Page> = ({ data }) => {
|
||||
return {
|
||||
...data,
|
||||
title: `${data.title} Copy`,
|
||||
uniqueField: data.uniqueField ? `${data.uniqueField}-copy` : '',
|
||||
}
|
||||
}
|
||||
|
||||
export const Page: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
admin: {
|
||||
hooks: {
|
||||
beforeDuplicate,
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'uniqueField',
|
||||
type: 'text',
|
||||
unique: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
You can import collection types as follows:
|
||||
|
||||
|
||||
81
docs/configuration/express.mdx
Normal file
81
docs/configuration/express.mdx
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: Express
|
||||
label: Express
|
||||
order: 60
|
||||
desc: Payload utilizes Express middleware packages, you can customize how they work by passing in configuration options.
|
||||
keywords: config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
Payload utilizes a few Express-specific middleware packages within its own routers. You can customize how they work by passing in configuration options to the main Payload config's `express` property.
|
||||
|
||||
### Custom Middleware
|
||||
|
||||
Payload allows you to pass in custom Express middleware to be used on all of the routes it opens. This is useful for adding logging or any other custom functionality to your endpoints.
|
||||
|
||||
There are 2 exposed properties. Each property is an array of middleware functions.
|
||||
|
||||
- `preMiddleware` - runs before any of the Payload middleware
|
||||
- `postMiddleware` - runs after all of the Payload middleware
|
||||
|
||||
```ts
|
||||
{
|
||||
express: {
|
||||
preMiddleware: [
|
||||
(req, res, next) => {
|
||||
// do something
|
||||
next()
|
||||
}
|
||||
],
|
||||
postMiddleware: [
|
||||
(req, res, next) => {
|
||||
// do something
|
||||
next()
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// Example logging middleware function
|
||||
const requestLoggerMiddleware = (req, res, next) => {
|
||||
req.payload.logger.info(`request: ${req.method} ${req.url}`)
|
||||
next()
|
||||
}
|
||||
```
|
||||
|
||||
### JSON
|
||||
|
||||
`express.json()` is used to parse JSON body content into JavaScript objects accessible on the Express `req`. Payload allows you to customize all of the `json` method's options. Common examples of customization use-cases are increasing the max allowed JSON body size which defaults to `2MB`.
|
||||
|
||||
**Example payload.config.js for how to increase the max JSON size allowed to be sent to Payload endpoints:**
|
||||
|
||||
```js
|
||||
{
|
||||
express: {
|
||||
json: {
|
||||
limit: '4mb',
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can find a list of all available options that are able to be passed to `express.json()` [here](https://expressjs.com/en/api.html).
|
||||
|
||||
### Compression
|
||||
|
||||
Payload uses the `compression` package to optimize transfer size for all of the routes it opens, and you can pass customization options through the Payload config.
|
||||
|
||||
To customize compression options, pass an object to the Payload config's `express` property.
|
||||
|
||||
**Example payload.config.js:**
|
||||
|
||||
```js
|
||||
{
|
||||
express: {
|
||||
compression: {
|
||||
// settings go here
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Typically, the default options for this package are suitable. However, for a list of all available customization options, [click here](http://expressjs.com/en/resources/middleware/compression.html).
|
||||
@@ -3,12 +3,16 @@ title: Global Configs
|
||||
label: Globals
|
||||
order: 30
|
||||
desc: Set up your Global config for your needs by defining fields, adding slugs and labels, establishing access control, tying in hooks and more.
|
||||
keywords: globals, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: globals, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
Global configs are in many ways similar to [Collections](/docs/configuration/collections). The big difference is that Collections will potentially contain _many_ documents, while a Global is a "one-off". Globals are perfect for things like header nav, site-wide banner alerts, app-wide localized strings, and other "global" data that your site or app might rely on.
|
||||
Global configs are in many ways similar to [Collections](/docs/configuration/collections). The big difference is that
|
||||
Collections will potentially contain _many_ documents, while a Global is a "one-off". Globals are perfect for things
|
||||
like header nav, site-wide banner alerts, app-wide localized strings, and other "global" data that your site or app
|
||||
might rely on.
|
||||
|
||||
As with Collection configs, it's often best practice to write your Globals in separate files and then import them into the main Payload config.
|
||||
As with Collection configs, it's often best practice to write your Globals in separate files and then import them into
|
||||
the main Payload config.
|
||||
|
||||
## Options
|
||||
|
||||
@@ -26,11 +30,11 @@ As with Collection configs, it's often best practice to write your Globals in se
|
||||
| **`graphQL.name`** | Text used in schema generation. Auto-generated from slug if not defined. |
|
||||
| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`dbName`** | Custom table or collection name for this global depending on the database adapter. Auto-generated from slug if not defined.
|
||||
| **`dbName`** | Custom table or collection name for this global depending on the database adapter. Auto-generated from slug if not defined. |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Simple Global example
|
||||
#### Simple Global example
|
||||
|
||||
```ts
|
||||
import { GlobalConfig } from 'payload/types'
|
||||
@@ -58,29 +62,33 @@ const Nav: GlobalConfig = {
|
||||
export default Nav
|
||||
```
|
||||
|
||||
### Global config example
|
||||
#### Global config example
|
||||
|
||||
You can find a few [example Global configs](https://github.com/payloadcms/public-demo/tree/master/src/payload/globals) in the Public Demo source code on GitHub.
|
||||
You can find a few [example Global configs](https://github.com/payloadcms/public-demo/tree/master/src/payload/globals)
|
||||
in the Public Demo source code on GitHub.
|
||||
|
||||
## Admin options
|
||||
### Admin options
|
||||
|
||||
You can customize the way that the Admin panel behaves on a Global-by-Global basis by defining the `admin` property on a Global's config.
|
||||
You can customize the way that the Admin panel behaves on a Global-by-Global basis by defining the `admin` property on a
|
||||
Global's config.
|
||||
|
||||
| Option | Description |
|
||||
| ------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `group` | Text used as a label for grouping collection and global links together in the navigation. |
|
||||
| `hidden` | Set to true or a function, called with the current user, returning true to exclude this global from navigation and admin routing. |
|
||||
| `components` | Swap in your own React components to be used within this Global. [More](/docs/admin/components#globals) |
|
||||
| `preview` | Function to generate a preview URL within the Admin panel for this global that can point to your app. [More](#preview). |
|
||||
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More](/docs/live-preview/overview). |
|
||||
| `hideAPIURL` | Hides the "API URL" meta field while editing documents within this collection. |
|
||||
| `meta` | Metadata overrides to apply to the [Admin panel](../admin/overview). Included properties are `description` and `openGraph`. |
|
||||
| Option | Description |
|
||||
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `group` | Text used as a label for grouping collection and global links together in the navigation. |
|
||||
| `hidden` | Set to true or a function, called with the current user, returning true to exclude this global from navigation and admin routing. |
|
||||
| `components` | Swap in your own React components to be used within this Global. [More](/docs/admin/components#globals) |
|
||||
| `preview` | Function to generate a preview URL within the Admin panel for this global that can point to your app. [More](#preview). |
|
||||
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More](/docs/live-preview/overview). |
|
||||
| `hideAPIURL` | Hides the "API URL" meta field while editing documents within this collection. |
|
||||
| `forceRenderAllFields ` | Forces all fields in the Edit view to render immediately, regardless of scroll position. By default, this is set to `false` to improve performance, as fields are progressively rendered to balance load times. Enabling this option can make it easier to locate fields using browser search (e.g., `CMD+F`). |
|
||||
|
||||
## Preview
|
||||
### Preview
|
||||
|
||||
Global `admin` options can accept a `preview` function that will be used to generate a link pointing to the frontend of your app to preview data.
|
||||
Global `admin` options can accept a `preview` function that will be used to generate a link pointing to the frontend of
|
||||
your app to preview data.
|
||||
|
||||
If the function is specified, a Preview button will automatically appear in the corresponding global's Edit view. Clicking the Preview button will link to the URL that is generated by the function.
|
||||
If the function is specified, a Preview button will automatically appear in the corresponding global's Edit view.
|
||||
Clicking the Preview button will link to the URL that is generated by the function.
|
||||
|
||||
**The preview function accepts two arguments:**
|
||||
|
||||
@@ -113,19 +121,24 @@ export const MyGlobal: GlobalConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
## Access control
|
||||
### Access control
|
||||
|
||||
As with Collections, you can specify extremely granular access control (what users can do with this Global) on a Global-by-Global basis. However, Globals only have `update` and `read` access control due to their nature of only having one document. To learn more, go to the [Access Control](/docs/access-control/overview) docs.
|
||||
As with Collections, you can specify extremely granular access control (what users can do with this Global) on a
|
||||
Global-by-Global basis. However, Globals only have `update` and `read` access control due to their nature of only having
|
||||
one document. To learn more, go to the [Access Control](/docs/access-control/overview) docs.
|
||||
|
||||
## Hooks
|
||||
### Hooks
|
||||
|
||||
Globals also fully support a smaller subset of Hooks. To learn more, go to the [Hooks](/docs/hooks/overview) documentation.
|
||||
Globals also fully support a smaller subset of Hooks. To learn more, go to the [Hooks](/docs/hooks/overview)
|
||||
documentation.
|
||||
|
||||
## Field types
|
||||
### Field types
|
||||
|
||||
Globals support all field types that Payload has to offer—including simple fields like text and checkboxes all the way to more complicated layout-building field groups like Blocks. [Click here](/docs/fields/overview) to learn more about field types.
|
||||
Globals support all field types that Payload has to offer—including simple fields like text and checkboxes all the way
|
||||
to more complicated layout-building field groups like Blocks. [Click here](/docs/fields/overview) to learn more about
|
||||
field types.
|
||||
|
||||
## TypeScript
|
||||
### TypeScript
|
||||
|
||||
You can import global types as follows:
|
||||
|
||||
|
||||
@@ -3,43 +3,12 @@ title: I18n
|
||||
label: I18n
|
||||
order: 40
|
||||
desc: Manage and customize internationalization support in your CMS editor experience
|
||||
keywords: internationalization, i18n, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: internationalization, i18n, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
Not only does Payload support managing localized content, it also has internationalization support so that users in the [Admin Panel](../admin/overview) can work in their preferred language. Payload's i18n comes by default without any additional setup, and you can extend the default settings to customize the language settings to your needs.
|
||||
Not only does Payload support managing localized content, it also has internationalization support so that admin users can work in their preferred language. Payload's i18n support is built on top of [i18next](https://www.i18next.com). It comes included by default and can be extended in your config.
|
||||
|
||||
### Configuration Options
|
||||
|
||||
In your Payload config, you can add translations and customize the settings in `i18n`. Payload will use your custom options and merge it with the default, allowing you to override the settings Payload provides.
|
||||
|
||||
**Example Payload config extending i18n:**
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
export default buildConfig({
|
||||
//...
|
||||
i18n: {
|
||||
fallbackLng: 'en', // default
|
||||
debug: false, // default
|
||||
resources: {
|
||||
en: {
|
||||
custom: {
|
||||
// namespace can be anything you want
|
||||
key1: 'Translation with {{variable}}', // translation
|
||||
},
|
||||
// override existing translation keys
|
||||
general: {
|
||||
dashboard: 'Home',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
//...
|
||||
})
|
||||
```
|
||||
|
||||
While Payload's built-in features come translated, you may want to also translate parts of your project's configuration too. This is possible in places like Collections and Globals labels and groups, field labels, descriptions and input placeholder text. The admin UI will display all the correct translations you provide based on the user's language.
|
||||
While Payload's built-in features come translated, you may want to also translate parts of your project's configuration too. This is possible in places like collections and globals labels and groups, field labels, descriptions and input placeholder text. The admin UI will display all the correct translations you provide based on the user's language.
|
||||
|
||||
Here is an example of a simple collection supporting both English and Spanish editors:
|
||||
|
||||
@@ -87,25 +56,27 @@ export const Articles: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
## Admin UI
|
||||
### Admin UI
|
||||
|
||||
The [Admin Panel](../admin/overview) reads the language settings of a user's browser and display all text in that language, or will fall back to English if the user's language is not yet supported.
|
||||
The Payload admin panel reads the language settings of a user's browser and display all text in that language, or will fall back to English if the user's language is not yet supported.
|
||||
After a user logs in, they can change their language selection in the `/account` view.
|
||||
|
||||
<Banner>
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
**Note:**
|
||||
|
||||
If there is a language that Payload does not yet support, we accept code
|
||||
[contributions](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md).
|
||||
</Banner>
|
||||
|
||||
## Node
|
||||
### Node Express
|
||||
|
||||
Payload's backend sets the language on incoming requests before they are handled. This allows backend validation to return error messages in the user's own language or system generated emails to be sent using the correct translation. You can make HTTP requests with the `accept-language` header and Payload will use that language.
|
||||
Payload's backend uses express middleware to set the language on incoming requests before they are handled. This allows backend validation to return error messages in the user's own language or system generated emails to be sent using the correct translation. You can make HTTP requests with the `accept-language` header and Payload will use that language.
|
||||
|
||||
Anywhere in your Payload app that you have access to the `req` object, you can access payload's extensive internationalization features assigned to `req.i18n`. To access text translations you can use `req.t('namespace:key')`.
|
||||
Anywhere in your Payload app that you have access to the `req` object, you can access i18next's extensive internationalization features assigned to `req.i18n`. To access text translations you can use `req.t('namespace:key')`.
|
||||
|
||||
## Configuration Options
|
||||
Read the i18next [API documentation](https://www.i18next.com/overview/api) to learn more.
|
||||
|
||||
### Configuration Options
|
||||
|
||||
In your Payload config, you can add translations and customize the settings in `i18n`. Payload will use your custom options and merge it with the default, allowing you to override the settings Payload provides.
|
||||
|
||||
@@ -117,8 +88,9 @@ import { buildConfig } from 'payload/config'
|
||||
export default buildConfig({
|
||||
//...
|
||||
i18n: {
|
||||
fallbackLanguage: 'en', // default
|
||||
translations: {
|
||||
fallbackLng: 'en', // default
|
||||
debug: false, // default
|
||||
resources: {
|
||||
en: {
|
||||
custom: {
|
||||
// namespace can be anything you want
|
||||
@@ -135,63 +107,4 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
## Types for custom translations
|
||||
|
||||
In order to use custom translations in your project, you need to provide the types for the translations. Here is an example of how you can define the types for the custom translations in a custom react component:
|
||||
|
||||
```ts
|
||||
'use client'
|
||||
import type { NestedKeysStripped } from '@payloadcms/translations'
|
||||
import type React from 'react'
|
||||
|
||||
import { useTranslation } from '@payloadcms/ui/providers/Translation'
|
||||
|
||||
const customTranslations = {
|
||||
en: {
|
||||
general: {
|
||||
test: 'Custom Translation',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type CustomTranslationObject = typeof customTranslations.en
|
||||
type CustomTranslationKeys = NestedKeysStripped<CustomTranslationObject>
|
||||
|
||||
export const MyComponent: React.FC = () => {
|
||||
const { i18n, t } = useTranslation<CustomTranslationObject, CustomTranslationKeys>() // These generics merge your custom translations with the default client translations
|
||||
|
||||
return t('general:test')
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Additionally, payload exposes the `t` function in various places, for example in labels. Here is how you would type those:
|
||||
|
||||
```ts
|
||||
import type {
|
||||
DefaultTranslationKeys,
|
||||
NestedKeysStripped,
|
||||
TFunction,
|
||||
} from '@payloadcms/translations'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
const customTranslations = {
|
||||
en: {
|
||||
general: {
|
||||
test: 'Custom Translation',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type CustomTranslationObject = typeof customTranslations.en
|
||||
type CustomTranslationKeys = NestedKeysStripped<CustomTranslationObject>
|
||||
|
||||
const field: Field = {
|
||||
name: 'myField',
|
||||
type: 'text',
|
||||
label: (
|
||||
{ t }: { t: TFunction<CustomTranslationKeys | DefaultTranslationKeys> }, // The generic passed to TFunction does not automatically merge the custom translations with the default translations. We need to merge them ourselves here
|
||||
) => t('fields:addLabel'),
|
||||
}
|
||||
```
|
||||
|
||||
See the i18next [configuration options](https://www.i18next.com/overview/configuration-options) to learn more.
|
||||
|
||||
@@ -3,13 +3,13 @@ title: Localization
|
||||
label: Localization
|
||||
order: 50
|
||||
desc: Add and maintain as many locales as you need by adding Localization to your Payload config, set options for default locale, fallbacks, fields and more.
|
||||
keywords: localization, internationalization, i18n, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: localization, internationalization, i18n, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
Payload features deep field-based localization support. Maintaining as many locales as you need is easy. All
|
||||
localization support is opt-in by default. To do so, follow the two steps below.
|
||||
|
||||
## Enabling in the Payload config
|
||||
### Enabling in the Payload config
|
||||
|
||||
Add the `localization` property to your Payload config to enable localization project-wide. You'll need to provide a
|
||||
list of all locales that you'd like to support as well as set a few other options.
|
||||
@@ -24,8 +24,8 @@ export default buildConfig({
|
||||
// collections go here
|
||||
],
|
||||
localization: {
|
||||
locales: ['en', 'es', 'de'],
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'es', 'de'], // required
|
||||
defaultLocale: 'en', // required
|
||||
fallback: true,
|
||||
},
|
||||
})
|
||||
@@ -54,7 +54,7 @@ export default buildConfig({
|
||||
rtl: true,
|
||||
},
|
||||
],
|
||||
defaultLocale: 'en',
|
||||
defaultLocale: 'en', // required
|
||||
fallback: true,
|
||||
},
|
||||
})
|
||||
@@ -87,7 +87,7 @@ export default buildConfig({
|
||||
code: 'nb',
|
||||
},
|
||||
],
|
||||
defaultLocale: 'en',
|
||||
defaultLocale: 'en', // required
|
||||
fallback: true,
|
||||
},
|
||||
})
|
||||
@@ -103,10 +103,10 @@ right-to-left), and `fallbackLocale` property. The locale codes do not need to b
|
||||
to define how to represent your locales. Common patterns are to use two-letter ISO 639 language codes or four-letter
|
||||
language and country codes (ISO 3166‑1) such as `en-US`, `en-UK`, `es-MX`, etc.
|
||||
|
||||
## Locale Object Properties
|
||||
### Locale Properties:
|
||||
|
||||
| Option | Description |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
||||
|----------------------|--------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`code`** \* | Unique code to identify the language throughout the APIs for `locale` and `fallbackLocale` |
|
||||
| **`label`** | A string to use for the selector when choosing a language, or an object keyed on the i18n keys for different languages in use. |
|
||||
| **`rtl`** | A boolean that when true will make the admin UI display in Right-To-Left. |
|
||||
@@ -125,7 +125,7 @@ Boolean enabling "fallback" locale functionality. If a document is requested in
|
||||
localized value corresponding to the requested locale, then if this property is enabled, the document will automatically
|
||||
fall back to the fallback locale value. If this property is not enabled, the value will not be populated.
|
||||
|
||||
## Field by field localization
|
||||
### Field by field localization
|
||||
|
||||
Payload localization works on a **field** level—not a document level. In addition to configuring the base Payload config
|
||||
to support localization, you need to specify each field that you would like to localize.
|
||||
@@ -149,8 +149,8 @@ All field types with a `name` property support the `localized` property—even t
|
||||
and `block`s.
|
||||
|
||||
<Banner>
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
**Note:**
|
||||
|
||||
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
|
||||
@@ -158,20 +158,20 @@ and `block`s.
|
||||
</Banner>
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
**Important:**
|
||||
|
||||
When converting an existing field to or from `localized: true` the data structure in the document
|
||||
will change for this field and so existing data for this field will be lost. Before changing the
|
||||
localization setting on fields with existing data, you may need to consider a field migration
|
||||
strategy.
|
||||
</Banner>
|
||||
|
||||
## Retrieving localized docs
|
||||
### Retrieving localized docs
|
||||
|
||||
When retrieving documents, you can specify which locale you'd like to receive as well as which fallback locale should be
|
||||
used.
|
||||
|
||||
#### REST API
|
||||
##### REST API
|
||||
|
||||
REST API locale functionality relies on URL query parameters.
|
||||
|
||||
@@ -190,7 +190,7 @@ valid locale as provided to your base Payload config, or `'null'`, `'false'`, or
|
||||
fetch('https://localhost:3000/api/pages?locale=es&fallback-locale=none');
|
||||
```
|
||||
|
||||
#### GraphQL API
|
||||
##### GraphQL API
|
||||
|
||||
In the GraphQL API, you can specify `locale` and `fallbackLocale` args to all relevant queries and mutations.
|
||||
|
||||
@@ -204,11 +204,11 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -218,7 +218,7 @@ query {
|
||||
arguments in nested related document queries.
|
||||
</Banner>
|
||||
|
||||
#### Local API
|
||||
##### Local API
|
||||
|
||||
You can specify `locale` as well as `fallbackLocale` within the Local API as well as properties on the `options`
|
||||
argument. The `locale` property will accept any valid locale, and the `fallbackLocale` property will accept any valid
|
||||
@@ -235,9 +235,9 @@ const posts = await payload.find({
|
||||
```
|
||||
|
||||
<Banner type="alert">
|
||||
<strong>Tip:</strong>
|
||||
<br />
|
||||
**Tip:**
|
||||
|
||||
The REST and Local APIs can return all localization data in one request by passing 'all' or '*' as
|
||||
the <strong>locale</strong> parameter. The response will be structured so that field values come
|
||||
the **locale** parameter. The response will be structured so that field values come
|
||||
back as the full objects keyed for each locale instead of the single, translated value.
|
||||
</Banner>
|
||||
|
||||
@@ -3,7 +3,7 @@ title: The Payload Config
|
||||
label: Overview
|
||||
order: 10
|
||||
desc: The Payload config is central to everything that Payload does, from adding custom React components, to modifying collections, controlling localization and much more.
|
||||
keywords: overview, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
keywords: overview, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
Payload is a _config-based_, code-first CMS and application framework. The Payload config is central to everything that Payload does. It scaffolds the data that Payload stores as well as maintains custom React components, hook logic, custom validations, and much more.
|
||||
@@ -11,56 +11,63 @@ Payload is a _config-based_, code-first CMS and application framework. The Paylo
|
||||
**Also, because the Payload source code is fully written in TypeScript, its configs are strongly typed—meaning that even if you aren't using TypeScript, your IDE (such as VSCode) may still provide helpful information like type-ahead suggestions while you write your config.**
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
The serializable portions of this config are included in the Payload Admin bundle, so make sure you do not embed any sensitive
|
||||
**Important:**
|
||||
|
||||
This file is included in the Payload admin bundle, so make sure you do not embed any sensitive
|
||||
information.
|
||||
</Banner>
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Description |
|
||||
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `admin` \* | Base Payload admin configuration. Specify, custom components, control metadata, set the Admin user collection, and [more](/docs/admin/overview#admin-options). Required. |
|
||||
| `editor` \* | Rich Text Editor which will be used by richText fields. Required. |
|
||||
| `db` \* | Database Adapter which will be used by Payload. Read more [here](/docs/database/overview). Required. |
|
||||
| `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). |
|
||||
| `globals` | An array of all Globals that Payload will manage. For more on Globals and their configs, [click here](/docs/configuration/globals). |
|
||||
| `cors` | Either a whitelist array of URLS to allow CORS requests from, or a wildcard string (`'*'`) to accept incoming requests from any domain. |
|
||||
| `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-options). |
|
||||
| `cookiePrefix` | A string that will be prefixed to all cookies that Payload sets. |
|
||||
| `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`. |
|
||||
| `indexSortableFields` | Automatically index all sortable top-level fields in the database to improve sort performance and add database compatibility for Azure Cosmos and similar. |
|
||||
| `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) |
|
||||
| `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) |
|
||||
| `custom` | Extension point for adding custom data (e.g. for plugins) |
|
||||
| Option | Description |
|
||||
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `admin` \* | Base Payload admin configuration. Specify bundler*, custom components, control metadata, set the Admin user collection, and [more](/docs/admin/overview#admin-options). Required. |
|
||||
| `editor` \* | Rich Text Editor which will be used by richText fields. Required. |
|
||||
| `db` \* | Database Adapter which will be used by Payload. Read more [here](/docs/database/overview). Required. |
|
||||
| `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). |
|
||||
| `globals` | An array of all Globals that Payload will manage. For more on Globals and their configs, [click here](/docs/configuration/globals). |
|
||||
| `cors` | Either a whitelist array of URLS to allow CORS requests from, or a wildcard string (`'*'`) to accept incoming requests from any domain. |
|
||||
| `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-options). |
|
||||
| `cookiePrefix` | A string that will be prefixed to all cookies that Payload sets. |
|
||||
| `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`. |
|
||||
| `indexSortableFields` | Automatically index all sortable top-level fields in the database to improve sort performance and add database compatibility for Azure Cosmos and similar. |
|
||||
| `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) |
|
||||
| `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) |
|
||||
| `custom` | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
### Simple example
|
||||
#### Simple example
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config'
|
||||
import { mongooseAdapter } from '@payloadcms/db-mongodb'
|
||||
import { postgresAdapter } from '@payloadcms/db-postgres' // beta
|
||||
|
||||
import { viteBundler } from '@payloadcms/bundler-vite'
|
||||
import { webpackBundler } from '@payloadcms/bundler-webpack'
|
||||
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical' // beta
|
||||
import { slateEditor } from '@payloadcms/richtext-slate'
|
||||
|
||||
export default buildConfig({
|
||||
db: mongooseAdapter({}) // or postgresAdapter({}),
|
||||
editor: lexicalEditor({}) // or slateEditor({})
|
||||
admin: {
|
||||
bundler: webpackBundler(), // or viteBundler()
|
||||
},
|
||||
db: mongooseAdapter({}), // or postgresAdapter({})
|
||||
editor: lexicalEditor({}), // or slateEditor({})
|
||||
collections: [
|
||||
{
|
||||
slug: 'pages',
|
||||
@@ -99,11 +106,11 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Full example config
|
||||
#### Full example config
|
||||
|
||||
You can see a full [example config](https://github.com/payloadcms/public-demo/blob/master/src/payload/payload.config.ts) in the Public Demo source code on GitHub.
|
||||
|
||||
## Using environment variables in your config
|
||||
### Using environment variables in your config
|
||||
|
||||
We suggest using the `dotenv` package to handle environment variables alongside of Payload. All that's necessary to do is to require the package as high up in your application as possible (for example, at the top of your `server.js` file), and ensure that it can find an `.env` file that you create.
|
||||
|
||||
@@ -128,14 +135,14 @@ project-name
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
<br />
|
||||
**Important:**
|
||||
|
||||
If you use an environment variable to configure any properties that are required for the Admin
|
||||
panel to function (ex. serverURL or any routes), you need to make sure that your Admin panel code
|
||||
can access it. [Click here](/docs/admin/environment-vars) for more info.
|
||||
can access it. [Click here](/docs/admin/webpack#admin-environment-vars) for more info.
|
||||
</Banner>
|
||||
|
||||
## Customizing & Automating Config Location Detection
|
||||
### Customizing & Automating Config Location Detection
|
||||
|
||||
Payload is designed to automatically locate your configuration file. By default, it will first look in the root of your current working directory for a file named `payload.config.js` or `payload.config.ts` if you're using TypeScript.
|
||||
|
||||
@@ -145,7 +152,7 @@ In production mode, Payload will first attempt to find the config file in the ou
|
||||
|
||||
Please ensure your `tsconfig.json` is properly configured if you want Payload to accurately auto-detect your configuration file location. If `tsconfig.json` does not exist or doesn't specify `rootDir` or `outDir`, Payload will default to the current working directory.
|
||||
|
||||
### Overriding the Config Location
|
||||
#### Overriding the Config Location
|
||||
|
||||
In addition to the above automated detection, you can specify your own location for the Payload config file. This is done by using the environment variable `PAYLOAD_CONFIG_PATH`. The path you provide via this environment variable can either be absolute or relative to your current working directory. This can be useful in situations where your Payload config is not in a standard location, or you wish to switch between multiple configurations.
|
||||
|
||||
@@ -161,11 +168,11 @@ In addition to the above automated detection, you can specify your own location
|
||||
|
||||
When `PAYLOAD_CONFIG_PATH` is set, Payload will use this path to load the configuration, bypassing all automated detection.
|
||||
|
||||
## Developing within the Config
|
||||
### Developing within the Config
|
||||
|
||||
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!
|
||||
|
||||
## TypeScript
|
||||
### TypeScript
|
||||
|
||||
You can import config types as follows:
|
||||
|
||||
@@ -183,7 +190,7 @@ import { SanitizedConfig } from 'payload/config'
|
||||
// Generally, this is only used internally by Payload.
|
||||
```
|
||||
|
||||
## Telemetry
|
||||
### 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.
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user