mirror of
https://github.com/gogs/gogs.git
synced 2026-02-28 17:20:59 +01:00
Compare commits
858 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23f2efa8c1 | ||
|
|
5791e1398c | ||
|
|
9b72661767 | ||
|
|
0958fe5a4e | ||
|
|
4a1dc29e23 | ||
|
|
2ca668e79e | ||
|
|
ee59016585 | ||
|
|
7ac09681a2 | ||
|
|
1863f38286 | ||
|
|
6b6f54b79b | ||
|
|
b67ec01d41 | ||
|
|
2c154ccbe7 | ||
|
|
3f95824e65 | ||
|
|
29722af1ae | ||
|
|
563fc7c6d7 | ||
|
|
9b8fa69c15 | ||
|
|
f5dc436441 | ||
|
|
d9d329bec8 | ||
|
|
76879e977b | ||
|
|
9c3c9a8eb9 | ||
|
|
2fd69f13d9 | ||
|
|
eb66060cd7 | ||
|
|
8a19f8a63c | ||
|
|
f7c11a27d0 | ||
|
|
418dab9b96 | ||
|
|
09ad42b918 | ||
|
|
074c92b0a3 | ||
|
|
e893e1fc63 | ||
|
|
6622b7b49a | ||
|
|
afab38b0d7 | ||
|
|
ede58ade4c | ||
|
|
a2dd9ec2e1 | ||
|
|
591c333dcd | ||
|
|
6971143dc5 | ||
|
|
5d6ea4a81b | ||
|
|
2374a3ef64 | ||
|
|
0d9e435bfe | ||
|
|
9af0dd23dd | ||
|
|
39eb6df769 | ||
|
|
c0f25d24b2 | ||
|
|
4f778da35e | ||
|
|
f0f8205b8b | ||
|
|
2bb1de1805 | ||
|
|
0081c6911d | ||
|
|
2527037973 | ||
|
|
1d951cfc49 | ||
|
|
32a0255ce3 | ||
|
|
be3a13a0d6 | ||
|
|
d293aa9ced | ||
|
|
0cfcaca351 | ||
|
|
412ba5b2a9 | ||
|
|
c17f93e6c0 | ||
|
|
a5afa37203 | ||
|
|
6bfbed0616 | ||
|
|
185c90df12 | ||
|
|
7463d9c51d | ||
|
|
ec5a967937 | ||
|
|
4bc98f7aa2 | ||
|
|
207960b459 | ||
|
|
7b3b46c675 | ||
|
|
5e01ecbc05 | ||
|
|
c98aa0e895 | ||
|
|
263203ec28 | ||
|
|
84f28fc5d6 | ||
|
|
9144ea2b1d | ||
|
|
0d73dcaf0f | ||
|
|
a6a3afd130 | ||
|
|
7aa53635fe | ||
|
|
2ee0c61e62 | ||
|
|
3d23c13160 | ||
|
|
e2afd886fd | ||
|
|
aa682b3b7e | ||
|
|
e43479d948 | ||
|
|
a2f2f7717a | ||
|
|
740192564b | ||
|
|
8bcc0e392e | ||
|
|
ae319da5fd | ||
|
|
6d6848af5c | ||
|
|
0d60b58434 | ||
|
|
ad57f18894 | ||
|
|
80cd8f6a29 | ||
|
|
8cb903fbbb | ||
|
|
d1c327d508 | ||
|
|
eaf57229d3 | ||
|
|
4e967bc765 | ||
|
|
64788ffff6 | ||
|
|
3143e35d83 | ||
|
|
8d5a693382 | ||
|
|
1e185787a9 | ||
|
|
1b2ecde1c9 | ||
|
|
e8c3e9bcf8 | ||
|
|
cdfcef04a1 | ||
|
|
8aa35577b3 | ||
|
|
c4086d43db | ||
|
|
8059175a5c | ||
|
|
f8fd084bd2 | ||
|
|
d528704503 | ||
|
|
98076ee72d | ||
|
|
e6ef75204b | ||
|
|
d674cb7f20 | ||
|
|
114c179e5a | ||
|
|
598e062241 | ||
|
|
bf70082c22 | ||
|
|
2994272e91 | ||
|
|
cdedc2d188 | ||
|
|
42a3bbb0f4 | ||
|
|
eb79532812 | ||
|
|
5feb68a589 | ||
|
|
03f7f3ee67 | ||
|
|
6383bf7480 | ||
|
|
f471ef1bc7 | ||
|
|
7ebe0a9916 | ||
|
|
89e93fe01e | ||
|
|
0f8a5fdf49 | ||
|
|
3c91c9063b | ||
|
|
e629c7583b | ||
|
|
539b07e205 | ||
|
|
16d3e7085e | ||
|
|
c47fbc629b | ||
|
|
d04b19545d | ||
|
|
3b0e2c1c3f | ||
|
|
79a2745b4a | ||
|
|
6cc992ea54 | ||
|
|
3376354ed8 | ||
|
|
c2e1370588 | ||
|
|
93616fe776 | ||
|
|
e5972bbcde | ||
|
|
952e510dfa | ||
|
|
8f442dde03 | ||
|
|
c1e53cdc72 | ||
|
|
e9f6a43073 | ||
|
|
67380cf47b | ||
|
|
39fdb0f9c4 | ||
|
|
85fbd6e9c6 | ||
|
|
2cfdce88e0 | ||
|
|
ca6cbb95cc | ||
|
|
7cb440273c | ||
|
|
d96f2a7184 | ||
|
|
adcb1d7c65 | ||
|
|
04fbfad2d4 | ||
|
|
bab051a8c1 | ||
|
|
ad6de46ce4 | ||
|
|
282f7fb8fa | ||
|
|
5034ef787c | ||
|
|
3925166d31 | ||
|
|
7358e46815 | ||
|
|
8f09fc64bd | ||
|
|
b2de3d71c5 | ||
|
|
44ed991726 | ||
|
|
34b92cdb44 | ||
|
|
d3b2ff17d6 | ||
|
|
cc45a8ab06 | ||
|
|
105c528369 | ||
|
|
5d0b334d56 | ||
|
|
b092733c2e | ||
|
|
baeccdb161 | ||
|
|
394fc61129 | ||
|
|
55dc9d898f | ||
|
|
552d5c7ceb | ||
|
|
95065de39a | ||
|
|
a3ea4b8802 | ||
|
|
6bcff7828f | ||
|
|
2cb5ec5983 | ||
|
|
2bec8a4f1e | ||
|
|
7e15ff9486 | ||
|
|
12445fe2ed | ||
|
|
491407ddf8 | ||
|
|
6da55159a2 | ||
|
|
73fedc7275 | ||
|
|
c50d59874d | ||
|
|
b3d9ca4ccd | ||
|
|
4efaf8e882 | ||
|
|
178556142a | ||
|
|
7c1fbed057 | ||
|
|
bc902b8f74 | ||
|
|
8ee14db51e | ||
|
|
5d35578811 | ||
|
|
d09fca3ca9 | ||
|
|
dadd35b636 | ||
|
|
c2afdf2192 | ||
|
|
152e715999 | ||
|
|
99c2ae7b35 | ||
|
|
cd9b926af7 | ||
|
|
9ac46fb983 | ||
|
|
8516dfcb6c | ||
|
|
c1ecb6c60a | ||
|
|
43297148b2 | ||
|
|
c0c1a4b01b | ||
|
|
47a3243ff1 | ||
|
|
22e14a0a67 | ||
|
|
48a0b5b026 | ||
|
|
16eb2eb6a3 | ||
|
|
e6ec1ca1f8 | ||
|
|
6f90835f95 | ||
|
|
643142acab | ||
|
|
7c31f235da | ||
|
|
4f40019130 | ||
|
|
780cc2d110 | ||
|
|
2a13f682e0 | ||
|
|
28cf0e6aaa | ||
|
|
92fb30c526 | ||
|
|
9f44c26789 | ||
|
|
3738b6399e | ||
|
|
62b0dc4853 | ||
|
|
429c92c0ce | ||
|
|
579e5e4fee | ||
|
|
ba27d71abe | ||
|
|
7115e3a4d5 | ||
|
|
0114fdcba4 | ||
|
|
dad5c15520 | ||
|
|
6e171c5225 | ||
|
|
f0b5c3b90a | ||
|
|
c30b856d14 | ||
|
|
13c106af77 | ||
|
|
ce1e4348da | ||
|
|
13a8823bd3 | ||
|
|
bbca2916f7 | ||
|
|
37305a59ca | ||
|
|
bb359a74f1 | ||
|
|
6b98d58906 | ||
|
|
8dca9f95fa | ||
|
|
f50e568fd1 | ||
|
|
f8a48ffaad | ||
|
|
67fb0fe6a5 | ||
|
|
0b273ac4d5 | ||
|
|
84b56c3c53 | ||
|
|
06602a84ff | ||
|
|
b710f6bd65 | ||
|
|
95bd19c509 | ||
|
|
7c5710d31f | ||
|
|
7f7216be6e | ||
|
|
ec332cf903 | ||
|
|
2c5411b00c | ||
|
|
a00c932bbc | ||
|
|
6f9a95f830 | ||
|
|
8bf57be9ba | ||
|
|
b1504ed99a | ||
|
|
9349def72e | ||
|
|
6cda35a75f | ||
|
|
2625193a48 | ||
|
|
f3c3258921 | ||
|
|
4042d1f0c3 | ||
|
|
4a46613916 | ||
|
|
6c8fcb3af2 | ||
|
|
61e27dedf7 | ||
|
|
94392a7af3 | ||
|
|
cc647ba9d5 | ||
|
|
5e89485cec | ||
|
|
8637e67e6f | ||
|
|
4a19fd6441 | ||
|
|
54e0ada9d5 | ||
|
|
cd89f6c502 | ||
|
|
660e7a178a | ||
|
|
15845cb287 | ||
|
|
d0a0239bac | ||
|
|
7e7613cdec | ||
|
|
a5b88c4d0c | ||
|
|
dccb0c15b9 | ||
|
|
3f7f4852ef | ||
|
|
0f33b04c87 | ||
|
|
fd3b9ca3aa | ||
|
|
f1a5a4277d | ||
|
|
f59d2dd034 | ||
|
|
5be881756b | ||
|
|
4296427214 | ||
|
|
7551141dbe | ||
|
|
5544a7037b | ||
|
|
dbed39ba05 | ||
|
|
aa1fc30b89 | ||
|
|
fa12c282f6 | ||
|
|
25b23c4bc9 | ||
|
|
7eafe3213f | ||
|
|
2cb04db526 | ||
|
|
96f92e6105 | ||
|
|
a47aef5460 | ||
|
|
1dd003bd4c | ||
|
|
70fbcd2f27 | ||
|
|
5850308a37 | ||
|
|
53c573ed02 | ||
|
|
10b47eddd2 | ||
|
|
5a9709fa9d | ||
|
|
7e9b42c87d | ||
|
|
b6c14f8b21 | ||
|
|
5077408d78 | ||
|
|
0885784f13 | ||
|
|
3380c946e1 | ||
|
|
d625e41c6c | ||
|
|
eb1bfe0e59 | ||
|
|
042d350762 | ||
|
|
0f26f3678a | ||
|
|
6a81632e36 | ||
|
|
b756806ee9 | ||
|
|
81e6f82caf | ||
|
|
29f76f3936 | ||
|
|
ea192147ea | ||
|
|
99812a80fd | ||
|
|
8ad92bb8a4 | ||
|
|
b85927e488 | ||
|
|
87b408a2e5 | ||
|
|
fc68fb951c | ||
|
|
5448d29b2e | ||
|
|
bb51eb5188 | ||
|
|
89f71b44f7 | ||
|
|
c5d4a9e046 | ||
|
|
edd786446c | ||
|
|
b0b88d9bc5 | ||
|
|
15b0cbe318 | ||
|
|
9f0c571238 | ||
|
|
f70343660d | ||
|
|
c8b45ecc27 | ||
|
|
9e8a8867ea | ||
|
|
2c82fc3edb | ||
|
|
bbe866122d | ||
|
|
87954dbfeb | ||
|
|
699c71d319 | ||
|
|
21f25799b4 | ||
|
|
90af997fec | ||
|
|
b73318bc62 | ||
|
|
e5bf4281b5 | ||
|
|
0c2b9bbb2b | ||
|
|
99385db0c4 | ||
|
|
90dd0657b5 | ||
|
|
ec92565f23 | ||
|
|
2772791fda | ||
|
|
08c976f811 | ||
|
|
72dd299ca0 | ||
|
|
30fda0f1ae | ||
|
|
ab9c5fb5e7 | ||
|
|
28dc5bb566 | ||
|
|
cf6d321991 | ||
|
|
50422f1fc2 | ||
|
|
db3d393576 | ||
|
|
2f105f3979 | ||
|
|
ee28fd9255 | ||
|
|
13d492e92a | ||
|
|
e7fd65f0cf | ||
|
|
2eeb0ec9b0 | ||
|
|
b1133c9934 | ||
|
|
991ce42c48 | ||
|
|
10dc330640 | ||
|
|
dfab54d5a2 | ||
|
|
3e22ae3412 | ||
|
|
55b4e77a5e | ||
|
|
ad7ea88923 | ||
|
|
f2884d8e31 | ||
|
|
36a63dd059 | ||
|
|
03ba257ad2 | ||
|
|
fe60ca408b | ||
|
|
0402c803c6 | ||
|
|
29ccf047d8 | ||
|
|
8aa0a76702 | ||
|
|
2d76de2574 | ||
|
|
4d8b905541 | ||
|
|
452aefd025 | ||
|
|
899e799459 | ||
|
|
2295fafb34 | ||
|
|
a562228c5e | ||
|
|
e74630ae3b | ||
|
|
1f2e173a74 | ||
|
|
46e96c008c | ||
|
|
256cd6374a | ||
|
|
57a47bbc05 | ||
|
|
024fcc836b | ||
|
|
250be011c7 | ||
|
|
4e822c1911 | ||
|
|
26d52ceb48 | ||
|
|
d90acacd86 | ||
|
|
69f5308761 | ||
|
|
4b5e09e4d6 | ||
|
|
4f78abe7dc | ||
|
|
e63b2881b1 | ||
|
|
745167d57a | ||
|
|
d7bdc1de8d | ||
|
|
c912494609 | ||
|
|
69dae1ec1c | ||
|
|
cf85e9eb7b | ||
|
|
599716bb1b | ||
|
|
ae88d76032 | ||
|
|
9266f28822 | ||
|
|
6488ee12be | ||
|
|
c2fb01a257 | ||
|
|
5761342f32 | ||
|
|
57af7432fc | ||
|
|
1c7dcdd6b9 | ||
|
|
25b3836418 | ||
|
|
5aa2bf86f4 | ||
|
|
b0eb47cb1c | ||
|
|
e7a4f96fb6 | ||
|
|
60110adc06 | ||
|
|
5ff2dfb23e | ||
|
|
eac32419fc | ||
|
|
e3d3d424b2 | ||
|
|
971e2c3bd6 | ||
|
|
c083d76567 | ||
|
|
52322ef624 | ||
|
|
a4ea3bd015 | ||
|
|
3d93532c87 | ||
|
|
fff615d5fc | ||
|
|
f1b8d52eb3 | ||
|
|
7ca5f8f119 | ||
|
|
194a742fb9 | ||
|
|
160956dd31 | ||
|
|
089bacd54e | ||
|
|
d950bf68e3 | ||
|
|
7796c9e122 | ||
|
|
528ec9bffd | ||
|
|
32ec4734f9 | ||
|
|
6a98e7d914 | ||
|
|
a752f09055 | ||
|
|
846bf2ca9f | ||
|
|
f4ab50501e | ||
|
|
de10387f41 | ||
|
|
1faaaeb3d9 | ||
|
|
12cb84b97f | ||
|
|
fdcca9292e | ||
|
|
a1f717f65a | ||
|
|
70a281a39b | ||
|
|
9fcf66f0e0 | ||
|
|
98b152030d | ||
|
|
467d7dacb6 | ||
|
|
d62ab49978 | ||
|
|
e30c701386 | ||
|
|
401bf944ef | ||
|
|
326c982660 | ||
|
|
4b25bdfbc4 | ||
|
|
528682a294 | ||
|
|
6aa00f7bcf | ||
|
|
f485fcde96 | ||
|
|
6f6b37f148 | ||
|
|
99c3a9390f | ||
|
|
f0df46c88a | ||
|
|
e84ac64964 | ||
|
|
3a30c06345 | ||
|
|
a10ca2c5f6 | ||
|
|
927d9f092b | ||
|
|
7938506e07 | ||
|
|
6f7276278d | ||
|
|
743d22669a | ||
|
|
84841c8c4b | ||
|
|
274a2ca528 | ||
|
|
d4aaef90e6 | ||
|
|
6efb1e5626 | ||
|
|
73b4acbb63 | ||
|
|
8a248696e9 | ||
|
|
8b35c194ec | ||
|
|
ac05f88641 | ||
|
|
4bbb878d20 | ||
|
|
2ce60ff314 | ||
|
|
17a4d8a5e5 | ||
|
|
04592c385b | ||
|
|
bc00da1721 | ||
|
|
76a0e43e88 | ||
|
|
24caccccdd | ||
|
|
26342b0c24 | ||
|
|
a4eaddff81 | ||
|
|
c041273dd3 | ||
|
|
fb970b9d87 | ||
|
|
0240f520ab | ||
|
|
3d105733a9 | ||
|
|
8df3ba96f3 | ||
|
|
d35a1c30f4 | ||
|
|
e9ae926e04 | ||
|
|
28c03f1147 | ||
|
|
84d9aff8a2 | ||
|
|
12d30255a7 | ||
|
|
7826eae452 | ||
|
|
8a2347592d | ||
|
|
bcd4adb3a0 | ||
|
|
bf5faf76eb | ||
|
|
f473895c41 | ||
|
|
fbf43c31e5 | ||
|
|
3c0c7a9f83 | ||
|
|
13216c5c20 | ||
|
|
6be9a2c1db | ||
|
|
d8612f7704 | ||
|
|
0a78d99a4d | ||
|
|
3df8eb60e3 | ||
|
|
0325bec283 | ||
|
|
dfad51fe9e | ||
|
|
7049cb9d97 | ||
|
|
78b8b63774 | ||
|
|
ba314a7a36 | ||
|
|
39356f4238 | ||
|
|
b3c05026df | ||
|
|
ce36fd7a49 | ||
|
|
69e00f9948 | ||
|
|
3257df0da3 | ||
|
|
0f2869069b | ||
|
|
a27c6f4b75 | ||
|
|
d27ca649c7 | ||
|
|
d9900e4dbc | ||
|
|
e75a1444af | ||
|
|
b35157f4ff | ||
|
|
762ab056a2 | ||
|
|
78481f0e42 | ||
|
|
746c7fd4e7 | ||
|
|
7f26ae0b45 | ||
|
|
b5948f2e71 | ||
|
|
79a1bfd963 | ||
|
|
ac53bb593d | ||
|
|
dd36c431ec | ||
|
|
b1d41cfa60 | ||
|
|
9dda9ef07c | ||
|
|
9a43fcb61c | ||
|
|
5ec8ef0230 | ||
|
|
d3db1b2591 | ||
|
|
3decc0b3d6 | ||
|
|
98b58fa050 | ||
|
|
5e11341232 | ||
|
|
90e93b1f3a | ||
|
|
e6f927f61a | ||
|
|
60ae8ac3d2 | ||
|
|
004fb30ebe | ||
|
|
3fb4f7f498 | ||
|
|
9e09e48502 | ||
|
|
c79774e8d4 | ||
|
|
d6b09c35f7 | ||
|
|
0048980da5 | ||
|
|
d169d00be6 | ||
|
|
ff731ea07d | ||
|
|
9a5a27ea8d | ||
|
|
94d7b62922 | ||
|
|
2df42e369e | ||
|
|
1f5bb08c25 | ||
|
|
01ff65a93e | ||
|
|
8540043c45 | ||
|
|
f57adf3637 | ||
|
|
a75fa58e1c | ||
|
|
6ccb2d36cf | ||
|
|
414e5f09c7 | ||
|
|
a04596659b | ||
|
|
149d62a648 | ||
|
|
db3905c0a3 | ||
|
|
3253e3c5aa | ||
|
|
c9321550e0 | ||
|
|
ac390d28b8 | ||
|
|
561e5f9ccb | ||
|
|
9df5c39bca | ||
|
|
9976fc6782 | ||
|
|
8966f5635d | ||
|
|
85d7afeba0 | ||
|
|
63e21c146a | ||
|
|
9bd9ad4205 | ||
|
|
dd6faf7f9b | ||
|
|
db4da7beec | ||
|
|
b4f47a7623 | ||
|
|
af8eccc02e | ||
|
|
820be19cf5 | ||
|
|
d733efc1cc | ||
|
|
985a0f3450 | ||
|
|
322515a50b | ||
|
|
13e1fa6789 | ||
|
|
c6277cce51 | ||
|
|
6530375dbf | ||
|
|
449a6e490b | ||
|
|
ebea20b4e7 | ||
|
|
f76d821bda | ||
|
|
263304b6b7 | ||
|
|
73e98c91c3 | ||
|
|
2bf8494332 | ||
|
|
df2bdf7ea3 | ||
|
|
514382e2eb | ||
|
|
cb1eadc276 | ||
|
|
1314ba219e | ||
|
|
5267dce210 | ||
|
|
97c48da49f | ||
|
|
1e74ee51ff | ||
|
|
067edcdb90 | ||
|
|
792c13cf0a | ||
|
|
3bd7d3b1c5 | ||
|
|
af847ef94e | ||
|
|
bfed3ea7d3 | ||
|
|
b44e4d7cb0 | ||
|
|
eed9966ad6 | ||
|
|
affa3c2dbf | ||
|
|
6775ac7334 | ||
|
|
2c626371b0 | ||
|
|
3cacec9163 | ||
|
|
ad513a20e9 | ||
|
|
72a8fa3bc8 | ||
|
|
0c9a616326 | ||
|
|
0e9bc2d410 | ||
|
|
0ea0c5ec4f | ||
|
|
58f0c68151 | ||
|
|
12b5a76b0d | ||
|
|
a4452864ea | ||
|
|
d57be01485 | ||
|
|
d8738f5bfd | ||
|
|
9cf7f3e46f | ||
|
|
14aba3489e | ||
|
|
13bd16af92 | ||
|
|
922a6f13a3 | ||
|
|
9c91e27933 | ||
|
|
c2ca103d30 | ||
|
|
c18f67ac6a | ||
|
|
a5b0400be7 | ||
|
|
045f14fbd0 | ||
|
|
05d8664f15 | ||
|
|
52fdecf97b | ||
|
|
9c0f84cee8 | ||
|
|
414eb22ef9 | ||
|
|
1d3ec27cb7 | ||
|
|
a0cd59bd0e | ||
|
|
a2f13eae55 | ||
|
|
2a931937a8 | ||
|
|
dfd6f8f7ab | ||
|
|
275464e7fb | ||
|
|
e2d370f0da | ||
|
|
4cb8bf1b75 | ||
|
|
5335e671be | ||
|
|
2d2d85bba4 | ||
|
|
9df6ce48c5 | ||
|
|
4d5911dbcf | ||
|
|
d57a2b908a | ||
|
|
2f228ddf31 | ||
|
|
1ca171dbe9 | ||
|
|
f6759a731a | ||
|
|
dfbda48afc | ||
|
|
260723e2cc | ||
|
|
1cbc5b49e3 | ||
|
|
f3358f5927 | ||
|
|
f946040fa9 | ||
|
|
434614506e | ||
|
|
7f2733fa1b | ||
|
|
edb7967dc7 | ||
|
|
c9901bbba5 | ||
|
|
4d930f3598 | ||
|
|
13e71acadf | ||
|
|
37ac743da7 | ||
|
|
c47a6c1510 | ||
|
|
94f9ff1ac9 | ||
|
|
97429a25ab | ||
|
|
9e89584cb4 | ||
|
|
ea80274229 | ||
|
|
42a556a082 | ||
|
|
a71a5bfeb4 | ||
|
|
6cee434b04 | ||
|
|
9d44cd79ee | ||
|
|
548440b48f | ||
|
|
8055a0bdac | ||
|
|
83c74878df | ||
|
|
d320915ad2 | ||
|
|
8e160edbd5 | ||
|
|
c0eaae200e | ||
|
|
79ae163296 | ||
|
|
7a91d7e776 | ||
|
|
6465adfe5c | ||
|
|
db14949209 | ||
|
|
ab4bc653ab | ||
|
|
ab4eacd15f | ||
|
|
7845075bd2 | ||
|
|
129638117f | ||
|
|
4438b7793b | ||
|
|
baaf6046a1 | ||
|
|
5418c2c5e4 | ||
|
|
c27038e392 | ||
|
|
51f15880d1 | ||
|
|
d324500959 | ||
|
|
3d218861e2 | ||
|
|
e721c5cf86 | ||
|
|
e3570ae45d | ||
|
|
2f27ee2232 | ||
|
|
c65bd65254 | ||
|
|
72ce06eab8 | ||
|
|
912f7b51e9 | ||
|
|
90fab0be6b | ||
|
|
c9516c4c60 | ||
|
|
6e74dd4388 | ||
|
|
d160c7e565 | ||
|
|
8ac04a3f29 | ||
|
|
b91b35b565 | ||
|
|
ac78bae7b5 | ||
|
|
926e75d721 | ||
|
|
d5a3021a7d | ||
|
|
d8a994ef24 | ||
|
|
7140dbac95 | ||
|
|
acf094fb07 | ||
|
|
7e0baf4136 | ||
|
|
a703f7d7b4 | ||
|
|
a9981d8099 | ||
|
|
5649556a33 | ||
|
|
834d92a47b | ||
|
|
e2f95c2845 | ||
|
|
5609585ec1 | ||
|
|
b7f3d94cd0 | ||
|
|
f6c98465c7 | ||
|
|
aa12135b97 | ||
|
|
f38d5e57dd | ||
|
|
341da3cea7 | ||
|
|
7162095635 | ||
|
|
0b54035d7a | ||
|
|
dbd4697001 | ||
|
|
2408df3f35 | ||
|
|
a1b28fc33c | ||
|
|
a467184e13 | ||
|
|
658bfc2704 | ||
|
|
736a46dff9 | ||
|
|
0f1b26ed1e | ||
|
|
60896c66af | ||
|
|
eb009923f4 | ||
|
|
338af89d56 | ||
|
|
2fdf8fc938 | ||
|
|
89d6b18dad | ||
|
|
b97780ba51 | ||
|
|
ccc94dd11c | ||
|
|
d5ca913b2f | ||
|
|
dab74f21b7 | ||
|
|
9eef2e706c | ||
|
|
12403bdfb0 | ||
|
|
3af1d3c581 | ||
|
|
24829f314b | ||
|
|
779b71eda4 | ||
|
|
9cf4fe043b | ||
|
|
2765b5c7cf | ||
|
|
632c27802c | ||
|
|
dc89c51f3e | ||
|
|
bb595666ac | ||
|
|
e9b9e6eb53 | ||
|
|
58e004f7da | ||
|
|
fd92d91da3 | ||
|
|
d8631b616e | ||
|
|
aa5e837c65 | ||
|
|
4f6c3e8bb2 | ||
|
|
a1d97e8f5c | ||
|
|
daa43cfb6e | ||
|
|
9adfe453d5 | ||
|
|
29cd8ac270 | ||
|
|
d710b5e791 | ||
|
|
c47866b34a | ||
|
|
15d37b7a95 | ||
|
|
15394f613f | ||
|
|
3650bd8528 | ||
|
|
de3be370f7 | ||
|
|
364874937e | ||
|
|
10e4887b2b | ||
|
|
643acdd32d | ||
|
|
8662990746 | ||
|
|
25845ea1a5 | ||
|
|
d3ca5accfd | ||
|
|
8ab5399e83 | ||
|
|
ec478b4b06 | ||
|
|
1feecd6beb | ||
|
|
a3e8c32a30 | ||
|
|
779bb890fa | ||
|
|
1fa4fe706a | ||
|
|
f4bc9263d9 | ||
|
|
600d8edaca | ||
|
|
ce3708b3ea | ||
|
|
23bc9a2911 | ||
|
|
47adc0e8f7 | ||
|
|
5258ee3740 | ||
|
|
59745c62b4 | ||
|
|
0ad5f51059 | ||
|
|
6b3e47b103 | ||
|
|
297e772c20 | ||
|
|
8bf3032b16 | ||
|
|
e40d94bb4f | ||
|
|
7ffe845c61 | ||
|
|
af8e323248 | ||
|
|
b09ddc72d2 | ||
|
|
a881f776d0 | ||
|
|
40f9d4f920 | ||
|
|
dd7608a36e | ||
|
|
662482d366 | ||
|
|
08ff1b7d4b | ||
|
|
3808638df1 | ||
|
|
ee53204e02 | ||
|
|
f15a2f9b25 | ||
|
|
3b102a71a2 | ||
|
|
133397ee0d | ||
|
|
e2b4a24cb6 | ||
|
|
d37cf09ccd | ||
|
|
2cfe6f8c60 | ||
|
|
16270ee9a4 | ||
|
|
1dfead1eef | ||
|
|
894946c319 | ||
|
|
45db167f7a | ||
|
|
10fbb1aa2f | ||
|
|
bc0eee48d5 | ||
|
|
fa5a1cb54f | ||
|
|
e797a225b6 | ||
|
|
6d5c986d4f | ||
|
|
45659d0fd2 | ||
|
|
acfc942ad7 | ||
|
|
db150f2e42 | ||
|
|
9fe9cd8b5c | ||
|
|
f8182ac521 | ||
|
|
4e96a4a62b | ||
|
|
90e9e3c89d | ||
|
|
d4583ebd4b | ||
|
|
b024de7bf5 | ||
|
|
4a0d2edc3d | ||
|
|
8e40f86d2c | ||
|
|
2bfb8bb5fd | ||
|
|
137a49e834 | ||
|
|
a3bdede2ce | ||
|
|
ddf9fa06c7 | ||
|
|
d91004ee71 | ||
|
|
739d5aa1d3 | ||
|
|
04be8c0de5 | ||
|
|
c3ff476ed6 | ||
|
|
fb1708e1af | ||
|
|
a47baa1b7a | ||
|
|
995487e822 | ||
|
|
5e97693e0e | ||
|
|
452bc385fe | ||
|
|
71bb7f6053 | ||
|
|
0255e6a703 | ||
|
|
5a27aea8e0 | ||
|
|
0e4ae27caa | ||
|
|
1c74612b3c | ||
|
|
d3ba246693 | ||
|
|
a93af59b36 | ||
|
|
0d41827f23 | ||
|
|
32efc3ec0a | ||
|
|
d33ed36fb4 | ||
|
|
9d67528c82 | ||
|
|
66d2ba1b4e | ||
|
|
84749736a8 | ||
|
|
857b340498 | ||
|
|
3e0a27157c | ||
|
|
3abad75a1b | ||
|
|
d568019306 | ||
|
|
b3e0efc0c3 | ||
|
|
caa4ca46c0 | ||
|
|
5d192c2ebf | ||
|
|
29c3e9f428 | ||
|
|
532f9fdd99 | ||
|
|
4848620594 | ||
|
|
bead46363b | ||
|
|
57c10aae33 | ||
|
|
0e0cd9100b | ||
|
|
90780a0d90 | ||
|
|
fdad234445 | ||
|
|
bba1847a8e | ||
|
|
a9d68a6884 | ||
|
|
9cf95e4e37 | ||
|
|
8c4588c4c9 | ||
|
|
e35791b2b2 | ||
|
|
5eafe2b17e | ||
|
|
1c1246fcb9 | ||
|
|
8436d69eaf | ||
|
|
20403f75fb | ||
|
|
295de51b99 | ||
|
|
b7b30cd85e | ||
|
|
81e5722bcc | ||
|
|
303d091ea9 | ||
|
|
c11c3b6c11 | ||
|
|
1ab8a60d73 | ||
|
|
85335c5f56 | ||
|
|
d943429672 | ||
|
|
3a9fd81f59 | ||
|
|
b31c7fe074 | ||
|
|
2665728ee7 | ||
|
|
f65dedc3be | ||
|
|
b921161666 | ||
|
|
2cc1ee3fc0 | ||
|
|
f36c82c3b3 |
@@ -1,6 +1,6 @@
|
||||
[run]
|
||||
init_cmds = [
|
||||
#["grep", "-rn", "FIXME", "."],
|
||||
["make", "build-dev"],
|
||||
["./gogs", "web"]
|
||||
]
|
||||
watch_all = true
|
||||
@@ -11,9 +11,9 @@ watch_dirs = [
|
||||
"$WORKDIR/routers"
|
||||
]
|
||||
watch_exts = [".go"]
|
||||
ignore_files = [".+_test.go"]
|
||||
build_delay = 1500
|
||||
cmds = [
|
||||
["go", "install", "-v", "-race"], # sqlite redis memcache cert pam tidb
|
||||
["go", "build", "-race"],
|
||||
["make", "build-dev"], # TAGS=sqlite cert pam tidb
|
||||
["./gogs", "web"]
|
||||
]
|
||||
7
.codebeatignore
Normal file
7
.codebeatignore
Normal file
@@ -0,0 +1,7 @@
|
||||
conf/**
|
||||
docker/**
|
||||
modules/bindata/**
|
||||
packager/**
|
||||
public/**
|
||||
scripts/**
|
||||
templates/**
|
||||
7
.codebeatsettings
Normal file
7
.codebeatsettings
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"GOLANG": {
|
||||
"TOTAL_LOC": [500, 999, 1999, 9999],
|
||||
"TOO_MANY_FUNCTIONS": [50, 99, 199, 999],
|
||||
"TOO_MANY_IVARS": [20, 50, 70, 99]
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,19 @@
|
||||
.git
|
||||
.git/
|
||||
.git/*
|
||||
conf
|
||||
conf/
|
||||
conf/*
|
||||
.git/**
|
||||
packager
|
||||
packager/
|
||||
packager/*
|
||||
packager/**
|
||||
scripts
|
||||
scripts/
|
||||
scripts/*
|
||||
scripts/**
|
||||
.github/
|
||||
.github/**
|
||||
config.codekit
|
||||
.dockerignore
|
||||
*.yml
|
||||
*.md
|
||||
.bra.toml
|
||||
.editorconfig
|
||||
.gitignore
|
||||
.gopmfile
|
||||
config.codekit
|
||||
LICENSE
|
||||
Dockerfile*
|
||||
vendor
|
||||
vendor/**
|
||||
gogs
|
||||
|
||||
@@ -5,8 +5,21 @@ root = true
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_style = tab
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.yml]
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.tmpl]
|
||||
indent_style = tab
|
||||
indent_size = 2
|
||||
|
||||
[*.{less,yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.js]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
11
.gitattributes
vendored
Normal file
11
.gitattributes
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
public/conf/gitignore/* linguist-vendored
|
||||
public/conf/license/* linguist-vendored
|
||||
public/assets/* linguist-vendored
|
||||
public/plugins/* linguist-vendored
|
||||
public/plugins/* linguist-vendored
|
||||
public/css/themes/* linguist-vendored
|
||||
public/css/github.min.css linguist-vendored
|
||||
public/css/semantic-2.2.7.min.css linguist-vendored
|
||||
public/js/libs/* linguist-vendored
|
||||
public/js/jquery-1.11.3.min.js linguist-vendored
|
||||
public/js/semantic-2.2.7.min.js linguist-vendored
|
||||
6
CONTRIBUTING.md → .github/CONTRIBUTING.md
vendored
6
CONTRIBUTING.md → .github/CONTRIBUTING.md
vendored
@@ -46,11 +46,7 @@ Please read detailed information on [Wiki](https://github.com/gogits/gogs/wiki/C
|
||||
|
||||
### Ask For Help
|
||||
|
||||
Before opening an issue, please make sure your problem isn't already addressed on the [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.md) and [FAQs](http://gogs.io/docs/intro/faqs.html) pages.
|
||||
|
||||
## Things To Notice
|
||||
|
||||
Please take a moment to check that an issue on [GitHub](https://github.com/gogits/gogs/issues) or card on [Trello](https://trello.com/b/uxAoeLUl/gogs-go-git-service) doesn't already exist documenting your bug report or improvement proposal. If it does, it never hurts to add a quick "+1" or "I have this problem too". This will help prioritize the most common problems and requests.
|
||||
Before opening an issue, please make sure your problem isn't already addressed on the [Troubleshooting](https://gogs.io/docs/intro/troubleshooting.html) and [FAQs](https://gogs.io/docs/intro/faqs.html) pages.
|
||||
|
||||
## Code of conduct
|
||||
|
||||
25
.github/ISSUE_TEMPLATE.md
vendored
Normal file
25
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
The issue will be closed without any reasons if it does not satisfy any of following requirements:
|
||||
|
||||
1. Please speak English, we have forum in [Chinese](https://discuss.gogs.io/c/getting-help/getting-help-chinese).
|
||||
2. Please post questions or config/deploy problems on our forum: https://discuss.gogs.io, here are bugs and feature requests only.
|
||||
3. Please take a moment to search that an issue doesn't already exist.
|
||||
4. Please give all relevant information below for bug reports; incomplete details considered invalid report.
|
||||
|
||||
**You MUST delete above content including this line before posting; too lazy to take this action considered invalid report.**
|
||||
|
||||
- Gogs version (or commit ref):
|
||||
- Git version:
|
||||
- Operating system:
|
||||
- Database (use `[x]`):
|
||||
- [ ] PostgreSQL
|
||||
- [ ] MySQL
|
||||
- [ ] SQLite
|
||||
- Can you reproduce the bug at https://try.gogs.io:
|
||||
- [ ] Yes (provide example URL)
|
||||
- [ ] No
|
||||
- [ ] Not relevant
|
||||
- Log gist:
|
||||
|
||||
## Description
|
||||
|
||||
...
|
||||
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
The pull request will be closed without any reasons if it does not satisfy any of following requirements:
|
||||
|
||||
1. Please make sure you are targeting the `develop` branch.
|
||||
2. Please read contributing guidelines:
|
||||
https://github.com/gogits/gogs/wiki/Contributing-Code
|
||||
3. Please describe what your pull request does and which issue you're targeting
|
||||
4. ... if it is not related to any particular issues, explain why we should not reject your pull request.
|
||||
|
||||
**You MUST delete above content including this line before posting; too lazy to take this action considered invalid pull request.**
|
||||
22
.gitignore
vendored
22
.gitignore
vendored
@@ -8,32 +8,12 @@ data/
|
||||
.idea/
|
||||
*.iml
|
||||
public/img/avatar/
|
||||
files/
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
_obj
|
||||
_test
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
_testmain.go
|
||||
*.exe
|
||||
*.exe~
|
||||
/gogs
|
||||
profile/
|
||||
__pycache__
|
||||
*.pem
|
||||
output*
|
||||
.brackets.json
|
||||
docker/fig.yml
|
||||
docker/docker/Dockerfile
|
||||
docker/docker/init_gogs.sh
|
||||
gogs.sublime-project
|
||||
gogs.sublime-workspace
|
||||
.tags*
|
||||
release
|
||||
/release
|
||||
|
||||
86
.gopmfile
86
.gopmfile
@@ -2,51 +2,63 @@
|
||||
path = github.com/gogits/gogs
|
||||
|
||||
[deps]
|
||||
github.com/bradfitz/gomemcache = commit:fb1f79c
|
||||
github.com/codegangsta/cli = commit:cf1f63a
|
||||
github.com/go-macaron/binding = commit:2502aaf
|
||||
github.com/bradfitz/gomemcache = commit:2fafb84
|
||||
github.com/urfave/cli = commit:347a988
|
||||
github.com/go-macaron/binding = commit:4892016
|
||||
github.com/go-macaron/cache = commit:5617353
|
||||
github.com/go-macaron/captcha = commit:8aa5919
|
||||
github.com/go-macaron/csrf = commit:715bca0
|
||||
github.com/go-macaron/gzip = commit:4938e9b
|
||||
github.com/go-macaron/i18n = commit:d2d3329
|
||||
github.com/go-macaron/inject = commit:c5ab7bf
|
||||
github.com/go-macaron/csrf = commit:6a9a7df
|
||||
github.com/go-macaron/gzip = commit:cad1c65
|
||||
github.com/go-macaron/i18n = commit:ef57533
|
||||
github.com/go-macaron/inject = commit:d8a0b86
|
||||
github.com/go-macaron/session = commit:66031fc
|
||||
github.com/go-macaron/toolbox = commit:82b5115
|
||||
github.com/go-sql-driver/mysql = commit:b4db83c
|
||||
github.com/go-xorm/core = commit:1e2868c
|
||||
github.com/go-xorm/xorm = commit:24c1f3c
|
||||
github.com/gogits/chardet = commit:2404f77725
|
||||
github.com/gogits/git-module = commit:3c8c495
|
||||
github.com/gogits/go-gogs-client = commit:2f4342d
|
||||
github.com/issue9/identicon = commit:f8c0d2c
|
||||
github.com/go-sql-driver/mysql = commit:2e00b5c
|
||||
github.com/go-xorm/builder = commit:867edcc
|
||||
github.com/go-xorm/core = commit:2fbe2c7
|
||||
github.com/go-xorm/xorm = commit:445a934
|
||||
github.com/gogits/chardet = commit:2404f77
|
||||
github.com/gogits/cron = commit:2fc07a4
|
||||
github.com/gogits/git-module = commit:172cbc2
|
||||
github.com/gogits/go-gogs-client = commit:98046bb
|
||||
github.com/gogits/go-libravatar = commit:cd1abbd
|
||||
github.com/issue9/identicon = commit:d36b545
|
||||
github.com/jaytaylor/html2text = commit:d16d412
|
||||
github.com/kardianos/minwinsvc = commit:cad6b2b
|
||||
github.com/klauspost/compress = commit:91e7b09
|
||||
github.com/klauspost/cpuid = commit:349c675
|
||||
github.com/klauspost/crc32 = commit:999f312
|
||||
github.com/lib/pq = commit:8ad2b29
|
||||
github.com/mattn/go-sqlite3 = commit:0cc1174
|
||||
github.com/mcuadros/go-version = commit:d52711f
|
||||
github.com/microcosm-cc/bluemonday = commit:4ac6f27
|
||||
github.com/klauspost/compress = commit:461e8fd
|
||||
github.com/klauspost/cpuid = commit:09cded8
|
||||
github.com/klauspost/crc32 = commit:cb6bfca
|
||||
github.com/lib/pq = commit:67c3f2a
|
||||
github.com/mattn/go-colorable = commit:d228849
|
||||
github.com/mattn/go-isatty = commit:30a891c
|
||||
github.com/mattn/go-sqlite3 = commit:ce9149a
|
||||
github.com/mcuadros/go-version = commit:257f7b9
|
||||
github.com/microcosm-cc/bluemonday = commit:e797637
|
||||
github.com/msteinert/pam = commit:02ccfbf
|
||||
github.com/nfnt/resize = commit:4d93a29
|
||||
github.com/russross/blackfriday = commit:006144a
|
||||
github.com/sergi/go-diff = commit:ec7fdbb
|
||||
github.com/shurcooL/sanitized_anchor_name = commit:10ef21a
|
||||
github.com/Unknwon/cae = commit:7f5e046
|
||||
github.com/nfnt/resize = commit:891127d
|
||||
github.com/russross/blackfriday = commit:5f33e7b
|
||||
github.com/satori/go.uuid = commit:b061729
|
||||
github.com/sergi/go-diff = commit:24e2351
|
||||
github.com/shurcooL/sanitized_anchor_name = commit:1dba4b3
|
||||
github.com/Unknwon/cae = commit:c6aac99
|
||||
github.com/Unknwon/com = commit:28b053d
|
||||
github.com/Unknwon/i18n = commit:3b48b66
|
||||
github.com/Unknwon/paginater = commit:7748a72
|
||||
golang.org/x/net = commit:04b9de9
|
||||
golang.org/x/text = commit:6fc2e00
|
||||
golang.org/x/crypto = commit:1f22c01
|
||||
github.com/Unknwon/i18n = commit:39d6f27
|
||||
github.com/Unknwon/paginater = commit:701c23f
|
||||
github.com/fatih/color = commit:42c364b
|
||||
golang.org/x/crypto = commit:dc137be
|
||||
golang.org/x/net = commit:f249948
|
||||
golang.org/x/sys = commit:d75a526
|
||||
golang.org/x/text = commit:ece019d
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 = commit:2caba25
|
||||
gopkg.in/asn1-ber.v1 = commit:4e86f43
|
||||
gopkg.in/gomail.v2 = commit:fbb71dd
|
||||
gopkg.in/ini.v1 = commit:afbd495
|
||||
gopkg.in/ldap.v2 = commit:e9a325d
|
||||
gopkg.in/macaron.v1 = commit:564f398
|
||||
gopkg.in/redis.v2 = commit:e617904962
|
||||
gopkg.in/bufio.v1 = commit:567b2bf
|
||||
gopkg.in/clog.v1 = commit:d359c28
|
||||
gopkg.in/editorconfig/editorconfig-core-go.v1 = commit:a872f05
|
||||
gopkg.in/gomail.v2 = commit:81ebce5
|
||||
gopkg.in/ini.v1 = commit:e3c2d47
|
||||
gopkg.in/ldap.v2 = commit:8168ee0
|
||||
gopkg.in/macaron.v1 = commit:78521e4
|
||||
gopkg.in/redis.v2 = commit:e617904
|
||||
|
||||
[res]
|
||||
include = public|scripts|templates
|
||||
|
||||
|
||||
2
.mailmap
Normal file
2
.mailmap
Normal file
@@ -0,0 +1,2 @@
|
||||
Unknwon <u@gogs.io> <joe2010xtmf@163.com>
|
||||
Unknwon <u@gogs.io> 无闻 <u@gogs.io>
|
||||
@@ -7,10 +7,15 @@ targets:
|
||||
- git
|
||||
debian-8:
|
||||
<<: *debian
|
||||
ubuntu-14.04:
|
||||
<<: *debian
|
||||
ubuntu-12.04:
|
||||
<<: *debian
|
||||
ubuntu-14.04:
|
||||
<<: *debian
|
||||
ubuntu-16.04:
|
||||
<<: *debian
|
||||
build_dependencies:
|
||||
- bzr
|
||||
- mercurial
|
||||
centos-6: &el
|
||||
build_dependencies:
|
||||
- pam-devel
|
||||
|
||||
27
.travis.yml
27
.travis.yml
@@ -1,26 +1,17 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- master
|
||||
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -y libpam-dev
|
||||
- go get github.com/msteinert/pam
|
||||
|
||||
install:
|
||||
- go get -t -v ./...
|
||||
env:
|
||||
- GO15VENDOREXPERIMENT=1
|
||||
|
||||
script: go build -v -tags "pam"
|
||||
|
||||
notifications:
|
||||
email:
|
||||
- u@gogs.io
|
||||
slack: gophercn:o5pSanyTeNhnfYc3QnG0X7Wx
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/b590f8e03882f7aedc3e
|
||||
on_success: change
|
||||
on_failure: always
|
||||
on_start: never
|
||||
script:
|
||||
- go build -v -tags "pam"
|
||||
- go test -v -cover -race ./...
|
||||
|
||||
@@ -2,11 +2,9 @@ FROM alpine:3.3
|
||||
MAINTAINER jp@roemer.im
|
||||
|
||||
# Install system utils & Gogs runtime dependencies
|
||||
ADD https://github.com/tianon/gosu/releases/download/1.6/gosu-amd64 /usr/sbin/gosu
|
||||
RUN echo "@edge http://dl-4.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories \
|
||||
&& apk -U --no-progress upgrade \
|
||||
&& apk -U --no-progress add ca-certificates bash git linux-pam s6@edge curl openssh socat \
|
||||
&& chmod +x /usr/sbin/gosu
|
||||
ADD https://github.com/tianon/gosu/releases/download/1.9/gosu-amd64 /usr/sbin/gosu
|
||||
RUN chmod +x /usr/sbin/gosu \
|
||||
&& apk --no-cache --no-progress add ca-certificates bash git linux-pam s6 curl openssh socat tzdata
|
||||
|
||||
ENV GOGS_CUSTOM /data/gogs
|
||||
|
||||
|
||||
22
Dockerfile.aarch64
Normal file
22
Dockerfile.aarch64
Normal file
@@ -0,0 +1,22 @@
|
||||
FROM aarch64/alpine:3.5
|
||||
MAINTAINER atzoum@gmail.com
|
||||
|
||||
# Install system utils & Gogs runtime dependencies
|
||||
ADD https://github.com/tianon/gosu/releases/download/1.9/gosu-arm64 /usr/sbin/gosu
|
||||
RUN chmod +x /usr/sbin/gosu \
|
||||
&& apk --no-cache --no-progress add ca-certificates bash git linux-pam s6 curl openssh socat tzdata
|
||||
|
||||
ENV GOGS_CUSTOM /data/gogs
|
||||
|
||||
COPY . /app/gogs/
|
||||
WORKDIR /app/gogs/
|
||||
RUN ./docker/build.sh
|
||||
|
||||
# Configure LibC Name Service
|
||||
COPY docker/nsswitch.conf /etc/nsswitch.conf
|
||||
|
||||
# Configure Docker Container
|
||||
VOLUME ["/data"]
|
||||
EXPOSE 22 3000
|
||||
ENTRYPOINT ["docker/start.sh"]
|
||||
CMD ["/bin/s6-svscan", "/app/gogs/docker/s6/"]
|
||||
@@ -2,13 +2,12 @@ FROM hypriot/rpi-alpine-scratch:v3.2
|
||||
MAINTAINER jp@roemer.im, raxetul@gmail.com
|
||||
|
||||
# Install system utils & Gogs runtime dependencies
|
||||
ADD https://github.com/tianon/gosu/releases/download/1.6/gosu-armhf /usr/sbin/gosu
|
||||
RUN echo "http://dl-4.alpinelinux.org/alpine/v3.3/main/" | tee /etc/apk/repositories \
|
||||
ADD https://github.com/tianon/gosu/releases/download/1.9/gosu-armhf /usr/sbin/gosu
|
||||
RUN chmod +x /usr/sbin/gosu \
|
||||
&& echo "http://dl-4.alpinelinux.org/alpine/v3.3/main/" | tee /etc/apk/repositories \
|
||||
&& echo "http://dl-4.alpinelinux.org/alpine/v3.3/community/" | tee -a /etc/apk/repositories \
|
||||
&& echo "@edge http://dl-4.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories \
|
||||
&& apk -U --no-progress upgrade \
|
||||
&& apk -U --no-progress add ca-certificates bash git linux-pam s6@edge curl openssh socat \
|
||||
&& chmod +x /usr/sbin/gosu
|
||||
&& apk -U --no-progress upgrade && rm -f /var/cache/apk/APKINDEX.* \
|
||||
&& apk --no-cache --no-progress add ca-certificates bash git linux-pam s6 curl openssh socat tzdata
|
||||
|
||||
ENV GOGS_CUSTOM /data/gogs
|
||||
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2014 All Gogs Contributors
|
||||
Copyright (c) The Gogs Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
39
Makefile
39
Makefile
@@ -5,22 +5,41 @@ DATA_FILES := $(shell find conf | sed 's/ /\\ /g')
|
||||
LESS_FILES := $(wildcard public/less/gogs.less public/less/_*.less)
|
||||
GENERATED := modules/bindata/bindata.go public/css/gogs.css
|
||||
|
||||
OS := $(shell uname)
|
||||
|
||||
TAGS = ""
|
||||
BUILD_FLAGS = "-v"
|
||||
|
||||
RELEASE_ROOT = "release"
|
||||
RELEASE_GOGS = "release/gogs"
|
||||
NOW = $(shell date -u '+%Y%m%d%I%M%S')
|
||||
GOVET = go tool vet -composites=false -methods=false -structtags=false
|
||||
|
||||
.PHONY: build pack release bindata clean
|
||||
|
||||
.IGNORE: public/css/gogs.css
|
||||
|
||||
build: $(GENERATED)
|
||||
go install -v -ldflags '$(LDFLAGS)' -tags '$(TAGS)'
|
||||
cp '$(GOPATH)/bin/gogs' .
|
||||
all: build
|
||||
|
||||
check: test
|
||||
|
||||
dist: release
|
||||
|
||||
govet:
|
||||
go tool vet -composites=false -methods=false -structtags=false .
|
||||
$(GOVET) gogs.go
|
||||
$(GOVET) models modules routers
|
||||
|
||||
build: $(GENERATED)
|
||||
go install $(BUILD_FLAGS) -ldflags '$(LDFLAGS)' -tags '$(TAGS)'
|
||||
cp '$(GOPATH)/bin/gogs' .
|
||||
|
||||
build-dev: $(GENERATED) govet
|
||||
go install $(BUILD_FLAGS) -tags '$(TAGS)'
|
||||
cp '$(GOPATH)/bin/gogs' .
|
||||
|
||||
build-dev-race: $(GENERATED) govet
|
||||
go install $(BUILD_FLAGS) -race -tags '$(TAGS)'
|
||||
cp '$(GOPATH)/bin/gogs' .
|
||||
|
||||
pack:
|
||||
rm -rf $(RELEASE_GOGS)
|
||||
@@ -48,4 +67,14 @@ clean-mac: clean
|
||||
find . -name ".DS_Store" -print0 | xargs -0 rm
|
||||
|
||||
test:
|
||||
go test ./...
|
||||
go test -cover -race ./...
|
||||
|
||||
fixme:
|
||||
grep -rnw "FIXME" routers models modules
|
||||
|
||||
todo:
|
||||
grep -rnw "TODO" routers models modules
|
||||
|
||||
# Legacy code should be remove by the time of release
|
||||
legacy:
|
||||
grep -rnw "LEGACY" routers models modules
|
||||
|
||||
54
README.md
54
README.md
@@ -1,9 +1,9 @@
|
||||
Gogs - Go Git Service [](https://travis-ci.org/gogits/gogs) [](https://quay.io/repository/gogs/gogs) [](https://crowdin.com/project/gogs) [](https://gitter.im/gogits/gogs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
Gogs - Go Git Service [](https://travis-ci.org/gogits/gogs) [](https://ci.appveyor.com/project/Unknwon/gogs/branch/master) [](https://crowdin.com/project/gogs) [](https://gitter.im/gogits/gogs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
=====================
|
||||
|
||||

|
||||
|
||||
##### Current version: 0.8.25
|
||||
##### Current tip version: [`.VERSION`](templates/.VERSION) (see [Releases](https://github.com/gogits/gogs/releases) for binary versions ~~or submit a task on [alpha stage automated binary building system](https://build.gogs.io/)~~)
|
||||
|
||||
| Web | UI | Preview |
|
||||
|:-------------:|:-------:|:-------:|
|
||||
@@ -11,15 +11,14 @@ Gogs - Go Git Service [|||
|
||||
||||
|
||||
|
||||
### NOTICES
|
||||
### Important Notes
|
||||
|
||||
- :bangbang: You **MUST** read [CONTRIBUTING.md](CONTRIBUTING.md) for bug report and contributing code. :bangbang:
|
||||
- Please [start discussion](http://forum.gogs.io/category/2/general-discussion) or [ask a question](http://forum.gogs.io/category/4/getting-help) on [the forum](http://forum.gogs.io/). GitHub issue tracker only keeps **bugs** and **feature requests**, all other topics will be closed without reason.
|
||||
- Due to testing purpose, data of [try.gogs.io](https://try.gogs.io) was reset in **Jan 28, 2015** and will reset multiple times after. Please do **NOT** put your important data on the site.
|
||||
- The demo site [try.gogs.io](https://try.gogs.io) is running under `develop` branch.
|
||||
- If you think there are vulnerabilities in the project, please talk privately to **u@gogs.io**. Thanks!
|
||||
- If you're interested in using APIs, we have experimental support with [documentation](https://github.com/gogits/go-gogs-client/wiki).
|
||||
- If your team/company is using Gogs and would like to put your logo on [our website](http://gogs.io), contact us by any means.
|
||||
1. **YOU MUST READ [Contributing Code](https://github.com/gogits/gogs/wiki/Contributing-Code) BEFORE STARTING TO WORK ON A PULL REQUEST**.
|
||||
2. Due to testing purpose, data of [try.gogs.io](https://try.gogs.io) was reset in **Jan 28, 2015** and will reset multiple times after. Please do **NOT** put your important data on the site.
|
||||
3. The demo site [try.gogs.io](https://try.gogs.io) is running under `develop` branch.
|
||||
4. If you think there are vulnerabilities in the project, please talk privately to **u@gogs.io**. Thanks!
|
||||
5. If you're interested in using APIs, we have experimental support with [documentation](https://github.com/gogits/go-gogs-client/wiki).
|
||||
6. If your team/company is using Gogs and would like to put your logo on [our website](https://gogs.io), contact us by any means.
|
||||
|
||||
[简体中文](README_ZH.md)
|
||||
|
||||
@@ -29,11 +28,10 @@ The goal of this project is to make the easiest, fastest, and most painless way
|
||||
|
||||
## Overview
|
||||
|
||||
- Please see the [Documentation](http://gogs.io/docs/intro) for common usages and change log.
|
||||
- See the [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team.
|
||||
- Please see the [Documentation](https://gogs.io/docs/intro) for common usages and change log.
|
||||
- Want to try it before doing anything else? Do it [online](https://try.gogs.io/gogs/gogs)!
|
||||
- Having trouble? Get help with [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.html).
|
||||
- Want to help with localization? Check out the [guide](http://gogs.io/docs/features/i18n.html)!
|
||||
- Having trouble? Get help with [Troubleshooting](https://gogs.io/docs/intro/troubleshooting.html) or [User Forum](https://discuss.gogs.io/).
|
||||
- Want to help with localization? Check out the [guide](https://gogs.io/docs/features/i18n.html)!
|
||||
|
||||
## Features
|
||||
|
||||
@@ -42,15 +40,17 @@ The goal of this project is to make the easiest, fastest, and most painless way
|
||||
- SMTP/LDAP/Reverse proxy authentication
|
||||
- Reverse proxy with sub-path
|
||||
- Account/Organization/Repository management
|
||||
- Add/Remove repository collaborators
|
||||
- Repository/Organization webhooks (including Slack)
|
||||
- Repository Git hooks/deploy keys
|
||||
- Repository issues, pull requests and wiki
|
||||
- Add/Remove repository collaborators
|
||||
- Gravatar and custom source
|
||||
- Migrate and mirror repository and its wiki
|
||||
- Web editor for repository files and wiki
|
||||
- Gravatar and Federated avatar with custom source
|
||||
- Mail service
|
||||
- Administration panel
|
||||
- Supports MySQL, PostgreSQL, SQLite3 and [TiDB](https://github.com/pingcap/tidb) (experimental)
|
||||
- Multi-language support ([14 languages](https://crowdin.com/project/gogs))
|
||||
- Multi-language support ([22 languages](https://crowdin.com/project/gogs))
|
||||
|
||||
## System Requirements
|
||||
|
||||
@@ -60,17 +60,17 @@ The goal of this project is to make the easiest, fastest, and most painless way
|
||||
## Browser Support
|
||||
|
||||
- Please see [Semantic UI](https://github.com/Semantic-Org/Semantic-UI#browser-support) for specific versions of supported browsers.
|
||||
- The official support minimal size is **1024*768**, UI may still looks right in smaller size but no promises and fixes.
|
||||
- The smallest resolution officially supported is **1024*768**, however the UI may still look right in smaller resolutions, but no promises or fixes.
|
||||
|
||||
## Installation
|
||||
|
||||
Make sure you install the [prerequisites](http://gogs.io/docs/installation) first.
|
||||
Make sure you install the [prerequisites](https://gogs.io/docs/installation) first.
|
||||
|
||||
There are 5 ways to install Gogs:
|
||||
|
||||
- [Install from binary](http://gogs.io/docs/installation/install_from_binary.html)
|
||||
- [Install from source](http://gogs.io/docs/installation/install_from_source.html)
|
||||
- [Install from packages](http://gogs.io/docs/installation/install_from_packages.html)
|
||||
- [Install from binary](https://gogs.io/docs/installation/install_from_binary.html)
|
||||
- [Install from source](https://gogs.io/docs/installation/install_from_source.html)
|
||||
- [Install from packages](https://gogs.io/docs/installation/install_from_packages.html)
|
||||
- [Ship with Docker](https://github.com/gogits/gogs/tree/master/docker)
|
||||
- [Install with Vagrant](https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs)
|
||||
|
||||
@@ -78,6 +78,8 @@ There are 5 ways to install Gogs:
|
||||
|
||||
- [How To Set Up Gogs on Ubuntu 14.04](https://www.digitalocean.com/community/tutorials/how-to-set-up-gogs-on-ubuntu-14-04)
|
||||
- [Run your own GitHub-like service with the help of Docker](http://blog.hypriot.com/post/run-your-own-github-like-service-with-docker/)
|
||||
- [Dockerized Gogs git server and alpine postgres in 20 minutes or less](http://garthwaite.org/docker-gogs.html)
|
||||
- [Host Your Own Private GitHub with Gogs.io](https://eladnava.com/host-your-own-private-github-with-gogs-io/)
|
||||
- [使用 Gogs 搭建自己的 Git 服务器](https://mynook.info/blog/post/host-your-own-git-server-using-gogs) (Chinese)
|
||||
- [阿里云上 Ubuntu 14.04 64 位安装 Gogs](http://my.oschina.net/luyao/blog/375654) (Chinese)
|
||||
- [Installing Gogs on FreeBSD](https://www.codejam.info/2015/03/installing-gogs-on-freebsd.html)
|
||||
@@ -86,6 +88,7 @@ There are 5 ways to install Gogs:
|
||||
|
||||
### Screencasts
|
||||
|
||||
- [How to install Gogs on a Linux Server (DigitalOcean)](https://www.youtube.com/watch?v=deSfX0gqefE)
|
||||
- [Instalando Gogs no Ubuntu](https://www.youtube.com/watch?v=4UkHAR1F7ZA) (Português)
|
||||
|
||||
### Deploy to Cloud
|
||||
@@ -96,6 +99,8 @@ There are 5 ways to install Gogs:
|
||||
- [Portal](https://portaldemo.xyz/cloud/)
|
||||
- [Sandstorm](https://github.com/cem/gogs-sandstorm)
|
||||
- [sloppy.io](https://github.com/sloppyio/quickstarters/tree/master/gogs)
|
||||
- [YunoHost](https://github.com/YunoHost-Apps/gogs_ynh)
|
||||
- [DPlatform](https://github.com/j8r/DPlatform)
|
||||
|
||||
## Software and Service Support
|
||||
|
||||
@@ -105,6 +110,7 @@ There are 5 ways to install Gogs:
|
||||
- [Puppet](https://forge.puppetlabs.com/Siteminds/gogs) (IT)
|
||||
- [Kanboard](http://kanboard.net/plugin/gogs-webhook) (Project Management)
|
||||
- [BearyChat](https://bearychat.com/) (Team Communication)
|
||||
- [HiWork](http://www.hiwork.cc/) (Team Communication)
|
||||
|
||||
### Product Support
|
||||
|
||||
@@ -114,11 +120,11 @@ There are 5 ways to install Gogs:
|
||||
## Acknowledgments
|
||||
|
||||
- Router and middleware mechanism of [Macaron](https://github.com/go-macaron/macaron).
|
||||
- Modules design is inspired by [WeTalk](https://github.com/beego/wetalk).
|
||||
- System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog).
|
||||
- Thanks [lavachen](http://www.lavachen.cn/) and [Rocker](http://weibo.com/rocker1989) for designing Logo.
|
||||
- Thanks [Rocker](http://weibo.com/rocker1989) for designing Logo.
|
||||
- Thanks [Crowdin](https://crowdin.com/project/gogs) for providing open source translation plan.
|
||||
- Thanks [DigitalOcean](https://www.digitalocean.com) for hosting home and demo sites.
|
||||
- Thanks [KeyCDN](https://www.keycdn.com/) and [QiNiu](http://www.qiniu.com/) for providing CDN service.
|
||||
|
||||
## Contributors
|
||||
|
||||
|
||||
30
README_ZH.md
30
README_ZH.md
@@ -9,11 +9,10 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
|
||||
## 项目概览
|
||||
|
||||
- 有关基本用法和变更日志,请通过 [使用手册](http://gogs.io/docs/intro/) 查看。
|
||||
- 您可以到 [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) 跟随开发团队的脚步。
|
||||
- 有关基本用法和变更日志,请通过 [使用手册](https://gogs.io/docs/intro/) 查看。
|
||||
- 想要先睹为快?直接去 [在线体验](https://try.gogs.io/gogs/gogs) 。
|
||||
- 使用过程中遇到问题?尝试从 [故障排查](http://gogs.io/docs/intro/troubleshooting.html) 页面获取帮助。
|
||||
- 希望帮助多国语言界面的翻译吗?请立即访问 [详情页面](http://gogs.io/docs/features/i18n.html)!
|
||||
- 使用过程中遇到问题?尝试从 [故障排查](https://gogs.io/docs/intro/troubleshooting.html) 页面或 [用户论坛](https://discuss.gogs.io/) 获取帮助。
|
||||
- 希望帮助多国语言界面的翻译吗?请立即访问 [详情页面](https://gogs.io/docs/features/i18n.html)!
|
||||
|
||||
## 功能特性
|
||||
|
||||
@@ -22,15 +21,17 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
- 支持 SMTP、LDAP 和反向代理的用户认证
|
||||
- 支持反向代理子路径
|
||||
- 支持用户、组织和仓库管理系统
|
||||
- 支持添加和删除仓库协作者
|
||||
- 支持仓库和组织级别 Web 钩子(包括 Slack 集成)
|
||||
- 支持仓库 Git 钩子和部署密钥
|
||||
- 支持仓库工单(Issue)、合并请求(Pull Request)以及 Wiki
|
||||
- 支持添加和删除仓库协作者
|
||||
- 支持 Gravatar 以及自定义源
|
||||
- 支持迁移和镜像仓库以及它的 Wiki
|
||||
- 支持在线编辑仓库文件和 Wiki
|
||||
- 支持自定义源的 Gravatar 和 Federated Avatar
|
||||
- 支持邮件服务
|
||||
- 支持后台管理面板
|
||||
- 支持 MySQL、PostgreSQL、SQLite3 和 [TiDB](https://github.com/pingcap/tidb)(实验性支持) 数据库
|
||||
- 支持多语言本地化([14 种语言]([more](https://crowdin.com/project/gogs)))
|
||||
- 支持多语言本地化([22 种语言]([more](https://crowdin.com/project/gogs)))
|
||||
|
||||
## 系统要求
|
||||
|
||||
@@ -44,13 +45,13 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
|
||||
## 安装部署
|
||||
|
||||
在安装 Gogs 之前,您需要先安装 [基本环境](http://gogs.io/docs/installation)。
|
||||
在安装 Gogs 之前,您需要先安装 [基本环境](https://gogs.io/docs/installation)。
|
||||
|
||||
然后,您可以通过以下 5 种方式来安装 Gogs:
|
||||
|
||||
- [二进制安装](http://gogs.io/docs/installation/install_from_binary.html)
|
||||
- [源码安装](http://gogs.io/docs/installation/install_from_source.html)
|
||||
- [包管理安装](http://gogs.io/docs/installation/install_from_packages.html)
|
||||
- [二进制安装](https://gogs.io/docs/installation/install_from_binary.html)
|
||||
- [源码安装](https://gogs.io/docs/installation/install_from_source.html)
|
||||
- [包管理安装](https://gogs.io/docs/installation/install_from_packages.html)
|
||||
- [采用 Docker 部署](https://github.com/gogits/gogs/tree/master/docker)
|
||||
- [通过 Vagrant 安装](https://github.com/geerlingguy/ansible-vagrant-examples/tree/master/gogs)
|
||||
|
||||
@@ -67,6 +68,8 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
- [Portal](https://portaldemo.xyz/cloud/)
|
||||
- [Sandstorm](https://github.com/cem/gogs-sandstorm)
|
||||
- [sloppy.io](https://github.com/sloppyio/quickstarters/tree/master/gogs)
|
||||
- [YunoHost](https://github.com/mbugeia/gogs_ynh)
|
||||
- [DPlatform](https://github.com/j8r/DPlatform)
|
||||
|
||||
## 软件及服务支持
|
||||
|
||||
@@ -76,6 +79,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
- [Puppet](https://forge.puppetlabs.com/Siteminds/gogs)(IT)
|
||||
- [Kanboard](http://kanboard.net/plugin/gogs-webhook)(项目管理)
|
||||
- [BearyChat](https://bearychat.com/)(团队交流)
|
||||
- [HiWork](http://www.hiwork.cc/)(团队交流)
|
||||
|
||||
### 产品支持
|
||||
|
||||
@@ -85,11 +89,11 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
|
||||
## 特别鸣谢
|
||||
|
||||
- 基于 [Macaron](https://github.com/go-macaron/macaron) 的路由与中间件机制。
|
||||
- 基于 [WeTalk](https://github.com/beego/wetalk) 修改的模块设计。
|
||||
- 基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改的系统监视状态。
|
||||
- 感谢 [lavachen](http://www.lavachen.cn/) 和 [Rocker](http://weibo.com/rocker1989) 设计的 Logo。
|
||||
- 感谢 [Rocker](http://weibo.com/rocker1989) 设计的 Logo。
|
||||
- 感谢 [Crowdin](https://crowdin.com/project/gogs) 提供免费的开源项目本地化支持。
|
||||
- 感谢 [DigitalOcean](https://www.digitalocean.com) 提供主站和体验站点的服务器赞助。
|
||||
- 感谢 [KeyCDN](https://www.keycdn.com/) 和 [七牛云存储](http://www.qiniu.com/) 提供 CDN 服务赞助。
|
||||
|
||||
## 贡献成员
|
||||
|
||||
|
||||
20
appveyor.yml
Normal file
20
appveyor.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
version: "{build}"
|
||||
skip_tags: true
|
||||
clone_folder: c:\gopath\src\github.com\gogits\gogs
|
||||
clone_depth: 1
|
||||
|
||||
environment:
|
||||
GOPATH: c:\gopath
|
||||
GOVERSION: 1.7
|
||||
|
||||
build: false
|
||||
deploy: false
|
||||
|
||||
install:
|
||||
- go build -v
|
||||
|
||||
notifications:
|
||||
- provider: Email
|
||||
to:
|
||||
- u@gogs.io
|
||||
on_build_success: false
|
||||
70
cmd/admin.go
Normal file
70
cmd/admin.go
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
CmdAdmin = cli.Command{
|
||||
Name: "admin",
|
||||
Usage: "Preform admin operations on command line",
|
||||
Description: `Allow using internal logic of Gogs without hacking into the source code
|
||||
to make automatic initialization process more smoothly`,
|
||||
Subcommands: []cli.Command{
|
||||
subcmdCreateUser,
|
||||
},
|
||||
}
|
||||
|
||||
subcmdCreateUser = cli.Command{
|
||||
Name: "create-user",
|
||||
Usage: "Create a new user in database",
|
||||
Action: runCreateUser,
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("name", "", "Username"),
|
||||
stringFlag("password", "", "User password"),
|
||||
stringFlag("email", "", "User email address"),
|
||||
boolFlag("admin", "User is an admin"),
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func runCreateUser(c *cli.Context) error {
|
||||
if !c.IsSet("name") {
|
||||
return fmt.Errorf("Username is not specified")
|
||||
} else if !c.IsSet("password") {
|
||||
return fmt.Errorf("Password is not specified")
|
||||
} else if !c.IsSet("email") {
|
||||
return fmt.Errorf("Email is not specified")
|
||||
}
|
||||
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
setting.NewContext()
|
||||
models.LoadConfigs()
|
||||
models.SetEngine()
|
||||
|
||||
if err := models.CreateUser(&models.User{
|
||||
Name: c.String("name"),
|
||||
Email: c.String("email"),
|
||||
Passwd: c.String("password"),
|
||||
IsActive: true,
|
||||
IsAdmin: c.Bool("admin"),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CreateUser: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("New user '%s' has been successfully created!\n", c.String("name"))
|
||||
return nil
|
||||
}
|
||||
10
cmd/cert.go
10
cmd/cert.go
@@ -22,7 +22,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var CmdCert = cli.Command{
|
||||
@@ -59,7 +59,7 @@ func pemBlockForKey(priv interface{}) *pem.Block {
|
||||
case *ecdsa.PrivateKey:
|
||||
b, err := x509.MarshalECPrivateKey(k)
|
||||
if err != nil {
|
||||
log.Fatal("unable to marshal ECDSA private key: %v", err)
|
||||
log.Fatalf("Unable to marshal ECDSA private key: %v\n", err)
|
||||
}
|
||||
return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
|
||||
default:
|
||||
@@ -67,7 +67,7 @@ func pemBlockForKey(priv interface{}) *pem.Block {
|
||||
}
|
||||
}
|
||||
|
||||
func runCert(ctx *cli.Context) {
|
||||
func runCert(ctx *cli.Context) error {
|
||||
if len(ctx.String("host")) == 0 {
|
||||
log.Fatal("Missing required --host parameter")
|
||||
}
|
||||
@@ -153,9 +153,11 @@ func runCert(ctx *cli.Context) {
|
||||
|
||||
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.Fatal("failed to open key.pem for writing: %v", err)
|
||||
log.Fatalf("Failed to open key.pem for writing: %v\n", err)
|
||||
}
|
||||
pem.Encode(keyOut, pemBlockForKey(priv))
|
||||
keyOut.Close()
|
||||
log.Println("Written key.pem")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var CmdCert = cli.Command{
|
||||
@@ -20,7 +20,9 @@ var CmdCert = cli.Command{
|
||||
Action: runCert,
|
||||
}
|
||||
|
||||
func runCert(ctx *cli.Context) {
|
||||
func runCert(ctx *cli.Context) error {
|
||||
fmt.Println("Command cert not available, please use build tags 'cert' to rebuild.")
|
||||
os.Exit(1)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ package cmd
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func stringFlag(name, value, usage string) cli.StringFlag {
|
||||
|
||||
43
cmd/dump.go
43
cmd/dump.go
@@ -14,7 +14,8 @@ import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/Unknwon/cae/zip"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
@@ -28,11 +29,12 @@ It can be used for backup and capture Gogs server image to send to maintainer`,
|
||||
Action: runDump,
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
boolFlag("verbose, v", "show process details"),
|
||||
boolFlag("verbose, v", "Show process details"),
|
||||
stringFlag("tempdir, t", os.TempDir(), "Temporary dir path"),
|
||||
},
|
||||
}
|
||||
|
||||
func runDump(ctx *cli.Context) {
|
||||
func runDump(ctx *cli.Context) error {
|
||||
if ctx.IsSet("config") {
|
||||
setting.CustomConf = ctx.String("config")
|
||||
}
|
||||
@@ -40,7 +42,11 @@ func runDump(ctx *cli.Context) {
|
||||
models.LoadConfigs()
|
||||
models.SetEngine()
|
||||
|
||||
TmpWorkDir, err := ioutil.TempDir(os.TempDir(), "gogs-dump-")
|
||||
tmpDir := ctx.String("tempdir")
|
||||
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
||||
log.Fatalf("Path does not exist: %s", tmpDir)
|
||||
}
|
||||
TmpWorkDir, err := ioutil.TempDir(tmpDir, "gogs-dump-")
|
||||
if err != nil {
|
||||
log.Fatalf("Fail to create tmp work directory: %v", err)
|
||||
}
|
||||
@@ -68,30 +74,49 @@ func runDump(ctx *cli.Context) {
|
||||
log.Fatalf("Fail to create %s: %v", fileName, err)
|
||||
}
|
||||
|
||||
if err := z.AddFile("gogs-repo.zip", reposDump); err !=nil {
|
||||
if err := z.AddFile("gogs-repo.zip", reposDump); err != nil {
|
||||
log.Fatalf("Fail to include gogs-repo.zip: %v", err)
|
||||
}
|
||||
if err := z.AddFile("gogs-db.sql", dbDump); err !=nil {
|
||||
if err := z.AddFile("gogs-db.sql", dbDump); err != nil {
|
||||
log.Fatalf("Fail to include gogs-db.sql: %v", err)
|
||||
}
|
||||
customDir, err := os.Stat(setting.CustomPath)
|
||||
if err == nil && customDir.IsDir() {
|
||||
if err := z.AddDir("custom", setting.CustomPath); err !=nil {
|
||||
if err := z.AddDir("custom", setting.CustomPath); err != nil {
|
||||
log.Fatalf("Fail to include custom: %v", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Printf("Custom dir %s doesn't exist, skipped", setting.CustomPath)
|
||||
}
|
||||
if err := z.AddDir("log", setting.LogRootPath); err !=nil {
|
||||
|
||||
if err := z.AddDir("log", setting.LogRootPath); err != nil {
|
||||
log.Fatalf("Fail to include log: %v", err)
|
||||
}
|
||||
|
||||
for _, dir := range []string{"attachments", "avatars"} {
|
||||
dirPath := path.Join(setting.AppDataPath, dir)
|
||||
if !com.IsDir(dirPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := z.AddDir(path.Join("data", dir), dirPath); err != nil {
|
||||
log.Fatalf("Fail to include '%s': %v", dirPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: SSH key file.
|
||||
if err = z.Close(); err != nil {
|
||||
os.Remove(fileName)
|
||||
log.Fatalf("Fail to save %s: %v", fileName, err)
|
||||
}
|
||||
|
||||
if err := os.Chmod(fileName, 0600); err != nil {
|
||||
log.Printf("Can't change file access permissions mask to 0600: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("Removing tmp work dir: %s", TmpWorkDir)
|
||||
os.RemoveAll(TmpWorkDir)
|
||||
log.Printf("Finish dumping in file %s", fileName)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
82
cmd/import.go
Normal file
82
cmd/import.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
CmdImport = cli.Command{
|
||||
Name: "import",
|
||||
Usage: "Import portable data as local Gogs data",
|
||||
Description: `Allow user import data from other Gogs installations to local instance
|
||||
without manually hacking the data files`,
|
||||
Subcommands: []cli.Command{
|
||||
subcmdImportLocale,
|
||||
},
|
||||
}
|
||||
|
||||
subcmdImportLocale = cli.Command{
|
||||
Name: "locale",
|
||||
Usage: "Import locale files to local repository",
|
||||
Action: runImportLocale,
|
||||
Flags: []cli.Flag{
|
||||
stringFlag("source", "", "Source directory that stores new locale files"),
|
||||
stringFlag("target", "", "Target directory that stores old locale files"),
|
||||
stringFlag("config, c", "custom/conf/app.ini", "Custom configuration file path"),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func runImportLocale(c *cli.Context) error {
|
||||
if !c.IsSet("source") {
|
||||
return fmt.Errorf("Source directory is not specified")
|
||||
} else if !c.IsSet("target") {
|
||||
return fmt.Errorf("Target directory is not specified")
|
||||
}
|
||||
if !com.IsDir(c.String("source")) {
|
||||
return fmt.Errorf("Source directory does not exist or is not a directory")
|
||||
} else if !com.IsDir(c.String("target")) {
|
||||
return fmt.Errorf("Target directory does not exist or is not a directory")
|
||||
}
|
||||
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
setting.NewContext()
|
||||
|
||||
now := time.Now()
|
||||
|
||||
// Cut out en-US.
|
||||
for _, lang := range setting.Langs[1:] {
|
||||
name := fmt.Sprintf("locale_%s.ini", lang)
|
||||
source := filepath.Join(c.String("source"), name)
|
||||
target := filepath.Join(c.String("target"), name)
|
||||
if !com.IsFile(source) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := com.Copy(source, target); err != nil {
|
||||
return fmt.Errorf("Copy file: %v", err)
|
||||
}
|
||||
|
||||
// Modification time of files from Crowdin often ahead of current,
|
||||
// so we need to set back to current.
|
||||
os.Chtimes(target, now, now)
|
||||
}
|
||||
|
||||
fmt.Println("Locale files has been successfully imported!")
|
||||
return nil
|
||||
}
|
||||
147
cmd/serve.go
147
cmd/serve.go
@@ -14,14 +14,15 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/codegangsta/cli"
|
||||
git "github.com/gogits/git-module"
|
||||
gouuid "github.com/satori/go.uuid"
|
||||
"github.com/urfave/cli"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/httplib"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/gogits/gogs/modules/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -40,12 +41,16 @@ var CmdServ = cli.Command{
|
||||
|
||||
func setup(logPath string) {
|
||||
setting.NewContext()
|
||||
log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath))
|
||||
|
||||
if setting.DisableSSH {
|
||||
println("Gogs: SSH has been disabled")
|
||||
os.Exit(1)
|
||||
}
|
||||
setting.NewService()
|
||||
log.New(log.FILE, log.FileConfig{
|
||||
Filename: filepath.Join(setting.LogRootPath, logPath),
|
||||
FileRotationConfig: log.FileRotationConfig{
|
||||
Rotate: true,
|
||||
Daily: true,
|
||||
MaxDays: 3,
|
||||
},
|
||||
})
|
||||
log.Delete(log.CONSOLE) // Remove primary logger
|
||||
|
||||
models.LoadConfigs()
|
||||
|
||||
@@ -65,6 +70,24 @@ func parseCmd(cmd string) (string, string) {
|
||||
return ss[0], strings.Replace(ss[1], "'/", "'", 1)
|
||||
}
|
||||
|
||||
func checkDeployKey(key *models.PublicKey, repo *models.Repository) {
|
||||
// Check if this deploy key belongs to current repository.
|
||||
if !models.HasDeployKey(key.ID, repo.ID) {
|
||||
fail("Key access denied", "Deploy key access denied: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
|
||||
}
|
||||
|
||||
// Update deploy key activity.
|
||||
deployKey, err := models.GetDeployKeyByRepo(key.ID, repo.ID)
|
||||
if err != nil {
|
||||
fail("Internal error", "GetDeployKey: %v", err)
|
||||
}
|
||||
|
||||
deployKey.Updated = time.Now()
|
||||
if err = models.UpdateDeployKey(deployKey); err != nil {
|
||||
fail("Internal error", "UpdateDeployKey: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
allowedCommands = map[string]models.AccessMode{
|
||||
"git-upload-pack": models.ACCESS_MODE_READ,
|
||||
@@ -80,11 +103,10 @@ func fail(userMessage, logMessage string, args ...interface{}) {
|
||||
if !setting.ProdMode {
|
||||
fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
|
||||
}
|
||||
log.GitLogger.Fatal(3, logMessage, args...)
|
||||
return
|
||||
log.Fatal(3, logMessage, args...)
|
||||
}
|
||||
|
||||
log.GitLogger.Close()
|
||||
log.Shutdown()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -92,27 +114,34 @@ func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string,
|
||||
task, err := models.GetUpdateTaskByUUID(uuid)
|
||||
if err != nil {
|
||||
if models.IsErrUpdateTaskNotExist(err) {
|
||||
log.GitLogger.Trace("No update task is presented: %s", uuid)
|
||||
log.Trace("No update task is presented: %s", uuid)
|
||||
return
|
||||
}
|
||||
log.GitLogger.Fatal(2, "GetUpdateTaskByUUID: %v", err)
|
||||
log.Fatal(2, "GetUpdateTaskByUUID: %v", err)
|
||||
} else if err = models.DeleteUpdateTaskByUUID(uuid); err != nil {
|
||||
log.GitLogger.Fatal(2, "DeleteUpdateTaskByUUID: %v", err)
|
||||
log.Fatal(2, "DeleteUpdateTaskByUUID: %v", err)
|
||||
}
|
||||
|
||||
if isWiki {
|
||||
return
|
||||
}
|
||||
|
||||
if err = models.Update(task.RefName, task.OldCommitID, task.NewCommitID,
|
||||
user.Name, repoUser.Name, reponame, user.Id); err != nil {
|
||||
log.GitLogger.Error(2, "Update: %v", err)
|
||||
if err = models.PushUpdate(models.PushUpdateOptions{
|
||||
RefFullName: task.RefName,
|
||||
OldCommitID: task.OldCommitID,
|
||||
NewCommitID: task.NewCommitID,
|
||||
PusherID: user.ID,
|
||||
PusherName: user.Name,
|
||||
RepoUserName: repoUser.Name,
|
||||
RepoName: reponame,
|
||||
}); err != nil {
|
||||
log.Error(2, "Update: %v", err)
|
||||
}
|
||||
|
||||
// Ask for running deliver hook and test pull request tasks.
|
||||
reqURL := setting.LocalURL + repoUser.Name + "/" + reponame + "/tasks/trigger?branch=" +
|
||||
strings.TrimPrefix(task.RefName, "refs/heads/") + "&secret=" + base.EncodeMD5(repoUser.Salt)
|
||||
log.GitLogger.Trace("Trigger task: %s", reqURL)
|
||||
strings.TrimPrefix(task.RefName, git.BRANCH_PREFIX) + "&secret=" + base.EncodeMD5(repoUser.Salt) + "&pusher=" + com.ToStr(user.ID)
|
||||
log.Trace("Trigger task: %s", reqURL)
|
||||
|
||||
resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
@@ -120,19 +149,25 @@ func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string,
|
||||
if err == nil {
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode/100 != 2 {
|
||||
log.GitLogger.Error(2, "Fail to trigger task: not 2xx response code")
|
||||
log.Error(2, "Fail to trigger task: not 2xx response code")
|
||||
}
|
||||
} else {
|
||||
log.GitLogger.Error(2, "Fail to trigger task: %v", err)
|
||||
log.Error(2, "Fail to trigger task: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func runServ(c *cli.Context) {
|
||||
func runServ(c *cli.Context) error {
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
setup("serv.log")
|
||||
|
||||
if setting.SSH.Disabled {
|
||||
println("Gogs: SSH has been disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(c.Args()) < 1 {
|
||||
fail("Not enough arguments", "Not enough arguments")
|
||||
}
|
||||
@@ -141,7 +176,7 @@ func runServ(c *cli.Context) {
|
||||
if len(cmd) == 0 {
|
||||
println("Hi there, You've successfully authenticated, but Gogs does not provide shell access.")
|
||||
println("If this is unexpected, please log in with password and setup Gogs under another user.")
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
verb, args := parseCmd(cmd)
|
||||
@@ -167,7 +202,7 @@ func runServ(c *cli.Context) {
|
||||
fail("Internal error", "Failed to get repository owner (%s): %v", username, err)
|
||||
}
|
||||
|
||||
repo, err := models.GetRepositoryByName(repoUser.Id, reponame)
|
||||
repo, err := models.GetRepositoryByName(repoUser.ID, reponame)
|
||||
if err != nil {
|
||||
if models.IsErrRepoNotExist(err) {
|
||||
fail(_ACCESS_DENIED_MESSAGE, "Repository does not exist: %s/%s", repoUser.Name, reponame)
|
||||
@@ -185,47 +220,25 @@ func runServ(c *cli.Context) {
|
||||
fail("mirror repository is read-only", "")
|
||||
}
|
||||
|
||||
// Allow anonymous clone for public repositories.
|
||||
var (
|
||||
keyID int64
|
||||
user *models.User
|
||||
)
|
||||
// Allow anonymous (user is nil) clone for public repositories.
|
||||
var user *models.User
|
||||
|
||||
key, err := models.GetPublicKeyByID(com.StrTo(strings.TrimPrefix(c.Args()[0], "key-")).MustInt64())
|
||||
if err != nil {
|
||||
fail("Invalid key ID", "Invalid key ID [%s]: %v", c.Args()[0], err)
|
||||
}
|
||||
|
||||
if requestedMode == models.ACCESS_MODE_WRITE || repo.IsPrivate {
|
||||
keys := strings.Split(c.Args()[0], "-")
|
||||
if len(keys) != 2 {
|
||||
fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
|
||||
}
|
||||
|
||||
key, err := models.GetPublicKeyByID(com.StrTo(keys[1]).MustInt64())
|
||||
if err != nil {
|
||||
fail("Invalid key ID", "Invalid key ID[%s]: %v", c.Args()[0], err)
|
||||
}
|
||||
keyID = key.ID
|
||||
|
||||
// Check deploy key or user key.
|
||||
if key.Type == models.KEY_TYPE_DEPLOY {
|
||||
if key.IsDeployKey() {
|
||||
if key.Mode < requestedMode {
|
||||
fail("Key permission denied", "Cannot push with deployment key: %d", key.ID)
|
||||
}
|
||||
// Check if this deploy key belongs to current repository.
|
||||
if !models.HasDeployKey(key.ID, repo.ID) {
|
||||
fail("Key access denied", "Deploy key access denied: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
|
||||
}
|
||||
|
||||
// Update deploy key activity.
|
||||
deployKey, err := models.GetDeployKeyByRepo(key.ID, repo.ID)
|
||||
if err != nil {
|
||||
fail("Internal error", "GetDeployKey: %v", err)
|
||||
}
|
||||
|
||||
deployKey.Updated = time.Now()
|
||||
if err = models.UpdateDeployKey(deployKey); err != nil {
|
||||
fail("Internal error", "UpdateDeployKey: %v", err)
|
||||
}
|
||||
checkDeployKey(key, repo)
|
||||
} else {
|
||||
user, err = models.GetUserByKeyID(key.ID)
|
||||
if err != nil {
|
||||
fail("internal error", "Failed to get user by key ID(%d): %v", keyID, err)
|
||||
fail("internal error", "Failed to get user by key ID(%d): %v", key.ID, err)
|
||||
}
|
||||
|
||||
mode, err := models.AccessLevel(user, repo)
|
||||
@@ -241,9 +254,17 @@ func runServ(c *cli.Context) {
|
||||
user.Name, requestedMode, repoPath)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Check if the key can access to the repository in case of it is a deploy key (a deploy keys != user key).
|
||||
// A deploy key doesn't represent a signed in user, so in a site with Service.RequireSignInView activated
|
||||
// we should give read access only in repositories where this deploy key is in use. In other case, a server
|
||||
// or system using an active deploy key can get read access to all the repositories in a Gogs service.
|
||||
if key.IsDeployKey() && setting.Service.RequireSignInView {
|
||||
checkDeployKey(key, repo)
|
||||
}
|
||||
}
|
||||
|
||||
uuid := uuid.NewV4().String()
|
||||
uuid := gouuid.NewV4().String()
|
||||
os.Setenv("uuid", uuid)
|
||||
|
||||
// Special handle for Windows.
|
||||
@@ -271,10 +292,10 @@ func runServ(c *cli.Context) {
|
||||
}
|
||||
|
||||
// Update user key activity.
|
||||
if keyID > 0 {
|
||||
key, err := models.GetPublicKeyByID(keyID)
|
||||
if key.ID > 0 {
|
||||
key, err := models.GetPublicKeyByID(key.ID)
|
||||
if err != nil {
|
||||
fail("Internal error", "GetPublicKeyById: %v", err)
|
||||
fail("Internal error", "GetPublicKeyByID: %v", err)
|
||||
}
|
||||
|
||||
key.Updated = time.Now()
|
||||
@@ -282,4 +303,6 @@ func runServ(c *cli.Context) {
|
||||
fail("Internal error", "UpdatePublicKey: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,16 +7,16 @@ package cmd
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/urfave/cli"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
var CmdUpdate = cli.Command{
|
||||
Name: "update",
|
||||
Usage: "This command should only be called by SSH shell",
|
||||
Usage: "This command should only be called by Git hook",
|
||||
Description: `Update get pushed info and insert into database`,
|
||||
Action: runUpdate,
|
||||
Flags: []cli.Flag{
|
||||
@@ -24,22 +24,23 @@ var CmdUpdate = cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func runUpdate(c *cli.Context) {
|
||||
func runUpdate(c *cli.Context) error {
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
|
||||
if cmd == "" {
|
||||
return
|
||||
}
|
||||
|
||||
setup("update.log")
|
||||
|
||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
log.Trace("SSH_ORIGINAL_COMMAND is empty")
|
||||
return nil
|
||||
}
|
||||
|
||||
args := c.Args()
|
||||
if len(args) != 3 {
|
||||
log.GitLogger.Fatal(2, "received less 3 parameters")
|
||||
} else if args[0] == "" {
|
||||
log.GitLogger.Fatal(2, "refName is empty, shouldn't use")
|
||||
log.Fatal(2, "Arguments received are not equal to three")
|
||||
} else if len(args[0]) == 0 {
|
||||
log.Fatal(2, "First argument 'refName' is empty, shouldn't use")
|
||||
}
|
||||
|
||||
task := models.UpdateTask{
|
||||
@@ -50,6 +51,8 @@ func runUpdate(c *cli.Context) {
|
||||
}
|
||||
|
||||
if err := models.AddUpdateTask(&task); err != nil {
|
||||
log.GitLogger.Fatal(2, "AddUpdateTask: %v", err)
|
||||
log.Fatal(2, "AddUpdateTask: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
291
cmd/web.go
291
cmd/web.go
@@ -7,15 +7,14 @@ package cmd
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
gotmpl "html/template"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/fcgi"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/go-macaron/binding"
|
||||
"github.com/go-macaron/cache"
|
||||
"github.com/go-macaron/captcha"
|
||||
@@ -26,6 +25,8 @@ import (
|
||||
"github.com/go-macaron/toolbox"
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/mcuadros/go-version"
|
||||
"github.com/urfave/cli"
|
||||
log "gopkg.in/clog.v1"
|
||||
"gopkg.in/ini.v1"
|
||||
"gopkg.in/macaron.v1"
|
||||
|
||||
@@ -34,10 +35,9 @@ import (
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/auth"
|
||||
"github.com/gogits/gogs/modules/avatar"
|
||||
"github.com/gogits/gogs/modules/bindata"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/middleware"
|
||||
"github.com/gogits/gogs/modules/context"
|
||||
"github.com/gogits/gogs/modules/mailer"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/gogits/gogs/modules/template"
|
||||
"github.com/gogits/gogs/routers"
|
||||
@@ -74,27 +74,35 @@ func checkVersion() {
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to read 'templates/.VERSION': %v", err)
|
||||
}
|
||||
if string(data) != setting.AppVer {
|
||||
log.Fatal(4, "Binary and template file version does not match, did you forget to recompile?")
|
||||
tplVer := string(data)
|
||||
if tplVer != setting.AppVer {
|
||||
if version.Compare(tplVer, setting.AppVer, ">") {
|
||||
log.Fatal(4, "Binary version is lower than template file version, did you forget to recompile Gogs?")
|
||||
} else {
|
||||
log.Fatal(4, "Binary version is higher than template file version, did you forget to update template files?")
|
||||
}
|
||||
}
|
||||
|
||||
// Check dependency version.
|
||||
checkers := []VerChecker{
|
||||
{"github.com/go-xorm/xorm", func() string { return xorm.Version }, "0.4.4.1029"},
|
||||
{"github.com/go-macaron/binding", binding.Version, "0.1.0"},
|
||||
{"github.com/go-xorm/xorm", func() string { return xorm.Version }, "0.6.0"},
|
||||
{"github.com/go-macaron/binding", binding.Version, "0.3.2"},
|
||||
{"github.com/go-macaron/cache", cache.Version, "0.1.2"},
|
||||
{"github.com/go-macaron/csrf", csrf.Version, "0.0.3"},
|
||||
{"github.com/go-macaron/i18n", i18n.Version, "0.2.0"},
|
||||
{"github.com/go-macaron/csrf", csrf.Version, "0.1.0"},
|
||||
{"github.com/go-macaron/i18n", i18n.Version, "0.3.0"},
|
||||
{"github.com/go-macaron/session", session.Version, "0.1.6"},
|
||||
{"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"},
|
||||
{"gopkg.in/ini.v1", ini.Version, "1.8.4"},
|
||||
{"gopkg.in/macaron.v1", macaron.Version, "0.8.0"},
|
||||
{"github.com/gogits/git-module", git.Version, "0.2.4"},
|
||||
{"github.com/gogits/go-gogs-client", gogs.Version, "0.7.2"},
|
||||
{"gopkg.in/macaron.v1", macaron.Version, "1.1.7"},
|
||||
{"github.com/gogits/git-module", git.Version, "0.4.6"},
|
||||
{"github.com/gogits/go-gogs-client", gogs.Version, "0.12.1"},
|
||||
}
|
||||
for _, c := range checkers {
|
||||
if !version.Compare(c.Version(), c.Expected, ">=") {
|
||||
log.Fatal(4, "Package '%s' version is too old (%s -> %s), did you forget to update?", c.ImportPath, c.Version(), c.Expected)
|
||||
log.Fatal(4, `Dependency outdated!
|
||||
Package '%s' current version (%s) is below requirement (%s),
|
||||
please use following command to update this package and recompile Gogs:
|
||||
go get -u %[1]s`, c.ImportPath, c.Version(), c.Expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,7 +117,7 @@ func newMacaron() *macaron.Macaron {
|
||||
if setting.EnableGzip {
|
||||
m.Use(gzip.Gziper())
|
||||
}
|
||||
if setting.Protocol == setting.FCGI {
|
||||
if setting.Protocol == setting.SCHEME_FCGI {
|
||||
m.SetURLPrefix(setting.AppSubUrl)
|
||||
}
|
||||
m.Use(macaron.Static(
|
||||
@@ -125,11 +133,16 @@ func newMacaron() *macaron.Macaron {
|
||||
SkipLogging: setting.DisableRouterLog,
|
||||
},
|
||||
))
|
||||
|
||||
funcMap := template.NewFuncMap()
|
||||
m.Use(macaron.Renderer(macaron.RenderOptions{
|
||||
Directory: path.Join(setting.StaticRootPath, "templates"),
|
||||
Funcs: []gotmpl.FuncMap{template.Funcs},
|
||||
IndentJSON: macaron.Env != macaron.PROD,
|
||||
Directory: path.Join(setting.StaticRootPath, "templates"),
|
||||
AppendDirectories: []string{path.Join(setting.CustomPath, "templates")},
|
||||
Funcs: funcMap,
|
||||
IndentJSON: macaron.Env != macaron.PROD,
|
||||
}))
|
||||
mailer.InitMailRender(path.Join(setting.StaticRootPath, "templates/mail"),
|
||||
path.Join(setting.CustomPath, "templates/mail"), funcMap)
|
||||
|
||||
localeNames, err := bindata.AssetDir("conf/locale")
|
||||
if err != nil {
|
||||
@@ -151,7 +164,7 @@ func newMacaron() *macaron.Macaron {
|
||||
m.Use(cache.Cacher(cache.Options{
|
||||
Adapter: setting.CacheAdapter,
|
||||
AdapterConfig: setting.CacheConn,
|
||||
Interval: setting.CacheInternal,
|
||||
Interval: setting.CacheInterval,
|
||||
}))
|
||||
m.Use(captcha.Captchaer(captcha.Options{
|
||||
SubURL: setting.AppSubUrl,
|
||||
@@ -159,6 +172,7 @@ func newMacaron() *macaron.Macaron {
|
||||
m.Use(session.Sessioner(setting.SessionConfig))
|
||||
m.Use(csrf.Csrfer(csrf.Options{
|
||||
Secret: setting.SecretKey,
|
||||
Cookie: setting.CSRFCookieName,
|
||||
SetCookie: true,
|
||||
Header: "X-Csrf-Token",
|
||||
CookiePath: setting.AppSubUrl,
|
||||
@@ -171,11 +185,11 @@ func newMacaron() *macaron.Macaron {
|
||||
},
|
||||
},
|
||||
}))
|
||||
m.Use(middleware.Contexter())
|
||||
m.Use(context.Contexter())
|
||||
return m
|
||||
}
|
||||
|
||||
func runWeb(ctx *cli.Context) {
|
||||
func runWeb(ctx *cli.Context) error {
|
||||
if ctx.IsSet("config") {
|
||||
setting.CustomConf = ctx.String("config")
|
||||
}
|
||||
@@ -184,26 +198,29 @@ func runWeb(ctx *cli.Context) {
|
||||
|
||||
m := newMacaron()
|
||||
|
||||
reqSignIn := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true})
|
||||
ignSignIn := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: setting.Service.RequireSignInView})
|
||||
ignSignInAndCsrf := middleware.Toggle(&middleware.ToggleOptions{DisableCsrf: true})
|
||||
reqSignOut := middleware.Toggle(&middleware.ToggleOptions{SignOutRequire: true})
|
||||
reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true})
|
||||
ignSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: setting.Service.RequireSignInView})
|
||||
ignSignInAndCsrf := context.Toggle(&context.ToggleOptions{DisableCSRF: true})
|
||||
reqSignOut := context.Toggle(&context.ToggleOptions{SignOutRequired: true})
|
||||
|
||||
bindIgnErr := binding.BindIgnErr
|
||||
|
||||
// FIXME: not all routes need go through same middlewares.
|
||||
// Especially some AJAX requests, we can reduce middleware number to improve performance.
|
||||
// Routers.
|
||||
m.Get("/", ignSignIn, routers.Home)
|
||||
m.Get("/explore", ignSignIn, routers.Explore)
|
||||
m.Group("/explore", func() {
|
||||
m.Get("", func(ctx *context.Context) {
|
||||
ctx.Redirect(setting.AppSubUrl + "/explore/repos")
|
||||
})
|
||||
m.Get("/repos", routers.ExploreRepos)
|
||||
m.Get("/users", routers.ExploreUsers)
|
||||
m.Get("/organizations", routers.ExploreOrganizations)
|
||||
}, ignSignIn)
|
||||
m.Combo("/install", routers.InstallInit).Get(routers.Install).
|
||||
Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost)
|
||||
m.Get("/^:type(issues|pulls)$", reqSignIn, user.Issues)
|
||||
|
||||
// ***** START: API *****
|
||||
m.Group("/api", func() {
|
||||
apiv1.RegisterRoutes(m)
|
||||
}, ignSignIn)
|
||||
// ***** END: API *****
|
||||
|
||||
// ***** START: User *****
|
||||
m.Group("/user", func() {
|
||||
m.Get("/login", user.SignIn)
|
||||
@@ -217,7 +234,9 @@ func runWeb(ctx *cli.Context) {
|
||||
m.Group("/user/settings", func() {
|
||||
m.Get("", user.Settings)
|
||||
m.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost)
|
||||
m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), user.SettingsAvatar)
|
||||
m.Combo("/avatar").Get(user.SettingsAvatar).
|
||||
Post(binding.MultipartForm(auth.AvatarForm{}), user.SettingsAvatarPost)
|
||||
m.Post("/avatar/delete", user.SettingsDeleteAvatar)
|
||||
m.Combo("/email").Get(user.SettingsEmails).
|
||||
Post(bindIgnErr(auth.AddEmailForm{}), user.SettingsEmailPost)
|
||||
m.Post("/email/delete", user.DeleteEmail)
|
||||
@@ -229,8 +248,14 @@ func runWeb(ctx *cli.Context) {
|
||||
m.Combo("/applications").Get(user.SettingsApplications).
|
||||
Post(bindIgnErr(auth.NewAccessTokenForm{}), user.SettingsApplicationsPost)
|
||||
m.Post("/applications/delete", user.SettingsDeleteApplication)
|
||||
|
||||
m.Group("/organizations", func() {
|
||||
m.Get("", user.SettingsOrganizations)
|
||||
m.Post("/leave", user.SettingsLeaveOrganization)
|
||||
})
|
||||
|
||||
m.Route("/delete", "GET,POST", user.SettingsDelete)
|
||||
}, reqSignIn, func(ctx *middleware.Context) {
|
||||
}, reqSignIn, func(ctx *context.Context) {
|
||||
ctx.Data["PageIsUserSettings"] = true
|
||||
})
|
||||
|
||||
@@ -245,17 +270,13 @@ func runWeb(ctx *cli.Context) {
|
||||
})
|
||||
// ***** END: User *****
|
||||
|
||||
// Gravatar service.
|
||||
avt := avatar.CacheServer("public/img/avatar/", "public/img/avatar_default.jpg")
|
||||
os.MkdirAll("public/img/avatar/", os.ModePerm)
|
||||
m.Get("/avatar/:hash", avt.ServeHTTP)
|
||||
|
||||
adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true})
|
||||
adminReq := context.Toggle(&context.ToggleOptions{SignInRequired: true, AdminRequired: true})
|
||||
|
||||
// ***** START: Admin *****
|
||||
m.Group("/admin", func() {
|
||||
m.Get("", adminReq, admin.Dashboard)
|
||||
m.Get("/config", admin.Config)
|
||||
m.Post("/config/test_mail", admin.SendTestMail)
|
||||
m.Get("/monitor", admin.Monitor)
|
||||
|
||||
m.Group("/users", func() {
|
||||
@@ -298,7 +319,7 @@ func runWeb(ctx *cli.Context) {
|
||||
m.Get("/stars", user.Stars)
|
||||
})
|
||||
|
||||
m.Get("/attachments/:uuid", func(ctx *middleware.Context) {
|
||||
m.Get("/attachments/:uuid", func(ctx *context.Context) {
|
||||
attach, err := models.GetAttachmentByUUID(ctx.Params(":uuid"))
|
||||
if err != nil {
|
||||
if models.IsErrAttachmentNotExist(err) {
|
||||
@@ -317,6 +338,7 @@ func runWeb(ctx *cli.Context) {
|
||||
defer fr.Close()
|
||||
|
||||
ctx.Header().Set("Cache-Control", "public,max-age=86400")
|
||||
ctx.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, attach.Name))
|
||||
// Fix #312. Attachments with , in their name are not handled correctly by Google Chrome.
|
||||
// We must put the name in " manually.
|
||||
if err = repo.ServeData(ctx, "\""+attach.Name+"\"", fr); err != nil {
|
||||
@@ -335,13 +357,19 @@ func runWeb(ctx *cli.Context) {
|
||||
m.Get("/template/*", dev.TemplatePreview)
|
||||
}
|
||||
|
||||
reqRepoAdmin := middleware.RequireRepoAdmin()
|
||||
reqRepoPusher := middleware.RequireRepoPusher()
|
||||
reqRepoAdmin := context.RequireRepoAdmin()
|
||||
reqRepoWriter := context.RequireRepoWriter()
|
||||
|
||||
// ***** START: Organization *****
|
||||
m.Group("/org", func() {
|
||||
m.Get("/create", org.Create)
|
||||
m.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost)
|
||||
m.Group("", func() {
|
||||
m.Get("/create", org.Create)
|
||||
m.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost)
|
||||
}, func(ctx *context.Context) {
|
||||
if !ctx.User.CanCreateOrganization() {
|
||||
ctx.NotFound()
|
||||
}
|
||||
})
|
||||
|
||||
m.Group("/:org", func() {
|
||||
m.Get("/dashboard", user.Dashboard)
|
||||
@@ -350,11 +378,14 @@ func runWeb(ctx *cli.Context) {
|
||||
m.Get("/members/action/:action", org.MembersAction)
|
||||
|
||||
m.Get("/teams", org.Teams)
|
||||
}, context.OrgAssignment(true))
|
||||
|
||||
m.Group("/:org", func() {
|
||||
m.Get("/teams/:team", org.TeamMembers)
|
||||
m.Get("/teams/:team/repositories", org.TeamRepositories)
|
||||
m.Route("/teams/:team/action/:action", "GET,POST", org.TeamsAction)
|
||||
m.Route("/teams/:team/action/repo/:action", "GET,POST", org.TeamsRepoAction)
|
||||
}, middleware.OrgAssignment(true))
|
||||
}, context.OrgAssignment(true, false, true))
|
||||
|
||||
m.Group("/:org", func() {
|
||||
m.Get("/teams/new", org.NewTeam)
|
||||
@@ -366,7 +397,8 @@ func runWeb(ctx *cli.Context) {
|
||||
m.Group("/settings", func() {
|
||||
m.Combo("").Get(org.Settings).
|
||||
Post(bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost)
|
||||
m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), org.SettingsAvatar)
|
||||
m.Post("/avatar", binding.MultipartForm(auth.AvatarForm{}), org.SettingsAvatar)
|
||||
m.Post("/avatar/delete", org.SettingsDeleteAvatar)
|
||||
|
||||
m.Group("/hooks", func() {
|
||||
m.Get("", org.Webhooks)
|
||||
@@ -383,7 +415,7 @@ func runWeb(ctx *cli.Context) {
|
||||
})
|
||||
|
||||
m.Route("/invitations/new", "GET,POST", org.Invitation)
|
||||
}, middleware.OrgAssignment(true, true))
|
||||
}, context.OrgAssignment(true, true))
|
||||
}, reqSignIn)
|
||||
// ***** END: Organization *****
|
||||
|
||||
@@ -401,7 +433,11 @@ func runWeb(ctx *cli.Context) {
|
||||
m.Group("/settings", func() {
|
||||
m.Combo("").Get(repo.Settings).
|
||||
Post(bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost)
|
||||
m.Route("/collaboration", "GET,POST", repo.Collaboration)
|
||||
m.Group("/collaboration", func() {
|
||||
m.Combo("").Get(repo.Collaboration).Post(repo.CollaborationPost)
|
||||
m.Post("/access_mode", repo.ChangeCollaborationAccessMode)
|
||||
m.Post("/delete", repo.DeleteCollaboration)
|
||||
})
|
||||
|
||||
m.Group("/hooks", func() {
|
||||
m.Get("", repo.Webhooks)
|
||||
@@ -418,7 +454,7 @@ func runWeb(ctx *cli.Context) {
|
||||
m.Get("", repo.GitHooks)
|
||||
m.Combo("/:name").Get(repo.GitHooksEdit).
|
||||
Post(repo.GitHooksEditPost)
|
||||
}, middleware.GitHookService())
|
||||
}, context.GitHookService())
|
||||
})
|
||||
|
||||
m.Group("/keys", func() {
|
||||
@@ -427,35 +463,41 @@ func runWeb(ctx *cli.Context) {
|
||||
m.Post("/delete", repo.DeleteDeployKey)
|
||||
})
|
||||
|
||||
}, func(ctx *middleware.Context) {
|
||||
}, func(ctx *context.Context) {
|
||||
ctx.Data["PageIsSettings"] = true
|
||||
})
|
||||
}, reqSignIn, middleware.RepoAssignment(), reqRepoAdmin, middleware.RepoRef())
|
||||
}, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.RepoRef())
|
||||
|
||||
m.Get("/:username/:reponame/action/:action", reqSignIn, middleware.RepoAssignment(), repo.Action)
|
||||
m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), repo.Action)
|
||||
m.Group("/:username/:reponame", func() {
|
||||
// FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest.
|
||||
// So they can apply their own enable/disable logic on routers.
|
||||
m.Group("/issues", func() {
|
||||
m.Combo("/new", repo.MustEnableIssues).Get(middleware.RepoRef(), repo.NewIssue).
|
||||
m.Combo("/new", repo.MustEnableIssues).Get(context.RepoRef(), repo.NewIssue).
|
||||
Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost)
|
||||
|
||||
m.Combo("/:index/comments").Post(bindIgnErr(auth.CreateCommentForm{}), repo.NewComment)
|
||||
m.Group("/:index", func() {
|
||||
m.Post("/label", repo.UpdateIssueLabel)
|
||||
m.Post("/milestone", repo.UpdateIssueMilestone)
|
||||
m.Post("/assignee", repo.UpdateIssueAssignee)
|
||||
}, reqRepoAdmin)
|
||||
}, reqRepoWriter)
|
||||
|
||||
m.Group("/:index", func() {
|
||||
m.Post("/title", repo.UpdateIssueTitle)
|
||||
m.Post("/content", repo.UpdateIssueContent)
|
||||
m.Combo("/comments").Post(bindIgnErr(auth.CreateCommentForm{}), repo.NewComment)
|
||||
})
|
||||
})
|
||||
m.Post("/comments/:id", repo.UpdateCommentContent)
|
||||
m.Group("/comments/:id", func() {
|
||||
m.Post("", repo.UpdateCommentContent)
|
||||
m.Post("/delete", repo.DeleteComment)
|
||||
})
|
||||
m.Group("/labels", func() {
|
||||
m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel)
|
||||
m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel)
|
||||
m.Post("/delete", repo.DeleteLabel)
|
||||
}, reqRepoAdmin, middleware.RepoRef())
|
||||
m.Post("/initialize", bindIgnErr(auth.InitializeLabelsForm{}), repo.InitializeLabels)
|
||||
}, reqRepoWriter, context.RepoRef())
|
||||
m.Group("/milestones", func() {
|
||||
m.Combo("/new").Get(repo.NewMilestone).
|
||||
Post(bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost)
|
||||
@@ -463,19 +505,66 @@ func runWeb(ctx *cli.Context) {
|
||||
m.Post("/:id/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost)
|
||||
m.Get("/:id/:action", repo.ChangeMilestonStatus)
|
||||
m.Post("/delete", repo.DeleteMilestone)
|
||||
}, reqRepoAdmin, middleware.RepoRef())
|
||||
}, reqRepoWriter, context.RepoRef())
|
||||
|
||||
m.Group("/releases", func() {
|
||||
m.Get("/new", repo.NewRelease)
|
||||
m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
|
||||
m.Get("/edit/:tagname", repo.EditRelease)
|
||||
m.Post("/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
|
||||
m.Post("/delete", repo.DeleteRelease)
|
||||
}, reqRepoAdmin, middleware.RepoRef())
|
||||
}, reqRepoWriter, context.RepoRef())
|
||||
|
||||
m.Combo("/compare/*", repo.MustEnablePulls).Get(repo.CompareAndPullRequest).
|
||||
m.Group("/releases", func() {
|
||||
m.Get("/edit/*", repo.EditRelease)
|
||||
m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
|
||||
}, reqRepoWriter, func(ctx *context.Context) {
|
||||
var err error
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
|
||||
if err != nil {
|
||||
ctx.Handle(500, "GetBranchCommit", err)
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount()
|
||||
if err != nil {
|
||||
ctx.Handle(500, "CommitsCount", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
|
||||
})
|
||||
|
||||
// FIXME: Should use ctx.Repo.PullRequest to unify template, currently we have inconsistent URL
|
||||
// for PR in same repository. After select branch on the page, the URL contains redundant head user name.
|
||||
// e.g. /org1/test-repo/compare/master...org1:develop
|
||||
// which should be /org1/test-repo/compare/master...develop
|
||||
m.Combo("/compare/*", repo.MustAllowPulls).Get(repo.CompareAndPullRequest).
|
||||
Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
|
||||
}, reqSignIn, middleware.RepoAssignment(), repo.MustBeNotBare)
|
||||
|
||||
m.Group("", func() {
|
||||
m.Combo("/_edit/*").Get(repo.EditFile).
|
||||
Post(bindIgnErr(auth.EditRepoFileForm{}), repo.EditFilePost)
|
||||
m.Combo("/_new/*").Get(repo.NewFile).
|
||||
Post(bindIgnErr(auth.EditRepoFileForm{}), repo.NewFilePost)
|
||||
m.Post("/_preview/*", bindIgnErr(auth.EditPreviewDiffForm{}), repo.DiffPreviewPost)
|
||||
m.Combo("/_delete/*").Get(repo.DeleteFile).
|
||||
Post(bindIgnErr(auth.DeleteRepoFileForm{}), repo.DeleteFilePost)
|
||||
|
||||
m.Group("", func() {
|
||||
m.Combo("/_upload/*").Get(repo.UploadFile).
|
||||
Post(bindIgnErr(auth.UploadRepoFileForm{}), repo.UploadFilePost)
|
||||
m.Post("/upload-file", repo.UploadFileToServer)
|
||||
m.Post("/upload-remove", bindIgnErr(auth.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer)
|
||||
}, func(ctx *context.Context) {
|
||||
if !setting.Repository.Upload.Enabled {
|
||||
ctx.Handle(404, "", nil)
|
||||
return
|
||||
}
|
||||
})
|
||||
}, reqRepoWriter, context.RepoRef(), func(ctx *context.Context) {
|
||||
if !ctx.Repo.Repository.CanEnableEditor() || ctx.Repo.IsViewCommit {
|
||||
ctx.Handle(404, "", nil)
|
||||
return
|
||||
}
|
||||
})
|
||||
}, reqSignIn, context.RepoAssignment(), repo.MustBeNotBare)
|
||||
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Group("", func() {
|
||||
@@ -484,9 +573,10 @@ func runWeb(ctx *cli.Context) {
|
||||
m.Get("/^:type(issues|pulls)$/:index", repo.ViewIssue)
|
||||
m.Get("/labels/", repo.RetrieveLabels, repo.Labels)
|
||||
m.Get("/milestones", repo.Milestones)
|
||||
}, middleware.RepoRef())
|
||||
}, context.RepoRef())
|
||||
|
||||
// m.Get("/branches", repo.Branches)
|
||||
m.Post("/branches/delete/*", reqSignIn, reqRepoWriter, repo.DeleteBranchPost)
|
||||
|
||||
m.Group("/wiki", func() {
|
||||
m.Get("/?:page", repo.Wiki)
|
||||
@@ -497,37 +587,39 @@ func runWeb(ctx *cli.Context) {
|
||||
Post(bindIgnErr(auth.NewWikiForm{}), repo.NewWikiPost)
|
||||
m.Combo("/:page/_edit").Get(repo.EditWiki).
|
||||
Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost)
|
||||
}, reqSignIn, reqRepoPusher)
|
||||
}, repo.MustEnableWiki, middleware.RepoRef())
|
||||
m.Post("/:page/delete", repo.DeleteWikiPagePost)
|
||||
}, reqSignIn, reqRepoWriter)
|
||||
}, repo.MustEnableWiki, context.RepoRef())
|
||||
|
||||
m.Get("/archive/*", repo.Download)
|
||||
|
||||
m.Group("/pulls/:index", func() {
|
||||
m.Get("/commits", middleware.RepoRef(), repo.ViewPullCommits)
|
||||
m.Get("/files", middleware.RepoRef(), repo.ViewPullFiles)
|
||||
m.Post("/merge", reqRepoAdmin, repo.MergePullRequest)
|
||||
}, repo.MustEnablePulls)
|
||||
m.Get("/commits", context.RepoRef(), repo.ViewPullCommits)
|
||||
m.Get("/files", context.RepoRef(), repo.ViewPullFiles)
|
||||
m.Post("/merge", reqRepoWriter, repo.MergePullRequest)
|
||||
}, repo.MustAllowPulls)
|
||||
|
||||
m.Group("", func() {
|
||||
m.Get("/src/*", repo.Home)
|
||||
m.Get("/raw/*", repo.SingleDownload)
|
||||
m.Get("/commits/*", repo.RefCommits)
|
||||
m.Get("/commit/*", repo.Diff)
|
||||
m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.Diff)
|
||||
m.Get("/forks", repo.Forks)
|
||||
}, middleware.RepoRef())
|
||||
}, context.RepoRef())
|
||||
m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.RawDiff)
|
||||
|
||||
m.Get("/compare/:before([a-z0-9]{40})...:after([a-z0-9]{40})", repo.CompareDiff)
|
||||
}, ignSignIn, middleware.RepoAssignment(), repo.MustBeNotBare)
|
||||
m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.CompareDiff)
|
||||
}, ignSignIn, context.RepoAssignment(), repo.MustBeNotBare)
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Get("/stars", repo.Stars)
|
||||
m.Get("/watchers", repo.Watchers)
|
||||
}, ignSignIn, middleware.RepoAssignment(), middleware.RepoRef())
|
||||
}, ignSignIn, context.RepoAssignment(), context.RepoRef())
|
||||
|
||||
m.Group("/:username", func() {
|
||||
m.Group("/:reponame", func() {
|
||||
m.Get("", repo.Home)
|
||||
m.Get("\\.git$", repo.Home)
|
||||
}, ignSignIn, middleware.RepoAssignment(true), middleware.RepoRef())
|
||||
}, ignSignIn, context.RepoAssignment(true), context.RepoRef())
|
||||
|
||||
m.Group("/:reponame", func() {
|
||||
m.Any("/*", ignSignInAndCsrf, repo.HTTP)
|
||||
@@ -536,8 +628,12 @@ func runWeb(ctx *cli.Context) {
|
||||
})
|
||||
// ***** END: Repository *****
|
||||
|
||||
m.Group("/api", func() {
|
||||
apiv1.RegisterRoutes(m)
|
||||
}, ignSignIn)
|
||||
|
||||
// robots.txt
|
||||
m.Get("/robots.txt", func(ctx *middleware.Context) {
|
||||
m.Get("/robots.txt", func(ctx *context.Context) {
|
||||
if setting.HasRobotsTxt {
|
||||
ctx.ServeFileContent(path.Join(setting.CustomPath, "robots.txt"))
|
||||
} else {
|
||||
@@ -550,21 +646,42 @@ func runWeb(ctx *cli.Context) {
|
||||
|
||||
// Flag for port number in case first time run conflict.
|
||||
if ctx.IsSet("port") {
|
||||
setting.AppUrl = strings.Replace(setting.AppUrl, setting.HttpPort, ctx.String("port"), 1)
|
||||
setting.HttpPort = ctx.String("port")
|
||||
setting.AppUrl = strings.Replace(setting.AppUrl, setting.HTTPPort, ctx.String("port"), 1)
|
||||
setting.HTTPPort = ctx.String("port")
|
||||
}
|
||||
|
||||
var err error
|
||||
listenAddr := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort)
|
||||
var listenAddr string
|
||||
if setting.Protocol == setting.SCHEME_UNIX_SOCKET {
|
||||
listenAddr = fmt.Sprintf("%s", setting.HTTPAddr)
|
||||
} else {
|
||||
listenAddr = fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.HTTPPort)
|
||||
}
|
||||
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubUrl)
|
||||
|
||||
var err error
|
||||
switch setting.Protocol {
|
||||
case setting.HTTP:
|
||||
case setting.SCHEME_HTTP:
|
||||
err = http.ListenAndServe(listenAddr, m)
|
||||
case setting.HTTPS:
|
||||
case setting.SCHEME_HTTPS:
|
||||
server := &http.Server{Addr: listenAddr, TLSConfig: &tls.Config{MinVersion: tls.VersionTLS10}, Handler: m}
|
||||
err = server.ListenAndServeTLS(setting.CertFile, setting.KeyFile)
|
||||
case setting.FCGI:
|
||||
case setting.SCHEME_FCGI:
|
||||
err = fcgi.Serve(nil, m)
|
||||
case setting.SCHEME_UNIX_SOCKET:
|
||||
os.Remove(listenAddr)
|
||||
|
||||
var listener *net.UnixListener
|
||||
listener, err = net.ListenUnix("unix", &net.UnixAddr{listenAddr, "unix"})
|
||||
if err != nil {
|
||||
break // Handle error after switch
|
||||
}
|
||||
|
||||
// FIXME: add proper implementation of signal capture on all protocols
|
||||
// execute this on SIGTERM or SIGINT: listener.Close()
|
||||
if err = os.Chmod(listenAddr, os.FileMode(setting.UnixSocketPermission)); err != nil {
|
||||
log.Fatal(4, "Failed to set permission of unix socket: %v", err)
|
||||
}
|
||||
err = http.Serve(listener, m)
|
||||
default:
|
||||
log.Fatal(4, "Invalid protocol: %s", setting.Protocol)
|
||||
}
|
||||
@@ -572,4 +689,6 @@ func runWeb(ctx *cli.Context) {
|
||||
if err != nil {
|
||||
log.Fatal(4, "Fail to start server: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
Execute following command in ROOT directory when anything is changed:
|
||||
|
||||
$ go-bindata -o=modules/bindata/bindata.go -ignore="\\.DS_Store|README.md" -pkg=bindata conf/...
|
||||
|
||||
Add -debug flag to make life easier in development(somehow isn't working):
|
||||
|
||||
$ go-bindata -debug -o=modules/bindata/bindata.go -ignore="\\.DS_Store|README.md" -pkg=bindata conf/...
|
||||
$ make bindata
|
||||
273
conf/app.ini
273
conf/app.ini
@@ -1,64 +1,102 @@
|
||||
# NEVER EVER MODIFY THIS FILE
|
||||
# PLEASE MAKE CHANGES ON CORRESPONDING CUSTOM CONFIG FILE
|
||||
# !!! NEVER EVER MODIFY THIS FILE !!!
|
||||
# !!! PLEASE MAKE CHANGES ON CORRESPONDING CUSTOM CONFIG FILE !!!
|
||||
# !!! IF YOU ARE PACKAGING PROVIDER, PLEASE MAKE OWN COPY OF IT !!!
|
||||
|
||||
; App name that shows on every page title
|
||||
APP_NAME = Gogs: Go Git Service
|
||||
; Change it if you run locally
|
||||
; The name of the system user that runs Gogs
|
||||
RUN_USER = git
|
||||
; Either "dev", "prod" or "test", default is "dev"
|
||||
; Either "dev", "prod" or "test"
|
||||
RUN_MODE = dev
|
||||
|
||||
[repository]
|
||||
; Root path for storing repositories's data, default is "~/<username>/gogs-repositories"
|
||||
ROOT =
|
||||
; The script type server supports, sometimes could be "sh"
|
||||
SCRIPT_TYPE = bash
|
||||
; Default ANSI charset
|
||||
ANSI_CHARSET =
|
||||
; Default ANSI charset for an unrecognized charset
|
||||
ANSI_CHARSET =
|
||||
; Force every new repository to be private
|
||||
FORCE_PRIVATE = false
|
||||
; Global maximum creation limit of repository per user, -1 means no limit
|
||||
MAX_CREATION_LIMIT = -1
|
||||
; Patch test queue length, make it as large as possible
|
||||
PULL_REQUEST_QUEUE_LENGTH = 10000
|
||||
; Mirror sync queue length, increase if mirror syncing starts hanging
|
||||
MIRROR_QUEUE_LENGTH = 1000
|
||||
; Patch test queue length, increase if pull request patch testing starts hanging
|
||||
PULL_REQUEST_QUEUE_LENGTH = 1000
|
||||
; Preferred Licenses to place at the top of the list
|
||||
; Name must match file name in conf/license or custom/conf/license
|
||||
PREFERRED_LICENSES = Apache License 2.0,MIT License
|
||||
; Disable ability to interact with repositories by HTTP protocol
|
||||
DISABLE_HTTP_GIT = false
|
||||
; Enable ability to migrate repository by local path
|
||||
ENABLE_LOCAL_PATH_MIGRATION = false
|
||||
|
||||
[ui]
|
||||
; Number of repositories that are showed in one explore page
|
||||
EXPLORE_PAGING_NUM = 20
|
||||
; Number of issues that are showed in one page
|
||||
ISSUE_PAGING_NUM = 10
|
||||
; Number of maximum commits showed in one activity feed
|
||||
FEED_MAX_COMMIT_NUM = 5
|
||||
[repository.editor]
|
||||
; List of file extensions that should have line wraps in the CodeMirror editor.
|
||||
; Separate extensions with a comma. To line wrap files without extension, just put a comma
|
||||
LINE_WRAP_EXTENSIONS = .txt,.md,.markdown,.mdown,.mkd,
|
||||
; Valid file modes that have a preview API associated with them, such as api/v1/markdown.
|
||||
; Separate values by commas. Preview tab in edit mode won't show if the file extension doesn't match
|
||||
PREVIEWABLE_FILE_MODES = markdown
|
||||
|
||||
[ui.admin]
|
||||
; Number of users that are showed in one page
|
||||
USER_PAGING_NUM = 50
|
||||
; Number of repos that are showed in one page
|
||||
REPO_PAGING_NUM = 50
|
||||
; Number of notices that are showed in one page
|
||||
NOTICE_PAGING_NUM = 25
|
||||
; Number of organization that are showed in one page
|
||||
ORG_PAGING_NUM = 50
|
||||
[repository.upload]
|
||||
; Enable repository file uploads.
|
||||
ENABLED = true
|
||||
; Path to temporarily store uploads (default path gets cleaned by Gogs in every start)
|
||||
TEMP_PATH = data/tmp/uploads
|
||||
; File types that are allowed to be uploaded, e.g. image/jpeg|image/png. Leave empty means allow any file type
|
||||
ALLOWED_TYPES =
|
||||
; Maximum size of each file in MB
|
||||
FILE_MAX_SIZE = 3
|
||||
; Maximum number of files per upload
|
||||
MAX_FILES = 5
|
||||
|
||||
[markdown]
|
||||
; Enable hard line break extension
|
||||
ENABLE_HARD_LINE_BREAK = false
|
||||
; List of custom URL-Schemes that are allowed as links when rendering Markdown
|
||||
; for example git,magnet
|
||||
CUSTOM_URL_SCHEMES =
|
||||
; List of file extensions that should be rendered/edited as Markdown
|
||||
; Separate extensions with a comma. To render files w/o extension as markdown, just put a comma
|
||||
FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd
|
||||
|
||||
[server]
|
||||
PROTOCOL = http
|
||||
DOMAIN = localhost
|
||||
ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
|
||||
HTTP_ADDR =
|
||||
HTTP_ADDR = 0.0.0.0
|
||||
HTTP_PORT = 3000
|
||||
; Permission for unix socket
|
||||
UNIX_SOCKET_PERMISSION = 666
|
||||
; Local (DMZ) URL for Gogs workers (such as SSH update) accessing web service.
|
||||
; In most cases you do not need to change the default value.
|
||||
; Alter it only if your SSH server node is not the same as HTTP node.
|
||||
LOCAL_ROOT_URL = http://localhost:%(HTTP_PORT)s/
|
||||
LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
|
||||
; Disable SSH feature when not available
|
||||
DISABLE_SSH = false
|
||||
; Whether use builtin SSH server or not.
|
||||
START_SSH_SERVER = false
|
||||
; Domain name to be exposed in SSH clone URL
|
||||
SSH_DOMAIN = %(DOMAIN)s
|
||||
; Port number to be exposed in SSH clone URL
|
||||
SSH_PORT = 22
|
||||
; Root path of SSH directory
|
||||
SSH_ROOT_PATH =
|
||||
; Network interface builtin SSH server listens on
|
||||
SSH_LISTEN_HOST = 0.0.0.0
|
||||
; Port number builtin SSH server listens on
|
||||
SSH_LISTEN_PORT = %(SSH_PORT)s
|
||||
; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'.
|
||||
SSH_ROOT_PATH =
|
||||
; Choose the ciphers to support for SSH connections
|
||||
SSH_SERVER_CIPHERS = aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, arcfour256, arcfour128
|
||||
; Directory to create temporary files when test publick key using ssh-keygen,
|
||||
; default is system temporary directory.
|
||||
SSH_KEY_TEST_PATH =
|
||||
; Path to ssh-keygen, default is 'ssh-keygen' and let shell find out which one to call.
|
||||
SSH_KEYGEN_PATH = ssh-keygen
|
||||
; Indicate whether to check minimum key size with corresponding type
|
||||
MINIMUM_KEY_SIZE_CHECK = false
|
||||
; Disable CDN even in "prod" mode
|
||||
OFFLINE_MODE = false
|
||||
DISABLE_ROUTER_LOG = false
|
||||
@@ -74,13 +112,26 @@ KEY_FILE = custom/https/key.pem
|
||||
; Upper level of template and static file path
|
||||
; default is the path where Gogs is executed
|
||||
STATIC_ROOT_PATH =
|
||||
; Default path for App data
|
||||
APP_DATA_PATH = data
|
||||
; Application level GZIP support
|
||||
ENABLE_GZIP = false
|
||||
; Landing page for non-logged users, can be "home" or "explore"
|
||||
LANDING_PAGE = home
|
||||
|
||||
[http]
|
||||
; Value for Access-Control-Allow-Origin header, default is not to present
|
||||
ACCESS_CONTROL_ALLOW_ORIGIN =
|
||||
|
||||
; Define allowed algorithms and their minimum key length (use -1 to disable a type)
|
||||
[ssh.minimum_key_sizes]
|
||||
ED25519 = 256
|
||||
ECDSA = 256
|
||||
RSA = 2048
|
||||
DSA = 1024
|
||||
|
||||
[database]
|
||||
; Either "mysql", "postgres" or "sqlite3", it's your choice
|
||||
; Either "mysql", "postgres" or "sqlite3", you can connect to TiDB with MySQL protocol
|
||||
DB_TYPE = mysql
|
||||
HOST = 127.0.0.1:3306
|
||||
NAME = gogs
|
||||
@@ -88,10 +139,12 @@ USER = root
|
||||
PASSWD =
|
||||
; For "postgres" only, either "disable", "require" or "verify-full"
|
||||
SSL_MODE = disable
|
||||
; For "sqlite3" and "tidb"
|
||||
; For "sqlite3" and "tidb", use absolute path when you start as service
|
||||
PATH = data/gogs.db
|
||||
|
||||
[admin]
|
||||
; Disable regular (non-admin) users to create organizations
|
||||
DISABLE_REGULAR_ORG_CREATION = false
|
||||
|
||||
[security]
|
||||
INSTALL_LOCK = false
|
||||
@@ -113,8 +166,6 @@ REGISTER_EMAIL_CONFIRM = false
|
||||
DISABLE_REGISTRATION = false
|
||||
; User must sign in to view anything.
|
||||
REQUIRE_SIGNIN_VIEW = false
|
||||
; Cache avatar as picture
|
||||
ENABLE_CACHE_AVATAR = false
|
||||
; Mail notification
|
||||
ENABLE_NOTIFY_MAIL = false
|
||||
; More detail: https://github.com/gogits/gogs/issues/165
|
||||
@@ -124,7 +175,7 @@ ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
|
||||
ENABLE_CAPTCHA = true
|
||||
|
||||
[webhook]
|
||||
; Hook task queue length
|
||||
; Hook task queue length, increase if webhook shooting starts hanging
|
||||
QUEUE_LENGTH = 1000
|
||||
; Deliver timeout in seconds
|
||||
DELIVER_TIMEOUT = 5
|
||||
@@ -141,24 +192,26 @@ SEND_BUFFER_LEN = 100
|
||||
SUBJECT = %(APP_NAME)s
|
||||
; Mail server
|
||||
; Gmail: smtp.gmail.com:587
|
||||
; QQ: smtp.qq.com:25
|
||||
; QQ: smtp.qq.com:465
|
||||
; Note, if the port ends with "465", SMTPS will be used. Using STARTTLS on port 587 is recommended per RFC 6409. If the server supports STARTTLS it will always be used.
|
||||
HOST =
|
||||
HOST =
|
||||
; Disable HELO operation when hostname are different.
|
||||
DISABLE_HELO =
|
||||
DISABLE_HELO =
|
||||
; Custom hostname for HELO operation, default is from system.
|
||||
HELO_HOSTNAME =
|
||||
HELO_HOSTNAME =
|
||||
; Do not verify the certificate of the server. Only use this for self-signed certificates
|
||||
SKIP_VERIFY =
|
||||
SKIP_VERIFY =
|
||||
; Use client certificate
|
||||
USE_CERTIFICATE = false
|
||||
CERT_FILE = custom/mailer/cert.pem
|
||||
KEY_FILE = custom/mailer/key.pem
|
||||
; Mail from address, RFC 5322. This can be just an email address, or the `"Name" <email@example.com>` format
|
||||
; Mail from address, RFC 5322. This can be just an email address, or the `"Name" <email@example.com>` format
|
||||
FROM =
|
||||
; Mailer user name and password
|
||||
USER =
|
||||
PASSWD =
|
||||
; Use text/html as alternative format of content
|
||||
ENABLE_HTML_ALTERNATIVE = false
|
||||
|
||||
[cache]
|
||||
; Either "memory", "redis", or "memcache", default is "memory"
|
||||
@@ -171,7 +224,7 @@ INTERVAL = 60
|
||||
HOST =
|
||||
|
||||
[session]
|
||||
; Either "memory", "file", "redis" or "mysql", default is "memory"
|
||||
; Either "memory", "file", or "redis", default is "memory"
|
||||
PROVIDER = memory
|
||||
; Provider config options
|
||||
; memory: not have any config yet
|
||||
@@ -191,13 +244,17 @@ GC_INTERVAL_TIME = 86400
|
||||
SESSION_LIFE_TIME = 86400
|
||||
|
||||
[picture]
|
||||
; The place to picture data, either "server" or "qiniu", default is "server"
|
||||
SERVICE = server
|
||||
; Path to store user uploaded avatars
|
||||
AVATAR_UPLOAD_PATH = data/avatars
|
||||
; Chinese users can choose "duoshuo"
|
||||
; or a custom avatar source, like: http://cn.gravatar.com/avatar/
|
||||
GRAVATAR_SOURCE = gravatar
|
||||
; This value will be forced to be true in offline mode.
|
||||
DISABLE_GRAVATAR = false
|
||||
; Federated avatar lookup uses DNS to discover avatar associated
|
||||
; with emails, see https://www.libravatar.org
|
||||
; This value will be forced to be false in offline mode or Gravatar is disbaled.
|
||||
ENABLE_FEDERATED_AVATAR = true
|
||||
|
||||
[attachment]
|
||||
; Whether attachments are enabled. Defaults to `true`
|
||||
@@ -217,66 +274,43 @@ MAX_FILES = 5
|
||||
; For more information about the format see http://golang.org/pkg/time/#pkg-constants
|
||||
FORMAT =
|
||||
|
||||
; General settings of loggers
|
||||
[log]
|
||||
ROOT_PATH =
|
||||
; Either "console", "file", "conn", "smtp" or "database", default is "console"
|
||||
; Can be "console" and "file", default is "console"
|
||||
; Use comma to separate multiple modes, e.g. "console, file"
|
||||
MODE = console
|
||||
; Buffer length of channel, keep it as it is if you don't know what it is.
|
||||
BUFFER_LEN = 10000
|
||||
; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace"
|
||||
BUFFER_LEN = 100
|
||||
; Either "Trace", "Info", "Warn", "Error", "Fatal", default is "Trace"
|
||||
LEVEL = Trace
|
||||
|
||||
; For "console" mode only
|
||||
[log.console]
|
||||
; leave empty to inherit
|
||||
LEVEL =
|
||||
|
||||
; For "file" mode only
|
||||
[log.file]
|
||||
; leave empty to inherit
|
||||
LEVEL =
|
||||
; This enables automated log rotate(switch of following options), default is true
|
||||
; This enables automated log rotate (switch of following options)
|
||||
LOG_ROTATE = true
|
||||
; Max line number of single file, default is 1000000
|
||||
MAX_LINES = 1000000
|
||||
; Segment log daily
|
||||
DAILY_ROTATE = true
|
||||
; Max size shift of single file, default is 28 means 1 << 28, 256MB
|
||||
MAX_SIZE_SHIFT = 28
|
||||
; Segment log daily, default is true
|
||||
DAILY_ROTATE = true
|
||||
; Expired days of log file(delete after max days), default is 7
|
||||
; Max line number of single file
|
||||
MAX_LINES = 1000000
|
||||
; Expired days of log file (delete after max days)
|
||||
MAX_DAYS = 7
|
||||
|
||||
; For "conn" mode only
|
||||
[log.conn]
|
||||
; For "slack" mode only
|
||||
[log.slack]
|
||||
; leave empty to inherit
|
||||
LEVEL =
|
||||
; Reconnect host for every single message, default is false
|
||||
RECONNECT_ON_MSG = false
|
||||
; Try to reconnect when connection is lost, default is false
|
||||
RECONNECT = false
|
||||
; Either "tcp", "unix" or "udp", default is "tcp"
|
||||
PROTOCOL = tcp
|
||||
; Host address
|
||||
ADDR =
|
||||
|
||||
; For "smtp" mode only
|
||||
[log.smtp]
|
||||
LEVEL =
|
||||
; Name displayed in mail title, default is "Diagnostic message from server"
|
||||
SUBJECT = Diagnostic message from server
|
||||
; Mail server
|
||||
HOST =
|
||||
; Mailer user name and password
|
||||
USER =
|
||||
PASSWD =
|
||||
; Receivers, can be one or more, e.g. ["1@example.com","2@example.com"]
|
||||
RECEIVERS =
|
||||
|
||||
; For "database" mode only
|
||||
[log.database]
|
||||
LEVEL =
|
||||
; Either "mysql" or "postgres"
|
||||
DRIVER =
|
||||
; Based on xorm, e.g.: root:root@localhost/gogs?charset=utf8
|
||||
CONN =
|
||||
; Webhook URL
|
||||
URL =
|
||||
|
||||
[cron]
|
||||
; Enable running cron tasks periodically.
|
||||
@@ -286,7 +320,7 @@ RUN_AT_START = false
|
||||
|
||||
; Update mirrors
|
||||
[cron.update_mirrors]
|
||||
SCHEDULE = @every 1h
|
||||
SCHEDULE = @every 10m
|
||||
|
||||
; Repository health check
|
||||
[cron.repo_health_check]
|
||||
@@ -294,7 +328,7 @@ SCHEDULE = @every 24h
|
||||
TIMEOUT = 60s
|
||||
; Arguments for command 'git fsck', e.g. "--unreachable --tags"
|
||||
; see more on http://git-scm.com/docs/git-fsck/1.7.5
|
||||
ARGS =
|
||||
ARGS =
|
||||
|
||||
; Check repository statistics
|
||||
[cron.check_repo_stats]
|
||||
@@ -302,20 +336,72 @@ RUN_AT_START = true
|
||||
SCHEDULE = @every 24h
|
||||
|
||||
[git]
|
||||
MAX_GIT_DIFF_LINES = 10000
|
||||
; Disables highlight of added and removed changes
|
||||
DISABLE_DIFF_HIGHLIGHT = false
|
||||
; Max number of lines allowed of a single file in diff view
|
||||
MAX_GIT_DIFF_LINES = 1000
|
||||
; Max number of characters of a line allowed in diff view
|
||||
MAX_GIT_DIFF_LINE_CHARACTERS = 500
|
||||
; Max number of files shown in diff view
|
||||
MAX_GIT_DIFF_FILES = 100
|
||||
; Arguments for command 'git gc', e.g. "--aggressive --auto"
|
||||
; see more on http://git-scm.com/docs/git-gc/1.7.5
|
||||
GC_ARGS =
|
||||
GC_ARGS =
|
||||
|
||||
; Operation timeout in seconds
|
||||
[git.timeout]
|
||||
MIGRATE = 600
|
||||
MIRROR = 300
|
||||
CLONE = 300
|
||||
PULL = 300
|
||||
GC = 60
|
||||
|
||||
[mirror]
|
||||
; Default interval in hours between each check
|
||||
DEFAULT_INTERVAL = 8
|
||||
|
||||
[api]
|
||||
; Max number of items will response in a page
|
||||
MAX_RESPONSE_ITEMS = 50
|
||||
|
||||
[ui]
|
||||
; Number of repositories that are showed in one explore page
|
||||
EXPLORE_PAGING_NUM = 20
|
||||
; Number of issues that are showed in one page
|
||||
ISSUE_PAGING_NUM = 10
|
||||
; Number of maximum commits showed in one activity feed
|
||||
FEED_MAX_COMMIT_NUM = 5
|
||||
; Value of "theme-color" meta tag, used by Android >= 5.0
|
||||
; An invalid color like "none" or "disable" will have the default style
|
||||
; More info: https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android
|
||||
THEME_COLOR_META_TAG = `#ff5343`
|
||||
; Max size in bytes of files to be displayed (default is 8MB)
|
||||
MAX_DISPLAY_FILE_SIZE = 8388608
|
||||
|
||||
[ui.admin]
|
||||
; Number of users that are showed in one page
|
||||
USER_PAGING_NUM = 50
|
||||
; Number of repos that are showed in one page
|
||||
REPO_PAGING_NUM = 50
|
||||
; Number of notices that are showed in one page
|
||||
NOTICE_PAGING_NUM = 25
|
||||
; Number of organization that are showed in one page
|
||||
ORG_PAGING_NUM = 50
|
||||
|
||||
[ui.user]
|
||||
; Number of repos that are showed in one page
|
||||
REPO_PAGING_NUM = 15
|
||||
|
||||
[i18n]
|
||||
LANGS = en-US,zh-CN,zh-HK,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT
|
||||
NAMES = English,简体中文,繁體中文,Deutsch,Français,Nederlands,Latviešu,Русский,日本語,Español,Português do Brasil,Polski,български,Italiano
|
||||
LANGS = en-US,zh-CN,zh-HK,zh-TW,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI,tr-TR,cs-CZ,sr-SP,sv-SE,ko-KR,gl-ES
|
||||
NAMES = English,简体中文,繁體中文(香港),繁體中文(台湾),Deutsch,Français,Nederlands,Latviešu,Русский,日本語,Español,Português do Brasil,Polski,български,Italiano,Suomalainen,Türkçe,čeština,Српски,Svenska,한국어,Galego
|
||||
|
||||
; Used for datetimepicker
|
||||
[i18n.datelang]
|
||||
en-US = en
|
||||
zh-CN = zh
|
||||
zh-HK = zh-TW
|
||||
zh-TW = zh-TW
|
||||
de-DE = de
|
||||
fr-FR = fr
|
||||
nl-NL = nl
|
||||
@@ -327,6 +413,13 @@ pt-BR = pt-BR
|
||||
pl-PL = pl
|
||||
bg-BG = bg
|
||||
it-IT = it
|
||||
fi-FI = fi
|
||||
tr-TR = tr
|
||||
cs-CZ = cs-CZ
|
||||
sr-SP = sr
|
||||
sv-SE = sv
|
||||
ko-KR = ko
|
||||
gl-ES = gl
|
||||
|
||||
; Extension mapping to highlight class
|
||||
; e.g. .toml=ini
|
||||
@@ -334,5 +427,7 @@ it-IT = it
|
||||
|
||||
[other]
|
||||
SHOW_FOOTER_BRANDING = false
|
||||
; Show version information about gogs and go in the footer
|
||||
; Show version information about Gogs and Go in the footer
|
||||
SHOW_FOOTER_VERSION = true
|
||||
; Show time of template execution in the footer
|
||||
SHOW_FOOTER_TEMPLATE_LOAD_TIME = true
|
||||
|
||||
7
conf/label/Default
Normal file
7
conf/label/Default
Normal file
@@ -0,0 +1,7 @@
|
||||
#ee0701 bug
|
||||
#cccccc duplicate
|
||||
#84b6eb enhancement
|
||||
#128a0c help wanted
|
||||
#e6e6e6 invalid
|
||||
#cc317c question
|
||||
#ffffff wontfix
|
||||
@@ -1,6 +1,5 @@
|
||||
ISC License:
|
||||
Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC")
|
||||
Copyright (c) 1995-2003 by Internet Software Consortium
|
||||
Copyright (c) Year(s), Company or Person's Name
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -12,32 +12,52 @@ Andrey Nering <andrey AT nering DOT com DOT br>
|
||||
Andrey Solomatin <toadron AT yandex DOT ru>
|
||||
Antoine GIRARD <sapk AT sapk DOT fr>
|
||||
Arthur Aslanyan <arthur DOT e DOT aslanyan AT gmail DOT com>
|
||||
Aurelien Darragon <aurelien DOT darragon AT gmail DOT com>
|
||||
Barış Arda Yılmaz <ardayilmazgamer AT gmail DOT com>
|
||||
Camille Baronnet <gogs AT camillebaronnet DOT fr>
|
||||
Christoph Kisfeld <christoph DOT kisfeld AT gmail DOT com>
|
||||
Cysioland
|
||||
Damaris Padieu <damizx AT hotmail DOT fr>
|
||||
Daniel Speichert <daniel AT speichert DOT pl>
|
||||
David Yzaguirre <dvdyzag AT gmail DOT com>
|
||||
Dmitriy Nogay <me AT catwhocode DOT ga>
|
||||
Enrico Testori hypertesto AT gmail DOT com
|
||||
Ezequiel Gonzalez Rial <gonrial AT gmail DOT com>
|
||||
Gabriel Dugny <gabriel DOT dugny AT gmail DOT com>
|
||||
Gregor Santner <gdev AT live DOT de>
|
||||
Halil Kaya <halil AT halilkaya DOT net>
|
||||
Hamid Feizabadi <hamidfzm AT gmail DOT com>
|
||||
Huimin Wang <wanghm2009 AT hotmail DOT co DOT jp>
|
||||
ilko <kontact-mr.k AT outlook DOT com">
|
||||
Ilya Makarov
|
||||
Jamie Mansfield <dev AT jamierocks DOT uk>
|
||||
Jean THOMAS <contact AT tibounise DOT com>
|
||||
Joubert RedRat <me+github AT redrat DOT com DOT br>
|
||||
Juraj Bubniak <contact AT jbub DOT eu>
|
||||
Lafriks <lafriks AT gmail DOT com>
|
||||
Lauri Ojansivu <x AT xet7 DOT org>
|
||||
Luc Stepniewski <luc AT stepniewski DOT fr>
|
||||
Luca Bozzo <luca AT bozzo DOT it>
|
||||
Luca Kröger <l DOT kroeger01 AT gmail DOT com>
|
||||
Marc Schiller <marc AT schiller DOT im>
|
||||
Marvin Menzerath <github AT marvin-menzerath DOT de>
|
||||
Michael Härtl <haertl DOT mike AT gmail DOT com>
|
||||
Miguel de la Cruz <miguel AT mcrx DOT me>
|
||||
Mikhail Burdin <xdshot9000 AT gmail DOT com>
|
||||
Morten Sørensen <klim8d AT gmail DOT com>
|
||||
Muhammad Fawwaz Orabi <mfawwaz93 AT gmail DOT com>
|
||||
Nakao Takamasa <at.mattenn AT gmail DOT com>
|
||||
Natan Albuquerque <natanalbuquerque5 AT gmail DOT com>
|
||||
Odilon Junior <odilon DOT junior93 AT gmail DOT com>
|
||||
Richard Bukovansky <richard DOT bukovansky @ gmail DOT com>
|
||||
Robert Nuske <robert DOT nuske AT web DOT de>
|
||||
Robin Hübner <profan AT prfn DOT se>
|
||||
SeongJae Park <sj38 DOT park AT gmail DOT com>
|
||||
Thomas Fanninger <gogs DOT thomas AT fanninger DOT at>
|
||||
Tilmann Bach <tilmann AT outlook DOT com>
|
||||
Toni Villena Jiménez <tonivj5 AT gmail DOT com>
|
||||
Vladimir Jigulin mogaika AT yandex DOT ru
|
||||
Vladimir Vissoultchev <wqweto AT gmail DOT com>
|
||||
YJSoft <yjsoft AT yjsoft DOT pe DOT kr>
|
||||
Łukasz Jan Niemier <lukasz AT niemier DOT pl>
|
||||
Pablo Saavedra <psaavedra AT igalia DOT com>
|
||||
|
||||
215
conf/locale/locale_bg-BG.ini
Executable file → Normal file
215
conf/locale/locale_bg-BG.ini
Executable file → Normal file
@@ -38,19 +38,12 @@ settings=Настройки
|
||||
your_profile=Вашият профил
|
||||
your_settings=Вашите настройки
|
||||
|
||||
news_feed=Поток новини
|
||||
activities=Активности
|
||||
pull_requests=Заявки за сливане
|
||||
issues=Задачи
|
||||
|
||||
cancel=Отказ
|
||||
|
||||
[search]
|
||||
search=Търсене...
|
||||
repository=Хранилище
|
||||
user=Потребител
|
||||
issue=Задача
|
||||
code=Код
|
||||
|
||||
[install]
|
||||
install=Инсталация
|
||||
title=Стъпки за инсталиране при първоначално стартиране
|
||||
@@ -65,7 +58,7 @@ db_name=Име на база данни
|
||||
db_helper=Моля, използвайте INNODB engine с utf8_general_ci кодиране на знаци за MySQL.
|
||||
ssl_mode=Режим SSL
|
||||
path=Път
|
||||
sqlite_helper=Файл на SQLite3 или TiDB база данни.
|
||||
sqlite_helper=Файл на SQLite3 или TiDB база данни.<br>Моля използвайте абсолютен път до файл когато стартирате Gogs като услуга.
|
||||
err_empty_db_path=Пътят до SQLite3 или TiDB база данни не може да е празен.
|
||||
err_invalid_tidb_name=TiDB не позволява "." и "-" в името на базата данни.
|
||||
no_admin_and_disable_registration=Невъзможно изключване на регистрациите без предварително да е създаден поне един административен профил.
|
||||
@@ -86,6 +79,8 @@ http_port=HTTP порт
|
||||
http_port_helper=Порт, на който приложението ще слуша.
|
||||
app_url=URL адрес на приложението
|
||||
app_url_helper=Този настройка променя HTTP/HTTPS адреса за клониране, а понякога и адреса на ел. поща.
|
||||
log_root_path=Път към журналите
|
||||
log_root_path_helper=Директория в която се записват журналите.
|
||||
|
||||
optional_title=Опционални настройки
|
||||
email_title=Настройки на пощенска услуга
|
||||
@@ -101,6 +96,8 @@ offline_mode=Включи офлайн режима
|
||||
offline_mode_popup=Изключи CDN дори в продукционен режим, всички ресурсни файлове ще бъдат доставяни локално.
|
||||
disable_gravatar=Изключи връзка с Gravatar
|
||||
disable_gravatar_popup=Изключва Gravatar и външни източници, така че всички аватари трябва да са или качени от потребителите или да се ползват аватари по подразбиране.
|
||||
federated_avatar_lookup=Използване на външни аватари
|
||||
federated_avatar_lookup_popup=Позволява използване на външни аватари от услуги съвместими с libravatar.
|
||||
disable_registration=Изключи саморегистрацията
|
||||
disable_registration_popup=Изключи потребителската саморегистрация, само администратор може да създава профили.
|
||||
enable_captcha=Включи Captcha
|
||||
@@ -119,15 +116,18 @@ sqlite3_not_available=Вашата версия не поддържа SQLite3,
|
||||
invalid_db_setting=Настройките на базата данни са некоректни: %v
|
||||
invalid_repo_path=Основният път към хранилищата е невалиден: %v
|
||||
run_user_not_match=Потребителският контекст на приложението не е на текущия потребител: %s -> %s
|
||||
invalid_smtp_from=SMTP From field is not valid: %v
|
||||
save_config_failed=Неуспешно запазване на конфигурация: %v
|
||||
invalid_admin_setting=Настройките на профил на администратора са невалидни: %v
|
||||
install_success=Добре дошли! Радваме се, че избрахте Gogs, и Ви пожелаваме приятна работа и сърдечни поздрави!
|
||||
invalid_log_root_path=Основният път към журналите е невалиден: %v
|
||||
|
||||
[home]
|
||||
uname_holder=Име или ел. поща
|
||||
password_holder=Парола
|
||||
switch_dashboard_context=Превключи контекст на таблото
|
||||
my_repos=Моите хранилища
|
||||
show_more_repos=Покажи още хранилища...
|
||||
collaborative_repos=Съвместни хранилища
|
||||
my_orgs=Моите организации
|
||||
my_mirrors=Моите огледала
|
||||
@@ -137,6 +137,9 @@ issues.in_your_repos=Във Вашите хранилища
|
||||
|
||||
[explore]
|
||||
repos=Хранилища
|
||||
users=Потребители
|
||||
organizations=Организации
|
||||
search=Търсене
|
||||
|
||||
[auth]
|
||||
create_new_account=Създай нов профил
|
||||
@@ -150,15 +153,17 @@ forget_password=Забравена парола?
|
||||
sign_up_now=Нуждаете се от профил? Регистрирайте се сега.
|
||||
confirmation_mail_sent_prompt=Ново писмо за потвърждение е изпратено до <b>%s</b>. Моля проверете пощенската си кутия в рамките на следващите %d часа, за да завършите процеса на регистрация.
|
||||
active_your_account=Активиране на профил
|
||||
prohibit_login=Влизане забранено
|
||||
prohibit_login_desc=Вашият профил е със забрана за влизане, моля свържете се с администратора.
|
||||
resent_limit_prompt=За съжаление Вие съвсем наскоро изпратихте писмо за активация. Моля изчакайте 3 минути, след което опитайте отново.
|
||||
has_unconfirmed_mail=Здравейте %s, имате непотвърден адрес на ел. поща (<b>%s</b>). Ако не сте получили писмо за потвърждение или имате нужда да се изпрати ново писмо, моля щракнете бутона по-долу.
|
||||
resend_mail=Щракнете тук, за да се изпрати ново писмо за потвърждение
|
||||
email_not_associate=Този адрес на ел. поща не е свързан с никой профил.
|
||||
send_reset_mail=Щракнете тук, за да получите (отново) писмо за нулиране на паролата
|
||||
reset_password=Нулиране на паролата
|
||||
invalid_code=За съжаление Вашия код за потвърждение е изтекъл или е невалиден.
|
||||
reset_password_helper=Щракнете тук, за да нулирате паролата си
|
||||
password_too_short=Размерът на паролата не може да бъде по-малък от 6 знака.
|
||||
non_local_account=Нелокални потребители не могат да сменят паролата си през Gogs.
|
||||
|
||||
[mail]
|
||||
activate_account=Моля активирайте Вашия профил
|
||||
@@ -185,6 +190,13 @@ TeamName=Име на екипа
|
||||
AuthName=Име на удостоверението
|
||||
AdminEmail=Ел. поща на администратора
|
||||
|
||||
NewBranchName=Име на нов клон
|
||||
CommitSummary=Резюме на ревизия
|
||||
CommitMessage=Текст на ревизия
|
||||
CommitChoice=Избор на ревизия
|
||||
TreeName=Път до файл
|
||||
Content=Съдържание
|
||||
|
||||
require_error=` не може да бъде празен.`
|
||||
alpha_dash_error=` трябва да e валидна буква, число или тире(-_).`
|
||||
alpha_dash_dot_error=` трябва да e валидна буква, число, тире(-_) или точка.`
|
||||
@@ -203,7 +215,6 @@ repo_name_been_taken=Името на хранилището вече се пол
|
||||
org_name_been_taken=Името на организацията вече се ползва.
|
||||
team_name_been_taken=Името на екипа вече се ползва.
|
||||
email_been_used=Този адрес на ел. поща вече се ползва.
|
||||
illegal_team_name=Името на екипа съдържа недопустими знаци.
|
||||
username_password_incorrect=Потребителското име или паролата не са верни.
|
||||
enterred_invalid_repo_name=Моля уверете се, че въведеното име на хранилище е вярно.
|
||||
enterred_invalid_owner_name=Моля уверете се, че въведеното име на притежател е вярно.
|
||||
@@ -219,13 +230,10 @@ still_own_repo=Вашият профил притежава поне едно х
|
||||
still_has_org=Вашият профил все още участва в поне една организация. Първо трябва да напуснете или изтриете Вашите участия в организациите.
|
||||
org_still_own_repo=Тази организация все още притежава хранилище. Първо трябва да го изтриете или да го прехвърлите на друга организация.
|
||||
|
||||
still_own_user=Това удостоверяване се използва от поне един потребител. Моля премахнете потребителите към него и опитайте отново.
|
||||
|
||||
target_branch_not_exist=Целевият клон не съществува.
|
||||
|
||||
[user]
|
||||
change_avatar=Сменете Вашия аватар на gravatar.com
|
||||
change_custom_avatar=Сменете Вашия аватар в настройките
|
||||
change_avatar=Проми своя аватар
|
||||
join_on=Регистриран
|
||||
repositories=Хранилища
|
||||
activity=Публична дейност
|
||||
@@ -241,6 +249,7 @@ form.name_pattern_not_allowed=Потребителското име '%s' не е
|
||||
[settings]
|
||||
profile=Профил
|
||||
password=Парола
|
||||
avatar=Аватар
|
||||
ssh_keys=SSH ключове
|
||||
social=Социални профили
|
||||
applications=Приложения
|
||||
@@ -261,12 +270,13 @@ change_username_prompt=Този промяна ще засегне всички
|
||||
continue=Продължи
|
||||
cancel=Отказ
|
||||
|
||||
lookup_avatar_by_mail=Търсене на аватари по адрес на ел. поща
|
||||
federated_avatar_lookup=Външно търсене на аватари
|
||||
enable_custom_avatar=Разреши потребителски аватар
|
||||
enable_custom_avatar_helper=Без зареждане от Gravatar
|
||||
choose_new_avatar=Избор на нов аватар
|
||||
update_avatar=Запази настройките на аватара
|
||||
delete_current_avatar=Изтрий аватар
|
||||
uploaded_avatar_not_a_image=Каченият файл не е изображение.
|
||||
no_custom_avatar_available=Невъзможно използване на външен аватар, защото не е активирано.
|
||||
update_avatar_success=Настройките на аватара са запазени успешно.
|
||||
|
||||
change_password=Промяна на собствената парола
|
||||
@@ -328,6 +338,10 @@ access_token_deletion=Изтрий индивидуален API ключ за д
|
||||
access_token_deletion_desc=При изтриване на този индивидуален API ключ за достъп ще се премахнат всички свързани права на приложението. Желаете ли да продължите?
|
||||
delete_token_success=Индивидуалният API ключ за достъп е изтрит успешно! Не забравяйте да преконфигурирате приложението също.
|
||||
|
||||
orgs.none=You are not a member of any organizations.
|
||||
orgs.leave_title=Leave an organization
|
||||
orgs.leave_desc=You will lose access to all repositories and teams after you left the organization. Do you want to continue?
|
||||
|
||||
delete_account=Изтриване на собствения профил
|
||||
delete_prompt=Тази операция ще изтрие Вашия профил завинаги и тя <strong>НЕ МОЖЕ</strong> да бъде отменена в последствие!
|
||||
confirm_delete_account=Потвърди изтриването
|
||||
@@ -348,7 +362,7 @@ fork_from=Разклонение от
|
||||
fork_visiblity_helper=Не може да променяте видимостта на разклонено хранилище.
|
||||
repo_desc=Описание
|
||||
repo_lang=Програмен език
|
||||
repo_lang_helper=Изберете .gitignore файлове
|
||||
repo_gitignore_helper=Избор на .gitignore шаблони
|
||||
license=Лиценз
|
||||
license_helper=Изберете лицензионен файл
|
||||
readme=Readme
|
||||
@@ -356,9 +370,12 @@ readme_helper=Изберете шаблон на readme
|
||||
auto_init=Инициализиране на това хранилище с избраните файлове и шаблон
|
||||
create_repo=Създай хранилище
|
||||
default_branch=Клон по подразбиране
|
||||
mirror_prune=Окастряне
|
||||
mirror_prune_desc=Премахва всички препратки за отдалечено проследяване, които не съществуват отдалечено
|
||||
mirror_interval=Интервал на отразяване (часове)
|
||||
mirror_address=Адрес на огледало
|
||||
mirror_address_desc=Моля включете потребител и парола в адреса ако са нужни.
|
||||
mirror_last_synced=Последна синхр.
|
||||
watchers=Наблюдаващи
|
||||
stargazers=Харесващи
|
||||
forks=Разклонения
|
||||
@@ -372,7 +389,8 @@ migrate_type=Тип мигриране
|
||||
migrate_type_helper=Това хранилище ще бъде <span class="text blue">огледало</span>
|
||||
migrate_repo=Мигрирай хранилище
|
||||
migrate.clone_address=Адрес за клониране
|
||||
migrate.clone_address_desc=Това може да е HTTP/HTTPS/GIT адрес или локален път на сървъра.
|
||||
migrate.clone_address_desc=This can be a HTTP/HTTPS/GIT URL.
|
||||
migrate.clone_address_desc_import_local=You're also allowed to migrate a repository by local server path.
|
||||
migrate.permission_denied=Недостатъчни права за импорт на локални хранилища.
|
||||
migrate.invalid_local_path=Невалиден път - не съществува или не е директория.
|
||||
migrate.failed=Грешка при миграция: %v
|
||||
@@ -397,7 +415,7 @@ create_new_repo_command=Създай ново хранилище чрез ком
|
||||
push_exist_repo=Предай съществуващо хранилище през командния ред
|
||||
repo_is_empty=Това хранилище е празно. Моля проверете по-късно пак!
|
||||
|
||||
code=Код
|
||||
files=Files
|
||||
branch=Клон
|
||||
tree=ИН на ревизия
|
||||
filter_branch_and_tag=Филтър по маркер или клон
|
||||
@@ -413,6 +431,47 @@ file_raw=Директен файл
|
||||
file_history=История
|
||||
file_view_raw=Виж директен файл
|
||||
file_permalink=Постоянна връзка
|
||||
file_too_large=Този файл е твърде голям за да се визуализира
|
||||
video_not_supported_in_browser=Вашият браузър не поддържа HTML5 видео тагове.
|
||||
|
||||
editor.new_file=Нов файл
|
||||
editor.upload_file=Качи файл
|
||||
editor.edit_file=Редактирай файл
|
||||
editor.preview_changes=Преглед на промени
|
||||
editor.cannot_edit_non_text_files=Невъзможна редакция на нетекстови файлове
|
||||
editor.edit_this_file=Редактирай този файл
|
||||
editor.must_be_on_a_branch=Трябва да сте избрали клон за да предложите промени в този файл
|
||||
editor.fork_before_edit=Първо трябва да разклоните хранилището преди да редактирате файл
|
||||
editor.delete_this_file=Изтрий този файл
|
||||
editor.must_have_write_access=Трябва да имате права за писане за да предложите промени в този файл
|
||||
editor.file_delete_success=Файл '%s' е изтрит успешно!
|
||||
editor.name_your_file=Име на файл...
|
||||
editor.filename_help=За да добавите директория, въведете името ѝ и натиснете /. За да я премахнете, позиционирайте се в началото на полето и натиснете BackSpace.
|
||||
editor.or=или
|
||||
editor.cancel_lower=отказ
|
||||
editor.commit_changes=Промени в ревизия
|
||||
editor.add_tmpl=Добави '%s/<filename>'
|
||||
editor.add=Добави '%s'
|
||||
editor.update=Модифицирай '%s'
|
||||
editor.delete=Изтрий '%s'
|
||||
editor.commit_message_desc=Добавяне на опционално разширено описание...
|
||||
editor.commit_directly_to_this_branch=Запази ревизия директно в клон <strong class="branch-name">%s</strong>.
|
||||
editor.create_new_branch=Създай <strong>нов клон</strong> от тази ревизия и изпрати заявки за сливане.
|
||||
editor.new_branch_name_desc=Име на нов клон...
|
||||
editor.cancel=Отказ
|
||||
editor.filename_cannot_be_empty=Името не може да бъде празно.
|
||||
editor.branch_already_exists=Клон '%s' вече съществува в това хранилище.
|
||||
editor.directory_is_a_file=Частта '%s' в пътя е файл, не директория в това хранилище.
|
||||
editor.file_is_a_symlink=Файл "%s" е символна връзка, която не може да се модифицира от редактора.
|
||||
editor.filename_is_a_directory=Име '%s' вече съществува като директория в това хранилище.
|
||||
editor.file_editing_no_longer_exists=Файл '%s' който редактирате вече не съществува в това хранилище.
|
||||
editor.file_changed_while_editing=Съдържанието на файла е било променено докато правихте редакциите. <a target="_blank" href="%s">Щракнете тук</a> за да прегледате какво е променено или <strong>натиснете Запис на ревизия</strong> отново за да презапишете чуждите промени.
|
||||
editor.file_already_exists=Файл с име '%s' вече съществува в това хранилище.
|
||||
editor.no_changes_to_show=Няма промени.
|
||||
editor.fail_to_update_file=Невъзможно модифициране/създаване на файл '%s' заради грешка: %v
|
||||
editor.add_subdir=Добави поддиректория...
|
||||
editor.unable_to_upload_files=Невъзможно качване на файлове в '%s' заради грешка: %v
|
||||
editor.upload_files_to_dir=Качи файлове в '%s'
|
||||
|
||||
commits.commits=Ревизии
|
||||
commits.search=Търсене в ревизии
|
||||
@@ -439,6 +498,11 @@ issues.create=Създай задача
|
||||
issues.new_label=Нов етикет
|
||||
issues.new_label_placeholder=Име на етикета...
|
||||
issues.create_label=Създай етикет
|
||||
issues.label_templates.title=Зареждане на предварително зададен набор от етикети
|
||||
issues.label_templates.info=Липсват етикети все още. Можете да щракнете върху "Нов етикет" по-горе, за да създадете нов или да изберете предварително зададени набори от по-долу.
|
||||
issues.label_templates.helper=Изберете набор етикети
|
||||
issues.label_templates.use=Използвай този набор етикети
|
||||
issues.label_templates.fail_to_load_file=Неуспешно зареждане на шаблон с етикети '%s': %v
|
||||
issues.open_tab=%d отворени
|
||||
issues.close_tab=%d затворени
|
||||
issues.filter_label=Етикет
|
||||
@@ -466,7 +530,8 @@ issues.next=Следваща
|
||||
issues.open_title=Отворени
|
||||
issues.closed_title=Затворени
|
||||
issues.num_comments=%d коментара
|
||||
issues.commented_at=`коментира <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commented_at=`коментира <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=Желаете ли да изтриете този коментар?
|
||||
issues.no_content=Все още няма съдържание.
|
||||
issues.close_issue=Затвори
|
||||
issues.close_comment_issue=Kоментирай и затвори
|
||||
@@ -477,10 +542,9 @@ issues.closed_at=`затвори <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at=`повторно отвори <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commit_ref_at=`посочи тази задача от ревизия <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.poster=Участник
|
||||
issues.admin=Администратор
|
||||
issues.collaborator=Сътрудник
|
||||
issues.owner=Притежател
|
||||
issues.sign_up_for_free=Регистрирай се безплатно
|
||||
issues.sign_in_require_desc=за да се включите в този разговор. Вече имате профил? <a href="%s">Влезте, за да коментирате</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Впишете се</a> за да се присъедините към разговора.
|
||||
issues.edit=Редакция
|
||||
issues.cancel=Отказ
|
||||
issues.save=Запис
|
||||
@@ -494,6 +558,9 @@ issues.label_modify=Промяна на етикет
|
||||
issues.label_deletion=Изтрий етикет
|
||||
issues.label_deletion_desc=При изтриване на този етикет ще се премахне информацията за него във всички свързани задачи. Желаете ли да продължите?
|
||||
issues.label_deletion_success=Етикетът е изтрит успешно!
|
||||
issues.num_participants=%d участника
|
||||
issues.attachment.open_tab=`Щракнете за да прегледате "%s" в нов раздел`
|
||||
issues.attachment.download=`Щракнете за да изтеглите "%s"`
|
||||
|
||||
pulls.new=Нова заявка за сливане
|
||||
pulls.compare_changes=Сравни промените
|
||||
@@ -520,6 +587,8 @@ pulls.cannot_auto_merge_desc=Не може да се извърши обедин
|
||||
pulls.cannot_auto_merge_helper=Моля, използвайте инструменти на командния ред за да разрешите конфликтите.
|
||||
pulls.merge_pull_request=Обедини заявка за сливане
|
||||
pulls.open_unmerged_pull_exists=`Невъзможно повторно отваряне, защото вече съществува заявка за сливане (#%d) от същото хранилище със същата информация за обединяване, която чака да бъде извършена`
|
||||
pulls.delete_branch=Изтрий клон
|
||||
pulls.delete_branch_has_new_commits=Клонът не може да бъде изтрит, защото има ревизии след последното обединяване.
|
||||
|
||||
milestones.new=Нов етап
|
||||
milestones.open_tab=%d отворени
|
||||
@@ -557,6 +626,8 @@ wiki.save_page=Запис на страница
|
||||
wiki.last_commit_info=%s редактира тази страница %s
|
||||
wiki.edit_page_button=Редакция
|
||||
wiki.new_page_button=Нова страница
|
||||
wiki.delete_page_button=Изтрий страница
|
||||
wiki.delete_page_notice_1=Това ще изтрие страница <code>"%s"</code>. Моля, бъдете сигурни.
|
||||
wiki.page_already_exists=Страница със същото име вече съществува.
|
||||
wiki.pages=Страници
|
||||
wiki.last_updated=Последна модификация на %s
|
||||
@@ -564,36 +635,57 @@ wiki.last_updated=Последна модификация на %s
|
||||
settings=Настройки
|
||||
settings.options=Опции
|
||||
settings.collaboration=Сътрудничество
|
||||
settings.collaboration.admin=За администрация
|
||||
settings.collaboration.write=За писане
|
||||
settings.collaboration.read=За четене
|
||||
settings.collaboration.undefined=Недефинирано
|
||||
settings.hooks=Уеб-куки
|
||||
settings.githooks=Git куки
|
||||
settings.basic_settings=Основни настройки
|
||||
settings.mirror_settings=Настройки на огледало
|
||||
settings.sync_mirror=Синхр. сега
|
||||
settings.mirror_sync_in_progress=Синхронизация на огледалото е в ход, моля обновете страницата след минута.
|
||||
settings.site=Официален сайт
|
||||
settings.update_settings=Запази настройките
|
||||
settings.change_reponame_prompt=Тази промяна ще засегне връзките, които се отнасят до това хранилището.
|
||||
settings.advanced_settings=Разширени настройки
|
||||
settings.wiki_desc=Включва уики за да може потребителите да създават документи
|
||||
settings.wiki_desc=Включи система за уики
|
||||
settings.use_internal_wiki=Използвай вградено уики
|
||||
settings.use_external_wiki=Използвай външно уики
|
||||
settings.external_wiki_url=URL адрес на външно уики
|
||||
settings.external_wiki_url_desc=Посетителите ще бъдат пренасочени към този URL адрес от връзката за раздел уики.
|
||||
settings.issues_desc=Включва вградена система за проследяване на задачи
|
||||
settings.issues_desc=Включи система за проследяване на задачи
|
||||
settings.use_internal_issue_tracker=Изполвай вградена система за проследяване на задачи
|
||||
settings.use_external_issue_tracker=Използвай външна система за проследяване на задачи
|
||||
settings.external_tracker_url=External Issue Tracker URL
|
||||
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
|
||||
settings.tracker_url_format=Формат на URL адрес на външна система за проследяване на задачи
|
||||
settings.tracker_issue_style=Стил на именуване на външна система за проследяване на задачи:
|
||||
settings.tracker_issue_style.numeric=Цифров
|
||||
settings.tracker_issue_style.alphanumeric=Символен
|
||||
settings.tracker_url_format_desc=Можете да използвате текстови маркери <code>{user} {repo} {index}</code> за потребителско име, име на хранилище и индекс на задача съответно.
|
||||
settings.pulls_desc=Включва заявки за сливане за да може да се приемат външни доработки
|
||||
settings.danger_zone=Опасна зона
|
||||
settings.new_owner_has_same_repo=Новият притежател вече има хранилище със същото име. Изберете друго име.
|
||||
settings.convert=Промени към редовно хранилище
|
||||
settings.convert_desc=Можете да промените това огледало към редовно хранилище. Конверсията не може да се отмени.
|
||||
settings.convert_notices_1=- Тази операция ще конвертира огледалото към редовно хранилище и не може да бъде отменена в последствие.
|
||||
settings.convert_confirm=Потвърдете конверсията
|
||||
settings.convert_succeed=Конверсията към редовно хранилище е извършена успешно.
|
||||
settings.transfer=Прехвърли притежание
|
||||
settings.transfer_desc=Прехвърля това хранилище на друг потребител или към организация, в която имате права на администратор.
|
||||
settings.new_owner_has_same_repo=Новият притежател вече има хранилище със същото име. Изберете друго име.
|
||||
settings.delete=Изтрий това хранилище
|
||||
settings.delete_desc=След като изтриете хранилището, няма връщане назад. Моля, бъдете сигурни.
|
||||
settings.transfer_notices_1=- Вие ще загубите достъп, ако новият притежател е индивидуален потребител.
|
||||
settings.transfer_notices_2=- Вие ще запазите достъпа си, ако новият притежател е организация и ако вие сте един от притежателите ѝ.
|
||||
settings.transfer_form_title=Моля въведете следната информация за да потвърдите операцията:
|
||||
settings.wiki_delete=Изтриване на данни на уики
|
||||
settings.wiki_delete_desc=След като изтриете данни за уики, няма връщане назад. Моля, бъдете сигурни.
|
||||
settings.wiki_delete_notices_1=- Това ще изтрие и ще деактивира уики за %s
|
||||
settings.wiki_deletion_success=Данните за уики на това хранилище са изтрити успешно.
|
||||
settings.delete=Изтрий това хранилище
|
||||
settings.delete_desc=След като изтриете хранилището, няма връщане назад. Моля, бъдете сигурни.
|
||||
settings.delete_notices_1=- Тази операция <strong>НЕ МОЖЕ</strong> да бъде отменена в последствие.
|
||||
settings.delete_notices_2=- Тази операция ще изтрие всичко от това хранилище, включително Git данни, задачи, коментари и достъпа на сътрудници.
|
||||
settings.delete_notices_fork_1=- Ако това хранилище е публично, всички негови разклонения ще останат независими след изтриването му.
|
||||
settings.delete_notices_fork_2=- Ако това хранилище е частно, всички негови разклонения ще бъдат премахнати по време на изтриването.
|
||||
settings.delete_notices_fork_3=- Ако желаете да запазите всички разклонения след изтриването му, първо направете хранилището публично.
|
||||
settings.delete_notices_fork_1=- Всички разклонения ще станат независими след изтриването.
|
||||
settings.deletion_success=Хранилището е изтрито успешно!
|
||||
settings.update_settings_success=Настройките на хранилището са запазени успешно.
|
||||
settings.transfer_owner=Нов притежател
|
||||
@@ -602,8 +694,12 @@ settings.transfer_succeed=Притежанието на хранилището
|
||||
settings.confirm_delete=Потвърди изтриването
|
||||
settings.add_collaborator=Добави нов сътрудник
|
||||
settings.add_collaborator_success=Добавен е нов сътрудник.
|
||||
settings.delete_collaborator=Премахни
|
||||
settings.collaborator_deletion=Премахване на сътрудник
|
||||
settings.collaborator_deletion_desc=Този потребител няма да има достъп на сътрудник до хранилището след изтриването. Желаете ли да продължите?
|
||||
settings.remove_collaborator_success=Сътрудникът е премахнат.
|
||||
settings.search_user_placeholder=Име на потребител...
|
||||
settings.org_not_allowed_to_be_collaborator=Невъзможно добавяне на организация като сътрудник.
|
||||
settings.user_is_org_member=Потребителят вече участва в организацията и не може да бъде добавен като сътрудник.
|
||||
settings.add_webhook=Добави уеб-кука
|
||||
settings.hooks_desc=Уеб-куките много приличат на обикновен HTTP POST тригер. Когато нещо се случи в Gogs, ние ще изпратим уведомление до сървъра, който посочите. Научете повече в <a target="_blank" href="%s">Ръководство за уеб-куки</a>.
|
||||
@@ -636,6 +732,8 @@ settings.event_send_everything=При <strong>всички</strong> събити
|
||||
settings.event_choose=Нека избера от какво имам нужда.
|
||||
settings.event_create=Създаване
|
||||
settings.event_create_desc=Създаване на клон или маркер
|
||||
settings.event_pull_request=Заявка за сливане
|
||||
settings.event_pull_request_desc=Заявка за сливане е отворена, затворена, отворена повторно, редактирана, възложена, отменена, при модифициран етикет, при премахнат етикет или е синхронизирана.
|
||||
settings.event_push=Предаване
|
||||
settings.event_push_desc=Git предаване към хранилището
|
||||
settings.active=Активна
|
||||
@@ -673,6 +771,8 @@ diff.show_unified_view=Обединен изглед
|
||||
diff.stats_desc=променени са <strong>%d файла</strong>, в които са <strong>добавени %d</strong> реда и са <strong>изтрити %d</strong> реда
|
||||
diff.bin=BIN
|
||||
diff.view_file=Целия файл
|
||||
diff.file_suppressed=Файловите разлики са ограничени, защото са твърде много
|
||||
diff.too_many_files=Някои файлове не бяха показани, защото твърде много файлове са промени
|
||||
|
||||
release.releases=Версии
|
||||
release.new_release=Нова версия
|
||||
@@ -703,6 +803,7 @@ release.deletion=Изтрий версията
|
||||
release.deletion_desc=При изтриване на тази версия ще се премахне и съответния Git маркер. Желаете ли да продължите?
|
||||
release.deletion_success=Версията беше изтрита успешно!
|
||||
release.tag_name_already_exist=Версия с това име на маркер вече съществува.
|
||||
release.tag_name_invalid=Името на етикета е невалидно.
|
||||
release.downloads=Изтегляния
|
||||
|
||||
[org]
|
||||
@@ -818,6 +919,8 @@ dashboard.resync_all_sshkeys=Презапис на ".ssh/authorized_keys" фай
|
||||
dashboard.resync_all_sshkeys_success=Всички публични ключове са презаписани успешно.
|
||||
dashboard.resync_all_update_hooks=Презапис на всички куки, закачени на актуализация на хранилищата (необходимо, когато се ползва собствен път за конфигурацията)
|
||||
dashboard.resync_all_update_hooks_success=Всички куки, закачени на актуализация на хранилищата, са презаписани успешно.
|
||||
dashboard.reinit_missing_repos=Реинициализира всички записи за хранилища
|
||||
dashboard.reinit_missing_repos_success=Всички записи за хранилища със загубени Git файлове са реинициализирани успешно.
|
||||
|
||||
dashboard.server_uptime=Операционно време
|
||||
dashboard.current_goroutine=Текущи Goroutines
|
||||
@@ -868,6 +971,7 @@ users.edit_account=Редактирай профил
|
||||
users.max_repo_creation=Макс. брой хранилища
|
||||
users.max_repo_creation_desc=(Задайте -1 за да се използва глобалния лимит)
|
||||
users.is_activated=Този профил е активиран
|
||||
users.prohibit_login=Този профил има забрана за влизане
|
||||
users.is_admin=Този профил има административни права
|
||||
users.allow_git_hook=Този профил има разрешение да създава Git куки
|
||||
users.allow_import_local=Този профил има права за импорт на локални хранилища
|
||||
@@ -898,6 +1002,7 @@ auths.enabled=Активно
|
||||
auths.updated=Последна модификация
|
||||
auths.auth_type=Тип на удостоверяване
|
||||
auths.auth_name=Име на удостоверяване
|
||||
auths.security_protocol=Протокол за защита
|
||||
auths.domain=Домейн
|
||||
auths.host=Сървър
|
||||
auths.port=Порт
|
||||
@@ -911,6 +1016,7 @@ auths.attribute_username_placeholder=Оставете празно за да и
|
||||
auths.attribute_name=Атрибут за име
|
||||
auths.attribute_surname=Атрибут за фамилия
|
||||
auths.attribute_mail=Атрибут за ел. поща
|
||||
auths.attributes_in_bind=Извличане на атрибути от контекста на име (DN) за свръзка
|
||||
auths.filter=Филтър за потребител
|
||||
auths.admin_filter=Филтър за администратор
|
||||
auths.ms_ad_sa=Ms Ad SA
|
||||
@@ -932,7 +1038,9 @@ auths.update=Запази настройки за удостоверяване
|
||||
auths.delete=Изтриване на това удостоверяване
|
||||
auths.delete_auth_title=Изтрий удостоверяването
|
||||
auths.delete_auth_desc=Това удостоверяване ще бъде изтрито. Желаете ли да продължите?
|
||||
auths.still_in_used=Това удостоверяване все още се използва от някои потребители. Моля изтрийте ги или ги конвертирайте до друг тип на влизане първо.
|
||||
auths.deletion_success=Удостоверяването е изтрито успешно!
|
||||
auths.login_source_exist=Източник за валидация на потребители "%s" вече съществува.
|
||||
|
||||
config.server_config=Сървърни настройки
|
||||
config.app_name=Име на приложението
|
||||
@@ -943,11 +1051,25 @@ config.offline_mode=Офлайн режим
|
||||
config.disable_router_log=Изключи журнал на маршрутизатора
|
||||
config.run_user=Потребителски контекст
|
||||
config.run_mode=Режим на изпълнение
|
||||
config.git_version=Git Version
|
||||
config.repo_root_path=Основен път към хранилища
|
||||
config.static_file_root_path=Път към статични файлове
|
||||
config.log_file_root_path=Път към журнал
|
||||
config.script_type=Тип на скрипта
|
||||
config.reverse_auth_user=Потребителско име при обратно удостоверяване
|
||||
|
||||
config.ssh_config=SSH конфигурация
|
||||
config.ssh_enabled=Активен
|
||||
config.ssh_start_builtin_server=Стартирай вграден сървър
|
||||
config.ssh_domain=Домейн
|
||||
config.ssh_port=Порт
|
||||
config.ssh_listen_port=Порт за слушане
|
||||
config.ssh_root_path=Основен път
|
||||
config.ssh_key_test_path=Път до ключове
|
||||
config.ssh_keygen_path=Път до генератор ('ssh-keygen')
|
||||
config.ssh_minimum_key_size_check=Проверка за минимален размер на ключове
|
||||
config.ssh_minimum_key_sizes=Минимален размер на ключове
|
||||
|
||||
config.db_config=Настройки на базата данни
|
||||
config.db_type=Тип
|
||||
config.db_host=Сървър
|
||||
@@ -957,33 +1079,41 @@ config.db_ssl_mode=SSL режим
|
||||
config.db_ssl_mode_helper=(само за postgres)
|
||||
config.db_path=Път
|
||||
config.db_path_helper=(за "sqlite3" и "tidb")
|
||||
|
||||
config.service_config=Настройка на услугата
|
||||
config.register_email_confirm=Изисквай потвърждение на адреси на ел. поща
|
||||
config.disable_register=Изключи нови регистрации
|
||||
config.show_registration_button=Покажи бутон за регистрация
|
||||
config.require_sign_in_view=Изисквай вписване за преглед
|
||||
config.enable_cache_avatar=Включи кеширане на аватари
|
||||
config.mail_notify=Уведомяване по ел. поща
|
||||
config.disable_key_size_check=Изключи проверка минимален размер на ключ
|
||||
config.enable_captcha=Включи Captcha
|
||||
config.active_code_lives=Кодове за активиране
|
||||
config.reset_password_code_lives=Кодове за изчистване на парола
|
||||
|
||||
config.webhook_config=Конфигурация на уеб-куки
|
||||
config.queue_length=Дължина на опашка
|
||||
config.deliver_timeout=Време за отказ при изпращане
|
||||
config.skip_tls_verify=Пропусни проверка на TLS
|
||||
|
||||
config.mailer_config=Конфигурация на мейлър
|
||||
config.mailer_enabled=Активен
|
||||
config.mailer_disable_helo=Изключи HELO
|
||||
config.mailer_name=Име
|
||||
config.mailer_host=Сървър
|
||||
config.mailer_user=Потребител
|
||||
config.send_test_mail=Изпрати тестово писмо
|
||||
config.test_mail_failed=Невъзможно изпращане на тестово писмо до '%s': %v
|
||||
config.test_mail_sent=Тестово писмо беше изпратено до '%s'.
|
||||
|
||||
config.oauth_config=OAuth конфигурация
|
||||
config.oauth_enabled=Активна
|
||||
|
||||
config.cache_config=Конфигурация на кеша
|
||||
config.cache_adapter=Кеш адаптер
|
||||
config.cache_interval=Кеш интервал
|
||||
config.cache_conn=Кеш на връзката
|
||||
|
||||
config.session_config=Конфигурация на сесии
|
||||
config.session_provider=Доставчик на сесии
|
||||
config.provider_config=Конфигурация на доставчик
|
||||
@@ -993,9 +1123,24 @@ config.gc_interval_time=GC през интервал
|
||||
config.session_life_time=Период на валидност на сесиите
|
||||
config.https_only=HTTPS само
|
||||
config.cookie_life_time=Период на валидност на бисквитките
|
||||
|
||||
config.picture_config=Конфигурация на изображения
|
||||
config.picture_service=Услуги за снимки
|
||||
config.disable_gravatar=Изключи Gravatar
|
||||
config.enable_federated_avatar=Включи външни аватари
|
||||
|
||||
config.git_config=Конфигурация на git
|
||||
config.git_disable_diff_highlight=Забрани оцветяване на синтаксис при преглед на разлики
|
||||
config.git_max_diff_lines=Максимален брой различни редове (за файл)
|
||||
config.git_max_diff_line_characters=Максимален брой различни символи (на ред)
|
||||
config.git_max_diff_files=Максимален брой променени файлове (при показване)
|
||||
config.git_gc_args=Аргументи на GC
|
||||
config.git_migrate_timeout=Време за отказ при миграция
|
||||
config.git_mirror_timeout=Време за отказ при синхр. на огледало
|
||||
config.git_clone_timeout=Време за отказ при клониране
|
||||
config.git_pull_timeout=Време за отказ при сливане
|
||||
config.git_gc_timeout=Време за отказ при GC
|
||||
|
||||
config.log_config=Конфигурация на журнал
|
||||
config.log_mode=Режим на журнал
|
||||
|
||||
@@ -1029,7 +1174,11 @@ create_repo=създаде хранилище <a href="%s"> %s</a>
|
||||
rename_repo=преименува хранилище от <code>%[1]s</code> на <a href="%[2]s">%[3]s</a>
|
||||
commit_repo=предаде към <a href="%[1]s/src/%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a>
|
||||
create_issue=`отвори задача <a href="%s/issues/%s">%s#%[2]s"</a>`
|
||||
close_issue=`затвори <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
reopen_issue=`повторно отвори <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
create_pull_request=`създаде заявка за сливане <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
close_pull_request=`затвори заявка за сливане <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
reopen_pull_request=`повторно отвори заявка за сливане <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
comment_issue=`коментира задача <a href="%s/issues/%s">%s#%[2]s"</a>`
|
||||
merge_pull_request=`обедини заявка за сливане <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
transfer_repo=прехвърли хранилище <code>%s</code> към <a href="%s">%s</a>
|
||||
|
||||
1214
conf/locale/locale_cs-CZ.ini
Normal file
1214
conf/locale/locale_cs-CZ.ini
Normal file
File diff suppressed because it is too large
Load Diff
911
conf/locale/locale_de-DE.ini
Executable file → Normal file
911
conf/locale/locale_de-DE.ini
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
@@ -38,19 +38,12 @@ settings = Settings
|
||||
your_profile = Your Profile
|
||||
your_settings = Your Settings
|
||||
|
||||
news_feed = News Feed
|
||||
activities = Activities
|
||||
pull_requests = Pull Requests
|
||||
issues = Issues
|
||||
|
||||
cancel = Cancel
|
||||
|
||||
[search]
|
||||
search = Search...
|
||||
repository = Repository
|
||||
user = User
|
||||
issue = Issue
|
||||
code = Code
|
||||
|
||||
[install]
|
||||
install = Installation
|
||||
title = Install Steps For First-time Run
|
||||
@@ -65,7 +58,7 @@ db_name = Database Name
|
||||
db_helper = Please use INNODB engine with utf8_general_ci charset for MySQL.
|
||||
ssl_mode = SSL Mode
|
||||
path = Path
|
||||
sqlite_helper = The file path of SQLite3 or TiDB database.
|
||||
sqlite_helper = The file path of SQLite3 or TiDB database. <br>Please use absolute path when you start as service.
|
||||
err_empty_db_path = SQLite3 or TiDB database path cannot be empty.
|
||||
err_invalid_tidb_name = TiDB database name does not allow characters "." and "-".
|
||||
no_admin_and_disable_registration = You cannot disable registration without creating an admin account.
|
||||
@@ -86,6 +79,8 @@ http_port = HTTP Port
|
||||
http_port_helper = Port number which application will listen on.
|
||||
app_url = Application URL
|
||||
app_url_helper = This affects HTTP/HTTPS clone URL and somewhere in email.
|
||||
log_root_path = Log Path
|
||||
log_root_path_helper = Directory to write log files to.
|
||||
|
||||
optional_title = Optional Settings
|
||||
email_title = Email Service Settings
|
||||
@@ -101,6 +96,8 @@ offline_mode = Enable Offline Mode
|
||||
offline_mode_popup = Disable CDN even in production mode, all resource files will be served locally.
|
||||
disable_gravatar = Disable Gravatar Service
|
||||
disable_gravatar_popup = Disable Gravatar and custom sources, all avatars are uploaded by users or default.
|
||||
federated_avatar_lookup = Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup = Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration = Disable Self-registration
|
||||
disable_registration_popup = Disable user self-registration, only admin can create accounts.
|
||||
enable_captcha = Enable Captcha
|
||||
@@ -119,15 +116,18 @@ sqlite3_not_available = Your release version does not support SQLite3, please do
|
||||
invalid_db_setting = Database setting is not correct: %v
|
||||
invalid_repo_path = Repository root path is invalid: %v
|
||||
run_user_not_match = Run user isn't the current user: %s -> %s
|
||||
invalid_smtp_from = SMTP From field is not valid: %v
|
||||
save_config_failed = Fail to save configuration: %v
|
||||
invalid_admin_setting = Admin account setting is invalid: %v
|
||||
install_success = Welcome! We're glad that you chose Gogs, have fun and take care.
|
||||
invalid_log_root_path = Log root path is invalid: %v
|
||||
|
||||
[home]
|
||||
uname_holder = Username or email
|
||||
password_holder = Password
|
||||
switch_dashboard_context = Switch Dashboard Context
|
||||
my_repos = My Repositories
|
||||
show_more_repos = Show more repositories...
|
||||
collaborative_repos = Collaborative Repositories
|
||||
my_orgs = My Organizations
|
||||
my_mirrors = My Mirrors
|
||||
@@ -137,6 +137,9 @@ issues.in_your_repos = In your repositories
|
||||
|
||||
[explore]
|
||||
repos = Repositories
|
||||
users = Users
|
||||
organizations = Organizations
|
||||
search = Search
|
||||
|
||||
[auth]
|
||||
create_new_account = Create New Account
|
||||
@@ -150,15 +153,17 @@ forget_password = Forgot password?
|
||||
sign_up_now = Need an account? Sign up now.
|
||||
confirmation_mail_sent_prompt = A new confirmation email has been sent to <b>%s</b>, please check your inbox within the next %d hours to complete the registration process.
|
||||
active_your_account = Activate Your Account
|
||||
prohibit_login = Login Prohibited
|
||||
prohibit_login_desc = Your account is prohibited to login, please contact site admin.
|
||||
resent_limit_prompt = Sorry, you already requested an activation email recently. Please wait 3 minutes then try again.
|
||||
has_unconfirmed_mail = Hi %s, you have an unconfirmed email address (<b>%s</b>). If you haven't received a confirmation email or need to resend a new one, please click on the button below.
|
||||
resend_mail = Click here to resend your activation email
|
||||
email_not_associate = This email address is not associated with any account.
|
||||
send_reset_mail = Click here to (re)send your password reset email
|
||||
reset_password = Reset Your Password
|
||||
invalid_code = Sorry, your confirmation code has expired or not valid.
|
||||
reset_password_helper = Click here to reset your password
|
||||
password_too_short = Password length cannot be less then 6.
|
||||
non_local_account = Non-local accounts cannot change passwords through Gogs.
|
||||
|
||||
[mail]
|
||||
activate_account = Please activate your account
|
||||
@@ -185,6 +190,13 @@ TeamName = Team name
|
||||
AuthName = Authorization name
|
||||
AdminEmail = Admin email
|
||||
|
||||
NewBranchName = New branch name
|
||||
CommitSummary = Commit summary
|
||||
CommitMessage = Commit message
|
||||
CommitChoice = Commit choice
|
||||
TreeName = File path
|
||||
Content = Content
|
||||
|
||||
require_error = ` cannot be empty.`
|
||||
alpha_dash_error = ` must be valid alpha or numeric or dash(-_) characters.`
|
||||
alpha_dash_dot_error = ` must be valid alpha or numeric or dash(-_) or dot characters.`
|
||||
@@ -218,13 +230,10 @@ still_own_repo = Your account still has ownership over at least one repository,
|
||||
still_has_org = Your account still has membership in at least one organization, you have to leave or delete your memberships first.
|
||||
org_still_own_repo = This organization still has ownership of repositories, you must delete or transfer them first.
|
||||
|
||||
still_own_user = This authentication is still in use by at least one user, please remove them from the authentication and try again.
|
||||
|
||||
target_branch_not_exist = Target branch does not exist.
|
||||
|
||||
[user]
|
||||
change_avatar = Change your avatar at gravatar.com
|
||||
change_custom_avatar = Change your avatar in settings
|
||||
change_avatar = Change your avatar
|
||||
join_on = Joined on
|
||||
repositories = Repositories
|
||||
activity = Public Activity
|
||||
@@ -240,6 +249,7 @@ form.name_pattern_not_allowed = Username pattern '%s' is not allowed.
|
||||
[settings]
|
||||
profile = Profile
|
||||
password = Password
|
||||
avatar = Avatar
|
||||
ssh_keys = SSH Keys
|
||||
social = Social Accounts
|
||||
applications = Applications
|
||||
@@ -260,12 +270,13 @@ change_username_prompt = This change will affect the way how links relate to you
|
||||
continue = Continue
|
||||
cancel = Cancel
|
||||
|
||||
enable_custom_avatar = Enable Custom Avatar
|
||||
enable_custom_avatar_helper = Disable fetch from Gravatar
|
||||
lookup_avatar_by_mail = Lookup Avatar by mail
|
||||
federated_avatar_lookup = Federated Avatar Lookup
|
||||
enable_custom_avatar = Use Custom Avatar
|
||||
choose_new_avatar = Choose new avatar
|
||||
update_avatar = Update Avatar Setting
|
||||
delete_current_avatar = Delete Current Avatar
|
||||
uploaded_avatar_not_a_image = Uploaded file is not a image.
|
||||
no_custom_avatar_available = No custom avatar available, cannot enable it.
|
||||
update_avatar_success = Your avatar setting has been updated successfully.
|
||||
|
||||
change_password = Change Password
|
||||
@@ -327,6 +338,10 @@ access_token_deletion = Personal Access Token Deletion
|
||||
access_token_deletion_desc = Delete this personal access token will remove all related accesses of application. Do you want to continue?
|
||||
delete_token_success = Personal access token has been removed successfully! Don't forget to update your application as well.
|
||||
|
||||
orgs.none = You are not a member of any organizations.
|
||||
orgs.leave_title = Leave an organization
|
||||
orgs.leave_desc = You will lose access to all repositories and teams after you left the organization. Do you want to continue?
|
||||
|
||||
delete_account = Delete Your Account
|
||||
delete_prompt = The operation will delete your account permanently, and <strong>CANNOT</strong> be undone!
|
||||
confirm_delete_account = Confirm Deletion
|
||||
@@ -347,7 +362,7 @@ fork_from = Fork From
|
||||
fork_visiblity_helper = You cannot alter the visibility of a forked repository.
|
||||
repo_desc = Description
|
||||
repo_lang = Language
|
||||
repo_lang_helper = Select .gitignore files
|
||||
repo_gitignore_helper = Select .gitignore templates
|
||||
license = License
|
||||
license_helper = Select a license file
|
||||
readme = Readme
|
||||
@@ -355,9 +370,12 @@ readme_helper = Select a readme template
|
||||
auto_init = Initialize this repository with selected files and template
|
||||
create_repo = Create Repository
|
||||
default_branch = Default Branch
|
||||
mirror_prune = Prune
|
||||
mirror_prune_desc = Remove any remote-tracking references that no longer exist on the remote
|
||||
mirror_interval = Mirror Interval (hour)
|
||||
mirror_address = Mirror Address
|
||||
mirror_address_desc = Please include necessary user credentials in the address.
|
||||
mirror_last_synced = Last Synced
|
||||
watchers = Watchers
|
||||
stargazers = Stargazers
|
||||
forks = Forks
|
||||
@@ -371,12 +389,13 @@ migrate_type = Migration Type
|
||||
migrate_type_helper = This repository will be a <span class="text blue">mirror</span>
|
||||
migrate_repo = Migrate Repository
|
||||
migrate.clone_address = Clone Address
|
||||
migrate.clone_address_desc = This can be a HTTP/HTTPS/GIT URL or local server path.
|
||||
migrate.clone_address_desc = This can be a HTTP/HTTPS/GIT URL.
|
||||
migrate.clone_address_desc_import_local = You're also allowed to migrate a repository by local server path.
|
||||
migrate.permission_denied = You are not allowed to import local repositories.
|
||||
migrate.invalid_local_path = Invalid local path, it does not exist or not a directory.
|
||||
migrate.failed = Migration failed: %v
|
||||
|
||||
mirror_from = mirror from
|
||||
mirror_from = mirror of
|
||||
forked_from = forked from
|
||||
fork_from_self = You cannot fork a repository you already own!
|
||||
copy_link = Copy
|
||||
@@ -396,7 +415,7 @@ create_new_repo_command = Create a new repository on the command line
|
||||
push_exist_repo = Push an existing repository from the command line
|
||||
repo_is_empty = This repository is empty, please come back later!
|
||||
|
||||
code = Code
|
||||
files = Files
|
||||
branch = Branch
|
||||
tree = Tree
|
||||
filter_branch_and_tag = Filter branch or tag
|
||||
@@ -412,6 +431,47 @@ file_raw = Raw
|
||||
file_history = History
|
||||
file_view_raw = View Raw
|
||||
file_permalink = Permalink
|
||||
file_too_large = This file is too large to be shown
|
||||
video_not_supported_in_browser = Your browser doesn't support HTML5 video tag.
|
||||
|
||||
editor.new_file = New file
|
||||
editor.upload_file = Upload file
|
||||
editor.edit_file = Edit file
|
||||
editor.preview_changes = Preview Changes
|
||||
editor.cannot_edit_non_text_files = Cannot edit non-text files
|
||||
editor.edit_this_file = Edit this file
|
||||
editor.must_be_on_a_branch = You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit = You must fork this repository before editing the file
|
||||
editor.delete_this_file = Delete this file
|
||||
editor.must_have_write_access = You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success = File '%s' has been deleted successfully!
|
||||
editor.name_your_file = Name your file...
|
||||
editor.filename_help = To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or = or
|
||||
editor.cancel_lower = cancel
|
||||
editor.commit_changes = Commit Changes
|
||||
editor.add_tmpl = Add '%s/<filename>'
|
||||
editor.add = Add '%s'
|
||||
editor.update = Update '%s'
|
||||
editor.delete = Delete '%s'
|
||||
editor.commit_message_desc = Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch = Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch = Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc = New branch name...
|
||||
editor.cancel = Cancel
|
||||
editor.filename_cannot_be_empty = Filename cannot be empty.
|
||||
editor.branch_already_exists = Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file = Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.file_is_a_symlink = The file '%s' is a symlink that cannot be modified from the web editor.
|
||||
editor.filename_is_a_directory = The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists = The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing = File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists = A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show = There are no changes to show.
|
||||
editor.fail_to_update_file = Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir = Add subdirectory...
|
||||
editor.unable_to_upload_files = Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir = Upload files to '%s'
|
||||
|
||||
commits.commits = Commits
|
||||
commits.search = Search commits
|
||||
@@ -438,6 +498,11 @@ issues.create = Create Issue
|
||||
issues.new_label = New Label
|
||||
issues.new_label_placeholder = Label name...
|
||||
issues.create_label = Create Label
|
||||
issues.label_templates.title = Load a predefined set of labels
|
||||
issues.label_templates.info = There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper = Select a label set
|
||||
issues.label_templates.use = Use this label set
|
||||
issues.label_templates.fail_to_load_file = Failed to load label template file '%s': %v
|
||||
issues.open_tab = %d Open
|
||||
issues.close_tab = %d Closed
|
||||
issues.filter_label = Label
|
||||
@@ -465,7 +530,8 @@ issues.next = Next
|
||||
issues.open_title = Open
|
||||
issues.closed_title = Closed
|
||||
issues.num_comments = %d comments
|
||||
issues.commented_at = `commented <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commented_at = `commented <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm = Are you sure you want to delete this comment?
|
||||
issues.no_content = There is no content yet.
|
||||
issues.close_issue = Close
|
||||
issues.close_comment_issue = Comment and close
|
||||
@@ -476,10 +542,9 @@ issues.closed_at = `closed <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at = `reopened <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commit_ref_at = `referenced this issue from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.poster = Poster
|
||||
issues.admin = Admin
|
||||
issues.collaborator = Collaborator
|
||||
issues.owner = Owner
|
||||
issues.sign_up_for_free = Sign up for free
|
||||
issues.sign_in_require_desc = to join this conversation. Already have an account? <a href="%s">Sign in to comment</a>
|
||||
issues.sign_in_require_desc = <a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit = Edit
|
||||
issues.cancel = Cancel
|
||||
issues.save = Save
|
||||
@@ -493,6 +558,9 @@ issues.label_modify = Label Modification
|
||||
issues.label_deletion = Label Deletion
|
||||
issues.label_deletion_desc = Deleting this label will remove its information in all related issues. Do you want to continue?
|
||||
issues.label_deletion_success = Label has been deleted successfully!
|
||||
issues.num_participants = %d Participants
|
||||
issues.attachment.open_tab = `Click to see "%s" in a new tab`
|
||||
issues.attachment.download = `Click to download "%s"`
|
||||
|
||||
pulls.new = New Pull Request
|
||||
pulls.compare_changes = Compare Changes
|
||||
@@ -519,6 +587,8 @@ pulls.cannot_auto_merge_desc = This pull request can't be merged automatically b
|
||||
pulls.cannot_auto_merge_helper = Please merge manually in order to resolve the conflicts.
|
||||
pulls.merge_pull_request = Merge Pull Request
|
||||
pulls.open_unmerged_pull_exists = `You can't perform reopen operation because there is already an open pull request (#%d) from same repository with same merge information and is waiting for merging.`
|
||||
pulls.delete_branch = Delete Branch
|
||||
pulls.delete_branch_has_new_commits = Branch cannot be deleted because it has new commits after mergence.
|
||||
|
||||
milestones.new = New Milestone
|
||||
milestones.open_tab = %d Open
|
||||
@@ -556,6 +626,8 @@ wiki.save_page = Save Page
|
||||
wiki.last_commit_info = %s edited this page %s
|
||||
wiki.edit_page_button = Edit
|
||||
wiki.new_page_button = New Page
|
||||
wiki.delete_page_button = Delete Page
|
||||
wiki.delete_page_notice_1 = This will delete the page <code>"%s"</code>. Please be certain.
|
||||
wiki.page_already_exists = Wiki page with same name already exists.
|
||||
wiki.pages = Pages
|
||||
wiki.last_updated = Last updated %s
|
||||
@@ -563,36 +635,57 @@ wiki.last_updated = Last updated %s
|
||||
settings = Settings
|
||||
settings.options = Options
|
||||
settings.collaboration = Collaboration
|
||||
settings.collaboration.admin = Admin
|
||||
settings.collaboration.write = Write
|
||||
settings.collaboration.read = Read
|
||||
settings.collaboration.undefined = Undefined
|
||||
settings.hooks = Webhooks
|
||||
settings.githooks = Git Hooks
|
||||
settings.basic_settings = Basic Settings
|
||||
settings.mirror_settings = Mirror Settings
|
||||
settings.sync_mirror = Sync Now
|
||||
settings.mirror_sync_in_progress = Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site = Official Site
|
||||
settings.update_settings = Update Settings
|
||||
settings.change_reponame_prompt = This change will affect how links relate to the repository.
|
||||
settings.advanced_settings = Advanced Settings
|
||||
settings.wiki_desc = Enable wiki to allow people write documents
|
||||
settings.wiki_desc = Enable wiki system
|
||||
settings.use_internal_wiki = Use builtin wiki
|
||||
settings.use_external_wiki = Use external wiki
|
||||
settings.external_wiki_url = External Wiki URL
|
||||
settings.external_wiki_url_desc = Visitors will be redirected to URL when they click on the tab.
|
||||
settings.issues_desc = Enable builtin lightweight issue tracker
|
||||
settings.issues_desc = Enable issue tracker
|
||||
settings.use_internal_issue_tracker = Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker = Use external issue tracker
|
||||
settings.external_tracker_url = External Issue Tracker URL
|
||||
settings.external_tracker_url_desc = Visitors will be redirected to URL when they click on the tab.
|
||||
settings.tracker_url_format = External Issue Tracker URL Format
|
||||
settings.tracker_issue_style = External Issue Tracker Naming Style:
|
||||
settings.tracker_issue_style.numeric = Numeric
|
||||
settings.tracker_issue_style.alphanumeric = Alphanumeric
|
||||
settings.tracker_url_format_desc = You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index.
|
||||
settings.pulls_desc = Enable pull requests to accept public contributions
|
||||
settings.danger_zone = Danger Zone
|
||||
settings.new_owner_has_same_repo = The new owner already has a repository with same name. Please choose another name.
|
||||
settings.convert = Convert To Regular Repository
|
||||
settings.convert_desc = You can convert this mirror to a regular repository. This cannot be reversed.
|
||||
settings.convert_notices_1 = - This operation will convert this repository mirror into a regular repository and cannot be undone.
|
||||
settings.convert_confirm = Confirm Conversion
|
||||
settings.convert_succeed = Repository has been converted to regular type successfully.
|
||||
settings.transfer = Transfer Ownership
|
||||
settings.transfer_desc = Transfer this repository to another user or to an organization in which you have admin rights.
|
||||
settings.new_owner_has_same_repo = The new owner already has a repository with same name. Please choose another name.
|
||||
settings.delete = Delete This Repository
|
||||
settings.delete_desc = Once you delete a repository, there is no going back. Please be certain.
|
||||
settings.transfer_notices_1 = - You will lose access if new owner is a individual user.
|
||||
settings.transfer_notices_2 = - You will conserve access if new owner is an organization and if you're one of the owners.
|
||||
settings.transfer_form_title = Please enter following information to confirm your operation:
|
||||
settings.wiki_delete = Erase Wiki Data
|
||||
settings.wiki_delete_desc = Once you erase wiki data there is no going back. Please be certain.
|
||||
settings.wiki_delete_notices_1 = - This will delete and disable the wiki for %s
|
||||
settings.wiki_deletion_success = Repository wiki data have been erased successfully.
|
||||
settings.delete = Delete This Repository
|
||||
settings.delete_desc = Once you delete a repository, there is no going back. Please be certain.
|
||||
settings.delete_notices_1 = - This operation <strong>CANNOT</strong> be undone.
|
||||
settings.delete_notices_2 = - This operation will permanently delete the everything of this repository, including Git data, issues, comments and accesses of collaborators.
|
||||
settings.delete_notices_fork_1 = - If this repository is public, all forks will be became independent after deletion.
|
||||
settings.delete_notices_fork_2 = - If this repository is private, all forks will be removed at the same time.
|
||||
settings.delete_notices_fork_3 = - If you want to keep all forks after deletion, please change visibility of this repository to public first.
|
||||
settings.delete_notices_fork_1 = - All forks will become independent after deletion.
|
||||
settings.deletion_success = Repository has been deleted successfully!
|
||||
settings.update_settings_success = Repository options has been updated successfully.
|
||||
settings.transfer_owner = New Owner
|
||||
@@ -601,8 +694,12 @@ settings.transfer_succeed = Repository ownership has been transferred successful
|
||||
settings.confirm_delete = Confirm Deletion
|
||||
settings.add_collaborator = Add New Collaborator
|
||||
settings.add_collaborator_success = New collaborator has been added.
|
||||
settings.delete_collaborator = Delete
|
||||
settings.collaborator_deletion = Collaborator Deletion
|
||||
settings.collaborator_deletion_desc = This user will no longer have collaboration access to this repository after deletion. Do you want to continue?
|
||||
settings.remove_collaborator_success = Collaborator has been removed.
|
||||
settings.search_user_placeholder = Search user...
|
||||
settings.org_not_allowed_to_be_collaborator = Organization is not allowed to be added as a collaborator.
|
||||
settings.user_is_org_member = User is organization member who cannot be added as a collaborator.
|
||||
settings.add_webhook = Add Webhook
|
||||
settings.hooks_desc = Webhooks are much like basic HTTP POST event triggers. Whenever something occurs in Gogs, we will handle the notification to the target host you specify. Learn more in this <a target="_blank" href="%s">Webhooks Guide</a>.
|
||||
@@ -635,6 +732,8 @@ settings.event_send_everything = I need <strong>everything</strong>.
|
||||
settings.event_choose = Let me choose what I need.
|
||||
settings.event_create = Create
|
||||
settings.event_create_desc = Branch, or tag created
|
||||
settings.event_pull_request = Pull Request
|
||||
settings.event_pull_request_desc = Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push = Push
|
||||
settings.event_push_desc = Git push to a repository
|
||||
settings.active = Active
|
||||
@@ -672,6 +771,8 @@ diff.show_unified_view = Unified View
|
||||
diff.stats_desc = <strong> %d changed files</strong> with <strong>%d additions</strong> and <strong>%d deletions</strong>
|
||||
diff.bin = BIN
|
||||
diff.view_file = View File
|
||||
diff.file_suppressed = File diff suppressed because it is too large
|
||||
diff.too_many_files = Some files were not shown because too many files changed in this diff
|
||||
|
||||
release.releases = Releases
|
||||
release.new_release = New Release
|
||||
@@ -702,6 +803,7 @@ release.deletion = Release Deletion
|
||||
release.deletion_desc = Deleting this release will delete the corresponding Git tag. Do you want to continue?
|
||||
release.deletion_success = Release has been deleted successfully!
|
||||
release.tag_name_already_exist = Release with this tag name already exists.
|
||||
release.tag_name_invalid = Tag name is not valid.
|
||||
release.downloads = Downloads
|
||||
|
||||
[org]
|
||||
@@ -796,7 +898,7 @@ first_page = First
|
||||
last_page = Last
|
||||
total = Total: %d
|
||||
|
||||
dashboard.statistic = Statistic
|
||||
dashboard.statistic = Statistics
|
||||
dashboard.operations = Operations
|
||||
dashboard.system_status = System Monitor Status
|
||||
dashboard.statistic_info = Gogs database has <b>%d</b> users, <b>%d</b> organizations, <b>%d</b> public keys, <b>%d</b> repositories, <b>%d</b> watches, <b>%d</b> stars, <b>%d</b> actions, <b>%d</b> accesses, <b>%d</b> issues, <b>%d</b> comments, <b>%d</b> social accounts, <b>%d</b> follows, <b>%d</b> mirrors, <b>%d</b> releases, <b>%d</b> login sources, <b>%d</b> webhooks, <b>%d</b> milestones, <b>%d</b> labels, <b>%d</b> hook tasks, <b>%d</b> teams, <b>%d</b> update tasks, <b>%d</b> attachments.
|
||||
@@ -817,6 +919,8 @@ dashboard.resync_all_sshkeys = Rewrite '.ssh/authorized_keys' file (caution: non
|
||||
dashboard.resync_all_sshkeys_success = All public keys have been rewritten successfully.
|
||||
dashboard.resync_all_update_hooks = Rewrite all update hook of repositories (needed when custom config path is changed)
|
||||
dashboard.resync_all_update_hooks_success = All repositories' update hook have been rewritten successfully.
|
||||
dashboard.reinit_missing_repos = Reinitialize all repository records that lost Git files
|
||||
dashboard.reinit_missing_repos_success = All repository records that lost Git files have been reinitialized successfully.
|
||||
|
||||
dashboard.server_uptime = Server Uptime
|
||||
dashboard.current_goroutine = Current Goroutines
|
||||
@@ -867,6 +971,7 @@ users.edit_account = Edit Account
|
||||
users.max_repo_creation = Maximum Repository Creation Limit
|
||||
users.max_repo_creation_desc = (Set -1 to use global default limit)
|
||||
users.is_activated = This account is activated
|
||||
users.prohibit_login = This account is prohibited to login
|
||||
users.is_admin = This account has administrator permissions
|
||||
users.allow_git_hook = This account has permissions to create Git hooks
|
||||
users.allow_import_local = This account has permissions to import local repositories
|
||||
@@ -897,6 +1002,7 @@ auths.enabled = Enabled
|
||||
auths.updated = Updated
|
||||
auths.auth_type = Authentication Type
|
||||
auths.auth_name = Authentication Name
|
||||
auths.security_protocol = Security Protocol
|
||||
auths.domain = Domain
|
||||
auths.host = Host
|
||||
auths.port = Port
|
||||
@@ -910,6 +1016,7 @@ auths.attribute_username_placeholder = Leave empty to use sign-in form field val
|
||||
auths.attribute_name = First name attribute
|
||||
auths.attribute_surname = Surname attribute
|
||||
auths.attribute_mail = Email attribute
|
||||
auths.attributes_in_bind = Fetch attributes in Bind DN context
|
||||
auths.filter = User Filter
|
||||
auths.admin_filter = Admin Filter
|
||||
auths.ms_ad_sa = Ms Ad SA
|
||||
@@ -924,14 +1031,16 @@ auths.pam_service_name = PAM Service Name
|
||||
auths.enable_auto_register = Enable Auto Registration
|
||||
auths.tips = Tips
|
||||
auths.edit = Edit Authentication Setting
|
||||
auths.activated = This authentication is activate
|
||||
auths.activated = This authentication is activated
|
||||
auths.new_success = New authentication '%s' has been added successfully.
|
||||
auths.update_success = Authentication setting has been updated successfully.
|
||||
auths.update = Update Authentication Setting
|
||||
auths.delete = Delete This Authentication
|
||||
auths.delete_auth_title = Authentication Deletion
|
||||
auths.delete_auth_desc = This authentication is going to be deleted, do you want to continue?
|
||||
auths.still_in_used = This authentication is still used by some users, please delete or convert these users to another login type first.
|
||||
auths.deletion_success = Authentication has been deleted successfully!
|
||||
auths.login_source_exist = Login source '%s' already exists.
|
||||
|
||||
config.server_config = Server Configuration
|
||||
config.app_name = Application Name
|
||||
@@ -942,11 +1051,25 @@ config.offline_mode = Offline Mode
|
||||
config.disable_router_log = Disable Router Log
|
||||
config.run_user = Run User
|
||||
config.run_mode = Run Mode
|
||||
config.git_version = Git Version
|
||||
config.repo_root_path = Repository Root Path
|
||||
config.static_file_root_path = Static File Root Path
|
||||
config.log_file_root_path = Log File Root Path
|
||||
config.script_type = Script Type
|
||||
config.reverse_auth_user = Reverse Authentication User
|
||||
|
||||
config.ssh_config = SSH Configuration
|
||||
config.ssh_enabled = Enabled
|
||||
config.ssh_start_builtin_server = Start Builtin Server
|
||||
config.ssh_domain = Domain
|
||||
config.ssh_port = Port
|
||||
config.ssh_listen_port = Listen Port
|
||||
config.ssh_root_path = Root Path
|
||||
config.ssh_key_test_path = Key Test Path
|
||||
config.ssh_keygen_path = Keygen ('ssh-keygen') Path
|
||||
config.ssh_minimum_key_size_check = Minimum Key Size Check
|
||||
config.ssh_minimum_key_sizes = Minimum Key Sizes
|
||||
|
||||
config.db_config = Database Configuration
|
||||
config.db_type = Type
|
||||
config.db_host = Host
|
||||
@@ -956,33 +1079,41 @@ config.db_ssl_mode = SSL Mode
|
||||
config.db_ssl_mode_helper = (for "postgres" only)
|
||||
config.db_path = Path
|
||||
config.db_path_helper = (for "sqlite3" and "tidb")
|
||||
|
||||
config.service_config = Service Configuration
|
||||
config.register_email_confirm = Require Email Confirmation
|
||||
config.disable_register = Disable Registration
|
||||
config.show_registration_button = Show Register Button
|
||||
config.require_sign_in_view = Require Sign In View
|
||||
config.enable_cache_avatar = Enable Cache Avatar
|
||||
config.mail_notify = Mail Notification
|
||||
config.disable_key_size_check = Disable Minimum Key Size Check
|
||||
config.enable_captcha = Enable Captcha
|
||||
config.active_code_lives = Active Code Lives
|
||||
config.reset_password_code_lives = Reset Password Code Lives
|
||||
|
||||
config.webhook_config = Webhook Configuration
|
||||
config.queue_length = Queue Length
|
||||
config.deliver_timeout = Deliver Timeout
|
||||
config.skip_tls_verify = Skip TLS Verify
|
||||
|
||||
config.mailer_config = Mailer Configuration
|
||||
config.mailer_enabled = Enabled
|
||||
config.mailer_disable_helo = Disable HELO
|
||||
config.mailer_name = Name
|
||||
config.mailer_host = Host
|
||||
config.mailer_user = User
|
||||
config.send_test_mail = Send Test Email
|
||||
config.test_mail_failed = Fail to send test email to '%s': %v
|
||||
config.test_mail_sent = Test email has been sent to '%s'.
|
||||
|
||||
config.oauth_config = OAuth Configuration
|
||||
config.oauth_enabled = Enabled
|
||||
|
||||
config.cache_config = Cache Configuration
|
||||
config.cache_adapter = Cache Adapter
|
||||
config.cache_interval = Cache Interval
|
||||
config.cache_conn = Cache Connection
|
||||
|
||||
config.session_config = Session Configuration
|
||||
config.session_provider = Session Provider
|
||||
config.provider_config = Provider Config
|
||||
@@ -992,9 +1123,24 @@ config.gc_interval_time = GC Interval Time
|
||||
config.session_life_time = Session Life Time
|
||||
config.https_only = HTTPS Only
|
||||
config.cookie_life_time = Cookie Life Time
|
||||
|
||||
config.picture_config = Picture Configuration
|
||||
config.picture_service = Picture Service
|
||||
config.disable_gravatar = Disable Gravatar
|
||||
config.enable_federated_avatar = Enable Federated Avatars
|
||||
|
||||
config.git_config = Git Configuration
|
||||
config.git_disable_diff_highlight = Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines = Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters = Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files = Max Diff Files (to be shown)
|
||||
config.git_gc_args = GC Arguments
|
||||
config.git_migrate_timeout = Migration Timeout
|
||||
config.git_mirror_timeout = Mirror Update Timeout
|
||||
config.git_clone_timeout = Clone Operation Timeout
|
||||
config.git_pull_timeout = Pull Operation Timeout
|
||||
config.git_gc_timeout = GC Operation Timeout
|
||||
|
||||
config.log_config = Log Configuration
|
||||
config.log_mode = Log Mode
|
||||
|
||||
@@ -1028,7 +1174,11 @@ create_repo = created repository <a href="%s">%s</a>
|
||||
rename_repo = renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a>
|
||||
commit_repo = pushed to <a href="%[1]s/src/%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a>
|
||||
create_issue = `opened issue <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
close_issue = `closed issue <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
reopen_issue = `reopened issue <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
create_pull_request = `created pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
close_pull_request = `closed pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
reopen_pull_request = `reopened pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
comment_issue = `commented on issue <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
merge_pull_request = `merged pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
transfer_repo = transfered repository <code>%s</code> to <a href="%s">%s</a>
|
||||
|
||||
385
conf/locale/locale_es-ES.ini
Executable file → Normal file
385
conf/locale/locale_es-ES.ini
Executable file → Normal file
@@ -6,9 +6,9 @@ explore=Explorar
|
||||
help=Ayuda
|
||||
sign_in=Iniciar sesión
|
||||
sign_out=Cerrar sesión
|
||||
sign_up=Suscripción
|
||||
sign_up=Registro
|
||||
register=Registro
|
||||
website=Página Web
|
||||
website=Página web
|
||||
version=Versión
|
||||
page=Página
|
||||
template=Plantilla
|
||||
@@ -25,11 +25,11 @@ captcha=Captcha
|
||||
|
||||
repository=Repositorio
|
||||
organization=Organización
|
||||
mirror=Mirror
|
||||
mirror=Réplica
|
||||
new_repo=Nuevo repositorio
|
||||
new_migrate=Nueva Migración
|
||||
new_migrate=Nueva migración
|
||||
new_mirror=Nueva réplica
|
||||
new_fork=Nuevo Fork del Repositorio
|
||||
new_fork=Nuevo fork del repositorio
|
||||
new_org=Nueva organización
|
||||
manage_org=Administrar organizaciones
|
||||
admin_panel=Panel de administración
|
||||
@@ -38,23 +38,16 @@ settings=Configuraciones
|
||||
your_profile=Tu perfil
|
||||
your_settings=Tu configuración
|
||||
|
||||
news_feed=Feed de noticias
|
||||
activities=Actividad
|
||||
pull_requests=Pull Requests
|
||||
issues=Incidencias
|
||||
|
||||
cancel=Cancelar
|
||||
|
||||
[search]
|
||||
search=Buscar...
|
||||
repository=Repositorio
|
||||
user=Usuario
|
||||
issue=Incidencia
|
||||
code=Código
|
||||
|
||||
[install]
|
||||
install=Instalación
|
||||
title=Pasos de la instalación por primera vez
|
||||
docker_helper=Si está ejecutando Gogs usando Docker, por favor lea <a target="_blank" href="%s"> estas pautas</a> antes de cambiar nada en esta página!
|
||||
docker_helper=Si está ejecutando Gogs usando Docker, ¡por favor lea <a target="_blank" href="%s"> estas pautas</a> antes de cambiar nada en esta página!
|
||||
requite_db_desc=Gogs requiere una base de datos MySQL, PostgreSQL, SQLite3 o TiDB.
|
||||
db_title=Configuración de base de datos
|
||||
db_type=Tipo de base de datos
|
||||
@@ -65,14 +58,14 @@ db_name=Nombre de la base de datos
|
||||
db_helper=Por favor utilice el motor INNODB con la configuración de caracteres utf8_general_ci para MySQL.
|
||||
ssl_mode=Modo SSL
|
||||
path=Ruta
|
||||
sqlite_helper=Ruta del archivo con la base de datos SQLite3 o TiDB.
|
||||
sqlite_helper=Ruta al archivo de base de datos SQLite3 o TiDB. <br>Por favor, usa una ruta absoluta cuando inicies como servicio.
|
||||
err_empty_db_path=La ruta a la base de datos SQLite3 o TiDB no puede estar vacía.
|
||||
err_invalid_tidb_name=El nombre de la base de datos TiDB no puede contener los caracteres "." ni "-".
|
||||
no_admin_and_disable_registration=No puede deshabilitar el registro sin crear una cuenta de administrador.
|
||||
err_empty_admin_password=La contraseña de administrador no puede estar vacía.
|
||||
|
||||
general_title=Configuración General de Gogs
|
||||
app_name=Nombre de la Aplicación
|
||||
app_name=Nombre de la aplicación
|
||||
app_name_helper=Pon aquí el nombre de tu organización, ¡alto y claro!
|
||||
repo_path=Ruta del repositorio de Raiz (Root)
|
||||
repo_path_helper=Todos los repositorios remotos de Git se guardarán en este directorio.
|
||||
@@ -86,9 +79,11 @@ http_port=Puerto HTTP
|
||||
http_port_helper=Puerto en el que escuchará la aplicación.
|
||||
app_url=URL de la aplicación
|
||||
app_url_helper=Esto afecta a las URLs para clonar por HTTP/HTTPS y a algunos correos electrónicos.
|
||||
log_root_path=Ruta del registro
|
||||
log_root_path_helper=Directorio donde almacenar los registros.
|
||||
|
||||
optional_title=Configuración Opcional
|
||||
email_title=Configuración del Servicio de Correo
|
||||
optional_title=Configuración opcional
|
||||
email_title=Configuración del servicio de correo
|
||||
smtp_host=SMTP Host
|
||||
smtp_from=Desde
|
||||
smtp_from_helper=Remitente del email, RFC 5322. Puede ser solamente una dirección de correo electrónico, o estar en el formato "Nombre" <email@example.com>.
|
||||
@@ -96,11 +91,13 @@ mailer_user=Remitente del Correo Electrónico
|
||||
mailer_password=Contraseña del Remitente
|
||||
register_confirm=Habilitar la Confirmación en el Registro
|
||||
mail_notify=Habilitar las Notificaciones de Correo
|
||||
server_service_title=Configuración de Servidor y Otros Servicios
|
||||
server_service_title=Configuración del servidor y otros servicios
|
||||
offline_mode=Activar el modo Sin Conexión
|
||||
offline_mode_popup=Desactivar el CDN incluso en el modo de producción, todos los recursos se servirán localmente.
|
||||
disable_gravatar=Desactivar el Servicio Gravatar
|
||||
disable_gravatar=Desactivar el servicio Gravatar
|
||||
disable_gravatar_popup=Desactivar Gravatar y cualquier otra fuente personalizada. Todos los avatares deben ser cargados por los usuarios o en su defecto se mostrará el avatar predeterminado.
|
||||
federated_avatar_lookup=Habilitar búsqueda de Avatares Federados
|
||||
federated_avatar_lookup_popup=Habilitar búsqueda de avatares federador para usar el servicio federado de código abierto basado en libravatar.
|
||||
disable_registration=Desactivar Auto-Registro
|
||||
disable_registration_popup=Desactivar auto-registro del usuario, solo el administrador podrá crear cuentas nuevas.
|
||||
enable_captcha=Activar la Captcha
|
||||
@@ -108,7 +105,7 @@ enable_captcha_popup=Requiere validar la captcha para el auto-registro de usuari
|
||||
require_sign_in_view=Activar el Inicio de Sesión obligatorio para Ver Páginas
|
||||
require_sign_in_view_popup=Solo los usuarios logados pueden ver páginas, los visitantes anónimos solo podrán ver las páginas de login/registro.
|
||||
admin_setting_desc=No es necesario crear una cuenta de administrador ahora mismo, el usuario que tenga ID=1 obtendrá privilegios de administrador automáticamente.
|
||||
admin_title=Configuración de la Cuenta de Administrador
|
||||
admin_title=Configuración de la cuenta de administrador
|
||||
admin_name=Nombre de usuario
|
||||
admin_password=Contraseña
|
||||
confirm_password=Confirmar Contraseña
|
||||
@@ -119,27 +116,33 @@ sqlite3_not_available=Tu versión no soporta SQLite3, por favor descarga el bina
|
||||
invalid_db_setting=La configuración de la base de datos no es correcta: %v
|
||||
invalid_repo_path=La ruta de la raíz del repositorio es inválida: %v
|
||||
run_user_not_match=El usuario que está ejecutando la aplicación no es el usuario actual: %s -> %s
|
||||
invalid_smtp_from=SMTP From field is not valid: %v
|
||||
save_config_failed=Error al guardar la configuración: %v
|
||||
invalid_admin_setting=La configuración de la cuenta de administración es inválida: %v
|
||||
install_success=Bienvenido! Estamos encantados de que hayas escogido Gogs, diviértete y cuídate.
|
||||
invalid_log_root_path=La ruta para los registros es inválida: %v
|
||||
|
||||
[home]
|
||||
uname_holder=Nombre de usuario o correo electrónico
|
||||
password_holder=Contraseña
|
||||
switch_dashboard_context=Cambiar el contexto del Dashboard
|
||||
my_repos=Mis Repositorios
|
||||
collaborative_repos=Repositorios Colaborativos
|
||||
my_orgs=Mis Organizaciones
|
||||
my_mirrors=Mis Mirrors
|
||||
my_repos=Mis repositorios
|
||||
show_more_repos=Mostrar más repositorios...
|
||||
collaborative_repos=Repositorios colaborativos
|
||||
my_orgs=Mis organizaciones
|
||||
my_mirrors=Mis réplicas
|
||||
view_home=Ver %s
|
||||
|
||||
issues.in_your_repos=En tus repositorios
|
||||
|
||||
[explore]
|
||||
repos=Repositorios
|
||||
users=Usuarios
|
||||
organizations=Organizaciones
|
||||
search=Buscar
|
||||
|
||||
[auth]
|
||||
create_new_account=Crear una Nueva Cuenta
|
||||
create_new_account=Crear una nueva cuenta
|
||||
register_hepler_msg=¿Ya tienes una cuenta? ¡Inicia sesión!
|
||||
social_register_hepler_msg=¿Ya tienes una cuenta? ¡Enlázala!
|
||||
disable_register_prompt=Lo sentimos, el registro está deshabilitado. Por favor, contacta con el administrador del sitio.
|
||||
@@ -150,15 +153,17 @@ forget_password=¿Has olvidado tu contraseña?
|
||||
sign_up_now=¿Necesitas una cuenta? Regístrate ahora.
|
||||
confirmation_mail_sent_prompt=Un nuevo correo de confirmación se ha enviado a <b>%s</b>. Por favor, comprueba tu bandeja de entrada en las siguientes %d horas para completar el proceso de registro.
|
||||
active_your_account=Activa tu cuenta
|
||||
prohibit_login=Ingreso prohibido
|
||||
prohibit_login_desc=Su cuenta tiene prohibido ingresar al sistema, fovor contactar al administrador del sistema.
|
||||
resent_limit_prompt=Lo sentimos, estás solicitando el reenvío del mail de activación con demasiada frecuencia. Por favor, espera 3 minutos.
|
||||
has_unconfirmed_mail=Hola %s, tu correo electrónico (<b>%s</b>) no está confirmado. Si no has recibido un correo de confirmación o necesitas que lo enviemos de nuevo, por favor, haz click en el siguiente botón.
|
||||
resend_mail=Haz click aquí para reenviar tu correo electrónico de activación
|
||||
email_not_associate=Esta dirección de correo electrónico no esta asociada a ninguna cuenta.
|
||||
send_reset_mail=Haga clic aquí para (re)enviar el correo para el restablecimiento de la contraseña
|
||||
reset_password=Restablecer su contraseña
|
||||
invalid_code=Lo sentimos, su código de confirmación ha expirado o no es valido.
|
||||
reset_password_helper=Haga Clic aquí para restablecer su contraseña
|
||||
password_too_short=La longitud de la contraseña no puede ser menor a 6.
|
||||
non_local_account=Cuentas que no son locales no pueden cambiar las contraseñas a través de Gogs.
|
||||
|
||||
[mail]
|
||||
activate_account=Por favor, active su cuenta
|
||||
@@ -185,6 +190,13 @@ TeamName=Nombre del equipo
|
||||
AuthName=Nombre de autorización
|
||||
AdminEmail=Correo electrónico del administrador
|
||||
|
||||
NewBranchName=Nuevo nombre de rama
|
||||
CommitSummary=Resumen del commit
|
||||
CommitMessage=Mensaje de commit
|
||||
CommitChoice=Hacer commit de la elección
|
||||
TreeName=Ruta del archivo
|
||||
Content=Contenido
|
||||
|
||||
require_error=` no puede estar vacío.`
|
||||
alpha_dash_error=` los caracteres deben ser Alfanumericos o dash(-_).`
|
||||
alpha_dash_dot_error=` debe ser un caracter alfanumérivo válido, un guión alto o bajo (-_) o un signo de puntuación.`
|
||||
@@ -203,7 +215,6 @@ repo_name_been_taken=Ya existe un repositorio con este nombre.
|
||||
org_name_been_taken=Ya existe una organización con este nombre.
|
||||
team_name_been_taken=Ya existe un equipo con este nombre.
|
||||
email_been_used=Esta dirección de correo electrónico ya está en uso.
|
||||
illegal_team_name=El nombre del equipo contiene caracteres inválidos.
|
||||
username_password_incorrect=Nombre de usuario o contraseña incorrectos.
|
||||
enterred_invalid_repo_name=Por favor, asegúrate de que has introducido correctamente el nombre del repositorio.
|
||||
enterred_invalid_owner_name=Por favor, asegúrate de que has introducido correctamente el nombre del propietario.
|
||||
@@ -219,14 +230,11 @@ still_own_repo=Tu cuenta es la propietaria de uno o más repositorios, tienes qu
|
||||
still_has_org=Tu cuenta es miembro de una o más organizaciones, tienes que abandonarlas o eliminarlas primero.
|
||||
org_still_own_repo=Esta organización es dueña de uno o más repositorios, tienes que eliminarlos o transferirlos primero.
|
||||
|
||||
still_own_user=Esta autenticación está en uso por algunos usuarios, debes moverlos y antes de eliminarla.
|
||||
|
||||
target_branch_not_exist=La rama de destino no existe
|
||||
|
||||
[user]
|
||||
change_avatar=Cambia tu avatar en gravatar.com
|
||||
change_custom_avatar=Cambia tu avatar en la configuración
|
||||
join_on=Registrado en
|
||||
change_avatar=Cambiar tu avatar
|
||||
join_on=Registrado el
|
||||
repositories=Repositorios
|
||||
activity=Actividad pública
|
||||
followers=Seguidores
|
||||
@@ -241,6 +249,7 @@ form.name_pattern_not_allowed=El patrón de nombre de usuario '%s' no está perm
|
||||
[settings]
|
||||
profile=Perfil
|
||||
password=Contraseña
|
||||
avatar=Avatar
|
||||
ssh_keys=Claves SSH
|
||||
social=Redes Sociales
|
||||
applications=Aplicaciones
|
||||
@@ -248,25 +257,26 @@ orgs=Organizaciones
|
||||
delete=Eliminar cuenta
|
||||
uid=UUID
|
||||
|
||||
public_profile=Perfil Público
|
||||
public_profile=Perfil público
|
||||
profile_desc=Tu correo electrónico es público y será usado para todas las notificaciones relacionadas con cualquier cuenta y cualquier operación hecha a través de la web.
|
||||
password_username_disabled=Los usuarios que no son locales no tienen permitido cambiar su nombre de usuario.
|
||||
full_name=Nombre Completo
|
||||
website=Página Web
|
||||
full_name=Nombre completo
|
||||
website=Página web
|
||||
location=Localización
|
||||
update_profile=Actualizar Perfil
|
||||
update_profile=Actualizar perfil
|
||||
update_profile_success=Tu perfil se ha actualizado correctamente.
|
||||
change_username=Nombre de usuario modificado
|
||||
change_username_prompt=Este cambio afectará a los enlaces que hacen referencia a su cuenta.
|
||||
continue=Continuar
|
||||
cancel=Cancelar
|
||||
|
||||
enable_custom_avatar=Activar Avatar Personalizado
|
||||
enable_custom_avatar_helper=Activa esto para desactivar los avatares de Gravatar
|
||||
lookup_avatar_by_mail=Buscar avatar por correo
|
||||
federated_avatar_lookup=Búsqueda de Avatar Federado
|
||||
enable_custom_avatar=Activar avatar personalizado
|
||||
choose_new_avatar=Selecciona nuevo avatar
|
||||
update_avatar=Actualizar Configuración del Avatar
|
||||
update_avatar=Actualizar configuración del avatar
|
||||
delete_current_avatar=Eliminar avatar
|
||||
uploaded_avatar_not_a_image=El archivo enviado no es una imagen.
|
||||
no_custom_avatar_available=No hay ningún avatar personalizado disponible, no se puede habilitar.
|
||||
update_avatar_success=La configuración de tu avatar se ha actualizado correctamente.
|
||||
|
||||
change_password=Cambiar contraseña
|
||||
@@ -328,6 +338,10 @@ access_token_deletion=Borrado de Token de Acceso Personal
|
||||
access_token_deletion_desc=Si elimina este token de acceso personal la aplicación asociada perderá el permiso de acceso. ¿Desea continuar?
|
||||
delete_token_success=¡El token de acceso personal ha sido eliminado con éxito! No se olvide de actualizar también las aplicaciones asociadas.
|
||||
|
||||
orgs.none=No eres un miembro de ninguna organización.
|
||||
orgs.leave_title=Salir de una organización
|
||||
orgs.leave_desc=Perderá el acceso a todos los repositorios y equipos después dejar la organización. ¿Desea continuar?
|
||||
|
||||
delete_account=Elimina tu cuenta
|
||||
delete_prompt=La operación eliminará tu cuenta de forma permanente y ¡<strong>NO</strong> se puede deshacer!
|
||||
confirm_delete_account=Confirmar Eliminación
|
||||
@@ -336,10 +350,10 @@ delete_account_desc=Esta cuenta se va a eliminar permanentemente, ¿quieres cont
|
||||
|
||||
[repo]
|
||||
owner=Propietario
|
||||
repo_name=Nombre del Repositorio
|
||||
repo_name=Nombre del repositorio
|
||||
repo_name_helper=Los grandes nombres de repositorios son cortos, memorables y <strong>únicos</strong>.
|
||||
visibility=Visibilidad
|
||||
visiblity_helper=Este repositorio es <span class="ui red text">Privado</span>
|
||||
visiblity_helper=Este repositorio es <span class="ui red text">privado</span>
|
||||
visiblity_helper_forced=El administrador web ha obligado a todos los repositorios nuevos a ser <span class="ui red text"> privados</span>
|
||||
visiblity_fork_helper=(Este cambio afectará a todos los forks)
|
||||
clone_helper=¿Necesitas ayuda con el clone? ¡Consulta la <a target="_blank" href="%s">Ayuda</a>!
|
||||
@@ -348,17 +362,20 @@ fork_from=Crear un Fork desde
|
||||
fork_visiblity_helper=No es posible cambiar la visibilidad de un Fork
|
||||
repo_desc=Descripción
|
||||
repo_lang=Idioma
|
||||
repo_lang_helper=Seleccione archivo .gitignore
|
||||
repo_gitignore_helper=Seleccionar plantillas de .gitignore
|
||||
license=Licencia
|
||||
license_helper=Selecciona un fichero de licencia
|
||||
readme=Readme
|
||||
readme_helper=Seleccione una plantilla de archivo readme
|
||||
auto_init=Inicializar los archivos seleccionados y plantillas de este repositorio
|
||||
create_repo=Crear Repositorio
|
||||
create_repo=Crear repositorio
|
||||
default_branch=Rama por defecto
|
||||
mirror_interval=Intervalo de mirror(en horas)
|
||||
mirror_prune=Purgar
|
||||
mirror_prune_desc=Remover referencias remotas que no existan remotamente
|
||||
mirror_interval=Intervalo de la réplica (en horas)
|
||||
mirror_address=Dirección de la réplica
|
||||
mirror_address_desc=Por favor, incluya las credenciales de usuario necesarias en la dirección.
|
||||
mirror_last_synced=Última sincronización
|
||||
watchers=Seguidores
|
||||
stargazers=Fans
|
||||
forks=Forks
|
||||
@@ -367,37 +384,38 @@ form.reach_limit_of_creation=El propietario ha alcanzado el límite máximo de %
|
||||
form.name_reserved=El nombre del repositorio '%s' está reservado.
|
||||
form.name_pattern_not_allowed=El patrón del nombre del repositorio '%s' no está permitido.
|
||||
|
||||
need_auth=Requiere Autorización
|
||||
migrate_type=Tipo de Migración
|
||||
migrate_type_helper=Este repositorio será un <span class="text blue">mirror</span>
|
||||
need_auth=Requiere autorización
|
||||
migrate_type=Tipo de migración
|
||||
migrate_type_helper=Este repositorio será una <span class="text blue">réplica</span>
|
||||
migrate_repo=Migrar Repositorio
|
||||
migrate.clone_address=Clonar Dirección
|
||||
migrate.clone_address_desc=Puede ser una URL HTTP/HTTPS/GIT o una ruta local del servidor.
|
||||
migrate.clone_address=Clonar dirección
|
||||
migrate.clone_address_desc=Esto puede ser una dirección URL HTTP/HTTPS/GIT.
|
||||
migrate.clone_address_desc_import_local=También se le permite migrar un repositorio por la ruta del servidor local.
|
||||
migrate.permission_denied=No te está permitido importar repositorios locales.
|
||||
migrate.invalid_local_path=Rutal local inválida, no existe o no es un directorio.
|
||||
migrate.failed=Migración fallida: %v
|
||||
|
||||
mirror_from=espejo de
|
||||
forked_from=forked de
|
||||
fork_from_self=Eres el propietario del repositorio, ¡no puedes hacer fork!
|
||||
fork_from_self=¡No puedes crear un fork de un repositorio que ya es tuyo!
|
||||
copy_link=Copiar
|
||||
copy_link_success=¡Copiado!
|
||||
copy_link_error=Presione ⌘ + C o Ctrl-C para copiar
|
||||
copied=Copiado correctamente
|
||||
unwatch=Dejar de vigilar
|
||||
watch=Vigilar
|
||||
watch=Seguir
|
||||
unstar=Eliminar destacado
|
||||
star=Destacar
|
||||
fork=Fork
|
||||
|
||||
no_desc=Sin Descripción
|
||||
no_desc=Sin descripción
|
||||
quick_guide=Guía Rápida
|
||||
clone_this_repo=Clonar este repositorio
|
||||
create_new_repo_command=Crear un nuevo repositorio desde línea de comandos
|
||||
push_exist_repo=Hacer Push de un repositorio existente desde línea de comandos
|
||||
repo_is_empty=Este repositorio está vacío, por favor, ¡vuelva más tarde!
|
||||
|
||||
code=Código
|
||||
files=Archivos
|
||||
branch=Rama
|
||||
tree=Árbol
|
||||
filter_branch_and_tag=Filtrar por rama o etiqueta
|
||||
@@ -413,9 +431,50 @@ file_raw=Raw
|
||||
file_history=Histórico
|
||||
file_view_raw=Ver Raw
|
||||
file_permalink=Permalink
|
||||
file_too_large=Este archivo es demasiado grande para ser mostrado
|
||||
video_not_supported_in_browser=Su navegador no soporta el tag video de HTML5.
|
||||
|
||||
editor.new_file=Nuevo archivo
|
||||
editor.upload_file=Subir archivo
|
||||
editor.edit_file=Editar archivo
|
||||
editor.preview_changes=Vista previa de los cambios
|
||||
editor.cannot_edit_non_text_files=Sólo puede editar archivos de texto
|
||||
editor.edit_this_file=Editar este archivo
|
||||
editor.must_be_on_a_branch=Debes estar en una rama para hacer o proponer cambios en este archivo
|
||||
editor.fork_before_edit=Debes hacer un fork de este repositorio antes de editar el archivo
|
||||
editor.delete_this_file=Eliminar este archivo
|
||||
editor.must_have_write_access=Debes tener permisos de escritura para hacer o proponer cambios a este archivo
|
||||
editor.file_delete_success=¡El archivo '%s' ha sido eliminado con éxito!
|
||||
editor.name_your_file=Nombre de archivo...
|
||||
editor.filename_help=Para añadir un directorio, simplemente escribelo y presiona /. Para eliminar un directorio, ve al principio del campo y presiona retroceso.
|
||||
editor.or=o
|
||||
editor.cancel_lower=cancelar
|
||||
editor.commit_changes=Hacer commit de los cambios
|
||||
editor.add_tmpl=Añadir '%s<filename>'
|
||||
editor.add=Añadir '%s'
|
||||
editor.update=Actualizar '%s'
|
||||
editor.delete=Eliminar '%s'
|
||||
editor.commit_message_desc=Añadir una descripción extendida opcional...
|
||||
editor.commit_directly_to_this_branch=Hacer commit directamente en la rama <strong class="branch-name">%s</strong>.
|
||||
editor.create_new_branch=Crear una <strong>nueva rama</strong> para este commit y hacer un pull request.
|
||||
editor.new_branch_name_desc=Nombre de la rama nueva...
|
||||
editor.cancel=Cancelar
|
||||
editor.filename_cannot_be_empty=El nombre del archivo no puede estar vacío.
|
||||
editor.branch_already_exists=La rama '%s' ya existe en este repositorio.
|
||||
editor.directory_is_a_file=La entrada '%s' en el directorio padre es un archivo no un directorio en este repositorio.
|
||||
editor.file_is_a_symlink=El archivo '%s' es un enlace simbólico que no puede ser modificado desde el editor de la web.
|
||||
editor.filename_is_a_directory=El nombre del fichero '%s' es un directorio existente en este repositorio.
|
||||
editor.file_editing_no_longer_exists=El archivo '%s' que estás editando ya no existe en este repositorio.
|
||||
editor.file_changed_while_editing=El contenido del archivo ha sido modificado desde que empezó a editarlo. <a target="_blank" href="%s">Clic aquí</a> para ver qué ha sido modificado o <strong>presiona confirmar de nuevo</strong> para sobrescribir estos cambios.
|
||||
editor.file_already_exists=Ya existe un archivo con nombre '%s' en este repositorio.
|
||||
editor.no_changes_to_show=No existen cambios para mostrar.
|
||||
editor.fail_to_update_file=Error al actualizar/crear el archivo '%s', error: %v
|
||||
editor.add_subdir=Añadir subdirectorio...
|
||||
editor.unable_to_upload_files=Error al subir archivos a '%s', error: %v
|
||||
editor.upload_files_to_dir=Subir archivos a '%s'
|
||||
|
||||
commits.commits=Commits
|
||||
commits.search=Buscar Commits
|
||||
commits.search=Buscar commits
|
||||
commits.find=Buscar
|
||||
commits.author=Autor
|
||||
commits.message=Mensaje
|
||||
@@ -423,7 +482,7 @@ commits.date=Fecha
|
||||
commits.older=Anterior
|
||||
commits.newer=Posterior
|
||||
|
||||
issues.new=Nueva Incidencia
|
||||
issues.new=Nueva incidencia
|
||||
issues.new.labels=Etiquetas
|
||||
issues.new.no_label=Sin etiquetas
|
||||
issues.new.clear_labels=Limpiar etiquetas
|
||||
@@ -439,6 +498,11 @@ issues.create=Crear incidencia
|
||||
issues.new_label=Nueva Etiqueta
|
||||
issues.new_label_placeholder=Nombre etiqueta...
|
||||
issues.create_label=Crear etiqueta
|
||||
issues.label_templates.title=Carga un conjunto predefinido de etiquetas
|
||||
issues.label_templates.info=Tdavía no hay ninguna etiqueta. Puede hacer clic en el botón "Nueva etiqueta" para crear una o utilizar un conjunto predefinido abajo.
|
||||
issues.label_templates.helper=Seleccionar un conjunto de etiquetas
|
||||
issues.label_templates.use=Usar este conjunto de etiquetas
|
||||
issues.label_templates.fail_to_load_file=Error al cargar el archivo de plantilla de etiqueta '%s': %v
|
||||
issues.open_tab=%d abiertas
|
||||
issues.close_tab=%d cerradas
|
||||
issues.filter_label=Etiqueta
|
||||
@@ -466,21 +530,21 @@ issues.next=Página Siguiente
|
||||
issues.open_title=Abierta
|
||||
issues.closed_title=Cerrada
|
||||
issues.num_comments=%d comentarios
|
||||
issues.commented_at=`comentada <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.no_content=Aun no existe contenido.
|
||||
issues.commented_at=`comentado <a href="#%s"> %s</a>`
|
||||
issues.delete_comment_confirm=¿Seguro que deseas eliminar este comentario?
|
||||
issues.no_content=Aún no existe contenido.
|
||||
issues.close_issue=Cerrar
|
||||
issues.close_comment_issue=Comentar y cerrar
|
||||
issues.reopen_issue=Reabrir
|
||||
issues.reopen_comment_issue=Comentar y reabrir
|
||||
issues.create_comment=Comentar
|
||||
issues.closed_at=`cerrada <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at=`reabierta <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.closed_at=`cerró <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at=`reabrió <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commit_ref_at=`mencionada esta incidencia en un commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.poster=Autor
|
||||
issues.admin=Administrador
|
||||
issues.collaborator=Colaborador
|
||||
issues.owner=Propietario
|
||||
issues.sign_up_for_free=Registro gratuito
|
||||
issues.sign_in_require_desc=para unirse a esta conversación. ¿Ya dispone de una cuenta? <a href="%s">Inicie sesión para comentar</a>
|
||||
issues.sign_in_require_desc=<a href="%s"> Inicie sesión</a> para unirse a esta conversación.
|
||||
issues.edit=Editar
|
||||
issues.cancel=Cancelar
|
||||
issues.save=Guardar
|
||||
@@ -494,9 +558,12 @@ issues.label_modify=Edición de Etiqueta
|
||||
issues.label_deletion=Borrado de Etiqueta
|
||||
issues.label_deletion_desc=Al borrar la etiqueta su información será eliminada de todas las incidencias relacionadas. Desea continuar?
|
||||
issues.label_deletion_success=Etiqueta borrada con éxito!
|
||||
issues.num_participants=%d participantes
|
||||
issues.attachment.open_tab='Haga clic para ver "%s" en una pestaña nueva'
|
||||
issues.attachment.download=`Haga clic para descargar "%s"`
|
||||
|
||||
pulls.new=Nuevo Pull Request
|
||||
pulls.compare_changes=Comparar Cambios
|
||||
pulls.compare_changes=Comparar cambios
|
||||
pulls.compare_changes_desc=Comparar dos ramas y generar un pull request con las diferencias.
|
||||
pulls.compare_base=base
|
||||
pulls.compare_compare=comparar con
|
||||
@@ -510,9 +577,9 @@ pulls.merged_title_desc=fusionados %[1]d commits de <code>%[2]s</code> en <code>
|
||||
pulls.tab_conversation=Conversación
|
||||
pulls.tab_commits=Commits
|
||||
pulls.tab_files=Archivos modificados
|
||||
pulls.reopen_to_merge=Por favor reabra este pull request para proceder con la operación de fusionado.
|
||||
pulls.merged=Fuisionado
|
||||
pulls.has_merged=¡Este pull request se ha completado con éxito!
|
||||
pulls.reopen_to_merge=Por favor reabra este Pull Request para proceder con la operación de fusionado.
|
||||
pulls.merged=Fusionado
|
||||
pulls.has_merged=¡Este Pull Request se ha completado con éxito!
|
||||
pulls.data_broken=Los datos de este pull request ya no están disponibles porque se ha eliminado la información del fork.
|
||||
pulls.is_checking=Se está procediendo a la búsqueda de conflictos, por favor actualice la página en unos momentos.
|
||||
pulls.can_auto_merge_desc=Este Pull Request puede ser fusionado automáticamente.
|
||||
@@ -520,6 +587,8 @@ pulls.cannot_auto_merge_desc=Este Pull Request no puede ser fusionado automátic
|
||||
pulls.cannot_auto_merge_helper=Por favor, fusiona manualmente para resolver los conflictos.
|
||||
pulls.merge_pull_request=Fusionar Pull Request
|
||||
pulls.open_unmerged_pull_exists=`Usted no puede realizar la operación de reapertura porque en estos momentos existe una solicitud de pull request (#%d) para el mismo repositorio con la misma información que se encuentra a la espera de aprobación`
|
||||
pulls.delete_branch=Eliminar la rama
|
||||
pulls.delete_branch_has_new_commits=La rama no se puede eliminar porque tiene nuevos commits después de la fusión.
|
||||
|
||||
milestones.new=Nuevo Milestone
|
||||
milestones.open_tab=%d abiertas
|
||||
@@ -557,6 +626,8 @@ wiki.save_page=Guardar página
|
||||
wiki.last_commit_info=%s editó esta página %s
|
||||
wiki.edit_page_button=Editar
|
||||
wiki.new_page_button=Nueva página
|
||||
wiki.delete_page_button=Eliminar página
|
||||
wiki.delete_page_notice_1=Esto eliminará la página <code>"%s"</code>. Por favor, asegúrate de que es lo que quieres.
|
||||
wiki.page_already_exists=Ya existe una página con el mismo nombre.
|
||||
wiki.pages=Páginas
|
||||
wiki.last_updated=Última actualización %s
|
||||
@@ -564,46 +635,71 @@ wiki.last_updated=Última actualización %s
|
||||
settings=Configuración
|
||||
settings.options=Opciones
|
||||
settings.collaboration=Colaboración
|
||||
settings.collaboration.admin=Administrador
|
||||
settings.collaboration.write=Escritura
|
||||
settings.collaboration.read=Lectura
|
||||
settings.collaboration.undefined=Indefinido
|
||||
settings.hooks=Webhooks
|
||||
settings.githooks=Git Hooks
|
||||
settings.basic_settings=Configuración Básica
|
||||
settings.site=Sitio Oficial
|
||||
settings.mirror_settings=Configuración de réplica
|
||||
settings.sync_mirror=Sincronizar ahora
|
||||
settings.mirror_sync_in_progress=Sincronización de réplica en curso, por favor actualice la página en unos minutos.
|
||||
settings.site=Sitio oficial
|
||||
settings.update_settings=Actualizar configuración
|
||||
settings.change_reponame_prompt=Este cambio afectará a los enlaces al repositorio.
|
||||
settings.advanced_settings=Ajustes avanzados
|
||||
settings.wiki_desc=Habilitar la Wiki para que los colaboradores documenten
|
||||
settings.wiki_desc=Activar sistema de wiki
|
||||
settings.use_internal_wiki=Usar wiki integrada
|
||||
settings.use_external_wiki=Usar Wiki externa
|
||||
settings.external_wiki_url=URL externa de la Wiki
|
||||
settings.external_wiki_url_desc=Los visitantes serán redireccionados a la URL cuando hagan click en la barra.
|
||||
settings.issues_desc=Habilitar tracker ligero de incidencias
|
||||
settings.issues_desc=Habilitar rastreo de incidencias
|
||||
settings.use_internal_issue_tracker=Usar rastreo de incidencias ligero incluido
|
||||
settings.use_external_issue_tracker=Usar tracker externo de incidencias
|
||||
settings.external_tracker_url=External Issue Tracker URL
|
||||
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
|
||||
settings.tracker_url_format=Formato URL del tracker de incidencias externo
|
||||
settings.tracker_issue_style=Estilo de etiquetado del tracker externo de incidencias:
|
||||
settings.tracker_issue_style.numeric=Numérico
|
||||
settings.tracker_issue_style.alphanumeric=Alfanumérico
|
||||
settings.tracker_url_format_desc=Puedes usar las plantillas <code>{user} {repo} {index}</code> para el nombre de usuario, nombre del repositorio e índice de la incidencia.
|
||||
settings.pulls_desc=Habilitar Pull Requests para aceptar contribuciones públicas
|
||||
settings.danger_zone=Zona de Peligro
|
||||
settings.transfer=Transferir la Propiedad
|
||||
settings.transfer_desc=Transferir este repositorio a otro usuario u organización donde tengas permisos de administración.
|
||||
settings.new_owner_has_same_repo=El nuevo propietario tiene un repositorio con el mismo nombre.
|
||||
settings.delete=Eliminar este Repositorio
|
||||
settings.delete_desc=Una vez has eliminado un repositorio, no hay vuelta atrás. Por favor, asegúrate de que es lo que quieres.
|
||||
settings.convert=Convertir en un repositorio normal
|
||||
settings.convert_desc=Puedes convertir este repositorio en un repositorio normal. Este cambio no se puede deshacer.
|
||||
settings.convert_notices_1=- Esta operación convertirá este repositorio espejo en un repositorio normal y no podrá deshacerse.
|
||||
settings.convert_confirm=Confirmar conversión
|
||||
settings.convert_succeed=El repositorio ha sido convertido en normal satisfactoriamente.
|
||||
settings.transfer=Transferir la propiedad
|
||||
settings.transfer_desc=Transferir este repositorio a otro usuario u organización donde tengas permisos de administración.
|
||||
settings.transfer_notices_1=- Perderá el permiso de acceso si el nuevo propietario es otro usuario.
|
||||
settings.transfer_notices_2=- Conservará el privilegio de acceso si el nuevo propietario es una organización y usted es uno de los propietarios de dicha organización.
|
||||
settings.transfer_form_title=Por favor introduzca esta información para confirmar la operación:
|
||||
settings.wiki_delete=Eliminar datos de la wiki
|
||||
settings.wiki_delete_desc=Una vez borrados los datos de la wiki no habrá vuelta atrás. Por favor, asegúrate de que es lo que quieres.
|
||||
settings.wiki_delete_notices_1=- Esto eliminará y deshabilitará la wiki para %s
|
||||
settings.wiki_deletion_success=Los datos de la wiki del repositorio han sido borrados correctamente.
|
||||
settings.delete=Eliminar este repositorio
|
||||
settings.delete_desc=Una vez has eliminado un repositorio, no hay vuelta atrás. Por favor, asegúrate de que es lo que quieres.
|
||||
settings.delete_notices_1=- Esta operación <strong>NO PUEDE</strong> revertirse.
|
||||
settings.delete_notices_2=- Esta operación eliminará de manera permanente todo el contenido de este repositorio, incluyendo los datos de git, las incidencias, los comentarios y los permisos de acceso de los colaboradores.
|
||||
settings.delete_notices_fork_1=- Si este repositorio es público, todos los forks se convertirán en repositorios independientes tras el borrado.
|
||||
settings.delete_notices_fork_2=- Si este repositorio es privado, todos los forks serán eliminados simultáneamente.
|
||||
settings.delete_notices_fork_3=- Si desea mantener los forks tras el borrado, por favor convierta este repositorio en público antes de proceder.
|
||||
settings.delete_notices_fork_1=- Todos los forks se convertirán en independientes tras el borrado.
|
||||
settings.deletion_success=¡El respositorio ha sido eliminado satisfactoriamente!
|
||||
settings.update_settings_success=Las opciones del repositorio se han actualizado correctamente.
|
||||
settings.transfer_owner=Nuevo Propietario
|
||||
settings.make_transfer=Transferir
|
||||
settings.transfer_succeed=La propiedad del repositorio ha sido transferida exitosamente.
|
||||
settings.confirm_delete=Confirmar eliminación
|
||||
settings.add_collaborator=Añadir Nuevo Colaborador
|
||||
settings.add_collaborator_success=Se ha añadido el nuevo colaborador.
|
||||
settings.remove_collaborator_success=Se ha eliminado el colaborador.
|
||||
settings.add_collaborator=Añadir nuevo colaborador
|
||||
settings.add_collaborator_success=El nuevo colaborador ha sido añadido.
|
||||
settings.delete_collaborator=Eliminar
|
||||
settings.collaborator_deletion=Eliminar colaborador
|
||||
settings.collaborator_deletion_desc=Este usuario no podrá colaborar en este repositorio tras eliminarlo. ¿Desea continuar?
|
||||
settings.remove_collaborator_success=El colaborador ha sido eliminado.
|
||||
settings.search_user_placeholder=Buscar usuario...
|
||||
settings.org_not_allowed_to_be_collaborator=Las organizaciones no tiene permitido ser añadidas como colaboradores.
|
||||
settings.user_is_org_member=El usuario es miembro de la organización, no puede ser añadido como colaborador.
|
||||
settings.add_webhook=Añadir Webhook
|
||||
settings.hooks_desc=Los Webhooks permiten a servicios externos recibir notificaciones cuando sucedan ciertos eventos en Gogs. Cuando sucedan los eventos especificados, enviaremos una petición POST a cada una de las URLs indicadas. Para obtener más información, consulta nuestra <a target="_blank" href="%s">Guía de Webhooks</a>.
|
||||
@@ -625,7 +721,7 @@ settings.githook_content=Contenido del Hook
|
||||
settings.update_githook=Actualizar Hook
|
||||
settings.add_webhook_desc=Enviaremos una petición <code>POST</code> a la siguiente URL con los detalles de cualquier evento suscrito. También puedes especificar qué formato de datos te gustaría recibir (JSON, <code>x-www-form-urlencoded</code>, <em>etc</em>). Puedes encontrar más información en la <a target="_blank" href="%s">Guía de Webhooks</a>.
|
||||
settings.payload_url=URL de Payload
|
||||
settings.content_type=Tipo de Contenido
|
||||
settings.content_type=Tipo de contenido
|
||||
settings.secret=Secreto
|
||||
settings.slack_username=Nombre de usuario
|
||||
settings.slack_icon_url=URL de icono
|
||||
@@ -636,6 +732,8 @@ settings.event_send_everything=Necesito <strong>todo</strong>.
|
||||
settings.event_choose=Déjeme elegir lo que necesito.
|
||||
settings.event_create=Crear
|
||||
settings.event_create_desc=Rama o etiqueta creada
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request, abierta, cerrada, reabierta, editada, asignada, desasignada, con etiqueta actualizada, con etiqueta eliminada, o sincronizada.
|
||||
settings.event_push=Push
|
||||
settings.event_push_desc=Git push a un repositorio
|
||||
settings.active=Activo
|
||||
@@ -667,12 +765,14 @@ diff.browse_source=Explorar el Código
|
||||
diff.parent=padre
|
||||
diff.commit=commit
|
||||
diff.data_not_available=Los datos del Diff no están disponibles.
|
||||
diff.show_diff_stats=Mostrar Estadísticas de Diff
|
||||
diff.show_diff_stats=Mostrar estadísticas de diff
|
||||
diff.show_split_view=Dividir vista
|
||||
diff.show_unified_view=Unificar vista
|
||||
diff.stats_desc=Se han <strong>modificado %d ficheros</strong> con <strong>%d adiciones</strong> y <strong>%d borrados</strong>
|
||||
diff.bin=BIN
|
||||
diff.view_file=Ver Fichero
|
||||
diff.view_file=Ver fichero
|
||||
diff.file_suppressed=La diferencia del archivo ha sido suprimido porque es demasiado grande
|
||||
diff.too_many_files=Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio
|
||||
|
||||
release.releases=Releases
|
||||
release.new_release=Nueva Release
|
||||
@@ -690,7 +790,7 @@ release.tag_helper=Escoge una etiqueta o crea una nueva al publicar.
|
||||
release.title=Título
|
||||
release.content=Contenido
|
||||
release.write=Escribir
|
||||
release.preview=Vista Previa
|
||||
release.preview=Vista previa
|
||||
release.loading=Cargando...
|
||||
release.prerelease_desc=Esta es una pre-release
|
||||
release.prerelease_helper=Esta release está marcada como no apta para producción.
|
||||
@@ -703,11 +803,12 @@ release.deletion=Eliminar Release
|
||||
release.deletion_desc=Eliminar este Release eliminará la etiqueta correspondiente. ¿Desea continuar?
|
||||
release.deletion_success=¡El release ha sido eliminado correctamente!
|
||||
release.tag_name_already_exist=Ya existe una Release con esta etiqueta.
|
||||
release.tag_name_invalid=Nombre de la etiqueta no es válido.
|
||||
release.downloads=Descargas
|
||||
|
||||
[org]
|
||||
org_name_holder=Nombre de la Organización
|
||||
org_full_name_holder=Nombre de la organización
|
||||
org_name_holder=Nombre de la organización
|
||||
org_full_name_holder=Nombre completo de la organización
|
||||
org_name_helper=Los grandes nombres de organizaciones son cortos y memorables.
|
||||
create_org=Crear Organización
|
||||
repo_updated=Actualizado
|
||||
@@ -716,9 +817,9 @@ invite_someone=Invitar a alguien
|
||||
teams=Equipos
|
||||
lower_members=miembros
|
||||
lower_repositories=repositorios
|
||||
create_new_team=Crear un Nuevo Equipo
|
||||
create_new_team=Crear un nuevo equipo
|
||||
org_desc=Descripción
|
||||
team_name=Nombre del Equipo
|
||||
team_name=Nombre del equipo
|
||||
team_desc=Descripción
|
||||
team_name_helper=Utiliza este nombre para mencionar a este equipo en las conversaciones.
|
||||
team_desc_helper=¿En qué consiste este equipo?
|
||||
@@ -729,8 +830,8 @@ form.name_pattern_not_allowed=El patrón de nombre de la organización '%s' no e
|
||||
|
||||
settings=Configuración
|
||||
settings.options=Opciones
|
||||
settings.full_name=Nombre Completo
|
||||
settings.website=Página Web
|
||||
settings.full_name=Nombre completo
|
||||
settings.website=Página web
|
||||
settings.location=Localización
|
||||
settings.update_settings=Actualizar configuración
|
||||
settings.update_setting_success=La configuración de la organización se ha actualizado correctamente.
|
||||
@@ -763,24 +864,24 @@ teams.read_access=Acceso de Lectura
|
||||
teams.read_access_helper=Este equipo podrá ver y clonar sus repositorios.
|
||||
teams.write_access=Acceso de Escritura
|
||||
teams.write_access_helper=Este equipo podrá leer sus repositorios, así como hacer push en ellos.
|
||||
teams.admin_access=Acceso de Administrador
|
||||
teams.admin_access=Acceso de administrador
|
||||
teams.admin_access_helper=Este equipo podrá hacer push/pull en sus repositorios, así como añadir colaboradores a ellos.
|
||||
teams.no_desc=Este equipo no tiene descripción
|
||||
teams.settings=Configuración
|
||||
teams.owners_permission_desc=Los propietarios tienen acceso completo a <strong>todos los repositorios</strong> y tienen <strong>derechos de administración</strong> en la organización.
|
||||
teams.members=Miembros del Equipo
|
||||
teams.members=Miembros del equipo
|
||||
teams.update_settings=Actualizar configuración
|
||||
teams.delete_team=Borrar este Equipo
|
||||
teams.add_team_member=Añadir Miembro al Equipo
|
||||
teams.delete_team_title=Eliminar Equipo
|
||||
teams.delete_team=Eliminar este equipo
|
||||
teams.add_team_member=Añadir miembro al equipo
|
||||
teams.delete_team_title=Eliminar equipo
|
||||
teams.delete_team_desc=Este equipo va a ser eliminado, ¿seguro que quieres continuar? Los miembros de este equipo pueden perder acceso a algunos repositorios.
|
||||
teams.delete_team_success=El Equipo se ha eliminado correctamente.
|
||||
teams.delete_team_success=El equipo ha sido eliminado correctamente.
|
||||
teams.read_permission_desc=Este equipo tiene permisos de <strong>Lectura</strong>: sus miembros pueden ver y clonar los repositorios del equipo.
|
||||
teams.write_permission_desc=Este equipo tiene permisos de <strong>Escritura</strong>: sus miembros pueden leer y hacer push a los repositorios del equipo.
|
||||
teams.admin_permission_desc=Este equipo tiene permisos de <strong>Administración</strong>: sus miembros pueden leer, hacer push y añadir colaboradores a los repositorios del equipo.
|
||||
teams.repositories=Repositorios del Equipo
|
||||
teams.repositories=Repositorios del equipo
|
||||
teams.search_repo_placeholder=Buscar repositorio...
|
||||
teams.add_team_repository=Añadir Repositorio al Equipo
|
||||
teams.add_team_repository=Añadir repositorio al equipo
|
||||
teams.remove_repo=Eliminar
|
||||
teams.add_nonexistent_repo=El repositorio que estás intentando añadir no existe, por favor, créalo primero.
|
||||
|
||||
@@ -791,7 +892,7 @@ organizations=Organizaciones
|
||||
repositories=Repositorios
|
||||
authentication=Autenticaciones
|
||||
config=Configuración
|
||||
notices=Avisos del Sistema
|
||||
notices=Notificaciones del sistema
|
||||
monitor=Monitorización
|
||||
first_page=Primera
|
||||
last_page=Última
|
||||
@@ -801,7 +902,7 @@ dashboard.statistic=Estadísticas
|
||||
dashboard.operations=Operaciones
|
||||
dashboard.system_status=Estado del Monitor del Sistema
|
||||
dashboard.statistic_info=La base de datos de Gogs contiene <b>%d</b> usuarios, <b>%d</b> organizaciones, <b>%d</b> claves públicas, <b>%d</b> repositorios, <b>%d</b> vigilados, <b>%d</b> destacados, <b>%d</b> acciones, <b>%d</b> accesos, <b>%d</b> incidencias, <b>%d</b> comentarios, <b>%d</b> cuentas de redes sociales, <b>%d</b> seguidores, <b>%d</b> mirrors, <b>%d</b> releases, <b>%d</b> fuentes de login, <b>%d</b> webhooks, <b>%d</b> milestones, <b>%d</b> etiquetas, <b>%d</b> hooks, <b>%d</b> equipos, <b>%d</b> tareas actualizadas, <b>%d</b> adjuntos.
|
||||
dashboard.operation_name=Nombre de la Operación
|
||||
dashboard.operation_name=Nombre de la operación
|
||||
dashboard.operation_switch=Interruptor
|
||||
dashboard.operation_run=Ejecutar
|
||||
dashboard.clean_unbind_oauth=Limpiar solicitudes de OAuth sin confirmar
|
||||
@@ -818,13 +919,15 @@ dashboard.resync_all_sshkeys=Reescribir el fichero '.ssh/authorized_keys'(atenci
|
||||
dashboard.resync_all_sshkeys_success=Todas las claves públicas se han reescrito correctamente.
|
||||
dashboard.resync_all_update_hooks=Reescribir todos los hooks de actualización de los repositorios (necesario cuando se modifica la ruta de configuración personalizada)
|
||||
dashboard.resync_all_update_hooks_success=Todos los hooks de actualización de los repositorios se han reescrito correctamente.
|
||||
dashboard.reinit_missing_repos=Reinicializar todos los registros del repositorio que tienen archivos Git eliminados
|
||||
dashboard.reinit_missing_repos_success=Todos los registros del repositorio con archivos Git eliminados han sido reinicializados con éxito.
|
||||
|
||||
dashboard.server_uptime=Tiempo de actividad del servidor
|
||||
dashboard.current_goroutine=Gorutinas actuales
|
||||
dashboard.current_memory_usage=Uso de memoria actual
|
||||
dashboard.total_memory_allocated=Total de Memoria Reservada
|
||||
dashboard.memory_obtained=Memoria Obtenida
|
||||
dashboard.pointer_lookup_times=Tiempos de Búsqueda de Punteros
|
||||
dashboard.pointer_lookup_times=Tiempos de búsqueda de punteros
|
||||
dashboard.memory_allocate_times=Tiempos de Reserva de Memoria
|
||||
dashboard.memory_free_times=Tiempos de Liberado de Memoria
|
||||
dashboard.current_heap_usage=Uso de Heap actual
|
||||
@@ -861,13 +964,14 @@ users.new_success=La cuenta '%s' ha sido creada con éxito.
|
||||
users.edit=Editar
|
||||
users.auth_source=Fuente de Autenticación
|
||||
users.local=Local
|
||||
users.auth_login_name=Nombre de Inicio de Sesión de Autenticación
|
||||
users.auth_login_name=Nombre de Inicio de sesión de autenticación
|
||||
users.password_helper=Deje el campo vacío si no desea cambiar la contraseña.
|
||||
users.update_profile_success=El perfil de la cuenta se ha actualizado correctamente.
|
||||
users.edit_account=Editar Cuenta
|
||||
users.max_repo_creation=Límite máximo de repositorios
|
||||
users.max_repo_creation_desc=(Configura a -1 para usar el límite global por defecto)
|
||||
users.is_activated=Esta cuenta está activada
|
||||
users.prohibit_login=Esta cuenta no tiene permitido ingresar
|
||||
users.is_admin=Esta cuenta tiene permisos de administrador
|
||||
users.allow_git_hook=Esta cuenta tiene permisos para crear hooks de Git
|
||||
users.allow_import_local=Esta cuenta dispone de permisos para importar repositorios locales
|
||||
@@ -890,36 +994,38 @@ repos.watches=Vigilantes
|
||||
repos.stars=Estrellas
|
||||
repos.issues=Incidencias
|
||||
|
||||
auths.auth_manage_panel=Panel de Administración de Autenticación
|
||||
auths.new=Añadir Nuevo Origen
|
||||
auths.auth_manage_panel=Panel de administración de autenticación
|
||||
auths.new=Añadir nuevo origen
|
||||
auths.name=Nombre
|
||||
auths.type=Tipo
|
||||
auths.enabled=Activo
|
||||
auths.updated=Actualizado
|
||||
auths.auth_type=Tipo de Autenticación
|
||||
auths.auth_name=Nombre de Autenticación
|
||||
auths.auth_type=Tipo de autenticación
|
||||
auths.auth_name=Nombre de autenticación
|
||||
auths.security_protocol=Protocolo de seguridad
|
||||
auths.domain=Dominio
|
||||
auths.host=Host
|
||||
auths.port=Puerto
|
||||
auths.bind_dn=Bind DN
|
||||
auths.bind_password=Contraseña Bind
|
||||
auths.bind_password_helper=Advertencia: La contraseña se almacena como texto plano. No utilice una cuenta con privilegios elevados.
|
||||
auths.user_base=Base de Búsqueda de Usuarios
|
||||
auths.user_base=Base de búsqueda de usuarios
|
||||
auths.user_dn=DN de Usuario
|
||||
auths.attribute_username=Atributo de nombre de usuario
|
||||
auths.attribute_username_placeholder=Dejar vacío para usar el campo de inicio de sesión como nombre de usuario.
|
||||
auths.attribute_name=Atributo nombre
|
||||
auths.attribute_surname=Atributo apellido
|
||||
auths.attribute_mail=Atributo correo electrónico
|
||||
auths.filter=Filtro de Usuario
|
||||
auths.admin_filter=Filtro de Aministrador
|
||||
auths.attributes_in_bind=Buscar atributos en el contexto del Bind DN
|
||||
auths.filter=Filtro de usuario
|
||||
auths.admin_filter=Filtro de aministrador
|
||||
auths.ms_ad_sa=Ms Ad SA
|
||||
auths.smtp_auth=Tipo de Autenticación SMTP
|
||||
auths.smtp_auth=Tipo de autenticación SMTP
|
||||
auths.smtphost=SMTP Host
|
||||
auths.smtpport=Puerto SMTP
|
||||
auths.allowed_domains=Dominios Permitidos
|
||||
auths.allowed_domains_helper=Deje el campo vacío si no desea restringir ningún dominio. Para restringir más de uno, separe los dominios con una coma ','.
|
||||
auths.enable_tls=Habilitar Cifrado TLS
|
||||
auths.enable_tls=Habilitar cifrado TLS
|
||||
auths.skip_tls_verify=Omitir la verificación TLS
|
||||
auths.pam_service_name=Nombre del Servicio PAM
|
||||
auths.enable_auto_register=Hablilitar Auto-Registro
|
||||
@@ -932,9 +1038,11 @@ auths.update=Actualizar la configuración de autenticación
|
||||
auths.delete=Eliminar Autenticación
|
||||
auths.delete_auth_title=Borrado de autenticación
|
||||
auths.delete_auth_desc=Esta autenticación será eliminada. ¿Deseas continuar?
|
||||
auths.still_in_used=Este método de autentificación aún es utilizado por algunos usuarios, por favor elimine o convierta estos usuarios a otro tipo de autentificación.
|
||||
auths.deletion_success=¡La autenticación ha sido eliminada con éxito!
|
||||
auths.login_source_exist=El origen de autenticación '%s' ya existe.
|
||||
|
||||
config.server_config=Configuración del Servidor
|
||||
config.server_config=Configuración del servidor
|
||||
config.app_name=Nombre de la Aplicación
|
||||
config.app_ver=Versión de la Aplicación
|
||||
config.app_url=URL de la Aplicación
|
||||
@@ -943,47 +1051,69 @@ config.offline_mode=Modo Sin Conexión
|
||||
config.disable_router_log=Deshabilitar Log del Router
|
||||
config.run_user=Ejecutada como Usuario
|
||||
config.run_mode=Modo de ejecución
|
||||
config.git_version=Git Version
|
||||
config.repo_root_path=Ruta del Repositorio
|
||||
config.static_file_root_path=Ruta de los Ficheros Estáticos
|
||||
config.log_file_root_path=Ruta de los Ficheros de Log
|
||||
config.script_type=Tipo de Script
|
||||
config.reverse_auth_user=Autenticación Inversa de Usuario
|
||||
|
||||
config.ssh_config=Configuración SSH
|
||||
config.ssh_enabled=Habilitado
|
||||
config.ssh_start_builtin_server=Iniciar servidor integrado
|
||||
config.ssh_domain=Dominio
|
||||
config.ssh_port=Puerto
|
||||
config.ssh_listen_port=Puerto de escucha
|
||||
config.ssh_root_path=Ruta raíz
|
||||
config.ssh_key_test_path=Ruta de la clave de prueba
|
||||
config.ssh_keygen_path=Ruta del generador de claves ('ssh-keygen')
|
||||
config.ssh_minimum_key_size_check=Tamaño mínimo de la clave de verificación
|
||||
config.ssh_minimum_key_sizes=Tamaños de clave mínimos
|
||||
|
||||
config.db_config=Configuración de la Base de Datos
|
||||
config.db_type=Tipo
|
||||
config.db_host=Host
|
||||
config.db_name=Nombre
|
||||
config.db_user=Usuario
|
||||
config.db_ssl_mode=Modo SSL
|
||||
config.db_ssl_mode_helper=(solo para "postgres")
|
||||
config.db_ssl_mode_helper=(sólo para "postgres")
|
||||
config.db_path=Ruta
|
||||
config.db_path_helper=(para "sqlite3" y "tidb")
|
||||
config.service_config=Configuración del Servicio
|
||||
|
||||
config.service_config=Configuración del servicio
|
||||
config.register_email_confirm=Solicitar Confirmación por Correo Electrónico
|
||||
config.disable_register=Deshabilitar el Registro
|
||||
config.show_registration_button=Mostrar Botón de Registro
|
||||
config.require_sign_in_view=Solicitar la Vista de Inicio de Sesión
|
||||
config.enable_cache_avatar=Activar la Caché de Avatar
|
||||
config.mail_notify=Notificación por Correo Electrónico
|
||||
config.disable_key_size_check=Deshabilitar la comprobación de Tamaño Mínimo de Clave
|
||||
config.enable_captcha=Activar Captcha
|
||||
config.active_code_lives=Habilitar Vida del Código
|
||||
config.reset_password_code_lives=Restablecer Contraseña de Vida del Código
|
||||
|
||||
config.webhook_config=Configuración de Webhooks
|
||||
config.queue_length=Tamaño de Cola de Envío
|
||||
config.deliver_timeout=Timeout de Entrega
|
||||
config.skip_tls_verify=Omitir la Verificación TLS
|
||||
config.mailer_config=Configuración del Mailer
|
||||
|
||||
config.mailer_config=Configuración del servidor de correo
|
||||
config.mailer_enabled=Activado
|
||||
config.mailer_disable_helo=Desactivar HELO
|
||||
config.mailer_name=Nombre
|
||||
config.mailer_host=Host
|
||||
config.mailer_user=Usuario
|
||||
config.send_test_mail=Enviar email de prueba
|
||||
config.test_mail_failed=Fallo al enviar el email de prueba a '%s': %v
|
||||
config.test_mail_sent=El email de prueba ha sido enviado a '%s'.
|
||||
|
||||
config.oauth_config=Configuración OAuth
|
||||
config.oauth_enabled=Activado
|
||||
|
||||
config.cache_config=Configuración de la Caché
|
||||
config.cache_adapter=Adaptador de la Caché
|
||||
config.cache_interval=Intervalo de la Caché
|
||||
config.cache_conn=Conexión de la Caché
|
||||
|
||||
config.session_config=Configuración de la Sesión
|
||||
config.session_provider=Proveedor de la Sesión
|
||||
config.provider_config=Configuración del Proveedor
|
||||
@@ -993,9 +1123,24 @@ config.gc_interval_time=Intervalo de tiempo del GC
|
||||
config.session_life_time=Tiempo de Vida de la Sesión
|
||||
config.https_only=Sólo HTTPS
|
||||
config.cookie_life_time=Tiempo de Vida de la Cookie
|
||||
|
||||
config.picture_config=Configuración de Imagen
|
||||
config.picture_service=Servicio de Imágen
|
||||
config.disable_gravatar=Desactivar Gravatar
|
||||
config.enable_federated_avatar=Habilitar Avatares Federados
|
||||
|
||||
config.git_config=Configuración de Git
|
||||
config.git_disable_diff_highlight=Desactivar resaltado de sintaxis del Diff
|
||||
config.git_max_diff_lines=Líneas de Diff máximas (por un solo archivo)
|
||||
config.git_max_diff_line_characters=Carácteres de Diff máximos (para una sola línea)
|
||||
config.git_max_diff_files=Máximo de archivos de Diff (que se mostrarán)
|
||||
config.git_gc_args=Argumentos de GC
|
||||
config.git_migrate_timeout=Tiempo de espera de migración
|
||||
config.git_mirror_timeout=Tiempo de espera de actualización de réplicas
|
||||
config.git_clone_timeout=Tiempo de espera de operación de clones
|
||||
config.git_pull_timeout=Tiempo de espera de operación de pull
|
||||
config.git_gc_timeout=Tiempo de espera de operación de GC
|
||||
|
||||
config.log_config=Configuración del Log
|
||||
config.log_mode=Modo del Log
|
||||
|
||||
@@ -1029,7 +1174,11 @@ create_repo=creó el repositorio <a href="%s">%s</a>
|
||||
rename_repo=repositorio renombrado de <code>%[1]s</code> a <a href="%[2]s">%[3]s</a>
|
||||
commit_repo=hizo push a <a href="%[1]s/src/%[2]s">%[3]s</a> en <a href="%[1]s">%[4]s</a>
|
||||
create_issue=`incidencia abierta <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
close_issue=`cerró la incidencia <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
reopen_issue=`reabrió la incidencia <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
create_pull_request=`creado pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
close_pull_request=`cerró el pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
reopen_pull_request=`reabrió el pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
comment_issue=`comentó en la incidencia <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
merge_pull_request=`fusionado pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
transfer_repo=transfirió el repositorio <code>%s</code> a <a href="%s">%s</a>
|
||||
@@ -1053,7 +1202,7 @@ hours=%[2]s %[1]d horas
|
||||
days=%[2]s %[1]d días
|
||||
weeks=%[2]s %[1]d semanas
|
||||
months=%[2]s %[1]d meses
|
||||
years=%s %d años
|
||||
years=%[2]s %[1]d años
|
||||
raw_seconds=segundos
|
||||
raw_minutes=minutos
|
||||
|
||||
|
||||
1214
conf/locale/locale_fi-FI.ini
Normal file
1214
conf/locale/locale_fi-FI.ini
Normal file
File diff suppressed because it is too large
Load Diff
529
conf/locale/locale_fr-FR.ini
Executable file → Normal file
529
conf/locale/locale_fr-FR.ini
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
1214
conf/locale/locale_gl-ES.ini
Normal file
1214
conf/locale/locale_gl-ES.ini
Normal file
File diff suppressed because it is too large
Load Diff
409
conf/locale/locale_it-IT.ini
Executable file → Normal file
409
conf/locale/locale_it-IT.ini
Executable file → Normal file
@@ -38,19 +38,12 @@ settings=Impostazioni
|
||||
your_profile=Il tuo profilo
|
||||
your_settings=Impostazioni
|
||||
|
||||
news_feed=Notizie
|
||||
activities=Attivitá
|
||||
pull_requests=Pull Request
|
||||
issues=Problemi
|
||||
|
||||
cancel=Annulla
|
||||
|
||||
[search]
|
||||
search=Ricerca...
|
||||
repository=Repository
|
||||
user=Utente
|
||||
issue=Problema
|
||||
code=Codice
|
||||
|
||||
[install]
|
||||
install=Installazione
|
||||
title=Passi d'installazione per il primo avvio
|
||||
@@ -65,7 +58,7 @@ db_name=Nome del database
|
||||
db_helper=Utilizza il motore INNODB con codifica utf8_general_ci per MySQL.
|
||||
ssl_mode=Modalità SSL
|
||||
path=Percorso
|
||||
sqlite_helper=Il percorso file del database SQLite3 o TiDB.
|
||||
sqlite_helper=Il path assoluto per il database SQLite3 o TiDB. <br>Per favore usa il path assoluto quando lo avvii come servizio.
|
||||
err_empty_db_path=Il percorso file del database SQLite3 o TiDB non può essere vuoto.
|
||||
err_invalid_tidb_name=Il nome del database TiDB non ammette caratteri "." e "-".
|
||||
no_admin_and_disable_registration=Non puoi disabilitare la registrazione senza aver creato un amministratore.
|
||||
@@ -79,13 +72,15 @@ repo_path_helper=Tutti i repository Git remoti saranno salvati in questa directo
|
||||
run_user=Esegui con l'utente
|
||||
run_user_helper=L'utente deve avere accesso al percorso root del repository e avviare Gogs.
|
||||
domain=Dominio
|
||||
domain_helper=Questo modifica lo SSH clone URLs.
|
||||
domain_helper=Questo influisce sugli URL per il clonaggio via SSH.
|
||||
ssh_port=Porta SSH
|
||||
ssh_port_helper=Numero di porta utilizzato dal server SSH, lasciare vuoto per disabilitare l'integrazione SSH.
|
||||
http_port=Porta HTTP
|
||||
http_port_helper=Porta di ascolto dell'applicazione.
|
||||
app_url=URL Applicazione
|
||||
app_url_helper=Questo influisce sugli URL per il clonaggio via HTTP/HTTPS e da qualche parte nella posta elettronica.
|
||||
log_root_path=Percorso dei log
|
||||
log_root_path_helper=Directory in cui scrivere i file di log.
|
||||
|
||||
optional_title=Impostazioni Facoltative
|
||||
email_title=Impostazioni E-mail
|
||||
@@ -101,6 +96,8 @@ offline_mode=Abilita Modalità Offline
|
||||
offline_mode_popup=Disabilita il CDN anche in modalità produttiva, tutte le risorse saranno servite localmente.
|
||||
disable_gravatar=Disattiva il servizio Gravatar
|
||||
disable_gravatar_popup=Disabilita Gravatar e sorgenti customizzate, tutti gli avatar vengono caricati dagli utenti o come predefinito.
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=Disabilita Registrazione Manuale
|
||||
disable_registration_popup=Disabilita la registrazione manuale degli utenti, solo gli amministratori possono creare account.
|
||||
enable_captcha=Abilita Captcha
|
||||
@@ -119,15 +116,18 @@ sqlite3_not_available=Questa versione non supporta SQLite3, si prega di scaricar
|
||||
invalid_db_setting=La configurazione del database non è corretta: %v
|
||||
invalid_repo_path=Percorso root del repository invalido: %v
|
||||
run_user_not_match=Run user non è l'utente corrente: %s -> %s
|
||||
invalid_smtp_from=SMTP From field is not valid: %v
|
||||
save_config_failed=Fallito il salvataggio della configurazione: %v
|
||||
invalid_admin_setting=Impostazioni account Admin non valide: %v
|
||||
install_success=Benvenuto! Siamo felici che tu abbia scelto Gogs, buon divertimento.
|
||||
invalid_log_root_path=Log root path is invalid: %v
|
||||
|
||||
[home]
|
||||
uname_holder=Nome Utente o E-mail
|
||||
password_holder=Password
|
||||
switch_dashboard_context=Cambia Dashboard Context
|
||||
my_repos=I miei Repository
|
||||
show_more_repos=Visualizza altre repositories...
|
||||
collaborative_repos=Repository Condivisi
|
||||
my_orgs=Le mie Organizzazioni
|
||||
my_mirrors=I miei Mirror
|
||||
@@ -137,6 +137,9 @@ issues.in_your_repos=Nei tuoi repository
|
||||
|
||||
[explore]
|
||||
repos=Repository
|
||||
users=Utenti
|
||||
organizations=Organizations
|
||||
search=Cerca
|
||||
|
||||
[auth]
|
||||
create_new_account=Crea un nuovo Account
|
||||
@@ -150,15 +153,17 @@ forget_password=Password dimenticata?
|
||||
sign_up_now=Bisogno di un account? Iscriviti ora.
|
||||
confirmation_mail_sent_prompt=Una nuova email di conferma è stata inviata a <b>%s</b>, verifica la tua casella di posta entro le prossime %d ore per completare la registrazione.
|
||||
active_your_account=Attiva il tuo Account
|
||||
prohibit_login=Accesso Vietato
|
||||
prohibit_login_desc=Il tuo account è impossibilitato al login, contatta l'amministratore del sito.
|
||||
resent_limit_prompt=Siamo spiacenti, si stanno inviando e-mail di attivazione troppo spesso. Si prega di attendere 3 minuti.
|
||||
has_unconfirmed_mail=Ciao %s, hai un indirizzo di posta elettronica non confermato (<b>%s</b>). Se non hai ricevuto una e-mail di conferma o vuoi riceverla nuovamente, fare clic sul pulsante qui sotto.
|
||||
resend_mail=Clicca qui per inviare nuovamente l'e-mail di attivazione
|
||||
email_not_associate=Questo indirizzo e-mail non è associato ad alcun account.
|
||||
send_reset_mail=Clicca qui per (ri)inviare la tua e-mail di reimpostazione password
|
||||
reset_password=Reimposta la tua Password
|
||||
invalid_code=Siamo spiacenti, il codice di conferma è scaduto o non valido.
|
||||
reset_password_helper=Clicca qui per reimpostare la password
|
||||
password_too_short=La lunghezza della password non può essere meno 6 caratteri.
|
||||
non_local_account=Gli account non locali non possono modificare le password tramite Gogs.
|
||||
|
||||
[mail]
|
||||
activate_account=Per favore attiva il tuo account
|
||||
@@ -185,6 +190,13 @@ TeamName=Nome Team
|
||||
AuthName=Nome autorizzazione
|
||||
AdminEmail=Email dell'Admin
|
||||
|
||||
NewBranchName=Nuovo nome del branch
|
||||
CommitSummary=Riepilogo dei commit
|
||||
CommitMessage=Messaggio di commit
|
||||
CommitChoice=Scelta di commit
|
||||
TreeName=Percorso del file
|
||||
Content=Contenuto
|
||||
|
||||
require_error=` non può essere vuoto.`
|
||||
alpha_dash_error=` ammessi solo caratteri alfanumerici o trattini(-_).`
|
||||
alpha_dash_dot_error=` ammessi solo caratteri alfanumerici o trattini(-_) o punti.`
|
||||
@@ -203,7 +215,6 @@ repo_name_been_taken=Il nome del Repository è già utilizzato.
|
||||
org_name_been_taken=Il nome dell'Organizzazione è già utlizzato.
|
||||
team_name_been_taken=Il nome del Team è già utilizzato.
|
||||
email_been_used=L'indirizzo E-mail è già utilizzato.
|
||||
illegal_team_name=Il nome del Team contiene caratteri non validi.
|
||||
username_password_incorrect=Nome utente o password incorretti.
|
||||
enterred_invalid_repo_name=Si prega di assicurarsi che il nome del repository inserito sia corretto.
|
||||
enterred_invalid_owner_name=Si prega di assicurarsi che il nome del proprietario inserito sia corretto.
|
||||
@@ -219,13 +230,10 @@ still_own_repo=Il tuo account possiede ancora almeno un repository, dovete prima
|
||||
still_has_org=Il tuo account è ancora associato ad almeno un'organizzazione, disassociarsi prima.
|
||||
org_still_own_repo=Questa organizzazione ha ancora la proprietà del repository, dovete cancellarla o trasferirli prima.
|
||||
|
||||
still_own_user=Questa autenticazione è ancora in uso da almeno un utente, per favore rimuovili dall'autenticazione e riprova.
|
||||
|
||||
target_branch_not_exist=Il ramo (branch) di destinazione non esiste.
|
||||
|
||||
[user]
|
||||
change_avatar=Cambia il tuo avatar su gravatar.com
|
||||
change_custom_avatar=Cambia il tuo avatar nelle impostazioni
|
||||
change_avatar=Cambia il tuo avatar
|
||||
join_on=Si è unito il
|
||||
repositories=Repository
|
||||
activity=Attività pubblica
|
||||
@@ -241,6 +249,7 @@ form.name_pattern_not_allowed=La struttura del nome utente '%s' non è consentit
|
||||
[settings]
|
||||
profile=Profilo
|
||||
password=Password
|
||||
avatar=Avatar
|
||||
ssh_keys=Chiavi SSH
|
||||
social=Account Sociali
|
||||
applications=Applicazioni
|
||||
@@ -261,12 +270,13 @@ change_username_prompt=Questa modifica influenzerà il modo in cui i link si rif
|
||||
continue=Continua
|
||||
cancel=Annulla
|
||||
|
||||
lookup_avatar_by_mail=Ricerca Avatar per mail
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=Abilita avatar personalizzato
|
||||
enable_custom_avatar_helper=Seleziona per disabilitare il fetch da Gravatar
|
||||
choose_new_avatar=Scegli un nuovo avatar
|
||||
update_avatar=Aggiorna le impostazioni avatar
|
||||
delete_current_avatar=Elimina Avatar attuale
|
||||
uploaded_avatar_not_a_image=Il file caricato non è un'immagine.
|
||||
no_custom_avatar_available=Nessun avatar personalizzato disponibile, impossibile abilitarlo.
|
||||
update_avatar_success=Le tue impostazioni avatar sono state aggiornate con successo.
|
||||
|
||||
change_password=Cambia Password
|
||||
@@ -328,6 +338,10 @@ access_token_deletion=Eliminazione Token di accesso personale
|
||||
access_token_deletion_desc=Eliminare questo token di accesso personale rimuoverà tutti i relativi accessi di applicazione. Si desidera continuare?
|
||||
delete_token_success=Il token di accesso personale è stato eliminato! Non dimenticare di aggiornare anche l'applicazione.
|
||||
|
||||
orgs.none=You are not a member of any organizations.
|
||||
orgs.leave_title=Leave an organization
|
||||
orgs.leave_desc=You will lose access to all repositories and teams after you left the organization. Do you want to continue?
|
||||
|
||||
delete_account=Elimina Account
|
||||
delete_prompt=L'operazione eliminerà permanentemente l'account e <strong>NON POTRÀ</strong> essere annullata!
|
||||
confirm_delete_account=Conferma Eliminazione
|
||||
@@ -348,7 +362,7 @@ fork_from=Forka da
|
||||
fork_visiblity_helper=Non puoi cambiare la visibilità di un repository forkato.
|
||||
repo_desc=Descrizione
|
||||
repo_lang=Lingua
|
||||
repo_lang_helper=Seleziona file .gitignore
|
||||
repo_gitignore_helper=Seleziona i templates di .gitignore
|
||||
license=Licenza
|
||||
license_helper=Selezionare un file di licenza
|
||||
readme=Readme
|
||||
@@ -356,9 +370,12 @@ readme_helper=Seleziona un template per il readme
|
||||
auto_init=Inizializzare questo repository con i file e il modello selezionati
|
||||
create_repo=Crea Repository
|
||||
default_branch=Ramo (Branch) predefinito
|
||||
mirror_prune=Rimuovi
|
||||
mirror_prune_desc=Remove any remote-tracking references that no longer exist on the remote
|
||||
mirror_interval=Intervallo Mirror (in ore)
|
||||
mirror_address=Indirizzo del mirror
|
||||
mirror_address_desc=Si prega di includere nell'indirizzo le credenziali utente necessarie.
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=Osservatori
|
||||
stargazers=Fan
|
||||
forks=Fork
|
||||
@@ -372,7 +389,8 @@ migrate_type=Tipo di migrazione
|
||||
migrate_type_helper=Questo repository sarà un <span class="text blue">mirror</span>
|
||||
migrate_repo=Migra Repository
|
||||
migrate.clone_address=Duplica Indirizzo
|
||||
migrate.clone_address_desc=Può essere un URL HTTP/HTTPS/GIT o il percorso del server locale.
|
||||
migrate.clone_address_desc=This can be a HTTP/HTTPS/GIT URL.
|
||||
migrate.clone_address_desc_import_local=You're also allowed to migrate a repository by local server path.
|
||||
migrate.permission_denied=Non è consentito importare repository locali.
|
||||
migrate.invalid_local_path=Percorso locale non valido, non esiste o non è una cartella.
|
||||
migrate.failed=Migrazione non riuscita: %v
|
||||
@@ -397,7 +415,7 @@ create_new_repo_command=Crea nuovo repository da riga di comando
|
||||
push_exist_repo=Push un repo esistente dalla riga di comando
|
||||
repo_is_empty=Questo repository è vuoto, si prega di tornare più tardi!
|
||||
|
||||
code=Codice
|
||||
files=Files
|
||||
branch=Ramo (Branch)
|
||||
tree=Albero (Tree)
|
||||
filter_branch_and_tag=Filtra per branch o tag
|
||||
@@ -413,6 +431,47 @@ file_raw=Originale
|
||||
file_history=Cronologia
|
||||
file_view_raw=Vedi originale
|
||||
file_permalink=Permalink
|
||||
file_too_large=Questo file è troppo grande per essere mostrato
|
||||
video_not_supported_in_browser=Your browser doesn't support HTML5 video tag.
|
||||
|
||||
editor.new_file=Nuovo file
|
||||
editor.upload_file=Carica File
|
||||
editor.edit_file=Modifica file
|
||||
editor.preview_changes=Anteprima modifiche
|
||||
editor.cannot_edit_non_text_files=Non è possibile modificare i file non di testo
|
||||
editor.edit_this_file=Modifica questo file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.file_is_a_symlink=The file '%s' is a symlink that cannot be modified from the web editor.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=Commits
|
||||
commits.search=Ricerca una versione
|
||||
@@ -439,6 +498,11 @@ issues.create=Crea Problema
|
||||
issues.new_label=Nuova etichetta
|
||||
issues.new_label_placeholder=Nome dell'etichetta...
|
||||
issues.create_label=Crea Etichetta
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d Aperti
|
||||
issues.close_tab=%d Chiusi
|
||||
issues.filter_label=Etichetta
|
||||
@@ -466,23 +530,23 @@ issues.next=Pagina successiva
|
||||
issues.open_title=Aperto
|
||||
issues.closed_title=Chiuso
|
||||
issues.num_comments=%d commenti
|
||||
issues.commented_at=`commented <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.no_content=There is no content yet.
|
||||
issues.commented_at=`commented <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=Are you sure you want to delete this comment?
|
||||
issues.no_content=Non ci sono ancora contenuti.
|
||||
issues.close_issue=Chiudi
|
||||
issues.close_comment_issue=Commenta e chiudi
|
||||
issues.reopen_issue=Riapri
|
||||
issues.reopen_comment_issue=Commenta e riapri
|
||||
issues.create_comment=Commento
|
||||
issues.closed_at=`closed <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at=`reopened <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.closed_at=`chiuso <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at=`riaperto <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commit_ref_at=`referenced this issue from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.poster=Autore
|
||||
issues.admin=Amministratore
|
||||
issues.collaborator=Collaboratori
|
||||
issues.owner=Proprietario
|
||||
issues.sign_up_for_free=Registrati gratuitamente
|
||||
issues.sign_in_require_desc=to join this conversation. Already have an account? <a href="%s">Sign in to comment</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=Modifica
|
||||
issues.cancel=Cancel
|
||||
issues.cancel=Annulla
|
||||
issues.save=Salva
|
||||
issues.label_title=Nome etichetta
|
||||
issues.label_color=Colore etichetta
|
||||
@@ -494,46 +558,51 @@ issues.label_modify=Modifica Etichetta
|
||||
issues.label_deletion=Elimina Etichetta
|
||||
issues.label_deletion_desc=Eliminare l'etichetta rimuovera le sue informazioni in tutti i problemi correlati. Vuoi continuare?
|
||||
issues.label_deletion_success=Etichetta eliminata con successo!
|
||||
issues.num_participants=%d Partecipanti
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=Nuova Pull Request
|
||||
pulls.compare_changes=Confronta le modifiche
|
||||
pulls.compare_changes_desc=Confronta due branch e fai una pull request per le modifiche.
|
||||
pulls.compare_base=base
|
||||
pulls.compare_compare=confronta
|
||||
pulls.filter_branch=Filter branch
|
||||
pulls.filter_branch=Filtra branch
|
||||
pulls.no_results=Nessun risultato trovato.
|
||||
pulls.nothing_to_compare=There is nothing to compare because base and head branches are even.
|
||||
pulls.has_pull_request=`There is already a pull request between these two targets: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
|
||||
pulls.nothing_to_compare=Non c'è niente da confrontare perchè i branch base e head uguali.
|
||||
pulls.has_pull_request=`E' già presente una pull request tra questi due trargets: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
|
||||
pulls.create=Crea Pull Request
|
||||
pulls.title_desc=wants to merge %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code>
|
||||
pulls.merged_title_desc=merged %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> %[4]s
|
||||
pulls.title_desc=vorrebbe unire %[1]d commit da <code>%[2]s</code> a <code>%[3]s</code>
|
||||
pulls.merged_title_desc=ha unito %[1]d commit da <code>%[2]s</code> a <code>%[3]s</code> %[4]s
|
||||
pulls.tab_conversation=Conversazione
|
||||
pulls.tab_commits=Commits
|
||||
pulls.tab_commits=Commit
|
||||
pulls.tab_files=File modificati
|
||||
pulls.reopen_to_merge=Riapri questa pull request per effettuare il merge.
|
||||
pulls.merged=Merged
|
||||
pulls.has_merged=This pull request has been merged successfully!
|
||||
pulls.data_broken=Data of this pull request has been broken due to deletion of fork information.
|
||||
pulls.is_checking=The conflict checking is still in progress, please refresh page in few moments.
|
||||
pulls.can_auto_merge_desc=This pull request can be merged automatically.
|
||||
pulls.cannot_auto_merge_desc=This pull request can't be merged automatically because there are conflicts.
|
||||
pulls.merged=Unito
|
||||
pulls.has_merged=Questo contributo è stato incluso con successo!
|
||||
pulls.data_broken=I dati di questa pull request si sono rotti causa dell'eliminazione delle informazioni di fork.
|
||||
pulls.is_checking=Il controllo dei conflitti è ancora in corso, per favore aggiorna pagina tra qualche istante.
|
||||
pulls.can_auto_merge_desc=La pull request non può essere mergiata automaticamente.
|
||||
pulls.cannot_auto_merge_desc=Questa pull request non può essere mergiata automaticamente perchè ci sono dei conflitti.
|
||||
pulls.cannot_auto_merge_helper=Effettua il merge manualmente per risolvere i conflitti.
|
||||
pulls.merge_pull_request=Unisci Pull Request
|
||||
pulls.open_unmerged_pull_exists=`You can't perform reopen operation because there is already an open pull request (#%d) from same repository with same merge information and is waiting for merging.`
|
||||
pulls.delete_branch=Delete Branch
|
||||
pulls.delete_branch_has_new_commits=Branch cannot be deleted because it has new commits after mergence.
|
||||
|
||||
milestones.new=Nuova Milestone
|
||||
milestones.open_tab=%d Open
|
||||
milestones.close_tab=%d Closed
|
||||
milestones.closed=Closed %s
|
||||
milestones.no_due_date=No due date
|
||||
milestones.open=Open
|
||||
milestones.close=Close
|
||||
milestones.new_subheader=Create milestones to organize your issues.
|
||||
milestones.open_tab=%d Aperti
|
||||
milestones.close_tab=%d Chiusi
|
||||
milestones.closed=Chiuso %s
|
||||
milestones.no_due_date=Nessuna data di scadenza
|
||||
milestones.open=Apri
|
||||
milestones.close=Chiudi
|
||||
milestones.new_subheader=Crea delle milestones per organizzare le tue issues.
|
||||
milestones.create=Crea Milestone
|
||||
milestones.title=Titolo
|
||||
milestones.desc=Descrizione
|
||||
milestones.due_date=Data di scadenza (opzionale)
|
||||
milestones.clear=Clear
|
||||
milestones.clear=Pulisci
|
||||
milestones.invalid_due_date_format=Il formato della data di scadenza non è valido, deve essere 'yyyy-mm-dd'.
|
||||
milestones.create_success=La Milestone '%s' è stata creata con successo!
|
||||
milestones.edit=Modifica Milestone
|
||||
@@ -557,6 +626,8 @@ wiki.save_page=Salva pagina
|
||||
wiki.last_commit_info=%s ha modificato questa pagina %s
|
||||
wiki.edit_page_button=Modifica
|
||||
wiki.new_page_button=Nuova pagina
|
||||
wiki.delete_page_button=Cancella Pagina
|
||||
wiki.delete_page_notice_1=Questo cancellerà lapagina <code>"%s"</code>. Si prega di esserne certi.
|
||||
wiki.page_already_exists=Esiste già una pagina Wiki con questo stesso nome.
|
||||
wiki.pages=Pagine
|
||||
wiki.last_updated=Ultimo aggiornamento: %s
|
||||
@@ -564,36 +635,57 @@ wiki.last_updated=Ultimo aggiornamento: %s
|
||||
settings=Impostazioni
|
||||
settings.options=Opzioni
|
||||
settings.collaboration=Collaborazione
|
||||
settings.collaboration.admin=Admin
|
||||
settings.collaboration.write=Write
|
||||
settings.collaboration.read=Read
|
||||
settings.collaboration.undefined=Undefined
|
||||
settings.hooks=Webhooks
|
||||
settings.githooks=Git Hooks
|
||||
settings.basic_settings=Impostazioni di Base
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=Sito Ufficiale
|
||||
settings.update_settings=Aggiorna Impostazioni
|
||||
settings.change_reponame_prompt=Questa modifica influirà i link al repository.
|
||||
settings.advanced_settings=Opzioni avanzate
|
||||
settings.wiki_desc=Abilitare il wiki per consentire alle persone di scrivere documenti
|
||||
settings.wiki_desc=Enable wiki system
|
||||
settings.use_internal_wiki=Use builtin wiki
|
||||
settings.use_external_wiki=Usa Wiki esterno
|
||||
settings.external_wiki_url=URL Wiki esterno
|
||||
settings.external_wiki_url_desc=I visitatori verranno reindirizzati all'URL quando cliccano sulla scheda.
|
||||
settings.issues_desc=Enable builtin lightweight issue tracker
|
||||
settings.issues_desc=Enable issue tracker
|
||||
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=Utilizza gestore di problemi esterno
|
||||
settings.tracker_url_format=External Issue Tracker URL Format
|
||||
settings.external_tracker_url=External Issue Tracker URL
|
||||
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
|
||||
settings.tracker_url_format=Formato URL Gestore Problemi Esterno
|
||||
settings.tracker_issue_style=External Issue Tracker Naming Style:
|
||||
settings.tracker_issue_style.numeric=Numeric
|
||||
settings.tracker_issue_style.alphanumeric=Alphanumeric
|
||||
settings.tracker_url_format_desc=You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index.
|
||||
settings.pulls_desc=Enable pull requests to accept public contributions
|
||||
settings.pulls_desc=Abilita le pull requests per accettare contributi pubblici
|
||||
settings.danger_zone=Zona Pericolosa
|
||||
settings.new_owner_has_same_repo=Il nuovo proprietario ha già un repository con lo stesso nome. Per favore scegli un altro nome.
|
||||
settings.convert=Converti in Repository Regolare
|
||||
settings.convert_desc=Puoi convertire questo mirror in un repository regolare. Questa operazione non può essere annullata.
|
||||
settings.convert_notices_1=- Questa operazione non potrà essere annullata e convertirà questo mirror in un repository regolare.
|
||||
settings.convert_confirm=Conferma la conversione
|
||||
settings.convert_succeed=Il repository è stato convertito con successo al formato normale.
|
||||
settings.transfer=Trasferisci proprietà
|
||||
settings.transfer_desc=Trasferisci questa repository a un altro utente o a un'organizzazione nella quale hai diritti d'amministratore.
|
||||
settings.new_owner_has_same_repo=Il nuovo proprietario ha già un repository con lo stesso nome. Per favore scegli un altro nome.
|
||||
settings.delete=Elimina questo repository
|
||||
settings.delete_desc=Una volta che hai cancellato il repository, non puoi tornare indietro. Si prega di fare attenzione.
|
||||
settings.transfer_notices_1=- You will lose access if new owner is a individual user.
|
||||
settings.transfer_notices_2=- You will conserve access if new owner is an organization and if you're one of the owners.
|
||||
settings.transfer_form_title=Per favore inserisci le informazioni seguenti per confermare l'operazione:
|
||||
settings.wiki_delete=Elimina i dati della Wiki
|
||||
settings.wiki_delete_desc=Once you erase wiki data there is no going back. Please be certain.
|
||||
settings.wiki_delete_notices_1=Questo eliminerà e disabiliterà la wiki per %s
|
||||
settings.wiki_deletion_success=I dati della wiki del repository sono stati eliminati con successo.
|
||||
settings.delete=Elimina questo repository
|
||||
settings.delete_desc=Una volta che hai cancellato il repository, non puoi tornare indietro. Si prega di fare attenzione.
|
||||
settings.delete_notices_1=-Questa operazione <strong>NON PUÒ</strong> essere annullata.
|
||||
settings.delete_notices_2=-Questa operazione eliminerà definitivamente il tutto il contenuto del repository, inclusi i dati di Git, incidenti, commenti e accessi dei collaboratori.
|
||||
settings.delete_notices_fork_1=-Se questo repository è pubblico, tutti i fork diventeranno indipendenti dopo la sua cancellazione.
|
||||
settings.delete_notices_fork_2=-Se questo repository è privato, tutti fork verranno rimossi assieme ad esso.
|
||||
settings.delete_notices_fork_3=- If you want to keep all forks after deletion, please change visibility of this repository to public first.
|
||||
settings.delete_notices_fork_1=- All forks will become independent after deletion.
|
||||
settings.deletion_success=Il repository è stato eliminato con successo!
|
||||
settings.update_settings_success=Le opzioni repository sono state aggiornate con successo.
|
||||
settings.transfer_owner=Nuovo Proprietario
|
||||
@@ -602,19 +694,23 @@ settings.transfer_succeed=Proprietà del repository trasferita con successo.
|
||||
settings.confirm_delete=Conferma eliminazione
|
||||
settings.add_collaborator=Aggiungi nuovo collaboratore
|
||||
settings.add_collaborator_success=Il nuovo collaboratore è stato aggiunto.
|
||||
settings.delete_collaborator=Elimina
|
||||
settings.collaborator_deletion=Eliminazione collaboratore
|
||||
settings.collaborator_deletion_desc=Questo utente non potrà più collaborare a questo repository dopo l'eliminazione. Si desidera continuare?
|
||||
settings.remove_collaborator_success=Il collaboratore è stato rimosso.
|
||||
settings.search_user_placeholder=Cerca utente...
|
||||
settings.org_not_allowed_to_be_collaborator=Un'organizzazione non può essere aggiunta come collaboratore.
|
||||
settings.user_is_org_member=L'utente è un membro dell'organizzazione che non può essere aggiunto come collaboratore.
|
||||
settings.add_webhook=Aggiungi Webhook
|
||||
settings.hooks_desc=I Webhooks sono molto simili a un basilare evento trigger HTTP POST. Ogni volta che qualcosa si verifica in Gogs, tratteremo la notifica all'host di destinazione specificato. Ulteriori informazioni in questa <a target="_blank" href="%s">Guida ai Webhooks</a>.
|
||||
settings.webhook_deletion=Elimina Webhook
|
||||
settings.webhook_deletion_desc=Delete this webhook will remove its information and all delivery history. Do you want to continue?
|
||||
settings.webhook_deletion_success=Webhook has been deleted successfully!
|
||||
settings.webhook.test_delivery=Test Delivery
|
||||
settings.webhook_deletion_success=Il Webhook è stato eliminato con successo!
|
||||
settings.webhook.test_delivery=Test di consegna
|
||||
settings.webhook.test_delivery_desc=Send a fake push event delivery to test your webhook settings
|
||||
settings.webhook.test_delivery_success=Test webhook has been added to delivery queue. It may take few seconds before it shows up in the delivery history.
|
||||
settings.webhook.request=Request
|
||||
settings.webhook.response=Response
|
||||
settings.webhook.request=Richiesta
|
||||
settings.webhook.response=Risposta
|
||||
settings.webhook.headers=Headers
|
||||
settings.webhook.payload=Payload
|
||||
settings.webhook.body=Body
|
||||
@@ -627,17 +723,19 @@ settings.add_webhook_desc=Gogs manderà una richiesta <code>POST</code> all'URL
|
||||
settings.payload_url=Payload URL
|
||||
settings.content_type=Content Type
|
||||
settings.secret=Secret
|
||||
settings.slack_username=Username
|
||||
settings.slack_username=Nome utente
|
||||
settings.slack_icon_url=URL icona
|
||||
settings.slack_color=Color
|
||||
settings.slack_color=Colore
|
||||
settings.event_desc=Quali eventi dovrebbero innescare questo webhook?
|
||||
settings.event_push_only=Solo l'evento <code>push</code>.
|
||||
settings.event_send_everything=I need <strong>everything</strong>.
|
||||
settings.event_choose=Let me choose what I need.
|
||||
settings.event_create=Create
|
||||
settings.event_create_desc=Branch, or tag created
|
||||
settings.event_send_everything=Ho bisogno di <strong>tutto</strong>.
|
||||
settings.event_choose=Lasciami scegliere ciò di cui ho bisogno.
|
||||
settings.event_create=Crea
|
||||
settings.event_create_desc=Branch, o tag creato
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=Push
|
||||
settings.event_push_desc=Git push to a repository
|
||||
settings.event_push_desc=Git push in un repository
|
||||
settings.active=Attivo
|
||||
settings.active_helper=Anche i dettagli riguardanti l'evento che ha innescato l'hook saranno inviati.
|
||||
settings.add_hook_success=Il nuovo webhook è stato aggiunto.
|
||||
@@ -651,28 +749,30 @@ settings.slack_token=Token
|
||||
settings.slack_domain=Dominio
|
||||
settings.slack_channel=Canale
|
||||
settings.deploy_keys=Dispiega Chiavi
|
||||
settings.add_deploy_key=Add Deploy Key
|
||||
settings.deploy_key_desc=Deploy keys have read-only access. They are not the same as personal account SSH keys.
|
||||
settings.no_deploy_keys=You haven't added any deploy keys.
|
||||
settings.title=Title
|
||||
settings.deploy_key_content=Content
|
||||
settings.key_been_used=Deploy key content has been used.
|
||||
settings.key_name_used=Deploy key with the same name already exists.
|
||||
settings.add_key_success=New deploy key '%s' has been added successfully!
|
||||
settings.deploy_key_deletion=Delete Deploy Key
|
||||
settings.deploy_key_deletion_desc=Deleting this deploy key will remove all related accesses for this repository. Do you want to continue?
|
||||
settings.deploy_key_deletion_success=Deploy key has been deleted successfully!
|
||||
settings.add_deploy_key=Aggiungi Deploy Key
|
||||
settings.deploy_key_desc=Le deploy keys hanno accesso in sola lettura. Non equivalgono alle chiavi SSH personali.
|
||||
settings.no_deploy_keys=Non hai aggiunto alcuna deploy key.
|
||||
settings.title=Titolo
|
||||
settings.deploy_key_content=Contenuto
|
||||
settings.key_been_used=La deploy key è già in uso.
|
||||
settings.key_name_used=Esiste già una deploy key con questo nome.
|
||||
settings.add_key_success=La nuova deploy key '%s' è stata aggiunta con successo!
|
||||
settings.deploy_key_deletion=Elimina Deploy Key
|
||||
settings.deploy_key_deletion_desc=Cancellando questa deploy key verrà rismosso ogni accesso relativo a questa repository. Vuoi continuare?
|
||||
settings.deploy_key_deletion_success=Deploy key eliminata con successo!
|
||||
|
||||
diff.browse_source=Sfoglia il codice sorgente
|
||||
diff.parent=parent
|
||||
diff.commit=commit
|
||||
diff.data_not_available=Diff Data non disponibile.
|
||||
diff.show_diff_stats=Mostra Diff Stats
|
||||
diff.show_split_view=Split View
|
||||
diff.show_unified_view=Unified View
|
||||
diff.show_split_view=Visualizzazione separata
|
||||
diff.show_unified_view=Visualizzazione unificata
|
||||
diff.stats_desc=<strong>%d ha cambiato i file</strong> con <strong>%d aggiunte</strong> e <strong>%d eliminazioni</strong>
|
||||
diff.bin=BIN
|
||||
diff.view_file=Vedi File
|
||||
diff.file_suppressed=File diff suppressed because it is too large
|
||||
diff.too_many_files=Some files were not shown because too many files changed in this diff
|
||||
|
||||
release.releases=Rilasci
|
||||
release.new_release=Nuovo Rilascio
|
||||
@@ -687,27 +787,28 @@ release.edit_subheader=Detailed change log can help users understand what has be
|
||||
release.tag_name=Nome tag
|
||||
release.target=Obbiettivo
|
||||
release.tag_helper=Scegli un tag esistente o crea un nuovo tag una volta pubblicato.
|
||||
release.title=Title
|
||||
release.content=Content
|
||||
release.title=Titolo
|
||||
release.content=Contenuto
|
||||
release.write=Scrivi
|
||||
release.preview=Anteprima
|
||||
release.loading=Caricamento...
|
||||
release.prerelease_desc=Questo è un pre-rilascio
|
||||
release.prerelease_helper=Precisiamo che questo rilascio non è pronta per la produzione.
|
||||
release.cancel=Cancel
|
||||
release.cancel=Annulla
|
||||
release.publish=Pubblica Rilascio
|
||||
release.save_draft=Salva Bozza
|
||||
release.edit_release=Modifica Rilascio
|
||||
release.delete_release=Delete This Release
|
||||
release.deletion=Release Deletion
|
||||
release.deletion_desc=Deleting this release will delete the corresponding Git tag. Do you want to continue?
|
||||
release.deletion_success=Release has been deleted successfully!
|
||||
release.delete_release=Cancela questa Release
|
||||
release.deletion=Eliminazione Release
|
||||
release.deletion_desc=Eliminando questa release cancellarai anche i tag Git corrispondenti. Vuoi continuare?
|
||||
release.deletion_success=La release è stata eliminata con successo!
|
||||
release.tag_name_already_exist=Un rilascio con questo tag esiste già.
|
||||
release.tag_name_invalid=Tag name is not valid.
|
||||
release.downloads=Download
|
||||
|
||||
[org]
|
||||
org_name_holder=Nome dell'Organizzazione
|
||||
org_full_name_holder=Organization Full Name
|
||||
org_full_name_holder=Nome completo dell'organizzazione
|
||||
org_name_helper=Le migliori organizzazioni hanno nomi brevi e memorabili.
|
||||
create_org=Crea Organizzazione
|
||||
repo_updated=Aggiornato
|
||||
@@ -734,7 +835,7 @@ settings.website=Sito Web
|
||||
settings.location=Residenza
|
||||
settings.update_settings=Aggiorna Impostazioni
|
||||
settings.update_setting_success=Impostazioni dell'organizzazione aggiornate con successo.
|
||||
settings.change_orgname_prompt=This change will affect how links relate to the organization.
|
||||
settings.change_orgname_prompt=Questa operazione modificherà il modo in cui i links sono in relazione con l'organizzazione.
|
||||
settings.update_avatar_success=Organization avatar setting has been updated successfully.
|
||||
settings.delete=Elimina organizzazione
|
||||
settings.delete_account=Elimina questa organizzazione
|
||||
@@ -749,7 +850,7 @@ members.public=Pubblico
|
||||
members.public_helper=rendi privato
|
||||
members.private=Privato
|
||||
members.private_helper=rendi pubblico
|
||||
members.member_role=Member Role:
|
||||
members.member_role=Ruolo del membro:
|
||||
members.owner=Proprietario
|
||||
members.member=Membro
|
||||
members.remove=Rimuovere
|
||||
@@ -779,7 +880,7 @@ teams.read_permission_desc=Questo Team concede accesso di <strong>Lettura</stron
|
||||
teams.write_permission_desc=Questo Team concede accesso di <strong>Scrittura</strong>: i membri possono leggere e pushare i repository del Team.
|
||||
teams.admin_permission_desc=Questo Team concede accesso di <strong>Amministratore</strong>: i membri possono leggere i, pushare a, e aggiungere collaboratori ai repository del Team.
|
||||
teams.repositories=Repository di Squadra
|
||||
teams.search_repo_placeholder=Search repository...
|
||||
teams.search_repo_placeholder=Cerca repository...
|
||||
teams.add_team_repository=Aggiungere Repository di Squadra
|
||||
teams.remove_repo=Rimuovi
|
||||
teams.add_nonexistent_repo=Il repository che stai tentando di aggiungere non esiste, crealo prima.
|
||||
@@ -793,8 +894,8 @@ authentication=Autenticazioni
|
||||
config=Configurazione
|
||||
notices=Avvisi di sistema
|
||||
monitor=Monitoraggio
|
||||
first_page=First
|
||||
last_page=Last
|
||||
first_page=Prima
|
||||
last_page=Ultima
|
||||
total=Totale: %d
|
||||
|
||||
dashboard.statistic=Statistiche
|
||||
@@ -818,6 +919,8 @@ dashboard.resync_all_sshkeys=Riscrivi il file '.ssh/authorized_keys' (attenzione
|
||||
dashboard.resync_all_sshkeys_success=Tutte le chiavi pubbliche riscritte con successo.
|
||||
dashboard.resync_all_update_hooks=Riscrivere tutti gli update hook dei repository (necessario quando il percorso di configurazione personalizzata viene modificato)
|
||||
dashboard.resync_all_update_hooks_success=Tutti gli update hook dei repository riscritti con successo.
|
||||
dashboard.reinit_missing_repos=Reinitialize all repository records that lost Git files
|
||||
dashboard.reinit_missing_repos_success=All repository records that lost Git files have been reinitialized successfully.
|
||||
|
||||
dashboard.server_uptime=Tempo in Attività del Server
|
||||
dashboard.current_goroutine=Goroutine Correnti
|
||||
@@ -865,9 +968,10 @@ users.auth_login_name=Authentication Login Name
|
||||
users.password_helper=Leave it empty to remain unchanged.
|
||||
users.update_profile_success=Profilo dell'account aggiornato con successo.
|
||||
users.edit_account=Modifica Account
|
||||
users.max_repo_creation=Maximum Repository Creation Limit
|
||||
users.max_repo_creation_desc=(Set -1 to use global default limit)
|
||||
users.max_repo_creation=Limite massimo per la creazione di Repository
|
||||
users.max_repo_creation_desc=(Inserire -1 per usare il limite globale di default)
|
||||
users.is_activated=Questo account è attivato
|
||||
users.prohibit_login=This account is prohibited to login
|
||||
users.is_admin=Questo account ha permessi di amministratore
|
||||
users.allow_git_hook=Questo account ha il permesso di creare hooks di Git
|
||||
users.allow_import_local=Questo account dispone delle autorizzazioni per importare repository locali
|
||||
@@ -891,48 +995,52 @@ repos.stars=Voti
|
||||
repos.issues=Problemi
|
||||
|
||||
auths.auth_manage_panel=Authentication Manage Panel
|
||||
auths.new=Add New Source
|
||||
auths.new=Aggiungi Nuova Origine
|
||||
auths.name=Nome
|
||||
auths.type=Tipo
|
||||
auths.enabled=Attivo
|
||||
auths.updated=Aggiornato
|
||||
auths.auth_type=Authentication Type
|
||||
auths.auth_name=Authentication Name
|
||||
auths.auth_type=Tipo di autenticazione
|
||||
auths.auth_name=Nome di autenticazione
|
||||
auths.security_protocol=Security Protocol
|
||||
auths.domain=Dominio
|
||||
auths.host=Host
|
||||
auths.port=Porta
|
||||
auths.bind_dn=Bind DN
|
||||
auths.bind_password=Bind Password
|
||||
auths.bind_password_helper=Warning: This password is stored in plain text. Do not use a high privileged account.
|
||||
auths.bind_dn=Binda DN
|
||||
auths.bind_password=Binda Password
|
||||
auths.bind_password_helper=Attenzione: Questa password è salvata in chiaro. Non usare su un acount con alti privilegi.
|
||||
auths.user_base=User Search Base
|
||||
auths.user_dn=User DN
|
||||
auths.attribute_username=Username attribute
|
||||
auths.user_dn=DN dell'utente
|
||||
auths.attribute_username=Attributo username
|
||||
auths.attribute_username_placeholder=Leave empty to use sign-in form field value for user name.
|
||||
auths.attribute_name=Attributo Nome
|
||||
auths.attribute_surname=Attributo Cognome
|
||||
auths.attribute_mail=Attributo Email
|
||||
auths.filter=User Filter
|
||||
auths.admin_filter=Admin Filter
|
||||
auths.attributes_in_bind=Fetch attributes in Bind DN context
|
||||
auths.filter=Fitro utente
|
||||
auths.admin_filter=Filtro Amministratore
|
||||
auths.ms_ad_sa=Ms Ad SA
|
||||
auths.smtp_auth=SMTP Authentication Type
|
||||
auths.smtp_auth=Tipo di autenticazione SMTP
|
||||
auths.smtphost=Host SMTP
|
||||
auths.smtpport=Porta SMTP
|
||||
auths.allowed_domains=Allowed Domains
|
||||
auths.allowed_domains=Domini consentiti
|
||||
auths.allowed_domains_helper=Leave it empty to not restrict any domains. Multiple domains should be separated by comma ','.
|
||||
auths.enable_tls=Abilitare Crittografia TLS
|
||||
auths.skip_tls_verify=Salta verifica TLS
|
||||
auths.pam_service_name=Nome del Servizio PAM
|
||||
auths.enable_auto_register=Abilitare Registrazione Automatica
|
||||
auths.tips=Consigli
|
||||
auths.edit=Edit Authentication Setting
|
||||
auths.edit=Modifica impostazioni di autenticazione
|
||||
auths.activated=Questa Autenticazione è stata attivata
|
||||
auths.new_success=New authentication '%s' has been added successfully.
|
||||
auths.update_success=Authentication setting has been updated successfully.
|
||||
auths.update=Update Authentication Setting
|
||||
auths.delete=Delete This Authentication
|
||||
auths.update=Aggiornare le impostazioni di autenticazione
|
||||
auths.delete=Elimina questa autenticazione
|
||||
auths.delete_auth_title=Authentication Deletion
|
||||
auths.delete_auth_desc=This authentication is going to be deleted, do you want to continue?
|
||||
auths.still_in_used=This authentication is still used by some users, please delete or convert these users to another login type first.
|
||||
auths.deletion_success=Authentication has been deleted successfully!
|
||||
auths.login_source_exist=Login source '%s' already exists.
|
||||
|
||||
config.server_config=Configurazione Server
|
||||
config.app_name=Nome Applicazione
|
||||
@@ -943,11 +1051,25 @@ config.offline_mode=Modalità Offline
|
||||
config.disable_router_log=Disattivare Log del Router
|
||||
config.run_user=Utente Esecutore
|
||||
config.run_mode=Modalità Esecuzione
|
||||
config.git_version=Git Version
|
||||
config.repo_root_path=Percorso Root del Repository
|
||||
config.static_file_root_path=Percorso Root del File Statico
|
||||
config.log_file_root_path=Percorso Root del File di Log
|
||||
config.script_type=Tipo di Script
|
||||
config.reverse_auth_user=Autenticazione Utente Inversa
|
||||
|
||||
config.ssh_config=Configurazione SSH
|
||||
config.ssh_enabled=Attivo
|
||||
config.ssh_start_builtin_server=Avvia server builtin
|
||||
config.ssh_domain=Dominio
|
||||
config.ssh_port=Porta
|
||||
config.ssh_listen_port=Porta in ascolto
|
||||
config.ssh_root_path=Percorso Root
|
||||
config.ssh_key_test_path=Percorso chiave di test
|
||||
config.ssh_keygen_path=Percorso Keygen ('ssh-keygen')
|
||||
config.ssh_minimum_key_size_check=Verifica delle dimensioni minime della chiave
|
||||
config.ssh_minimum_key_sizes=Dimensioni minime della chiave
|
||||
|
||||
config.db_config=Configurazione Database
|
||||
config.db_type=Tipo
|
||||
config.db_host=Host
|
||||
@@ -956,34 +1078,42 @@ config.db_user=Utente
|
||||
config.db_ssl_mode=Modalità SSL
|
||||
config.db_ssl_mode_helper=(solo per "postgres")
|
||||
config.db_path=Percorso
|
||||
config.db_path_helper=(for "sqlite3" and "tidb")
|
||||
config.db_path_helper=(per "sqlite3" e "tidb")
|
||||
|
||||
config.service_config=Configurazione Servizio
|
||||
config.register_email_confirm=Richiedono Conferma dell'Email
|
||||
config.disable_register=Disabilita Registrazione
|
||||
config.show_registration_button=Mostra Pulsane Registrazione
|
||||
config.require_sign_in_view=Richiesto Accesso per Vedere
|
||||
config.enable_cache_avatar=Abilitare Cache dell'Avatar
|
||||
config.mail_notify=Email di Notifica
|
||||
config.disable_key_size_check=Disable Minimum Key Size Check
|
||||
config.disable_key_size_check=Disabilita controllo sulle dimensioni minime della chiave
|
||||
config.enable_captcha=Abilita Captcha
|
||||
config.active_code_lives=Attiva Vita del Codice
|
||||
config.reset_password_code_lives=Reimpostare Password della Vita del Codice
|
||||
|
||||
config.webhook_config=Configurazione Webhook
|
||||
config.queue_length=Queue Length
|
||||
config.queue_length=Lunghezza della coda
|
||||
config.deliver_timeout=Tempo Limite di Consegna
|
||||
config.skip_tls_verify=Salta verifiche TLS
|
||||
|
||||
config.mailer_config=Configurazione Mailer
|
||||
config.mailer_enabled=Attivo
|
||||
config.mailer_disable_helo=Disattiva HELO
|
||||
config.mailer_name=Nome
|
||||
config.mailer_host=Host
|
||||
config.mailer_user=Utente
|
||||
config.send_test_mail=Invia email di test
|
||||
config.test_mail_failed=Impossibile inviare mail a '%s': %v
|
||||
config.test_mail_sent=Una mail di prova è stata inviata a '%s'.
|
||||
|
||||
config.oauth_config=Configurazione OAuth
|
||||
config.oauth_enabled=Attivo
|
||||
|
||||
config.cache_config=Configurazione Cache
|
||||
config.cache_adapter=Adattatore Cache
|
||||
config.cache_interval=Intervallo Cache
|
||||
config.cache_conn=Connessione Cache
|
||||
|
||||
config.session_config=Configurazione Sessione
|
||||
config.session_provider=Fornitore Sessione
|
||||
config.provider_config=Impostazioni Provider
|
||||
@@ -993,9 +1123,24 @@ config.gc_interval_time=Intervallo di tempo della GC
|
||||
config.session_life_time=Durata Sessione
|
||||
config.https_only=Solo HTTPS
|
||||
config.cookie_life_time=Durata Cookie
|
||||
|
||||
config.picture_config=Configurazione Foto
|
||||
config.picture_service=Servizio foto
|
||||
config.disable_gravatar=Disabilita Gravatar
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Git Configuration
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=GC Arguments
|
||||
config.git_migrate_timeout=Migration Timeout
|
||||
config.git_mirror_timeout=Mirror Update Timeout
|
||||
config.git_clone_timeout=Clone Operation Timeout
|
||||
config.git_pull_timeout=Pull Operation Timeout
|
||||
config.git_gc_timeout=GC Operation Timeout
|
||||
|
||||
config.log_config=Configurazione Log
|
||||
config.log_mode=Modalità Log
|
||||
|
||||
@@ -1011,30 +1156,34 @@ monitor.start=Orario Avvio
|
||||
monitor.execute_time=Tempo di Esecuzione
|
||||
|
||||
notices.system_notice_list=Avvisi di Sistema
|
||||
notices.view_detail_header=View Notice Detail
|
||||
notices.actions=Actions
|
||||
notices.select_all=Select All
|
||||
notices.deselect_all=Deselect All
|
||||
notices.inverse_selection=Inverse Selection
|
||||
notices.delete_selected=Delete Selected
|
||||
notices.delete_all=Delete All Notices
|
||||
notices.view_detail_header=Visualizza dettagli dell'avviso
|
||||
notices.actions=Azioni
|
||||
notices.select_all=Seleziona tutto
|
||||
notices.deselect_all=Deseleziona tutto
|
||||
notices.inverse_selection=Inverti selezione
|
||||
notices.delete_selected=Elimina selezionati
|
||||
notices.delete_all=Elimina tutti gli avvisi
|
||||
notices.type=Tipo
|
||||
notices.type_1=Repository
|
||||
notices.desc=Descrizione
|
||||
notices.op=Op.
|
||||
notices.delete_success=System notices have been deleted successfully.
|
||||
notices.delete_success=Gli avvisi di sistema sono stati successivamente eliminati.
|
||||
|
||||
[action]
|
||||
create_repo=ha creato il repository <a href="%s">%s</a>
|
||||
rename_repo=repository rinominato da <code>%[1]s</code> a <a href="%[2]s">[3]s</a>
|
||||
commit_repo=ha pushato nel <a href="%[1]s/src/%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a>
|
||||
create_issue=`ha aperto il problema <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
close_issue=`closed issue <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
reopen_issue=`reopened issue <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
create_pull_request=`creata pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
close_pull_request=`closed pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
reopen_pull_request=`reopened pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
comment_issue=`ha commentato il problema <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
merge_pull_request=`merged pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
transfer_repo=ha trasferito il repository <code>%s</code> a <a href="%s">%s</a>
|
||||
push_tag=ha pushato il tag <a href="%s/src/%s">%[2]s</a> a <a href="%[1]s">%[3]s</a>
|
||||
compare_commits=View comparison for these %d commits
|
||||
compare_commits=Visualizza comparazione tra questi %d commit
|
||||
|
||||
[tool]
|
||||
ago=fa
|
||||
@@ -1058,8 +1207,8 @@ raw_seconds=secondi
|
||||
raw_minutes=minuti
|
||||
|
||||
[dropzone]
|
||||
default_message=Drop files here or click to upload.
|
||||
invalid_input_type=You can't upload files of this type.
|
||||
file_too_big=File size ({{filesize}} MB) exceeds maximum size ({{maxFilesize}} MB).
|
||||
remove_file=Remove file
|
||||
default_message=Trascina i file qui o clicca per caricare.
|
||||
invalid_input_type=Non è possibile caricare file di questo tipo.
|
||||
file_too_big=La dimensione del file ({{filesize}} MB) supera la dimensione massima ({{maxFilesize}} MB).
|
||||
remove_file=Rimuovi file
|
||||
|
||||
|
||||
317
conf/locale/locale_ja-JP.ini
Executable file → Normal file
317
conf/locale/locale_ja-JP.ini
Executable file → Normal file
@@ -2,7 +2,7 @@ app_desc=Go言語で実装したセルフホストGitサービス
|
||||
|
||||
home=ホーム
|
||||
dashboard=ダッシュボード
|
||||
explore=エスクプローラ
|
||||
explore=エクスプローラ
|
||||
help=ヘルプ
|
||||
sign_in=サインイン
|
||||
sign_out=サインアウト
|
||||
@@ -21,7 +21,7 @@ username=ユーザ名
|
||||
email=E-mail
|
||||
password=パスワード
|
||||
re_type=再入力
|
||||
captcha=キャプチャ
|
||||
captcha=CAPTCHA
|
||||
|
||||
repository=リポジトリ
|
||||
organization=組織
|
||||
@@ -35,26 +35,19 @@ manage_org=組織を管理
|
||||
admin_panel=管理者パネル
|
||||
account_settings=アカウント設定
|
||||
settings=設定
|
||||
your_profile=あなたのプロファイル
|
||||
your_settings=あなたの設定
|
||||
your_profile=プロフィール
|
||||
your_settings=設定
|
||||
|
||||
news_feed=ニュースのフィード
|
||||
activities=アクティビティ
|
||||
pull_requests=プルリクエスト
|
||||
issues=課題
|
||||
|
||||
cancel=キャンセル
|
||||
|
||||
[search]
|
||||
search=検索...
|
||||
repository=リポジトリ
|
||||
user=ユーザ
|
||||
issue=課題
|
||||
code=コード
|
||||
|
||||
[install]
|
||||
install=インストール
|
||||
title=初回実行のインストール手順
|
||||
docker_helper=DockerでGogsを稼動する場合、このページに変更を加えるまえに、 <a target="_blank" href="%s">Guidelines</a>をよく読んでください!
|
||||
title=インストールをする前に必要な準備をしましょう
|
||||
docker_helper=DockerでGogsを稼動する場合は、このページに変更を加える前に、 <a target="_blank" href="%s">ガイドライン</a>をよく読んでください!
|
||||
requite_db_desc=Gogs は、MySQL、PostgreSQL、SQLite3 または TiDB が必要です。
|
||||
db_title=データベース設定
|
||||
db_type=データベースの種類
|
||||
@@ -62,10 +55,10 @@ host=ホスト
|
||||
user=ユーザ
|
||||
password=パスワード
|
||||
db_name=データベース名
|
||||
db_helper=Mysql INNODB エンジン utf8_general_ci の文字セットを使用してください。
|
||||
db_helper=MySQLではエンジンがINNODB、文字セットがutf8_general_ciである必要があります。
|
||||
ssl_mode=SSL モード
|
||||
path=パス
|
||||
sqlite_helper=SQLite3 または TiDB のデータベースのファイル パス。
|
||||
sqlite_helper=SQLite3かTiDBのデータベースのファイルパス。<br>サービスとして開始する際には絶対パスを利用してください。
|
||||
err_empty_db_path=SQLite3 または TiDB データベースのパスを空にすることはできません。
|
||||
err_invalid_tidb_name=TiDB データベース名は文字"."と"-"を許可しない。
|
||||
no_admin_and_disable_registration=管理者アカウントを作成せずに登録を無効にすることはできません。
|
||||
@@ -79,16 +72,18 @@ repo_path_helper=すべての Git リモート リポジトリはこのディレ
|
||||
run_user=実行ユーザ
|
||||
run_user_helper=ユーザーはリポジトリ ルートパスへのアクセス、及びGogs を実行する権限を所有する必要があります。
|
||||
domain=ドメイン
|
||||
domain_helper=これはSSHクローンURLに影響する。
|
||||
domain_helper=これはSSH用クローンURLに影響します。
|
||||
ssh_port=SSH ポート
|
||||
ssh_port_helper=あならのSSHサーバおポート番号、SSH機能を無効するにはここを空白のままにしてください。
|
||||
ssh_port_helper=SSHサーバーを使用する場合はポート番号を入力してください。 空白にした場合は無効化されます。
|
||||
http_port=HTTP ポート
|
||||
http_port_helper=アプリケーションが待ち受けするポート番号。
|
||||
app_url=アプリケーションの URL
|
||||
app_url_helper=この設定は、HTTP / HTTPSのクローンURLおよび、一部のメールボックスへのリンクに影響を与えます。
|
||||
log_root_path=ログのパス
|
||||
log_root_path_helper=ログファイルを書き込むディレクトリ。
|
||||
|
||||
optional_title=オプション設定
|
||||
email_title=E-mailサービス設定
|
||||
email_title=メールサービス設定
|
||||
smtp_host=SMTP ホスト
|
||||
smtp_from=差出人
|
||||
smtp_from_helper=送信者メールアドレス、RFC 5322。フォーマットはメールアドレスのみ、または"Name" <email@example.com>。
|
||||
@@ -97,10 +92,12 @@ mailer_password=送信者のパスワード
|
||||
register_confirm=登録の確認を有効にする
|
||||
mail_notify=メール通知を有効にする
|
||||
server_service_title=サーバーとその他のサービスの設定
|
||||
offline_mode=オフラインモード有効化
|
||||
offline_mode_popup=プロダクション モードでCDN を無効にし、すべてのリソースファイルをローカルで提供します 。
|
||||
offline_mode=オフラインモードを有効にする
|
||||
offline_mode_popup=プロダクションモードでは、CDNを使用せずにローカルからリソースファイルを使用します。
|
||||
disable_gravatar=Gravatarのサービスを無効にします
|
||||
disable_gravatar_popup=Disable Gravatar and custom sources, all avatars are uploaded by users or default.
|
||||
disable_gravatar_popup=Gravatarとカスタムソースを無効にして、全てのアバターをユーザーによってアップロードされたものかデフォルトなものにします。
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=自己登録を無効にする
|
||||
disable_registration_popup=自己登録を無効にし、管理者のみがアカウント作成できる
|
||||
enable_captcha=Captchaを有効にする
|
||||
@@ -119,53 +116,61 @@ sqlite3_not_available=このリリース バージョンは SQLite3 をサポー
|
||||
invalid_db_setting=データベースの設定が正しくありません: %v
|
||||
invalid_repo_path=リポジトリのルート パスが無効です: %v
|
||||
run_user_not_match=実行ユーザーは、現在のユーザーではない: %s-> %s
|
||||
invalid_smtp_from=SMTP From field is not valid: %v
|
||||
save_config_failed=構成の保存に失敗した: %v
|
||||
invalid_admin_setting=管理者アカウントの設定が無効です: %v
|
||||
install_success=ようこそ!我々はあなたが Gogs を選んでくれて嬉しいです!楽しみましょう!
|
||||
invalid_log_root_path=ログのルートパスがむこうです: %v
|
||||
|
||||
[home]
|
||||
uname_holder=ユーザー名またはEメール
|
||||
password_holder=パスワード
|
||||
switch_dashboard_context=ダッシュ ボードのコンテキストを切替
|
||||
my_repos=私のリポジトリ
|
||||
my_repos=自分のリポジトリ
|
||||
show_more_repos=リポジトリをさらに表示…
|
||||
collaborative_repos=共同リポジトリ
|
||||
my_orgs=私の組織
|
||||
my_mirrors=私のミラー
|
||||
my_orgs=自分の組織
|
||||
my_mirrors=自分のミラー
|
||||
view_home=ビュー %s
|
||||
|
||||
issues.in_your_repos=あなたのリポジトリ
|
||||
|
||||
[explore]
|
||||
repos=リポジトリ
|
||||
users=ユーザ
|
||||
organizations=組織
|
||||
search=検索
|
||||
|
||||
[auth]
|
||||
create_new_account=新規アカウントを作成
|
||||
register_hepler_msg=すでにアカウントをお持ちですか?今すぐログイン !
|
||||
social_register_hepler_msg=すでにアカウントをお持ちですか?今すぐバインド !
|
||||
disable_register_prompt=申し訳ありませんが、登録が無効になっています。サイト管理者に問い合わせてください。
|
||||
register_hepler_msg=既にアカウントをお持ちですか?今すぐログインしましょう!
|
||||
social_register_hepler_msg=既にアカウントをお持ちですか?連携しましょう!
|
||||
disable_register_prompt=申し訳ありませんが、現在登録は受け付けておりません。サイトの管理者にお問い合わせください。
|
||||
disable_register_mail=申し訳ありませんが、登録メールの確認機能が無効になっています。
|
||||
remember_me=ログイン状態を保持する
|
||||
remember_me=ログインしたままにする
|
||||
forgot_password=パスワードを忘れた
|
||||
forget_password=パスワードを忘れた?
|
||||
sign_up_now=アカウントが必要ですか?今すぐサインアップ
|
||||
forget_password=パスワードを忘れましたか?
|
||||
sign_up_now=アカウントが必要ですか?今すぐ登録しましょう!
|
||||
confirmation_mail_sent_prompt=新しい確認メールを <b>%s</b> に送りました。登録を完了させるために、%d時間以内にあなたのメールボックスを確認してください。
|
||||
active_your_account=アカウントをアクティブ
|
||||
active_your_account=アカウントを有効化
|
||||
prohibit_login=ログイン禁止
|
||||
prohibit_login_desc=あなたのアカウントはログインを禁止されています。サイト管理者にお問い合わせください。
|
||||
resent_limit_prompt=申し訳ありませんが、アクティベーションメールは頻繁に送信しています。3 分お待ちください。
|
||||
has_unconfirmed_mail=こんにちは %s さん、あなたの電子メール アドレス (<b>%s</b>) は未確認です。もし確認メールをまだ確認できていないか、改めて再送信する場合は、下のボタンをクリックしてください。
|
||||
resend_mail=アクティベーションメールを再送信するにはここをクリック
|
||||
email_not_associate=この電子メール アドレスは、アカウントには関連付けられません。
|
||||
send_reset_mail=パスワードリセットのメールを再送するにはここをクリック
|
||||
reset_password=パスワードリセット
|
||||
invalid_code=申し訳ありませんが、確認用コードが期限切れまたは無効です。
|
||||
reset_password_helper=パスワードをリセットするにはここをクリック
|
||||
password_too_short=6文字未満のパスワードは設定できません。
|
||||
non_local_account=Non-local accounts cannot change passwords through Gogs.
|
||||
|
||||
[mail]
|
||||
activate_account=あなたのアカウントを有効にしてください。
|
||||
activate_email=電子メール アドレスを確認します。
|
||||
reset_password=パスワードをリセットします.
|
||||
register_success=ようこそ、登録成功
|
||||
register_notify=Welcome on board
|
||||
register_notify=ボードへようこそ
|
||||
|
||||
[modal]
|
||||
yes=はい
|
||||
@@ -185,6 +190,13 @@ TeamName=チーム名
|
||||
AuthName=承認名
|
||||
AdminEmail=管理者の電子メール
|
||||
|
||||
NewBranchName=新しいブランチ名
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=コンテンツ
|
||||
|
||||
require_error=空にできません
|
||||
alpha_dash_error=アルファベット、数字、ハイフン"-"、アンダースコア"_"のいずれかの必要があります
|
||||
alpha_dash_dot_error=' アルファベット、数値、ダッシュ(-)、アンダースコア(_) 、ドット(.)のいずれかを入力する必要があります。 '
|
||||
@@ -203,7 +215,6 @@ repo_name_been_taken=リポジトリ名は既に使用されています。
|
||||
org_name_been_taken=組織名は既に使用されています。
|
||||
team_name_been_taken=チーム名は既に使用されています。
|
||||
email_been_used=電子メール アドレスは既に使用されています。
|
||||
illegal_team_name=チーム名に無効な文字が含まれています。
|
||||
username_password_incorrect=ユーザー名またはパスワードが正しくありません。
|
||||
enterred_invalid_repo_name=入力したリポジトリの名前が正しいかどうかを確認してください。
|
||||
enterred_invalid_owner_name=入力された所有者名が正しいかどうかを確認してください。
|
||||
@@ -219,13 +230,10 @@ still_own_repo=アカウント所有のリポジトリがあり、リポジト
|
||||
still_has_org=アカウントはまだ組織のメンバーであり、組織から退出するか削除する必要があります。
|
||||
org_still_own_repo=この組織はまだリポジトリの所有しています、リポジトリを削除または転送する必要があります。
|
||||
|
||||
still_own_user=この認証はまだ一部のユーザーによって使用されています。一部のユーザを移動させてから、もう一度削除してください。
|
||||
|
||||
target_branch_not_exist=ターゲットブランチが存在しない
|
||||
|
||||
[user]
|
||||
change_avatar=gravatar.com で自分のアバターを変更
|
||||
change_custom_avatar=設定で自分のアバターを変更
|
||||
change_avatar=アバターを変更
|
||||
join_on=参加しました
|
||||
repositories=リポジトリ
|
||||
activity=パブリック・アクティビティ
|
||||
@@ -241,6 +249,7 @@ form.name_pattern_not_allowed=ユーザ名のパターン '%s' は許可され
|
||||
[settings]
|
||||
profile=プロフィール
|
||||
password=パスワード
|
||||
avatar=アバター
|
||||
ssh_keys=SSH キー
|
||||
social=SNSアカウント
|
||||
applications=アプリケーション
|
||||
@@ -261,12 +270,13 @@ change_username_prompt=この変更はリンクをアカウントに関連付け
|
||||
continue=続行
|
||||
cancel=キャンセル
|
||||
|
||||
lookup_avatar_by_mail=メールからアバターを取得
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=カスタムのアバターを有効にする
|
||||
enable_custom_avatar_helper=Gravatarからのフェッチを無効にするのを、有効にします
|
||||
choose_new_avatar=新しいアバターを選択
|
||||
update_avatar=アバターの設定を更新
|
||||
delete_current_avatar=現在のアバターを削除
|
||||
uploaded_avatar_not_a_image=アップロードされたファイルは画像ではない。
|
||||
no_custom_avatar_available=利用可能なカスタム アバターがないため、有効にできません。
|
||||
update_avatar_success=あなたのアバターの設定が更新されました。
|
||||
|
||||
change_password=パスワードを変更
|
||||
@@ -316,7 +326,7 @@ social_desc=これは関連付けられたソーシャルアカウントのリ
|
||||
unbind=バインド解除
|
||||
unbind_success=SNSアカウントがバインドされていない。
|
||||
|
||||
manage_access_token=個人のアクセス トークンを管理
|
||||
manage_access_token=パーソナルアクセス トークンを管理
|
||||
generate_new_token=新しいトークンを生成
|
||||
tokens_desc=生成したトークンを利用して Gogs の API にアクセスすることができます。
|
||||
new_token_desc=今のところ、全てのトークンはあなたのアカウントにフルアクセスできます。
|
||||
@@ -328,8 +338,12 @@ access_token_deletion=パーソナルアクセストークンの削除
|
||||
access_token_deletion_desc=パーソナルアクセストークンを削除すると、関連するアプリケーションのすべてのアクセスが削除されます。続行しますか?
|
||||
delete_token_success=パーソナルアクセストークンは正常に削除されました!同時にあなたのアプリケーションを更新することを忘れないでください。
|
||||
|
||||
orgs.none=You are not a member of any organizations.
|
||||
orgs.leave_title=Leave an organization
|
||||
orgs.leave_desc=You will lose access to all repositories and teams after you left the organization. Do you want to continue?
|
||||
|
||||
delete_account=アカウントを削除
|
||||
delete_prompt=この操作はあなたのアカウントを完全に削除し、復旧<strong>できない</strong> !
|
||||
delete_prompt=この操作をするとアカウントが完全に削除され、<strong>二度と元に戻すことができなくなります</strong> !
|
||||
confirm_delete_account=削除の確認
|
||||
delete_account_title=アカウントの削除
|
||||
delete_account_desc=このアカウントは永久に削除しようとしている、継続しますか?
|
||||
@@ -337,18 +351,18 @@ delete_account_desc=このアカウントは永久に削除しようとしてい
|
||||
[repo]
|
||||
owner=オーナー
|
||||
repo_name=リポジトリ名
|
||||
repo_name_helper=偉大なリポジトリ名は短い。思い出に残り、そして<strong>一意</strong>だ。
|
||||
visibility=ビジビリティ
|
||||
repo_name_helper=短くて分かりやすく<strong>重複しない</strong>リポジトリ名を決めてください。
|
||||
visibility=公開/非公開
|
||||
visiblity_helper=このリポジトリは<span class="ui red text">プライベート</span>です。
|
||||
visiblity_helper_forced=サイト管理者は、強制的にすべての新しいリポジトリを<span class="ui red text"> プライベート</span> にしています。
|
||||
visiblity_fork_helper=(この値の変更はすべてのフォークに適用されます)
|
||||
clone_helper=クローニングのヘルプが必要ですか?<a target="_blank"href="%s"> ヘルプ</a> を参照してください!
|
||||
fork_repo=フォークのリポジトリ
|
||||
clone_helper=クローンに関してお困りであれば<a target="_blank"href="%s"> ヘルプ</a> を参照しましょう。
|
||||
fork_repo=リポジトリをフォーク
|
||||
fork_from=フォーク元
|
||||
fork_visiblity_helper=フォークされたリポジトリは可視状態を変更できません
|
||||
fork_visiblity_helper=フォークされたリポジトリの可視状態は変更できません。
|
||||
repo_desc=説明
|
||||
repo_lang=言語
|
||||
repo_lang_helper=.gitignoreファイルを選択
|
||||
repo_gitignore_helper=Select .gitignore templates
|
||||
license=ライセンス
|
||||
license_helper=ライセンス ファイルを選択
|
||||
readme=Readme
|
||||
@@ -356,28 +370,32 @@ readme_helper=Readme ファイルのテンプレートを選択
|
||||
auto_init=選択されたファイルおよびテンプレートでリポジトリを初期化
|
||||
create_repo=リポジトリを作成
|
||||
default_branch=デフォルトのブランチ
|
||||
mirror_prune=Prune
|
||||
mirror_prune_desc=Remove any remote-tracking references that no longer exist on the remote
|
||||
mirror_interval=ミラー 間隔(時)
|
||||
mirror_address=ミラー アドレス
|
||||
mirror_address_desc=Please include necessary user credentials in the address.
|
||||
mirror_last_synced=最終同期
|
||||
watchers=ウォッチャー
|
||||
stargazers=Stargazers
|
||||
forks=フォーク
|
||||
|
||||
form.reach_limit_of_creation=The owner has reached maximum creation limit of %d repositories.
|
||||
form.name_reserved=リポジトリ名 '%s' は予約されています。
|
||||
form.name_pattern_not_allowed=リポジトリ名のパターン '%s' は許可されていません。
|
||||
form.name_reserved=リポジトリ名 '%s' は使用されています。
|
||||
form.name_pattern_not_allowed=リポジトリ名に '%s' は使用できません。
|
||||
|
||||
need_auth=認証が必要
|
||||
migrate_type=マイグレーションの種類
|
||||
migrate_type_helper=このリポジトリは、<span class="text blue"> ミラー</span> になります
|
||||
migrate_repo=リポジトリを移行
|
||||
migrate.clone_address=クローンアドレス
|
||||
migrate.clone_address_desc=これは、HTTP/HTTPS/GIT URL またはローカル サーバー パスを設定できます。
|
||||
migrate.clone_address_desc=This can be a HTTP/HTTPS/GIT URL.
|
||||
migrate.clone_address_desc_import_local=You're also allowed to migrate a repository by local server path.
|
||||
migrate.permission_denied=ローカル リポジトリをインポートすることはできません。
|
||||
migrate.invalid_local_path=ローカルパスが無効です。存在しないかディレクトリではありません。
|
||||
migrate.failed=移行に失敗しました: %v
|
||||
|
||||
mirror_from=mirror from
|
||||
mirror_from=同期ミラー
|
||||
forked_from=フォーク元
|
||||
fork_from_self=すでにあなたの所有しているリポジトリはフォークできません
|
||||
copy_link=コピー
|
||||
@@ -397,7 +415,7 @@ create_new_repo_command=コマンドラインで新しいリポジトリを作
|
||||
push_exist_repo=コマンド ・ ラインから既存のリポジトリをプッシュ
|
||||
repo_is_empty=このリポジトリは空です、後で戻って来て下さい!
|
||||
|
||||
code=コード
|
||||
files=Files
|
||||
branch=ブランチ
|
||||
tree=ツリー
|
||||
filter_branch_and_tag=ブランチまたはタグをフィルタリング
|
||||
@@ -409,10 +427,51 @@ labels=ラベル
|
||||
milestones=マイルストーン
|
||||
commits=コミット
|
||||
releases=リリース
|
||||
file_raw=生データ
|
||||
file_raw=Raw
|
||||
file_history=履歴
|
||||
file_view_raw=生データを見る
|
||||
file_view_raw=Rawデータを見る
|
||||
file_permalink=パーマリンク
|
||||
file_too_large=このファイルは大きすぎるため、表示できません。
|
||||
video_not_supported_in_browser=Your browser doesn't support HTML5 video tag.
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=ファイルを編集
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=このファイルを編集
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=このファイルを削除
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=キャンセル
|
||||
editor.commit_changes=変更をコミット
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add='%s' を追加
|
||||
editor.update='%s' を更新
|
||||
editor.delete='%s' を削除
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=キャンセル
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.file_is_a_symlink=The file '%s' is a symlink that cannot be modified from the web editor.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=コミット
|
||||
commits.search=コミットの検索
|
||||
@@ -439,6 +498,11 @@ issues.create=問題を作成
|
||||
issues.new_label=新しいラベル
|
||||
issues.new_label_placeholder=ラベル名...
|
||||
issues.create_label=ラベルを作成
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d オープン
|
||||
issues.close_tab=%d クローズ
|
||||
issues.filter_label=ラベル
|
||||
@@ -449,8 +513,8 @@ issues.filter_assignee=アサインされた人
|
||||
issues.filter_assginee_no_select=選択可能な担当者がいない
|
||||
issues.filter_type=タイプ
|
||||
issues.filter_type.all_issues=すべての問題
|
||||
issues.filter_type.assigned_to_you=あなたに割り当てられました。
|
||||
issues.filter_type.created_by_you=あなたが作成しました。
|
||||
issues.filter_type.assigned_to_you=担当中のリポジトリ
|
||||
issues.filter_type.created_by_you=作成したリポジトリ
|
||||
issues.filter_type.mentioning_you=あなたに伝える
|
||||
issues.filter_sort=並べ替え
|
||||
issues.filter_sort.latest=最新
|
||||
@@ -466,10 +530,11 @@ issues.next=次ページ
|
||||
issues.open_title=オープン
|
||||
issues.closed_title=クローズ
|
||||
issues.num_comments=%d コメント
|
||||
issues.commented_at=`コメント <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commented_at=`commented <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=Are you sure you want to delete this comment?
|
||||
issues.no_content=まだコンテンツがありません
|
||||
issues.close_issue=閉じる
|
||||
issues.close_comment_issue=コメントと閉じる
|
||||
issues.close_comment_issue=コメントしてクローズ
|
||||
issues.reopen_issue=Reopen
|
||||
issues.reopen_comment_issue=コメントと再開
|
||||
issues.create_comment=コメント
|
||||
@@ -477,10 +542,9 @@ issues.closed_at=`closed <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at=`reopened <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commit_ref_at=`referenced this issue from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.poster=ポスター
|
||||
issues.admin=アドミン
|
||||
issues.collaborator=コラボレータ
|
||||
issues.owner=オーナー
|
||||
issues.sign_up_for_free=無料でサインアップ
|
||||
issues.sign_in_require_desc=to join this conversation. Already have an account? <a href="%s">Sign in to comment</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=編集
|
||||
issues.cancel=キャンセル
|
||||
issues.save=保存
|
||||
@@ -494,6 +558,9 @@ issues.label_modify=ラベルの変更
|
||||
issues.label_deletion=ラベルの削除
|
||||
issues.label_deletion_desc=ラベルを削除すると、関連するすべての問題の情報が削除されます。続行しますか。
|
||||
issues.label_deletion_success=ラベルは正常に削除されました。
|
||||
issues.num_participants=%d Participants
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=新しいプルリクエスト
|
||||
pulls.compare_changes=変更を比較
|
||||
@@ -503,7 +570,7 @@ pulls.compare_compare=比較
|
||||
pulls.filter_branch=フィルターブランチ
|
||||
pulls.no_results=結果が見つかりませんでした。
|
||||
pulls.nothing_to_compare=There is nothing to compare because base and head branches are even.
|
||||
pulls.has_pull_request=`There is already a pull request between these two targets: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
|
||||
pulls.has_pull_request=`既にプルリクエストがこれらのターゲット間に存在します: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
|
||||
pulls.create=プルリクエストを作成します。
|
||||
pulls.title_desc=wants to merge %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code>
|
||||
pulls.merged_title_desc=merged %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> %[4]s
|
||||
@@ -520,6 +587,8 @@ pulls.cannot_auto_merge_desc=This pull request can't be merged automatically bec
|
||||
pulls.cannot_auto_merge_helper=競合を解決するためには、手動でマージする必要があります。
|
||||
pulls.merge_pull_request=プルリクエストをマージします。
|
||||
pulls.open_unmerged_pull_exists=`You can't perform reopen operation because there is already an open pull request (#%d) from same repository with same merge information and is waiting for merging.`
|
||||
pulls.delete_branch=Delete Branch
|
||||
pulls.delete_branch_has_new_commits=Branch cannot be deleted because it has new commits after mergence.
|
||||
|
||||
milestones.new=新しいマイルストーン
|
||||
milestones.open_tab=%d オープン
|
||||
@@ -557,6 +626,8 @@ wiki.save_page=ページを保存
|
||||
wiki.last_commit_info=%s このページを編集 %s
|
||||
wiki.edit_page_button=編集
|
||||
wiki.new_page_button=新規ページ
|
||||
wiki.delete_page_button=ページの削除
|
||||
wiki.delete_page_notice_1=This will delete the page <code>"%s"</code>. Please be certain.
|
||||
wiki.page_already_exists=既に同じ名前のWiki ページが存在します。
|
||||
wiki.pages=ページ
|
||||
wiki.last_updated=最終更新 %s
|
||||
@@ -564,46 +635,71 @@ wiki.last_updated=最終更新 %s
|
||||
settings=設定
|
||||
settings.options=オプション
|
||||
settings.collaboration=コラボレーション
|
||||
settings.collaboration.admin=管理
|
||||
settings.collaboration.write=書込
|
||||
settings.collaboration.read=読込
|
||||
settings.collaboration.undefined=Undefined
|
||||
settings.hooks=Webhooks
|
||||
settings.githooks=Git のフック
|
||||
settings.basic_settings=基本設定
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=今すぐ同期
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=公式サイト
|
||||
settings.update_settings=設定の更新
|
||||
settings.change_reponame_prompt=この変更はリンクがリポジトリに関連付ける方法に影響します。
|
||||
settings.advanced_settings=拡張設定
|
||||
settings.wiki_desc=Wikiでドキュメントの作成を許可
|
||||
settings.wiki_desc=Enable wiki system
|
||||
settings.use_internal_wiki=Use builtin wiki
|
||||
settings.use_external_wiki=外部 wiki を使用します。
|
||||
settings.external_wiki_url=外部 Wiki の URL
|
||||
settings.external_wiki_url_desc=Visitors will be redirected to URL when they click on the tab.
|
||||
settings.issues_desc=組み込み簡易課題トラッカーを有効
|
||||
settings.issues_desc=Enable issue tracker
|
||||
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=外部課題トラッキングシステムを使用
|
||||
settings.external_tracker_url=External Issue Tracker URL
|
||||
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
|
||||
settings.tracker_url_format=外部課題トラッキングツール URLのフォーマット
|
||||
settings.tracker_issue_style=External Issue Tracker Naming Style:
|
||||
settings.tracker_issue_style.numeric=数値
|
||||
settings.tracker_issue_style.alphanumeric=Alphanumeric
|
||||
settings.tracker_url_format_desc=You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index.
|
||||
settings.pulls_desc=Enable pull requests to accept public contributions
|
||||
settings.danger_zone=危険地帯
|
||||
settings.new_owner_has_same_repo=新しいオーナーは、既に同じ名前のリポジトリを持っています。
|
||||
settings.convert=Convert To Regular Repository
|
||||
settings.convert_desc=You can convert this mirror to a regular repository. This cannot be reversed.
|
||||
settings.convert_notices_1=- This operation will convert this repository mirror into a regular repository and cannot be undone.
|
||||
settings.convert_confirm=Confirm Conversion
|
||||
settings.convert_succeed=Repository has been converted to regular type successfully.
|
||||
settings.transfer=オーナー移転
|
||||
settings.transfer_desc=リポジトリをあなたが管理者権限を持っている別のユーザーまた組織に移譲します。
|
||||
settings.new_owner_has_same_repo=新しいオーナーは、既に同じ名前のリポジトリを持っています。
|
||||
settings.delete=このリポジトリを削除
|
||||
settings.delete_desc=リポジトリを削除すると元に戻せません。確実に確認してください。
|
||||
settings.transfer_notices_1=-新しい所有者が個人ユーザーの場合、あなたがアクセスできなくなります。
|
||||
settings.transfer_notices_2=- You will conserve access if new owner is an organization and if you're one of the owners.
|
||||
settings.transfer_form_title=操作を確認するために、以下の情報を入力してください。
|
||||
settings.wiki_delete=Wikiのデータ消去
|
||||
settings.wiki_delete_desc=Wikiのデータを消去すると元に戻すことは出来ません。よく確認してください。
|
||||
settings.wiki_delete_notices_1=- This will delete and disable the wiki for %s
|
||||
settings.wiki_deletion_success=Wikiのデータ消去が完了しました。
|
||||
settings.delete=このリポジトリを削除
|
||||
settings.delete_desc=リポジトリを削除すると元に戻せません。確実に確認してください。
|
||||
settings.delete_notices_1=-この操作は<strong>元に戻せません</strong> 。
|
||||
settings.delete_notices_2=- This operation will permanently delete the everything of this repository, including Git data, issues, comments and accesses of collaborators.
|
||||
settings.delete_notices_fork_1=- If this repository is public, all forks will be became independent after deletion.
|
||||
settings.delete_notices_fork_2=- If this repository is private, all forks will be removed at the same time.
|
||||
settings.delete_notices_fork_3=- If you want to keep all forks after deletion, please change visibility of this repository to public first.
|
||||
settings.delete_notices_fork_1=- All forks will become independent after deletion.
|
||||
settings.deletion_success=Repository has been deleted successfully!
|
||||
settings.update_settings_success=リポジトリ オプションが更新されました。
|
||||
settings.transfer_owner=新しいオーナー
|
||||
settings.make_transfer=転送
|
||||
settings.transfer_succeed=リポジトリの所有権は正常に転送されました。
|
||||
settings.confirm_delete=削除の確認
|
||||
settings.confirm_delete=削除を確認
|
||||
settings.add_collaborator=新しい共同編集者を追加
|
||||
settings.add_collaborator_success=新しい共同編集者が追加されました。
|
||||
settings.delete_collaborator=Delete
|
||||
settings.collaborator_deletion=Collaborator Deletion
|
||||
settings.collaborator_deletion_desc=This user will no longer have collaboration access to this repository after deletion. Do you want to continue?
|
||||
settings.remove_collaborator_success=共同編集者が削除されました。
|
||||
settings.search_user_placeholder=Search users
|
||||
settings.org_not_allowed_to_be_collaborator=組織を共同編集者として追加することはできません。
|
||||
settings.user_is_org_member=ユーザーは組織の一員なので、共同編集者として追加することはできません。
|
||||
settings.add_webhook=Webhook を追加
|
||||
settings.hooks_desc=Webhooksは、Gogsで特定のイベントの発生時に指定された外部サービスに通知を許可します。イベントが発生すると、それぞれ指定されたUrlに、POSTリクエストが送られます。詳細はこちらのの <a target="_blank"href="%s"> Webhooks ガイド</a>をご覧ください。
|
||||
@@ -636,6 +732,8 @@ settings.event_send_everything=<strong>すべて</strong> が必要です。
|
||||
settings.event_choose=必要なものを選択しましょう。
|
||||
settings.event_create=Create
|
||||
settings.event_create_desc=ブランチ、またはタグを作成
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=プッシュ
|
||||
settings.event_push_desc=Git リポジトリにプッシュ
|
||||
settings.active=アクティブ
|
||||
@@ -668,11 +766,13 @@ diff.parent=親
|
||||
diff.commit=コミット
|
||||
diff.data_not_available=差分データは利用できません。
|
||||
diff.show_diff_stats=差分情報を表示
|
||||
diff.show_split_view=Split View
|
||||
diff.show_split_view=分割表示
|
||||
diff.show_unified_view=Unified View
|
||||
diff.stats_desc=共有<strong>%d 個のファイルを変更した</strong>、<strong>%d 個の追加</strong> と <strong>%d 個の削除</strong>を含む
|
||||
diff.bin=BIN
|
||||
diff.view_file=ファイルの表示
|
||||
diff.file_suppressed=File diff suppressed because it is too large
|
||||
diff.too_many_files=Some files were not shown because too many files changed in this diff
|
||||
|
||||
release.releases=リリース
|
||||
release.new_release=新しいリリース
|
||||
@@ -700,9 +800,10 @@ release.save_draft=下書きを保存
|
||||
release.edit_release=リリースを編集
|
||||
release.delete_release=このリリースを削除
|
||||
release.deletion=リリースの削除
|
||||
release.deletion_desc=Deleting this release will delete the corresponding Git tag. Do you want to continue?
|
||||
release.deletion_desc=このリリースを削除すると、対応するGitのタグも削除されます。よろしいですか?
|
||||
release.deletion_success=リリースが正常に削除されました。
|
||||
release.tag_name_already_exist=このタグ名には既にリリースが存在します。
|
||||
release.tag_name_invalid=Tag name is not valid.
|
||||
release.downloads=Downloads
|
||||
|
||||
[org]
|
||||
@@ -710,7 +811,7 @@ org_name_holder=組織名
|
||||
org_full_name_holder=組織のフルネーム
|
||||
org_name_helper=偉大な組織の名は短く覚えやすいです。
|
||||
create_org=組織を作成
|
||||
repo_updated=更新した
|
||||
repo_updated=最終更新
|
||||
people=人々
|
||||
invite_someone=誰かを招待
|
||||
teams=チーム
|
||||
@@ -738,8 +839,8 @@ settings.change_orgname_prompt=This change will affect how links relate to the o
|
||||
settings.update_avatar_success=組織のアバター画像が正常に更新されました。
|
||||
settings.delete=組織を削除
|
||||
settings.delete_account=この組織を削除
|
||||
settings.delete_prompt=操作はこの組織を完全に削除し、復旧<strong>できない</strong>!
|
||||
settings.confirm_delete_account=削除の確認
|
||||
settings.delete_prompt=この操作をすると組織が完全に削除され、<strong>二度と元に戻すことができなくなります</strong> !
|
||||
settings.confirm_delete_account=削除を確認
|
||||
settings.delete_org_title=組織の削除
|
||||
settings.delete_org_desc=この組織は完全に削除されます、継続しますか?
|
||||
settings.hooks_desc=この組織のもとで <strong>すべてのリポジトリ</strong> に対してトリガーされる webhook を追加します。
|
||||
@@ -818,6 +919,8 @@ dashboard.resync_all_sshkeys='.ssh/ authorized_keys' ファイルを再生成し
|
||||
dashboard.resync_all_sshkeys_success=すべての公開鍵が正常に書き換えられました。
|
||||
dashboard.resync_all_update_hooks=リポジトリの update フックをすべて再更新する(カスタムの設定パスが変更されたときに必要)
|
||||
dashboard.resync_all_update_hooks_success=リポジトリの update フックがすべて正常に再更新されました。
|
||||
dashboard.reinit_missing_repos=Reinitialize all repository records that lost Git files
|
||||
dashboard.reinit_missing_repos_success=All repository records that lost Git files have been reinitialized successfully.
|
||||
|
||||
dashboard.server_uptime=サーバーの稼働時間
|
||||
dashboard.current_goroutine=現在のGoroutine
|
||||
@@ -868,6 +971,7 @@ users.edit_account=アカウントの編集
|
||||
users.max_repo_creation=Maximum Repository Creation Limit
|
||||
users.max_repo_creation_desc=(Set -1 to use global default limit)
|
||||
users.is_activated=アカウントがアクティブされました
|
||||
users.prohibit_login=This account is prohibited to login
|
||||
users.is_admin=このアカウントには管理者の権限を持つ
|
||||
users.allow_git_hook=このアカウントには Git のフックを作成する権限を持つ
|
||||
users.allow_import_local=This account has permissions to import local repositories
|
||||
@@ -895,9 +999,10 @@ auths.new=新しいソースを追加
|
||||
auths.name=名前
|
||||
auths.type=タイプ
|
||||
auths.enabled=Enabled
|
||||
auths.updated=Updated
|
||||
auths.updated=更新しました
|
||||
auths.auth_type=認証タイプ
|
||||
auths.auth_name=認証名
|
||||
auths.security_protocol=Security Protocol
|
||||
auths.domain=ドメイン
|
||||
auths.host=ホスト
|
||||
auths.port=ポート
|
||||
@@ -911,6 +1016,7 @@ auths.attribute_username_placeholder=Leave empty to use sign-in form field value
|
||||
auths.attribute_name=名前属性
|
||||
auths.attribute_surname=名字属性
|
||||
auths.attribute_mail=Eメール属性
|
||||
auths.attributes_in_bind=Fetch attributes in Bind DN context
|
||||
auths.filter=User フィルター
|
||||
auths.admin_filter=Admin フィルター
|
||||
auths.ms_ad_sa=Ms Ad SA
|
||||
@@ -932,7 +1038,9 @@ auths.update=認証設定を更新
|
||||
auths.delete=この認証を削除
|
||||
auths.delete_auth_title=認証削除
|
||||
auths.delete_auth_desc=認証を削除します、継続しますか?
|
||||
auths.still_in_used=This authentication is still used by some users, please delete or convert these users to another login type first.
|
||||
auths.deletion_success=認証が正常に削除されました。
|
||||
auths.login_source_exist=Login source '%s' already exists.
|
||||
|
||||
config.server_config=サーバーの構成
|
||||
config.app_name=アプリケーション名
|
||||
@@ -943,11 +1051,25 @@ config.offline_mode=オフラインモード
|
||||
config.disable_router_log=ルーターのログを無効にする
|
||||
config.run_user=実行ユーザ
|
||||
config.run_mode=実行モード
|
||||
config.git_version=Git Version
|
||||
config.repo_root_path=リポジトリのルートパス
|
||||
config.static_file_root_path=静的ファイルのルートパス
|
||||
config.log_file_root_path=ログ ファイルのルート パス
|
||||
config.script_type=スクリプトの種類
|
||||
config.reverse_auth_user=リバース認証ユーザ
|
||||
|
||||
config.ssh_config=SSH Configuration
|
||||
config.ssh_enabled=Enabled
|
||||
config.ssh_start_builtin_server=Start Builtin Server
|
||||
config.ssh_domain=Domain
|
||||
config.ssh_port=Port
|
||||
config.ssh_listen_port=Listen Port
|
||||
config.ssh_root_path=Root Path
|
||||
config.ssh_key_test_path=Key Test Path
|
||||
config.ssh_keygen_path=Keygen ('ssh-keygen') Path
|
||||
config.ssh_minimum_key_size_check=Minimum Key Size Check
|
||||
config.ssh_minimum_key_sizes=Minimum Key Sizes
|
||||
|
||||
config.db_config=データベースの構成
|
||||
config.db_type=タイプ
|
||||
config.db_host=ホスト
|
||||
@@ -957,45 +1079,68 @@ config.db_ssl_mode=SSL モード
|
||||
config.db_ssl_mode_helper=(「postgres」のみ)
|
||||
config.db_path=パス
|
||||
config.db_path_helper=(for "sqlite3" and "tidb")
|
||||
|
||||
config.service_config=サービスの構成
|
||||
config.register_email_confirm=電子メールの確認を必要
|
||||
config.disable_register=登録を無効にする
|
||||
config.show_registration_button=登録ボタンを表示します。
|
||||
config.require_sign_in_view=サインインを要求
|
||||
config.enable_cache_avatar=アバターのキャッシュを有効にします。
|
||||
config.mail_notify=メール通知
|
||||
config.disable_key_size_check=最小キー サイズ チェックを無効にします
|
||||
config.enable_captcha=Captchaを有効にする
|
||||
config.active_code_lives=コードリンクの有効期限をアクティブ
|
||||
config.reset_password_code_lives=パスワードリンクの有効期限をリセット
|
||||
|
||||
config.webhook_config=Webhook設定
|
||||
config.queue_length=キューの長さ
|
||||
config.deliver_timeout=送信タイムアウト
|
||||
config.skip_tls_verify=TLSの確認を省略
|
||||
|
||||
config.mailer_config=メーラーの構成
|
||||
config.mailer_enabled=有効にした
|
||||
config.mailer_disable_helo=HELOコマンド無効
|
||||
config.mailer_name=名前
|
||||
config.mailer_host=ホスト
|
||||
config.mailer_user=ユーザ
|
||||
config.send_test_mail=Send Test Email
|
||||
config.test_mail_failed=Fail to send test email to '%s': %v
|
||||
config.test_mail_sent=Test email has been sent to '%s'.
|
||||
|
||||
config.oauth_config=OAuth 構成
|
||||
config.oauth_enabled=Enabled
|
||||
|
||||
config.cache_config=キャッシュの構成
|
||||
config.cache_adapter=キャッシュ アダプター
|
||||
config.cache_interval=キャッシュ間隔
|
||||
config.cache_conn=キャッシュ接続
|
||||
|
||||
config.session_config=セッションの構成
|
||||
config.session_provider=セッション プロバイダー
|
||||
config.provider_config=プロバイダーの構成
|
||||
config.cookie_name=クッキー名
|
||||
config.cookie_name=クッキーの名前
|
||||
config.enable_set_cookie=クッキーの設定を有効にする
|
||||
config.gc_interval_time=GC 間隔
|
||||
config.session_life_time=セッションのライフタイム
|
||||
config.https_only=HTTPS のみ
|
||||
config.cookie_life_time=クッキーのライフタイム
|
||||
|
||||
config.picture_config=画像構成
|
||||
config.picture_service=画像サービス
|
||||
config.disable_gravatar=グラバターを無効にする
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Git Configuration
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=GC Arguments
|
||||
config.git_migrate_timeout=Migration Timeout
|
||||
config.git_mirror_timeout=Mirror Update Timeout
|
||||
config.git_clone_timeout=Clone Operation Timeout
|
||||
config.git_pull_timeout=Pull Operation Timeout
|
||||
config.git_gc_timeout=GC Operation Timeout
|
||||
|
||||
config.log_config=ログの構成
|
||||
config.log_mode=ログ モード
|
||||
|
||||
@@ -1025,11 +1170,15 @@ notices.op=Op。
|
||||
notices.delete_success=システム通知が正常に削除されました。
|
||||
|
||||
[action]
|
||||
create_repo=リポジトリ <a href="%s"> %s</a>を作成しました
|
||||
rename_repo=<code>%[1]s</code> から <a href="%[2]s">[3]s</a> にリポジトリ名を変更した
|
||||
create_repo=がリポジトリ <a href="%s"> %s</a> を作成しました
|
||||
rename_repo=<code>%[1]s</code> から <a href="%[2]s">[3]s</a> にリポジトリ名を変更しました
|
||||
commit_repo=<a href="%[1]s">%[4]s</a>を<a href="%[1]s/src/%[2]s">%[3]s</a>にプッシュしました
|
||||
create_issue=`問題 <a href="%s/issues/%s">%s#%[2]s</a> を開きました`
|
||||
close_issue=`closed issue <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
reopen_issue=`reopened issue <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
create_pull_request=`プルリクエスト <a href="%s/pulls/%s"> %s[2]s</a>を作成`
|
||||
close_pull_request=`closed pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
reopen_pull_request=`reopened pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
comment_issue=`問題 <a href="%s/issues/%s">%s#%[2]s</a> のコメント`
|
||||
merge_pull_request=`プルリクエスト <a href="%s/pulls/%s"> %s[2]s</a>をマージしました`
|
||||
transfer_repo=リポジトリ <code>%s</code> を <a href="%s">%s</a> へ転送しました
|
||||
@@ -1048,7 +1197,7 @@ now=今
|
||||
1mon=1 ヶ月 %s
|
||||
1y=1 年間 %s
|
||||
seconds=%d 秒 %s
|
||||
minutes=%d 分の %s
|
||||
minutes=%d分%s
|
||||
hours=%d 時間 %s
|
||||
days=%d 日 %s
|
||||
weeks=%d 週間 %s
|
||||
|
||||
1215
conf/locale/locale_ko-KR.ini
Normal file
1215
conf/locale/locale_ko-KR.ini
Normal file
File diff suppressed because it is too large
Load Diff
217
conf/locale/locale_lv-LV.ini
Executable file → Normal file
217
conf/locale/locale_lv-LV.ini
Executable file → Normal file
@@ -6,7 +6,7 @@ explore=Izpētīt
|
||||
help=Palīdzība
|
||||
sign_in=Pierakstīties
|
||||
sign_out=Izrakstīties
|
||||
sign_up=Pieteikties
|
||||
sign_up=Reģistrēties
|
||||
register=Reģistrēties
|
||||
website=Mājas lapa
|
||||
version=Versija
|
||||
@@ -38,19 +38,12 @@ settings=Iestatījumi
|
||||
your_profile=Tavs profils
|
||||
your_settings=Tavi iestatījumi
|
||||
|
||||
news_feed=Jaunumu plūsma
|
||||
activities=Aktivitāte
|
||||
pull_requests=Izmaiņu pieprasījumi
|
||||
issues=Problēmas
|
||||
|
||||
cancel=Atcelt
|
||||
|
||||
[search]
|
||||
search=Meklēt...
|
||||
repository=Repozitorijs
|
||||
user=Lietotājs
|
||||
issue=Kļūda
|
||||
code=Kods
|
||||
|
||||
[install]
|
||||
install=Instalācija
|
||||
title=Instalācijas soļi pirmo reizi palaižot
|
||||
@@ -65,7 +58,7 @@ db_name=Datu bāzes nosaukums
|
||||
db_helper=Nepieciešams izmantot MySQL INNODB dzini ar rakstzīmju kopu utf8_general_ci.
|
||||
ssl_mode=SSL režīms
|
||||
path=Ceļš
|
||||
sqlite_helper=SQLite3 vai TiDB datu bāzes faila atrašanās vieta.
|
||||
sqlite_helper=SQLite3 vai TiDB datu bāzēs faila ceļš.<br>Izmantojiet absolūto ceļu, startējot kā servisu.
|
||||
err_empty_db_path=Nepieciešams norādīt SQLite3 vai TiDB datu bāzes atrašanās vietu.
|
||||
err_invalid_tidb_name=TiDB datu bāzes nosaukums nevar saturēt simbolus "." un "-".
|
||||
no_admin_and_disable_registration=Reģistrāciju nevar atslēgt, kamēr nav izveidots administratora konts.
|
||||
@@ -86,6 +79,8 @@ http_port=HTTP ports
|
||||
http_port_helper=Porta numurs pēc kura lietojumprogrammai būs iespējams pieslēgties.
|
||||
app_url=Lietotnes URL
|
||||
app_url_helper=Tas ietekmē HTTP/HTTPS klonēšanas URL un e-pasta saturā izsūtītās saites.
|
||||
log_root_path=Žurnalizēšanas direktorija
|
||||
log_root_path_helper=Direktorija, kurā tiks glabāti žurnāla faili.
|
||||
|
||||
optional_title=Neobligātie iestatījumi
|
||||
email_title=E-pasta pakalpojuma iestatījumi
|
||||
@@ -101,6 +96,8 @@ offline_mode=Iespējot bezsaistes režīmu
|
||||
offline_mode_popup=Atspējot CDN arī produkcijas režīmā, visi resursu faili tiks piegādāti no servera.
|
||||
disable_gravatar=Atspējot Gravatar pakalpojumu
|
||||
disable_gravatar_popup=Atspējot Gravatar un citus avotus, visus avatarus augšupielādēts lietotāji vai izmantos noklusēto attēlu.
|
||||
federated_avatar_lookup=Iespējot apvienoto profila bilžu meklētāju
|
||||
federated_avatar_lookup_popup=Iespējot apvienoto profila bilžu meklētāju, lai izmantotu atvērtā koda apvienoto servisu balstītu uz libravatar.
|
||||
disable_registration=Atspējot lietotāju reģistrāciju
|
||||
disable_registration_popup=Atspējot lietotāju reģistrāciju, tikai administrators varēs izveidot jaunus lietotāju kontus.
|
||||
enable_captcha=Iespējot drošības kodu
|
||||
@@ -119,15 +116,18 @@ sqlite3_not_available=Jūsu versija neatbalsta SQLite3, lūdzu lejupielādējiet
|
||||
invalid_db_setting=Datu bāzes iestatījums nav pareizs: %v
|
||||
invalid_repo_path=Repozitorija atrašanās vieta ir nekorekta: %v
|
||||
run_user_not_match=Izpildes lietotājs nav pašreizējais lietotājs: %s -> %s
|
||||
invalid_smtp_from=SMTP From field is not valid: %v
|
||||
save_config_failed=Neizdevās saglabāt konfigurāciju: %v
|
||||
invalid_admin_setting=Nekorekts admin konta iestatījums: %v
|
||||
install_success=Laipni lūdzam! Mēs priecājamies, ka Jūs izvēlaties Gogs, patīkamu lietošanu!
|
||||
invalid_log_root_path=Norādītā žurnalizēšanas direktorija ir kļūdaina: %v
|
||||
|
||||
[home]
|
||||
uname_holder=Lietotājvārds vai e-pasts
|
||||
password_holder=Parole
|
||||
switch_dashboard_context=Mainīt infopaneļa kontekstu
|
||||
my_repos=Mani repozitoriji
|
||||
show_more_repos=Parādīt vairāk repozitorojus...
|
||||
collaborative_repos=Sadarbības repozitoriji
|
||||
my_orgs=Manas organizācijas
|
||||
my_mirrors=Mani spoguļi
|
||||
@@ -137,6 +137,9 @@ issues.in_your_repos=Jūsu repozitorijos
|
||||
|
||||
[explore]
|
||||
repos=Repozitoriji
|
||||
users=Lietotāji
|
||||
organizations=Organizations
|
||||
search=Meklēt
|
||||
|
||||
[auth]
|
||||
create_new_account=Izveidot jaunu kontu
|
||||
@@ -150,15 +153,17 @@ forget_password=Aizmirsi paroli?
|
||||
sign_up_now=Nepieciešams konts? Reģistrējies tagad.
|
||||
confirmation_mail_sent_prompt=Jauns apstiprināšanas e-pasts ir nosūtīts uz <b>%s</b>, lūdzu, pārbaudies savu e-pasta kontu tuvāko %d stundu laikā, lai pabeigtu reģistrācijas procesu.
|
||||
active_your_account=Aktivizēt savu kontu
|
||||
prohibit_login=Aizliegt pieteikšanos
|
||||
prohibit_login_desc=Ar Jūsu kontu nav atļauts pieteikties, sazinoties ar lapas administratoru.
|
||||
resent_limit_prompt=Atvainojiet, Jūs sūtījāt aktivizācijas e-pastu pārāk bieži. Lūdzu, gaidiet 3 minūtes.
|
||||
has_unconfirmed_mail=Sveiki %s, Jums ir neapstiprināta e-pasta adrese (<b>%s</b>). Ja neesat saņēmis apstiprināšanas e-pastu vai Jums ir nepieciešams nosūtīt jaunu, lūdzu, nospiediet pogu, kas atrodas zemāk.
|
||||
resend_mail=Nospiediet šeit, lai vēlreiz nosūtītu aktivizācijas e-pastu
|
||||
email_not_associate=Šī e-pasta adrese nav saistīta ar Jūsu kontu.
|
||||
send_reset_mail=Spiediet šeit, lai nosūtītu paroles maiņas vēstuli uz Jūsu e-pastu
|
||||
reset_password=Atjaunot savu paroli
|
||||
invalid_code=Atvainojiet, Jūsu apstiprināšanas kodam ir beidzies derīguma termiņš vai arī tas ir nepareizs.
|
||||
reset_password_helper=Nospiediet šeit, lai atjaunotu paroli
|
||||
password_too_short=Paroles garums nedrīkst būt mazāks par 6.
|
||||
non_local_account=Tikai lokālie konti var nomainīt savu paroli Gogs.
|
||||
|
||||
[mail]
|
||||
activate_account=Lūdzu, aktivizējiet savu kontu
|
||||
@@ -185,6 +190,13 @@ TeamName=Komandas nosaukums
|
||||
AuthName=Autorizācijas nosaukums
|
||||
AdminEmail=Admin e-pasta adrese
|
||||
|
||||
NewBranchName=Jauna atzara nosaukums
|
||||
CommitSummary=Revīzijas kopsavilkums
|
||||
CommitMessage=Revīzijas ziņojums
|
||||
CommitChoice=Revīzijas izvēle
|
||||
TreeName=Faila ceļš
|
||||
Content=Saturs
|
||||
|
||||
require_error=` nedrīkst būt tukšs.`
|
||||
alpha_dash_error=` drīkst saturēt tikai latīņu alfabēta burtus, ciparus vai domuzīmes (-_).`
|
||||
alpha_dash_dot_error=` drīkst saturēt tikai latīņu alfabēta burtus, ciparus, domuzīmes (-_) vai punktu.`
|
||||
@@ -203,7 +215,6 @@ repo_name_been_taken=Repozitorija vārds ir jau aizņemts.
|
||||
org_name_been_taken=Organizācijas nosaukums ir jau aizņemts.
|
||||
team_name_been_taken=Komandas nosaukums ir jau aizņemts.
|
||||
email_been_used=E-pasta adrese jau tiek izmantota.
|
||||
illegal_team_name=Grupas nosaukums satur neatļautas rakstzīmes.
|
||||
username_password_incorrect=Lietotājvārds vai parole nav pareiza.
|
||||
enterred_invalid_repo_name=Lūdzu, pārliecinieties, vai ievadītā repozitorija nosaukums ir pareizs.
|
||||
enterred_invalid_owner_name=Lūdzu, pārliecinieties, vai ievadītā īpašnieka vārds ir pareizs.
|
||||
@@ -219,13 +230,10 @@ still_own_repo=Jūsu esat vismaz viena repozitorija īpašnieks, tos sākumā ir
|
||||
still_has_org=Jūsu esat vismaz vienas organizācijas biedrs, sākumā nepieciešams pamest vai izdzēst šo organizāciju.
|
||||
org_still_own_repo=Šī organizācija ir vismaz viena repozitorija īpašnieks, tos sākumā ir nepieciešams izdzēst vai nomainīt to īpašnieku.
|
||||
|
||||
still_own_user=Šo autentifikāciju joprojām izmanto vismaz viens lietotājs, nepieciešams šiem lietotājiem nomainīt autentifikācijas veidu vai tos izdzēst.
|
||||
|
||||
target_branch_not_exist=Mērķa atzars neeksistē
|
||||
|
||||
[user]
|
||||
change_avatar=Mainīt savu profila attēlu vietnē gravatar.com
|
||||
change_custom_avatar=Mainīt savu profila attēlu iestatījumos
|
||||
change_avatar=Mainīt profila attēlu
|
||||
join_on=Pievienojās
|
||||
repositories=Repozitoriji
|
||||
activity=Publiskā aktivitāte
|
||||
@@ -241,6 +249,7 @@ form.name_pattern_not_allowed=Lietotāja vārds '%s' nav atļauts.
|
||||
[settings]
|
||||
profile=Profils
|
||||
password=Parole
|
||||
avatar=Profila attēls
|
||||
ssh_keys=SSH atslēgas
|
||||
social=Sociālie konti
|
||||
applications=Lietotnes
|
||||
@@ -261,12 +270,13 @@ change_username_prompt=Šī izmaiņa ietekmēs saites, kas norāda uz Jūsu kont
|
||||
continue=Turpināt
|
||||
cancel=Atcelt
|
||||
|
||||
lookup_avatar_by_mail=Meklēt profila bildes pēc e-pasta
|
||||
federated_avatar_lookup=Apvienotais profila bilžu meklētājs
|
||||
enable_custom_avatar=Iespējot maināmu profila attēlu
|
||||
enable_custom_avatar_helper=Iespējojiet šo, lai atslēgtu profilu attēlu ņemšanu no gravatar.com
|
||||
choose_new_avatar=Izvēlēties jaunu profila attēlu
|
||||
update_avatar=Saglabāt profila bildi
|
||||
delete_current_avatar=Dzēst pašreizējo profila bildi
|
||||
uploaded_avatar_not_a_image=Augšupielādētais fails nav attēls.
|
||||
no_custom_avatar_available=Nav iespējams mainīt profila bildi.
|
||||
update_avatar_success=Jūsu profila bilde tika veiksmīgi saglabāta.
|
||||
|
||||
change_password=Mainīt paroli
|
||||
@@ -328,6 +338,10 @@ access_token_deletion=Personīgā piekļuves talona dzēšana
|
||||
access_token_deletion_desc=Dzēšot personīgo piekļuves talonu, tiks liegta piekļuve aplikācijām, kas to izmanto. Vai vēlaties turpināt?
|
||||
delete_token_success=Personīgās piekļuves talons veiksmīgi izdzēsts! Neaizmirstiet nomainīt uz citu aplikācijās, kas to izmantoja.
|
||||
|
||||
orgs.none=You are not a member of any organizations.
|
||||
orgs.leave_title=Leave an organization
|
||||
orgs.leave_desc=You will lose access to all repositories and teams after you left the organization. Do you want to continue?
|
||||
|
||||
delete_account=Dzēst savu kontu
|
||||
delete_prompt=Šī darbība pilnībā izdzēsīs Jūsu kontu, kā arī tā ir <strong>NEATGRIEZENISKA</strong>!
|
||||
confirm_delete_account=Apstiprināt dzēšanu
|
||||
@@ -348,7 +362,7 @@ fork_from=Atdalīt no
|
||||
fork_visiblity_helper=Atdalītam repozitorijam nav iespējams nomainīt tā redzamību
|
||||
repo_desc=Apraksts
|
||||
repo_lang=Valoda
|
||||
repo_lang_helper=Izvēlieties .gitignore failus
|
||||
repo_gitignore_helper=Izvēlieties .gitignore sagatavi
|
||||
license=Licence
|
||||
license_helper=Izvēlieties licences failu
|
||||
readme=LasiMani
|
||||
@@ -356,9 +370,12 @@ readme_helper=Izvēlieties faila LasiMani sagatavi
|
||||
auto_init=Inicializēt šo repozitoriju ar izvēlētajiem failiem un sagatavi
|
||||
create_repo=Izveidot repozitoriju
|
||||
default_branch=Noklusējuma atzars
|
||||
mirror_prune=Izmest
|
||||
mirror_prune_desc=Izdzēst visas ārējās atsauces, kas ārējā repozitorijā vairs neeksistē
|
||||
mirror_interval=Spoguļošanas intervāls (stundās)
|
||||
mirror_address=Spoguļa adrese
|
||||
mirror_address_desc=Lūdzu iekļaujiet adresē nepieciešamo lietotājvārdu/paroli.
|
||||
mirror_last_synced=Pēdējo reizi sinhronizēts
|
||||
watchers=Novērotāji
|
||||
stargazers=Zvaigžņdevēji
|
||||
forks=Atdalītie repozitoriji
|
||||
@@ -372,7 +389,8 @@ migrate_type=Migrācijas veids
|
||||
migrate_type_helper=Šis repozitorijs būs <span class="text blue">spogulis</span>
|
||||
migrate_repo=Migrēt repozitoriju
|
||||
migrate.clone_address=Klonēšanas adrese
|
||||
migrate.clone_address_desc=Tas var būt HTTP/HTTPS/GIT URL vai ceļš uz lokālā servera.
|
||||
migrate.clone_address_desc=This can be a HTTP/HTTPS/GIT URL.
|
||||
migrate.clone_address_desc_import_local=You're also allowed to migrate a repository by local server path.
|
||||
migrate.permission_denied=Jums nav tiesību importēt lokālu repozitoriju.
|
||||
migrate.invalid_local_path=Nekorents lokālais ceļš, tas neeksistē vai nav direktorijs.
|
||||
migrate.failed=Migrācija neizdevās: %v
|
||||
@@ -397,7 +415,7 @@ create_new_repo_command=Izveidot jaunu repozitoriju komandrindā
|
||||
push_exist_repo=Nosūtīt izmaiņas no komandrindas eksistējošam repozitorijam
|
||||
repo_is_empty=Šis repozitorijs ir tukšs, apskatiet atkal vēlāk!
|
||||
|
||||
code=Kods
|
||||
files=Files
|
||||
branch=Atzars
|
||||
tree=Koks
|
||||
filter_branch_and_tag=Filtrēt atzarus vai tagus
|
||||
@@ -413,6 +431,47 @@ file_raw=Neapstrādāts
|
||||
file_history=Vēsture
|
||||
file_view_raw=Rādīt neapstrādātu
|
||||
file_permalink=Patstāvīgā saite
|
||||
file_too_large=Šis fails ir par lielu, lai to parādītu
|
||||
video_not_supported_in_browser=Your browser doesn't support HTML5 video tag.
|
||||
|
||||
editor.new_file=Jauns fails
|
||||
editor.upload_file=Augšupielādēt failu
|
||||
editor.edit_file=Labot failu
|
||||
editor.preview_changes=Priekšskatīt izmaiņas
|
||||
editor.cannot_edit_non_text_files=Nevar rediģēt failus, kas nav teksta faili
|
||||
editor.edit_this_file=Rediģēt šo failu
|
||||
editor.must_be_on_a_branch=Ir jābūt izvēlētam atzaram, lai varētu veikt vai piedāvāt izmaiņas šim failam
|
||||
editor.fork_before_edit=Lai varētu labot failu ir nepieciešams atdalīt repozitoriju
|
||||
editor.delete_this_file=Dzēst šo failu
|
||||
editor.must_have_write_access=Jums ir jābūt rakstīšanas tiesībām, lai varētu veikt vai piedāvāt izmaiņas šim failam
|
||||
editor.file_delete_success=Fails '%s' ir veiksmīgi izdzēsts!
|
||||
editor.name_your_file=Ievadiet faila nosaukumu...
|
||||
editor.filename_help=Lai pievienotu direktoriju, ierakstiet tās nosaukumu un nospiediet /. Lai noņemtu direktoriju, ielieciet kursoru pirms faila nosaukuma un nospiediet atpakaļatkāpes taustiņu.
|
||||
editor.or=vai
|
||||
editor.cancel_lower=atcelt
|
||||
editor.commit_changes=Pabeigt revīziju
|
||||
editor.add_tmpl=Pievienot '%s/<filename>'
|
||||
editor.add=Pievienot '%s'
|
||||
editor.update=Atjaunināt '%s'
|
||||
editor.delete=Dzēst '%s'
|
||||
editor.commit_message_desc=Pievienot neobligātu paplašinātu aprakstu...
|
||||
editor.commit_directly_to_this_branch=Apstiprināt revīzijas izmaiņas atzarā <strong class="branch-name">%s</strong>.
|
||||
editor.create_new_branch=Izveidot <strong>jaunu atzaru</strong> un izmaiņu pieprasījumu šai revīzijai.
|
||||
editor.new_branch_name_desc=Jaunā atzara nosaukums...
|
||||
editor.cancel=Atcelt
|
||||
editor.filename_cannot_be_empty=Nav ievadīts faila nosaukums.
|
||||
editor.branch_already_exists=Atzars '%s' šajā repozitorijā jau eksistē.
|
||||
editor.directory_is_a_file=Ieraksts '%s' vecāka ceļā ir fails nevis direktorija šajā repozitorijā.
|
||||
editor.file_is_a_symlink=The file '%s' is a symlink that cannot be modified from the web editor.
|
||||
editor.filename_is_a_directory=Faila nosaukums '%s' sakrīt ar direktorijas nosaukumu šajā repozitorijā.
|
||||
editor.file_editing_no_longer_exists=Fails '%s', ko labojat, vairs neeksistē repozitorijā.
|
||||
editor.file_changed_while_editing=Faila saturs ir mainījies kopš brīža, kad sākāt to labot. <a target="_blank" href="%s">Nospiediet šeit</a>, lai redzētu kas ir mainījies vai <strong>nospiediet atkārtoti pabeigt revīziju</strong>, lai pārrakstītu izmaiņas.
|
||||
editor.file_already_exists=Fails ar nosaukumu '%s' repozitorijā jau eksistē.
|
||||
editor.no_changes_to_show=Nav izmaiņu, ko rādīt.
|
||||
editor.fail_to_update_file=Neizdevās izmainīt/izveidot failu '%s', kļūda: %v
|
||||
editor.add_subdir=Pievienot apakšdirektoriju...
|
||||
editor.unable_to_upload_files=Neizdevās augšupielādēt failus uz direktoriju '%s', kļūda: %v
|
||||
editor.upload_files_to_dir=Augšupielādēt failus uz direktoriju '%s'
|
||||
|
||||
commits.commits=Revīzijas
|
||||
commits.search=Meklēt revīzijas
|
||||
@@ -439,6 +498,11 @@ issues.create=Pieteikt problēmu
|
||||
issues.new_label=Jauna etiķete
|
||||
issues.new_label_placeholder=Etiķetes nosaukums...
|
||||
issues.create_label=Izveidot etiķeti
|
||||
issues.label_templates.title=Ielādēt sākotnēji noteikto etiķešu kopu
|
||||
issues.label_templates.info=Nav definēta neviena etiķete. Nospiediet pogu "Izveidot etiķeti", lai to izveidotu vai izmantojiet zemāk piedāvātās etiķetes.
|
||||
issues.label_templates.helper=Izvēlieties etiķešu kopu
|
||||
issues.label_templates.use=Izmantot šo etiķešu kopu
|
||||
issues.label_templates.fail_to_load_file=Neizdevās ielādēt etiķetes sagataves failu '%s': %v
|
||||
issues.open_tab=%d atvērti
|
||||
issues.close_tab=%d aizvērti
|
||||
issues.filter_label=Etiķete
|
||||
@@ -466,7 +530,8 @@ issues.next=Nākamā
|
||||
issues.open_title=Atvērta
|
||||
issues.closed_title=Slēgta
|
||||
issues.num_comments=%d komentāri
|
||||
issues.commented_at=`komentēja <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commented_at=` komentēja <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=Vai patiešām vēlaties dzēst šo komentāru?
|
||||
issues.no_content=Vēl nav satura.
|
||||
issues.close_issue=Aizvērt
|
||||
issues.close_comment_issue=Komentēt un aizvērt
|
||||
@@ -477,10 +542,9 @@ issues.closed_at=`aizvērts <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at=`atvērts atkārtoti <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commit_ref_at=`pieminēja šo problēmu revīzijā <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.poster=Autors
|
||||
issues.admin=Administrators
|
||||
issues.collaborator=Līdzstrādnieks
|
||||
issues.owner=Īpašnieks
|
||||
issues.sign_up_for_free=Pievienojieties
|
||||
issues.sign_in_require_desc=, lai piedalītos diskusijā. Jau ir konts? <a href="%s">Pierakstieties, lai komentētu</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Pierakstieties</a>, lai pievienotos šai sarunai.
|
||||
issues.edit=Labot
|
||||
issues.cancel=Atcelt
|
||||
issues.save=Saglabāt
|
||||
@@ -494,6 +558,9 @@ issues.label_modify=Etiķetes labošana
|
||||
issues.label_deletion=Etiķetes dzēšana
|
||||
issues.label_deletion_desc=Dzēšot šo etiķeti, tā tiks noņemta no visām saistītajām problēmām. Vai vēlaties turpināt?
|
||||
issues.label_deletion_success=Etiķete tika veiksmīgi izdzēsta!
|
||||
issues.num_participants=%d dalībnieki
|
||||
issues.attachment.open_tab=`Noklikšķiniet, lai apskatītos "%s" jaunā logā`
|
||||
issues.attachment.download=`Noklikšķiniet, lai lejupielādētu "%s"`
|
||||
|
||||
pulls.new=Jauns izmaiņu pieprasījums
|
||||
pulls.compare_changes=Salīdzināt izmaiņas
|
||||
@@ -520,6 +587,8 @@ pulls.cannot_auto_merge_desc=Šis izmaiņu pieprasījums nevar tikt automātiski
|
||||
pulls.cannot_auto_merge_helper=Lūdzu sapludiniet manuāli, lai atrisinātu konfliktus.
|
||||
pulls.merge_pull_request=Izmaiņu pieprasījuma sapludināšana
|
||||
pulls.open_unmerged_pull_exists=`Jūs nevarat veikt atkārtotas atvēršanas darbību, jo jau eksistē izmaiņu pieprasījums (#%d) no šī repozitorija ar tādu pašu sapludināšanas informāciju un gaida sapludināšanu.`
|
||||
pulls.delete_branch=Delete Branch
|
||||
pulls.delete_branch_has_new_commits=Branch cannot be deleted because it has new commits after mergence.
|
||||
|
||||
milestones.new=Jauns atskaites punkts
|
||||
milestones.open_tab=%d atvērti
|
||||
@@ -557,6 +626,8 @@ wiki.save_page=Saglabāt lapu
|
||||
wiki.last_commit_info=%s laboja lapu %s
|
||||
wiki.edit_page_button=Labot
|
||||
wiki.new_page_button=Jauna lapa
|
||||
wiki.delete_page_button=Dzēst lapu
|
||||
wiki.delete_page_notice_1=Tiks izdzēsta lapa <code>"%s"</code>. Pārliecinieties, ka patiešām to vēlaties.
|
||||
wiki.page_already_exists=Vikivietnes lapa ar šādu nosaukumu jau eksistē.
|
||||
wiki.pages=Lapas
|
||||
wiki.last_updated=Pēdējo reizi labota %s
|
||||
@@ -564,36 +635,57 @@ wiki.last_updated=Pēdējo reizi labota %s
|
||||
settings=Iestatījumi
|
||||
settings.options=Opcijas
|
||||
settings.collaboration=Sadarbība
|
||||
settings.collaboration.admin=Administrators
|
||||
settings.collaboration.write=Rakstīšanas
|
||||
settings.collaboration.read=Skatīšanās
|
||||
settings.collaboration.undefined=Nedefinētas
|
||||
settings.hooks=Tīmekļa āķi
|
||||
settings.githooks=Git āķi
|
||||
settings.basic_settings=Pamatiestatījumi
|
||||
settings.mirror_settings=Spoguļa iestatījumi
|
||||
settings.sync_mirror=Sinhronizēt tagad
|
||||
settings.mirror_sync_in_progress=Notiek spoguļa sinhronizācija, uzgaidiet aptuveni minūti un atjaunojiet lapu.
|
||||
settings.site=Oficiālā mājas lapa
|
||||
settings.update_settings=Mainīt iestatījumus
|
||||
settings.change_reponame_prompt=Šī izmaiņa ietekmēs saites, kas ir saistītas ar šo repozitoriju.
|
||||
settings.advanced_settings=Papildu iestatījumi
|
||||
settings.wiki_desc=Iespējot vikivietni, lai atļautu cilvēkiem rakstīt dokumentus
|
||||
settings.wiki_desc=Iespējot vikivietnes
|
||||
settings.use_internal_wiki=Izmantot iebūvēto vikivietni
|
||||
settings.use_external_wiki=Izmantot ārējo vikivietni
|
||||
settings.external_wiki_url=Ārējās Vikivietnes adrese
|
||||
settings.external_wiki_url_desc=Apmeklētāji tiks novirzīti uz adresi, kad viņi uzklikšķinās uz cilnes.
|
||||
settings.issues_desc=Iespējot iebūvētu vieglu problēmu sekotāju
|
||||
settings.issues_desc=Iespējot problēmu sekotāju
|
||||
settings.use_internal_issue_tracker=Izmantot iebūvētu vieglu problēmu sekotāju
|
||||
settings.use_external_issue_tracker=Izmantot ārējo problēmu sekotāju
|
||||
settings.external_tracker_url=External Issue Tracker URL
|
||||
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
|
||||
settings.tracker_url_format=Ārējā problēmu sekotāja adreses formāts
|
||||
settings.tracker_issue_style=Ārējā problēmu reģistra nosaukumu stils:
|
||||
settings.tracker_issue_style.numeric=Cipari
|
||||
settings.tracker_issue_style.alphanumeric=Burti un cipari
|
||||
settings.tracker_url_format_desc=Jūs varat izmantot <code>{user}{repo}{index}</code> lietotājvārdam, repozitorija nosaukumam un problēmas identifikātoram.
|
||||
settings.pulls_desc=Iespējot izmaiņu pieprasījumus lai saņemtu publiskus ieguldījumus
|
||||
settings.danger_zone=Bīstamā zona
|
||||
settings.new_owner_has_same_repo=Jaunajam īpašniekam jau ir repozitorijs ar šādu nosaukumu.
|
||||
settings.convert=Konvertēt uz parastu repozitoriju
|
||||
settings.convert_desc=Šo spoguli ir iespējams konvertēt par parastu repozitoriju. Šī ir neatgriezeniska darbība.
|
||||
settings.convert_notices_1=- Šī darbība konvertēs šo repozitoriju par parastu repozitoriju un to nebūs iespējams atcelt.
|
||||
settings.convert_confirm=Apstiprināt konvertēšanu
|
||||
settings.convert_succeed=Repozitorijs tika veiksmīgi konvertēts uz parastu repozitoriju.
|
||||
settings.transfer=Mainīt īpašnieku
|
||||
settings.transfer_desc=Mainīt šī repozitorija īpašnieku uz citu lietotāju vai organizāciju, kurai Jums ir administratora tiesībs.
|
||||
settings.new_owner_has_same_repo=Jaunajam īpašniekam jau ir repozitorijs ar šādu nosaukumu.
|
||||
settings.delete=Dzēst šo repozitoriju
|
||||
settings.delete_desc=Dzēšot repozitoriju, tā datus vairs nebūs iespējams atgūt. Pirms dzēšanas pārliecinieites vai patiešām vēlaties to darīt.
|
||||
settings.transfer_notices_1=- Jūs pazaudēsiet piekļuvi, ja jaunais īpašnieks ir lietotājs.
|
||||
settings.transfer_notices_2=- Jūs saglabāsiet piekļuvi, ja jaunais īpašnieks ir organizācija un Jūs esat viens no tās īpašniekiem.
|
||||
settings.transfer_form_title=Lūdzu, ievadiet sekojošu informāciju, lai apstiprinātu šo darbību:
|
||||
settings.wiki_delete=Dzēst Vikivietnes datus
|
||||
settings.wiki_delete_desc=Vikivietnes datu dzēšana ir neatgriezeniska. Pārliecinieties vai patiešām to vēlaties.
|
||||
settings.wiki_delete_notices_1=- Šī darbība dzēsīs un atspējos %s Vikivietni
|
||||
settings.wiki_deletion_success=Repozitorija Vikivietnes dati tika veiksmīgi izdzēsti.
|
||||
settings.delete=Dzēst šo repozitoriju
|
||||
settings.delete_desc=Dzēšot repozitoriju, tā datus vairs nebūs iespējams atgūt. Pirms dzēšanas pārliecinieites vai patiešām vēlaties to darīt.
|
||||
settings.delete_notices_1=- Šī darbība ir <strong>NEATGRIEZENISKA</strong>.
|
||||
settings.delete_notices_2=- Šī darbība neatgriezeniski izdzēsīs visus šī repozitorija datus, tai skaitā Git datus, problēmu ziņojumus, komentārus un definētās piekļuves tiesības.
|
||||
settings.delete_notices_fork_1=- Ja repozitorijs ir publisks, visi atdalītie repozitoriji kļūs neatkarīgi.
|
||||
settings.delete_notices_fork_2=- Ja repozitorijs ir privāts, tiks dzēsti arī visi atdalītie repozitoriji.
|
||||
settings.delete_notices_fork_3=- Ja vēlaties saglabāt atdalīts repozitorijus pēc dzēšanas, sākumā nomainiet repozitorija redzamību uz publisku.
|
||||
settings.delete_notices_fork_1=- Visi atdalītie repozitoriji kļūs neatkarīgi pēc dzēšanas.
|
||||
settings.deletion_success=Repozitorijs tika veiksmīgi dzēsts!
|
||||
settings.update_settings_success=Repozitorija opcijas ir veiksmīgi saglabātas.
|
||||
settings.transfer_owner=Jaunais īpašnieks
|
||||
@@ -602,8 +694,12 @@ settings.transfer_succeed=Repozitorija īpašnieks ir veiksmīgi nomainīts.
|
||||
settings.confirm_delete=Apstiprināt dzēšanu
|
||||
settings.add_collaborator=Pievienot jaunu līdzstrādnieku
|
||||
settings.add_collaborator_success=Jauns līdzstrādnieks ir pievienots.
|
||||
settings.delete_collaborator=Dzēst
|
||||
settings.collaborator_deletion=Līdzstrādnieka dzēšana
|
||||
settings.collaborator_deletion_desc=Šim lietotājam pēc dzēšanas vairs nebūs sadarbības pieejas šai krātuvei. Vai vēlaties turpināt?
|
||||
settings.remove_collaborator_success=Līdzstrādnieks tika noņemts.
|
||||
settings.search_user_placeholder=Meklēt lietotāju...
|
||||
settings.org_not_allowed_to_be_collaborator=Organizāciju nav atļauts pievienot kā līdzstrādnieku.
|
||||
settings.user_is_org_member=Lietotājs ir organizācijas biedrs, kas nevar tikt pievienots kā līdzstrādnieks.
|
||||
settings.add_webhook=Pievienot tīmekļa āķi
|
||||
settings.hooks_desc=Tīmekļa āķi ļauj paziņot ārējiem servisiem par noteiktiem notikomiem, kas notiek Git servisā. Kad iestāsies kāds notikums, katram ārējā servisa URL tiks nosūtīts POST pieprasījums. Lai uzzinātu sīkāk skatieties <a target="_blank" href="%s">Tīmekļa āķu rokasgrāmatā</a>.
|
||||
@@ -636,6 +732,8 @@ settings.event_send_everything=Vēlos saņemt <strong>visu</strong>.
|
||||
settings.event_choose=Atzīmēt, ko vēlos saņemt.
|
||||
settings.event_create=Izveidot
|
||||
settings.event_create_desc=Atzara vai taga izveidošana
|
||||
settings.event_pull_request=Izmaiņu pieprasījums
|
||||
settings.event_pull_request_desc=Atvērts, aizvērts, atkāroti atvērts, labots, piešķirts vai noņemts izmaiņu pieprasījums, vai mainīta etiķete, vai veikta sinhronizācija.
|
||||
settings.event_push=Izmaiņu nosūtīšana
|
||||
settings.event_push_desc=Git izmaiņu nosūtīšana uz repozitoriju
|
||||
settings.active=Aktīvs
|
||||
@@ -673,6 +771,8 @@ diff.show_unified_view=Apvienotais skats
|
||||
diff.stats_desc=<strong>%d mainītis faili</strong> ar <strong>%d papildinājumiem</strong> un <strong>%d dzēšanām</strong>
|
||||
diff.bin=BIN
|
||||
diff.view_file=Parādīt failu
|
||||
diff.file_suppressed=Failā izmaiņas netiks attēlotas, jo tās ir par lielu
|
||||
diff.too_many_files=Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels
|
||||
|
||||
release.releases=Laidieni
|
||||
release.new_release=Jauns laidiens
|
||||
@@ -703,6 +803,7 @@ release.deletion=Laidiena dzēšana
|
||||
release.deletion_desc=Dzēšot šo laidienu tiks dzēsts arī atbilstošs Git tags. Vai vēlaties turpināt?
|
||||
release.deletion_success=Laidiens tika veiksmīgi dzēsts!
|
||||
release.tag_name_already_exist=Laidiens ar šādu taga nosaukumu jau eksistē.
|
||||
release.tag_name_invalid=Nekorekts birkas nosaukams.
|
||||
release.downloads=Lejupielādes
|
||||
|
||||
[org]
|
||||
@@ -818,6 +919,8 @@ dashboard.resync_all_sshkeys=Pārrakstīt '.ssh/authorized_keys' failu (brīdin
|
||||
dashboard.resync_all_sshkeys_success=Visas publiskās atslēgas tika veiksmīgi pārrakstītas.
|
||||
dashboard.resync_all_update_hooks=Pārrakstīt visu repozitoriju izmaiņu āķus (nepieciešams, ja tiek mainīta konfigurācijas faila atrašanās vieta)
|
||||
dashboard.resync_all_update_hooks_success=Visu repozitoriju izmaiņu āķi tika veiksmīgi pārrakstīti.
|
||||
dashboard.reinit_missing_repos=Atkārtoti inicializēt visus repozitorija ierakstus, kam trūkst Git failu
|
||||
dashboard.reinit_missing_repos_success=Visi repozitorija ieraksti, kam trūkst Git faili, tika atkārtoti inicializēti.
|
||||
|
||||
dashboard.server_uptime=Servera darbības laiks
|
||||
dashboard.current_goroutine=Izmantotās Gorutīnas
|
||||
@@ -868,6 +971,7 @@ users.edit_account=Labot kontu
|
||||
users.max_repo_creation=Maksimāls repozitoriju veidošanas limits
|
||||
users.max_repo_creation_desc=(Uzlikt -1 lai izmantotu globālu limitu pēc noklusējuma)
|
||||
users.is_activated=Konts ir aktivizēts
|
||||
users.prohibit_login=Šim kontam ir aizliegts autorizēties
|
||||
users.is_admin=Šim kontam ir administratora piekļuves tiesības
|
||||
users.allow_git_hook=Šim kontam ir tiesības pievienot/labot Git āķus
|
||||
users.allow_import_local=Šim kontam ir tiesības importēt lokālus repozitorijus
|
||||
@@ -898,6 +1002,7 @@ auths.enabled=Iespējota
|
||||
auths.updated=Atjaunināta
|
||||
auths.auth_type=Autentifikācijas tips
|
||||
auths.auth_name=Autentifikācijas nosaukums
|
||||
auths.security_protocol=Drošības protokols
|
||||
auths.domain=Domēns
|
||||
auths.host=Resursdators
|
||||
auths.port=Ports
|
||||
@@ -911,6 +1016,7 @@ auths.attribute_username_placeholder=Atstājiet tukšu, lai izmantotu lietotājv
|
||||
auths.attribute_name=Vārda atribūts
|
||||
auths.attribute_surname=Uzvārda atribūts
|
||||
auths.attribute_mail=E-pasta atribūts
|
||||
auths.attributes_in_bind=Nolasīt atribūtus no saistīšanas DN konteksta
|
||||
auths.filter=Lietotāju filts
|
||||
auths.admin_filter=Administratoru filtrs
|
||||
auths.ms_ad_sa=MS Ad SA
|
||||
@@ -932,7 +1038,9 @@ auths.update=Mainīt autentifikācijas iestatījumus
|
||||
auths.delete=Dzēst šo autentifikāciju
|
||||
auths.delete_auth_title=Autentifikācijas dzēšana
|
||||
auths.delete_auth_desc=Šī autentifikācija tiks dzēsta, vai vēlaties turpināt?
|
||||
auths.still_in_used=Daži lietotāji joprojām izmanto šo autentifikācijas veidu. Nepieciešams veikt šo lietotāju konvertāciju vai dzēšanu.
|
||||
auths.deletion_success=Autentifikācija tika veiksmīgi izdzēsta!
|
||||
auths.login_source_exist=Login source '%s' already exists.
|
||||
|
||||
config.server_config=Servera konfigurācija
|
||||
config.app_name=Lietotnes nosaukums
|
||||
@@ -943,11 +1051,25 @@ config.offline_mode=Bezsaistes režīms
|
||||
config.disable_router_log=Atspējot maršrutētāja žurnalizēšanu
|
||||
config.run_user=Izpildes lietotājs
|
||||
config.run_mode=Izpildes režīms
|
||||
config.git_version=Git Version
|
||||
config.repo_root_path=Repozitoriju glabāšanas vieta
|
||||
config.static_file_root_path=Statisko failu atrašanās vieta
|
||||
config.log_file_root_path=Žurnalizēšanas failu glabāšanas vieta
|
||||
config.script_type=Skripta veids
|
||||
config.reverse_auth_user=Reversā lietotāja autentifikācija
|
||||
|
||||
config.ssh_config=SSH konfigurācija
|
||||
config.ssh_enabled=Iespējots
|
||||
config.ssh_start_builtin_server=Startēt iebūvēto serveri
|
||||
config.ssh_domain=Domēns
|
||||
config.ssh_port=Ports
|
||||
config.ssh_listen_port=Klausīšanās ports
|
||||
config.ssh_root_path=Saknes ceļš
|
||||
config.ssh_key_test_path=Atslēgu pārbaudes ceļš
|
||||
config.ssh_keygen_path=Keygen ('ssh-keygen') ceļš
|
||||
config.ssh_minimum_key_size_check=Minimālā atslēgas lieluma pārbaude
|
||||
config.ssh_minimum_key_sizes=Minimālais atslēgas lielums
|
||||
|
||||
config.db_config=Datu bāzes konfigurācija
|
||||
config.db_type=Veids
|
||||
config.db_host=Resursdators
|
||||
@@ -957,33 +1079,41 @@ config.db_ssl_mode=SSL režīms
|
||||
config.db_ssl_mode_helper=(tikai PostgreSQL datu bāzei)
|
||||
config.db_path=Ceļš
|
||||
config.db_path_helper=(priekš "sqlite3" and "tidb")
|
||||
|
||||
config.service_config=Pakalpojuma konfigurācija
|
||||
config.register_email_confirm=Pieprasīt e-pasta apstiprināšanu
|
||||
config.disable_register=Atspējot jaunu lietotāju reģistrāciju
|
||||
config.show_registration_button=Rādīt reģistrēšanās pogu
|
||||
config.require_sign_in_view=Nepieciešama autorizācija
|
||||
config.enable_cache_avatar=Glabāt profila attēlus kešatmiņā
|
||||
config.mail_notify=Pasta paziņojumi
|
||||
config.disable_key_size_check=Atspējot atslēgas minimālā garuma pārbaudi
|
||||
config.enable_captcha=Iespējot drošības kodu
|
||||
config.active_code_lives=Aktīvā koda ilgums
|
||||
config.reset_password_code_lives=Paroles atiestatīšanas koda ilgums
|
||||
|
||||
config.webhook_config=Tīkla āķu konfigurācija
|
||||
config.queue_length=Rindas garums
|
||||
config.deliver_timeout=Piegādes noildze
|
||||
config.skip_tls_verify=Izlaist TLS pārbaudi
|
||||
|
||||
config.mailer_config=Sūtītāja konfigurācija
|
||||
config.mailer_enabled=Iespējots
|
||||
config.mailer_disable_helo=Atspējot HELO
|
||||
config.mailer_name=Nosaukums
|
||||
config.mailer_host=Resursdators
|
||||
config.mailer_user=Lietotājs
|
||||
config.send_test_mail=Nosūtīt pārbaudes e-pastu
|
||||
config.test_mail_failed=Neizdevās nosūtīt pārbaudes e-pasta vēstuli uz '%s': %v
|
||||
config.test_mail_sent=Pārbaudes e-pasta vēstule tika nosūtīta uz '%s'.
|
||||
|
||||
config.oauth_config=OAuth konfigurācija
|
||||
config.oauth_enabled=Iespējota
|
||||
|
||||
config.cache_config=Kešatmiņas konfigurācija
|
||||
config.cache_adapter=Kešatmiņas adapteris
|
||||
config.cache_interval=Kešatmiņas intervāls
|
||||
config.cache_conn=Kešatmiņas pieslēguma parametri
|
||||
|
||||
config.session_config=Sesijas konfigurācja
|
||||
config.session_provider=Sesijas nodrošinātājs
|
||||
config.provider_config=Pakalpojumu sniedzēja konfigurācija
|
||||
@@ -993,9 +1123,24 @@ config.gc_interval_time=GC laika intervāls
|
||||
config.session_life_time=Sesijas ilgums
|
||||
config.https_only=Tikai HTTPS
|
||||
config.cookie_life_time=Sīkdatņu glabāšanas ilgums
|
||||
|
||||
config.picture_config=Attēlu konfigurācija
|
||||
config.picture_service=Lokāli attēli
|
||||
config.disable_gravatar=Atspējot Gravatar
|
||||
config.enable_federated_avatar=Iespējot apvienotās profila bildes
|
||||
|
||||
config.git_config=Git konfigurācija
|
||||
config.git_disable_diff_highlight=Atspējot salīdzināšanas sintakses iekrāsošanu
|
||||
config.git_max_diff_lines=Maksimālais salīdzināmo rindu skaits vienam failam
|
||||
config.git_max_diff_line_characters=Maksimālais salīdzināmo simbolu skaits vienai rindai
|
||||
config.git_max_diff_files=Maksimālais salīdzināmo failu skaits, ko attēlot
|
||||
config.git_gc_args=GC argumenti
|
||||
config.git_migrate_timeout=Migrācijas noilgums
|
||||
config.git_mirror_timeout=Spoguļa atjaunošanas noilgums
|
||||
config.git_clone_timeout=Klonēšanas darbības noilgums
|
||||
config.git_pull_timeout=Izmaiņu saņemšanas darbības noilgums
|
||||
config.git_gc_timeout=GC darbības noilgums
|
||||
|
||||
config.log_config=Žurnalizēšanas konfigurācija
|
||||
config.log_mode=Žurnalizēšanas veids
|
||||
|
||||
@@ -1029,7 +1174,11 @@ create_repo=izveidoja repozitoriju <a href="%s">%s</a>
|
||||
rename_repo=pārsauca repozitoriju no <code>%[1]s</code> uz <a href="%[2]s">%[3]s</a>
|
||||
commit_repo=veica izmaiņu nosūtīšanu atzaram <a href="%[1]s/src/%[2]s">%[3]s</a> repozitorijā <a href="%[1]s">%[4]s</a>
|
||||
create_issue=`reģistrēja problēmu <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
close_issue=`slēdza problēmu <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
reopen_issue=`atkārtoti atvēra problēmu <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
create_pull_request=`izveidoja izmaiņu pieprasījumu <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
close_pull_request=`aizvēra izmaiņu pieprasījumu <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
reopen_pull_request=`atkārtoti atvēra izmaiņu pieprasījumu <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
comment_issue=`pievienoja komentāru problēmai <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
merge_pull_request=`sapludināja izmaiņu pieprasījumu <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
transfer_repo=mainīja repozitorija <code>%s</code> īpašnieku uz <a href="%s">%s</a>
|
||||
|
||||
455
conf/locale/locale_nl-NL.ini
Executable file → Normal file
455
conf/locale/locale_nl-NL.ini
Executable file → Normal file
@@ -5,9 +5,9 @@ dashboard=Dashboard
|
||||
explore=Verkennen
|
||||
help=Help
|
||||
sign_in=Inloggen
|
||||
sign_out=Afmelden
|
||||
sign_out=Uitloggen
|
||||
sign_up=Aanmelden
|
||||
register=Registreer
|
||||
register=Registreren
|
||||
website=Website
|
||||
version=Versie
|
||||
page=Pagina
|
||||
@@ -19,17 +19,17 @@ signed_in_as=Aangemeld als
|
||||
|
||||
username=Gebruikersnaam
|
||||
email=E-mail
|
||||
password=Wachttwoord
|
||||
password=Wachtwoord
|
||||
re_type=Verificatie
|
||||
captcha=CAPTCHA
|
||||
|
||||
repository=Repositorie
|
||||
repository=Repository
|
||||
organization=Organisatie
|
||||
mirror=Spiegel
|
||||
new_repo=Nieuwe repositorie
|
||||
new_repo=Nieuwe repository
|
||||
new_migrate=Nieuwe migratie
|
||||
new_mirror=Nieuwe Kopie
|
||||
new_fork=Nieuwe vork Repository
|
||||
new_fork=Nieuwe Fork
|
||||
new_org=Nieuwe organisatie
|
||||
manage_org=Beheer organisaties
|
||||
admin_panel=Adminpaneel
|
||||
@@ -38,25 +38,18 @@ settings=Instellingen
|
||||
your_profile=Uw profiel
|
||||
your_settings=Uw instellingen
|
||||
|
||||
news_feed=Nieuwsfeed
|
||||
pull_requests=Pull-aanvragen
|
||||
activities=Activiteiten
|
||||
pull_requests=Pull requests
|
||||
issues=Kwesties
|
||||
|
||||
cancel=Annuleer
|
||||
|
||||
[search]
|
||||
search=Zoeken...
|
||||
repository=Opslagplaats
|
||||
user=Gebruiker
|
||||
issue=Probleem
|
||||
code=Code
|
||||
cancel=Annuleren
|
||||
|
||||
[install]
|
||||
install=Installatie
|
||||
title=Installatiestappen voor de eerste keer opstarten
|
||||
docker_helper=Als u gebruik maakt Gogs binnen Docker, lees dan de <a target="_blank" href="%s">richtlijnen</a> voordat u iets veranderen op deze pagina!
|
||||
requite_db_desc=Gogs vereist MySQL, PostgreSQL, SQite3 of TiDB.
|
||||
db_title=Database instellingen
|
||||
db_title=Database-instellingen
|
||||
db_type=Database-type
|
||||
host=Host
|
||||
user=Gebruikersnaam
|
||||
@@ -65,7 +58,7 @@ db_name=Database naam
|
||||
db_helper=Gebruik InnoDB engine met utf8_general_ci karakterset voor MySQL.
|
||||
ssl_mode=SSL-modus
|
||||
path=Pad
|
||||
sqlite_helper=Het bestandspad van de SQLite3 of TiDB databank.
|
||||
sqlite_helper=Het pad van de SQLite3- of TiDB-database.<br>Als u Gogs start als een service, geef dan een absoluut pad op.
|
||||
err_empty_db_path=SQLite3 of TiDB databankpad mag niet leeg.
|
||||
err_invalid_tidb_name=TiDB databank naam niet tekens kunnen "." en "-".
|
||||
no_admin_and_disable_registration=Je kunt niet de registratie uit te schakelen zonder een beheerders account.
|
||||
@@ -86,6 +79,8 @@ http_port=HTTP-poort
|
||||
http_port_helper=Poortnummer waar het programma naar luistert.
|
||||
app_url=Applicatie URL
|
||||
app_url_helper=Dit heeft invloed op de HTTP/HTTPS kloon urls en de urls die in de email worden gebruikt
|
||||
log_root_path=Log-pad
|
||||
log_root_path_helper=Directory waar logbestanden opgeslagen worden.
|
||||
|
||||
optional_title=Optionele instellingen
|
||||
email_title=E-mail service instellingen
|
||||
@@ -101,6 +96,8 @@ offline_mode=Off line modus inschakelen
|
||||
offline_mode_popup=Schakel CDN uit in productiemodus, alle bestanden worden lokaal aangeboden.
|
||||
disable_gravatar=Gravatar Service uitschakelen
|
||||
disable_gravatar_popup=Schakel Gravatar en andere bronnen uit, alle avatars worden door gebruikers geüpload of zijn standaard.
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=Schakel zelfregistratie uit
|
||||
disable_registration_popup=Schakel zelfregistratie uit, alleen admins kunnen accounts maken.
|
||||
enable_captcha=Inschakelen Captcha
|
||||
@@ -117,17 +114,20 @@ install_gogs=Installeer Gogs
|
||||
test_git_failed=Git test niet gelukt: 'git' commando %v
|
||||
sqlite3_not_available=Uw versie biedt geen ondersteuning voor SQLite3, download de officiële binaire versie van %s, niet de gobuild versie.
|
||||
invalid_db_setting=Uw database instellingen zijn niet correct: %v
|
||||
invalid_repo_path=Repositorie basis pad is niet correct: %v
|
||||
invalid_repo_path=Repositorie basis map is niet correct: %v
|
||||
run_user_not_match=De uitvoerende gebruiker is niet de huidig gebruiker: %s -> %s
|
||||
invalid_smtp_from=SMTP From field is not valid: %v
|
||||
save_config_failed=Kan de configuratie niet opslaan: %v
|
||||
invalid_admin_setting=Uw admin-instellingen zijn niet geldig: %v
|
||||
install_success=Welkom! Wij zijn veheugd dat u voor Gogs heeft gekozen, veel plezier en tot ziens
|
||||
invalid_log_root_path=Ongeldig log-pad: %v
|
||||
|
||||
[home]
|
||||
uname_holder=Gebruikersnaam of e-mail
|
||||
password_holder=Wachtwoord
|
||||
switch_dashboard_context=Wissel voorpaginacontext
|
||||
my_repos=Mijn repositories
|
||||
show_more_repos=Toon meer repositories...
|
||||
collaborative_repos=Gedeelde repositories
|
||||
my_orgs=Mijn organisaties
|
||||
my_mirrors=Mijn mirrors
|
||||
@@ -137,6 +137,9 @@ issues.in_your_repos=In uw repositories
|
||||
|
||||
[explore]
|
||||
repos=Repositories
|
||||
users=Gebruikers
|
||||
organizations=Organizations
|
||||
search=Zoeken
|
||||
|
||||
[auth]
|
||||
create_new_account=Maak nieuw account aan
|
||||
@@ -150,15 +153,17 @@ forget_password=Wachtwoord vergeten?
|
||||
sign_up_now=Een account nodig? Meld u nu aan.
|
||||
confirmation_mail_sent_prompt=Een bevestigingsemail is gestuurd naar <b>%s</b>, Bevestig u aanvraag binnen %d uren om uw registratie te voltooien.
|
||||
active_your_account=Activeer uw account
|
||||
prohibit_login=Login Prohibited
|
||||
prohibit_login_desc=Your account is prohibited to login, please contact site admin.
|
||||
resent_limit_prompt=Sorry, u heeft te snel na elkaar een aanvraag gedaan voor een activatie mail. Wacht drie minuten voor uw volgende aanvraag.
|
||||
has_unconfirmed_mail=Beste %s, u heeft een onbevestigd e-mailadres (<b>%s</b>). Als u nog geen bevestiging heeft ontvangen, of u een nieuwe aanvraag wilt doen, klik dan op de onderstaande knop.
|
||||
resend_mail=Klik hier om uw activatie mail nog een keer te verzenden
|
||||
email_not_associate=Dit e-mailadres is niet gekoppeld aan een account.
|
||||
send_reset_mail=Klik hier om uw wachtwoord reset mail (nogmaals) te versturen
|
||||
reset_password=Reset uw wachtwoord
|
||||
invalid_code=Sorry, uw bevestigingscode is verlopen of niet meer geldig.
|
||||
reset_password_helper=Klik hier om uw wachtwoord opnieuw in te stellen.
|
||||
password_too_short=De lengte van uw wachtwoord moet minimaal zes karakters zijn.
|
||||
non_local_account=Non-local accounts cannot change passwords through Gogs.
|
||||
|
||||
[mail]
|
||||
activate_account=Activeer uw account
|
||||
@@ -174,7 +179,7 @@ modify=Aanpassen
|
||||
|
||||
[form]
|
||||
UserName=Gebruikersnaam
|
||||
RepoName=Repositorie naam
|
||||
RepoName=Naam van repository
|
||||
Email=e-mailadres
|
||||
Password=Wachtwoord
|
||||
Retype=Verifieer wachtwoord
|
||||
@@ -185,6 +190,13 @@ TeamName=Team naam
|
||||
AuthName=Autorisatienaam
|
||||
AdminEmail=E-mail beheerder
|
||||
|
||||
NewBranchName=Nieuwe branch naam
|
||||
CommitSummary=Commit samenvatting
|
||||
CommitMessage=Commit bericht
|
||||
CommitChoice=Commit choice
|
||||
TreeName=Bestandspad
|
||||
Content=Inhoud
|
||||
|
||||
require_error=kan niet leeg zijn.
|
||||
alpha_dash_error=moet een valide alfanumeriek of dash(-_) karakter zijn.
|
||||
alpha_dash_dot_error=moet een valide alfanumeriek, dash(-_) of (.) punt karakter zijn.
|
||||
@@ -199,11 +211,10 @@ captcha_incorrect=Captcha komt niet overeen.
|
||||
password_not_match=Wachtwoord en verificatie wachtwoord komen niet overeen.
|
||||
|
||||
username_been_taken=Gebruikersnaam is al in gebruik.
|
||||
repo_name_been_taken=Repositorie naam is al in gebruik.
|
||||
repo_name_been_taken=Deze naam is al in gebruik.
|
||||
org_name_been_taken=Organisatie naam is al in gebruik.
|
||||
team_name_been_taken=Team naam is al in gebruik.
|
||||
email_been_used=e-mailadres is al in gebruik.
|
||||
illegal_team_name=Team naam bevat illegale karakters.
|
||||
username_password_incorrect=Gebruikersnaam of wachtwoord is niet correct.
|
||||
enterred_invalid_repo_name=U heeft een onjuiste repositorie naam ingevoerd.
|
||||
enterred_invalid_owner_name=U heeft een onjuiste eigenaar ingevoerd.
|
||||
@@ -219,13 +230,10 @@ still_own_repo=Uw account heeft nog een eigendom op een repositorie. U moet deze
|
||||
still_has_org=Uw account nog steeds lidmaatschap van organisatie, u hebt naar links of hen eerst verwijderen.
|
||||
org_still_own_repo=De organisatie heeft nog eigendomen op repositories. U moet deze eerst verwijderen of overdragen.
|
||||
|
||||
still_own_user=Deze authenticatie methode wordt nog gebruikt door sommige gebruikers. U moet hen eerst verplaatsen of verwijderen.
|
||||
|
||||
target_branch_not_exist=Doel branch bestaat niet
|
||||
|
||||
[user]
|
||||
change_avatar=Verander uw avatar op Gravatar.com
|
||||
change_custom_avatar=Wijzig uw avatar in de instellingen
|
||||
change_avatar=Wijzig je profielfoto
|
||||
join_on=Aangemeld op
|
||||
repositories=repositories
|
||||
activity=Openbare activiteit
|
||||
@@ -241,6 +249,7 @@ form.name_pattern_not_allowed=Het gebruikersnaam patroon '%s' is niet toegestaan
|
||||
[settings]
|
||||
profile=Profiel
|
||||
password=Wachtwoord
|
||||
avatar=Profielfoto
|
||||
ssh_keys=SSH-sleutels
|
||||
social=Sociale netwerk-accounts
|
||||
applications=Toepassingen
|
||||
@@ -261,12 +270,13 @@ change_username_prompt=Deze verandering zal de weg links hebben betrekking op uw
|
||||
continue=Doorgaan
|
||||
cancel=Annuleren
|
||||
|
||||
lookup_avatar_by_mail=Zoek profielfoto per email
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=Aangepaste avatar inschakelen
|
||||
enable_custom_avatar_helper=Avatar niet ophalen van Gravatar
|
||||
choose_new_avatar=Kies een nieuwe avatar
|
||||
update_avatar=Avatar instelling bijwerken
|
||||
delete_current_avatar=Verwijder huidige avatar
|
||||
uploaded_avatar_not_a_image=Geüpload bestand is geen afbeelding.
|
||||
no_custom_avatar_available=Geen aangepaste avatar beschikbaar, kan niet worden ingeschakeld.
|
||||
update_avatar_success=Instellingen voor avatar succesvol bijgewerkt.
|
||||
|
||||
change_password=Verander wachtwoord
|
||||
@@ -328,6 +338,10 @@ access_token_deletion=Persoonlijke toegang token verwijderen
|
||||
access_token_deletion_desc=Verwijderen van deze persoonlijke toegang token zal alle verwante toegang verwijderen. Wilt u doorgaan?
|
||||
delete_token_success=Persoonlijke toegangstoken is met succes verwijderd! Vergeet niet uw toepassingen ook bij te werken.
|
||||
|
||||
orgs.none=You are not a member of any organizations.
|
||||
orgs.leave_title=Leave an organization
|
||||
orgs.leave_desc=You will lose access to all repositories and teams after you left the organization. Do you want to continue?
|
||||
|
||||
delete_account=Verwijder uw account
|
||||
delete_prompt=Deze handeling zal uw account definitief verwijderen, u kunt dit <strong> NIET </strong> terug draaien!
|
||||
confirm_delete_account=Bevestig verwijdering
|
||||
@@ -336,30 +350,33 @@ delete_account_desc=Dit account zal permanent worden verwijderd. Wilt u doorgaan
|
||||
|
||||
[repo]
|
||||
owner=Eigenaar
|
||||
repo_name=Repositorie naam
|
||||
repo_name_helper=Een goede repositorie naam is kort, memorabel en <strong>uniek</strong>.
|
||||
repo_name=Naam van repository
|
||||
repo_name_helper=Een goede repository-naam is kort, makkelijk te onthouden en <strong>uniek</strong>.
|
||||
visibility=Zichtbaarheid
|
||||
visiblity_helper=Deze repositorie is <span class="ui red text">privaat</span>
|
||||
visiblity_helper_forced=Sitebeheerder heeft alle nieuwe repositories gedwongen <span class="ui red text">privé</span> te zijn
|
||||
visiblity_fork_helper=(Verandering van deze waarde zal van invloed zijn op alle forks)
|
||||
clone_helper=De behoeftehulp van klonen? Bezoek <a target="_blank" href="%s"> helpen</a>!
|
||||
fork_repo=Vork Repository
|
||||
fork_repo=Repository forken
|
||||
fork_from=Afsplitsing van
|
||||
fork_visiblity_helper=Gevorkte repository wijzigen zijn bereik potentiële kopers niet
|
||||
fork_visiblity_helper=U kunt de zichtbaarheid van een geforkte repository niet aanpassen.
|
||||
repo_desc=Omschrijving
|
||||
repo_lang=Taal
|
||||
repo_lang_helper=Selecteer .gitignore bestanden
|
||||
repo_gitignore_helper=Selecteer .gitignore sjablonen
|
||||
license=Licentie
|
||||
license_helper=Selecteer een licentie bestand
|
||||
readme=Leesmij-bestand
|
||||
readme_helper=Selecteer een sjabloon voor het Leesmij-bestand
|
||||
auto_init=Initialiseer deze repositorie met de geselecteerde bestanden en sjabloon
|
||||
create_repo=Nieuwe Repositorie
|
||||
create_repo=Nieuwe repository
|
||||
default_branch=Standaard branch
|
||||
mirror_prune=Opschonen
|
||||
mirror_prune_desc=Remove any remote-tracking references that no longer exist on the remote
|
||||
mirror_interval=Mirror interval(uur)
|
||||
mirror_address=Kopie-adres
|
||||
mirror_address_desc=Gelieve noodzakelijke gebruikersgegevens in de adresbalk.
|
||||
watchers=Watchers
|
||||
mirror_last_synced=Laatste synchronisatie
|
||||
watchers=Volgers
|
||||
stargazers=Stargazers
|
||||
forks=Forks
|
||||
|
||||
@@ -372,7 +389,8 @@ migrate_type=Migratie type
|
||||
migrate_type_helper=Deze repositorie zal een <span class="text blue">mirror</span> zijn
|
||||
migrate_repo=Migreer repositorie
|
||||
migrate.clone_address=Clone adres
|
||||
migrate.clone_address_desc=Dit kan een HTTP/HTTPS/GIT URL zijn of een lokaal pad.
|
||||
migrate.clone_address_desc=This can be a HTTP/HTTPS/GIT URL.
|
||||
migrate.clone_address_desc_import_local=You're also allowed to migrate a repository by local server path.
|
||||
migrate.permission_denied=U bent niet toegestaan om deze lokale repositories te importeren.
|
||||
migrate.invalid_local_path=Ongeldig lokaal pad, het pad bestaat niet of het is geen map.
|
||||
migrate.failed=Migratie is mislukt: %v
|
||||
@@ -397,7 +415,7 @@ create_new_repo_command=Maak een nieuwe repositorie aan vanaf de console
|
||||
push_exist_repo=Push een bestaande repositorie vanaf de console
|
||||
repo_is_empty=Deze repositories is leeg is, probeer het later opnieuw!
|
||||
|
||||
code=Code
|
||||
files=Files
|
||||
branch=Aftakking
|
||||
tree=Boom
|
||||
filter_branch_and_tag=Filter branch of tag
|
||||
@@ -413,6 +431,47 @@ file_raw=Ruwe
|
||||
file_history=Geschiedenis
|
||||
file_view_raw=Weergave ruwe
|
||||
file_permalink=Permalink
|
||||
file_too_large=Dit bestand is te groot om te worden getoond
|
||||
video_not_supported_in_browser=Your browser doesn't support HTML5 video tag.
|
||||
|
||||
editor.new_file=Nieuw bestand
|
||||
editor.upload_file=Bestand uploaden
|
||||
editor.edit_file=Bewerk bestand
|
||||
editor.preview_changes=Wijzigingen nagaan
|
||||
editor.cannot_edit_non_text_files=Kan niet-tekstbestanden niet bewerken
|
||||
editor.edit_this_file=Bestand aanpassen
|
||||
editor.must_be_on_a_branch=Je moet in een branch zijn om aanpassingen te maken of voor te stellen
|
||||
editor.fork_before_edit=Je moet deze repository eerst vorken om dit bestand aan te kunnen passen
|
||||
editor.delete_this_file=Verwijder dit bestand
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=Bestand '%s' is succesvol verwijderd!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=of
|
||||
editor.cancel_lower=annuleren
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.file_is_a_symlink=The file '%s' is a symlink that cannot be modified from the web editor.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=Commits
|
||||
commits.search=Zoeken
|
||||
@@ -432,13 +491,18 @@ issues.new.no_milestone=Geen mijlpaal
|
||||
issues.new.clear_milestone=Verwijder mijlpaal
|
||||
issues.new.open_milestone=Open mijlpalen
|
||||
issues.new.closed_milestone=Gesloten mijlpalen
|
||||
issues.new.assignee=Verantwoordelijke
|
||||
issues.new.assignee=Toegewezen aan
|
||||
issues.new.clear_assignee=Verwijder verantwoordelijke
|
||||
issues.new.no_assignee=Geen verantwoordelijke
|
||||
issues.create=Maak probleem
|
||||
issues.new_label=Nieuw Label
|
||||
issues.new_label_placeholder=Tekst label...
|
||||
issues.create_label=Maak label
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d Open
|
||||
issues.close_tab=%d gesloten
|
||||
issues.filter_label=Label
|
||||
@@ -466,7 +530,8 @@ issues.next=Volgende
|
||||
issues.open_title=Open
|
||||
issues.closed_title=Gesloten
|
||||
issues.num_comments=%d opmerkingen
|
||||
issues.commented_at=`gaf commentaar op <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commented_at=`commented <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=Are you sure you want to delete this comment?
|
||||
issues.no_content=Er is nog geen inhoud.
|
||||
issues.close_issue=Sluit
|
||||
issues.close_comment_issue=Sluit en geef commentaar
|
||||
@@ -475,12 +540,11 @@ issues.reopen_comment_issue=Heropen en geef commentaar
|
||||
issues.create_comment=Reageer
|
||||
issues.closed_at=`gesloten om <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at=`heropend om <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commit_ref_at='verwees naar dit probleem vanuit een commit <a id="%[1]s" href="#%[1]s"> %[2]s'</a>
|
||||
issues.commit_ref_at=`verwees naar dit probleem vanuit een commit <a id="%[1]s" href="#%[1]s"> %[2]s'</a>`
|
||||
issues.poster=Poster
|
||||
issues.admin=Admin
|
||||
issues.collaborator=Medewerker
|
||||
issues.owner=Eigenaar
|
||||
issues.sign_up_for_free=Gratis aanmelden
|
||||
issues.sign_in_require_desc=om deel te nemen in deze conversatie. Heeft u al een account? <a href="%s">Meld u aan om te reageren</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=Bewerken
|
||||
issues.cancel=Annuleren
|
||||
issues.save=Opslaan
|
||||
@@ -494,6 +558,9 @@ issues.label_modify=Wijzig label
|
||||
issues.label_deletion=Verwijder label
|
||||
issues.label_deletion_desc=Het verwijderen van dit label zal alle informatie in de gerelateerde problemen verwijderen. Wilt u doorgaan?
|
||||
issues.label_deletion_success=Label werd met succes verwijderd!
|
||||
issues.num_participants=%d deelnemers
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=Nieuwe Pull aanvraag
|
||||
pulls.compare_changes=Vergelijk veranderingen
|
||||
@@ -506,20 +573,22 @@ pulls.nothing_to_compare=Er is niets te vergelijken omdat base en head branches
|
||||
pulls.has_pull_request=' Er is al een pull-aanvraag tussen deze twee targets: <a href="%[1]s/pulls/%[3]d"> %[2]s #% [3]d</a>'
|
||||
pulls.create=Pull verzoek aanmaken
|
||||
pulls.title_desc=wil %[1]d commits van <code>%[2]s</code> samenvoegen met <code>%[3]s</code>
|
||||
pulls.merged_title_desc=%[1] commits samengevoegd van <code>%[2]s</code> naar <code>%[3]s</code> %[4]s
|
||||
pulls.merged_title_desc=heeft %[1]d commits samengevoegd van <code>%[2]s</code> naar <code>%[3]s</code> %[4]s
|
||||
pulls.tab_conversation=Discussie
|
||||
pulls.tab_commits=Commits
|
||||
pulls.tab_files=Bestanden gewijzigd
|
||||
pulls.reopen_to_merge=Please reopen this pull request to perform merge operation.
|
||||
pulls.reopen_to_merge=Heropen deze pull request aub om een een merge actie uit te voeren.
|
||||
pulls.merged=Samengevoegd
|
||||
pulls.has_merged=Dit pull-request is samengevoegd!
|
||||
pulls.data_broken=Data of this pull request has been broken due to deletion of fork information.
|
||||
pulls.data_broken=Omdat informatie over de fork is verwijderd, zijn de gegevens van dit pull-request niet beschikbaar.
|
||||
pulls.is_checking=Controle van conflicten is nog bezig, ververs deze pagina in enkele ogenblikken.
|
||||
pulls.can_auto_merge_desc=Dit pull-request kan automatisch samengevoegd worden.
|
||||
pulls.cannot_auto_merge_desc=This pull request can't be merged automatically because there are conflicts.
|
||||
pulls.cannot_auto_merge_helper=Please merge manually in order to resolve the conflicts.
|
||||
pulls.cannot_auto_merge_desc=Dit pull-request kan niet worden gemerged omdat er conflicten zijn.
|
||||
pulls.cannot_auto_merge_helper=Gelieve beide versies manueel samen te voegen om de conflicten op te lossen.
|
||||
pulls.merge_pull_request=Samenvoegen van pull verzoek
|
||||
pulls.open_unmerged_pull_exists=`You can't perform reopen operation because there is already an open pull request (#%d) from same repository with same merge information and is waiting for merging.`
|
||||
pulls.open_unmerged_pull_exists=U kan de bewerking 'heropenen' niet uitvoeren omdat er al een pull-aanvraag (#%d) is van dezelfde repository met dezelfde informatie. Voeg deze eerst samen.
|
||||
pulls.delete_branch=Delete Branch
|
||||
pulls.delete_branch_has_new_commits=Branch cannot be deleted because it has new commits after mergence.
|
||||
|
||||
milestones.new=Nieuwe mijlpaal
|
||||
milestones.open_tab=%d geopend
|
||||
@@ -546,55 +615,78 @@ milestones.deletion_desc=Het verwijderen van dit label zal alle informatie in de
|
||||
milestones.deletion_success=Mijlpaal is met succes verwijderd!
|
||||
|
||||
wiki=Wiki
|
||||
wiki.welcome=Welcome to Wiki!
|
||||
wiki.welcome_desc=Wiki is the place where you would like to document your project together and make it better.
|
||||
wiki.welcome=Welkom op de Wiki!
|
||||
wiki.welcome_desc=In een wiki kunnen gebruikers samen een project documenteren en bediscussiëren.
|
||||
wiki.create_first_page=Maak de eerste pagina
|
||||
wiki.page=Pagina
|
||||
wiki.filter_page=Filter pagina
|
||||
wiki.new_page=Maak nieuwe pagina
|
||||
wiki.default_commit_message=Schrijf een notitie over deze aanpassing (optioneel).
|
||||
wiki.save_page=Pagina opslaan
|
||||
wiki.last_commit_info=%s edited this page %s
|
||||
wiki.last_commit_info=%s heeft deze pagina aangepast %s
|
||||
wiki.edit_page_button=Bewerken
|
||||
wiki.new_page_button=New Page
|
||||
wiki.page_already_exists=Wiki page with same name already exists.
|
||||
wiki.pages=Pages
|
||||
wiki.last_updated=Last updated %s
|
||||
wiki.new_page_button=Nieuwe pagina
|
||||
wiki.delete_page_button=Verwijder pagina
|
||||
wiki.delete_page_notice_1=Dit zal pagina <code>"%s"</code> verwijderen. Weet u het zeker?
|
||||
wiki.page_already_exists=Er bestaat al een wiki-pagina met deze naam.
|
||||
wiki.pages=Pagina’s
|
||||
wiki.last_updated=Laatst bijgewerkt: %s
|
||||
|
||||
settings=Instellingen
|
||||
settings.options=Opties
|
||||
settings.collaboration=Samenwerking
|
||||
settings.collaboration.admin=Admin
|
||||
settings.collaboration.write=Write
|
||||
settings.collaboration.read=Read
|
||||
settings.collaboration.undefined=Undefined
|
||||
settings.hooks=Webhooks
|
||||
settings.githooks=Git haken
|
||||
settings.githooks=Git-hooks
|
||||
settings.basic_settings=Basis instellingen
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=Officiële site
|
||||
settings.update_settings=Instellingen bewerken
|
||||
settings.change_reponame_prompt=This change will affect how links relate to the repository.
|
||||
settings.advanced_settings=Advanced Settings
|
||||
settings.wiki_desc=Enable wiki to allow people write documents
|
||||
settings.use_external_wiki=Use external wiki
|
||||
settings.external_wiki_url=External Wiki URL
|
||||
settings.external_wiki_url_desc=Visitors will be redirected to URL when they click on the tab.
|
||||
settings.issues_desc=Enable builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=Use external issue tracker
|
||||
settings.tracker_url_format=External Issue Tracker URL Format
|
||||
settings.tracker_url_format_desc=You can use placeholder <code>{user} {repo} {index}</code> for user name, repository name and issue index.
|
||||
settings.pulls_desc=Enable pull requests to accept public contributions
|
||||
settings.change_reponame_prompt=Deze verandering zal gevolgen hebben voor hoe links zich verhouden tot de repository.
|
||||
settings.advanced_settings=Geavanceerde opties
|
||||
settings.wiki_desc=Enable wiki system
|
||||
settings.use_internal_wiki=Use builtin wiki
|
||||
settings.use_external_wiki=Externe wiki gebruiken
|
||||
settings.external_wiki_url=Externe wiki-URL
|
||||
settings.external_wiki_url_desc=Bezoekers worden doorgestuurd naar de URL als ze op het tabblad klikken.
|
||||
settings.issues_desc=Enable issue tracker
|
||||
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=Externe issuetracker gebruiken
|
||||
settings.external_tracker_url=External Issue Tracker URL
|
||||
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
|
||||
settings.tracker_url_format=URL-formaat externe issuetracker
|
||||
settings.tracker_issue_style=External Issue Tracker Naming Style:
|
||||
settings.tracker_issue_style.numeric=Numeric
|
||||
settings.tracker_issue_style.alphanumeric=Alphanumeric
|
||||
settings.tracker_url_format_desc=U kan de aanduidingen <code>{user} {repo} {index}</code> gebruiken voor de gebruikersnaam, de naam van de repository en de lijst van open tickets.
|
||||
settings.pulls_desc=Schakel 'pull request' in om publieke bijdragen te mogelijk te maken
|
||||
settings.danger_zone=Gevaren zone
|
||||
settings.new_owner_has_same_repo=De nieuwe eigenaar heeft al een repositorie met deze naam
|
||||
settings.convert=Converteren naar gewone repository
|
||||
settings.convert_desc=U kunt deze mirror converteren naar een gewone repository. Dit kan niet ongedaan worden gemaakt.
|
||||
settings.convert_notices_1=- Deze operatie zet de mirror repository om in een gewone repository en dit kan niet ongedaan gemaakt worden.
|
||||
settings.convert_confirm=Conversie bevestigen
|
||||
settings.convert_succeed=Deze repository is geconverteerd naar een normale repository.
|
||||
settings.transfer=Eigendom overdragen
|
||||
settings.transfer_desc=Draag deze repo over aan een andere gebruiker of een organisatie waar u beheerders rechten heeft.
|
||||
settings.new_owner_has_same_repo=De nieuwe eigenaar heeft al een repositorie met deze naam
|
||||
settings.transfer_notices_1=- U verliest toegang als de nieuwe gebruiker een individuele gebruiker is.
|
||||
settings.transfer_notices_2=- U behoudt toegang indien de nieuwe eigenaar een organisatie is en U één van de eigenaren van de organisatie bent.
|
||||
settings.transfer_form_title=Voer de volgende informatie in om de bewerking te bevestigen:
|
||||
settings.wiki_delete=Wiki gegevens verwijderen
|
||||
settings.wiki_delete_desc=Als U wiki informatie wist gaat deze onherroepelijk verloren. Bent U zeker?
|
||||
settings.wiki_delete_notices_1=- Deze operatie wist de wiki voor %s en schakelt de wiki uit
|
||||
settings.wiki_deletion_success=De repository met wiki data is succesvol gewist.
|
||||
settings.delete=Verwijder deze repositorie
|
||||
settings.delete_desc=Als u eenmaal een repositorie verwijderd is er geen weg terug. Gelieve zeker te zijn van uw acties.
|
||||
settings.transfer_notices_1=- You will lose access if new owner is a individual user.
|
||||
settings.transfer_notices_2=- You will conserve access if new owner is an organization and if you're one of the owners.
|
||||
settings.transfer_form_title=Please enter following information to confirm your operation:
|
||||
settings.delete_notices_1=- This operation <strong>CANNOT</strong> be undone.
|
||||
settings.delete_notices_2=- This operation will permanently delete the everything of this repository, including Git data, issues, comments and accesses of collaborators.
|
||||
settings.delete_notices_fork_1=- If this repository is public, all forks will be became independent after deletion.
|
||||
settings.delete_notices_fork_2=- If this repository is private, all forks will be removed at the same time.
|
||||
settings.delete_notices_fork_3=- If you want to keep all forks after deletion, please change visibility of this repository to public first.
|
||||
settings.deletion_success=Repository has been deleted successfully!
|
||||
settings.delete_notices_1=- Deze bewerking kan <strong>NIET</strong> ongedaan gemaakt worden.
|
||||
settings.delete_notices_2=- Deze bewerking verwijdert permanent alle informatie van deze repository met inbegrip van Git gegevens, tickets, opmerkingen en de toegang van medewerkers.
|
||||
settings.delete_notices_fork_1=- All forks will become independent after deletion.
|
||||
settings.deletion_success=Repository is succesvol verwijderd!
|
||||
settings.update_settings_success=Repositorie instellingen zijn succesvol bijgewerkt.
|
||||
settings.transfer_owner=Nieuwe eigenaar
|
||||
settings.make_transfer=Maak overdracht
|
||||
@@ -602,17 +694,21 @@ settings.transfer_succeed=Eigendom repositorie succesvol overgedragen
|
||||
settings.confirm_delete=Bevestig verwijdering
|
||||
settings.add_collaborator=Nieuwe medewerker toevoegen
|
||||
settings.add_collaborator_success=medewerker is toegevoegd.
|
||||
settings.delete_collaborator=Verwijderen
|
||||
settings.collaborator_deletion=Verwijder Medewerker
|
||||
settings.collaborator_deletion_desc=Deze gebruiker zal niet langer toegang hebben tot deze repository. Wilt U doorgaan?
|
||||
settings.remove_collaborator_success=medewerker is verwijderd.
|
||||
settings.search_user_placeholder=Search user...
|
||||
settings.search_user_placeholder=Zoek gebruiker...
|
||||
settings.org_not_allowed_to_be_collaborator=De organisatie kan niet toegevoegd worden als medewerker.
|
||||
settings.user_is_org_member=Gebruiker is lid van de organisatie die als een medewerker kan niet worden toegevoegd.
|
||||
settings.add_webhook=Webhook toevoegen
|
||||
settings.hooks_desc=Webhooks dat de externe diensten om kennisgevingen te ontvangen wanneer bepaalde gebeurtenissen op Gogs plaatsvinden. Wanneer de opgegeven gebeurtenissen plaatsvinden, sturen we een POST-aanvraag naar elk van de URL's die u opgeeft. Meer informatie vindt u in onze <a target="_blank" href="%s"> Webhooks gids</a>.
|
||||
settings.webhook_deletion=Webhook verwijderen
|
||||
settings.webhook_deletion_desc=Delete this webhook will remove its information and all delivery history. Do you want to continue?
|
||||
settings.webhook_deletion_success=Webhook has been deleted successfully!
|
||||
settings.webhook.test_delivery=Test Delivery
|
||||
settings.webhook.test_delivery_desc=Send a fake push event delivery to test your webhook settings
|
||||
settings.webhook.test_delivery_success=Test webhook has been added to delivery queue. It may take few seconds before it shows up in the delivery history.
|
||||
settings.webhook_deletion_desc=Verwijderen van deze webhook zal de informatie en alle geschiedenis verwijderen. Wilt u doorgaan?
|
||||
settings.webhook_deletion_success=Webhook is succesvol verwijderd!
|
||||
settings.webhook.test_delivery=Test-bezorging
|
||||
settings.webhook.test_delivery_desc=Stuur een nep push bericht om de webhook te testen
|
||||
settings.webhook.test_delivery_success=De test webhook is toegevoegd aan de wachtrij. Het kan enkele seconden duren voor deze in de geschiedenis wordt weergegeven.
|
||||
settings.webhook.request=Verzoek
|
||||
settings.webhook.response=Antwoord
|
||||
settings.webhook.headers=Headers
|
||||
@@ -636,6 +732,8 @@ settings.event_send_everything=Ik moet <strong>alles</strong> hebben.
|
||||
settings.event_choose=Laat me kiezen wat ik nodig heb.
|
||||
settings.event_create=Creëer
|
||||
settings.event_create_desc=Branch, of tag aangemaakt
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=Push
|
||||
settings.event_push_desc=Git push naar een repository
|
||||
settings.active=Actief
|
||||
@@ -652,7 +750,7 @@ settings.slack_domain=Slack domein
|
||||
settings.slack_channel=Slack kanaal
|
||||
settings.deploy_keys=Installeer sleutels
|
||||
settings.add_deploy_key=Toevoegen deploy sleutel
|
||||
settings.deploy_key_desc=Deploy keys have read-only access. They are not the same as personal account SSH keys.
|
||||
settings.deploy_key_desc=Sleutels voor uitrol hebben enkel leesrechten. Ze zijn niet dezelfde als de SSH sleutels van persoonlijke accounts.
|
||||
settings.no_deploy_keys=U hebt nog geen deploy sleutels toegevoegd.
|
||||
settings.title=Titel
|
||||
settings.deploy_key_content=Inhoud
|
||||
@@ -668,11 +766,13 @@ diff.parent=bovenliggende
|
||||
diff.commit=commit
|
||||
diff.data_not_available=Diff gegevens niet beschikbaar.
|
||||
diff.show_diff_stats=Toon Diff Stats
|
||||
diff.show_split_view=Split View
|
||||
diff.show_unified_view=Unified View
|
||||
diff.show_split_view=Zij-aan-zij weergave
|
||||
diff.show_unified_view=Gecombineerde weergave
|
||||
diff.stats_desc=<strong>%d gewijzigde bestanden</strong> met <strong>toevoegingen van %d</strong> en <strong>%d verwijderingen</strong>
|
||||
diff.bin=BIN
|
||||
diff.view_file=Bestand weergeven
|
||||
diff.file_suppressed=File diff suppressed because it is too large
|
||||
diff.too_many_files=Some files were not shown because too many files changed in this diff
|
||||
|
||||
release.releases=Releases
|
||||
release.new_release=Nieuwe release
|
||||
@@ -682,32 +782,33 @@ release.stable=Stabiel
|
||||
release.edit=bewerken
|
||||
release.ahead=<strong>%d</strong> aanpassingen aan %s sinds deze versie
|
||||
release.source_code=Broncode
|
||||
release.new_subheader=Publish releases to iterate product.
|
||||
release.edit_subheader=Detailed change log can help users understand what has been improved.
|
||||
release.new_subheader=Publiceer releases om te itereren.
|
||||
release.edit_subheader=Een gedetailleerd changelog helpt gebruikers te begrijpen wat er is verbeterd in deze release.
|
||||
release.tag_name=Tagnaam
|
||||
release.target=Doel
|
||||
release.tag_helper=Kies een bestaande tag, of creëer een nieuwe tag bij publiceren.
|
||||
release.title=Title
|
||||
release.content=Content
|
||||
release.title=Titel
|
||||
release.content=Inhoud
|
||||
release.write=Schrijf
|
||||
release.preview=Voorbeeld
|
||||
release.loading=Laden...
|
||||
release.prerelease_desc=Dit is een beta-versie
|
||||
release.prerelease_helper=Wij wijzen u erop dat deze release is niet geschikt voor productie doeleinden.
|
||||
release.cancel=Cancel
|
||||
release.cancel=Annuleren
|
||||
release.publish=Release publiceren
|
||||
release.save_draft=Concept opslaan
|
||||
release.edit_release=Release bewerken
|
||||
release.delete_release=Delete This Release
|
||||
release.deletion=Release Deletion
|
||||
release.deletion_desc=Deleting this release will delete the corresponding Git tag. Do you want to continue?
|
||||
release.deletion_success=Release has been deleted successfully!
|
||||
release.delete_release=Deze release verwijderen
|
||||
release.deletion=Release verwijderen
|
||||
release.deletion_desc=Als deze release verwijdert, worden de bijbehorende Git tag ook gewist. Wilt u doorgaan?
|
||||
release.deletion_success=Release is verwijderd!
|
||||
release.tag_name_already_exist=Versie met deze naam bestaat al.
|
||||
release.tag_name_invalid=Tag name is not valid.
|
||||
release.downloads=Downloads
|
||||
|
||||
[org]
|
||||
org_name_holder=Organisatienaam
|
||||
org_full_name_holder=Organization Full Name
|
||||
org_full_name_holder=Volledige naam organisatie
|
||||
org_name_helper=Een goede organisatienaam is kort en memorabel.
|
||||
create_org=Nieuwe organisatie aanmaken
|
||||
repo_updated=Geupdate
|
||||
@@ -734,8 +835,8 @@ settings.website=Website
|
||||
settings.location=Locatie
|
||||
settings.update_settings=Instellingen bijwerken
|
||||
settings.update_setting_success=Organisatie instellingen zijn succesvol bijgewerkt.
|
||||
settings.change_orgname_prompt=This change will affect how links relate to the organization.
|
||||
settings.update_avatar_success=Organization avatar setting has been updated successfully.
|
||||
settings.change_orgname_prompt=Deze verandering beinvloed de verhouding tussen links en de organisatie.
|
||||
settings.update_avatar_success=Organisatie avatar-instellingen zijn succesvol gewijzigd.
|
||||
settings.delete=Verwijder organisatie
|
||||
settings.delete_account=Verwijder deze organisatie
|
||||
settings.delete_prompt=Deze actie zal de origanisatie permanent verwijderen. U kunt dit <strong>NIET</strong> terug draaien!
|
||||
@@ -744,21 +845,21 @@ settings.delete_org_title=Verwijderen organsiatie
|
||||
settings.delete_org_desc=Deze organisatie zal permanent worden verwijderd, wilt u doorgaan?
|
||||
settings.hooks_desc=Een webhook toevoegen die door <strong>alle repositories</strong> in deze organisatie getriggerd kan worden.
|
||||
|
||||
members.membership_visibility=Membership Visibility:
|
||||
members.membership_visibility=Zichtbaarheid lidmaatschap:
|
||||
members.public=Openbaar
|
||||
members.public_helper=maak prive
|
||||
members.private=Prive
|
||||
members.private_helper=maak openbaar
|
||||
members.member_role=Member Role:
|
||||
members.member_role=Rol van lid:
|
||||
members.owner=Eigenaar
|
||||
members.member=Lid
|
||||
members.remove=Verwijderen
|
||||
members.leave=Verlaat
|
||||
members.invite_desc=Add a new member to %s:
|
||||
members.invite_desc=Voeg nieuw lid toe aan %s:
|
||||
members.invite_now=Nu uitnodigen
|
||||
|
||||
teams.join=Lid worden
|
||||
teams.leave=Vertlaat
|
||||
teams.leave=Verlaat
|
||||
teams.read_access=Leestoegang
|
||||
teams.read_access_helper=Dit team is in staat om zijn repositories te bekijken en te klonen.
|
||||
teams.write_access=Schrijf toegang
|
||||
@@ -779,10 +880,10 @@ teams.read_permission_desc=Dit team heeft <strong>Lees</strong> rechten : leden
|
||||
teams.write_permission_desc=Dit team heeft <strong>Schrijf</strong> rechten : leden kunnen repositories lezen en push aanvragen verwerken.
|
||||
teams.admin_permission_desc=Dit team heeft <strong>Beheerders</strong> rechten : leden kunnen repositories lezen en push aanvragen verwerken en medewerkers toevoegen.
|
||||
teams.repositories=Teamrepositories
|
||||
teams.search_repo_placeholder=Search repository...
|
||||
teams.search_repo_placeholder=Repository zoeken...
|
||||
teams.add_team_repository=Nieuwe teamrepositorie aanmaken
|
||||
teams.remove_repo=Verwijder
|
||||
teams.add_nonexistent_repo=De opslagplaats die u probeert toe te voegen niet bestaat, kunt u het eerst aanmaken.
|
||||
teams.add_nonexistent_repo=De opslagplaats die u probeert toe te voegen bestaat niet: maak deze eerst aan.
|
||||
|
||||
[admin]
|
||||
dashboard=Dashboard
|
||||
@@ -793,9 +894,9 @@ authentication=Autenticaties
|
||||
config=Configuratie
|
||||
notices=Systeem aankondigingen
|
||||
monitor=Bijhouden
|
||||
first_page=First
|
||||
last_page=Last
|
||||
total=Total: %d
|
||||
first_page=Eerste
|
||||
last_page=Laatste
|
||||
total=Totaal: %d
|
||||
|
||||
dashboard.statistic=Statistieken
|
||||
dashboard.operations=Bewerkingen
|
||||
@@ -810,14 +911,16 @@ dashboard.delete_inactivate_accounts=Verwijder alle inactieve accounts
|
||||
dashboard.delete_inactivate_accounts_success=Alle inactivering van rekeningen hebben verwijderd.
|
||||
dashboard.delete_repo_archives=Verwijderen van alle repositories archieven
|
||||
dashboard.delete_repo_archives_success=Alle repositories archieven hebben verwijderd.
|
||||
dashboard.delete_missing_repos=Delete all repository records that lost Git files
|
||||
dashboard.delete_missing_repos_success=All repository records that lost Git files have been deleted successfully.
|
||||
dashboard.delete_missing_repos=Verwijder alle repositories zonder Git files
|
||||
dashboard.delete_missing_repos_success=Alle repositories zonder Git files verwijderd.
|
||||
dashboard.git_gc_repos=Garbage collectie uitvoeren
|
||||
dashboard.git_gc_repos_success=Garbage collectie met succes uitgevoerd.
|
||||
dashboard.resync_all_sshkeys=Herschrijf '.ssh/authorized_keys' (Let op: alle sleutels die niet van Gogs zijn zullen verloren gaan!)
|
||||
dashboard.resync_all_sshkeys_success=Alle publieke sleutels zijn herschreven.
|
||||
dashboard.resync_all_update_hooks=Herschrijf alle repositorie-hooks (nodig als de configuratie bestandslocatie is gewijzigd)
|
||||
dashboard.resync_all_update_hooks_success=Alle repositorie-hooks zijn herschreven.
|
||||
dashboard.reinit_missing_repos=Alle repositories zonder Git files opnieuw initialiseren
|
||||
dashboard.reinit_missing_repos_success=Alle repositories zonder Git files zijn succesvol opnieuw geinitializeerd.
|
||||
|
||||
dashboard.server_uptime=Uptime server
|
||||
dashboard.current_goroutine=Huidige Goroutines
|
||||
@@ -856,26 +959,27 @@ users.activated=Geactiveerd
|
||||
users.admin=Admin
|
||||
users.repos=Repos
|
||||
users.created=Aangemaakt
|
||||
users.send_register_notify=Send Registration Notification To User
|
||||
users.new_success=New account '%s' has been created successfully.
|
||||
users.send_register_notify=Stuur notificatie voor registratie naar gebruiker
|
||||
users.new_success=Nieuw account '%s' is aangemaakt.
|
||||
users.edit=Bewerken
|
||||
users.auth_source=Authentication Source
|
||||
users.auth_source=Authenticatiebron
|
||||
users.local=Lokaal
|
||||
users.auth_login_name=Authentication Login Name
|
||||
users.password_helper=Leave it empty to remain unchanged.
|
||||
users.auth_login_name=Authenticatie-loginnaam
|
||||
users.password_helper=Laat leeg om ongewijzigd te blijven.
|
||||
users.update_profile_success=Profiel is succesvol bijgewerkt.
|
||||
users.edit_account=Bewerk account
|
||||
users.max_repo_creation=Maximum Repository Creation Limit
|
||||
users.max_repo_creation_desc=(Set -1 to use global default limit)
|
||||
users.max_repo_creation=Maximum-limiet voor aanmaken van repositories
|
||||
users.max_repo_creation_desc=(Zet op -1 om de globale limiet te gebruiken)
|
||||
users.is_activated=Dit account is geactiveerd
|
||||
users.prohibit_login=This account is prohibited to login
|
||||
users.is_admin=Dit account heeft beheerdersrechten
|
||||
users.allow_git_hook=Deze account beschikt over machtigingen voor het maken van Git haken
|
||||
users.allow_import_local=This account has permissions to import local repositories
|
||||
users.allow_import_local=Dit account mag lokale repositories importeren
|
||||
users.update_profile=Account profiel bijwerken
|
||||
users.delete_account=Dit account verwijderen
|
||||
users.still_own_repo=Dit account is nog steeds eigendom van een repositorie. U moet deze repositorie eerst verwijderen of overdragen.
|
||||
users.still_has_org=Deze account nog steeds lidmaatschap van organisatie, u hebt naar links of hen eerst verwijderen.
|
||||
users.deletion_success=Account has been deleted successfully!
|
||||
users.deletion_success=Het account is verwijderd!
|
||||
|
||||
orgs.org_manage_panel=Organisaties beheren
|
||||
orgs.name=Naam
|
||||
@@ -890,49 +994,53 @@ repos.watches=Volgers
|
||||
repos.stars=Sterren
|
||||
repos.issues=Kwesties
|
||||
|
||||
auths.auth_manage_panel=Authentication Manage Panel
|
||||
auths.new=Add New Source
|
||||
auths.auth_manage_panel=Authenticatie-beheer paneel
|
||||
auths.new=Nieuwe bron toevoegen
|
||||
auths.name=Naam
|
||||
auths.type=Type
|
||||
auths.enabled=Ingeschakeld
|
||||
auths.updated=Bijgewerkt
|
||||
auths.auth_type=Authentication Type
|
||||
auths.auth_name=Authentication Name
|
||||
auths.auth_type=Authenticatietype
|
||||
auths.auth_name=Authenticatienaam
|
||||
auths.security_protocol=Security Protocol
|
||||
auths.domain=Domein
|
||||
auths.host=Host
|
||||
auths.port=Poort
|
||||
auths.bind_dn=Binden DN
|
||||
auths.bind_password=Bind Password
|
||||
auths.bind_password_helper=Warning: This password is stored in plain text. Do not use a high privileged account.
|
||||
auths.bind_password=Bind wachtwoord
|
||||
auths.bind_password_helper=Opgelet: Dit wachtwoord wordt opgeslagen als leesbare tekst. Gebruik geen account met verhoogde rechten.
|
||||
auths.user_base=User Search Base
|
||||
auths.user_dn=User DN
|
||||
auths.attribute_username=Username attribute
|
||||
auths.attribute_username_placeholder=Leave empty to use sign-in form field value for user name.
|
||||
auths.attribute_username=Gebruikersnaam attribuut
|
||||
auths.attribute_username_placeholder=Laat leeg om het login veld van het formulier te gebruiken als gebruikersnaam.
|
||||
auths.attribute_name=Voornaam attribuut
|
||||
auths.attribute_surname=Achternaam attribuut
|
||||
auths.attribute_mail=E-mail attribuut
|
||||
auths.filter=User Filter
|
||||
auths.admin_filter=Admin Filter
|
||||
auths.attributes_in_bind=Verkrijg attributes van de Bind DN context
|
||||
auths.filter=Gebruikersfilter
|
||||
auths.admin_filter=Beheerdersfilter
|
||||
auths.ms_ad_sa=MS Ad SA
|
||||
auths.smtp_auth=SMTP Authentication Type
|
||||
auths.smtp_auth=SMTP-authenticatietype
|
||||
auths.smtphost=SMTP host
|
||||
auths.smtpport=SMTP poort
|
||||
auths.allowed_domains=Allowed Domains
|
||||
auths.allowed_domains_helper=Leave it empty to not restrict any domains. Multiple domains should be separated by comma ','.
|
||||
auths.allowed_domains=Toegelaten domeinen
|
||||
auths.allowed_domains_helper=Laat dit leeg om geen enkel domein te beperken. Meerdere domeinen moeten door een komma (',') gescheiden worden.
|
||||
auths.enable_tls=Activeer TLS-encryptie
|
||||
auths.skip_tls_verify=Skip TLS Verify
|
||||
auths.skip_tls_verify=TLS-verificatie overslaan
|
||||
auths.pam_service_name=PAM servicenaam
|
||||
auths.enable_auto_register=Activeer automatische registratie
|
||||
auths.tips=Tips
|
||||
auths.edit=Edit Authentication Setting
|
||||
auths.edit=Verificatie-instelling bewerken
|
||||
auths.activated=Deze autorisatiemethode is geactiveerd
|
||||
auths.new_success=New authentication '%s' has been added successfully.
|
||||
auths.update_success=Authentication setting has been updated successfully.
|
||||
auths.update=Update Authentication Setting
|
||||
auths.delete=Delete This Authentication
|
||||
auths.delete_auth_title=Authentication Deletion
|
||||
auths.delete_auth_desc=This authentication is going to be deleted, do you want to continue?
|
||||
auths.new_success=Nieuwe authenticatie '%s' werd toegevoegd.
|
||||
auths.update_success=Authenticatie instellingen zijn succesvol gewijzigd.
|
||||
auths.update=Authenticatie-instellingen bijwerken
|
||||
auths.delete=Deze authenticatiewijze verwijderen
|
||||
auths.delete_auth_title=Authenticatie verwijderd
|
||||
auths.delete_auth_desc=Deze authenticatie zal verwijderd worden, wil je verdergaan?
|
||||
auths.still_in_used=This authentication is still used by some users, please delete or convert these users to another login type first.
|
||||
auths.deletion_success=Authentication has been deleted successfully!
|
||||
auths.login_source_exist=Login source '%s' already exists.
|
||||
|
||||
config.server_config=Serverconfiguratie
|
||||
config.app_name=Applicatienaam
|
||||
@@ -943,11 +1051,25 @@ config.offline_mode=Offline-modus
|
||||
config.disable_router_log=Router-log uitschakelen
|
||||
config.run_user=Uitvoerende gebruiker
|
||||
config.run_mode=Uitvoer modus
|
||||
config.git_version=Git Version
|
||||
config.repo_root_path=Repositorie basis pad
|
||||
config.static_file_root_path=Statische bestanden basis pad
|
||||
config.log_file_root_path=Log bestand basis pad
|
||||
config.script_type=Script type
|
||||
config.reverse_auth_user=Omgekeerde verificatie gebruiker
|
||||
|
||||
config.ssh_config=SSH-configuratie
|
||||
config.ssh_enabled=Ingeschakeld
|
||||
config.ssh_start_builtin_server=Ingebouwde server starten
|
||||
config.ssh_domain=Domein
|
||||
config.ssh_port=Poort
|
||||
config.ssh_listen_port=Luister op poort
|
||||
config.ssh_root_path=Root-pad
|
||||
config.ssh_key_test_path=Pad voor key-tests
|
||||
config.ssh_keygen_path=Pad van keygen ('ssh-keygen')
|
||||
config.ssh_minimum_key_size_check=Controleer minimale key-lengte
|
||||
config.ssh_minimum_key_sizes=Minimale key-lengtes
|
||||
|
||||
config.db_config=Databaseconfiguratie
|
||||
config.db_type=Type
|
||||
config.db_host=Host
|
||||
@@ -956,34 +1078,42 @@ config.db_user=Gebruiker
|
||||
config.db_ssl_mode=SSL modus
|
||||
config.db_ssl_mode_helper=(alleen voor "postgres")
|
||||
config.db_path=Pad
|
||||
config.db_path_helper=(for "sqlite3" and "tidb")
|
||||
config.db_path_helper=(voor "sqlite3" en "tidb")
|
||||
|
||||
config.service_config=Serviceconfiguratie
|
||||
config.register_email_confirm=E-mailbevestiging registreren
|
||||
config.disable_register=Registratie uitgeschakeld
|
||||
config.show_registration_button=Registeren knop weergeven
|
||||
config.require_sign_in_view=Inloggen vereist om te kunnen inzien
|
||||
config.enable_cache_avatar=Avatar Cache inschakelen
|
||||
config.mail_notify=E-mailnotificaties
|
||||
config.disable_key_size_check=Disable Minimum Key Size Check
|
||||
config.enable_captcha=Enable Captcha
|
||||
config.disable_key_size_check=Controle op key-lengte uitschakelen
|
||||
config.enable_captcha=CAPTCHA inschakelen
|
||||
config.active_code_lives=Actieve Code leven
|
||||
config.reset_password_code_lives=Reset wachtwoord Code leven
|
||||
|
||||
config.webhook_config=Webhook configuratie
|
||||
config.queue_length=Lengte van wachtrij
|
||||
config.deliver_timeout=Bezorging verlooptijd
|
||||
config.skip_tls_verify=TLS certificaat controle overslaan
|
||||
|
||||
config.mailer_config=Mailerconfiguatie
|
||||
config.mailer_enabled=Ingeschakeld
|
||||
config.mailer_disable_helo=Schakel HELO uit
|
||||
config.mailer_name=Naam
|
||||
config.mailer_host=Host
|
||||
config.mailer_user=Gebruiker
|
||||
config.send_test_mail=Testbericht verzenden
|
||||
config.test_mail_failed=Verzending van een testmail naar '%s' is mislukt: %v
|
||||
config.test_mail_sent=Test-email is verstuurd naar '%s'.
|
||||
|
||||
config.oauth_config=OAuth-configuratie
|
||||
config.oauth_enabled=Ingeschakeld
|
||||
|
||||
config.cache_config=Cache-configuratie
|
||||
config.cache_adapter=Cache-adapter
|
||||
config.cache_interval=Cache-interval
|
||||
config.cache_conn=Cache-connectie
|
||||
|
||||
config.session_config=Sessieconfiguratie
|
||||
config.session_provider=Sessieprovider
|
||||
config.provider_config=Provider config
|
||||
@@ -993,9 +1123,24 @@ config.gc_interval_time=GC interval time
|
||||
config.session_life_time=Sessie duur
|
||||
config.https_only=Alleen HTTPS
|
||||
config.cookie_life_time=Cookie duur leeftijd
|
||||
|
||||
config.picture_config=Foto configuratie
|
||||
config.picture_service=Foto service
|
||||
config.disable_gravatar=Gravatar uitschakelen
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Git Configuration
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=GC Arguments
|
||||
config.git_migrate_timeout=Migration Timeout
|
||||
config.git_mirror_timeout=Mirror Update Timeout
|
||||
config.git_clone_timeout=Clone Operation Timeout
|
||||
config.git_pull_timeout=Pull Operation Timeout
|
||||
config.git_gc_timeout=GC Operation Timeout
|
||||
|
||||
config.log_config=Logconfiguratie
|
||||
config.log_mode=Log-modus
|
||||
|
||||
@@ -1011,13 +1156,13 @@ monitor.start=Starttijd
|
||||
monitor.execute_time=Uitvoertijd
|
||||
|
||||
notices.system_notice_list=Systeem aankondigingen
|
||||
notices.view_detail_header=View Notice Detail
|
||||
notices.actions=Actions
|
||||
notices.select_all=Select All
|
||||
notices.deselect_all=Deselect All
|
||||
notices.inverse_selection=Inverse Selection
|
||||
notices.delete_selected=Delete Selected
|
||||
notices.delete_all=Delete All Notices
|
||||
notices.view_detail_header=Bekijk bericht details
|
||||
notices.actions=Acties
|
||||
notices.select_all=Alles selecteren
|
||||
notices.deselect_all=Alles deselecteren
|
||||
notices.inverse_selection=Selectie omkeren
|
||||
notices.delete_selected=Selectie verwijderen
|
||||
notices.delete_all=Verwijder alle berichten
|
||||
notices.type=Type
|
||||
notices.type_1=Opslagplaats
|
||||
notices.desc=Beschrijving
|
||||
@@ -1029,12 +1174,16 @@ create_repo=repositorie aangemaakt in <a href="%s">%s</a>
|
||||
rename_repo=renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a>
|
||||
commit_repo=push update naar <a href="%[1]s/src/%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a>
|
||||
create_issue=`opende issue in <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
close_issue=`closed issue <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
reopen_issue=`reopened issue <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
create_pull_request=`created pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
close_pull_request=`closed pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
reopen_pull_request=`reopened pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
comment_issue=`reactie op issue <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
merge_pull_request=`merged pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
transfer_repo=repositorie verplaatst naar <code>%s</code> naar <a href="%s">%s</a>
|
||||
push_tag=geduwd label <a href="%s/src/%s"> %[2]s</a> naar <a href="%[1]s"> %[3]s</a>
|
||||
compare_commits=View comparison for these %d commits
|
||||
compare_commits=Toon vergelijking voor deze %d commits
|
||||
|
||||
[tool]
|
||||
ago=geleden
|
||||
|
||||
249
conf/locale/locale_pl-PL.ini
Executable file → Normal file
249
conf/locale/locale_pl-PL.ini
Executable file → Normal file
@@ -38,19 +38,12 @@ settings=Ustawienia
|
||||
your_profile=Twój profil
|
||||
your_settings=Twoje ustawienia
|
||||
|
||||
news_feed=Kanał aktualności
|
||||
activities=Aktywności
|
||||
pull_requests=Oczekujące zmiany
|
||||
issues=Problemy
|
||||
|
||||
cancel=Anuluj
|
||||
|
||||
[search]
|
||||
search=Wyszukiwanie...
|
||||
repository=Repozytorium
|
||||
user=Użytkownik
|
||||
issue=Problem
|
||||
code=Kod
|
||||
|
||||
[install]
|
||||
install=Instalacja
|
||||
title=Kroki instalacyjne dla pierwszego uruchomienia
|
||||
@@ -65,9 +58,9 @@ db_name=Nazwa bazy danych
|
||||
db_helper=Proszę użyć silnika INNODB z kodowaniem utf8_general_ci dla MySQL.
|
||||
ssl_mode=Tryb SSL
|
||||
path=Ścieżka
|
||||
sqlite_helper=Ścieżka do pliku bazy danych SQLite3 lub TiDB.
|
||||
sqlite_helper=Ścieżka do pliku bazy danych SQLite3 lub TiDB. <br>Proszę użyć ścieżki bezwzględnej podczas uruchamiania usługi.
|
||||
err_empty_db_path=Ścieżka do bazy danych SQLite3 lub TiDB nie może być pusta.
|
||||
err_invalid_tidb_name=Nazwa bazy danych TiDB nie może zawierać znaków "." i "-".
|
||||
err_invalid_tidb_name=Nazwa bazy danych TiDB nie może zawierać znaków „.” i „-”.
|
||||
no_admin_and_disable_registration=Rejestracji nie można wyłączyć bez tworzenia konta admina.
|
||||
err_empty_admin_password=Hasło admina nie może być puste.
|
||||
|
||||
@@ -86,12 +79,14 @@ http_port=Port HTTP
|
||||
http_port_helper=Numer portu na którym aplikacja jest dostępna.
|
||||
app_url=Adres URL aplikacji
|
||||
app_url_helper=To wpłynie na adresy klonowania HTTP/HTTPS i w wiadomościach e-mail.
|
||||
log_root_path=Ścieżka dla logów
|
||||
log_root_path_helper=Katalog do zapisu logów.
|
||||
|
||||
optional_title=Ustawienia opcjonalne
|
||||
email_title=Ustawienia serwera e-mail
|
||||
smtp_host=Serwer SMTP
|
||||
smtp_from=Od
|
||||
smtp_from_helper=Adres w polu "Od", zgodnie z RFC 5322. Może być to po prostu adres email, bądź adres w formacie "Nazwa" <email@example.com>.
|
||||
smtp_from_helper=Adres w polu „Od”, zgodnie z RFC 5322. Może być to po prostu adres email, bądź adres w formacie „Nazwa” <email@example.com>.
|
||||
mailer_user=E-mail nadawcy
|
||||
mailer_password=Hasło nadawcy
|
||||
register_confirm=Włącz potwierdzenia rejestracji
|
||||
@@ -101,6 +96,8 @@ offline_mode=Włącz tryb offline
|
||||
offline_mode_popup=Wyłącz CDN, nawet w trybie produkcyjnym, wszystkie pliki zasobów będą podawane lokalnie.
|
||||
disable_gravatar=Wyłącz usługę Gravatar
|
||||
disable_gravatar_popup=Wyłącz Gravatar i niestandardowe źrodła, awatary muszą być przesyłane przez użytkowników.
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=Wyłącz samodzielną rejestrację
|
||||
disable_registration_popup=Wyłącz samodzielną rejestrację użytkownika, tylko administrator będzie mógł tworzyć konta.
|
||||
enable_captcha=Włącz Captcha
|
||||
@@ -114,20 +111,23 @@ admin_password=Hasło
|
||||
confirm_password=Potwierdź hasło
|
||||
admin_email=E-mail administratora
|
||||
install_gogs=Zainstaluj Gogs
|
||||
test_git_failed=Nie udało się przetestować polecenia "git": %v
|
||||
test_git_failed=Nie udało się przetestować polecenia „git”: %v
|
||||
sqlite3_not_available=Twoje wydanie nie obsługuje SQLite3, proszę pobrać oficjalne wydanie z %s, a NIE wersję z gobuild.
|
||||
invalid_db_setting=Ustawienia bazy danych nie są poprawne: %v
|
||||
invalid_repo_path=Ścieżka repozytoriów nie jest poprawna: %v
|
||||
run_user_not_match=Użytkownik aplikacji nie jest aktualnym użytkownikiem: %s -> %s
|
||||
invalid_smtp_from=SMTP From field is not valid: %v
|
||||
save_config_failed=Nie udało się zapisać konfiguracji: %v
|
||||
invalid_admin_setting=Nieprawidłowe ustawienia konta admina: %v
|
||||
install_success=Cześć! Cieszymy się, że wybierałeś Gogs, baw się dobrze.
|
||||
invalid_log_root_path=Ścieżka dla logów jest niepoprawna: %v
|
||||
|
||||
[home]
|
||||
uname_holder=Nazwa użytkownika lub e-mail
|
||||
password_holder=Hasło
|
||||
switch_dashboard_context=Przełącz kontekst pulpitu
|
||||
my_repos=Moje repozytoria
|
||||
show_more_repos=Pokaż więcej repozytoriów...
|
||||
collaborative_repos=Wspólne repozytoria
|
||||
my_orgs=Moje organizacje
|
||||
my_mirrors=Moje kopie lustrzane
|
||||
@@ -137,6 +137,9 @@ issues.in_your_repos=W Twoich repozytoriach
|
||||
|
||||
[explore]
|
||||
repos=Repozytoria
|
||||
users=Użytkownicy
|
||||
organizations=Organizations
|
||||
search=Wyszukiwanie
|
||||
|
||||
[auth]
|
||||
create_new_account=Załóż nowe konto
|
||||
@@ -150,15 +153,17 @@ forget_password=Zapomniałeś hasła?
|
||||
sign_up_now=Potrzebujesz konta? Zarejestruj się teraz.
|
||||
confirmation_mail_sent_prompt=Nowa wiadomość e-mail z potwierdzeniem została wysłana do <b>%s</b>, proszę sprawdzić swoją skrzynkę odbiorczą w ciągu najbliższych godzin %d aby dokończyć proces rejestracji.
|
||||
active_your_account=Aktywuj swoje konto
|
||||
prohibit_login=Logowanie zabronione
|
||||
prohibit_login_desc=Nie możesz się zalogować na to konto, skontaktuj się z administratorem strony.
|
||||
resent_limit_prompt=Niestety, zbyt często wysyłasz e-mail aktywacyjny. Proszę odczekać 3 minuty.
|
||||
has_unconfirmed_mail=Witaj, %s, masz niepotwierdzony adres e-mail (<b>%s</b>). Jeśli nie otrzymałeś wiadomości e-mail z potwierdzeniem lub potrzebujesz wysłać nową, kliknij na poniższy przycisk.
|
||||
resend_mail=Kliknij tutaj, aby wysłać e-mail aktywacyjny
|
||||
email_not_associate=Ten adres e-mail nie jest skojarzony z żadnym kontem.
|
||||
send_reset_mail=Kliknij tutaj, aby (ponownie) wysłać e-mail z instrukcjami resetowania hasła
|
||||
reset_password=Resetowanie hasła
|
||||
invalid_code=Niestety, Twój kod potwierdzający wygasł lub jest nieprawidłowy.
|
||||
reset_password_helper=Kliknij tutaj, aby zresetować hasło
|
||||
password_too_short=Długość hasła nie może być mniejsza niż 6 znaków.
|
||||
non_local_account=Nie lokalne konta nie mogą zmieniać haseł przez Gogs.
|
||||
|
||||
[mail]
|
||||
activate_account=Prosimy aktywować swoje konto
|
||||
@@ -185,6 +190,13 @@ TeamName=Nazwa zespołu
|
||||
AuthName=Nazwa autoryzacji
|
||||
AdminEmail=E-mail administratora
|
||||
|
||||
NewBranchName=Nazwa nowej gałęzi
|
||||
CommitSummary=Podsumowanie commitu
|
||||
CommitMessage=Wiadomość commitu
|
||||
CommitChoice=Wybór commitu
|
||||
TreeName=Ścieżka pliku
|
||||
Content=Treść
|
||||
|
||||
require_error=` nie może być puste.`
|
||||
alpha_dash_error=` musi się składać z prawidłowych znaków alfanumerycznych, myślników oraz podkreśleń.`
|
||||
alpha_dash_dot_error=` musi się składać z prawidłowych znaków alfanumerycznych, myślników, podkreśleń oraz kropek.`
|
||||
@@ -203,7 +215,6 @@ repo_name_been_taken=Nazwa repozytorium jest już zajęta.
|
||||
org_name_been_taken=Nazwa organizacji jest już zajęta.
|
||||
team_name_been_taken=Nazwa zespołu jest już zajęta.
|
||||
email_been_used=Adres e-mail jest już zarejestrowany.
|
||||
illegal_team_name=Nazwa zespołu zawiera niedozwolone znaki.
|
||||
username_password_incorrect=Nazwa użytkownika lub hasło nie jest prawidłowe.
|
||||
enterred_invalid_repo_name=Upewnij się, że wprowadzona nazwa repozytorium jest poprawna.
|
||||
enterred_invalid_owner_name=Upewnij się, że nazwa właściciela repozytorium jest poprawna.
|
||||
@@ -219,28 +230,26 @@ still_own_repo=Twoje konto dalej posiada przynajmniej jedno repozytorium, które
|
||||
still_has_org=Twoje konto dalej posiada członkostwo w przynajmniej jednej organizacji, którą musisz najpierw opuścić.
|
||||
org_still_own_repo=Ta organizacja dalej jest właścicielem repozytorium, które musisz usunąć bądź przekazać.
|
||||
|
||||
still_own_user=To uwierzytelnienie dalej jest używane przez kilku użytkowników, których musisz z niego usunąć i spróbować ponownie.
|
||||
|
||||
target_branch_not_exist=Gałąź docelowa nie istnieje.
|
||||
|
||||
[user]
|
||||
change_avatar=Zmień swój avatar na gravatar.com
|
||||
change_custom_avatar=Zmień awatar w ustawieniach
|
||||
change_avatar=Zmień swój awatar
|
||||
join_on=Dołączył
|
||||
repositories=Repozytoria
|
||||
activity=Publiczna aktywność
|
||||
followers=Obserwujący
|
||||
starred=Polubionych
|
||||
following=Obserwowani
|
||||
follow=Follow
|
||||
unfollow=Unfollow
|
||||
follow=Obserwuj
|
||||
unfollow=Przestań obserwować
|
||||
|
||||
form.name_reserved=Nazwa użytkownika "%s" jest zarezerwowana.
|
||||
form.name_pattern_not_allowed=Wzorzec nazwy użytkownika "%s" jest niedozwolony.
|
||||
form.name_reserved=Nazwa użytkownika „%s” jest zarezerwowana.
|
||||
form.name_pattern_not_allowed=Wzorzec nazwy użytkownika „%s” jest niedozwolony.
|
||||
|
||||
[settings]
|
||||
profile=Profil
|
||||
password=Hasło
|
||||
avatar=Awatar
|
||||
ssh_keys=Klucze SSH
|
||||
social=Konta społecznościowe
|
||||
applications=Aplikacje
|
||||
@@ -261,12 +270,13 @@ change_username_prompt=Ta zmiana wpłynie na sposób w jaki łącza odnoszą si
|
||||
continue=Kontynuuj
|
||||
cancel=Anuluj
|
||||
|
||||
lookup_avatar_by_mail=Wyszukaj Avatar po mailu
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=Włącz niestandardowe awatary
|
||||
enable_custom_avatar_helper=Wyłącz pobieranie z Gravatar
|
||||
choose_new_avatar=Wybierz nowy avatar
|
||||
update_avatar=Zaktualizuj ustawienia awatara
|
||||
delete_current_avatar=Usuń obecny Avatar
|
||||
uploaded_avatar_not_a_image=Załadowany plik nie jest obrazem.
|
||||
no_custom_avatar_available=Własny avatar niedostępny, nie można go włączyć.
|
||||
update_avatar_success=Ustawienia awatarów zostały pomyślnie zaktualizowane.
|
||||
|
||||
change_password=Zmień hasło
|
||||
@@ -328,6 +338,10 @@ access_token_deletion=Usuwanie osobistego tokena dostępu
|
||||
access_token_deletion_desc=Usunięcie tego tokena osobistego dostęp spowoduje usunięcie wszystkich powiązanych dostępów do aplikacji. Czy chcesz kontynuować?
|
||||
delete_token_success=Osobisty token dostępu został usunięty pomyślnie! Nie zapomnij również zaktualizować swoich aplikacji.
|
||||
|
||||
orgs.none=You are not a member of any organizations.
|
||||
orgs.leave_title=Leave an organization
|
||||
orgs.leave_desc=You will lose access to all repositories and teams after you left the organization. Do you want to continue?
|
||||
|
||||
delete_account=Usuń swoje konto
|
||||
delete_prompt=Ta operacja trwale usunie Twoje konto i <strong>NIE MOŻE</strong> zostać cofnięta!
|
||||
confirm_delete_account=Potwierdź usunięcie
|
||||
@@ -348,7 +362,7 @@ fork_from=Forkuj z
|
||||
fork_visiblity_helper=Fork nie może zmieniać swojej widoczności
|
||||
repo_desc=Opis
|
||||
repo_lang=Język
|
||||
repo_lang_helper=Wybierz pliki .gitignore
|
||||
repo_gitignore_helper=Wybierz szablony pliku .gitignore
|
||||
license=Licencja
|
||||
license_helper=Wybierz plik licencji
|
||||
readme=Readme
|
||||
@@ -356,23 +370,27 @@ readme_helper=Wybierz szablon readme
|
||||
auto_init=Zainicjuj to repozytorium używając wybranych plików i szablonu
|
||||
create_repo=Utwórz repozytorium
|
||||
default_branch=Domyślna gałąź
|
||||
mirror_prune=Wyczyść
|
||||
mirror_prune_desc=Usuń wszystkie śledzone odwołania które nie istnieją w zdalnym repozytorium
|
||||
mirror_interval=Częstotliwość kopiowania (godziny)
|
||||
mirror_address=Adres kopii lustrzanej
|
||||
mirror_address_desc=Proszę podać wymagane poświadczenia użytkownika w adresie.
|
||||
mirror_last_synced=Ostatnia synchronizacja
|
||||
watchers=Obserwujący
|
||||
stargazers=Polubienia
|
||||
forks=Forki
|
||||
|
||||
form.reach_limit_of_creation=Właściciel osiągnął limit maksymalnej ilości repozytoriów %d.
|
||||
form.name_reserved=Nazwa repozytorium "%s" jest zarezerwowana.
|
||||
form.name_pattern_not_allowed=Wzorzec nazwy repozytorium "%s" jest niedozwolony.
|
||||
form.name_reserved=Nazwa repozytorium „%s” jest zarezerwowana.
|
||||
form.name_pattern_not_allowed=Wzorzec nazwy repozytorium „%s” jest niedozwolony.
|
||||
|
||||
need_auth=Wymaga autoryzacji
|
||||
migrate_type=Typ migracji
|
||||
migrate_type_helper=To repozytorium będzie <span class="text blue">kopią lustrzaną</span>
|
||||
migrate_repo=Przenieś repozytorium
|
||||
migrate.clone_address=Sklonuj adres
|
||||
migrate.clone_address_desc=To może być adres HTTP/HTTPS/GIT lub ścieżka lokalna serwera.
|
||||
migrate.clone_address_desc=This can be a HTTP/HTTPS/GIT URL.
|
||||
migrate.clone_address_desc_import_local=You're also allowed to migrate a repository by local server path.
|
||||
migrate.permission_denied=Nie możesz importować lokalnych repozytoriów.
|
||||
migrate.invalid_local_path=Ścieżka jest niepoprawna. Nie istnieje lub nie jest katalogiem.
|
||||
migrate.failed=Migracja nie powiodła się: %v
|
||||
@@ -397,7 +415,7 @@ create_new_repo_command=Utwórz nowe repozytorium z wiersza poleceń
|
||||
push_exist_repo=Prześlij istniejące repozytorium z wiersza poleceń
|
||||
repo_is_empty=To repozytorium jest puste, proszę wrócić później!
|
||||
|
||||
code=Kod
|
||||
files=Files
|
||||
branch=Gałąź
|
||||
tree=Drzewo
|
||||
filter_branch_and_tag=Filtruj gałąź lub tag
|
||||
@@ -413,6 +431,47 @@ file_raw=Czysty
|
||||
file_history=Historia
|
||||
file_view_raw=Zobacz czysty
|
||||
file_permalink=Bezpośredni odnośnik
|
||||
file_too_large=Ten plik jest zbyt duży, aby go wyświetlić
|
||||
video_not_supported_in_browser=Your browser doesn't support HTML5 video tag.
|
||||
|
||||
editor.new_file=Nowy plik
|
||||
editor.upload_file=Załaduj plik
|
||||
editor.edit_file=Edytuj plik
|
||||
editor.preview_changes=Podgląd zmian
|
||||
editor.cannot_edit_non_text_files=Nie można edytować plików nietekstowych
|
||||
editor.edit_this_file=Edytuj ten plik
|
||||
editor.must_be_on_a_branch=Musisz być na gałęzi aby zgłosić lub zaproponować zmiany do tego pliku
|
||||
editor.fork_before_edit=Musisz sforkować to repozytorium przed edycją tego pliku
|
||||
editor.delete_this_file=Usuń ten plik
|
||||
editor.must_have_write_access=Musisz mieć uprawnienia do zapisu aby zgłosić lub zaproponować zmiany do tego pliku
|
||||
editor.file_delete_success=Plik '%s' został usunięty pomyślnie!
|
||||
editor.name_your_file=Nazwij plik...
|
||||
editor.filename_help=Aby dodać katalog, wpisz nazwę i naciśnij przycisk /. Aby usunąć katalog, przejdź do początku pola i naciśnij klawisz backspace.
|
||||
editor.or=lub
|
||||
editor.cancel_lower=anuluj
|
||||
editor.commit_changes=Zatwierdź zmiany
|
||||
editor.add_tmpl=Dodaj '%s/<filename>'
|
||||
editor.add=Dodaj '%s'
|
||||
editor.update=Zaktualizuj '%s'
|
||||
editor.delete=Usuń '%s'
|
||||
editor.commit_message_desc=Dodaj dodatkowy rozszerzony opis...
|
||||
editor.commit_directly_to_this_branch=Commituj bezpośrednio do gałęzi <strong class="branch-name">%s</strong>.
|
||||
editor.create_new_branch=Stwórz <strong>nową gałąź</strong> dla tego commita i rozpocznij pull request.
|
||||
editor.new_branch_name_desc=Nazwa nowej gałęzi...
|
||||
editor.cancel=Anuluj
|
||||
editor.filename_cannot_be_empty=Nazwa pliku nie może być pusta.
|
||||
editor.branch_already_exists=Gałąź '%s' już istnieje w tym repozytorium.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.file_is_a_symlink=The file '%s' is a symlink that cannot be modified from the web editor.
|
||||
editor.filename_is_a_directory=Nazwa '%s' jest istniejącym katalogiem w tym repozytorium.
|
||||
editor.file_editing_no_longer_exists=Plik '%s' który edytujesz nie istnieje już w tym repozytorium.
|
||||
editor.file_changed_while_editing=Zawartość pliku została zmieniona od rozpoczęcia edycji. <a target="_blank" href="%s">Kliknij tutaj</a> aby zobaczyć, co zostało zmienione lub <strong>naciśnij commit ponownie</strong> aby nadpisać te zmiany.
|
||||
editor.file_already_exists=Nazwa pliku '%s' już istnieje w tym repozytorium.
|
||||
editor.no_changes_to_show=Brak zmian do pokazania.
|
||||
editor.fail_to_update_file=Tworzenie/aktualizacja pliku '%s' nie powiodła się z błędem: %v
|
||||
editor.add_subdir=Dodaj podkatalog...
|
||||
editor.unable_to_upload_files=Wysyłanie plików do '%s' nie powiodło się z błędem: %v
|
||||
editor.upload_files_to_dir=Prześlij pliki do '%s'
|
||||
|
||||
commits.commits=Commity
|
||||
commits.search=Przeszukaj commity
|
||||
@@ -439,6 +498,11 @@ issues.create=Utwórz problem
|
||||
issues.new_label=Nowa etykieta
|
||||
issues.new_label_placeholder=Etykieta...
|
||||
issues.create_label=Utwórz etykietę
|
||||
issues.label_templates.title=Załaduj wstępnie przygotowany zestaw etykiet
|
||||
issues.label_templates.info=Nie ma jeszcze żadnych etykiet. Kliknij na przycisk „Nowa etykieta” powyżej, aby utworzyć lub użyć poniższego zestawu wstępnie zdefiniowanego.
|
||||
issues.label_templates.helper=Wybierz zestaw etykiet
|
||||
issues.label_templates.use=Użyj ten zestaw etykiet
|
||||
issues.label_templates.fail_to_load_file=Ładowanie pliku szablonu etykiety '%s' nie powiodło się: %v
|
||||
issues.open_tab=Otwarte %d
|
||||
issues.close_tab=Zamknięte %d
|
||||
issues.filter_label=Etykieta
|
||||
@@ -466,7 +530,8 @@ issues.next=Następny
|
||||
issues.open_title=Otwarty
|
||||
issues.closed_title=Zamknięty
|
||||
issues.num_comments=%d komentarzy
|
||||
issues.commented_at=`komentuje <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commented_at=`skomentował <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=Czy na pewno chcesz usunąć ten komentarz?
|
||||
issues.no_content=Nie ma jeszcze treści.
|
||||
issues.close_issue=Zamknij
|
||||
issues.close_comment_issue=Skomentuj i zamknij
|
||||
@@ -477,10 +542,9 @@ issues.closed_at=`zamyka <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at=`otwiera ponownie <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commit_ref_at=`wspomina ten problem w commicie <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.poster=Autor
|
||||
issues.admin=Admin
|
||||
issues.collaborator=Współpracownik
|
||||
issues.owner=Właściciel
|
||||
issues.sign_up_for_free=Zarejestruj się za darmo
|
||||
issues.sign_in_require_desc=do przyłączenia się do tej rozmowy. Masz już konto? <a href="%s">Zaloguj się by komentować</a>
|
||||
issues.sign_in_require_desc=<a href="%s"> Zaloguj się</a>, aby dołączyć do tej rozmowy.
|
||||
issues.edit=Edytuj
|
||||
issues.cancel=Anuluj
|
||||
issues.save=Zapisz
|
||||
@@ -494,6 +558,9 @@ issues.label_modify=Modyfikacja etykiety
|
||||
issues.label_deletion=Usunięcie etykiety
|
||||
issues.label_deletion_desc=Usunięcie tej etykiety spowoduje usuniecie jej ze wszystkich powiązanych problemów. Czy na pewno chcesz kontynuować?
|
||||
issues.label_deletion_success=Etykieta została usunięta pomyślnie!
|
||||
issues.num_participants=%d uczestników
|
||||
issues.attachment.open_tab=`Kliknij, aby zobaczyć „%s” w nowej karcie`
|
||||
issues.attachment.download=`Kliknij, aby pobrać „%s”`
|
||||
|
||||
pulls.new=Nowy pull request
|
||||
pulls.compare_changes=Porównaj zmiany
|
||||
@@ -520,6 +587,8 @@ pulls.cannot_auto_merge_desc=Pull request nie może być automatycznie scalony z
|
||||
pulls.cannot_auto_merge_helper=Proszę scalić ręcznie, aby rozwiązać konflikty.
|
||||
pulls.merge_pull_request=Scal Pull Request
|
||||
pulls.open_unmerged_pull_exists=`Nie można otworzyć ponownie ponieważ już istnieje gotowy do scalenia pull request (#%d) z tego samego repozytorium z tymi samymi informacjami.`
|
||||
pulls.delete_branch=Delete Branch
|
||||
pulls.delete_branch_has_new_commits=Branch cannot be deleted because it has new commits after mergence.
|
||||
|
||||
milestones.new=Nowy kamień milowy
|
||||
milestones.open_tab=Otwarte %d
|
||||
@@ -535,7 +604,7 @@ milestones.desc=Opis
|
||||
milestones.due_date=Termin realizacji (opcjonalnie)
|
||||
milestones.clear=Wyczyść
|
||||
milestones.invalid_due_date_format=Format daty realizacji jest nieprawidłowy, musi być "rrrr-mm-dd".
|
||||
milestones.create_success=Kamień milowy "%s" został utworzony pomyślnie!
|
||||
milestones.create_success=Kamień milowy „%s” został utworzony pomyślnie!
|
||||
milestones.edit=Edytuj kamień milowy
|
||||
milestones.edit_subheader=Użyj lepszego opisu, tak aby nie wprowadzać w błąd użytkowników.
|
||||
milestones.cancel=Anuluj
|
||||
@@ -557,6 +626,8 @@ wiki.save_page=Zapisz stronę
|
||||
wiki.last_commit_info=%s edytuje tę stronę %s
|
||||
wiki.edit_page_button=Edytuj
|
||||
wiki.new_page_button=Nowa strona
|
||||
wiki.delete_page_button=Usuń stronę
|
||||
wiki.delete_page_notice_1=Strona zostanie usunięta <code>„%s”</code>. Bądź ostrożny.
|
||||
wiki.page_already_exists=Strona Wiki o tej samej nazwie już istnieje.
|
||||
wiki.pages=Strony
|
||||
wiki.last_updated=Ostatnia aktualizacja %s
|
||||
@@ -564,36 +635,57 @@ wiki.last_updated=Ostatnia aktualizacja %s
|
||||
settings=Ustawienia
|
||||
settings.options=Opcje
|
||||
settings.collaboration=Współpraca
|
||||
settings.collaboration.admin=Administrator
|
||||
settings.collaboration.write=Zapis
|
||||
settings.collaboration.read=Odczyt
|
||||
settings.collaboration.undefined=Niezdefiniowany
|
||||
settings.hooks=Webhooki
|
||||
settings.githooks=Hooki Git
|
||||
settings.basic_settings=Ustawienia podstawowe
|
||||
settings.mirror_settings=Kopia lustrzana ustawień
|
||||
settings.sync_mirror=Synchronizuj teraz
|
||||
settings.mirror_sync_in_progress=Synchronizacja kopii lustrzanej jest w toku, odśwież stronę w ciągu minuty.
|
||||
settings.site=Oficjalna Strona
|
||||
settings.update_settings=Aktualizuj ustawienia
|
||||
settings.change_reponame_prompt=Zmiana nazwy repozytorium wpłynie na linki do niego.
|
||||
settings.advanced_settings=Ustawienia zaawansowane
|
||||
settings.wiki_desc=Włączenie Wiki pozwoli innym pisać dokumenty
|
||||
settings.wiki_desc=Włącz system wiki
|
||||
settings.use_internal_wiki=Użyj wbudowanego wiki
|
||||
settings.use_external_wiki=Użyj zewnętrznego Wiki
|
||||
settings.external_wiki_url=Adres URL zewnętrznego Wiki
|
||||
settings.external_wiki_url_desc=Odwiedzający zostaną przekierowani do adresu URL po kliknięciu zakładki.
|
||||
settings.issues_desc=Włącz wbudowany lekki system zgłaszania problemów
|
||||
settings.issues_desc=Włącz system zgłaszania problemów
|
||||
settings.use_internal_issue_tracker=Użyj wbudowany lekki system zgłaszania problemów
|
||||
settings.use_external_issue_tracker=Użyj zewnętrznego systemu zgłaszania problemów
|
||||
settings.external_tracker_url=External Issue Tracker URL
|
||||
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
|
||||
settings.tracker_url_format=Format dla adresu URL zewnętrznego systemu
|
||||
settings.tracker_issue_style=Styl nazw zewnętrznego systemu zgłaszania problemów:
|
||||
settings.tracker_issue_style.numeric=Numeryczny
|
||||
settings.tracker_issue_style.alphanumeric=Alfanumeryczne
|
||||
settings.tracker_url_format_desc=Symbole zastępcze <code>{user} {repo} {index}</code> mogą być użyte dla nazwy użytkownika, nazwy repozytorium i numeru problemu.
|
||||
settings.pulls_desc=Włącz obsługę pull request, aby akceptować publiczny wkład
|
||||
settings.danger_zone=Strefa niebezpieczeństwa
|
||||
settings.new_owner_has_same_repo=Nowy właściciel już posiada repozytorium o tej samej nazwie.
|
||||
settings.convert=Konwersja na repozytorium regularne
|
||||
settings.convert_desc=Możesz przekonwertować ten mirror na repozytorium regularne. Ta czynność nie może być odwrócona.
|
||||
settings.convert_notices_1=- Ta operacja przekonwertuje mirror tego repozytorium na repozytorium regularne. Ta czynność nie może być odwrócona.
|
||||
settings.convert_confirm=Potwierdź konwersję
|
||||
settings.convert_succeed=Typ repozytorium został zamieniony na regularne.
|
||||
settings.transfer=Przeniesienie własności
|
||||
settings.transfer_desc=Przenieś to repozytorium do innego użytkownika lub organizacji gdzie masz uprawnienia administratora.
|
||||
settings.new_owner_has_same_repo=Nowy właściciel już posiada repozytorium o tej samej nazwie.
|
||||
settings.delete=Usuń to repozytorium
|
||||
settings.delete_desc=Po usunięciu repozytorium nie ma odwrotu. Upewnij się, że tego chcesz.
|
||||
settings.transfer_notices_1=- Stracisz dostęp jeśli nowy właściciel jest indywidualnym użytkownikiem.
|
||||
settings.transfer_notices_2=- Zachowasz dostęp jeśli nowym właścicielem jest organizacja, której jesteś współwłaścicielem.
|
||||
settings.transfer_form_title=Proszę wpisz co następuje w celu potwierdzenia operacji:
|
||||
settings.wiki_delete=Kasowanie danych Wiki
|
||||
settings.wiki_delete_desc=Usunięcie danych z wiki jest nieodwracalne. Bądź ostrożny.
|
||||
settings.wiki_delete_notices_1=- To usunie i wyłączy wiki dla %s
|
||||
settings.wiki_deletion_success=Dane wiki zostały usunięte.
|
||||
settings.delete=Usuń to repozytorium
|
||||
settings.delete_desc=Po usunięciu repozytorium nie ma odwrotu. Upewnij się, że tego chcesz.
|
||||
settings.delete_notices_1=- Ta operacja <strong>NIE MOŻE</strong> zostać cofnięta.
|
||||
settings.delete_notices_2=- Ta operacja trwale usunie wszystko z tego repozytorium, w tym dane Git, problemy, komentarze i dostęp dla współpracowników.
|
||||
settings.delete_notices_fork_1=- Jeśli to repozytorium jest publiczne, wszystkie forki staną się niezależne.
|
||||
settings.delete_notices_fork_2=- Jeśli to repozytorium jest prywatne, forki zostaną usunięte wraz z usunięciem tego repozytorium.
|
||||
settings.delete_notices_fork_3=- Jeśli chcesz zachować wszystkie forki po usunięciu, proszę najpierw uczyń to repozytorium publicznym.
|
||||
settings.delete_notices_fork_1=Wszystkie forki staną się niezależne po usunięciu.
|
||||
settings.deletion_success=Repozytorium zostało pomyślnie usunięte!
|
||||
settings.update_settings_success=Opcje repozytorium zostały pomyślnie zaktualizowane.
|
||||
settings.transfer_owner=Nowy właściciel
|
||||
@@ -602,8 +694,12 @@ settings.transfer_succeed=Własność repozytorium została przeniesiona pomyśl
|
||||
settings.confirm_delete=Potwierdź usunięcie
|
||||
settings.add_collaborator=Dodaj nowego współpracownika
|
||||
settings.add_collaborator_success=Został dodany nowy współpracownik.
|
||||
settings.delete_collaborator=Usuń
|
||||
settings.collaborator_deletion=Usunięcie współpracownika
|
||||
settings.collaborator_deletion_desc=Ten użytkownik nie będzie miał dostępu współpracownika do repozytorium. Czy chcesz kontynuować?
|
||||
settings.remove_collaborator_success=Współpracownik został usunięty.
|
||||
settings.search_user_placeholder=Szukaj użytkownika...
|
||||
settings.org_not_allowed_to_be_collaborator=Organizacji nie można dodać jako współpracownika.
|
||||
settings.user_is_org_member=Użytkownik jest członkiem organizacji, który nie może być dodany jako współpracownik.
|
||||
settings.add_webhook=Dodaj webhooka
|
||||
settings.hooks_desc=Webooki działają tak jak proste wywołania HTTP POST. Jeśli cokolwiek zdarzy się w Gogs, wyślemy powiadomienie do wybranego hosta. Więcej informacji można znaleźć w <a target="_blank" href="%s">przewodniku webhooków</a>.
|
||||
@@ -636,6 +732,8 @@ settings.event_send_everything=Potrzebuję <strong>wszystkiego</strong>.
|
||||
settings.event_choose=Pozwól mi wybrać, czego potrzebuję.
|
||||
settings.event_create=Utwórz
|
||||
settings.event_create_desc=Utworzono gałąź lub tag
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Otworzono żądanie pull, zamknięto, otwarto ponownie, zaktualizowano, przypisano, nieprzypisano, zaktualizowano etykietę, wyczyszczono etykietę lub zsynchronizowano.
|
||||
settings.event_push=Wypchnięcie
|
||||
settings.event_push_desc=Wypchnięcie (push) do repozytorium Git
|
||||
settings.active=Aktywny
|
||||
@@ -668,11 +766,13 @@ diff.parent=rodzic
|
||||
diff.commit=commit
|
||||
diff.data_not_available=Informacje nt. zmiany nie są dostępne.
|
||||
diff.show_diff_stats=Pokaż statystyki zmian
|
||||
diff.show_split_view=Split View
|
||||
diff.show_unified_view=Unified View
|
||||
diff.show_split_view=Widok podzielony
|
||||
diff.show_unified_view=Zunifikowany widok
|
||||
diff.stats_desc=<strong>%d zmienionych plików</strong> z <strong>%d dodań</strong> i <strong>%d usunięć</strong>
|
||||
diff.bin=BIN
|
||||
diff.view_file=Wyświetl plik
|
||||
diff.file_suppressed=Plik diff jest za duży
|
||||
diff.too_many_files=Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików
|
||||
|
||||
release.releases=Wydania
|
||||
release.new_release=Nowe wydanie
|
||||
@@ -693,7 +793,7 @@ release.write=Napisz
|
||||
release.preview=Podgląd
|
||||
release.loading=Ładowanie...
|
||||
release.prerelease_desc=To jest wersja wstępna
|
||||
release.prerelease_helper=Chcemy zwrócić uwagę, że ta wersja jest oznaczona jako eksperymentalna.
|
||||
release.prerelease_helper=Oznacz to wydanie jako wersję wstępną.
|
||||
release.cancel=Anuluj
|
||||
release.publish=Publikuj wersję
|
||||
release.save_draft=Zapisz szkic
|
||||
@@ -703,6 +803,7 @@ release.deletion=Usuwanie wydania
|
||||
release.deletion_desc=Usunięcie tego wydania spowoduje usunięcie odpowiednich tagów Git. Czy chcesz kontynuować?
|
||||
release.deletion_success=Wydanie zostało pomyślnie usunięte!
|
||||
release.tag_name_already_exist=Wersja o tej nazwie tagu już istnieje.
|
||||
release.tag_name_invalid=Nazwa tagu jest niepoprawna.
|
||||
release.downloads=Pliki do pobrania
|
||||
|
||||
[org]
|
||||
@@ -724,8 +825,8 @@ team_name_helper=Będziesz używał tej nazwy do wywoływania tego zespołu w dy
|
||||
team_desc_helper=Czym zajmuje się ten zespół?
|
||||
team_permission_desc=Jaki poziom uprawnień powinien mieć ten zespół?
|
||||
|
||||
form.name_reserved=Nazwa organizacji "%s" jest zarezerwowana.
|
||||
form.name_pattern_not_allowed=Wzorzec nazwy organizacji "%s" jest niedozwolony.
|
||||
form.name_reserved=Nazwa organizacji „%s” jest zarezerwowana.
|
||||
form.name_pattern_not_allowed=Wzorzec nazwy organizacji „%s” jest niedozwolony.
|
||||
|
||||
settings=Ustawienia
|
||||
settings.options=Opcje
|
||||
@@ -818,6 +919,8 @@ dashboard.resync_all_sshkeys=Przeładuj klucze publiczne w pliku '.ssh/authorize
|
||||
dashboard.resync_all_sshkeys_success=Przeładowanie kluczy publicznych zakończyło się sukcesem.
|
||||
dashboard.resync_all_update_hooks=Przepisz pliki update hook repozytoriów (wymagane przy zmianie ścieżki do pliku konfiguracji)
|
||||
dashboard.resync_all_update_hooks_success=Wszystkie pliki update hook repozytoriów zostały pomyślnie przepisane.
|
||||
dashboard.reinit_missing_repos=Ponownie inicjalizuj wszystkie repozytoria, które straciły pliki Git
|
||||
dashboard.reinit_missing_repos_success=Wszystkie repozytoria, które straciły pliki Git, zostały ponownie zainicjować pomyślnie.
|
||||
|
||||
dashboard.server_uptime=Uptime serwera
|
||||
dashboard.current_goroutine=Bieżące Goroutines
|
||||
@@ -868,6 +971,7 @@ users.edit_account=Edytuj konto
|
||||
users.max_repo_creation=Maksymalna liczba repozytoriów
|
||||
users.max_repo_creation_desc=(Ustaw -1, aby użyć globalnego limitu)
|
||||
users.is_activated=To konto jest aktywne
|
||||
users.prohibit_login=Nie możesz się zalogować na to konto
|
||||
users.is_admin=To konto ma uprawnienia administratora
|
||||
users.allow_git_hook=To konto posiada uprawnienia do tworzenia hooków Git
|
||||
users.allow_import_local=To konto ma uprawnienia do importu lokalnych repozytoriów
|
||||
@@ -898,6 +1002,7 @@ auths.enabled=Włączono
|
||||
auths.updated=Zaktualizowano
|
||||
auths.auth_type=Typ uwierzytelniania
|
||||
auths.auth_name=Nazwa uwierzytelniania
|
||||
auths.security_protocol=Protokół zabezpieczeń
|
||||
auths.domain=Domena
|
||||
auths.host=Host
|
||||
auths.port=Port
|
||||
@@ -911,6 +1016,7 @@ auths.attribute_username_placeholder=Zostaw puste aby użyć wartości podanej p
|
||||
auths.attribute_name=Atrybut imienia
|
||||
auths.attribute_surname=Atrybut nazwiska
|
||||
auths.attribute_mail=Atrybut e-mail
|
||||
auths.attributes_in_bind=Pobierz atrybuty w kontekście Bind DN
|
||||
auths.filter=Filtr użytkownika
|
||||
auths.admin_filter=Filtr administratora
|
||||
auths.ms_ad_sa=Ms Ad SA
|
||||
@@ -932,7 +1038,9 @@ auths.update=Aktualizuj ustawienia uwierzytelniania
|
||||
auths.delete=Usuń to uwierzytelnienie
|
||||
auths.delete_auth_title=Usunięcie uwierzytelnienia
|
||||
auths.delete_auth_desc=To uwierzytelnienie zostanie usunięte, czy chcesz kontynuować?
|
||||
auths.still_in_used=Ten rodzaj autentykacji jest wciąż wykorzystywany przez niektórych użytkowników. Usuń lub przekonwertuj użytkowników, aby wykorzystywali inny typ logowania.
|
||||
auths.deletion_success=Uwierzytelnienie zostało usunięte pomyślnie!
|
||||
auths.login_source_exist=Login source '%s' already exists.
|
||||
|
||||
config.server_config=Konfiguracja serwera
|
||||
config.app_name=Nazwa aplikacji
|
||||
@@ -943,47 +1051,69 @@ config.offline_mode=Tryb offline
|
||||
config.disable_router_log=Wyłącz dziennik routera
|
||||
config.run_user=Użytkownik uruchomieniowy
|
||||
config.run_mode=Tryb uruchamienia
|
||||
config.git_version=Git Version
|
||||
config.repo_root_path=Ścieżka repozytoriów
|
||||
config.static_file_root_path=Ścieżka plików statycznych
|
||||
config.log_file_root_path=Ścieżka plików dziennika
|
||||
config.script_type=Typ skryptu
|
||||
config.reverse_auth_user=Użytkownik dostarczony przez odwrotne proxy
|
||||
|
||||
config.ssh_config=Konfiguracja SSH
|
||||
config.ssh_enabled=Aktywne
|
||||
config.ssh_start_builtin_server=Uruchom serwer wbudowany
|
||||
config.ssh_domain=Domena
|
||||
config.ssh_port=Port
|
||||
config.ssh_listen_port=Port nasłuchu
|
||||
config.ssh_root_path=Ścieżka katalogu głównego
|
||||
config.ssh_key_test_path=Ścieżka klucza testowego
|
||||
config.ssh_keygen_path=Ścieżka generatora ('ssh-keygen')
|
||||
config.ssh_minimum_key_size_check=Sprawdzanie minimalnej długości klucza
|
||||
config.ssh_minimum_key_sizes=Minimalne rozmiary kluczy
|
||||
|
||||
config.db_config=Konfiguracja bazy danych
|
||||
config.db_type=Typ
|
||||
config.db_host=Host
|
||||
config.db_name=Nazwa
|
||||
config.db_user=Użytkownik
|
||||
config.db_ssl_mode=Tryb SSL
|
||||
config.db_ssl_mode_helper=(tylko dla "postgres")
|
||||
config.db_ssl_mode_helper=(tylko dla „postgres”)
|
||||
config.db_path=Ścieżka
|
||||
config.db_path_helper=(dla "sqlite3" i "tidb")
|
||||
config.db_path_helper=(dla „sqlite3” i „tidb”)
|
||||
|
||||
config.service_config=Konfiguracja usługi
|
||||
config.register_email_confirm=Wymagaj potwierdzenia e-mail
|
||||
config.disable_register=Wyłącz rejestrację
|
||||
config.show_registration_button=Pokazuj przycisk rejestracji
|
||||
config.require_sign_in_view=Wymagaj bycia zalogowanym
|
||||
config.enable_cache_avatar=Włącz cache awatarów
|
||||
config.mail_notify=Powiadomienia e-mail
|
||||
config.disable_key_size_check=Wyłącz sprawdzanie minimalnego rozmiaru klucza
|
||||
config.enable_captcha=Włącz Captcha
|
||||
config.active_code_lives=Ważność kodów aktywacyjnych
|
||||
config.reset_password_code_lives=Czas życia kodu resetowania hasła
|
||||
|
||||
config.webhook_config=Konfiguracja webhooka
|
||||
config.queue_length=Długość kolejki
|
||||
config.deliver_timeout=Limit czasu zdarzenia
|
||||
config.skip_tls_verify=Pomiń weryfikację protokołu TLS
|
||||
|
||||
config.mailer_config=Konfiguracja poczty
|
||||
config.mailer_enabled=Aktywne
|
||||
config.mailer_disable_helo=Wyłącz HELO
|
||||
config.mailer_name=Nazwa
|
||||
config.mailer_host=Host
|
||||
config.mailer_user=Użytkownik
|
||||
config.send_test_mail=Wyślij email testowy
|
||||
config.test_mail_failed=Nieudane wysłanie wiadomości email do '%s': %v
|
||||
config.test_mail_sent=Testowa wiadomość email została wysłana do '%s'.
|
||||
|
||||
config.oauth_config=Konfiguracja OAuth
|
||||
config.oauth_enabled=Aktywne
|
||||
|
||||
config.cache_config=Konfiguracja cache
|
||||
config.cache_adapter=Adapter cache
|
||||
config.cache_interval=Interwał pamięci podręcznej
|
||||
config.cache_conn=Połączenie z pamięcią podręczną
|
||||
|
||||
config.session_config=Konfiguracja sesji
|
||||
config.session_provider=Dostawca sesji
|
||||
config.provider_config=Konfiguracja dostawcy
|
||||
@@ -993,9 +1123,24 @@ config.gc_interval_time=Interwał odśmiecania
|
||||
config.session_life_time=Czas życia sesji
|
||||
config.https_only=Tylko HTTPS
|
||||
config.cookie_life_time=Czas życia ciasteczka
|
||||
|
||||
config.picture_config=Ustawienia obrazów
|
||||
config.picture_service=Serwis obrazów
|
||||
config.disable_gravatar=Wyłącz Gravatara
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Konfiguracja Git
|
||||
config.git_disable_diff_highlight=Wyłączyć wyróżnianie składni diff
|
||||
config.git_max_diff_lines=Maksymalna ilość linii diff (dla pojedynczego pliku)
|
||||
config.git_max_diff_line_characters=Maksymalna ilość znaków diff (dla pojedynczego pliku)
|
||||
config.git_max_diff_files=Maksymalna ilość plików diff (które zostaną wyświetlone)
|
||||
config.git_gc_args=Argumenty GC
|
||||
config.git_migrate_timeout=Limit czasu migracji
|
||||
config.git_mirror_timeout=Limit czasu aktualizacji kopii lustrzanej
|
||||
config.git_clone_timeout=Limit czasu operacji klonowania
|
||||
config.git_pull_timeout=Limit czasu dla operacji pull
|
||||
config.git_gc_timeout=Limit czasu odśmiecania pamięci
|
||||
|
||||
config.log_config=Konfiguracja dziennika
|
||||
config.log_mode=Tryb dziennika
|
||||
|
||||
@@ -1029,7 +1174,11 @@ create_repo=tworzy repozytorium <a href="%s">%s</a>
|
||||
rename_repo=zmienia nazwę repozytorium <code>%[1]s</code> na <a href="%[2]s">%[3]s</a>
|
||||
commit_repo=wypycha do <a href="%[1]s/src/%[2]s">%[3]s</a> w <a href="%[1]s">%[4]s</a>
|
||||
create_issue=`zgłasza problem <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
close_issue=`zamknięcie problemu <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
reopen_issue=`ponowne otwarcie problemu <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
create_pull_request=`tworzy pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
close_pull_request=`zamknięcie pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
reopen_pull_request=`ponowne otwarcie pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
comment_issue=`komentuje problem <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
merge_pull_request=`scala pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
transfer_repo=przenosi repozytorium <code>%s</code> do <a href="%s">%s</a>
|
||||
|
||||
827
conf/locale/locale_pt-BR.ini
Executable file → Normal file
827
conf/locale/locale_pt-BR.ini
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
297
conf/locale/locale_ru-RU.ini
Executable file → Normal file
297
conf/locale/locale_ru-RU.ini
Executable file → Normal file
@@ -1,10 +1,10 @@
|
||||
app_desc=Удобная служба для собственного Git-репозитория, написанная на языке Go
|
||||
app_desc=Удобная служба для собственного Git-репозитория
|
||||
|
||||
home=Главная
|
||||
dashboard=Панель мониторинга
|
||||
dashboard=Панель управления
|
||||
explore=Обзор
|
||||
help=Помощь
|
||||
sign_in=Войти
|
||||
sign_in=Вход
|
||||
sign_out=Выход
|
||||
sign_up=Регистрация
|
||||
register=Регистрация
|
||||
@@ -14,43 +14,36 @@ page=Страница
|
||||
template=Шаблон
|
||||
language=Язык
|
||||
create_new=Создать...
|
||||
user_profile_and_more=Профиль и остальное
|
||||
user_profile_and_more=Профиль пользователя и прочее
|
||||
signed_in_as=Вы вошли как
|
||||
|
||||
username=Имя пользователя
|
||||
email=Эл. почта
|
||||
password=Пароль
|
||||
re_type=Повтор
|
||||
captcha=Captcha
|
||||
re_type=Введите повторно
|
||||
captcha=Капча
|
||||
|
||||
repository=Репозиторий
|
||||
organization=Организация
|
||||
mirror=Зеркало
|
||||
new_repo=Новый репозиторий
|
||||
new_migrate=Новая Миграция
|
||||
new_migrate=Новая миграция
|
||||
new_mirror=Новое зеркало
|
||||
new_fork=Новый проект из репозитория
|
||||
new_fork=Новое ответвление репозитория
|
||||
new_org=Новая организация
|
||||
manage_org=Управление организацией
|
||||
manage_org=Управление организациями
|
||||
admin_panel=Панель администратора
|
||||
account_settings=Настройки аккаунта
|
||||
settings=Настройки
|
||||
your_profile=Ваш профиль
|
||||
your_settings=Ваши настройки
|
||||
|
||||
news_feed=Лента новостей
|
||||
activities=Активность
|
||||
pull_requests=Запросы на слияние
|
||||
issues=Задачи
|
||||
|
||||
cancel=Отмена
|
||||
|
||||
[search]
|
||||
search=Поиск...
|
||||
repository=Репозиторий
|
||||
user=Пользователь
|
||||
issue=Задача
|
||||
code=Код
|
||||
|
||||
[install]
|
||||
install=Установка
|
||||
title=Установочные шаги для первого запуска
|
||||
@@ -65,7 +58,7 @@ db_name=Имя базы данных
|
||||
db_helper=Для MySQL используйте тип таблиц InnoDB с кодировкой utf8_general_ci.
|
||||
ssl_mode=Режим SSL
|
||||
path=Путь
|
||||
sqlite_helper=Путь к файлу базы данных SQLite3 или TiDB.
|
||||
sqlite_helper=Путь к файлу базы данных SQLite3 или TiDB. <br>Укажите абсолютный путь при запуске в качестве службы.
|
||||
err_empty_db_path=Путь к базе данных SQLite3 или TiDB не может быть пустым.
|
||||
err_invalid_tidb_name=Имя базы данных TiDB не может содержать символы "." и "-".
|
||||
no_admin_and_disable_registration=Вы не можете отключить регистрацию до создания учетной записи администратора.
|
||||
@@ -86,6 +79,8 @@ http_port=Порт HTTP
|
||||
http_port_helper=Номер порта, который приложение будет слушать.
|
||||
app_url=URL приложения
|
||||
app_url_helper=Этот параметр влияет на URL для клонирования по HTTP/HTTPS и на адреса в электронной почте.
|
||||
log_root_path=Путь к журналу
|
||||
log_root_path_helper=Каталог для записи файлов журнала.
|
||||
|
||||
optional_title=Расширенные настройки
|
||||
email_title=Настройки службы электронной почты
|
||||
@@ -101,6 +96,8 @@ offline_mode=Включение офлайн режима
|
||||
offline_mode_popup=Отключить CDN даже в производственном режиме, все файлы ресурсов будут раздаваться локально.
|
||||
disable_gravatar=Отключить службу Gravatar
|
||||
disable_gravatar_popup=Отключить Gravatar и пользовательские источники, все аватары по-умолчанию загружаются пользователями.
|
||||
federated_avatar_lookup=Включить поиск внешних Аватаров
|
||||
federated_avatar_lookup_popup=Включите Поиск федеративного аватара для использования федеративной службы с открытым исходным кодом на основе libravatar.
|
||||
disable_registration=Отключить самостоятельную регистрацию
|
||||
disable_registration_popup=Запретить пользователям самостоятельную регистрацию, только администратор может создавать аккаунты.
|
||||
enable_captcha=Включить капчу
|
||||
@@ -119,17 +116,20 @@ sqlite3_not_available=Ваша версия не поддерживает SQLite
|
||||
invalid_db_setting=Настройки базы данных не правильные: %v
|
||||
invalid_repo_path=Недопустимый путь к корню репозитория: %v
|
||||
run_user_not_match=Текущий пользователь не является пользователем для запуска: %s -> %s
|
||||
invalid_smtp_from=SMTP From field is not valid: %v
|
||||
save_config_failed=Не удалось сохранить конфигурацию: %v
|
||||
invalid_admin_setting=Указан недопустимый параметр учетной записи администратора: %v
|
||||
install_success=Добро пожаловать! Мы рады, что вы выбрали Gogs. Веселитесь и берегите себя.
|
||||
invalid_log_root_path=Недопустимый путь для логов: %v
|
||||
|
||||
[home]
|
||||
uname_holder=Имя пользователь или E-mail
|
||||
password_holder=Пароль
|
||||
switch_dashboard_context=Переключить контекст панели управления
|
||||
my_repos=Мои репозитории
|
||||
show_more_repos=Показать больше репозиториев...
|
||||
collaborative_repos=Совместные репозитории
|
||||
my_orgs=Моя Организация
|
||||
my_orgs=Мои организации
|
||||
my_mirrors=Мои зеркала
|
||||
view_home=Показать %s
|
||||
|
||||
@@ -137,6 +137,9 @@ issues.in_your_repos=В ваших репозиториях
|
||||
|
||||
[explore]
|
||||
repos=Репозитории
|
||||
users=Пользователи
|
||||
organizations=Организации
|
||||
search=Поиск
|
||||
|
||||
[auth]
|
||||
create_new_account=Создать новый аккаунт
|
||||
@@ -150,15 +153,17 @@ forget_password=Забыли пароль?
|
||||
sign_up_now=Нужен аккаунт? Зарегистрируйтесь.
|
||||
confirmation_mail_sent_prompt=Новое письмо для подтверждения было направлено на <b>%s</b>, пожалуйста, проверьте ваш почтовый ящик в течение %d часов для завершения регистрации.
|
||||
active_your_account=Активируйте свой аккаунт
|
||||
resent_limit_prompt=Вы слишком часто отправляете письмо с активацией. Подождите 3 минуты, пожалуйста.
|
||||
prohibit_login=Вход запрещен
|
||||
prohibit_login_desc=Вход для вашей учетной записи был запрещен, пожалуйста, свяжитесь с администратором сайта.
|
||||
resent_limit_prompt=Извините, вы уже запросили активацию по электронной почте недавно. Пожалуйста, подождите 3 минуты, а затем повторите попытку.
|
||||
has_unconfirmed_mail=Здравствуйте, %s! У вас есть неподтвержденный адрес электронной почты (<b>%s</b>). Если вам не приходило письмо с подтверждением или нужно выслать новое письмо, нажмите на кнопку ниже.
|
||||
resend_mail=Нажмите здесь, чтобы переотправить активационное письмо
|
||||
email_not_associate=Этот адрес электронной почты не связан ни с одной учетной записью.
|
||||
send_reset_mail=Нажмите сюда, чтобы отправить письмо для сброса пароля
|
||||
reset_password=Сброс пароля
|
||||
invalid_code=Извините, ваш код подтверждения истек или не является допустимым.
|
||||
reset_password_helper=Нажмите здесь, чтобы сбросить свой пароль
|
||||
password_too_short=Длина пароля не менее 6 символов.
|
||||
non_local_account=Нелокальные аккаунты не могут изменить пароль через Gogs.
|
||||
|
||||
[mail]
|
||||
activate_account=Пожалуйста активируйте свой аккаунт
|
||||
@@ -185,6 +190,13 @@ TeamName=Название команды
|
||||
AuthName=Имя авторизации
|
||||
AdminEmail=Электронная почта администратора
|
||||
|
||||
NewBranchName=Новая ветка
|
||||
CommitSummary=Резюме коммита
|
||||
CommitMessage=Зафиксировать сообщение
|
||||
CommitChoice=Выбор коммита
|
||||
TreeName=Путь к файлу
|
||||
Content=Содержимое
|
||||
|
||||
require_error=` не может быть пустым.`
|
||||
alpha_dash_error=«должен быть допустимым символьным, числовым или dash(-_) значением.»
|
||||
alpha_dash_dot_error=«должен быть допустимым символьным, числовым или dash(-_) символами, включая точку.»
|
||||
@@ -192,24 +204,23 @@ size_error=` должен быть размер %s.`
|
||||
min_size_error=«должен содержать по крайней мере %s символов.»
|
||||
max_size_error=` должен содержать максимум %s символов.`
|
||||
email_error=«не является адресом электронной почты.»
|
||||
url_error=«не является допустимым URL-адресом.»
|
||||
include_error=` должен содержать '%s'`
|
||||
url_error=` не является допустимым URL-адресом.`
|
||||
include_error=` должен содержать '%s'.`
|
||||
unknown_error=Неизвестная ошибка:
|
||||
captcha_incorrect=CAPTCHA не совпадает.
|
||||
password_not_match=Пароль и подтверждение пароля не совпадают.
|
||||
captcha_incorrect=Капча не пройдена.
|
||||
password_not_match=Пароли не совпадают.
|
||||
|
||||
username_been_taken=Имя пользователя уже принято.
|
||||
repo_name_been_taken=Имя репозитория уже принято.
|
||||
org_name_been_taken=Название организации было уже принято.
|
||||
team_name_been_taken=Название команды было уже принято.
|
||||
username_been_taken=Имя пользователя занято.
|
||||
repo_name_been_taken=Имя репозитория занято.
|
||||
org_name_been_taken=Название организации занято.
|
||||
team_name_been_taken=Название команды занято.
|
||||
email_been_used=Адрес электронной почты уже используется.
|
||||
illegal_team_name=Имя группы содержит недопустимые знаки.
|
||||
username_password_incorrect=Имя пользователя или пароль не правильный.
|
||||
enterred_invalid_repo_name=Пожалуйста, убедитесь, что введенно правильное имя хранилища.
|
||||
enterred_invalid_repo_name=Пожалуйста, убедитесь, что введено правильное имя репозитория.
|
||||
enterred_invalid_owner_name=Убедитесь, что введенное имя владельца верное.
|
||||
enterred_invalid_password=Убедитесь, что введенный пароль верен.
|
||||
user_not_exist=Данный пользователь не существует.
|
||||
last_org_owner=Удаляемый пользователь является последним в команде владельцев. Должен быть хотя бы один владелец.
|
||||
last_org_owner=Удаление последнего пользователя из команды владельцев невозможно, поскольку всегда должен быть хотя бы один владелец в любой организации.
|
||||
|
||||
invalid_ssh_key=К сожалению, мы не смогли проверить ваш SSH-ключ: %s
|
||||
unable_verify_ssh_key=Gogs не может проверить ваш SSH-ключ, но мы допускаем, что он действителен. Пожалуйста, удостоверьтесь самостоятельно, что ключ действителен.
|
||||
@@ -219,13 +230,10 @@ still_own_repo=На вашем аккаунте все еще остается
|
||||
still_has_org=Вы находитесь в организации, сперва Вам необходимо покинуть ее или удалить.
|
||||
org_still_own_repo=Данная организация все еще является владельцем репозиториев, необходимо удалить или переместить их в начале.
|
||||
|
||||
still_own_user=Эта проверка подлинности по-прежнему используется некоторыми пользователями, вы должны переместить их и затем снова удалить.
|
||||
|
||||
target_branch_not_exist=Целевая ветка не существует
|
||||
|
||||
[user]
|
||||
change_avatar=Измените ваш аватар на gravatar.com
|
||||
change_custom_avatar=Измените ваш аватар в настройках
|
||||
change_avatar=Изменить аватар
|
||||
join_on=Присоединился
|
||||
repositories=Репозитории
|
||||
activity=Активность
|
||||
@@ -241,6 +249,7 @@ form.name_pattern_not_allowed=Имя пользователя «%s» не доп
|
||||
[settings]
|
||||
profile=Профиль
|
||||
password=Пароль
|
||||
avatar=Аватар
|
||||
ssh_keys=SSH ключи
|
||||
social=Учетные записи в соцсетях
|
||||
applications=Приложения
|
||||
@@ -261,12 +270,13 @@ change_username_prompt=Это изменение может повлечь за
|
||||
continue=Далее
|
||||
cancel=Отмена
|
||||
|
||||
lookup_avatar_by_mail=Найти Аватар по адресу эл. почты
|
||||
federated_avatar_lookup=Найти внешний Аватар
|
||||
enable_custom_avatar=Включить собственный аватар
|
||||
enable_custom_avatar_helper=Включите эту опцию, чтоб отключить загрузку с Gravatar
|
||||
choose_new_avatar=Выбрать новый аватар
|
||||
update_avatar=Обновить настройку аватара
|
||||
delete_current_avatar=Удалить текущий аватар
|
||||
uploaded_avatar_not_a_image=Загружаемый файл не является изображением.
|
||||
no_custom_avatar_available=Собственный аватар недоступен, включить его невозможно.
|
||||
update_avatar_success=Настройка вашего аватара обновлена успешно.
|
||||
|
||||
change_password=Сменить пароль
|
||||
@@ -324,10 +334,14 @@ token_name=Имя маркера
|
||||
generate_token=Генерировать маркер
|
||||
generate_token_succees=Успешно создан новый токен доступа! Пожалуйста сделайте копию вашего нового токена персонального доступа. Вы не сможете увидеть его снова!
|
||||
delete_token=Удалить
|
||||
access_token_deletion=Удаление персонального токена доступа
|
||||
access_token_deletion=Удаление личного токена доступа
|
||||
access_token_deletion_desc=Удаление этого персонального токена доступа приведет к удалению всех связанных прав доступа к приложению. Вы хотите продолжить?
|
||||
delete_token_success=Персональный токен доступа успешно удален! Не забудьте изменить настройки вашего приложения.
|
||||
|
||||
orgs.none=Вы не состоите ни в одной организации.
|
||||
orgs.leave_title=Покинуть организацию
|
||||
orgs.leave_desc=Вы потеряете доступ ко всем репозиториям и командам, как только покинете организацию. Вы хотите продолжить?
|
||||
|
||||
delete_account=Удалить свой аккаунт
|
||||
delete_prompt=Этим действием вы удалите свою учетную запись навсегда и <strong>НЕ СМОЖЕТЕ</strong> ее вернуть!
|
||||
confirm_delete_account=Подтвердите удаление
|
||||
@@ -341,14 +355,14 @@ repo_name_helper=Лучшие названия репозиториев коро
|
||||
visibility=Видимость
|
||||
visiblity_helper=<span class="ui red text">Личный</span> репозиторий
|
||||
visiblity_helper_forced=Все новые репозитории являются <span class="ui red text">Личными</span> по желанию администратора сайта
|
||||
visiblity_fork_helper=(Изменение этого значения затронет все форки)
|
||||
visiblity_fork_helper=(Изменение этого значения затронет все ответвления)
|
||||
clone_helper=Нужна помощь в клонировании? Посетите страницу <a target="_blank" href="%s">помощи</a>!
|
||||
fork_repo=Ответвить репозиторий
|
||||
fork_from=Ответвление от
|
||||
fork_visiblity_helper=Ответвленному репозиторию нельзя поменять уровень видимости
|
||||
repo_desc=Описание
|
||||
repo_lang=Язык
|
||||
repo_lang_helper=Выберите файлы .gitignore
|
||||
repo_gitignore_helper=Выберите шаблоны .gitignore
|
||||
license=Лицензия
|
||||
license_helper=Выберите файл лицензии
|
||||
readme=Readme
|
||||
@@ -356,12 +370,15 @@ readme_helper=Выберите шаблон для файла readme
|
||||
auto_init=Инициализировать этот репозиторий выбранными файлами и шаблоном
|
||||
create_repo=Создать репозиторий
|
||||
default_branch=Ветка по умолчанию
|
||||
mirror_prune=Очистить
|
||||
mirror_prune_desc=Удалите ссылки на удаленно отслеживаемые объекты, которых больше нет на удаленном сервере
|
||||
mirror_interval=Интервал зеркалирования (час)
|
||||
mirror_address=Адрес зеркала
|
||||
mirror_address_desc=Укажите необходимые учетные данные в адрес.
|
||||
mirror_last_synced=Последняя синхронизация
|
||||
watchers=Наблюдатели
|
||||
stargazers=Звездочеты
|
||||
forks=Форки
|
||||
forks=Ответвления
|
||||
|
||||
form.reach_limit_of_creation=У владельца достигнут максимальный предел в %d создаваемых репозиториев.
|
||||
form.name_reserved=Имя репозитория '%s' зарезервировано.
|
||||
@@ -372,14 +389,15 @@ migrate_type=Тип миграции
|
||||
migrate_type_helper=Этот репозиторий будет <span class="text blue">зеркалом</span>
|
||||
migrate_repo=Перенос репозитория
|
||||
migrate.clone_address=Скопировать адрес
|
||||
migrate.clone_address_desc=Это может быть HTTP/HTTPS/GIT адрес или локальный путь на сервере.
|
||||
migrate.clone_address_desc=Это может быть HTTP/HTTPS/GIT URL-адрес.
|
||||
migrate.clone_address_desc_import_local=You're also allowed to migrate a repository by local server path.
|
||||
migrate.permission_denied=У вас нет прав на импорт локальных репозиториев.
|
||||
migrate.invalid_local_path=Недопустимый локальный путь. Возможно он не существует или является не папкой.
|
||||
migrate.failed=Миграция не удалась: %v
|
||||
|
||||
mirror_from=зеркало из
|
||||
forked_from=форк от
|
||||
fork_from_self=Вы не можете форкнуть репозитарий, так как Вы уже его владелец!
|
||||
forked_from=ответвлено от
|
||||
fork_from_self=Вы не можете ответвить репозиторий, так как Вы уже его владелец!
|
||||
copy_link=Скопировать
|
||||
copy_link_success=Скопировано!
|
||||
copy_link_error=Нажмите ⌘-C или Ctrl-C для копирования
|
||||
@@ -397,7 +415,7 @@ create_new_repo_command=Создать новый репозиторий из к
|
||||
push_exist_repo=Отправить существующий репозиторий из командной строки
|
||||
repo_is_empty=Этот репозиторий пуст, пожалуйста, возвращайтесь позже!
|
||||
|
||||
code=Код
|
||||
files=Файлы
|
||||
branch=Ветка
|
||||
tree=Дерево
|
||||
filter_branch_and_tag=Фильтр по ветке или тегу
|
||||
@@ -413,6 +431,47 @@ file_raw=Исходник
|
||||
file_history=История
|
||||
file_view_raw=Посмотреть исходник
|
||||
file_permalink=Постоянная ссылка
|
||||
file_too_large=Этот файл слишком большой, поэтому он не может быть отображен
|
||||
video_not_supported_in_browser=Ваш браузер не поддерживает HTML5 видео тэг.
|
||||
|
||||
editor.new_file=Новый файл
|
||||
editor.upload_file=Загрузить файл
|
||||
editor.edit_file=Редактировать файл
|
||||
editor.preview_changes=Просмотр изменений
|
||||
editor.cannot_edit_non_text_files=Возможно редактировать только текстовые файлы
|
||||
editor.edit_this_file=Отредактируйте этот файл
|
||||
editor.must_be_on_a_branch=Чтобы сделать или предложить изменения вы должны выбрать ветку
|
||||
editor.fork_before_edit=Создайте ветку репозитория перед редактированием файла
|
||||
editor.delete_this_file=Удалить файл
|
||||
editor.must_have_write_access=Вам необходимо иметь доступ на запись, чтобы вносить или предлагать правки этого файла
|
||||
editor.file_delete_success=Файл «%s» был успешно удален!
|
||||
editor.name_your_file=Назовите свой файл...
|
||||
editor.filename_help=Чтобы добавить каталог, просто наберите название и нажмите /. Чтобы удалить каталог, перейдите к началу поля и нажмите клавишу backspace.
|
||||
editor.or=или
|
||||
editor.cancel_lower=отмена
|
||||
editor.commit_changes=Фиксация изменений
|
||||
editor.add_tmpl=Добавить '%s/<filename>'
|
||||
editor.add=Добавить '%s'
|
||||
editor.update=Обновить '%s'
|
||||
editor.delete=Удалить '%s'
|
||||
editor.commit_message_desc=Добавьте необязательное расширенное описание...
|
||||
editor.commit_directly_to_this_branch=Сделайте коммит прямо в ветку <strong class="branch-name">%s</strong>.
|
||||
editor.create_new_branch=Создайте <strong>новую ветвь</strong> для этого коммита, и сделайте пул запрос.
|
||||
editor.new_branch_name_desc=Новое название ветки...
|
||||
editor.cancel=Отмена
|
||||
editor.filename_cannot_be_empty=Имя файла не может быть пустым.
|
||||
editor.branch_already_exists=Ветка «%s» уже существует в этом репозитории.
|
||||
editor.directory_is_a_file=Запись «%s» в пути на верх является файлом, а не каталогом этого репозитория.
|
||||
editor.file_is_a_symlink=Файл '%s' является символической ссылкой, которая не может быть изменена через веб-редактор.
|
||||
editor.filename_is_a_directory=Файл «%s» является каталогом в этом репозитории.
|
||||
editor.file_editing_no_longer_exists=Редактируемый вами файл «%s» больше не существует в репозитории.
|
||||
editor.file_changed_while_editing=Содержимое файла изменилось со времени начала редактирования. <a target="_blank" href="%s"> нажмите здесь,</a> чтобы увидеть, что было изменено, или <strong>нажмите кнопку commit снова</strong>, чтобы перезаписать эти изменения.
|
||||
editor.file_already_exists=Файл с именем «%s» уже существует в этом репозитории.
|
||||
editor.no_changes_to_show=Нет изменений.
|
||||
editor.fail_to_update_file=Не удалось обновить/создать файл «%s» из-за ошибки: %v
|
||||
editor.add_subdir=Добавьте подкаталог...
|
||||
editor.unable_to_upload_files=Не удалось загрузить файлы в «%s» из-за ошибки: %v
|
||||
editor.upload_files_to_dir=Загрузить файлы '%s'
|
||||
|
||||
commits.commits=Коммиты
|
||||
commits.search=Поиск коммитов
|
||||
@@ -439,6 +498,11 @@ issues.create=Добавить задачу
|
||||
issues.new_label=Новая метка
|
||||
issues.new_label_placeholder=Имя метки...
|
||||
issues.create_label=Добавить метку
|
||||
issues.label_templates.title=Загрузить набор предопределённых меток
|
||||
issues.label_templates.info=Меток пока нет. Вы можете нажать на кнопку «Создать метку», чтобы создать новую или использовать одну из готового набора ниже.
|
||||
issues.label_templates.helper=Выберите метку
|
||||
issues.label_templates.use=Использовать ярлык
|
||||
issues.label_templates.fail_to_load_file=Не удалось загрузить файл шаблона метки «%s»: %v
|
||||
issues.open_tab=%d открыто(ы)
|
||||
issues.close_tab=%d закрыто(ы)
|
||||
issues.filter_label=Метка
|
||||
@@ -466,7 +530,8 @@ issues.next=Следующая страница
|
||||
issues.open_title=Открыто
|
||||
issues.closed_title=Закрыто
|
||||
issues.num_comments=комментариев: %d
|
||||
issues.commented_at=` прокомментировал <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commented_at=«прокомментировал <a href="#%s"> %s</a>»
|
||||
issues.delete_comment_confirm=Вы уверены, что хотите удалить этот комментарий?
|
||||
issues.no_content=Пока нет содержимого.
|
||||
issues.close_issue=Закрыть
|
||||
issues.close_comment_issue=Прокомментировать и закрыть
|
||||
@@ -477,10 +542,9 @@ issues.closed_at=`закрыл <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at=`открыл снова <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commit_ref_at=`упомянул эту задачу в коммите <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.poster=Автор
|
||||
issues.admin=Администратор
|
||||
issues.collaborator=Соавтор
|
||||
issues.owner=Владелец
|
||||
issues.sign_up_for_free=Зарегистрируйтесь бесплатно
|
||||
issues.sign_in_require_desc=чтобы присоединиться к обсуждению. Уже есть аккаунт? <a href="%s">Войдите чтобы прокомментировать</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Войдите</a>, чтобы присоединиться к обсуждению.
|
||||
issues.edit=Изменить
|
||||
issues.cancel=Отмена
|
||||
issues.save=Сохранить
|
||||
@@ -494,6 +558,9 @@ issues.label_modify=Изменение метки
|
||||
issues.label_deletion=Удаление метки
|
||||
issues.label_deletion_desc=Удаление ярлыка затронет все связанные задачи. Продолжить?
|
||||
issues.label_deletion_success=Метка была удалена успешно!
|
||||
issues.num_participants=%d участников
|
||||
issues.attachment.open_tab=`Нажмите, чтобы увидеть "%s" в новой вкладке`
|
||||
issues.attachment.download=`Нажмите, чтобы скачать "%s"`
|
||||
|
||||
pulls.new=Новый запрос на слияние
|
||||
pulls.compare_changes=Сравнить изменения
|
||||
@@ -503,7 +570,7 @@ pulls.compare_compare=сравнить
|
||||
pulls.filter_branch=Фильтр по ветке
|
||||
pulls.no_results=Результатов не найдено.
|
||||
pulls.nothing_to_compare=Нечего сравнивать, родительская и текущая ветка одинаковые.
|
||||
pulls.has_pull_request=`Уже существует пулл-реквест между двумя целями <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
|
||||
pulls.has_pull_request=`Уже существует запрос на слияние между двумя целями: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
|
||||
pulls.create=Создать запрос на слияние
|
||||
pulls.title_desc=хочет смерджить %[1]d коммит(ов) из <code>%[2]s</code> в <code>%[3]s</code>
|
||||
pulls.merged_title_desc=слито %[1]d коммит(ов) из <code>%[2]s</code> в <code>%[3]s</code> %[4]s
|
||||
@@ -513,13 +580,15 @@ pulls.tab_files=Измененные файлы
|
||||
pulls.reopen_to_merge=Пожалуйста снова откройте этот запрос для слияния.
|
||||
pulls.merged=Слито
|
||||
pulls.has_merged=Слияние этого запроса успешно завершено!
|
||||
pulls.data_broken=Содержимое этого пулл-реквеста было нарушено, вследствии удаления или клонирования информации.
|
||||
pulls.data_broken=Содержимое этого запроса было нарушено вследствие удаления информации ответвления.
|
||||
pulls.is_checking=Продолжается проверка конфликтов, пожалуйста обновите страницу несколько позже.
|
||||
pulls.can_auto_merge_desc=Этот запрос на слияние может быть объединён автоматически.
|
||||
pulls.cannot_auto_merge_desc=Этот запрос на слияние не может быть объединён автоматически.
|
||||
pulls.cannot_auto_merge_helper=Пожалуйста, совершите слияние вручную для урегулирования конфликтов.
|
||||
pulls.merge_pull_request=Слить пулл-реквест
|
||||
pulls.open_unmerged_pull_exists=`Вы не можете снова открыть, поскольку уже существует запрос на слияние (#%d) из того же репозитория с той же информацией о слиянии и ожидающий слияния. `
|
||||
pulls.delete_branch=Удалить ветку
|
||||
pulls.delete_branch_has_new_commits=Нельзя удалить ветку, так как она имеет новые коммиты после слияния.
|
||||
|
||||
milestones.new=Новая контрольная точка
|
||||
milestones.open_tab=%d открыты
|
||||
@@ -547,7 +616,7 @@ milestones.deletion_success=Контрольная точка успешно у
|
||||
|
||||
wiki=Вики
|
||||
wiki.welcome=Добро пожаловать в Вики!
|
||||
wiki.welcome_desc=Вики это место, где вы хотели бы документировать проект вместе и сделать его лучше.
|
||||
wiki.welcome_desc=Вики это место, где вы можете документировать проект вместе и сделать его лучше.
|
||||
wiki.create_first_page=Создать первую страницу
|
||||
wiki.page=Страница
|
||||
wiki.filter_page=Фильтр страницы
|
||||
@@ -557,6 +626,8 @@ wiki.save_page=Сохранить страницу
|
||||
wiki.last_commit_info=%s редактировал эту страницу %s
|
||||
wiki.edit_page_button=Редактировать
|
||||
wiki.new_page_button=Новая страница
|
||||
wiki.delete_page_button=Удалить страницу
|
||||
wiki.delete_page_notice_1=Будьте внимательны! Это приведет к удалению страницы <code>«%s»</code>.
|
||||
wiki.page_already_exists=Вики-страница с таким именем уже существует.
|
||||
wiki.pages=Страницы
|
||||
wiki.last_updated=Последнее обновление %s
|
||||
@@ -564,46 +635,71 @@ wiki.last_updated=Последнее обновление %s
|
||||
settings=Настройки
|
||||
settings.options=Опции
|
||||
settings.collaboration=Сотрудничество
|
||||
settings.collaboration.admin=Администратор
|
||||
settings.collaboration.write=Запись
|
||||
settings.collaboration.read=Просмотр
|
||||
settings.collaboration.undefined=Не определено
|
||||
settings.hooks=Автоматическое обновление
|
||||
settings.githooks=Git хуки
|
||||
settings.basic_settings=Основные параметры
|
||||
settings.mirror_settings=Настройки Зеркала
|
||||
settings.sync_mirror=Синхронизировать
|
||||
settings.mirror_sync_in_progress=Выполняется синхронизация Зеркала, пожалуйста, обновите эту страницу через минуту.
|
||||
settings.site=Официальный сайт
|
||||
settings.update_settings=Обновить настройки
|
||||
settings.change_reponame_prompt=Это изменение повлияет на отношения ссылок к этому репозиторию.
|
||||
settings.advanced_settings=Расширенные настройки
|
||||
settings.wiki_desc=Включить Вики, чтобы позволить людям писать документы
|
||||
settings.wiki_desc=Включить систему Wiki
|
||||
settings.use_internal_wiki=Использовать встроенную wiki
|
||||
settings.use_external_wiki=Использовать внешнюю Wiki
|
||||
settings.external_wiki_url=URL-адрес внешней вики
|
||||
settings.external_wiki_url=URL-адрес внешней Вики
|
||||
settings.external_wiki_url_desc=Посетители будут перенаправлены на URL-адрес, когда они кликнут по вкладке.
|
||||
settings.issues_desc=Включить встроенную, легковесную систему отслеживания ошибок
|
||||
settings.issues_desc=Включить систему отслеживания ошибок
|
||||
settings.use_internal_issue_tracker=Использовать встроенную легковесную систему отслеживания ошибок
|
||||
settings.use_external_issue_tracker=Использовать внешнюю систему отслеживания ошибок
|
||||
settings.external_tracker_url=External Issue Tracker URL
|
||||
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
|
||||
settings.tracker_url_format=Внешний формат ссылки системы отслеживания ошибок.
|
||||
settings.tracker_issue_style=Стиль Именования Внешней Системы Учета Задач:
|
||||
settings.tracker_issue_style.numeric=Цифровой
|
||||
settings.tracker_issue_style.alphanumeric=Буквенноцифровой
|
||||
settings.tracker_url_format_desc=Вы можете использовать шаблон <code>{user} {repo} {index}</code> для имени пользователя, репозитория и номера задачи.
|
||||
settings.pulls_desc=Включить публичные запросы на слияние
|
||||
settings.danger_zone=Опасная зона
|
||||
settings.new_owner_has_same_repo=У нового владельца уже есть хранилище с таким названием.
|
||||
settings.convert=Преобразовать в обычный репозиторий
|
||||
settings.convert_desc=Это зеркало можно преобразовать в обычный репозиторий. Это не может быть отменено.
|
||||
settings.convert_notices_1=- Эта операция преобразует это зеркало в обычный репозиторий, и она не может быть отменена.
|
||||
settings.convert_confirm=Подтвердите преобразование
|
||||
settings.convert_succeed=Репозиторий был успешно преобразован в обычный.
|
||||
settings.transfer=Передать права собственности
|
||||
settings.transfer_desc=Передать репозиторий другому пользователю или организации где у вас есть права администратора.
|
||||
settings.new_owner_has_same_repo=У нового владельца уже есть хранилище с таким названием.
|
||||
settings.delete=Удалить этот репозиторий
|
||||
settings.delete_desc=Как только вы удалите репозиторий — пути назад не будет. Удостоверьтесь, что вам это точно нужно.
|
||||
settings.transfer_notices_1=- Вы можете потерять доступ, если новый владелец является отдельным пользователем.
|
||||
settings.transfer_notices_2=- Вы сохраните доступ, если новым владельцем станет организация, владельцем которой вы являетесь.
|
||||
settings.transfer_form_title=Введите сопутствующую информацию для подтверждения операции:
|
||||
settings.wiki_delete=Стереть данные Вики
|
||||
settings.wiki_delete_desc=Будьте внимательны! Как только вы удалите Вики — пути назад не будет.
|
||||
settings.wiki_delete_notices_1=-Это будет удалено и отключит Вики для %s
|
||||
settings.wiki_deletion_success=Данные Вики успешно стерты.
|
||||
settings.delete=Удалить этот репозиторий
|
||||
settings.delete_desc=Будьте внимательны! Как только вы удалите репозиторий — пути назад не будет.
|
||||
settings.delete_notices_1=- Эта операция <strong>НЕ МОЖЕТ</strong> быть отменена.
|
||||
settings.delete_notices_2=- Эта операция навсегда удалит всё из этого репозитория, включая данные Git, связанные с ним задачи, комментарии и права доступа для сотрудников.
|
||||
settings.delete_notices_fork_1=- Если данный репозиторий является публичным, все склонированные репозитории останутся независимыми, после его удаления.
|
||||
settings.delete_notices_fork_2=- Если данный репозиторий является приватным, все его форки будут удалены вместе с ним.
|
||||
settings.delete_notices_fork_3=- Если вы хотите сохранить все форки после удаления репозитория, то сначала сделайте его публичным.
|
||||
settings.delete_notices_fork_1=- Все отвлетвления станут независимыми после удаления.
|
||||
settings.deletion_success=Репозиторий был успешно удалён!
|
||||
settings.update_settings_success=Настройка репозитория обновлена успешно.
|
||||
settings.update_settings_success=Настройки репозитория обновлены успешно.
|
||||
settings.transfer_owner=Новый владелец
|
||||
settings.make_transfer=Выполнить передачу
|
||||
settings.transfer_succeed=Владение репозиторием было успешно передано.
|
||||
settings.confirm_delete=Подтвердить удаление
|
||||
settings.add_collaborator=Добавить нового соавтора
|
||||
settings.add_collaborator_success=Был добавлен новый соавтор.
|
||||
settings.delete_collaborator=Удалить
|
||||
settings.collaborator_deletion=Удаление соавтора
|
||||
settings.collaborator_deletion_desc=Этот пользователь больше не будет иметь доступа для совместной работы в этом репозитории после удаления. Вы хотите продолжить?
|
||||
settings.remove_collaborator_success=Соавтор был удален.
|
||||
settings.search_user_placeholder=Поиск пользователя...
|
||||
settings.org_not_allowed_to_be_collaborator=Организации не могут быть добавлены как соавторы.
|
||||
settings.user_is_org_member=Пользователь является членом организации, члены которой не могут быть добавлены в качестве соавтора.
|
||||
settings.add_webhook=Добавить Webhook
|
||||
settings.hooks_desc=Webhooks позволяют внешним службам получать уведомления при возникновении определенных событий на Gogs. При возникновении указанных событий мы отправим запрос POST на каждый заданный вами URL. Узнать больше можно в нашем <a target="_blank" href="%s">Руководстве по Webhooks</a>.
|
||||
@@ -636,6 +732,8 @@ settings.event_send_everything=Мне нужно <strong>все</strong>.
|
||||
settings.event_choose=Позвольте мне выбрать то, что нужно.
|
||||
settings.event_create=Создать
|
||||
settings.event_create_desc=Ветка или тэг созданы
|
||||
settings.event_pull_request=Запросы на слияние
|
||||
settings.event_pull_request_desc=Запрос слияния открыт, закрыт, переоткрыт, изменён, назначен, снят, метка обновлена, метка убрана, или синхронизирован.
|
||||
settings.event_push=Push
|
||||
settings.event_push_desc=Push в репозиторий
|
||||
settings.active=Активен
|
||||
@@ -666,18 +764,20 @@ settings.deploy_key_deletion_success=Ключ развертывания усп
|
||||
diff.browse_source=Просмотр исходного кода
|
||||
diff.parent=Родитель
|
||||
diff.commit=Сommit
|
||||
diff.data_not_available=Данные Diff не доступны.
|
||||
diff.data_not_available=Данные Diff недоступны.
|
||||
diff.show_diff_stats=Показать статистику Diff
|
||||
diff.show_split_view=Разделённый вид
|
||||
diff.show_unified_view=Единый вид
|
||||
diff.stats_desc=<strong> %d измененных файлов</strong> с <strong>%d добавлено</strong> и <strong>%d удалено</strong>
|
||||
diff.bin=BIN
|
||||
diff.view_file=Просмотреть файл
|
||||
diff.file_suppressed=Разница между файлами не показана из-за своего большого размера
|
||||
diff.too_many_files=Некоторые файлы не были показаны из-за большого количества измененных файлов
|
||||
|
||||
release.releases=Релизы
|
||||
release.new_release=Новый релиз
|
||||
release.draft=Черновик
|
||||
release.prerelease=Предрелиз
|
||||
release.prerelease=Пре-релиз
|
||||
release.stable=Стабильный
|
||||
release.edit=Редактировать
|
||||
release.ahead=<strong>%d</strong> коммитов %s начиная с этого релиза
|
||||
@@ -700,9 +800,10 @@ release.save_draft=Сохранить черновик
|
||||
release.edit_release=Редактировать релиз
|
||||
release.delete_release=Удалить этот релиз
|
||||
release.deletion=Удаление релиза
|
||||
release.deletion_desc=Удаление данного релиза так же удалит все относящиеся к нему Git теги. Продолжить?
|
||||
release.deletion_desc=Удаление этого релиза удалит соответствующую Git метку. Вы хотите продолжить?
|
||||
release.deletion_success=Релиз был успешно удален!
|
||||
release.tag_name_already_exist=Релиз с этим именем тега уже существует.
|
||||
release.tag_name_already_exist=Релиз с этим именем метки уже существует.
|
||||
release.tag_name_invalid=Имя тега является не допустимым.
|
||||
release.downloads=Загрузки
|
||||
|
||||
[org]
|
||||
@@ -787,7 +888,7 @@ teams.add_nonexistent_repo=Вы добавляете в отсутствующи
|
||||
[admin]
|
||||
dashboard=Панель управления
|
||||
users=Пользователи
|
||||
organizations=Организации
|
||||
organizations=Группы
|
||||
repositories=Репозитории
|
||||
authentication=Авторизация
|
||||
config=Настройки
|
||||
@@ -818,6 +919,8 @@ dashboard.resync_all_sshkeys=Переписать файл «.ssh/authorized_key
|
||||
dashboard.resync_all_sshkeys_success=Были успешно переписаны все открытые ключи.
|
||||
dashboard.resync_all_update_hooks=Перезаписать все апдейт-хуки этого репозитория (необходимо, когда изменен путь до папки конфигураций)
|
||||
dashboard.resync_all_update_hooks_success=Апдейт-хуки всех репозиториев успешно перезаписаны.
|
||||
dashboard.reinit_missing_repos=Реинициализировать все репозитории с утерянными Git файлами
|
||||
dashboard.reinit_missing_repos_success=Все репозитории с утерянными Git файлами успешно реинициализированы.
|
||||
|
||||
dashboard.server_uptime=Время непрерывной работы сервера
|
||||
dashboard.current_goroutine=Текущий Goroutines
|
||||
@@ -868,6 +971,7 @@ users.edit_account=Изменение учетной записи
|
||||
users.max_repo_creation=Ограничение максимального количества создаваемых репозиториев
|
||||
users.max_repo_creation_desc=(Установить -1 для использования стандартного глобального значения предела)
|
||||
users.is_activated=Эта учетная запись активирована
|
||||
users.prohibit_login=Вход с данной учетной записи запрещен
|
||||
users.is_admin=У этой учетной записи есть права администратора
|
||||
users.allow_git_hook=Пользователь имеет право создать Git перехватчик
|
||||
users.allow_import_local=Пользователь имеет право импортировать локальные репозитории
|
||||
@@ -898,6 +1002,7 @@ auths.enabled=Включено
|
||||
auths.updated=Обновлено
|
||||
auths.auth_type=Тип аутентификации
|
||||
auths.auth_name=Имя аутентификации
|
||||
auths.security_protocol=Протокол безопасности
|
||||
auths.domain=Домен
|
||||
auths.host=Хост
|
||||
auths.port=Порт
|
||||
@@ -911,6 +1016,7 @@ auths.attribute_username_placeholder=Оставьте пустым, чтобы
|
||||
auths.attribute_name=Имя аттрибута
|
||||
auths.attribute_surname=Фамилия аттрибута
|
||||
auths.attribute_mail=Электронная почта аттрибута
|
||||
auths.attributes_in_bind=Извлечение атрибутов в виде Bind DN
|
||||
auths.filter=Фильтр пользователя
|
||||
auths.admin_filter=Фильтр администратора
|
||||
auths.ms_ad_sa=Ms Ad SA
|
||||
@@ -932,7 +1038,9 @@ auths.update=Обновить параметры аутентификации
|
||||
auths.delete=Удалить этот канал аутентификации
|
||||
auths.delete_auth_title=Удаление канала аутентификации
|
||||
auths.delete_auth_desc=Этот канал аутентификации будет удален. Вы уверены что хотите продолжить?
|
||||
auths.still_in_used=Эта проверка подлинности до сих пор используется некоторыми пользователями, удалите или преобразуйте этих пользователей в другой тип входа в систему.
|
||||
auths.deletion_success=Канал аутентификации успешно удален!
|
||||
auths.login_source_exist=Источник входа '%s' уже существует.
|
||||
|
||||
config.server_config=Конфигурация сервера
|
||||
config.app_name=Имя приложения
|
||||
@@ -943,11 +1051,25 @@ config.offline_mode=Автономный режим
|
||||
config.disable_router_log=Отключение журнала маршрутизатора
|
||||
config.run_user=Запуск пользователем
|
||||
config.run_mode=Режим выполнения
|
||||
config.git_version=Git Version
|
||||
config.repo_root_path=Путь до корня репозитория
|
||||
config.static_file_root_path=Статичный путь до файла
|
||||
config.log_file_root_path=Путь до папки с логами
|
||||
config.script_type=Тип сценария
|
||||
config.reverse_auth_user=Заголовок с именем пользователя для авторизации на reverse proxy
|
||||
|
||||
config.ssh_config=Конфигурация SSH
|
||||
config.ssh_enabled=Включено
|
||||
config.ssh_start_builtin_server=Запустить встроенный сервер
|
||||
config.ssh_domain=Домен
|
||||
config.ssh_port=Порт
|
||||
config.ssh_listen_port=Прослушиваемый порт
|
||||
config.ssh_root_path=Корневой путь
|
||||
config.ssh_key_test_path=Путь к тестовому ключу
|
||||
config.ssh_keygen_path=Путь к генератору ключей ('ssh-keygen')
|
||||
config.ssh_minimum_key_size_check=Минимальный размер ключа проверки
|
||||
config.ssh_minimum_key_sizes=Минимальные размеры ключа
|
||||
|
||||
config.db_config=Конфигурация базы данных
|
||||
config.db_type=Тип
|
||||
config.db_host=Хост
|
||||
@@ -957,33 +1079,41 @@ config.db_ssl_mode=Режим SSL
|
||||
config.db_ssl_mode_helper=(только для «postgres»)
|
||||
config.db_path=Путь
|
||||
config.db_path_helper=(для "SQLite3" и "TiDB")
|
||||
|
||||
config.service_config=Сервисная конфигурация
|
||||
config.register_email_confirm=Требуется подтверждение по электронной почте
|
||||
config.disable_register=Отключить регистрацию
|
||||
config.show_registration_button=Показать кнопку регистрации
|
||||
config.require_sign_in_view=Для просмотра необходима авторизация
|
||||
config.enable_cache_avatar=Кешировать аватар
|
||||
config.mail_notify=Почтовые уведомления
|
||||
config.disable_key_size_check=Отключить проверку на минимальный размер ключа
|
||||
config.enable_captcha=Включить капчу
|
||||
config.active_code_lives=Время жизни кода для активации
|
||||
config.reset_password_code_lives=Время жизни кода сброса пароля
|
||||
|
||||
config.webhook_config=Настройка автоматического обновления репозиции
|
||||
config.queue_length=Длина очереди
|
||||
config.deliver_timeout=Задержка доставки
|
||||
config.skip_tls_verify=Пропустить TLS проверка
|
||||
|
||||
config.mailer_config=Настройки почты
|
||||
config.mailer_enabled=Включено
|
||||
config.mailer_disable_helo=Отключить HELO
|
||||
config.mailer_name=Имя
|
||||
config.mailer_host=Сервер
|
||||
config.mailer_user=Пользователь
|
||||
config.send_test_mail=Отправить тестовое письмо
|
||||
config.test_mail_failed=Не удалось отправить тестовое письмо «%s»: %v
|
||||
config.test_mail_sent=Тестовое письмо было отправлено «%s».
|
||||
|
||||
config.oauth_config=Конфигурация OAuth
|
||||
config.oauth_enabled=Включено
|
||||
|
||||
config.cache_config=Настройки кеша
|
||||
config.cache_adapter=Адаптер кэша
|
||||
config.cache_interval=Интервал кэширования
|
||||
config.cache_conn=Подключение кэша
|
||||
|
||||
config.session_config=Конфигурация сессии
|
||||
config.session_provider=Провайдер сессии
|
||||
config.provider_config=Конфигурация провайдера
|
||||
@@ -993,9 +1123,24 @@ config.gc_interval_time=Интервал работы сборщика мусо
|
||||
config.session_life_time=Время жизни сессии
|
||||
config.https_only=Только HTTPS
|
||||
config.cookie_life_time=Время жизни файла cookie
|
||||
|
||||
config.picture_config=Настройка изображения
|
||||
config.picture_service=Сервис изображений
|
||||
config.disable_gravatar=Отключить Gravatar
|
||||
config.enable_federated_avatar=Включить внешние Аватары
|
||||
|
||||
config.git_config=Конфигурация GIT
|
||||
config.git_disable_diff_highlight=Отключить подсветку синтаксиса Diff
|
||||
config.git_max_diff_lines=Максимальное количество строк Diff (на файл)
|
||||
config.git_max_diff_line_characters=Максимальное количество символов Diff (в строке)
|
||||
config.git_max_diff_files=Максимальное количество Diff-файлов (при показе)
|
||||
config.git_gc_args=Аргументы GC
|
||||
config.git_migrate_timeout=Тайм-аут миграции
|
||||
config.git_mirror_timeout=Время Ожидания Обновления Зеркала
|
||||
config.git_clone_timeout=Время Ожидания Операции Клонирования
|
||||
config.git_pull_timeout=Время Ожидания Операции Извлечения
|
||||
config.git_gc_timeout=Время Ожидания Операции Сборки Мусора
|
||||
|
||||
config.log_config=Конфигурация журнала
|
||||
config.log_mode=Режим журналирования
|
||||
|
||||
@@ -1025,15 +1170,19 @@ notices.op=Op.
|
||||
notices.delete_success=Системное уведомление успешно удалено.
|
||||
|
||||
[action]
|
||||
create_repo=создал репозиторий <a href="%s"> %s</a>
|
||||
rename_repo=репозиторий переименован из <code>%[1]s</code>на <a href="%[2]s">%[3]s</a>
|
||||
commit_repo=запушил <a href="%[1]s/src/%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a>
|
||||
create_repo=создал(а) репозиторий <a href="%s"> %s</a>
|
||||
rename_repo=переименовал(а) репозиторий из <code>%[1]s</code> на <a href="%[2]s">%[3]s</a>
|
||||
commit_repo=запушил(а) <a href="%[1]s/src/%[2]s">%[3]s</a> в <a href="%[1]s">%[4]s</a>
|
||||
create_issue=`открыл(а) задачу <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
create_pull_request=`созданный пулл-реквест <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
close_issue=`закрыл(а) задачу <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
reopen_issue=`возобновил(а) задачу <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
create_pull_request=`создал запрос на слияние <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
close_pull_request=`закрыл запрос на слияние <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
reopen_pull_request=`открыл снова запрос на слияние <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
comment_issue=`прокомментировал(а) вопрос <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
merge_pull_request=`слил пул реквест <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
transfer_repo=перенес репозиторий <code>%s</code> в <a href="%s">%s</a>
|
||||
push_tag=запушил тэг <a href="%s/src/%s">%[2]s</a> в <a href="%[1]s">%[3]s</a>
|
||||
push_tag=запушил(а) метку <a href="%s/src/%s">%[2]s</a> в <a href="%[1]s">%[3]s</a>
|
||||
compare_commits=Просмотр сравнение для этих %d коммитов
|
||||
|
||||
[tool]
|
||||
|
||||
1214
conf/locale/locale_sr-SP.ini
Normal file
1214
conf/locale/locale_sr-SP.ini
Normal file
File diff suppressed because it is too large
Load Diff
1214
conf/locale/locale_sv-SE.ini
Normal file
1214
conf/locale/locale_sv-SE.ini
Normal file
File diff suppressed because it is too large
Load Diff
1214
conf/locale/locale_tr-TR.ini
Normal file
1214
conf/locale/locale_tr-TR.ini
Normal file
File diff suppressed because it is too large
Load Diff
217
conf/locale/locale_zh-CN.ini
Executable file → Normal file
217
conf/locale/locale_zh-CN.ini
Executable file → Normal file
@@ -38,19 +38,12 @@ settings=帐户设置
|
||||
your_profile=个人信息
|
||||
your_settings=用户设置
|
||||
|
||||
news_feed=最新活动
|
||||
activities=最近活动
|
||||
pull_requests=合并请求
|
||||
issues=工单管理
|
||||
|
||||
cancel=取消
|
||||
|
||||
[search]
|
||||
search=搜索...
|
||||
repository=仓库
|
||||
user=用户
|
||||
issue=工单
|
||||
code=代码
|
||||
|
||||
[install]
|
||||
install=安装页面
|
||||
title=首次运行安装程序
|
||||
@@ -65,7 +58,7 @@ db_name=数据库名称
|
||||
db_helper=如果您使用 MySQL,请使用 INNODB 引擎以及 utf8_general_ci 字符集。
|
||||
ssl_mode=SSL 模式
|
||||
path=数据库文件路径
|
||||
sqlite_helper=SQLite3 或 TiDB 的数据库路径。
|
||||
sqlite_helper=SQLite3 或 TiDB 数据库文件路径。<br>作为服务启动时,请使用绝对路径。
|
||||
err_empty_db_path=SQLite3 或 TiDB 的数据库路径不能为空。
|
||||
err_invalid_tidb_name=TiDB 数据库名称不允许包含字符 "." 或 "-" 。
|
||||
no_admin_and_disable_registration=您不能够在未创建管理员用户的情况下禁止注册。
|
||||
@@ -86,6 +79,8 @@ http_port=HTTP 端口号
|
||||
http_port_helper=应用监听的端口号
|
||||
app_url=应用 URL
|
||||
app_url_helper=该设置影响 HTTP/HTTPS 克隆地址和一些邮箱中的链接。
|
||||
log_root_path=日志路径
|
||||
log_root_path_helper=存放日志文件的目录
|
||||
|
||||
optional_title=可选设置
|
||||
email_title=邮件服务设置
|
||||
@@ -101,6 +96,8 @@ offline_mode=启用离线模式
|
||||
offline_mode_popup=在部署模式下也禁用从 CDN 获取文件,所以的资源都将从本地服务器获取。
|
||||
disable_gravatar=禁用 Gravatar 服务
|
||||
disable_gravatar_popup=禁用 Gravatar 和自定义源,仅使用由用户上传的或默认的头像。
|
||||
federated_avatar_lookup=启用 Federated Avatars 查找
|
||||
federated_avatar_lookup_popup=启用 Federated Avatars 查找以使用开源的 Libravatar 服务。
|
||||
disable_registration=禁止用户自主注册
|
||||
disable_registration_popup=禁止用户自行注册功能,只有管理员可以添加帐号。
|
||||
enable_captcha=启用验证码服务
|
||||
@@ -119,15 +116,18 @@ sqlite3_not_available=您所使用的发行版不支持 SQLite3,请从 %s 下
|
||||
invalid_db_setting=数据库设置不正确:%v
|
||||
invalid_repo_path=仓库根目录设置不正确:%v
|
||||
run_user_not_match=运行系统用户非当前用户:%s -> %s
|
||||
invalid_smtp_from=SMTP From 字段不合法:%v
|
||||
save_config_failed=应用配置保存失败:%v
|
||||
invalid_admin_setting=管理员帐户设置不正确:%v
|
||||
install_success=您好!我们很高兴您选择使用 Gogs,祝您使用愉快,代码从此无 BUG!
|
||||
invalid_log_root_path=无效的日志路径:%v
|
||||
|
||||
[home]
|
||||
uname_holder=用户名或邮箱
|
||||
password_holder=密码
|
||||
switch_dashboard_context=切换控制面板用户
|
||||
my_repos=我的仓库
|
||||
show_more_repos=显示更多仓库...
|
||||
collaborative_repos=参与协作的仓库
|
||||
my_orgs=我的组织
|
||||
my_mirrors=我的镜像
|
||||
@@ -136,7 +136,10 @@ view_home=访问 %s
|
||||
issues.in_your_repos=属于该用户仓库的
|
||||
|
||||
[explore]
|
||||
repos=探索仓库
|
||||
repos=仓库
|
||||
users=用户
|
||||
organizations=组织
|
||||
search=搜索
|
||||
|
||||
[auth]
|
||||
create_new_account=创建帐户
|
||||
@@ -150,15 +153,17 @@ forget_password=忘记密码?
|
||||
sign_up_now=还没帐户?马上注册。
|
||||
confirmation_mail_sent_prompt=一封新的确认邮件已经被发送至 <b>%s</b>,请检查您的收件箱并在 %d 小时内完成确认注册操作。
|
||||
active_your_account=激活您的帐户
|
||||
prohibit_login=禁止登录
|
||||
prohibit_login_desc=您的帐户被禁止登录,请联系网站管理员。
|
||||
resent_limit_prompt=对不起,您请求发送激活邮件过于频繁,请等待 3 分钟后再试!
|
||||
has_unconfirmed_mail=%s 您好,系统检测到您有一封发送至 <b>%s</b> 但未被确认的邮件。如果您未收到激活邮件,或需要重新发送,请单击下方的按钮。
|
||||
resend_mail=单击此处重新发送确认邮件
|
||||
email_not_associate=您输入的邮箱地址未被关联到任何帐号!
|
||||
send_reset_mail=单击此处(重新)发送您的密码重置邮件
|
||||
reset_password=重置密码
|
||||
invalid_code=对不起,您的确认代码已过期或已失效。
|
||||
reset_password_helper=单击此处重置密码
|
||||
password_too_short=密码长度不能少于 6 位!
|
||||
non_local_account=非本地类型的帐户无法通过 Gogs 修改密码。
|
||||
|
||||
[mail]
|
||||
activate_account=请激活您的帐户
|
||||
@@ -185,6 +190,13 @@ TeamName=团队名称
|
||||
AuthName=认证名称
|
||||
AdminEmail=管理员邮箱
|
||||
|
||||
NewBranchName=新的分支名称
|
||||
CommitSummary=提交小结
|
||||
CommitMessage=提交消息
|
||||
CommitChoice=提交选择
|
||||
TreeName=文件路径
|
||||
Content=内容
|
||||
|
||||
require_error=不能为空。
|
||||
alpha_dash_error=必须为英文字母、阿拉伯数字或横线(-_)。
|
||||
alpha_dash_dot_error=必须为英文字母、阿拉伯数字、横线(-_)或点。
|
||||
@@ -203,7 +215,6 @@ repo_name_been_taken=仓库名称已经被占用。
|
||||
org_name_been_taken=组织名称已经被占用。
|
||||
team_name_been_taken=团队名称已经被占用。
|
||||
email_been_used=邮箱地址已经被使用。
|
||||
illegal_team_name=团队名称包含非法字符。
|
||||
username_password_incorrect=用户名或密码不正确。
|
||||
enterred_invalid_repo_name=请检查您输入的仓库名称是正确。
|
||||
enterred_invalid_owner_name=请检查您输入的新所有者用户名是否正确。
|
||||
@@ -219,13 +230,10 @@ still_own_repo=您的帐户仍然是某些仓库的拥有者,您必须先转
|
||||
still_has_org=您的帐户仍旧是某些组织的成员,您必须先离开或删除组织。
|
||||
org_still_own_repo=该组织仍然是某些仓库的拥有者,您必须先转移或删除它们才能执行删除组织操作!
|
||||
|
||||
still_own_user=该授权认证依旧被部分用户使用,请先删除该部分用户后再试!
|
||||
|
||||
target_branch_not_exist=目标分支不存在。
|
||||
|
||||
[user]
|
||||
change_avatar=到 gravatar.com 上修改您的头像
|
||||
change_custom_avatar=到个人设置中修改头像
|
||||
change_avatar=修改头像
|
||||
join_on=加入于
|
||||
repositories=仓库列表
|
||||
activity=公开活动
|
||||
@@ -241,6 +249,7 @@ form.name_pattern_not_allowed=用户名不允许 '%s' 的格式。
|
||||
[settings]
|
||||
profile=个人信息
|
||||
password=修改密码
|
||||
avatar=头像设置
|
||||
ssh_keys=管理 SSH 密钥
|
||||
social=社交帐号绑定
|
||||
applications=管理授权应用
|
||||
@@ -261,12 +270,13 @@ change_username_prompt=该操作将会影响到所有与您帐户有关的链接
|
||||
continue=继续操作
|
||||
cancel=取消操作
|
||||
|
||||
lookup_avatar_by_mail=通过邮箱地址获取头像
|
||||
federated_avatar_lookup=Federated Avatar 查找
|
||||
enable_custom_avatar=启动自定义头像
|
||||
enable_custom_avatar_helper=激活该选项来禁止从 Gravatar 获取头像
|
||||
choose_new_avatar=选择新的头像
|
||||
update_avatar=更新头像设置
|
||||
delete_current_avatar=删除当前头像
|
||||
uploaded_avatar_not_a_image=上传的文件不是一张图片!
|
||||
no_custom_avatar_available=未上传过自定义头像,无法激活该选项。
|
||||
update_avatar_success=您的头像设置更新成功!
|
||||
|
||||
change_password=修改密码
|
||||
@@ -328,6 +338,10 @@ access_token_deletion=删除个人操作令牌操作
|
||||
access_token_deletion_desc=删除该个人操作令牌将删除所有相关的应用程序的访问权限。是否继续?
|
||||
delete_token_success=个人操作令牌删除成功!请更新与该令牌有关的所有应用。
|
||||
|
||||
orgs.none=您现在还不是任何组织的成员。
|
||||
orgs.leave_title=离开组织
|
||||
orgs.leave_desc=离开组织后,组织相关的所有仓库和团队权限将被收回。是否继续?
|
||||
|
||||
delete_account=删除当前帐户
|
||||
delete_prompt=删除操作会永久清除您的帐户信息,并且 <strong>不可恢复</strong>!
|
||||
confirm_delete_account=确认删除帐户
|
||||
@@ -348,7 +362,7 @@ fork_from=派生自
|
||||
fork_visiblity_helper=派生仓库无法修改可见性
|
||||
repo_desc=仓库描述
|
||||
repo_lang=仓库语言
|
||||
repo_lang_helper=请选择 .gitignore 文件
|
||||
repo_gitignore_helper=选择 .gitignore 模板
|
||||
license=授权许可
|
||||
license_helper=请选择授权许可文件
|
||||
readme=自述文档
|
||||
@@ -356,9 +370,12 @@ readme_helper=请选择自述文档模板
|
||||
auto_init=使用选定的文件和模板初始化仓库
|
||||
create_repo=创建仓库
|
||||
default_branch=默认分支
|
||||
mirror_prune=修剪
|
||||
mirror_prune_desc=当远程追踪的引用被删除时本地也同步删除
|
||||
mirror_interval=镜像同步周期(小时)
|
||||
mirror_address=镜像地址
|
||||
mirror_address_desc=请在镜像地址中写入必要的用户凭据信息。
|
||||
mirror_last_synced=上次同步时间:
|
||||
watchers=关注者
|
||||
stargazers=称赞者
|
||||
forks=派生仓库
|
||||
@@ -372,7 +389,8 @@ migrate_type=迁移类型
|
||||
migrate_type_helper=该仓库将是一个 <span class="text blue">镜像</span>
|
||||
migrate_repo=迁移仓库
|
||||
migrate.clone_address=克隆地址
|
||||
migrate.clone_address_desc=该地址可以是 HTTP/HTTPS/GIT URL 或本地服务器路径。
|
||||
migrate.clone_address_desc=该地址可以是 HTTP/HTTPS/GIT 类型的 URL。
|
||||
migrate.clone_address_desc_import_local=您被允许使用服务器本地路径作为仓库的远程地址进行迁移。
|
||||
migrate.permission_denied=您没有获得导入本地仓库的权限。
|
||||
migrate.invalid_local_path=无效的本地路径,不存在或不是一个目录!
|
||||
migrate.failed=迁移失败:%v
|
||||
@@ -397,7 +415,7 @@ create_new_repo_command=从命令行创建一个新的仓库
|
||||
push_exist_repo=从命令行推送已经创建的仓库
|
||||
repo_is_empty=该仓库不包含任何内容,请稍后再进行访问!
|
||||
|
||||
code=代码
|
||||
files=文件
|
||||
branch=分支
|
||||
tree=目录树
|
||||
filter_branch_and_tag=过滤分支或标签
|
||||
@@ -413,6 +431,47 @@ file_raw=原始文件
|
||||
file_history=文件历史
|
||||
file_view_raw=查看原始文件
|
||||
file_permalink=永久链接
|
||||
file_too_large=文件过大导致无法显示
|
||||
video_not_supported_in_browser=您的浏览器不支持使用 HTML5 播放视频。
|
||||
|
||||
editor.new_file=新的文件
|
||||
editor.upload_file=上传文件
|
||||
editor.edit_file=编辑文件
|
||||
editor.preview_changes=预览变更
|
||||
editor.cannot_edit_non_text_files=无法编辑非文本文件
|
||||
editor.edit_this_file=编辑此文件
|
||||
editor.must_be_on_a_branch=您必须在某个分支上才能对此文件进行修改操作
|
||||
editor.fork_before_edit=您必须派生此仓库才能对此文件进行修改操作
|
||||
editor.delete_this_file=删除此文件
|
||||
editor.must_have_write_access=您必须具有可写权限才能对此文件进行修改操作
|
||||
editor.file_delete_success=文件 '%s' 删除成功!
|
||||
editor.name_your_file=命名文件...
|
||||
editor.filename_help=输入名称后按下 / 键即可添加目录,或将光标移至输入框最左侧按下退格键移除目录。
|
||||
editor.or=或
|
||||
editor.cancel_lower=取消
|
||||
editor.commit_changes=提交变更
|
||||
editor.add_tmpl=添加 '%s/<文件名>'
|
||||
editor.add=添加 '%s'
|
||||
editor.update=更新 '%s'
|
||||
editor.delete=删除 '%s'
|
||||
editor.commit_message_desc=添加一个可选的扩展描述...
|
||||
editor.commit_directly_to_this_branch=直接提交至 <strong class="branch-name">%s</strong> 分支。
|
||||
editor.create_new_branch=为此提交创建一个 <strong>新的分支</strong> 并发起合并请求。
|
||||
editor.new_branch_name_desc=新的分支名称...
|
||||
editor.cancel=取消
|
||||
editor.filename_cannot_be_empty=文件名不能为空。
|
||||
editor.branch_already_exists=此仓库已存在名为 '%s' 的分支。
|
||||
editor.directory_is_a_file=路径 '%s' 的父路径中包含此仓库已存在的文件名。
|
||||
editor.file_is_a_symlink=文件 '%s' 为一个符号链接,无法通过 Web 编辑器编辑内容。
|
||||
editor.filename_is_a_directory=文件名 '%s' 是此仓库中已存在的目录名。
|
||||
editor.file_editing_no_longer_exists=您编辑的文件 '%s' 已经不存在于此仓库中。
|
||||
editor.file_changed_while_editing=文件内容在您进行编辑时已经发生变动。<a target="_blank" href="%s">单击此处</a> 查看变动的具体内容,或者 <strong>再次提交</strong> 覆盖已发生的变动。
|
||||
editor.file_already_exists=此仓库已经存在名为 '%s' 的文件。
|
||||
editor.no_changes_to_show=没有可以显示的变更。
|
||||
editor.fail_to_update_file=更新/创建文件 '%s' 时发生错误:%v
|
||||
editor.add_subdir=添加子目录...
|
||||
editor.unable_to_upload_files=上传文件至 '%s' 时发生错误:%v
|
||||
editor.upload_files_to_dir=上传文件至 '%s'
|
||||
|
||||
commits.commits=次代码提交
|
||||
commits.search=搜索提交历史
|
||||
@@ -439,6 +498,11 @@ issues.create=创建工单
|
||||
issues.new_label=创建标签
|
||||
issues.new_label_placeholder=标签名称...
|
||||
issues.create_label=创建标签
|
||||
issues.label_templates.title=加载预定义的标签模板
|
||||
issues.label_templates.info=此仓库还未创建任何标签,您可以通过上方的 "创建标签" 创建一个新的标签或加载一组预定义的标签。
|
||||
issues.label_templates.helper=选择标签模板
|
||||
issues.label_templates.use=加载标签模板
|
||||
issues.label_templates.fail_to_load_file=加载标签模板文件 '%s' 时发生错误:%v
|
||||
issues.open_tab=%d 个开启中
|
||||
issues.close_tab=%d 个已关闭
|
||||
issues.filter_label=标签筛选
|
||||
@@ -466,7 +530,8 @@ issues.next=下一页
|
||||
issues.open_title=开启中
|
||||
issues.closed_title=已关闭
|
||||
issues.num_comments=%d 条评论
|
||||
issues.commented_at=`于 <a id="%[1]s" href="#%[1]s">%[2]s</a> 评论`
|
||||
issues.commented_at=`评论于 <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=您确定要删除该条评论吗?
|
||||
issues.no_content=这个人很懒,什么都没留下。
|
||||
issues.close_issue=关闭
|
||||
issues.close_comment_issue=评论并关闭
|
||||
@@ -477,10 +542,9 @@ issues.closed_at=`于 <a id="%[1]s" href="#%[1]s">%[2]s</a> 关闭`
|
||||
issues.reopened_at=`于 <a id="%[1]s" href="#%[1]s">%[2]s</a> 重新开启`
|
||||
issues.commit_ref_at=`在代码提交 <a id="%[1]s" href="#%[1]s">%[2]s</a> 中引用了该工单`
|
||||
issues.poster=发布者
|
||||
issues.admin=管理员
|
||||
issues.collaborator=协作者
|
||||
issues.owner=所有者
|
||||
issues.sign_up_for_free=免费注册
|
||||
issues.sign_in_require_desc=并加入到对话中来。如果您已经注册,可以直接 <a href="%s">登录并评论</a>
|
||||
issues.sign_in_require_desc=<a href="%s">登陆</a> 并参与到对话中。
|
||||
issues.edit=编辑
|
||||
issues.cancel=取消
|
||||
issues.save=保存
|
||||
@@ -494,6 +558,9 @@ issues.label_modify=修改标签
|
||||
issues.label_deletion=删除标签操作
|
||||
issues.label_deletion_desc=删除该标签将会移除所有工单中相关的信息。是否继续?
|
||||
issues.label_deletion_success=标签删除成功!
|
||||
issues.num_participants=%d 名参与者
|
||||
issues.attachment.open_tab=`在新的标签页中查看 '%s'`
|
||||
issues.attachment.download=`点击下载 '%s'`
|
||||
|
||||
pulls.new=创建合并请求
|
||||
pulls.compare_changes=对比文件变化
|
||||
@@ -520,6 +587,8 @@ pulls.cannot_auto_merge_desc=该合并请求存在冲突,无法进行自动合
|
||||
pulls.cannot_auto_merge_helper=请手动拉取代码变更以解决冲突。
|
||||
pulls.merge_pull_request=合并请求
|
||||
pulls.open_unmerged_pull_exists=`由于已经存在来自相同仓库和合并信息的未合并请求(#%d),您无法执行重新开启操作。`
|
||||
pulls.delete_branch=删除分支
|
||||
pulls.delete_branch_has_new_commits=该分支在完成合并后又推送了新的提交,无法进行删除操作。
|
||||
|
||||
milestones.new=新的里程碑
|
||||
milestones.open_tab=%d 开启中
|
||||
@@ -557,6 +626,8 @@ wiki.save_page=保存页面
|
||||
wiki.last_commit_info=%s 于 %s 修改了此页面
|
||||
wiki.edit_page_button=修改
|
||||
wiki.new_page_button=新的页面
|
||||
wiki.delete_page_button=删除页面
|
||||
wiki.delete_page_notice_1=此操作将删除页面 <code>"%s"</code>,请三思而后行。
|
||||
wiki.page_already_exists=相同名称的 Wiki 页面已经存在。
|
||||
wiki.pages=所有页面
|
||||
wiki.last_updated=最后更新于 %s
|
||||
@@ -564,36 +635,57 @@ wiki.last_updated=最后更新于 %s
|
||||
settings=仓库设置
|
||||
settings.options=基本设置
|
||||
settings.collaboration=管理协作者
|
||||
settings.collaboration.admin=管理权限
|
||||
settings.collaboration.write=可写权限
|
||||
settings.collaboration.read=可读权限
|
||||
settings.collaboration.undefined=未定义
|
||||
settings.hooks=管理 Web 钩子
|
||||
settings.githooks=管理 Git 钩子
|
||||
settings.basic_settings=基本设置
|
||||
settings.mirror_settings=镜像设置
|
||||
settings.sync_mirror=立即同步
|
||||
settings.mirror_sync_in_progress=镜像同步请求已经生效,请稍后刷新页面。
|
||||
settings.site=官方网站
|
||||
settings.update_settings=更新仓库设置
|
||||
settings.change_reponame_prompt=该操作将会影响到所有与该仓库有关的链接
|
||||
settings.advanced_settings=高级设置
|
||||
settings.wiki_desc=启用 Wiki 以允许用户协作文档
|
||||
settings.wiki_desc=启用 Wiki 系统
|
||||
settings.use_internal_wiki=使用内置 Wiki 系统
|
||||
settings.use_external_wiki=使用外部 Wiki
|
||||
settings.external_wiki_url=外部 Wiki 链接
|
||||
settings.external_wiki_url_desc=当访问者单击分页标签时,将会被重定向到该链接。
|
||||
settings.issues_desc=启用内置的轻量级工单管理系统
|
||||
settings.issues_desc=启用工单管理系统
|
||||
settings.use_internal_issue_tracker=使用内置的轻量级工单管理系统
|
||||
settings.use_external_issue_tracker=使用外部的工单管理系统
|
||||
settings.external_tracker_url=外部工单管理系统 URL
|
||||
settings.external_tracker_url_desc=当访问者单击分页标签时,将会被重定向到该链接。
|
||||
settings.tracker_url_format=外部工单管理系统的 URL 格式
|
||||
settings.tracker_issue_style=外部工单管理系统命名风格:
|
||||
settings.tracker_issue_style.numeric=纯数字形式
|
||||
settings.tracker_issue_style.alphanumeric=英文字母数字组合形式
|
||||
settings.tracker_url_format_desc=您可以使用 <code>{user} {repo} {index}</code> 分别作为用户名、仓库名和工单索引的占位符。
|
||||
settings.pulls_desc=启用合并请求以接受社区贡献
|
||||
settings.danger_zone=危险操作区
|
||||
settings.new_owner_has_same_repo=新的仓库拥有者已经存在同名仓库!
|
||||
settings.convert=转换为普通仓库
|
||||
settings.convert_desc=您可以将该镜像仓库转换为普通仓库,且此操作不可逆。
|
||||
settings.convert_notices_1=- 该操作会将该镜像仓库转换为普通仓库,且操作不可逆。
|
||||
settings.convert_confirm=确认转换
|
||||
settings.convert_succeed=转换为普通仓库类型成功!
|
||||
settings.transfer=转移仓库所有权
|
||||
settings.transfer_desc=您可以将仓库转移至您拥有管理员权限的帐户或组织。
|
||||
settings.new_owner_has_same_repo=新的仓库拥有者已经存在同名仓库!
|
||||
settings.delete=删除本仓库
|
||||
settings.delete_desc=删除仓库操作不可逆转,请三思而后行。
|
||||
settings.transfer_notices_1=- 如果您将仓库转移给个人用户,您将会丢失操作权限。
|
||||
settings.transfer_notices_2=- 如果您将仓库转移给您是所有者的组织,您的操作权限将被保留。
|
||||
settings.transfer_form_title=请输入以下信息以确认您的操作:
|
||||
settings.wiki_delete=清除 Wiki 数据
|
||||
settings.wiki_delete_desc=清除 Wiki 数据操作不可逆转,请三思而后行。
|
||||
settings.wiki_delete_notices_1=- 此操作将会清除并禁用仓库 %s 的 Wiki
|
||||
settings.wiki_deletion_success=仓库 Wiki 数据清除成功!
|
||||
settings.delete=删除本仓库
|
||||
settings.delete_desc=删除仓库操作不可逆转,请三思而后行。
|
||||
settings.delete_notices_1=- 此操作 <strong>不可以</strong> 被回滚。
|
||||
settings.delete_notices_2=- 此操作将永久删除该仓库,包括 Git 数据、 工单、 评论和协作者的操作权限。
|
||||
settings.delete_notices_fork_1=- 如果该仓库为公开的,则在删除仓库后所有的派生仓库都将转换成独立的仓库。
|
||||
settings.delete_notices_fork_2=- 如果该仓库为私有,则会同时删除所有的派生仓库。
|
||||
settings.delete_notices_fork_3=- 如果您想要保留派生仓库,请先将可见性修改为公开的后再进行删除操作。
|
||||
settings.delete_notices_fork_1=- 删除完成后所有的派生仓库都将转换为独立的仓库。
|
||||
settings.deletion_success=仓库删除成功!
|
||||
settings.update_settings_success=仓库设置更新成功!
|
||||
settings.transfer_owner=新拥有者
|
||||
@@ -602,8 +694,12 @@ settings.transfer_succeed=仓库所有权转移成功!
|
||||
settings.confirm_delete=确认删除仓库
|
||||
settings.add_collaborator=增加新的协作者
|
||||
settings.add_collaborator_success=成功添加新的协作者!
|
||||
settings.delete_collaborator=删除
|
||||
settings.collaborator_deletion=删除协作者
|
||||
settings.collaborator_deletion_desc=此用户被删除后将不再拥有相关的协作权限。是否继续?
|
||||
settings.remove_collaborator_success=被操作的协作者已经被收回权限!
|
||||
settings.search_user_placeholder=搜索用户...
|
||||
settings.org_not_allowed_to_be_collaborator=组织不允许被添加为仓库协作者!
|
||||
settings.user_is_org_member=被操作的用户是组织成员,因此无法添加为协作者!
|
||||
settings.add_webhook=添加 Web 钩子
|
||||
settings.hooks_desc=Web 钩子允许您设定在 Gogs 上发生指定事件时对指定 URL 发送 POST 通知。查看 <a target="_blank" href="%s">Webhooks 文档</a> 获取更多信息。
|
||||
@@ -636,6 +732,8 @@ settings.event_send_everything=请把 <strong>一切</strong> 都给我
|
||||
settings.event_choose=我的命运自己主宰
|
||||
settings.event_create=创建
|
||||
settings.event_create_desc=创建分支或标签
|
||||
settings.event_pull_request=合并请求
|
||||
settings.event_pull_request_desc=开启、关闭、重新开启、编辑、指派、取消指派、更新标签、清除标签或同步合并请求
|
||||
settings.event_push=推送
|
||||
settings.event_push_desc=Git 仓库推送
|
||||
settings.active=是否激活
|
||||
@@ -673,6 +771,8 @@ diff.show_unified_view=合并视图
|
||||
diff.stats_desc=共有 <strong> %d 个文件被更改</strong>,包括 <strong>%d 次插入</strong> 和 <strong>%d 次删除</strong>
|
||||
diff.bin=二进制
|
||||
diff.view_file=查看文件
|
||||
diff.file_suppressed=文件差异内容过多而无法显示
|
||||
diff.too_many_files=部分文件因为文件数量过多而无法显示
|
||||
|
||||
release.releases=版本发布
|
||||
release.new_release=发布新版
|
||||
@@ -703,6 +803,7 @@ release.deletion=删除版本发布操作
|
||||
release.deletion_desc=删除该版本发布将会移除相应的 Git 标签。是否继续?
|
||||
release.deletion_success=版本发布删除成功!
|
||||
release.tag_name_already_exist=已经存在使用相同标签进行发布的版本。
|
||||
release.tag_name_invalid=标签名称不是有效的名称。
|
||||
release.downloads=下载附件
|
||||
|
||||
[org]
|
||||
@@ -818,6 +919,8 @@ dashboard.resync_all_sshkeys=重新生成 '.ssh/authorized_keys' 文件(警告
|
||||
dashboard.resync_all_sshkeys_success=所有公钥重新生成成功!
|
||||
dashboard.resync_all_update_hooks=重新生成所有仓库的 Update 钩子(用于自定义配置文件被修改)
|
||||
dashboard.resync_all_update_hooks_success=所有仓库的 Update 钩子重新生成成功!
|
||||
dashboard.reinit_missing_repos=重新初始化所有丢失 Git 文件的仓库
|
||||
dashboard.reinit_missing_repos_success=所有丢失 Git 文件的仓库重新初始化成功!
|
||||
|
||||
dashboard.server_uptime=服务运行时间
|
||||
dashboard.current_goroutine=当前 Goroutines 数量
|
||||
@@ -868,6 +971,7 @@ users.edit_account=编辑用户信息
|
||||
users.max_repo_creation=最大允许创建仓库数量
|
||||
users.max_repo_creation_desc=(设置为 -1 表示使用全局默认值)
|
||||
users.is_activated=该用户已被激活
|
||||
users.prohibit_login=该帐户被禁止登录
|
||||
users.is_admin=该用户具有管理员权限
|
||||
users.allow_git_hook=该用户具有创建 Git 钩子的权限
|
||||
users.allow_import_local=该用户具有导入本地仓库的权限
|
||||
@@ -898,6 +1002,7 @@ auths.enabled=已启用
|
||||
auths.updated=最后更新时间
|
||||
auths.auth_type=认证类型
|
||||
auths.auth_name=认证名称
|
||||
auths.security_protocol=安全协议
|
||||
auths.domain=域名
|
||||
auths.host=主机地址
|
||||
auths.port=主机端口
|
||||
@@ -911,6 +1016,7 @@ auths.attribute_username_placeholder=留空表示使用用户登录时所使用
|
||||
auths.attribute_name=名字属性
|
||||
auths.attribute_surname=姓氏属性
|
||||
auths.attribute_mail=邮箱属性
|
||||
auths.attributes_in_bind=从 Bind DN 中拉取属性信息
|
||||
auths.filter=用户过滤规则
|
||||
auths.admin_filter=管理员过滤规则
|
||||
auths.ms_ad_sa=Ms Ad SA
|
||||
@@ -932,7 +1038,9 @@ auths.update=更新认证设置
|
||||
auths.delete=删除该认证
|
||||
auths.delete_auth_title=删除认证操作
|
||||
auths.delete_auth_desc=该认证将被删除。是否继续?
|
||||
auths.still_in_used=此认证仍旧与一些用户有关联,请先删除或者将这些用户转换为其它登录类型。
|
||||
auths.deletion_success=授权源删除成功!
|
||||
auths.login_source_exist=登录源 '%s' 已存在。
|
||||
|
||||
config.server_config=服务器配置
|
||||
config.app_name=应用名称
|
||||
@@ -943,11 +1051,25 @@ config.offline_mode=离线模式
|
||||
config.disable_router_log=关闭路由日志
|
||||
config.run_user=运行用户
|
||||
config.run_mode=运行模式
|
||||
config.git_version=Git 版本
|
||||
config.repo_root_path=仓库根目录
|
||||
config.static_file_root_path=静态文件根目录
|
||||
config.log_file_root_path=日志文件根目录
|
||||
config.script_type=脚本类型
|
||||
config.reverse_auth_user=反向代理认证
|
||||
|
||||
config.ssh_config=SSH 配置
|
||||
config.ssh_enabled=启用服务
|
||||
config.ssh_start_builtin_server=启用内置服务
|
||||
config.ssh_domain=域名
|
||||
config.ssh_port=端口
|
||||
config.ssh_listen_port=监听端口
|
||||
config.ssh_root_path=根目录
|
||||
config.ssh_key_test_path=密钥测试路径
|
||||
config.ssh_keygen_path=密钥生成器('ssh-keygen')路径
|
||||
config.ssh_minimum_key_size_check=密钥最小长度检查
|
||||
config.ssh_minimum_key_sizes=密钥最小长度限制
|
||||
|
||||
config.db_config=数据库配置
|
||||
config.db_type=数据库类型
|
||||
config.db_host=主机地址
|
||||
@@ -957,33 +1079,41 @@ config.db_ssl_mode=SSL 模式
|
||||
config.db_ssl_mode_helper=(仅限 "postgres" 使用)
|
||||
config.db_path=数据库路径
|
||||
config.db_path_helper=(用于 "sqlite3" 和 "tidb")
|
||||
|
||||
config.service_config=服务配置
|
||||
config.register_email_confirm=注册邮件确认
|
||||
config.disable_register=关闭注册功能
|
||||
config.show_registration_button=显示注册按钮
|
||||
config.require_sign_in_view=强制登录浏览
|
||||
config.enable_cache_avatar=开启缓存头像
|
||||
config.mail_notify=邮件通知提醒
|
||||
config.disable_key_size_check=禁用密钥最小长度检查
|
||||
config.enable_captcha=启用验证码服务
|
||||
config.active_code_lives=激活用户链接有效期
|
||||
config.reset_password_code_lives=重置密码链接有效期
|
||||
|
||||
config.webhook_config=Web 钩子配置
|
||||
config.queue_length=队列长度
|
||||
config.deliver_timeout=推送超时
|
||||
config.skip_tls_verify=忽略 TLS 验证
|
||||
|
||||
config.mailer_config=邮件配置
|
||||
config.mailer_enabled=启用服务
|
||||
config.mailer_disable_helo=禁用 HELO 操作
|
||||
config.mailer_name=发送者名称
|
||||
config.mailer_host=邮件主机地址
|
||||
config.mailer_user=发送者帐号
|
||||
config.send_test_mail=发送测试邮件
|
||||
config.test_mail_failed=发送测试邮件至 '%s' 时失败:%v
|
||||
config.test_mail_sent=测试邮件已经发送至 '%s'。
|
||||
|
||||
config.oauth_config=社交帐号配置
|
||||
config.oauth_enabled=启用服务
|
||||
|
||||
config.cache_config=Cache 配置
|
||||
config.cache_adapter=Cache 适配器
|
||||
config.cache_interval=Cache 周期
|
||||
config.cache_conn=Cache 连接字符串
|
||||
|
||||
config.session_config=Session 配置
|
||||
config.session_provider=Session 提供者
|
||||
config.provider_config=提供者配置
|
||||
@@ -993,9 +1123,24 @@ config.gc_interval_time=GC 周期
|
||||
config.session_life_time=Session 生命周期
|
||||
config.https_only=仅限 HTTPS
|
||||
config.cookie_life_time=Cookie 生命周期
|
||||
|
||||
config.picture_config=图片配置
|
||||
config.picture_service=图片服务
|
||||
config.disable_gravatar=禁用 Gravatar 头像
|
||||
config.enable_federated_avatar=启用 Federated Avatars
|
||||
|
||||
config.git_config=Git 配置
|
||||
config.git_disable_diff_highlight=禁用差异对比语法高亮
|
||||
config.git_max_diff_lines=差异对比显示的最大行数(单个文件)
|
||||
config.git_max_diff_line_characters=差异对比显示的最大字符数(单行)
|
||||
config.git_max_diff_files=差异对比显示的最大文件数
|
||||
config.git_gc_args=GC 参数
|
||||
config.git_migrate_timeout=迁移操作超时
|
||||
config.git_mirror_timeout=镜像更新操作超时
|
||||
config.git_clone_timeout=克隆操作超时
|
||||
config.git_pull_timeout=拉取操作超时
|
||||
config.git_gc_timeout=GC 操作超时
|
||||
|
||||
config.log_config=日志配置
|
||||
config.log_mode=日志模式
|
||||
|
||||
@@ -1029,7 +1174,11 @@ create_repo=创建了仓库 <a href="%s">%s</a>
|
||||
rename_repo=重命名仓库 <code>%[1]s</code> 为 <a href="%[2]s">%[3]s</a>
|
||||
commit_repo=推送了 <a href="%[1]s/src/%[2]s">%[3]s</a> 分支的代码到 <a href="%[1]s">%[4]s</a>
|
||||
create_issue=`创建了工单 <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
close_issue=`关闭了工单 <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
reopen_issue=`重新开启了工单 <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
create_pull_request=`创建了合并请求 <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
close_pull_request=`关闭了合并请求 <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
reopen_pull_request=`重新开启了合并请求 <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
comment_issue=`评论了工单 <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
merge_pull_request=`合并了合并请求 <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
transfer_repo=将仓库 <code>%s</code> 转移至 <a href="%s">%s</a>
|
||||
|
||||
217
conf/locale/locale_zh-HK.ini
Executable file → Normal file
217
conf/locale/locale_zh-HK.ini
Executable file → Normal file
@@ -38,19 +38,12 @@ settings=設定
|
||||
your_profile=個人資料
|
||||
your_settings=用戶設定
|
||||
|
||||
news_feed=動態消息
|
||||
activities=活動
|
||||
pull_requests=合併請求
|
||||
issues=問題
|
||||
|
||||
cancel=取消
|
||||
|
||||
[search]
|
||||
search=搜尋...
|
||||
repository=倉庫
|
||||
user=用戶
|
||||
issue=工單
|
||||
code=程式碼
|
||||
|
||||
[install]
|
||||
install=安裝頁面
|
||||
title=首次安裝步驟
|
||||
@@ -65,7 +58,7 @@ db_name=資料庫名稱
|
||||
db_helper=如果您使用 MySQL,請使用 INNODB 引擎以及 utf8_general_ci 字符集。
|
||||
ssl_mode=SSL 模式
|
||||
path=數據庫文件路徑
|
||||
sqlite_helper=SQLite3 或 TiDB 的數據庫路徑。
|
||||
sqlite_helper=SQLite3 或 TiDB 資料庫檔案路徑。<br>作為以服務執行時,請使用絕對路徑。
|
||||
err_empty_db_path=SQLite3 或 TiDB 的數據庫路徑不能為空。
|
||||
err_invalid_tidb_name=TiDB 數據庫名稱不允許包含字符 "." 或 "-" 。
|
||||
no_admin_and_disable_registration=您不能夠在未創建管理員用戶的情況下禁止註冊。
|
||||
@@ -86,6 +79,8 @@ http_port=HTTP 端口號
|
||||
http_port_helper=應用監聽的端口號
|
||||
app_url=應用程式網址
|
||||
app_url_helper=該設置影響 HTTP/HTTPS 複製地址和一些郵箱中的連結。
|
||||
log_root_path=日誌路徑
|
||||
log_root_path_helper=寫入日誌檔目錄
|
||||
|
||||
optional_title=可選設置
|
||||
email_title=電子郵件服務設定
|
||||
@@ -101,6 +96,8 @@ offline_mode=啓用離線模式
|
||||
offline_mode_popup=在部署模式下也禁用從 CDN 獲取文件,所有的資源將從本地伺服器獲取。
|
||||
disable_gravatar=禁用 Gravatar 服務
|
||||
disable_gravatar_popup=禁用 Gravatar 和自定義源,僅使用由用戶上傳或默認的頭像。
|
||||
federated_avatar_lookup=Enable Federated Avatars Lookup
|
||||
federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
|
||||
disable_registration=禁止用戶自主註冊
|
||||
disable_registration_popup=禁止用戶自主註冊功能,只有管理員可以添加帳號。
|
||||
enable_captcha=啟用驗證碼服務
|
||||
@@ -119,15 +116,18 @@ sqlite3_not_available=您所使用的發行版本不支持 SQLite3,請從 %s
|
||||
invalid_db_setting=數據庫設置不正確:%v
|
||||
invalid_repo_path=倉庫根目錄設置不正確:%v
|
||||
run_user_not_match=執行系統用戶非當前用戶:%s -> %s
|
||||
invalid_smtp_from=SMTP From field is not valid: %v
|
||||
save_config_failed=應用配置保存失敗:%v
|
||||
invalid_admin_setting=管理員帳戶設置不正確:%v
|
||||
install_success=您好!我們很高興您選擇使用 Gogs,祝您使用愉快,代碼從此無 BUG!
|
||||
invalid_log_root_path=日誌根目錄無效: %v
|
||||
|
||||
[home]
|
||||
uname_holder=用戶名或郵箱
|
||||
password_holder=密碼
|
||||
switch_dashboard_context=切換控制面版用戶
|
||||
my_repos=我的倉庫
|
||||
show_more_repos=Show more repositories...
|
||||
collaborative_repos=參與協作的倉庫
|
||||
my_orgs=我的組織
|
||||
my_mirrors=我的鏡像
|
||||
@@ -137,6 +137,9 @@ issues.in_your_repos=屬於該用戶倉庫的
|
||||
|
||||
[explore]
|
||||
repos=探索倉庫
|
||||
users=用戶
|
||||
organizations=Organizations
|
||||
search=搜索
|
||||
|
||||
[auth]
|
||||
create_new_account=創建帳戶
|
||||
@@ -150,15 +153,17 @@ forget_password=忘記密碼?
|
||||
sign_up_now=還沒帳戶?馬上註冊。
|
||||
confirmation_mail_sent_prompt=一封新的確認郵件已經被發送至 <b>%s</b>,請檢查您的收件箱並在 %d 小時內完成確認註冊操作。
|
||||
active_your_account=激活您的帳戶
|
||||
prohibit_login=Login Prohibited
|
||||
prohibit_login_desc=Your account is prohibited to login, please contact site admin.
|
||||
resent_limit_prompt=對不起,您請求發送激活郵件過於頻繁,請等待 3 分鐘後再試!
|
||||
has_unconfirmed_mail=%s 您好,您有一封發送至( <b>%s</b>) 但未被確認的郵件。如果您未收到激活郵件,或需要重新發送,請單擊下方的按鈕。
|
||||
resend_mail=單擊此處重新發送確認郵件
|
||||
email_not_associate=您輸入的郵箱地址未被關聯到任何帳號!
|
||||
send_reset_mail=單擊此處(重新)發送您的密碼重置郵件
|
||||
reset_password=重置密碼
|
||||
invalid_code=對不起,您的確認代碼已過期或已失效。
|
||||
reset_password_helper=單擊此處重置密碼
|
||||
password_too_short=密碼長度不能少於 6 位!
|
||||
non_local_account=Non-local accounts cannot change passwords through Gogs.
|
||||
|
||||
[mail]
|
||||
activate_account=請激活您的帳戶
|
||||
@@ -185,6 +190,13 @@ TeamName=團隊名稱
|
||||
AuthName=認證名稱
|
||||
AdminEmail=管理員郵箱
|
||||
|
||||
NewBranchName=New branch name
|
||||
CommitSummary=Commit summary
|
||||
CommitMessage=Commit message
|
||||
CommitChoice=Commit choice
|
||||
TreeName=File path
|
||||
Content=Content
|
||||
|
||||
require_error=不能為空。
|
||||
alpha_dash_error=必須為英文字母、阿拉伯數字或橫線(-_)。
|
||||
alpha_dash_dot_error=必須為英文字母、阿拉伯數字、橫線(-_)或點。
|
||||
@@ -203,7 +215,6 @@ repo_name_been_taken=倉庫名稱已經被佔用。
|
||||
org_name_been_taken=組織名稱已經被佔用。
|
||||
team_name_been_taken=團隊名稱已經被佔用。
|
||||
email_been_used=郵箱地址已經被使用。
|
||||
illegal_team_name=團隊名稱包含不合法字符。
|
||||
username_password_incorrect=用戶名或密碼不正確。
|
||||
enterred_invalid_repo_name=請檢查您輸入的倉庫名稱是正確。
|
||||
enterred_invalid_owner_name=請檢查您輸入的新所有者用戶名是否正確。
|
||||
@@ -219,13 +230,10 @@ still_own_repo=您的帳戶仍然是某些倉庫的擁有者,您必須先轉
|
||||
still_has_org=您的帳戶仍舊是某些組織的成員,您必須先離開或刪除組織。
|
||||
org_still_own_repo=該組織仍然是某些倉庫的擁有者,您必須先轉移或刪除它們才能執行刪除組織操作!
|
||||
|
||||
still_own_user=該授權認證依舊被部分用戶使用,請先刪除該部分用戶後再試!
|
||||
|
||||
target_branch_not_exist=目標分支不存在
|
||||
|
||||
[user]
|
||||
change_avatar=到 gravatar.com 上修改您的頭像
|
||||
change_custom_avatar=到個人設置中修改頭像
|
||||
change_avatar=Change your avatar
|
||||
join_on=加入於
|
||||
repositories=倉庫列表
|
||||
activity=公開活動
|
||||
@@ -241,6 +249,7 @@ form.name_pattern_not_allowed=用戶名不允許 '%s' 的格式。
|
||||
[settings]
|
||||
profile=個人信息
|
||||
password=修改密碼
|
||||
avatar=Avatar
|
||||
ssh_keys=管理 SSH 密鑰
|
||||
social=社交帳號綁定
|
||||
applications=管理授權應用
|
||||
@@ -261,12 +270,13 @@ change_username_prompt=該操作將會影響到所有與您帳戶有關的鏈接
|
||||
continue=繼續操作
|
||||
cancel=取消操作
|
||||
|
||||
lookup_avatar_by_mail=Lookup Avatar by mail
|
||||
federated_avatar_lookup=Federated Avatar Lookup
|
||||
enable_custom_avatar=啟動自定義頭像
|
||||
enable_custom_avatar_helper=激活該選項來禁止從 Gravatar 獲取頭像
|
||||
choose_new_avatar=選擇新的頭像
|
||||
update_avatar=更新頭像設置
|
||||
delete_current_avatar=刪除當前頭像
|
||||
uploaded_avatar_not_a_image=上傳的文件不是一張圖片!
|
||||
no_custom_avatar_available=沒有任何自定義頭像,無法激活該選項。
|
||||
update_avatar_success=您的頭像設置更新成功!
|
||||
|
||||
change_password=修改密碼
|
||||
@@ -328,6 +338,10 @@ access_token_deletion=刪除個人的連接token
|
||||
access_token_deletion_desc=刪除此連接token將會刪除與相關應用程式的連結。您想要繼續嗎?
|
||||
delete_token_success=您的連接token已成功刪除。請記得更新您的應用程式。
|
||||
|
||||
orgs.none=You are not a member of any organizations.
|
||||
orgs.leave_title=Leave an organization
|
||||
orgs.leave_desc=You will lose access to all repositories and teams after you left the organization. Do you want to continue?
|
||||
|
||||
delete_account=刪除當前帳戶
|
||||
delete_prompt=刪除操作會永久清除您的帳戶信息,並且 <strong>不可恢復</strong>!
|
||||
confirm_delete_account=確認刪除帳戶
|
||||
@@ -348,7 +362,7 @@ fork_from=派生自
|
||||
fork_visiblity_helper=派生倉庫無法修改可見性。
|
||||
repo_desc=倉庫描述
|
||||
repo_lang=倉庫語言
|
||||
repo_lang_helper=請選擇 .gitignore 文件
|
||||
repo_gitignore_helper=Select .gitignore templates
|
||||
license=授權許可
|
||||
license_helper=請選擇授權許可文件
|
||||
readme=Readme
|
||||
@@ -356,9 +370,12 @@ readme_helper=請選擇readme模板
|
||||
auto_init=使用選定的文件和模板初始化倉庫
|
||||
create_repo=創建倉庫
|
||||
default_branch=默認分支
|
||||
mirror_prune=Prune
|
||||
mirror_prune_desc=Remove any remote-tracking references that no longer exist on the remote
|
||||
mirror_interval=鏡像同步周期(小時)
|
||||
mirror_address=鏡像地址
|
||||
mirror_address_desc=請在位址中包括必要的使用者憑據。
|
||||
mirror_last_synced=Last Synced
|
||||
watchers=關注者
|
||||
stargazers=稱讚者
|
||||
forks=派生倉庫
|
||||
@@ -372,7 +389,8 @@ migrate_type=遷移類型
|
||||
migrate_type_helper=該倉庫將是一個 <span class="text blue">鏡像</span>
|
||||
migrate_repo=遷移倉庫
|
||||
migrate.clone_address=複製地址
|
||||
migrate.clone_address_desc=該地址可以是 HTTP/HTTPS/GIT URL 或本地服務器路徑。
|
||||
migrate.clone_address_desc=This can be a HTTP/HTTPS/GIT URL.
|
||||
migrate.clone_address_desc_import_local=You're also allowed to migrate a repository by local server path.
|
||||
migrate.permission_denied=您並沒有導入本地倉庫的權限。
|
||||
migrate.invalid_local_path=無效的本地路徑,該路徑不存在或不是一個目錄!
|
||||
migrate.failed=遷移失敗:%v
|
||||
@@ -388,7 +406,7 @@ unwatch=取消關注
|
||||
watch=關註
|
||||
unstar=取消讚好
|
||||
star=讚好
|
||||
fork=派生
|
||||
fork=複刻
|
||||
|
||||
no_desc=暫無描述
|
||||
quick_guide=快速幫助
|
||||
@@ -397,7 +415,7 @@ create_new_repo_command=從命令行創建一個新的倉庫
|
||||
push_exist_repo=從命令行推送已經創建的倉庫
|
||||
repo_is_empty=這倉庫不包含任何內容,請稍後再訪問!
|
||||
|
||||
code=代碼
|
||||
files=Files
|
||||
branch=分支
|
||||
tree=目錄樹
|
||||
filter_branch_and_tag=過濾分支或標籤
|
||||
@@ -413,6 +431,47 @@ file_raw=原始文件
|
||||
file_history=文件歷史
|
||||
file_view_raw=查看原始文件
|
||||
file_permalink=永久連結
|
||||
file_too_large=This file is too large to be shown
|
||||
video_not_supported_in_browser=Your browser doesn't support HTML5 video tag.
|
||||
|
||||
editor.new_file=New file
|
||||
editor.upload_file=Upload file
|
||||
editor.edit_file=Edit file
|
||||
editor.preview_changes=Preview Changes
|
||||
editor.cannot_edit_non_text_files=Cannot edit non-text files
|
||||
editor.edit_this_file=Edit this file
|
||||
editor.must_be_on_a_branch=You must be on a branch to make or propose changes to this file
|
||||
editor.fork_before_edit=You must fork this repository before editing the file
|
||||
editor.delete_this_file=Delete this file
|
||||
editor.must_have_write_access=You must have write access to make or propose changes to this file
|
||||
editor.file_delete_success=File '%s' has been deleted successfully!
|
||||
editor.name_your_file=Name your file...
|
||||
editor.filename_help=To add directory, just type it and press /. To remove a directory, go to the beginning of the field and press backspace.
|
||||
editor.or=or
|
||||
editor.cancel_lower=cancel
|
||||
editor.commit_changes=Commit Changes
|
||||
editor.add_tmpl=Add '%s/<filename>'
|
||||
editor.add=Add '%s'
|
||||
editor.update=Update '%s'
|
||||
editor.delete=Delete '%s'
|
||||
editor.commit_message_desc=Add an optional extended description...
|
||||
editor.commit_directly_to_this_branch=Commit directly to the <strong class="branch-name">%s</strong> branch.
|
||||
editor.create_new_branch=Create a <strong>new branch</strong> for this commit and start a pull request.
|
||||
editor.new_branch_name_desc=New branch name...
|
||||
editor.cancel=Cancel
|
||||
editor.filename_cannot_be_empty=Filename cannot be empty.
|
||||
editor.branch_already_exists=Branch '%s' already exists in this repository.
|
||||
editor.directory_is_a_file=Entry '%s' in the parent path is a file not a directory in this repository.
|
||||
editor.file_is_a_symlink=The file '%s' is a symlink that cannot be modified from the web editor.
|
||||
editor.filename_is_a_directory=The filename '%s' is an existing directory in this repository.
|
||||
editor.file_editing_no_longer_exists=The file '%s' you are editing no longer exists in the repository.
|
||||
editor.file_changed_while_editing=File content has been changed since you started editing. <a target="_blank" href="%s">Click here</a> to see what have been changed or <strong>press commit again</strong> to overwrite those changes.
|
||||
editor.file_already_exists=A file with name '%s' already exists in this repository.
|
||||
editor.no_changes_to_show=There are no changes to show.
|
||||
editor.fail_to_update_file=Failed to update/create file '%s' with error: %v
|
||||
editor.add_subdir=Add subdirectory...
|
||||
editor.unable_to_upload_files=Failed to upload files to '%s' with error: %v
|
||||
editor.upload_files_to_dir=Upload files to '%s'
|
||||
|
||||
commits.commits=次代碼提交
|
||||
commits.search=搜索提交歷史
|
||||
@@ -439,6 +498,11 @@ issues.create=創建問題
|
||||
issues.new_label=創建標籤
|
||||
issues.new_label_placeholder=標籤名稱...
|
||||
issues.create_label=創建標籤
|
||||
issues.label_templates.title=Load a predefined set of labels
|
||||
issues.label_templates.info=There aren’t any labels yet. You can click on the "New Label" button above to create one or use a predefined set below.
|
||||
issues.label_templates.helper=Select a label set
|
||||
issues.label_templates.use=Use this label set
|
||||
issues.label_templates.fail_to_load_file=Failed to load label template file '%s': %v
|
||||
issues.open_tab=%d 個開啓中
|
||||
issues.close_tab=%d 個已關閉
|
||||
issues.filter_label=標籤篩選
|
||||
@@ -466,7 +530,8 @@ issues.next=下一頁
|
||||
issues.open_title=開啟中
|
||||
issues.closed_title=已關閉
|
||||
issues.num_comments=%d 條評論
|
||||
issues.commented_at=`於 <a id="%[1]s" href="#%[1]s">%[2]s</a> 評論`
|
||||
issues.commented_at=`commented <a href="#%s">%s</a>`
|
||||
issues.delete_comment_confirm=Are you sure you want to delete this comment?
|
||||
issues.no_content=尚未有任何內容
|
||||
issues.close_issue=關閉
|
||||
issues.close_comment_issue=關閉及評論
|
||||
@@ -477,10 +542,9 @@ issues.closed_at=`於 <a id="%[1]s" href="#%[1]s">%[2]s</a> 關閉`
|
||||
issues.reopened_at=`於 <a id="%[1]s" href="#%[1]s">%[2]s</a> 重新開啟`
|
||||
issues.commit_ref_at=`在代碼提交 <a id="%[1]s" href="#%[1]s">%[2]s</a> 中引用了該問題`
|
||||
issues.poster=發佈者
|
||||
issues.admin=管理員
|
||||
issues.collaborator=協同者
|
||||
issues.owner=所有者
|
||||
issues.sign_up_for_free=免費註冊
|
||||
issues.sign_in_require_desc=及加入到對話當中。如果您已經註冊,可以直接 <a href="%s">登錄及評論</a>
|
||||
issues.sign_in_require_desc=<a href="%s">Sign in</a> to join this conversation.
|
||||
issues.edit=編輯
|
||||
issues.cancel=取消
|
||||
issues.save=保存
|
||||
@@ -494,6 +558,9 @@ issues.label_modify=修改標籤
|
||||
issues.label_deletion=刪除標籤
|
||||
issues.label_deletion_desc=刪除該標籤將會移除所有問題中相關的訊息。是否繼續?
|
||||
issues.label_deletion_success=標籤刪除成功!
|
||||
issues.num_participants=%d 參與者
|
||||
issues.attachment.open_tab=`Click to see "%s" in a new tab`
|
||||
issues.attachment.download=`Click to download "%s"`
|
||||
|
||||
pulls.new=創建合併請求
|
||||
pulls.compare_changes=對比文件變化
|
||||
@@ -520,6 +587,8 @@ pulls.cannot_auto_merge_desc=由於存在衝突,不能自動合併這推送請
|
||||
pulls.cannot_auto_merge_helper=請手動合併來解決衝突。
|
||||
pulls.merge_pull_request=合併請求
|
||||
pulls.open_unmerged_pull_exists=`由於已經存在來自相同倉庫和合併信息的未合併請求(#%d),您無法執行重新開啟操作。`
|
||||
pulls.delete_branch=Delete Branch
|
||||
pulls.delete_branch_has_new_commits=Branch cannot be deleted because it has new commits after mergence.
|
||||
|
||||
milestones.new=新的里程碑
|
||||
milestones.open_tab=%d 開啟中
|
||||
@@ -557,6 +626,8 @@ wiki.save_page=保存頁面
|
||||
wiki.last_commit_info=%s 於 %s 修改了此頁面
|
||||
wiki.edit_page_button=修改
|
||||
wiki.new_page_button=新的頁面
|
||||
wiki.delete_page_button=刪除頁面
|
||||
wiki.delete_page_notice_1=這將刪除頁面 <code>"%s"</code>。請三思而後行。
|
||||
wiki.page_already_exists=相同名稱的 Wiki 頁面已經存在。
|
||||
wiki.pages=所有頁面
|
||||
wiki.last_updated=最後更新於 %s
|
||||
@@ -564,36 +635,57 @@ wiki.last_updated=最後更新於 %s
|
||||
settings=倉庫設置
|
||||
settings.options=基本設置
|
||||
settings.collaboration=管理協作者
|
||||
settings.collaboration.admin=Admin
|
||||
settings.collaboration.write=Write
|
||||
settings.collaboration.read=Read
|
||||
settings.collaboration.undefined=Undefined
|
||||
settings.hooks=管理 Web 鉤子
|
||||
settings.githooks=管理 Git 鉤子
|
||||
settings.basic_settings=基本設置
|
||||
settings.mirror_settings=Mirror Settings
|
||||
settings.sync_mirror=Sync Now
|
||||
settings.mirror_sync_in_progress=Mirror syncing is in progress, please refresh page in about a minute.
|
||||
settings.site=官方網站
|
||||
settings.update_settings=更新倉庫設置
|
||||
settings.change_reponame_prompt=該操作將會影響到所有與該倉庫有關的鏈接
|
||||
settings.advanced_settings=高級設置
|
||||
settings.wiki_desc=啟用 Wiki 以允許用戶協作文檔
|
||||
settings.wiki_desc=Enable wiki system
|
||||
settings.use_internal_wiki=Use builtin wiki
|
||||
settings.use_external_wiki=使用外部 wiki
|
||||
settings.external_wiki_url=外部 Wiki 連結
|
||||
settings.external_wiki_url_desc=當分頁上按一下,訪客將會重新導到 URL。
|
||||
settings.issues_desc=啟用內置的輕量級問題管理系統
|
||||
settings.issues_desc=Enable issue tracker
|
||||
settings.use_internal_issue_tracker=Use builtin lightweight issue tracker
|
||||
settings.use_external_issue_tracker=使用外部的問題管理系統
|
||||
settings.external_tracker_url=External Issue Tracker URL
|
||||
settings.external_tracker_url_desc=Visitors will be redirected to URL when they click on the tab.
|
||||
settings.tracker_url_format=外部問題管理系統的 URL 格式
|
||||
settings.tracker_issue_style=External Issue Tracker Naming Style:
|
||||
settings.tracker_issue_style.numeric=Numeric
|
||||
settings.tracker_issue_style.alphanumeric=Alphanumeric
|
||||
settings.tracker_url_format_desc=您可以使用 <code>{user} {repo} {index}</code> 分別作為用戶名、倉庫名和問題索引的占位符。
|
||||
settings.pulls_desc=啟用合併請求以接受社區貢獻
|
||||
settings.danger_zone=危險操作區
|
||||
settings.new_owner_has_same_repo=新的倉庫擁有者已經存在同名倉庫!
|
||||
settings.convert=轉換為正規倉庫
|
||||
settings.convert_desc=您可以將此鏡像轉成正規倉庫。此動做不可逆。
|
||||
settings.convert_notices_1=- This operation will convert this repository mirror into a regular repository and cannot be undone.
|
||||
settings.convert_confirm=Confirm Conversion
|
||||
settings.convert_succeed=Repository has been converted to regular type successfully.
|
||||
settings.transfer=轉移倉庫所有權
|
||||
settings.transfer_desc=您可以將倉庫轉移至您擁有管理員權限的帳戶或組織。
|
||||
settings.new_owner_has_same_repo=新的倉庫擁有者已經存在同名倉庫!
|
||||
settings.delete=刪除本倉庫
|
||||
settings.delete_desc=刪除倉庫操作不可逆轉,請三思而後行。
|
||||
settings.transfer_notices_1=- 如果您將倉庫轉移給個人用戶,您將會丟失操作權限。
|
||||
settings.transfer_notices_2=- 如果您將倉庫轉移給您是所有者的組織,您的操作權限將被保留。
|
||||
settings.transfer_form_title=請輸入以下信息以確認您的操作:
|
||||
settings.wiki_delete=刪除 Wiki 資料
|
||||
settings.wiki_delete_desc=Once you erase wiki data there is no going back. Please be certain.
|
||||
settings.wiki_delete_notices_1=- 將刪除和停用 %s 的 wiki
|
||||
settings.wiki_deletion_success=Repository wiki data have been erased successfully.
|
||||
settings.delete=刪除本倉庫
|
||||
settings.delete_desc=刪除倉庫操作不可逆轉,請三思而後行。
|
||||
settings.delete_notices_1=- 此操作 <strong>不可以</strong> 被回滾。
|
||||
settings.delete_notices_2=- 此操作將永久刪除該倉庫,包括 Git 數據、 問題、 評論和協作者的操作權限。
|
||||
settings.delete_notices_fork_1=- 如果該倉庫為公開的,則在刪除倉庫後所有的派生倉庫都將轉換成獨立的倉庫。
|
||||
settings.delete_notices_fork_2=- 如果該倉庫為私有,則會同時刪除所有的派生倉庫。
|
||||
settings.delete_notices_fork_3=- 如果您想要保留派生倉庫,請先將可見性修改為公開的後再進行刪除操作。
|
||||
settings.delete_notices_fork_1=- All forks will become independent after deletion.
|
||||
settings.deletion_success=倉庫刪除成功!
|
||||
settings.update_settings_success=倉庫設置更新成功!
|
||||
settings.transfer_owner=新擁有者
|
||||
@@ -602,8 +694,12 @@ settings.transfer_succeed=倉庫所有權轉移成功!
|
||||
settings.confirm_delete=確認刪除倉庫
|
||||
settings.add_collaborator=增加新的協作者
|
||||
settings.add_collaborator_success=成功添加新的協作者!
|
||||
settings.delete_collaborator=刪除
|
||||
settings.collaborator_deletion=Collaborator Deletion
|
||||
settings.collaborator_deletion_desc=This user will no longer have collaboration access to this repository after deletion. Do you want to continue?
|
||||
settings.remove_collaborator_success=被操作的協作者已經被收回權限!
|
||||
settings.search_user_placeholder=搜索用戶...
|
||||
settings.org_not_allowed_to_be_collaborator=Organization is not allowed to be added as a collaborator.
|
||||
settings.user_is_org_member=被操作的用戶是組織成員,因此無法添加為協作者!
|
||||
settings.add_webhook=添加 Web 鉤子
|
||||
settings.hooks_desc=Web 鉤子允許您設定在 Gogs 上發生指定事件時對指定 URL 發送 POST 通知。查看 <a target="_blank" href="%s">Webhooks 文檔</a> 獲取更多信息。
|
||||
@@ -636,6 +732,8 @@ settings.event_send_everything=推送 <strong>所有</strong> 事件
|
||||
settings.event_choose=讓我選擇我的需要
|
||||
settings.event_create=創建
|
||||
settings.event_create_desc=創建分支或標籤
|
||||
settings.event_pull_request=Pull Request
|
||||
settings.event_pull_request_desc=Pull request opened, closed, reopened, edited, assigned, unassigned, label updated, label cleared, or synchronized.
|
||||
settings.event_push=推送
|
||||
settings.event_push_desc=Git 倉庫推送
|
||||
settings.active=是否激活
|
||||
@@ -673,6 +771,8 @@ diff.show_unified_view=統一視圖
|
||||
diff.stats_desc=共有 <strong> %d 個文件被更改</strong>,包括 <strong>%d 次插入</strong> 和 <strong>%d 次删除</strong>
|
||||
diff.bin=二進制
|
||||
diff.view_file=查看文件
|
||||
diff.file_suppressed=File diff suppressed because it is too large
|
||||
diff.too_many_files=Some files were not shown because too many files changed in this diff
|
||||
|
||||
release.releases=版本發佈
|
||||
release.new_release=發佈新版本
|
||||
@@ -703,6 +803,7 @@ release.deletion=刪除版本發布操作
|
||||
release.deletion_desc=刪除該版本發布將會移除相應的 Git 標籤。是否繼續?
|
||||
release.deletion_success=版本發布刪除成功!
|
||||
release.tag_name_already_exist=已經存在使用相同標籤的發佈版本。
|
||||
release.tag_name_invalid=Tag name is not valid.
|
||||
release.downloads=下載附件
|
||||
|
||||
[org]
|
||||
@@ -818,6 +919,8 @@ dashboard.resync_all_sshkeys=重新生成 '.ssh/authorized_keys' 文件(警告
|
||||
dashboard.resync_all_sshkeys_success=所有公鑰重新生成成功!
|
||||
dashboard.resync_all_update_hooks=重新生成所有倉庫的 Update 鈎子(用於被修改的自定義配置文件)
|
||||
dashboard.resync_all_update_hooks_success=已成功重新生成所有倉庫的 Update 鈎子!
|
||||
dashboard.reinit_missing_repos=Reinitialize all repository records that lost Git files
|
||||
dashboard.reinit_missing_repos_success=All repository records that lost Git files have been reinitialized successfully.
|
||||
|
||||
dashboard.server_uptime=服務執行時間
|
||||
dashboard.current_goroutine=當前 Goroutines 數量
|
||||
@@ -868,6 +971,7 @@ users.edit_account=編輯用戶信息
|
||||
users.max_repo_creation=最大儲存庫新增限制
|
||||
users.max_repo_creation_desc=(設定 -1 使用全域預設限制)
|
||||
users.is_activated=該用戶已被激活
|
||||
users.prohibit_login=This account is prohibited to login
|
||||
users.is_admin=該用戶具有管理員權限
|
||||
users.allow_git_hook=該帳戶具有創建 Git 鉤子的權限
|
||||
users.allow_import_local=該用戶具有導入本地倉庫的權限
|
||||
@@ -898,6 +1002,7 @@ auths.enabled=已啟用
|
||||
auths.updated=最後更新時間
|
||||
auths.auth_type=認證類型
|
||||
auths.auth_name=認證名稱
|
||||
auths.security_protocol=Security Protocol
|
||||
auths.domain=域名
|
||||
auths.host=主機地址
|
||||
auths.port=主機端口
|
||||
@@ -911,6 +1016,7 @@ auths.attribute_username_placeholder=留空表示使用用戶登錄時所使用
|
||||
auths.attribute_name=名子屬性
|
||||
auths.attribute_surname=姓氏屬性
|
||||
auths.attribute_mail=電子郵箱屬性
|
||||
auths.attributes_in_bind=Fetch attributes in Bind DN context
|
||||
auths.filter=使用者篩選器
|
||||
auths.admin_filter=管理者篩選器
|
||||
auths.ms_ad_sa=Ms Ad SA
|
||||
@@ -932,7 +1038,9 @@ auths.update=更新認證設置
|
||||
auths.delete=刪除該認證
|
||||
auths.delete_auth_title=刪除認證操作
|
||||
auths.delete_auth_desc=該認證將被刪除。是否繼續?
|
||||
auths.still_in_used=This authentication is still used by some users, please delete or convert these users to another login type first.
|
||||
auths.deletion_success=認證源刪除成功!
|
||||
auths.login_source_exist=Login source '%s' already exists.
|
||||
|
||||
config.server_config=服務器配置
|
||||
config.app_name=應用名稱
|
||||
@@ -943,11 +1051,25 @@ config.offline_mode=離線模式
|
||||
config.disable_router_log=關閉路由日志
|
||||
config.run_user=執行用戶
|
||||
config.run_mode=執行模式
|
||||
config.git_version=Git Version
|
||||
config.repo_root_path=倉庫根目錄
|
||||
config.static_file_root_path=靜態文件根目錄
|
||||
config.log_file_root_path=日志文件根目錄
|
||||
config.script_type=腳本類型
|
||||
config.reverse_auth_user=反向代理認證
|
||||
|
||||
config.ssh_config=SSH 配置
|
||||
config.ssh_enabled=已啟用
|
||||
config.ssh_start_builtin_server=啟動內建伺服器
|
||||
config.ssh_domain=Domain
|
||||
config.ssh_port=埠
|
||||
config.ssh_listen_port=監聽埠
|
||||
config.ssh_root_path=根路徑
|
||||
config.ssh_key_test_path=金鑰測試路徑
|
||||
config.ssh_keygen_path=金鑰產生 (' ssh-keygen ') 路徑
|
||||
config.ssh_minimum_key_size_check=金鑰最小大小檢查
|
||||
config.ssh_minimum_key_sizes=金鑰最小大小
|
||||
|
||||
config.db_config=數據庫配置
|
||||
config.db_type=數據庫類型
|
||||
config.db_host=主機地址
|
||||
@@ -957,33 +1079,41 @@ config.db_ssl_mode=SSL 模式
|
||||
config.db_ssl_mode_helper=(僅限 "postgres" 使用)
|
||||
config.db_path=數據庫路徑
|
||||
config.db_path_helper=(用於 "sqlite3" 和 "tidb")
|
||||
|
||||
config.service_config=服務配置
|
||||
config.register_email_confirm=註冊電子郵件確認
|
||||
config.disable_register=關閉註冊功能
|
||||
config.show_registration_button=顯示註冊按鈕
|
||||
config.require_sign_in_view=強制登錄瀏覽
|
||||
config.enable_cache_avatar=開啟緩存頭像
|
||||
config.mail_notify=郵件通知提醒
|
||||
config.disable_key_size_check=禁用密鑰最小長度檢查
|
||||
config.enable_captcha=啟用驗證碼服務
|
||||
config.active_code_lives=激活用戶連結有效期
|
||||
config.reset_password_code_lives=重置密碼連結有效期
|
||||
|
||||
config.webhook_config=Web 鉤子配置
|
||||
config.queue_length=隊列長度
|
||||
config.deliver_timeout=推送超時
|
||||
config.skip_tls_verify=忽略 TLS 驗證
|
||||
|
||||
config.mailer_config=郵件配置
|
||||
config.mailer_enabled=啟用服務
|
||||
config.mailer_disable_helo=禁用 HELO 操作
|
||||
config.mailer_name=發送者名稱
|
||||
config.mailer_host=郵件主機地址
|
||||
config.mailer_user=發送者帳號
|
||||
config.send_test_mail=發送測試郵件
|
||||
config.test_mail_failed=無法向 '%s' 發送測試郵件: %v
|
||||
config.test_mail_sent=測試電子郵件已發送到 '%s'。
|
||||
|
||||
config.oauth_config=社交帳號配置
|
||||
config.oauth_enabled=啟用服務
|
||||
|
||||
config.cache_config=Cache 配置
|
||||
config.cache_adapter=Cache 適配器
|
||||
config.cache_interval=Cache 周期
|
||||
config.cache_conn=Cache 連接字符串
|
||||
|
||||
config.session_config=Session 配置
|
||||
config.session_provider=Session 提供者
|
||||
config.provider_config=提供者配置
|
||||
@@ -993,9 +1123,24 @@ config.gc_interval_time=垃圾收集周期
|
||||
config.session_life_time=Session 生命周期
|
||||
config.https_only=僅限 HTTPS
|
||||
config.cookie_life_time=Cookie 生命周期
|
||||
|
||||
config.picture_config=圖片配置
|
||||
config.picture_service=圖片服務
|
||||
config.disable_gravatar=禁用 Gravatar 頭像
|
||||
config.enable_federated_avatar=Enable Federated Avatars
|
||||
|
||||
config.git_config=Git Configuration
|
||||
config.git_disable_diff_highlight=Disable Diff Syntax Highlight
|
||||
config.git_max_diff_lines=Max Diff Lines (for a single file)
|
||||
config.git_max_diff_line_characters=Max Diff Characters (for a single line)
|
||||
config.git_max_diff_files=Max Diff Files (to be shown)
|
||||
config.git_gc_args=GC Arguments
|
||||
config.git_migrate_timeout=Migration Timeout
|
||||
config.git_mirror_timeout=Mirror Update Timeout
|
||||
config.git_clone_timeout=Clone Operation Timeout
|
||||
config.git_pull_timeout=Pull Operation Timeout
|
||||
config.git_gc_timeout=GC Operation Timeout
|
||||
|
||||
config.log_config=日誌配置
|
||||
config.log_mode=日誌模式
|
||||
|
||||
@@ -1029,7 +1174,11 @@ create_repo=創建了儲存庫 <a href="%s">%s</a>
|
||||
rename_repo=重新命名倉庫 <code>%[1]s</code> 為 <a href="%[2]s">%[3]s</a>
|
||||
commit_repo=推送了 <a href="%[1]s/src/%[2]s">%[3]s</a> 分支的代碼到 <a href="%[1]s">%[4]s</a>
|
||||
create_issue=`創建了問題 <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
close_issue=`closed issue <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
reopen_issue=`reopened issue <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
create_pull_request=`創建了合併請求 <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
close_pull_request=`closed pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
reopen_pull_request=`reopened pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
comment_issue=`評論了問題 <a href="%s/issues/%s">%s#%[2]s</a>`
|
||||
merge_pull_request=`合併了合併請求 <a href="%s/pulls/%s">%s#%[2]s</a>`
|
||||
transfer_repo=將儲存庫 <code>%s</code> 轉移至 <a href="%s">%s</a>
|
||||
|
||||
1214
conf/locale/locale_zh-TW.ini
Normal file
1214
conf/locale/locale_zh-TW.ini
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,10 @@
|
||||
# Docker for Gogs
|
||||
|
||||
Visit [Docker Hub](https://hub.docker.com/r/gogs/gogs/) or [Quay](https://quay.io/repository/gogs/gogs) see all available tags.
|
||||
Visit [Docker Hub](https://hub.docker.com/r/gogs/) see all available images and tags.
|
||||
|
||||
## Usage
|
||||
|
||||
To keep your data out of Docker container, we do a volume(`/var/gogs` -> `/data`) here, and you can change it based on your situation.
|
||||
To keep your data out of Docker container, we do a volume (`/var/gogs` -> `/data`) here, and you can change it based on your situation.
|
||||
|
||||
```
|
||||
# Pull image from Docker Hub.
|
||||
@@ -20,6 +20,8 @@ $ docker run --name=gogs -p 10022:22 -p 10080:3000 -v /var/gogs:/data gogs/gogs
|
||||
$ docker start gogs
|
||||
```
|
||||
|
||||
Note: It is important to map the Gogs ssh service from the container to the host and set the appropriate SSH Port and URI settings when setting up Gogs for the first time. To access and clone Gogs Git repositories with the above configuration you would use: `git clone ssh://git@hostname:10022/username/myrepo.git` for example.
|
||||
|
||||
Files will be store in local path `/var/gogs` in my case.
|
||||
|
||||
Directory `/var/gogs` keeps Git repositories and Gogs data:
|
||||
@@ -41,9 +43,11 @@ If you're more comfortable with mounting data to a data container, the commands
|
||||
```
|
||||
# Create data container
|
||||
docker run --name=gogs-data --entrypoint /bin/true gogs/gogs
|
||||
|
||||
# Use `docker run` for the first time.
|
||||
docker run --name=gogs --volumes-from gogs-data -p 10022:22 -p 10080:3000 gogs/gogs
|
||||
```
|
||||
|
||||
#### Using Docker 1.9 Volume command
|
||||
|
||||
```
|
||||
@@ -56,16 +60,40 @@ $ docker run --name=gogs -p 10022:22 -p 10080:3000 -v gogs-data:/data gogs/gogs
|
||||
|
||||
## Settings
|
||||
|
||||
### Application
|
||||
|
||||
Most of settings are obvious and easy to understand, but there are some settings can be confusing by running Gogs inside Docker:
|
||||
|
||||
- **Repository Root Path**: keep it as default value `/home/git/gogs-repositories` because `start.sh` already made a symbolic link for you.
|
||||
- **Run User**: keep it as default value `git` because `start.sh` already setup a user with name `git`.
|
||||
- **Domain**: fill in with Docker container IP(e.g. `192.168.99.100`). But if you want to access your Gogs instance from a different physical machine, please fill in with the hostname or IP address of the Docker host machine.
|
||||
- **Domain**: fill in with Docker container IP (e.g. `192.168.99.100`). But if you want to access your Gogs instance from a different physical machine, please fill in with the hostname or IP address of the Docker host machine.
|
||||
- **SSH Port**: Use the exposed port from Docker container. For example, your SSH server listens on `22` inside Docker, but you expose it by `10022:22`, then use `10022` for this value. **Builtin SSH server is not recommended inside Docker Container**
|
||||
- **HTTP Port**: Use port you want Gogs to listen on inside Docker container. For example, your Gogs listens on `3000` inside Docker, and you expose it by `10080:3000`, but you still use `3000` for this value.
|
||||
- **Application URL**: Use combination of **Domain** and **exposed HTTP Port** values(e.g. `http://192.168.99.100:10080/`).
|
||||
- **Application URL**: Use combination of **Domain** and **exposed HTTP Port** values (e.g. `http://192.168.99.100:10080/`).
|
||||
|
||||
Full documentation of settings can be found [here](http://gogs.io/docs/advanced/configuration_cheat_sheet.html).
|
||||
Full documentation of application settings can be found [here](https://gogs.io/docs/advanced/configuration_cheat_sheet.html).
|
||||
|
||||
### Container options
|
||||
|
||||
This container have some options available via environment variables, these options are opt-in features that can help the administration of this container:
|
||||
|
||||
- **SOCAT_LINK**:
|
||||
- <u>Possible value:</u>
|
||||
`true`, `false`, `1`, `0`
|
||||
- <u>Default:</u>
|
||||
`true`
|
||||
- <u>Action:</u>
|
||||
Bind linked docker container to localhost socket using socat.
|
||||
Any exported port from a linked container will be binded to the matching port on localhost.
|
||||
- <u>Disclaimer:</u>
|
||||
As this option rely on the environment variable created by docker when a container is linked, this option should be deactivated in managed environment such as Rancher or Kubernetes (set to `0` or `false`)
|
||||
- **RUN_CROND**:
|
||||
- <u>Possible value:</u>
|
||||
`true`, `false`, `1`, `0`
|
||||
- <u>Default:</u>
|
||||
`false`
|
||||
- <u>Action:</u>
|
||||
Request crond to be run inside the container. Its default configuration will periodically run all scripts from `/etc/periodic/${period}` but custom crontabs can be added to `/var/spool/cron/crontabs/`.
|
||||
|
||||
## Upgrade
|
||||
|
||||
@@ -80,4 +108,4 @@ Steps to upgrade Gogs with Docker:
|
||||
|
||||
## Known Issues
|
||||
|
||||
- `.dockerignore` seems to be ignored during Docker Hub Automated build
|
||||
- The docker container can not currently be build on Raspberry 1 (armv6l) as our base image `alpine` does not have a `go` package available for this platform.
|
||||
|
||||
@@ -5,16 +5,17 @@ set -e
|
||||
# Set temp environment vars
|
||||
export GOPATH=/tmp/go
|
||||
export PATH=${PATH}:${GOPATH}/bin
|
||||
export GO15VENDOREXPERIMENT=1
|
||||
|
||||
# Install build deps
|
||||
apk -U --no-progress add --virtual build-deps linux-pam-dev go gcc musl-dev
|
||||
apk --no-cache --no-progress add --virtual build-deps build-base linux-pam-dev go
|
||||
|
||||
# Init go environment to build Gogs
|
||||
# Build Gogs
|
||||
mkdir -p ${GOPATH}/src/github.com/gogits/
|
||||
ln -s /app/gogs/ ${GOPATH}/src/github.com/gogits/gogs
|
||||
cd ${GOPATH}/src/github.com/gogits/gogs
|
||||
go get -v -tags "sqlite cert pam"
|
||||
go build -tags "sqlite cert pam"
|
||||
go get -v -tags "sqlite cert pam" ./...
|
||||
make build TAGS="sqlite cert pam"
|
||||
|
||||
# Cleanup GOPATH
|
||||
rm -r $GOPATH
|
||||
|
||||
0
docker/s6/crond/down
Normal file
0
docker/s6/crond/down
Normal file
9
docker/s6/crond/run
Executable file
9
docker/s6/crond/run
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
# Crontabs are located by default in /var/spool/cron/crontabs/
|
||||
# The default configuration is also calling all the scripts in /etc/periodic/${period}
|
||||
|
||||
if test -f ./setup; then
|
||||
source ./setup
|
||||
fi
|
||||
|
||||
exec gosu root /usr/sbin/crond -fS
|
||||
@@ -48,6 +48,15 @@ else
|
||||
create_socat_links
|
||||
fi
|
||||
|
||||
CROND=$(echo "$RUN_CROND" | tr '[:upper:]' '[:lower:]')
|
||||
if [ "$CROND" = "true" -o "$CROND" = "1" ]; then
|
||||
echo "init:crond | Cron Daemon (crond) will be run as requested by s6" 1>&2
|
||||
rm -f /app/gogs/docker/s6/crond/down
|
||||
else
|
||||
# Tell s6 not to run the crond service
|
||||
touch /app/gogs/docker/s6/crond/down
|
||||
fi
|
||||
|
||||
# Exec CMD or S6 by default if nothing present
|
||||
if [ $# -gt 0 ];then
|
||||
exec "$@"
|
||||
|
||||
12
gogs.go
12
gogs.go
@@ -1,4 +1,4 @@
|
||||
// +build go1.4
|
||||
// +build go1.5
|
||||
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
@@ -9,25 +9,23 @@ package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
"github.com/gogits/gogs/cmd"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
const APP_VER = "0.8.25.0129"
|
||||
const APP_VER = "0.9.141.0211"
|
||||
|
||||
func init() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
setting.AppVer = APP_VER
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "Gogs"
|
||||
app.Usage = "Go Git Service"
|
||||
app.Usage = "Go Git Service: a painless self-hosted Git service"
|
||||
app.Version = APP_VER
|
||||
app.Commands = []cli.Command{
|
||||
cmd.CmdWeb,
|
||||
@@ -35,6 +33,8 @@ func main() {
|
||||
cmd.CmdUpdate,
|
||||
cmd.CmdDump,
|
||||
cmd.CmdCert,
|
||||
cmd.CmdAdmin,
|
||||
cmd.CmdImport,
|
||||
}
|
||||
app.Flags = append(app.Flags, []cli.Flag{}...)
|
||||
app.Run(os.Args)
|
||||
|
||||
@@ -7,19 +7,46 @@ package models
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
log "gopkg.in/clog.v1"
|
||||
)
|
||||
|
||||
type AccessMode int
|
||||
|
||||
const (
|
||||
ACCESS_MODE_NONE AccessMode = iota
|
||||
ACCESS_MODE_READ
|
||||
ACCESS_MODE_WRITE
|
||||
ACCESS_MODE_ADMIN
|
||||
ACCESS_MODE_OWNER
|
||||
ACCESS_MODE_NONE AccessMode = iota // 0
|
||||
ACCESS_MODE_READ // 1
|
||||
ACCESS_MODE_WRITE // 2
|
||||
ACCESS_MODE_ADMIN // 3
|
||||
ACCESS_MODE_OWNER // 4
|
||||
)
|
||||
|
||||
func (mode AccessMode) String() string {
|
||||
switch mode {
|
||||
case ACCESS_MODE_READ:
|
||||
return "read"
|
||||
case ACCESS_MODE_WRITE:
|
||||
return "write"
|
||||
case ACCESS_MODE_ADMIN:
|
||||
return "admin"
|
||||
case ACCESS_MODE_OWNER:
|
||||
return "owner"
|
||||
default:
|
||||
return "none"
|
||||
}
|
||||
}
|
||||
|
||||
// ParseAccessMode returns corresponding access mode to given permission string.
|
||||
func ParseAccessMode(permission string) AccessMode {
|
||||
switch permission {
|
||||
case "write":
|
||||
return ACCESS_MODE_WRITE
|
||||
case "admin":
|
||||
return ACCESS_MODE_ADMIN
|
||||
default:
|
||||
return ACCESS_MODE_READ
|
||||
}
|
||||
}
|
||||
|
||||
// Access represents the highest access level of a user to the repository. The only access type
|
||||
// that is not in this table is the real owner of a repository. In case of an organization
|
||||
// repository, the members of the owners team are in this table.
|
||||
@@ -40,11 +67,11 @@ func accessLevel(e Engine, u *User, repo *Repository) (AccessMode, error) {
|
||||
return mode, nil
|
||||
}
|
||||
|
||||
if u.Id == repo.OwnerID {
|
||||
if u.ID == repo.OwnerID {
|
||||
return ACCESS_MODE_OWNER, nil
|
||||
}
|
||||
|
||||
a := &Access{UserID: u.Id, RepoID: repo.ID}
|
||||
a := &Access{UserID: u.ID, RepoID: repo.ID}
|
||||
if has, err := e.Get(a); !has || err != nil {
|
||||
return mode, err
|
||||
}
|
||||
@@ -70,7 +97,7 @@ func HasAccess(u *User, repo *Repository, testMode AccessMode) (bool, error) {
|
||||
// GetRepositoryAccesses finds all repositories with their access mode where a user has access but does not own.
|
||||
func (u *User) GetRepositoryAccesses() (map[*Repository]AccessMode, error) {
|
||||
accesses := make([]*Access, 0, 10)
|
||||
if err := x.Find(&accesses, &Access{UserID: u.Id}); err != nil {
|
||||
if err := x.Find(&accesses, &Access{UserID: u.ID}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -86,7 +113,7 @@ func (u *User) GetRepositoryAccesses() (map[*Repository]AccessMode, error) {
|
||||
}
|
||||
if err = repo.GetOwner(); err != nil {
|
||||
return nil, err
|
||||
} else if repo.OwnerID == u.Id {
|
||||
} else if repo.OwnerID == u.ID {
|
||||
continue
|
||||
}
|
||||
repos[repo] = access.Mode
|
||||
@@ -94,23 +121,17 @@ func (u *User) GetRepositoryAccesses() (map[*Repository]AccessMode, error) {
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
// GetAccessibleRepositories finds all repositories where a user has access but does not own.
|
||||
func (u *User) GetAccessibleRepositories() ([]*Repository, error) {
|
||||
accesses := make([]*Access, 0, 10)
|
||||
if err := x.Find(&accesses, &Access{UserID: u.Id}); err != nil {
|
||||
return nil, err
|
||||
// GetAccessibleRepositories finds repositories which the user has access but does not own.
|
||||
// If limit is smaller than 1 means returns all found results.
|
||||
func (user *User) GetAccessibleRepositories(limit int) (repos []*Repository, _ error) {
|
||||
sess := x.Where("owner_id !=? ", user.ID).Desc("updated_unix")
|
||||
if limit > 0 {
|
||||
sess.Limit(limit)
|
||||
repos = make([]*Repository, 0, limit)
|
||||
} else {
|
||||
repos = make([]*Repository, 0, 10)
|
||||
}
|
||||
|
||||
if len(accesses) == 0 {
|
||||
return []*Repository{}, nil
|
||||
}
|
||||
|
||||
repoIDs := make([]int64, 0, len(accesses))
|
||||
for _, access := range accesses {
|
||||
repoIDs = append(repoIDs, access.RepoID)
|
||||
}
|
||||
repos := make([]*Repository, 0, len(repoIDs))
|
||||
return repos, x.Where("owner_id != ?", u.Id).In("id", repoIDs).Desc("updated").Find(&repos)
|
||||
return repos, sess.Join("INNER", "access", "access.user_id = ? AND access.repo_id = repository.id", user.ID).Find(&repos)
|
||||
}
|
||||
|
||||
func maxAccessMode(modes ...AccessMode) AccessMode {
|
||||
@@ -151,15 +172,14 @@ func (repo *Repository) refreshAccesses(e Engine, accessMap map[int64]AccessMode
|
||||
return nil
|
||||
}
|
||||
|
||||
// FIXME: should be able to have read-only access.
|
||||
// Give all collaborators write access.
|
||||
// refreshCollaboratorAccesses retrieves repository collaborations with their access modes.
|
||||
func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]AccessMode) error {
|
||||
collaborators, err := repo.getCollaborators(e)
|
||||
collaborations, err := repo.getCollaborations(e)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getCollaborators: %v", err)
|
||||
return fmt.Errorf("getCollaborations: %v", err)
|
||||
}
|
||||
for _, c := range collaborators {
|
||||
accessMap[c.Id] = ACCESS_MODE_WRITE
|
||||
for _, c := range collaborations {
|
||||
accessMap[c.UserID] = c.Mode
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -201,7 +221,7 @@ func (repo *Repository) recalculateTeamAccesses(e Engine, ignTeamID int64) (err
|
||||
return fmt.Errorf("getMembers '%d': %v", t.ID, err)
|
||||
}
|
||||
for _, m := range t.Members {
|
||||
accessMap[m.Id] = maxAccessMode(accessMap[m.Id], t.Authorize)
|
||||
accessMap[m.ID] = maxAccessMode(accessMap[m.ID], t.Authorize)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
320
models/action.go
320
models/action.go
@@ -6,7 +6,6 @@ package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
@@ -16,33 +15,33 @@ import (
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
"github.com/gogits/git-module"
|
||||
api "github.com/gogits/go-gogs-client"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
type ActionType int
|
||||
|
||||
const (
|
||||
CREATE_REPO ActionType = iota + 1 // 1
|
||||
RENAME_REPO // 2
|
||||
STAR_REPO // 3
|
||||
FOLLOW_REPO // 4
|
||||
COMMIT_REPO // 5
|
||||
CREATE_ISSUE // 6
|
||||
CREATE_PULL_REQUEST // 7
|
||||
TRANSFER_REPO // 8
|
||||
PUSH_TAG // 9
|
||||
COMMENT_ISSUE // 10
|
||||
MERGE_PULL_REQUEST // 11
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotImplemented = errors.New("Not implemented yet")
|
||||
ACTION_CREATE_REPO ActionType = iota + 1 // 1
|
||||
ACTION_RENAME_REPO // 2
|
||||
ACTION_STAR_REPO // 3
|
||||
ACTION_WATCH_REPO // 4
|
||||
ACTION_COMMIT_REPO // 5
|
||||
ACTION_CREATE_ISSUE // 6
|
||||
ACTION_CREATE_PULL_REQUEST // 7
|
||||
ACTION_TRANSFER_REPO // 8
|
||||
ACTION_PUSH_TAG // 9
|
||||
ACTION_COMMENT_ISSUE // 10
|
||||
ACTION_MERGE_PULL_REQUEST // 11
|
||||
ACTION_CLOSE_ISSUE // 12
|
||||
ACTION_REOPEN_ISSUE // 13
|
||||
ACTION_CLOSE_PULL_REQUEST // 14
|
||||
ACTION_REOPEN_PULL_REQUEST // 15
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -64,7 +63,7 @@ func init() {
|
||||
IssueReferenceKeywordsPat = regexp.MustCompile(`(?i)(?:)(^| )\S+`)
|
||||
}
|
||||
|
||||
// Action represents user operation type and other information to repository.,
|
||||
// Action represents user operation type and other information to repository,
|
||||
// it implemented interface base.Actioner so that can be used in template render.
|
||||
type Action struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
@@ -72,7 +71,6 @@ type Action struct {
|
||||
OpType ActionType
|
||||
ActUserID int64 // Action user id.
|
||||
ActUserName string // Action user name.
|
||||
ActEmail string
|
||||
ActAvatar string `xorm:"-"`
|
||||
RepoID int64
|
||||
RepoUserName string
|
||||
@@ -80,13 +78,18 @@ type Action struct {
|
||||
RefName string
|
||||
IsPrivate bool `xorm:"NOT NULL DEFAULT false"`
|
||||
Content string `xorm:"TEXT"`
|
||||
Created time.Time `xorm:"created"`
|
||||
Created time.Time `xorm:"-"`
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
func (a *Action) BeforeInsert() {
|
||||
a.CreatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (a *Action) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created":
|
||||
a.Created = regulateTimeZone(a.Created)
|
||||
case "created_unix":
|
||||
a.Created = time.Unix(a.CreatedUnix, 0).Local()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,10 +105,6 @@ func (a *Action) ShortActUserName() string {
|
||||
return base.EllipsisString(a.ActUserName, 20)
|
||||
}
|
||||
|
||||
func (a *Action) GetActEmail() string {
|
||||
return a.ActEmail
|
||||
}
|
||||
|
||||
func (a *Action) GetRepoUserName() string {
|
||||
return a.RepoUserName
|
||||
}
|
||||
@@ -160,7 +159,7 @@ func (a *Action) GetIssueTitle() string {
|
||||
log.Error(4, "GetIssueByIndex: %v", err)
|
||||
return "500 when get issue"
|
||||
}
|
||||
return issue.Name
|
||||
return issue.Title
|
||||
}
|
||||
|
||||
func (a *Action) GetIssueContent() string {
|
||||
@@ -175,16 +174,15 @@ func (a *Action) GetIssueContent() string {
|
||||
|
||||
func newRepoAction(e Engine, u *User, repo *Repository) (err error) {
|
||||
if err = notifyWatchers(e, &Action{
|
||||
ActUserID: u.Id,
|
||||
ActUserID: u.ID,
|
||||
ActUserName: u.Name,
|
||||
ActEmail: u.Email,
|
||||
OpType: CREATE_REPO,
|
||||
OpType: ACTION_CREATE_REPO,
|
||||
RepoID: repo.ID,
|
||||
RepoUserName: repo.Owner.Name,
|
||||
RepoName: repo.Name,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("notify watchers '%d/%d': %v", u.Id, repo.ID, err)
|
||||
return fmt.Errorf("notify watchers '%d/%d': %v", u.ID, repo.ID, err)
|
||||
}
|
||||
|
||||
log.Trace("action.newRepoAction: %s/%s", u.Name, repo.Name)
|
||||
@@ -198,10 +196,9 @@ func NewRepoAction(u *User, repo *Repository) (err error) {
|
||||
|
||||
func renameRepoAction(e Engine, actUser *User, oldRepoName string, repo *Repository) (err error) {
|
||||
if err = notifyWatchers(e, &Action{
|
||||
ActUserID: actUser.Id,
|
||||
ActUserID: actUser.ID,
|
||||
ActUserName: actUser.Name,
|
||||
ActEmail: actUser.Email,
|
||||
OpType: RENAME_REPO,
|
||||
OpType: ACTION_RENAME_REPO,
|
||||
RepoID: repo.ID,
|
||||
RepoUserName: repo.Owner.Name,
|
||||
RepoName: repo.Name,
|
||||
@@ -225,16 +222,19 @@ func issueIndexTrimRight(c rune) bool {
|
||||
}
|
||||
|
||||
type PushCommit struct {
|
||||
Sha1 string
|
||||
Message string
|
||||
AuthorEmail string
|
||||
AuthorName string
|
||||
Sha1 string
|
||||
Message string
|
||||
AuthorEmail string
|
||||
AuthorName string
|
||||
CommitterEmail string
|
||||
CommitterName string
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
type PushCommits struct {
|
||||
Len int
|
||||
Commits []*PushCommit
|
||||
CompareUrl string
|
||||
CompareURL string
|
||||
|
||||
avatars map[string]string
|
||||
}
|
||||
@@ -247,21 +247,33 @@ func NewPushCommits() *PushCommits {
|
||||
|
||||
func (pc *PushCommits) ToApiPayloadCommits(repoLink string) []*api.PayloadCommit {
|
||||
commits := make([]*api.PayloadCommit, len(pc.Commits))
|
||||
for i, cmt := range pc.Commits {
|
||||
author_username := ""
|
||||
author, err := GetUserByEmail(cmt.AuthorEmail)
|
||||
for i, commit := range pc.Commits {
|
||||
authorUsername := ""
|
||||
author, err := GetUserByEmail(commit.AuthorEmail)
|
||||
if err == nil {
|
||||
author_username = author.Name
|
||||
authorUsername = author.Name
|
||||
}
|
||||
committerUsername := ""
|
||||
committer, err := GetUserByEmail(commit.CommitterEmail)
|
||||
if err == nil {
|
||||
// TODO: check errors other than email not found.
|
||||
committerUsername = committer.Name
|
||||
}
|
||||
commits[i] = &api.PayloadCommit{
|
||||
ID: cmt.Sha1,
|
||||
Message: cmt.Message,
|
||||
URL: fmt.Sprintf("%s/commit/%s", repoLink, cmt.Sha1),
|
||||
Author: &api.PayloadAuthor{
|
||||
Name: cmt.AuthorName,
|
||||
Email: cmt.AuthorEmail,
|
||||
UserName: author_username,
|
||||
ID: commit.Sha1,
|
||||
Message: commit.Message,
|
||||
URL: fmt.Sprintf("%s/commit/%s", repoLink, commit.Sha1),
|
||||
Author: &api.PayloadUser{
|
||||
Name: commit.AuthorName,
|
||||
Email: commit.AuthorEmail,
|
||||
UserName: authorUsername,
|
||||
},
|
||||
Committer: &api.PayloadUser{
|
||||
Name: commit.CommitterName,
|
||||
Email: commit.CommitterEmail,
|
||||
UserName: committerUsername,
|
||||
},
|
||||
Timestamp: commit.Timestamp,
|
||||
}
|
||||
}
|
||||
return commits
|
||||
@@ -279,15 +291,15 @@ func (push *PushCommits) AvatarLink(email string) string {
|
||||
log.Error(4, "GetUserByEmail: %v", err)
|
||||
}
|
||||
} else {
|
||||
push.avatars[email] = u.AvatarLink()
|
||||
push.avatars[email] = u.RelAvatarLink()
|
||||
}
|
||||
}
|
||||
|
||||
return push.avatars[email]
|
||||
}
|
||||
|
||||
// updateIssuesCommit checks if issues are manipulated by commit message.
|
||||
func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string, commits []*PushCommit) error {
|
||||
// UpdateIssuesCommit checks if issues are manipulated by commit message.
|
||||
func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit) error {
|
||||
// Commits are appended in the reverse order.
|
||||
for i := len(commits) - 1; i >= 0; i-- {
|
||||
c := commits[i]
|
||||
@@ -303,7 +315,7 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
|
||||
|
||||
// Add repo name if missing
|
||||
if ref[0] == '#' {
|
||||
ref = fmt.Sprintf("%s/%s%s", repoUserName, repoName, ref)
|
||||
ref = fmt.Sprintf("%s%s", repo.FullName(), ref)
|
||||
} else if !strings.Contains(ref, "/") {
|
||||
// FIXME: We don't support User#ID syntax yet
|
||||
// return ErrNotImplemented
|
||||
@@ -323,9 +335,8 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
|
||||
}
|
||||
refMarked[issue.ID] = true
|
||||
|
||||
url := fmt.Sprintf("%s/%s/%s/commit/%s", setting.AppSubUrl, repoUserName, repoName, c.Sha1)
|
||||
message := fmt.Sprintf(`<a href="%s">%s</a>`, url, c.Message)
|
||||
if err = CreateRefComment(u, repo, issue, message, c.Sha1); err != nil {
|
||||
message := fmt.Sprintf(`<a href="%s/commit/%s">%s</a>`, repo.Link(), c.Sha1, c.Message)
|
||||
if err = CreateRefComment(doer, repo, issue, message, c.Sha1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -342,7 +353,7 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
|
||||
|
||||
// Add repo name if missing
|
||||
if ref[0] == '#' {
|
||||
ref = fmt.Sprintf("%s/%s%s", repoUserName, repoName, ref)
|
||||
ref = fmt.Sprintf("%s%s", repo.FullName(), ref)
|
||||
} else if !strings.Contains(ref, "/") {
|
||||
// We don't support User#ID syntax yet
|
||||
// return ErrNotImplemented
|
||||
@@ -366,7 +377,7 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
|
||||
continue
|
||||
}
|
||||
|
||||
if err = issue.ChangeStatus(u, true); err != nil {
|
||||
if err = issue.ChangeStatus(doer, repo, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -382,7 +393,7 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
|
||||
|
||||
// Add repo name if missing
|
||||
if ref[0] == '#' {
|
||||
ref = fmt.Sprintf("%s/%s%s", repoUserName, repoName, ref)
|
||||
ref = fmt.Sprintf("%s%s", repo.FullName(), ref)
|
||||
} else if !strings.Contains(ref, "/") {
|
||||
// We don't support User#ID syntax yet
|
||||
// return ErrNotImplemented
|
||||
@@ -406,7 +417,7 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
|
||||
continue
|
||||
}
|
||||
|
||||
if err = issue.ChangeStatus(u, false); err != nil {
|
||||
if err = issue.ChangeStatus(doer, repo, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -414,26 +425,26 @@ func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string
|
||||
return nil
|
||||
}
|
||||
|
||||
// CommitRepoAction adds new action for committing repository.
|
||||
func CommitRepoAction(
|
||||
userID, repoUserID int64,
|
||||
userName, actEmail string,
|
||||
repoID int64,
|
||||
repoUserName, repoName string,
|
||||
refFullName string,
|
||||
commit *PushCommits,
|
||||
oldCommitID string, newCommitID string) error {
|
||||
type CommitRepoActionOptions struct {
|
||||
PusherName string
|
||||
RepoOwnerID int64
|
||||
RepoName string
|
||||
RefFullName string
|
||||
OldCommitID string
|
||||
NewCommitID string
|
||||
Commits *PushCommits
|
||||
}
|
||||
|
||||
u, err := GetUserByID(userID)
|
||||
// CommitRepoAction adds new commit actio to the repository, and prepare corresponding webhooks.
|
||||
func CommitRepoAction(opts CommitRepoActionOptions) error {
|
||||
pusher, err := GetUserByName(opts.PusherName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetUserByID: %v", err)
|
||||
return fmt.Errorf("GetUserByName [%s]: %v", opts.PusherName, err)
|
||||
}
|
||||
|
||||
repo, err := GetRepositoryByName(repoUserID, repoName)
|
||||
repo, err := GetRepositoryByName(opts.RepoOwnerID, opts.RepoName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetRepositoryByName: %v", err)
|
||||
} else if err = repo.GetOwner(); err != nil {
|
||||
return fmt.Errorf("GetOwner: %v", err)
|
||||
return fmt.Errorf("GetRepositoryByName [owner_id: %d, name: %s]: %v", opts.RepoOwnerID, opts.RepoName, err)
|
||||
}
|
||||
|
||||
// Change repository bare status and update last updated time.
|
||||
@@ -443,81 +454,66 @@ func CommitRepoAction(
|
||||
}
|
||||
|
||||
isNewBranch := false
|
||||
opType := COMMIT_REPO
|
||||
opType := ACTION_COMMIT_REPO
|
||||
// Check it's tag push or branch.
|
||||
if strings.HasPrefix(refFullName, "refs/tags/") {
|
||||
opType = PUSH_TAG
|
||||
commit = &PushCommits{}
|
||||
if strings.HasPrefix(opts.RefFullName, git.TAG_PREFIX) {
|
||||
opType = ACTION_PUSH_TAG
|
||||
opts.Commits = &PushCommits{}
|
||||
} else {
|
||||
// if not the first commit, set the compareUrl
|
||||
if !strings.HasPrefix(oldCommitID, "0000000") {
|
||||
commit.CompareUrl = repo.ComposeCompareURL(oldCommitID, newCommitID)
|
||||
} else {
|
||||
// if not the first commit, set the compare URL.
|
||||
if opts.OldCommitID == git.EMPTY_SHA {
|
||||
isNewBranch = true
|
||||
} else {
|
||||
opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
|
||||
}
|
||||
|
||||
if err = updateIssuesCommit(u, repo, repoUserName, repoName, commit.Commits); err != nil {
|
||||
if err = UpdateIssuesCommit(pusher, repo, opts.Commits.Commits); err != nil {
|
||||
log.Error(4, "updateIssuesCommit: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(commit.Commits) > setting.FeedMaxCommitNum {
|
||||
commit.Commits = commit.Commits[:setting.FeedMaxCommitNum]
|
||||
if len(opts.Commits.Commits) > setting.UI.FeedMaxCommitNum {
|
||||
opts.Commits.Commits = opts.Commits.Commits[:setting.UI.FeedMaxCommitNum]
|
||||
}
|
||||
|
||||
bs, err := json.Marshal(commit)
|
||||
data, err := json.Marshal(opts.Commits)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Marshal: %v", err)
|
||||
}
|
||||
|
||||
refName := git.RefEndName(refFullName)
|
||||
|
||||
refName := git.RefEndName(opts.RefFullName)
|
||||
if err = NotifyWatchers(&Action{
|
||||
ActUserID: u.Id,
|
||||
ActUserName: userName,
|
||||
ActEmail: actEmail,
|
||||
ActUserID: pusher.ID,
|
||||
ActUserName: pusher.Name,
|
||||
OpType: opType,
|
||||
Content: string(bs),
|
||||
Content: string(data),
|
||||
RepoID: repo.ID,
|
||||
RepoUserName: repoUserName,
|
||||
RepoName: repoName,
|
||||
RepoUserName: repo.MustOwner().Name,
|
||||
RepoName: repo.Name,
|
||||
RefName: refName,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("NotifyWatchers: %v", err)
|
||||
}
|
||||
|
||||
payloadRepo := repo.ComposePayload()
|
||||
|
||||
pusher_email, pusher_name := "", ""
|
||||
pusher, err := GetUserByName(userName)
|
||||
if err == nil {
|
||||
pusher_email = pusher.Email
|
||||
pusher_name = pusher.DisplayName()
|
||||
}
|
||||
payloadSender := &api.PayloadUser{
|
||||
UserName: pusher.Name,
|
||||
ID: pusher.Id,
|
||||
AvatarUrl: setting.AppUrl + pusher.RelAvatarLink(),
|
||||
}
|
||||
defer func() {
|
||||
go HookQueue.Add(repo.ID)
|
||||
}()
|
||||
|
||||
apiPusher := pusher.APIFormat()
|
||||
apiRepo := repo.APIFormat(nil)
|
||||
switch opType {
|
||||
case COMMIT_REPO: // Push
|
||||
p := &api.PushPayload{
|
||||
Ref: refFullName,
|
||||
Before: oldCommitID,
|
||||
After: newCommitID,
|
||||
CompareUrl: setting.AppUrl + commit.CompareUrl,
|
||||
Commits: commit.ToApiPayloadCommits(repo.FullRepoLink()),
|
||||
Repo: payloadRepo,
|
||||
Pusher: &api.PayloadAuthor{
|
||||
Name: pusher_name,
|
||||
Email: pusher_email,
|
||||
UserName: userName,
|
||||
},
|
||||
Sender: payloadSender,
|
||||
}
|
||||
if err = PrepareWebhooks(repo, HOOK_EVENT_PUSH, p); err != nil {
|
||||
case ACTION_COMMIT_REPO: // Push
|
||||
if err = PrepareWebhooks(repo, HOOK_EVENT_PUSH, &api.PushPayload{
|
||||
Ref: opts.RefFullName,
|
||||
Before: opts.OldCommitID,
|
||||
After: opts.NewCommitID,
|
||||
CompareURL: setting.AppUrl + opts.Commits.CompareURL,
|
||||
Commits: opts.Commits.ToApiPayloadCommits(repo.HTMLURL()),
|
||||
Repo: apiRepo,
|
||||
Pusher: apiPusher,
|
||||
Sender: apiPusher,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("PrepareWebhooks: %v", err)
|
||||
}
|
||||
|
||||
@@ -525,61 +521,59 @@ func CommitRepoAction(
|
||||
return PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{
|
||||
Ref: refName,
|
||||
RefType: "branch",
|
||||
Repo: payloadRepo,
|
||||
Sender: payloadSender,
|
||||
Repo: apiRepo,
|
||||
Sender: apiPusher,
|
||||
})
|
||||
}
|
||||
|
||||
case PUSH_TAG: // Create
|
||||
case ACTION_PUSH_TAG: // Create
|
||||
return PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{
|
||||
Ref: refName,
|
||||
RefType: "tag",
|
||||
Repo: payloadRepo,
|
||||
Sender: payloadSender,
|
||||
Repo: apiRepo,
|
||||
Sender: apiPusher,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func transferRepoAction(e Engine, actUser, oldOwner, newOwner *User, repo *Repository) (err error) {
|
||||
func transferRepoAction(e Engine, doer, oldOwner *User, repo *Repository) (err error) {
|
||||
if err = notifyWatchers(e, &Action{
|
||||
ActUserID: actUser.Id,
|
||||
ActUserName: actUser.Name,
|
||||
ActEmail: actUser.Email,
|
||||
OpType: TRANSFER_REPO,
|
||||
ActUserID: doer.ID,
|
||||
ActUserName: doer.Name,
|
||||
OpType: ACTION_TRANSFER_REPO,
|
||||
RepoID: repo.ID,
|
||||
RepoUserName: newOwner.Name,
|
||||
RepoUserName: repo.Owner.Name,
|
||||
RepoName: repo.Name,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
Content: path.Join(oldOwner.LowerName, repo.LowerName),
|
||||
Content: path.Join(oldOwner.Name, repo.Name),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("notify watchers '%d/%d': %v", actUser.Id, repo.ID, err)
|
||||
return fmt.Errorf("notifyWatchers: %v", err)
|
||||
}
|
||||
|
||||
// Remove watch for organization.
|
||||
if repo.Owner.IsOrganization() {
|
||||
if err = watchRepo(e, repo.Owner.Id, repo.ID, false); err != nil {
|
||||
return fmt.Errorf("watch repository: %v", err)
|
||||
if oldOwner.IsOrganization() {
|
||||
if err = watchRepo(e, oldOwner.ID, repo.ID, false); err != nil {
|
||||
return fmt.Errorf("watchRepo [false]: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Trace("action.transferRepoAction: %s/%s", actUser.Name, repo.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TransferRepoAction adds new action for transferring repository.
|
||||
func TransferRepoAction(actUser, oldOwner, newOwner *User, repo *Repository) error {
|
||||
return transferRepoAction(x, actUser, oldOwner, newOwner, repo)
|
||||
// TransferRepoAction adds new action for transferring repository,
|
||||
// the Owner field of repository is assumed to be new owner.
|
||||
func TransferRepoAction(doer, oldOwner *User, repo *Repository) error {
|
||||
return transferRepoAction(x, doer, oldOwner, repo)
|
||||
}
|
||||
|
||||
func mergePullRequestAction(e Engine, actUser *User, repo *Repository, pull *Issue) error {
|
||||
func mergePullRequestAction(e Engine, doer *User, repo *Repository, issue *Issue) error {
|
||||
return notifyWatchers(e, &Action{
|
||||
ActUserID: actUser.Id,
|
||||
ActUserName: actUser.Name,
|
||||
ActEmail: actUser.Email,
|
||||
OpType: MERGE_PULL_REQUEST,
|
||||
Content: fmt.Sprintf("%d|%s", pull.Index, pull.Name),
|
||||
ActUserID: doer.ID,
|
||||
ActUserName: doer.Name,
|
||||
OpType: ACTION_MERGE_PULL_REQUEST,
|
||||
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
|
||||
RepoID: repo.ID,
|
||||
RepoUserName: repo.Owner.Name,
|
||||
RepoName: repo.Name,
|
||||
@@ -593,12 +587,30 @@ func MergePullRequestAction(actUser *User, repo *Repository, pull *Issue) error
|
||||
}
|
||||
|
||||
// GetFeeds returns action list of given user in given context.
|
||||
func GetFeeds(uid, offset int64, isProfile bool) ([]*Action, error) {
|
||||
// actorID is the user who's requesting, ctxUserID is the user/org that is requested.
|
||||
// actorID can be -1 when isProfile is true or to skip the permission check.
|
||||
func GetFeeds(ctxUser *User, actorID, offset int64, isProfile bool) ([]*Action, error) {
|
||||
actions := make([]*Action, 0, 20)
|
||||
sess := x.Limit(20, int(offset)).Desc("id").Where("user_id=?", uid)
|
||||
sess := x.Limit(20, int(offset)).Desc("id").Where("user_id = ?", ctxUser.ID)
|
||||
if isProfile {
|
||||
sess.And("is_private=?", false).And("act_user_id=?", uid)
|
||||
sess.And("is_private = ?", false).And("act_user_id = ?", ctxUser.ID)
|
||||
} else if actorID != -1 && ctxUser.IsOrganization() {
|
||||
// FIXME: only need to get IDs here, not all fields of repository.
|
||||
repos, _, err := ctxUser.GetUserRepositories(actorID, 1, ctxUser.NumRepos)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetUserRepositories: %v", err)
|
||||
}
|
||||
|
||||
var repoIDs []int64
|
||||
for _, repo := range repos {
|
||||
repoIDs = append(repoIDs, repo.ID)
|
||||
}
|
||||
|
||||
if len(repoIDs) > 0 {
|
||||
sess.In("repo_id", repoIDs)
|
||||
}
|
||||
}
|
||||
|
||||
err := sess.Find(&actions)
|
||||
return actions, err
|
||||
}
|
||||
|
||||
@@ -5,12 +5,18 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
type NoticeType int
|
||||
@@ -24,7 +30,19 @@ type Notice struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Type NoticeType
|
||||
Description string `xorm:"TEXT"`
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
Created time.Time `xorm:"-"`
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
func (n *Notice) BeforeInsert() {
|
||||
n.CreatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (n *Notice) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created_unix":
|
||||
n.Created = time.Unix(n.CreatedUnix, 0).Local()
|
||||
}
|
||||
}
|
||||
|
||||
// TrStr returns a translation format string.
|
||||
@@ -34,6 +52,11 @@ func (n *Notice) TrStr() string {
|
||||
|
||||
// CreateNotice creates new system notice.
|
||||
func CreateNotice(tp NoticeType, desc string) error {
|
||||
// prevent panic if database connection is not available at this point
|
||||
if x == nil {
|
||||
return fmt.Errorf("Could not save notice due database connection not being available: %d %s", tp, desc)
|
||||
}
|
||||
|
||||
n := &Notice{
|
||||
Type: tp,
|
||||
Description: desc,
|
||||
@@ -47,6 +70,31 @@ func CreateRepositoryNotice(desc string) error {
|
||||
return CreateNotice(NOTICE_REPOSITORY, desc)
|
||||
}
|
||||
|
||||
// RemoveAllWithNotice removes all directories in given path and
|
||||
// creates a system notice when error occurs.
|
||||
func RemoveAllWithNotice(title, path string) {
|
||||
var err error
|
||||
// workaround for Go not being able to remove read-only files/folders: https://github.com/golang/go/issues/9606
|
||||
// this bug should be fixed on Go 1.7, so the workaround should be removed when Gogs don't support Go 1.6 anymore:
|
||||
// https://github.com/golang/go/commit/2ffb3e5d905b5622204d199128dec06cefd57790
|
||||
// Note: Windows complains when delete target does not exist, therefore we can skip deletion in such cases.
|
||||
if setting.IsWindows && com.IsExist(path) {
|
||||
// converting "/" to "\" in path on Windows
|
||||
path = strings.Replace(path, "/", "\\", -1)
|
||||
err = exec.Command("cmd", "/C", "rmdir", "/S", "/Q", path).Run()
|
||||
} else {
|
||||
err = os.RemoveAll(path)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
desc := fmt.Sprintf("%s [%s]: %v", title, path, err)
|
||||
log.Warn(desc)
|
||||
if err = CreateRepositoryNotice(desc); err != nil {
|
||||
log.Error(4, "CreateRepositoryNotice: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CountNotices returns number of notices.
|
||||
func CountNotices() int64 {
|
||||
count, _ := x.Count(new(Notice))
|
||||
|
||||
175
models/attachment.go
Normal file
175
models/attachment.go
Normal file
@@ -0,0 +1,175 @@
|
||||
// Copyright 2017 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
gouuid "github.com/satori/go.uuid"
|
||||
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
// Attachment represent a attachment of issue/comment/release.
|
||||
type Attachment struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UUID string `xorm:"uuid UNIQUE"`
|
||||
IssueID int64 `xorm:"INDEX"`
|
||||
CommentID int64
|
||||
ReleaseID int64 `xorm:"INDEX"`
|
||||
Name string
|
||||
|
||||
Created time.Time `xorm:"-"`
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
func (a *Attachment) BeforeInsert() {
|
||||
a.CreatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (a *Attachment) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created_unix":
|
||||
a.Created = time.Unix(a.CreatedUnix, 0).Local()
|
||||
}
|
||||
}
|
||||
|
||||
// AttachmentLocalPath returns where attachment is stored in local file system based on given UUID.
|
||||
func AttachmentLocalPath(uuid string) string {
|
||||
return path.Join(setting.AttachmentPath, uuid[0:1], uuid[1:2], uuid)
|
||||
}
|
||||
|
||||
// LocalPath returns where attachment is stored in local file system.
|
||||
func (attach *Attachment) LocalPath() string {
|
||||
return AttachmentLocalPath(attach.UUID)
|
||||
}
|
||||
|
||||
// NewAttachment creates a new attachment object.
|
||||
func NewAttachment(name string, buf []byte, file multipart.File) (_ *Attachment, err error) {
|
||||
attach := &Attachment{
|
||||
UUID: gouuid.NewV4().String(),
|
||||
Name: name,
|
||||
}
|
||||
|
||||
localPath := attach.LocalPath()
|
||||
if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil {
|
||||
return nil, fmt.Errorf("MkdirAll: %v", err)
|
||||
}
|
||||
|
||||
fw, err := os.Create(localPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Create: %v", err)
|
||||
}
|
||||
defer fw.Close()
|
||||
|
||||
if _, err = fw.Write(buf); err != nil {
|
||||
return nil, fmt.Errorf("Write: %v", err)
|
||||
} else if _, err = io.Copy(fw, file); err != nil {
|
||||
return nil, fmt.Errorf("Copy: %v", err)
|
||||
}
|
||||
|
||||
if _, err := x.Insert(attach); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return attach, nil
|
||||
}
|
||||
|
||||
func getAttachmentByUUID(e Engine, uuid string) (*Attachment, error) {
|
||||
attach := &Attachment{UUID: uuid}
|
||||
has, err := x.Get(attach)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrAttachmentNotExist{0, uuid}
|
||||
}
|
||||
return attach, nil
|
||||
}
|
||||
|
||||
func getAttachmentsByUUIDs(e Engine, uuids []string) ([]*Attachment, error) {
|
||||
if len(uuids) == 0 {
|
||||
return []*Attachment{}, nil
|
||||
}
|
||||
|
||||
// Silently drop invalid uuids.
|
||||
attachments := make([]*Attachment, 0, len(uuids))
|
||||
return attachments, e.In("uuid", uuids).Find(&attachments)
|
||||
}
|
||||
|
||||
// GetAttachmentByUUID returns attachment by given UUID.
|
||||
func GetAttachmentByUUID(uuid string) (*Attachment, error) {
|
||||
return getAttachmentByUUID(x, uuid)
|
||||
}
|
||||
|
||||
func getAttachmentsByIssueID(e Engine, issueID int64) ([]*Attachment, error) {
|
||||
attachments := make([]*Attachment, 0, 10)
|
||||
return attachments, e.Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments)
|
||||
}
|
||||
|
||||
// GetAttachmentsByIssueID returns all attachments of an issue.
|
||||
func GetAttachmentsByIssueID(issueID int64) ([]*Attachment, error) {
|
||||
return getAttachmentsByIssueID(x, issueID)
|
||||
}
|
||||
|
||||
func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) {
|
||||
attachments := make([]*Attachment, 0, 10)
|
||||
return attachments, e.Where("comment_id=?", commentID).Find(&attachments)
|
||||
}
|
||||
|
||||
// GetAttachmentsByCommentID returns all attachments if comment by given ID.
|
||||
func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) {
|
||||
return getAttachmentsByCommentID(x, commentID)
|
||||
}
|
||||
|
||||
// DeleteAttachment deletes the given attachment and optionally the associated file.
|
||||
func DeleteAttachment(a *Attachment, remove bool) error {
|
||||
_, err := DeleteAttachments([]*Attachment{a}, remove)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteAttachments deletes the given attachments and optionally the associated files.
|
||||
func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) {
|
||||
for i, a := range attachments {
|
||||
if remove {
|
||||
if err := os.Remove(a.LocalPath()); err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := x.Delete(a); err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
|
||||
return len(attachments), nil
|
||||
}
|
||||
|
||||
// DeleteAttachmentsByIssue deletes all attachments associated with the given issue.
|
||||
func DeleteAttachmentsByIssue(issueId int64, remove bool) (int, error) {
|
||||
attachments, err := GetAttachmentsByIssueID(issueId)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return DeleteAttachments(attachments, remove)
|
||||
}
|
||||
|
||||
// DeleteAttachmentsByComment deletes all attachments associated with the given comment.
|
||||
func DeleteAttachmentsByComment(commentId int64, remove bool) (int, error) {
|
||||
attachments, err := GetAttachmentsByCommentID(commentId)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return DeleteAttachments(attachments, remove)
|
||||
}
|
||||
472
models/comment.go
Normal file
472
models/comment.go
Normal file
@@ -0,0 +1,472 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
api "github.com/gogits/go-gogs-client"
|
||||
|
||||
"github.com/gogits/gogs/modules/markdown"
|
||||
)
|
||||
|
||||
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
|
||||
type CommentType int
|
||||
|
||||
const (
|
||||
// Plain comment, can be associated with a commit (CommitID > 0) and a line (LineNum > 0)
|
||||
COMMENT_TYPE_COMMENT CommentType = iota
|
||||
COMMENT_TYPE_REOPEN
|
||||
COMMENT_TYPE_CLOSE
|
||||
|
||||
// References.
|
||||
COMMENT_TYPE_ISSUE_REF
|
||||
// Reference from a commit (not part of a pull request)
|
||||
COMMENT_TYPE_COMMIT_REF
|
||||
// Reference from a comment
|
||||
COMMENT_TYPE_COMMENT_REF
|
||||
// Reference from a pull request
|
||||
COMMENT_TYPE_PULL_REF
|
||||
)
|
||||
|
||||
type CommentTag int
|
||||
|
||||
const (
|
||||
COMMENT_TAG_NONE CommentTag = iota
|
||||
COMMENT_TAG_POSTER
|
||||
COMMENT_TAG_WRITER
|
||||
COMMENT_TAG_OWNER
|
||||
)
|
||||
|
||||
// Comment represents a comment in commit and issue page.
|
||||
type Comment struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Type CommentType
|
||||
PosterID int64
|
||||
Poster *User `xorm:"-"`
|
||||
IssueID int64 `xorm:"INDEX"`
|
||||
Issue *Issue `xorm:"-"`
|
||||
CommitID int64
|
||||
Line int64
|
||||
Content string `xorm:"TEXT"`
|
||||
RenderedContent string `xorm:"-"`
|
||||
|
||||
Created time.Time `xorm:"-"`
|
||||
CreatedUnix int64
|
||||
Updated time.Time `xorm:"-"`
|
||||
UpdatedUnix int64
|
||||
|
||||
// Reference issue in commit message
|
||||
CommitSHA string `xorm:"VARCHAR(40)"`
|
||||
|
||||
Attachments []*Attachment `xorm:"-"`
|
||||
|
||||
// For view issue page.
|
||||
ShowTag CommentTag `xorm:"-"`
|
||||
}
|
||||
|
||||
func (c *Comment) BeforeInsert() {
|
||||
c.CreatedUnix = time.Now().Unix()
|
||||
c.UpdatedUnix = c.CreatedUnix
|
||||
}
|
||||
|
||||
func (c *Comment) BeforeUpdate() {
|
||||
c.UpdatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (c *Comment) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created_unix":
|
||||
c.Created = time.Unix(c.CreatedUnix, 0).Local()
|
||||
case "updated_unix":
|
||||
c.Updated = time.Unix(c.UpdatedUnix, 0).Local()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Comment) loadAttributes(e Engine) (err error) {
|
||||
if c.Poster == nil {
|
||||
c.Poster, err = GetUserByID(c.PosterID)
|
||||
if err != nil {
|
||||
if IsErrUserNotExist(err) {
|
||||
c.PosterID = -1
|
||||
c.Poster = NewGhostUser()
|
||||
} else {
|
||||
return fmt.Errorf("getUserByID.(Poster) [%d]: %v", c.PosterID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.Issue == nil {
|
||||
c.Issue, err = getRawIssueByID(e, c.IssueID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getIssueByID [%d]: %v", c.IssueID, err)
|
||||
}
|
||||
}
|
||||
|
||||
if c.Attachments == nil {
|
||||
c.Attachments, err = getAttachmentsByCommentID(e, c.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getAttachmentsByCommentID [%d]: %v", c.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Comment) LoadAttributes() error {
|
||||
return c.loadAttributes(x)
|
||||
}
|
||||
|
||||
func (c *Comment) AfterDelete() {
|
||||
_, err := DeleteAttachmentsByComment(c.ID, true)
|
||||
|
||||
if err != nil {
|
||||
log.Info("Could not delete files for comment %d on issue #%d: %s", c.ID, c.IssueID, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Comment) HTMLURL() string {
|
||||
return fmt.Sprintf("%s#issuecomment-%d", c.Issue.HTMLURL(), c.ID)
|
||||
}
|
||||
|
||||
// This method assumes following fields have been assigned with valid values:
|
||||
// Required - Poster, Issue
|
||||
func (c *Comment) APIFormat() *api.Comment {
|
||||
return &api.Comment{
|
||||
ID: c.ID,
|
||||
HTMLURL: c.HTMLURL(),
|
||||
Poster: c.Poster.APIFormat(),
|
||||
Body: c.Content,
|
||||
Created: c.Created,
|
||||
Updated: c.Updated,
|
||||
}
|
||||
}
|
||||
|
||||
// HashTag returns unique hash tag for comment.
|
||||
func (c *Comment) HashTag() string {
|
||||
return "issuecomment-" + com.ToStr(c.ID)
|
||||
}
|
||||
|
||||
// EventTag returns unique event hash tag for comment.
|
||||
func (c *Comment) EventTag() string {
|
||||
return "event-" + com.ToStr(c.ID)
|
||||
}
|
||||
|
||||
// mailParticipants sends new comment emails to repository watchers
|
||||
// and mentioned people.
|
||||
func (cmt *Comment) mailParticipants(e Engine, opType ActionType, issue *Issue) (err error) {
|
||||
mentions := markdown.FindAllMentions(cmt.Content)
|
||||
if err = updateIssueMentions(e, cmt.IssueID, mentions); err != nil {
|
||||
return fmt.Errorf("UpdateIssueMentions [%d]: %v", cmt.IssueID, err)
|
||||
}
|
||||
|
||||
switch opType {
|
||||
case ACTION_COMMENT_ISSUE:
|
||||
issue.Content = cmt.Content
|
||||
case ACTION_CLOSE_ISSUE:
|
||||
issue.Content = fmt.Sprintf("Closed #%d", issue.Index)
|
||||
case ACTION_REOPEN_ISSUE:
|
||||
issue.Content = fmt.Sprintf("Reopened #%d", issue.Index)
|
||||
}
|
||||
if err = mailIssueCommentToParticipants(issue, cmt.Poster, mentions); err != nil {
|
||||
log.Error(4, "mailIssueCommentToParticipants: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) {
|
||||
comment := &Comment{
|
||||
Type: opts.Type,
|
||||
PosterID: opts.Doer.ID,
|
||||
Poster: opts.Doer,
|
||||
IssueID: opts.Issue.ID,
|
||||
CommitID: opts.CommitID,
|
||||
CommitSHA: opts.CommitSHA,
|
||||
Line: opts.LineNum,
|
||||
Content: opts.Content,
|
||||
}
|
||||
if _, err = e.Insert(comment); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Compose comment action, could be plain comment, close or reopen issue/pull request.
|
||||
// This object will be used to notify watchers in the end of function.
|
||||
act := &Action{
|
||||
ActUserID: opts.Doer.ID,
|
||||
ActUserName: opts.Doer.Name,
|
||||
Content: fmt.Sprintf("%d|%s", opts.Issue.Index, strings.Split(opts.Content, "\n")[0]),
|
||||
RepoID: opts.Repo.ID,
|
||||
RepoUserName: opts.Repo.Owner.Name,
|
||||
RepoName: opts.Repo.Name,
|
||||
IsPrivate: opts.Repo.IsPrivate,
|
||||
}
|
||||
|
||||
// Check comment type.
|
||||
switch opts.Type {
|
||||
case COMMENT_TYPE_COMMENT:
|
||||
act.OpType = ACTION_COMMENT_ISSUE
|
||||
|
||||
if _, err = e.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check attachments
|
||||
attachments := make([]*Attachment, 0, len(opts.Attachments))
|
||||
for _, uuid := range opts.Attachments {
|
||||
attach, err := getAttachmentByUUID(e, uuid)
|
||||
if err != nil {
|
||||
if IsErrAttachmentNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("getAttachmentByUUID [%s]: %v", uuid, err)
|
||||
}
|
||||
attachments = append(attachments, attach)
|
||||
}
|
||||
|
||||
for i := range attachments {
|
||||
attachments[i].IssueID = opts.Issue.ID
|
||||
attachments[i].CommentID = comment.ID
|
||||
// No assign value could be 0, so ignore AllCols().
|
||||
if _, err = e.Id(attachments[i].ID).Update(attachments[i]); err != nil {
|
||||
return nil, fmt.Errorf("update attachment [%d]: %v", attachments[i].ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
case COMMENT_TYPE_REOPEN:
|
||||
act.OpType = ACTION_REOPEN_ISSUE
|
||||
if opts.Issue.IsPull {
|
||||
act.OpType = ACTION_REOPEN_PULL_REQUEST
|
||||
}
|
||||
|
||||
if opts.Issue.IsPull {
|
||||
_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls-1 WHERE id=?", opts.Repo.ID)
|
||||
} else {
|
||||
_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues-1 WHERE id=?", opts.Repo.ID)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case COMMENT_TYPE_CLOSE:
|
||||
act.OpType = ACTION_CLOSE_ISSUE
|
||||
if opts.Issue.IsPull {
|
||||
act.OpType = ACTION_CLOSE_PULL_REQUEST
|
||||
}
|
||||
|
||||
if opts.Issue.IsPull {
|
||||
_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls+1 WHERE id=?", opts.Repo.ID)
|
||||
} else {
|
||||
_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues+1 WHERE id=?", opts.Repo.ID)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Notify watchers for whatever action comes in, ignore if no action type.
|
||||
if act.OpType > 0 {
|
||||
if err = notifyWatchers(e, act); err != nil {
|
||||
log.Error(4, "notifyWatchers: %v", err)
|
||||
}
|
||||
if err = comment.mailParticipants(e, act.OpType, opts.Issue); err != nil {
|
||||
log.Error(4, "MailParticipants: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return comment, comment.loadAttributes(e)
|
||||
}
|
||||
|
||||
func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue) (*Comment, error) {
|
||||
cmtType := COMMENT_TYPE_CLOSE
|
||||
if !issue.IsClosed {
|
||||
cmtType = COMMENT_TYPE_REOPEN
|
||||
}
|
||||
return createComment(e, &CreateCommentOptions{
|
||||
Type: cmtType,
|
||||
Doer: doer,
|
||||
Repo: repo,
|
||||
Issue: issue,
|
||||
})
|
||||
}
|
||||
|
||||
type CreateCommentOptions struct {
|
||||
Type CommentType
|
||||
Doer *User
|
||||
Repo *Repository
|
||||
Issue *Issue
|
||||
|
||||
CommitID int64
|
||||
CommitSHA string
|
||||
LineNum int64
|
||||
Content string
|
||||
Attachments []string // UUIDs of attachments
|
||||
}
|
||||
|
||||
// CreateComment creates comment of issue or commit.
|
||||
func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) {
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
comment, err = createComment(sess, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return comment, sess.Commit()
|
||||
}
|
||||
|
||||
// CreateIssueComment creates a plain issue comment.
|
||||
func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content string, attachments []string) (*Comment, error) {
|
||||
return CreateComment(&CreateCommentOptions{
|
||||
Type: COMMENT_TYPE_COMMENT,
|
||||
Doer: doer,
|
||||
Repo: repo,
|
||||
Issue: issue,
|
||||
Content: content,
|
||||
Attachments: attachments,
|
||||
})
|
||||
}
|
||||
|
||||
// CreateRefComment creates a commit reference comment to issue.
|
||||
func CreateRefComment(doer *User, repo *Repository, issue *Issue, content, commitSHA string) error {
|
||||
if len(commitSHA) == 0 {
|
||||
return fmt.Errorf("cannot create reference with empty commit SHA")
|
||||
}
|
||||
|
||||
// Check if same reference from same commit has already existed.
|
||||
has, err := x.Get(&Comment{
|
||||
Type: COMMENT_TYPE_COMMIT_REF,
|
||||
IssueID: issue.ID,
|
||||
CommitSHA: commitSHA,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("check reference comment: %v", err)
|
||||
} else if has {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = CreateComment(&CreateCommentOptions{
|
||||
Type: COMMENT_TYPE_COMMIT_REF,
|
||||
Doer: doer,
|
||||
Repo: repo,
|
||||
Issue: issue,
|
||||
CommitSHA: commitSHA,
|
||||
Content: content,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetCommentByID returns the comment by given ID.
|
||||
func GetCommentByID(id int64) (*Comment, error) {
|
||||
c := new(Comment)
|
||||
has, err := x.Id(id).Get(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrCommentNotExist{id, 0}
|
||||
}
|
||||
return c, c.LoadAttributes()
|
||||
}
|
||||
|
||||
// FIXME: use CommentList to improve performance.
|
||||
func loadCommentsAttributes(e Engine, comments []*Comment) (err error) {
|
||||
for i := range comments {
|
||||
if err = comments[i].loadAttributes(e); err != nil {
|
||||
return fmt.Errorf("loadAttributes [%d]: %v", comments[i].ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCommentsByIssueIDSince(e Engine, issueID, since int64) ([]*Comment, error) {
|
||||
comments := make([]*Comment, 0, 10)
|
||||
sess := e.Where("issue_id = ?", issueID).Asc("created_unix")
|
||||
if since > 0 {
|
||||
sess.And("updated_unix >= ?", since)
|
||||
}
|
||||
|
||||
if err := sess.Find(&comments); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return comments, loadCommentsAttributes(e, comments)
|
||||
}
|
||||
|
||||
func getCommentsByRepoIDSince(e Engine, repoID, since int64) ([]*Comment, error) {
|
||||
comments := make([]*Comment, 0, 10)
|
||||
sess := e.Where("issue.repo_id = ?", repoID).Join("INNER", "issue", "issue.id = comment.issue_id", repoID).Asc("created_unix")
|
||||
if since > 0 {
|
||||
sess.And("updated_unix >= ?", since)
|
||||
}
|
||||
if err := sess.Find(&comments); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return comments, loadCommentsAttributes(e, comments)
|
||||
}
|
||||
|
||||
func getCommentsByIssueID(e Engine, issueID int64) ([]*Comment, error) {
|
||||
return getCommentsByIssueIDSince(e, issueID, -1)
|
||||
}
|
||||
|
||||
// GetCommentsByIssueID returns all comments of an issue.
|
||||
func GetCommentsByIssueID(issueID int64) ([]*Comment, error) {
|
||||
return getCommentsByIssueID(x, issueID)
|
||||
}
|
||||
|
||||
// GetCommentsByIssueIDSince returns a list of comments of an issue since a given time point.
|
||||
func GetCommentsByIssueIDSince(issueID, since int64) ([]*Comment, error) {
|
||||
return getCommentsByIssueIDSince(x, issueID, since)
|
||||
}
|
||||
|
||||
// GetCommentsByRepoIDSince returns a list of comments for all issues in a repo since a given time point.
|
||||
func GetCommentsByRepoIDSince(repoID, since int64) ([]*Comment, error) {
|
||||
return getCommentsByRepoIDSince(x, repoID, since)
|
||||
}
|
||||
|
||||
// UpdateComment updates information of comment.
|
||||
func UpdateComment(c *Comment) error {
|
||||
_, err := x.Id(c.ID).AllCols().Update(c)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCommentByID deletes the comment by given ID.
|
||||
func DeleteCommentByID(id int64) error {
|
||||
comment, err := GetCommentByID(id)
|
||||
if err != nil {
|
||||
if IsErrCommentNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.Id(comment.ID).Delete(new(Comment)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if comment.Type == COMMENT_TYPE_COMMENT {
|
||||
if _, err = sess.Exec("UPDATE `issue` SET num_comments = num_comments - 1 WHERE id = ?", comment.IssueID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cron
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gogits/gogs/models"
|
||||
"github.com/gogits/gogs/modules/cron"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
var c = cron.New()
|
||||
|
||||
func NewContext() {
|
||||
var (
|
||||
entry *cron.Entry
|
||||
err error
|
||||
)
|
||||
if setting.Cron.UpdateMirror.Enabled {
|
||||
entry, err = c.AddFunc("Update mirrors", setting.Cron.UpdateMirror.Schedule, models.MirrorUpdate)
|
||||
if err != nil {
|
||||
log.Fatal(4, "Cron[Update mirrors]: %v", err)
|
||||
}
|
||||
if setting.Cron.UpdateMirror.RunAtStart {
|
||||
entry.Prev = time.Now()
|
||||
go models.MirrorUpdate()
|
||||
}
|
||||
}
|
||||
if setting.Cron.RepoHealthCheck.Enabled {
|
||||
entry, err = c.AddFunc("Repository health check", setting.Cron.RepoHealthCheck.Schedule, models.GitFsck)
|
||||
if err != nil {
|
||||
log.Fatal(4, "Cron[Repository health check]: %v", err)
|
||||
}
|
||||
if setting.Cron.RepoHealthCheck.RunAtStart {
|
||||
entry.Prev = time.Now()
|
||||
go models.GitFsck()
|
||||
}
|
||||
}
|
||||
if setting.Cron.CheckRepoStats.Enabled {
|
||||
entry, err = c.AddFunc("Check repository statistics", setting.Cron.CheckRepoStats.Schedule, models.CheckRepoStats)
|
||||
if err != nil {
|
||||
log.Fatal(4, "Cron[Check repository statistics]: %v", err)
|
||||
}
|
||||
if setting.Cron.CheckRepoStats.RunAtStart {
|
||||
entry.Prev = time.Now()
|
||||
go models.CheckRepoStats()
|
||||
}
|
||||
}
|
||||
c.Start()
|
||||
}
|
||||
|
||||
// ListTasks returns all running cron tasks.
|
||||
func ListTasks() []*cron.Entry {
|
||||
return c.Entries()
|
||||
}
|
||||
142
models/error.go
142
models/error.go
@@ -280,6 +280,18 @@ func (err ErrAccessTokenNotExist) Error() string {
|
||||
return fmt.Sprintf("access token does not exist [sha: %s]", err.SHA)
|
||||
}
|
||||
|
||||
type ErrAccessTokenEmpty struct {
|
||||
}
|
||||
|
||||
func IsErrAccessTokenEmpty(err error) bool {
|
||||
_, ok := err.(ErrAccessTokenEmpty)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrAccessTokenEmpty) Error() string {
|
||||
return fmt.Sprintf("access token is empty")
|
||||
}
|
||||
|
||||
// ________ .__ __ .__
|
||||
// \_____ \_______ _________ ____ |__|____________ _/ |_|__| ____ ____
|
||||
// / | \_ __ \/ ___\__ \ / \| \___ /\__ \\ __\ |/ _ \ / \
|
||||
@@ -375,7 +387,7 @@ func IsErrReleaseAlreadyExist(err error) bool {
|
||||
}
|
||||
|
||||
func (err ErrReleaseAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("Release tag already exist [tag_name: %s]", err.TagName)
|
||||
return fmt.Sprintf("release tag already exist [tag_name: %s]", err.TagName)
|
||||
}
|
||||
|
||||
type ErrReleaseNotExist struct {
|
||||
@@ -389,7 +401,53 @@ func IsErrReleaseNotExist(err error) bool {
|
||||
}
|
||||
|
||||
func (err ErrReleaseNotExist) Error() string {
|
||||
return fmt.Sprintf("Release tag does not exist [id: %d, tag_name: %s]", err.ID, err.TagName)
|
||||
return fmt.Sprintf("release tag does not exist [id: %d, tag_name: %s]", err.ID, err.TagName)
|
||||
}
|
||||
|
||||
type ErrInvalidTagName struct {
|
||||
TagName string
|
||||
}
|
||||
|
||||
func IsErrInvalidTagName(err error) bool {
|
||||
_, ok := err.(ErrInvalidTagName)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrInvalidTagName) Error() string {
|
||||
return fmt.Sprintf("release tag name is not valid [tag_name: %s]", err.TagName)
|
||||
}
|
||||
|
||||
type ErrRepoFileAlreadyExist struct {
|
||||
FileName string
|
||||
}
|
||||
|
||||
func IsErrRepoFileAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrRepoFileAlreadyExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrRepoFileAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("repository file already exists [file_name: %s]", err.FileName)
|
||||
}
|
||||
|
||||
// __________ .__
|
||||
// \______ \____________ ____ ____ | |__
|
||||
// | | _/\_ __ \__ \ / \_/ ___\| | \
|
||||
// | | \ | | \// __ \| | \ \___| Y \
|
||||
// |______ / |__| (____ /___| /\___ >___| /
|
||||
// \/ \/ \/ \/ \/
|
||||
|
||||
type ErrBranchNotExist struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func IsErrBranchNotExist(err error) bool {
|
||||
_, ok := err.(ErrBranchNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrBranchNotExist) Error() string {
|
||||
return fmt.Sprintf("branch does not exist [name: %s]", err.Name)
|
||||
}
|
||||
|
||||
// __ __ ___. .__ __
|
||||
@@ -468,7 +526,8 @@ func (err ErrPullRequestNotExist) Error() string {
|
||||
// \/ \/ \/ \/ \/
|
||||
|
||||
type ErrCommentNotExist struct {
|
||||
ID int64
|
||||
ID int64
|
||||
IssueID int64
|
||||
}
|
||||
|
||||
func IsErrCommentNotExist(err error) bool {
|
||||
@@ -477,7 +536,7 @@ func IsErrCommentNotExist(err error) bool {
|
||||
}
|
||||
|
||||
func (err ErrCommentNotExist) Error() string {
|
||||
return fmt.Sprintf("comment does not exist [id: %d]", err.ID)
|
||||
return fmt.Sprintf("comment does not exist [id: %d, issue_id: %d]", err.ID, err.IssueID)
|
||||
}
|
||||
|
||||
// .____ ___. .__
|
||||
@@ -488,7 +547,8 @@ func (err ErrCommentNotExist) Error() string {
|
||||
// \/ \/ \/ \/
|
||||
|
||||
type ErrLabelNotExist struct {
|
||||
ID int64
|
||||
LabelID int64
|
||||
RepoID int64
|
||||
}
|
||||
|
||||
func IsErrLabelNotExist(err error) bool {
|
||||
@@ -497,7 +557,7 @@ func IsErrLabelNotExist(err error) bool {
|
||||
}
|
||||
|
||||
func (err ErrLabelNotExist) Error() string {
|
||||
return fmt.Sprintf("label does not exist [id: %d]", err.ID)
|
||||
return fmt.Sprintf("label does not exist [label_id: %d, repo_id: %d]", err.LabelID, err.RepoID)
|
||||
}
|
||||
|
||||
// _____ .__.__ __
|
||||
@@ -542,24 +602,50 @@ func (err ErrAttachmentNotExist) Error() string {
|
||||
return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
|
||||
}
|
||||
|
||||
// _____ __ .__ __ .__ __ .__
|
||||
// / _ \ __ ___/ |_| |__ ____ _____/ |_|__| ____ _____ _/ |_|__| ____ ____
|
||||
// / /_\ \| | \ __\ | \_/ __ \ / \ __\ |/ ___\\__ \\ __\ |/ _ \ / \
|
||||
// / | \ | /| | | Y \ ___/| | \ | | \ \___ / __ \| | | ( <_> ) | \
|
||||
// \____|__ /____/ |__| |___| /\___ >___| /__| |__|\___ >____ /__| |__|\____/|___| /
|
||||
// \/ \/ \/ \/ \/ \/ \/
|
||||
// .____ .__ _________
|
||||
// | | ____ ____ |__| ____ / _____/ ____ __ _________ ____ ____
|
||||
// | | / _ \ / ___\| |/ \ \_____ \ / _ \| | \_ __ \_/ ___\/ __ \
|
||||
// | |__( <_> ) /_/ > | | \ / ( <_> ) | /| | \/\ \__\ ___/
|
||||
// |_______ \____/\___ /|__|___| / /_______ /\____/|____/ |__| \___ >___ >
|
||||
// \/ /_____/ \/ \/ \/ \/
|
||||
|
||||
type ErrAuthenticationNotExist struct {
|
||||
type ErrLoginSourceNotExist struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
func IsErrAuthenticationNotExist(err error) bool {
|
||||
_, ok := err.(ErrAuthenticationNotExist)
|
||||
func IsErrLoginSourceNotExist(err error) bool {
|
||||
_, ok := err.(ErrLoginSourceNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrAuthenticationNotExist) Error() string {
|
||||
return fmt.Sprintf("authentication does not exist [id: %d]", err.ID)
|
||||
func (err ErrLoginSourceNotExist) Error() string {
|
||||
return fmt.Sprintf("login source does not exist [id: %d]", err.ID)
|
||||
}
|
||||
|
||||
type ErrLoginSourceAlreadyExist struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func IsErrLoginSourceAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrLoginSourceAlreadyExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrLoginSourceAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("login source already exists [name: %s]", err.Name)
|
||||
}
|
||||
|
||||
type ErrLoginSourceInUse struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
func IsErrLoginSourceInUse(err error) bool {
|
||||
_, ok := err.(ErrLoginSourceInUse)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrLoginSourceInUse) Error() string {
|
||||
return fmt.Sprintf("login source is still used by some users [id: %d]", err.ID)
|
||||
}
|
||||
|
||||
// ___________
|
||||
@@ -582,3 +668,25 @@ func IsErrTeamAlreadyExist(err error) bool {
|
||||
func (err ErrTeamAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("team already exists [org_id: %d, name: %s]", err.OrgID, err.Name)
|
||||
}
|
||||
|
||||
// ____ ___ .__ .___
|
||||
// | | \______ | | _________ __| _/
|
||||
// | | /\____ \| | / _ \__ \ / __ |
|
||||
// | | / | |_> > |_( <_> ) __ \_/ /_/ |
|
||||
// |______/ | __/|____/\____(____ /\____ |
|
||||
// |__| \/ \/
|
||||
//
|
||||
|
||||
type ErrUploadNotExist struct {
|
||||
ID int64
|
||||
UUID string
|
||||
}
|
||||
|
||||
func IsErrUploadNotExist(err error) bool {
|
||||
_, ok := err.(ErrAttachmentNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUploadNotExist) Error() string {
|
||||
return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
|
||||
}
|
||||
|
||||
@@ -20,12 +20,14 @@ import (
|
||||
"github.com/sergi/go-diff/diffmatchpatch"
|
||||
"golang.org/x/net/html/charset"
|
||||
"golang.org/x/text/transform"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
"github.com/gogits/git-module"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/gogits/gogs/modules/template/highlight"
|
||||
)
|
||||
|
||||
type DiffLineType uint8
|
||||
@@ -47,10 +49,10 @@ const (
|
||||
)
|
||||
|
||||
type DiffLine struct {
|
||||
LeftIdx int
|
||||
RightIdx int
|
||||
Type DiffLineType
|
||||
Content string
|
||||
LeftIdx int
|
||||
RightIdx int
|
||||
Type DiffLineType
|
||||
Content string
|
||||
}
|
||||
|
||||
func (d *DiffLine) GetType() int {
|
||||
@@ -69,17 +71,27 @@ var (
|
||||
)
|
||||
|
||||
func diffToHTML(diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTML {
|
||||
var buf bytes.Buffer
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
// Reproduce signs which are cutted for inline diff before.
|
||||
switch lineType {
|
||||
case DIFF_LINE_ADD:
|
||||
buf.WriteByte('+')
|
||||
case DIFF_LINE_DEL:
|
||||
buf.WriteByte('-')
|
||||
}
|
||||
|
||||
for i := range diffs {
|
||||
if diffs[i].Type == diffmatchpatch.DiffInsert && lineType == DIFF_LINE_ADD {
|
||||
switch {
|
||||
case diffs[i].Type == diffmatchpatch.DiffInsert && lineType == DIFF_LINE_ADD:
|
||||
buf.Write(addedCodePrefix)
|
||||
buf.WriteString(html.EscapeString(diffs[i].Text))
|
||||
buf.Write(codeTagSuffix)
|
||||
} else if diffs[i].Type == diffmatchpatch.DiffDelete && lineType == DIFF_LINE_DEL {
|
||||
case diffs[i].Type == diffmatchpatch.DiffDelete && lineType == DIFF_LINE_DEL:
|
||||
buf.Write(removedCodePrefix)
|
||||
buf.WriteString(html.EscapeString(diffs[i].Text))
|
||||
buf.Write(codeTagSuffix)
|
||||
} else if diffs[i].Type == diffmatchpatch.DiffEqual {
|
||||
case diffs[i].Type == diffmatchpatch.DiffEqual:
|
||||
buf.WriteString(html.EscapeString(diffs[i].Text))
|
||||
}
|
||||
}
|
||||
@@ -89,62 +101,86 @@ func diffToHTML(diffs []diffmatchpatch.Diff, lineType DiffLineType) template.HTM
|
||||
|
||||
// get an specific line by type (add or del) and file line number
|
||||
func (diffSection *DiffSection) GetLine(lineType DiffLineType, idx int) *DiffLine {
|
||||
difference := 0
|
||||
var (
|
||||
difference = 0
|
||||
addCount = 0
|
||||
delCount = 0
|
||||
matchDiffLine *DiffLine
|
||||
)
|
||||
|
||||
LOOP:
|
||||
for _, diffLine := range diffSection.Lines {
|
||||
if diffLine.Type == DIFF_LINE_PLAIN {
|
||||
// get the difference of line numbers between ADD and DEL versions
|
||||
switch diffLine.Type {
|
||||
case DIFF_LINE_ADD:
|
||||
addCount++
|
||||
case DIFF_LINE_DEL:
|
||||
delCount++
|
||||
default:
|
||||
if matchDiffLine != nil {
|
||||
break LOOP
|
||||
}
|
||||
difference = diffLine.RightIdx - diffLine.LeftIdx
|
||||
continue
|
||||
addCount = 0
|
||||
delCount = 0
|
||||
}
|
||||
|
||||
if lineType == DIFF_LINE_DEL {
|
||||
switch lineType {
|
||||
case DIFF_LINE_DEL:
|
||||
if diffLine.RightIdx == 0 && diffLine.LeftIdx == idx-difference {
|
||||
return diffLine
|
||||
matchDiffLine = diffLine
|
||||
}
|
||||
} else if lineType == DIFF_LINE_ADD {
|
||||
case DIFF_LINE_ADD:
|
||||
if diffLine.LeftIdx == 0 && diffLine.RightIdx == idx+difference {
|
||||
return diffLine
|
||||
matchDiffLine = diffLine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if addCount == delCount {
|
||||
return matchDiffLine
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var diffMatchPatch = diffmatchpatch.New()
|
||||
|
||||
func init() {
|
||||
diffMatchPatch.DiffEditCost = 100
|
||||
}
|
||||
|
||||
// computes inline diff for the given line
|
||||
func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) template.HTML {
|
||||
var compareDiffLine *DiffLine
|
||||
var diff1, diff2 string
|
||||
|
||||
getDefaultReturn := func() template.HTML {
|
||||
if setting.Git.DisableDiffHighlight {
|
||||
return template.HTML(html.EscapeString(diffLine.Content[1:]))
|
||||
}
|
||||
|
||||
// just compute diff for adds and removes
|
||||
if diffLine.Type != DIFF_LINE_ADD && diffLine.Type != DIFF_LINE_DEL {
|
||||
return getDefaultReturn()
|
||||
}
|
||||
var (
|
||||
compareDiffLine *DiffLine
|
||||
diff1 string
|
||||
diff2 string
|
||||
)
|
||||
|
||||
// try to find equivalent diff line. ignore, otherwise
|
||||
if diffLine.Type == DIFF_LINE_ADD {
|
||||
switch diffLine.Type {
|
||||
case DIFF_LINE_ADD:
|
||||
compareDiffLine = diffSection.GetLine(DIFF_LINE_DEL, diffLine.RightIdx)
|
||||
if compareDiffLine == nil {
|
||||
return getDefaultReturn()
|
||||
return template.HTML(html.EscapeString(diffLine.Content))
|
||||
}
|
||||
diff1 = compareDiffLine.Content
|
||||
diff2 = diffLine.Content
|
||||
} else {
|
||||
case DIFF_LINE_DEL:
|
||||
compareDiffLine = diffSection.GetLine(DIFF_LINE_ADD, diffLine.LeftIdx)
|
||||
if compareDiffLine == nil {
|
||||
return getDefaultReturn()
|
||||
return template.HTML(html.EscapeString(diffLine.Content))
|
||||
}
|
||||
diff1 = diffLine.Content
|
||||
diff2 = compareDiffLine.Content
|
||||
default:
|
||||
return template.HTML(html.EscapeString(diffLine.Content))
|
||||
}
|
||||
|
||||
dmp := diffmatchpatch.New()
|
||||
diffRecord := dmp.DiffMain(diff1[1:], diff2[1:], true)
|
||||
diffRecord = dmp.DiffCleanupSemantic(diffRecord)
|
||||
diffRecord := diffMatchPatch.DiffMain(diff1[1:], diff2[1:], true)
|
||||
diffRecord = diffMatchPatch.DiffCleanupEfficiency(diffRecord)
|
||||
|
||||
return diffToHTML(diffRecord, diffLine.Type)
|
||||
}
|
||||
@@ -159,16 +195,23 @@ type DiffFile struct {
|
||||
IsDeleted bool
|
||||
IsBin bool
|
||||
IsRenamed bool
|
||||
IsSubmodule bool
|
||||
Sections []*DiffSection
|
||||
IsIncomplete bool
|
||||
}
|
||||
|
||||
func (diffFile *DiffFile) GetType() int {
|
||||
return int(diffFile.Type)
|
||||
}
|
||||
|
||||
func (diffFile *DiffFile) GetHighlightClass() string {
|
||||
return highlight.FileNameToHighlightClass(diffFile.Name)
|
||||
}
|
||||
|
||||
type Diff struct {
|
||||
TotalAddition, TotalDeletion int
|
||||
Files []*DiffFile
|
||||
IsIncomplete bool
|
||||
}
|
||||
|
||||
func (diff *Diff) NumFiles() int {
|
||||
@@ -177,7 +220,8 @@ func (diff *Diff) NumFiles() int {
|
||||
|
||||
const DIFF_HEAD = "diff --git "
|
||||
|
||||
func ParsePatch(maxlines int, reader io.Reader) (*Diff, error) {
|
||||
// TODO: move this function to gogits/git-module
|
||||
func ParsePatch(maxLines, maxLineCharacteres, maxFiles int, reader io.Reader) (*Diff, error) {
|
||||
var (
|
||||
diff = &Diff{Files: make([]*DiffFile, 0)}
|
||||
|
||||
@@ -188,15 +232,12 @@ func ParsePatch(maxlines int, reader io.Reader) (*Diff, error) {
|
||||
|
||||
leftLine, rightLine int
|
||||
lineCount int
|
||||
curFileLinesCount int
|
||||
)
|
||||
|
||||
input := bufio.NewReader(reader)
|
||||
isEOF := false
|
||||
for {
|
||||
if isEOF {
|
||||
break
|
||||
}
|
||||
|
||||
for !isEOF {
|
||||
line, err := input.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
@@ -211,20 +252,16 @@ func ParsePatch(maxlines int, reader io.Reader) (*Diff, error) {
|
||||
line = line[:len(line)-1]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, "+++ ") || strings.HasPrefix(line, "--- ") {
|
||||
continue
|
||||
} else if len(line) == 0 {
|
||||
if strings.HasPrefix(line, "+++ ") || strings.HasPrefix(line, "--- ") || len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
curFileLinesCount++
|
||||
lineCount++
|
||||
|
||||
// Diff data too large, we only show the first about maxlines lines
|
||||
if lineCount >= maxlines {
|
||||
log.Warn("Diff data too large")
|
||||
io.Copy(ioutil.Discard, reader)
|
||||
diff.Files = nil
|
||||
return diff, nil
|
||||
if curFileLinesCount >= maxLines || len(line) >= maxLineCharacteres {
|
||||
curFile.IsIncomplete = true
|
||||
}
|
||||
|
||||
switch {
|
||||
@@ -299,8 +336,14 @@ func ParsePatch(maxlines int, reader io.Reader) (*Diff, error) {
|
||||
Sections: make([]*DiffSection, 0, 10),
|
||||
}
|
||||
diff.Files = append(diff.Files, curFile)
|
||||
if len(diff.Files) >= maxFiles {
|
||||
diff.IsIncomplete = true
|
||||
io.Copy(ioutil.Discard, reader)
|
||||
break
|
||||
}
|
||||
curFileLinesCount = 0
|
||||
|
||||
// Check file diff type.
|
||||
// Check file diff type and is submodule.
|
||||
for {
|
||||
line, err := input.ReadString('\n')
|
||||
if err != nil {
|
||||
@@ -327,6 +370,9 @@ func ParsePatch(maxlines int, reader io.Reader) (*Diff, error) {
|
||||
curFile.Name = b
|
||||
}
|
||||
if curFile.Type > 0 {
|
||||
if strings.HasSuffix(line, " 160000\n") {
|
||||
curFile.IsSubmodule = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -361,13 +407,13 @@ func ParsePatch(maxlines int, reader io.Reader) (*Diff, error) {
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
func GetDiffRange(repoPath, beforeCommitID string, afterCommitID string, maxlines int) (*Diff, error) {
|
||||
repo, err := git.OpenRepository(repoPath)
|
||||
func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
|
||||
gitRepo, err := git.OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commit, err := repo.GetCommit(afterCommitID)
|
||||
commit, err := gitRepo.GetCommit(afterCommitID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -397,10 +443,10 @@ func GetDiffRange(repoPath, beforeCommitID string, afterCommitID string, maxline
|
||||
return nil, fmt.Errorf("Start: %v", err)
|
||||
}
|
||||
|
||||
pid := process.Add(fmt.Sprintf("GetDiffRange (%s)", repoPath), cmd)
|
||||
pid := process.Add(fmt.Sprintf("GetDiffRange [repo_path: %s]", repoPath), cmd)
|
||||
defer process.Remove(pid)
|
||||
|
||||
diff, err := ParsePatch(maxlines, stdout)
|
||||
diff, err := ParsePatch(maxLines, maxLineCharacteres, maxFiles, stdout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ParsePatch: %v", err)
|
||||
}
|
||||
@@ -412,6 +458,59 @@ func GetDiffRange(repoPath, beforeCommitID string, afterCommitID string, maxline
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
func GetDiffCommit(repoPath, commitId string, maxlines int) (*Diff, error) {
|
||||
return GetDiffRange(repoPath, "", commitId, maxlines)
|
||||
type RawDiffType string
|
||||
|
||||
const (
|
||||
RAW_DIFF_NORMAL RawDiffType = "diff"
|
||||
RAW_DIFF_PATCH RawDiffType = "patch"
|
||||
)
|
||||
|
||||
// GetRawDiff dumps diff results of repository in given commit ID to io.Writer.
|
||||
// TODO: move this function to gogits/git-module
|
||||
func GetRawDiff(repoPath, commitID string, diffType RawDiffType, writer io.Writer) error {
|
||||
repo, err := git.OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("OpenRepository: %v", err)
|
||||
}
|
||||
|
||||
commit, err := repo.GetCommit(commitID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetCommit: %v", err)
|
||||
}
|
||||
|
||||
var cmd *exec.Cmd
|
||||
switch diffType {
|
||||
case RAW_DIFF_NORMAL:
|
||||
if commit.ParentCount() == 0 {
|
||||
cmd = exec.Command("git", "show", commitID)
|
||||
} else {
|
||||
c, _ := commit.Parent(0)
|
||||
cmd = exec.Command("git", "diff", "-M", c.ID.String(), commitID)
|
||||
}
|
||||
case RAW_DIFF_PATCH:
|
||||
if commit.ParentCount() == 0 {
|
||||
cmd = exec.Command("git", "format-patch", "--no-signature", "--stdout", "--root", commitID)
|
||||
} else {
|
||||
c, _ := commit.Parent(0)
|
||||
query := fmt.Sprintf("%s...%s", commitID, c.ID.String())
|
||||
cmd = exec.Command("git", "format-patch", "--no-signature", "--stdout", query)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid diffType: %s", diffType)
|
||||
}
|
||||
|
||||
stderr := new(bytes.Buffer)
|
||||
|
||||
cmd.Dir = repoPath
|
||||
cmd.Stdout = writer
|
||||
cmd.Stderr = stderr
|
||||
|
||||
if err = cmd.Run(); err != nil {
|
||||
return fmt.Errorf("Run: %v - %s", err, stderr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetDiffCommit(repoPath, commitID string, maxLines, maxLineCharacteres, maxFiles int) (*Diff, error) {
|
||||
return GetDiffRange(repoPath, "", commitID, maxLines, maxLineCharacteres, maxFiles)
|
||||
}
|
||||
|
||||
@@ -1,70 +1,35 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
dmp "github.com/sergi/go-diff/diffmatchpatch"
|
||||
"html/template"
|
||||
"testing"
|
||||
dmp "github.com/sergi/go-diff/diffmatchpatch"
|
||||
"html/template"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func assertEqual(t *testing.T, s1 string, s2 template.HTML) {
|
||||
if s1 != string(s2) {
|
||||
t.Errorf("%s should be equal %s", s2, s1)
|
||||
}
|
||||
if s1 != string(s2) {
|
||||
t.Errorf("%s should be equal %s", s2, s1)
|
||||
}
|
||||
}
|
||||
|
||||
func assertLineEqual(t *testing.T, d1 *DiffLine, d2 *DiffLine) {
|
||||
if d1 != d2 {
|
||||
t.Errorf("%v should be equal %v", d1, d2)
|
||||
}
|
||||
if d1 != d2 {
|
||||
t.Errorf("%v should be equal %v", d1, d2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiffToHTML(t *testing.T) {
|
||||
assertEqual(t, "foo <span class=\"added-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
||||
dmp.Diff{dmp.DiffEqual, "foo "},
|
||||
dmp.Diff{dmp.DiffInsert, "bar"},
|
||||
dmp.Diff{dmp.DiffDelete, " baz"},
|
||||
dmp.Diff{dmp.DiffEqual, " biz"},
|
||||
}, DIFF_LINE_ADD))
|
||||
assertEqual(t, "+foo <span class=\"added-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
||||
dmp.Diff{dmp.DiffEqual, "foo "},
|
||||
dmp.Diff{dmp.DiffInsert, "bar"},
|
||||
dmp.Diff{dmp.DiffDelete, " baz"},
|
||||
dmp.Diff{dmp.DiffEqual, " biz"},
|
||||
}, DIFF_LINE_ADD))
|
||||
|
||||
assertEqual(t, "foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
||||
dmp.Diff{dmp.DiffEqual, "foo "},
|
||||
dmp.Diff{dmp.DiffDelete, "bar"},
|
||||
dmp.Diff{dmp.DiffInsert, " baz"},
|
||||
dmp.Diff{dmp.DiffEqual, " biz"},
|
||||
}, DIFF_LINE_DEL))
|
||||
}
|
||||
|
||||
// test if GetLine is return the correct lines
|
||||
func TestGetLine(t *testing.T) {
|
||||
ds := DiffSection{Lines: []*DiffLine{
|
||||
&DiffLine{LeftIdx: 28, RightIdx: 28, Type: DIFF_LINE_PLAIN},
|
||||
&DiffLine{LeftIdx: 29, RightIdx: 29, Type: DIFF_LINE_PLAIN},
|
||||
&DiffLine{LeftIdx: 30, RightIdx: 30, Type: DIFF_LINE_PLAIN},
|
||||
&DiffLine{LeftIdx: 31, RightIdx: 0, Type: DIFF_LINE_DEL},
|
||||
&DiffLine{LeftIdx: 0, RightIdx: 31, Type: DIFF_LINE_ADD},
|
||||
&DiffLine{LeftIdx: 0, RightIdx: 32, Type: DIFF_LINE_ADD},
|
||||
&DiffLine{LeftIdx: 32, RightIdx: 33, Type: DIFF_LINE_PLAIN},
|
||||
&DiffLine{LeftIdx: 33, RightIdx: 0, Type: DIFF_LINE_DEL},
|
||||
&DiffLine{LeftIdx: 34, RightIdx: 0, Type: DIFF_LINE_DEL},
|
||||
&DiffLine{LeftIdx: 35, RightIdx: 0, Type: DIFF_LINE_DEL},
|
||||
&DiffLine{LeftIdx: 36, RightIdx: 0, Type: DIFF_LINE_DEL},
|
||||
&DiffLine{LeftIdx: 0, RightIdx: 34, Type: DIFF_LINE_ADD},
|
||||
&DiffLine{LeftIdx: 0, RightIdx: 35, Type: DIFF_LINE_ADD},
|
||||
&DiffLine{LeftIdx: 0, RightIdx: 36, Type: DIFF_LINE_ADD},
|
||||
&DiffLine{LeftIdx: 0, RightIdx: 37, Type: DIFF_LINE_ADD},
|
||||
&DiffLine{LeftIdx: 37, RightIdx: 38, Type: DIFF_LINE_PLAIN},
|
||||
&DiffLine{LeftIdx: 38, RightIdx: 39, Type: DIFF_LINE_PLAIN},
|
||||
}}
|
||||
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 31), ds.Lines[4])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 31), ds.Lines[3])
|
||||
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 33), ds.Lines[11])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 34), ds.Lines[12])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 35), ds.Lines[13])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_ADD, 36), ds.Lines[14])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 34), ds.Lines[7])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 35), ds.Lines[8])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 36), ds.Lines[9])
|
||||
assertLineEqual(t, ds.GetLine(DIFF_LINE_DEL, 37), ds.Lines[10])
|
||||
assertEqual(t, "-foo <span class=\"removed-code\">bar</span> biz", diffToHTML([]dmp.Diff{
|
||||
dmp.Diff{dmp.DiffEqual, "foo "},
|
||||
dmp.Diff{dmp.DiffDelete, "bar"},
|
||||
dmp.Diff{dmp.DiffInsert, " baz"},
|
||||
dmp.Diff{dmp.DiffEqual, " biz"},
|
||||
}, DIFF_LINE_DEL))
|
||||
}
|
||||
|
||||
2032
models/issue.go
2032
models/issue.go
File diff suppressed because it is too large
Load Diff
332
models/issue_label.go
Normal file
332
models/issue_label.go
Normal file
@@ -0,0 +1,332 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
|
||||
api "github.com/gogits/go-gogs-client"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
)
|
||||
|
||||
var labelColorPattern = regexp.MustCompile("#([a-fA-F0-9]{6})")
|
||||
|
||||
// GetLabelTemplateFile loads the label template file by given name,
|
||||
// then parses and returns a list of name-color pairs.
|
||||
func GetLabelTemplateFile(name string) ([][2]string, error) {
|
||||
data, err := getRepoInitFile("label", name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getRepoInitFile: %v", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(data), "\n")
|
||||
list := make([][2]string, 0, len(lines))
|
||||
for i := 0; i < len(lines); i++ {
|
||||
line := strings.TrimSpace(lines[i])
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
fields := strings.SplitN(line, " ", 2)
|
||||
if len(fields) != 2 {
|
||||
return nil, fmt.Errorf("line is malformed: %s", line)
|
||||
}
|
||||
|
||||
if !labelColorPattern.MatchString(fields[0]) {
|
||||
return nil, fmt.Errorf("bad HTML color code in line: %s", line)
|
||||
}
|
||||
|
||||
fields[1] = strings.TrimSpace(fields[1])
|
||||
list = append(list, [2]string{fields[1], fields[0]})
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// Label represents a label of repository for issues.
|
||||
type Label struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX"`
|
||||
Name string
|
||||
Color string `xorm:"VARCHAR(7)"`
|
||||
NumIssues int
|
||||
NumClosedIssues int
|
||||
NumOpenIssues int `xorm:"-"`
|
||||
IsChecked bool `xorm:"-"`
|
||||
}
|
||||
|
||||
func (label *Label) APIFormat() *api.Label {
|
||||
return &api.Label{
|
||||
ID: label.ID,
|
||||
Name: label.Name,
|
||||
Color: label.Color,
|
||||
}
|
||||
}
|
||||
|
||||
// CalOpenIssues calculates the open issues of label.
|
||||
func (label *Label) CalOpenIssues() {
|
||||
label.NumOpenIssues = label.NumIssues - label.NumClosedIssues
|
||||
}
|
||||
|
||||
// ForegroundColor calculates the text color for labels based
|
||||
// on their background color.
|
||||
func (l *Label) ForegroundColor() template.CSS {
|
||||
if strings.HasPrefix(l.Color, "#") {
|
||||
if color, err := strconv.ParseUint(l.Color[1:], 16, 64); err == nil {
|
||||
r := float32(0xFF & (color >> 16))
|
||||
g := float32(0xFF & (color >> 8))
|
||||
b := float32(0xFF & color)
|
||||
luminance := (0.2126*r + 0.7152*g + 0.0722*b) / 255
|
||||
|
||||
if luminance < 0.66 {
|
||||
return template.CSS("#fff")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default to black
|
||||
return template.CSS("#000")
|
||||
}
|
||||
|
||||
// NewLabels creates new label(s) for a repository.
|
||||
func NewLabels(labels ...*Label) error {
|
||||
_, err := x.Insert(labels)
|
||||
return err
|
||||
}
|
||||
|
||||
// getLabelOfRepoByID returns a label by ID in given repository.
|
||||
// If pass repoID as 0, then ORM will ignore limitation of repository
|
||||
// and can return arbitrary label with any valid ID.
|
||||
func getLabelOfRepoByID(e Engine, repoID, labelID int64) (*Label, error) {
|
||||
if labelID <= 0 {
|
||||
return nil, ErrLabelNotExist{labelID, repoID}
|
||||
}
|
||||
|
||||
l := &Label{
|
||||
ID: labelID,
|
||||
RepoID: repoID,
|
||||
}
|
||||
has, err := x.Get(l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrLabelNotExist{l.ID, l.RepoID}
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// GetLabelByID returns a label by given ID.
|
||||
func GetLabelByID(id int64) (*Label, error) {
|
||||
return getLabelOfRepoByID(x, 0, id)
|
||||
}
|
||||
|
||||
// GetLabelOfRepoByID returns a label by ID in given repository.
|
||||
func GetLabelOfRepoByID(repoID, labelID int64) (*Label, error) {
|
||||
return getLabelOfRepoByID(x, repoID, labelID)
|
||||
}
|
||||
|
||||
// GetLabelsInRepoByIDs returns a list of labels by IDs in given repository,
|
||||
// it silently ignores label IDs that are not belong to the repository.
|
||||
func GetLabelsInRepoByIDs(repoID int64, labelIDs []int64) ([]*Label, error) {
|
||||
labels := make([]*Label, 0, len(labelIDs))
|
||||
return labels, x.Where("repo_id = ?", repoID).In("id", base.Int64sToStrings(labelIDs)).Asc("name").Find(&labels)
|
||||
}
|
||||
|
||||
// GetLabelsByRepoID returns all labels that belong to given repository by ID.
|
||||
func GetLabelsByRepoID(repoID int64) ([]*Label, error) {
|
||||
labels := make([]*Label, 0, 10)
|
||||
return labels, x.Where("repo_id = ?", repoID).Asc("name").Find(&labels)
|
||||
}
|
||||
|
||||
func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) {
|
||||
issueLabels, err := getIssueLabels(e, issueID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getIssueLabels: %v", err)
|
||||
} else if len(issueLabels) == 0 {
|
||||
return []*Label{}, nil
|
||||
}
|
||||
|
||||
labelIDs := make([]int64, len(issueLabels))
|
||||
for i := range issueLabels {
|
||||
labelIDs[i] = issueLabels[i].LabelID
|
||||
}
|
||||
|
||||
labels := make([]*Label, 0, len(labelIDs))
|
||||
return labels, e.Where("id > 0").In("id", base.Int64sToStrings(labelIDs)).Asc("name").Find(&labels)
|
||||
}
|
||||
|
||||
// GetLabelsByIssueID returns all labels that belong to given issue by ID.
|
||||
func GetLabelsByIssueID(issueID int64) ([]*Label, error) {
|
||||
return getLabelsByIssueID(x, issueID)
|
||||
}
|
||||
|
||||
func updateLabel(e Engine, l *Label) error {
|
||||
_, err := e.Id(l.ID).AllCols().Update(l)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateLabel updates label information.
|
||||
func UpdateLabel(l *Label) error {
|
||||
return updateLabel(x, l)
|
||||
}
|
||||
|
||||
// DeleteLabel delete a label of given repository.
|
||||
func DeleteLabel(repoID, labelID int64) error {
|
||||
_, err := GetLabelOfRepoByID(repoID, labelID)
|
||||
if err != nil {
|
||||
if IsErrLabelNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.Id(labelID).Delete(new(Label)); err != nil {
|
||||
return err
|
||||
} else if _, err = sess.Where("label_id = ?", labelID).Delete(new(IssueLabel)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// .___ .____ ___. .__
|
||||
// | | ______ ________ __ ____ | | _____ \_ |__ ____ | |
|
||||
// | |/ ___// ___/ | \_/ __ \| | \__ \ | __ \_/ __ \| |
|
||||
// | |\___ \ \___ \| | /\ ___/| |___ / __ \| \_\ \ ___/| |__
|
||||
// |___/____ >____ >____/ \___ >_______ (____ /___ /\___ >____/
|
||||
// \/ \/ \/ \/ \/ \/ \/
|
||||
|
||||
// IssueLabel represetns an issue-lable relation.
|
||||
type IssueLabel struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
IssueID int64 `xorm:"UNIQUE(s)"`
|
||||
LabelID int64 `xorm:"UNIQUE(s)"`
|
||||
}
|
||||
|
||||
func hasIssueLabel(e Engine, issueID, labelID int64) bool {
|
||||
has, _ := e.Where("issue_id = ? AND label_id = ?", issueID, labelID).Get(new(IssueLabel))
|
||||
return has
|
||||
}
|
||||
|
||||
// HasIssueLabel returns true if issue has been labeled.
|
||||
func HasIssueLabel(issueID, labelID int64) bool {
|
||||
return hasIssueLabel(x, issueID, labelID)
|
||||
}
|
||||
|
||||
func newIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
|
||||
if _, err = e.Insert(&IssueLabel{
|
||||
IssueID: issue.ID,
|
||||
LabelID: label.ID,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
label.NumIssues++
|
||||
if issue.IsClosed {
|
||||
label.NumClosedIssues++
|
||||
}
|
||||
return updateLabel(e, label)
|
||||
}
|
||||
|
||||
// NewIssueLabel creates a new issue-label relation.
|
||||
func NewIssueLabel(issue *Issue, label *Label) (err error) {
|
||||
if HasIssueLabel(issue.ID, label.ID) {
|
||||
return nil
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = newIssueLabel(sess, issue, label); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func newIssueLabels(e *xorm.Session, issue *Issue, labels []*Label) (err error) {
|
||||
for i := range labels {
|
||||
if hasIssueLabel(e, issue.ID, labels[i].ID) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = newIssueLabel(e, issue, labels[i]); err != nil {
|
||||
return fmt.Errorf("newIssueLabel: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewIssueLabels creates a list of issue-label relations.
|
||||
func NewIssueLabels(issue *Issue, labels []*Label) (err error) {
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = newIssueLabels(sess, issue, labels); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func getIssueLabels(e Engine, issueID int64) ([]*IssueLabel, error) {
|
||||
issueLabels := make([]*IssueLabel, 0, 10)
|
||||
return issueLabels, e.Where("issue_id=?", issueID).Asc("label_id").Find(&issueLabels)
|
||||
}
|
||||
|
||||
// GetIssueLabels returns all issue-label relations of given issue by ID.
|
||||
func GetIssueLabels(issueID int64) ([]*IssueLabel, error) {
|
||||
return getIssueLabels(x, issueID)
|
||||
}
|
||||
|
||||
func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
|
||||
if _, err = e.Delete(&IssueLabel{
|
||||
IssueID: issue.ID,
|
||||
LabelID: label.ID,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
label.NumIssues--
|
||||
if issue.IsClosed {
|
||||
label.NumClosedIssues--
|
||||
}
|
||||
return updateLabel(e, label)
|
||||
}
|
||||
|
||||
// DeleteIssueLabel deletes issue-label relation.
|
||||
func DeleteIssueLabel(issue *Issue, label *Label) (err error) {
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = deleteIssueLabel(sess, issue, label); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
153
models/issue_mail.go
Normal file
153
models/issue_mail.go
Normal file
@@ -0,0 +1,153 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
"github.com/gogits/gogs/modules/mailer"
|
||||
"github.com/gogits/gogs/modules/markdown"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
func (issue *Issue) MailSubject() string {
|
||||
return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.Name, issue.Title, issue.Index)
|
||||
}
|
||||
|
||||
// mailerUser is a wrapper for satisfying mailer.User interface.
|
||||
type mailerUser struct {
|
||||
user *User
|
||||
}
|
||||
|
||||
func (this mailerUser) ID() int64 {
|
||||
return this.user.ID
|
||||
}
|
||||
|
||||
func (this mailerUser) DisplayName() string {
|
||||
return this.user.DisplayName()
|
||||
}
|
||||
|
||||
func (this mailerUser) Email() string {
|
||||
return this.user.Email
|
||||
}
|
||||
|
||||
func (this mailerUser) GenerateActivateCode() string {
|
||||
return this.user.GenerateActivateCode()
|
||||
}
|
||||
|
||||
func (this mailerUser) GenerateEmailActivateCode(email string) string {
|
||||
return this.user.GenerateEmailActivateCode(email)
|
||||
}
|
||||
|
||||
func NewMailerUser(u *User) mailer.User {
|
||||
return mailerUser{u}
|
||||
}
|
||||
|
||||
// mailerRepo is a wrapper for satisfying mailer.Repository interface.
|
||||
type mailerRepo struct {
|
||||
repo *Repository
|
||||
}
|
||||
|
||||
func (this mailerRepo) FullName() string {
|
||||
return this.repo.FullName()
|
||||
}
|
||||
|
||||
func (this mailerRepo) HTMLURL() string {
|
||||
return this.repo.HTMLURL()
|
||||
}
|
||||
|
||||
func (this mailerRepo) ComposeMetas() map[string]string {
|
||||
return this.repo.ComposeMetas()
|
||||
}
|
||||
|
||||
func NewMailerRepo(repo *Repository) mailer.Repository {
|
||||
return mailerRepo{repo}
|
||||
}
|
||||
|
||||
// mailerIssue is a wrapper for satisfying mailer.Issue interface.
|
||||
type mailerIssue struct {
|
||||
issue *Issue
|
||||
}
|
||||
|
||||
func (this mailerIssue) MailSubject() string {
|
||||
return this.issue.MailSubject()
|
||||
}
|
||||
|
||||
func (this mailerIssue) Content() string {
|
||||
return this.issue.Content
|
||||
}
|
||||
|
||||
func (this mailerIssue) HTMLURL() string {
|
||||
return this.issue.HTMLURL()
|
||||
}
|
||||
|
||||
func NewMailerIssue(issue *Issue) mailer.Issue {
|
||||
return mailerIssue{issue}
|
||||
}
|
||||
|
||||
// mailIssueCommentToParticipants can be used for both new issue creation and comment.
|
||||
func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string) error {
|
||||
if !setting.Service.EnableNotifyMail {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mail wahtcers.
|
||||
watchers, err := GetWatchers(issue.RepoID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetWatchers [%d]: %v", issue.RepoID, err)
|
||||
}
|
||||
|
||||
tos := make([]string, 0, len(watchers)) // List of email addresses.
|
||||
names := make([]string, 0, len(watchers))
|
||||
for i := range watchers {
|
||||
if watchers[i].UserID == doer.ID {
|
||||
continue
|
||||
}
|
||||
|
||||
to, err := GetUserByID(watchers[i].UserID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetUserByID [%d]: %v", watchers[i].UserID, err)
|
||||
}
|
||||
if to.IsOrganization() {
|
||||
continue
|
||||
}
|
||||
|
||||
tos = append(tos, to.Email)
|
||||
names = append(names, to.Name)
|
||||
}
|
||||
mailer.SendIssueCommentMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), tos)
|
||||
|
||||
// Mail mentioned people and exclude watchers.
|
||||
names = append(names, doer.Name)
|
||||
tos = make([]string, 0, len(mentions)) // list of user names.
|
||||
for i := range mentions {
|
||||
if com.IsSliceContainsStr(names, mentions[i]) {
|
||||
continue
|
||||
}
|
||||
|
||||
tos = append(tos, mentions[i])
|
||||
}
|
||||
mailer.SendIssueMentionMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), GetUserEmailsByNames(tos))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MailParticipants sends new issue thread created emails to repository watchers
|
||||
// and mentioned people.
|
||||
func (issue *Issue) MailParticipants() (err error) {
|
||||
mentions := markdown.FindAllMentions(issue.Content)
|
||||
if err = updateIssueMentions(x, issue.ID, mentions); err != nil {
|
||||
return fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err)
|
||||
}
|
||||
|
||||
if err = mailIssueCommentToParticipants(issue, issue.Poster, mentions); err != nil {
|
||||
log.Error(4, "mailIssueCommentToParticipants: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -15,38 +15,40 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-macaron/binding"
|
||||
"github.com/go-xorm/core"
|
||||
"github.com/go-xorm/xorm"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
"github.com/gogits/gogs/modules/auth/ldap"
|
||||
"github.com/gogits/gogs/modules/auth/pam"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
)
|
||||
|
||||
type LoginType int
|
||||
|
||||
// Note: new type must be added at the end of list to maintain compatibility.
|
||||
// Note: new type must append to the end of list to maintain compatibility.
|
||||
const (
|
||||
LOGIN_NOTYPE LoginType = iota
|
||||
LOGIN_PLAIN
|
||||
LOGIN_LDAP
|
||||
LOGIN_SMTP
|
||||
LOGIN_PAM
|
||||
LOGIN_DLDAP
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAuthenticationAlreadyExist = errors.New("Authentication already exist")
|
||||
ErrAuthenticationUserUsed = errors.New("Authentication has been used by some users")
|
||||
LOGIN_PLAIN // 1
|
||||
LOGIN_LDAP // 2
|
||||
LOGIN_SMTP // 3
|
||||
LOGIN_PAM // 4
|
||||
LOGIN_DLDAP // 5
|
||||
)
|
||||
|
||||
var LoginNames = map[LoginType]string{
|
||||
LOGIN_LDAP: "LDAP (via BindDN)",
|
||||
LOGIN_DLDAP: "LDAP (simple auth)",
|
||||
LOGIN_DLDAP: "LDAP (simple auth)", // Via direct bind
|
||||
LOGIN_SMTP: "SMTP",
|
||||
LOGIN_PAM: "PAM",
|
||||
}
|
||||
|
||||
var SecurityProtocolNames = map[ldap.SecurityProtocol]string{
|
||||
ldap.SECURITY_PROTOCOL_UNENCRYPTED: "Unencrypted",
|
||||
ldap.SECURITY_PROTOCOL_LDAPS: "LDAPS",
|
||||
ldap.SECURITY_PROTOCOL_START_TLS: "StartTLS",
|
||||
}
|
||||
|
||||
// Ensure structs implemented interface.
|
||||
var (
|
||||
_ core.Conversion = &LDAPConfig{}
|
||||
@@ -66,6 +68,10 @@ func (cfg *LDAPConfig) ToDB() ([]byte, error) {
|
||||
return json.Marshal(cfg)
|
||||
}
|
||||
|
||||
func (cfg *LDAPConfig) SecurityProtocolName() string {
|
||||
return SecurityProtocolNames[cfg.SecurityProtocol]
|
||||
}
|
||||
|
||||
type SMTPConfig struct {
|
||||
Auth string
|
||||
Host string
|
||||
@@ -95,14 +101,27 @@ func (cfg *PAMConfig) ToDB() ([]byte, error) {
|
||||
return json.Marshal(cfg)
|
||||
}
|
||||
|
||||
// LoginSource represents an external way for authorizing users.
|
||||
type LoginSource struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Type LoginType
|
||||
Name string `xorm:"UNIQUE"`
|
||||
IsActived bool `xorm:"NOT NULL DEFAULT false"`
|
||||
Cfg core.Conversion `xorm:"TEXT"`
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
Updated time.Time `xorm:"UPDATED"`
|
||||
|
||||
Created time.Time `xorm:"-"`
|
||||
CreatedUnix int64
|
||||
Updated time.Time `xorm:"-"`
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
func (s *LoginSource) BeforeInsert() {
|
||||
s.CreatedUnix = time.Now().Unix()
|
||||
s.UpdatedUnix = s.CreatedUnix
|
||||
}
|
||||
|
||||
func (s *LoginSource) BeforeUpdate() {
|
||||
s.UpdatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
// Cell2Int64 converts a xorm.Cell type to int64,
|
||||
@@ -132,6 +151,15 @@ func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LoginSource) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created_unix":
|
||||
s.Created = time.Unix(s.CreatedUnix, 0).Local()
|
||||
case "updated_unix":
|
||||
s.Updated = time.Unix(s.UpdatedUnix, 0).Local()
|
||||
}
|
||||
}
|
||||
|
||||
func (source *LoginSource) TypeName() string {
|
||||
return LoginNames[source.Type]
|
||||
}
|
||||
@@ -152,10 +180,16 @@ func (source *LoginSource) IsPAM() bool {
|
||||
return source.Type == LOGIN_PAM
|
||||
}
|
||||
|
||||
func (source *LoginSource) HasTLS() bool {
|
||||
return ((source.IsLDAP() || source.IsDLDAP()) &&
|
||||
source.LDAP().SecurityProtocol > ldap.SECURITY_PROTOCOL_UNENCRYPTED) ||
|
||||
source.IsSMTP()
|
||||
}
|
||||
|
||||
func (source *LoginSource) UseTLS() bool {
|
||||
switch source.Type {
|
||||
case LOGIN_LDAP, LOGIN_DLDAP:
|
||||
return source.LDAP().UseSSL
|
||||
return source.LDAP().SecurityProtocol != ldap.SECURITY_PROTOCOL_UNENCRYPTED
|
||||
case LOGIN_SMTP:
|
||||
return source.SMTP().TLS
|
||||
}
|
||||
@@ -185,15 +219,15 @@ func (source *LoginSource) SMTP() *SMTPConfig {
|
||||
func (source *LoginSource) PAM() *PAMConfig {
|
||||
return source.Cfg.(*PAMConfig)
|
||||
}
|
||||
func CreateLoginSource(source *LoginSource) error {
|
||||
has, err := x.Get(&LoginSource{Name: source.Name})
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
return ErrLoginSourceAlreadyExist{source.Name}
|
||||
}
|
||||
|
||||
// CountLoginSources returns number of login sources.
|
||||
func CountLoginSources() int64 {
|
||||
count, _ := x.Count(new(LoginSource))
|
||||
return count
|
||||
}
|
||||
|
||||
func CreateSource(source *LoginSource) error {
|
||||
_, err := x.Insert(source)
|
||||
_, err = x.Insert(source)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -209,7 +243,7 @@ func GetLoginSourceByID(id int64) (*LoginSource, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrAuthenticationNotExist{id}
|
||||
return nil, ErrLoginSourceNotExist{id}
|
||||
}
|
||||
return source, nil
|
||||
}
|
||||
@@ -224,12 +258,18 @@ func DeleteSource(source *LoginSource) error {
|
||||
if err != nil {
|
||||
return err
|
||||
} else if count > 0 {
|
||||
return ErrAuthenticationUserUsed
|
||||
return ErrLoginSourceInUse{source.ID}
|
||||
}
|
||||
_, err = x.Id(source.ID).Delete(new(LoginSource))
|
||||
return err
|
||||
}
|
||||
|
||||
// CountLoginSources returns number of login sources.
|
||||
func CountLoginSources() int64 {
|
||||
count, _ := x.Count(new(LoginSource))
|
||||
return count
|
||||
}
|
||||
|
||||
// .____ ________ _____ __________
|
||||
// | | \______ \ / _ \\______ \
|
||||
// | | | | \ / /_\ \| ___/
|
||||
@@ -237,55 +277,57 @@ func DeleteSource(source *LoginSource) error {
|
||||
// |_______ \/_______ /\____|__ /____|
|
||||
// \/ \/ \/
|
||||
|
||||
// LoginUserLDAPSource queries if loginName/passwd can login against the LDAP directory pool,
|
||||
func composeFullName(firstname, surname, username string) string {
|
||||
switch {
|
||||
case len(firstname) == 0 && len(surname) == 0:
|
||||
return username
|
||||
case len(firstname) == 0:
|
||||
return surname
|
||||
case len(surname) == 0:
|
||||
return firstname
|
||||
default:
|
||||
return firstname + " " + surname
|
||||
}
|
||||
}
|
||||
|
||||
// LoginViaLDAP queries if login/password is valid against the LDAP directory pool,
|
||||
// and create a local user if success when enabled.
|
||||
// It returns the same LoginUserPlain semantic.
|
||||
func LoginUserLDAPSource(u *User, loginName, passwd string, source *LoginSource, autoRegister bool) (*User, error) {
|
||||
cfg := source.Cfg.(*LDAPConfig)
|
||||
directBind := (source.Type == LOGIN_DLDAP)
|
||||
name, fn, sn, mail, admin, logged := cfg.SearchEntry(loginName, passwd, directBind)
|
||||
if !logged {
|
||||
func LoginViaLDAP(user *User, login, password string, source *LoginSource, autoRegister bool) (*User, error) {
|
||||
username, fn, sn, mail, isAdmin, succeed := source.Cfg.(*LDAPConfig).SearchEntry(login, password, source.Type == LOGIN_DLDAP)
|
||||
if !succeed {
|
||||
// User not in LDAP, do nothing
|
||||
return nil, ErrUserNotExist{0, loginName}
|
||||
return nil, ErrUserNotExist{0, login}
|
||||
}
|
||||
|
||||
if !autoRegister {
|
||||
return u, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// Fallback.
|
||||
if len(name) == 0 {
|
||||
name = loginName
|
||||
if len(username) == 0 {
|
||||
username = login
|
||||
}
|
||||
if len(mail) == 0 {
|
||||
mail = fmt.Sprintf("%s@localhost", name)
|
||||
// Validate username make sure it satisfies requirement.
|
||||
if binding.AlphaDashDotPattern.MatchString(username) {
|
||||
return nil, fmt.Errorf("Invalid pattern for attribute 'username' [%s]: must be valid alpha or numeric or dash(-_) or dot characters", username)
|
||||
}
|
||||
|
||||
u = &User{
|
||||
LowerName: strings.ToLower(name),
|
||||
Name: name,
|
||||
FullName: composeFullName(fn, sn, name),
|
||||
if len(mail) == 0 {
|
||||
mail = fmt.Sprintf("%s@localhost", username)
|
||||
}
|
||||
|
||||
user = &User{
|
||||
LowerName: strings.ToLower(username),
|
||||
Name: username,
|
||||
FullName: composeFullName(fn, sn, username),
|
||||
Email: mail,
|
||||
LoginType: source.Type,
|
||||
LoginSource: source.ID,
|
||||
LoginName: loginName,
|
||||
Email: mail,
|
||||
IsAdmin: admin,
|
||||
LoginName: login,
|
||||
IsActive: true,
|
||||
IsAdmin: isAdmin,
|
||||
}
|
||||
return u, CreateUser(u)
|
||||
}
|
||||
|
||||
func composeFullName(firstName, surename, userName string) string {
|
||||
switch {
|
||||
case len(firstName) == 0 && len(surename) == 0:
|
||||
return userName
|
||||
case len(firstName) == 0:
|
||||
return surename
|
||||
case len(surename) == 0:
|
||||
return firstName
|
||||
default:
|
||||
return firstName + " " + surename
|
||||
}
|
||||
return user, CreateUser(user)
|
||||
}
|
||||
|
||||
// _________ __________________________
|
||||
@@ -295,25 +337,21 @@ func composeFullName(firstName, surename, userName string) string {
|
||||
// /_______ /\____|__ /____| |____|
|
||||
// \/ \/
|
||||
|
||||
type loginAuth struct {
|
||||
type smtpLoginAuth struct {
|
||||
username, password string
|
||||
}
|
||||
|
||||
func LoginAuth(username, password string) smtp.Auth {
|
||||
return &loginAuth{username, password}
|
||||
func (auth *smtpLoginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
|
||||
return "LOGIN", []byte(auth.username), nil
|
||||
}
|
||||
|
||||
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
|
||||
return "LOGIN", []byte(a.username), nil
|
||||
}
|
||||
|
||||
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||
func (auth *smtpLoginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||
if more {
|
||||
switch string(fromServer) {
|
||||
case "Username:":
|
||||
return []byte(a.username), nil
|
||||
return []byte(auth.username), nil
|
||||
case "Password:":
|
||||
return []byte(a.password), nil
|
||||
return []byte(auth.password), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
@@ -359,25 +397,24 @@ func SMTPAuth(a smtp.Auth, cfg *SMTPConfig) error {
|
||||
return ErrUnsupportedLoginType
|
||||
}
|
||||
|
||||
// Query if name/passwd can login against the LDAP directory pool
|
||||
// Create a local user if success
|
||||
// Return the same LoginUserPlain semantic
|
||||
func LoginUserSMTPSource(u *User, name, passwd string, sourceID int64, cfg *SMTPConfig, autoRegister bool) (*User, error) {
|
||||
// LoginViaSMTP queries if login/password is valid against the SMTP,
|
||||
// and create a local user if success when enabled.
|
||||
func LoginViaSMTP(user *User, login, password string, sourceID int64, cfg *SMTPConfig, autoRegister bool) (*User, error) {
|
||||
// Verify allowed domains.
|
||||
if len(cfg.AllowedDomains) > 0 {
|
||||
idx := strings.Index(name, "@")
|
||||
idx := strings.Index(login, "@")
|
||||
if idx == -1 {
|
||||
return nil, ErrUserNotExist{0, name}
|
||||
} else if !com.IsSliceContainsStr(strings.Split(cfg.AllowedDomains, ","), name[idx+1:]) {
|
||||
return nil, ErrUserNotExist{0, name}
|
||||
return nil, ErrUserNotExist{0, login}
|
||||
} else if !com.IsSliceContainsStr(strings.Split(cfg.AllowedDomains, ","), login[idx+1:]) {
|
||||
return nil, ErrUserNotExist{0, login}
|
||||
}
|
||||
}
|
||||
|
||||
var auth smtp.Auth
|
||||
if cfg.Auth == SMTP_PLAIN {
|
||||
auth = smtp.PlainAuth("", name, passwd, cfg.Host)
|
||||
auth = smtp.PlainAuth("", login, password, cfg.Host)
|
||||
} else if cfg.Auth == SMTP_LOGIN {
|
||||
auth = LoginAuth(name, passwd)
|
||||
auth = &smtpLoginAuth{login, password}
|
||||
} else {
|
||||
return nil, errors.New("Unsupported SMTP auth type")
|
||||
}
|
||||
@@ -388,33 +425,32 @@ func LoginUserSMTPSource(u *User, name, passwd string, sourceID int64, cfg *SMTP
|
||||
tperr, ok := err.(*textproto.Error)
|
||||
if (ok && tperr.Code == 535) ||
|
||||
strings.Contains(err.Error(), "Username and Password not accepted") {
|
||||
return nil, ErrUserNotExist{0, name}
|
||||
return nil, ErrUserNotExist{0, login}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !autoRegister {
|
||||
return u, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
var loginName = name
|
||||
idx := strings.Index(name, "@")
|
||||
username := login
|
||||
idx := strings.Index(login, "@")
|
||||
if idx > -1 {
|
||||
loginName = name[:idx]
|
||||
username = login[:idx]
|
||||
}
|
||||
// fake a local user creation
|
||||
u = &User{
|
||||
LowerName: strings.ToLower(loginName),
|
||||
Name: strings.ToLower(loginName),
|
||||
|
||||
user = &User{
|
||||
LowerName: strings.ToLower(username),
|
||||
Name: strings.ToLower(username),
|
||||
Email: login,
|
||||
Passwd: password,
|
||||
LoginType: LOGIN_SMTP,
|
||||
LoginSource: sourceID,
|
||||
LoginName: name,
|
||||
LoginName: login,
|
||||
IsActive: true,
|
||||
Passwd: passwd,
|
||||
Email: name,
|
||||
}
|
||||
err := CreateUser(u)
|
||||
return u, err
|
||||
return user, CreateUser(user)
|
||||
}
|
||||
|
||||
// __________ _____ _____
|
||||
@@ -424,101 +460,99 @@ func LoginUserSMTPSource(u *User, name, passwd string, sourceID int64, cfg *SMTP
|
||||
// |____| \____|__ /\____|__ /
|
||||
// \/ \/
|
||||
|
||||
// Query if name/passwd can login against PAM
|
||||
// Create a local user if success
|
||||
// Return the same LoginUserPlain semantic
|
||||
func LoginUserPAMSource(u *User, name, passwd string, sourceID int64, cfg *PAMConfig, autoRegister bool) (*User, error) {
|
||||
if err := pam.PAMAuth(cfg.ServiceName, name, passwd); err != nil {
|
||||
// LoginViaPAM queries if login/password is valid against the PAM,
|
||||
// and create a local user if success when enabled.
|
||||
func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMConfig, autoRegister bool) (*User, error) {
|
||||
if err := pam.PAMAuth(cfg.ServiceName, login, password); err != nil {
|
||||
if strings.Contains(err.Error(), "Authentication failure") {
|
||||
return nil, ErrUserNotExist{0, name}
|
||||
return nil, ErrUserNotExist{0, login}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !autoRegister {
|
||||
return u, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// fake a local user creation
|
||||
u = &User{
|
||||
LowerName: strings.ToLower(name),
|
||||
Name: name,
|
||||
user = &User{
|
||||
LowerName: strings.ToLower(login),
|
||||
Name: login,
|
||||
Email: login,
|
||||
Passwd: password,
|
||||
LoginType: LOGIN_PAM,
|
||||
LoginSource: sourceID,
|
||||
LoginName: name,
|
||||
LoginName: login,
|
||||
IsActive: true,
|
||||
Passwd: passwd,
|
||||
Email: name,
|
||||
}
|
||||
return u, CreateUser(u)
|
||||
return user, CreateUser(user)
|
||||
}
|
||||
|
||||
func ExternalUserLogin(u *User, name, passwd string, source *LoginSource, autoRegister bool) (*User, error) {
|
||||
func ExternalUserLogin(user *User, login, password string, source *LoginSource, autoRegister bool) (*User, error) {
|
||||
if !source.IsActived {
|
||||
return nil, ErrLoginSourceNotActived
|
||||
}
|
||||
|
||||
switch source.Type {
|
||||
case LOGIN_LDAP, LOGIN_DLDAP:
|
||||
return LoginUserLDAPSource(u, name, passwd, source, autoRegister)
|
||||
return LoginViaLDAP(user, login, password, source, autoRegister)
|
||||
case LOGIN_SMTP:
|
||||
return LoginUserSMTPSource(u, name, passwd, source.ID, source.Cfg.(*SMTPConfig), autoRegister)
|
||||
return LoginViaSMTP(user, login, password, source.ID, source.Cfg.(*SMTPConfig), autoRegister)
|
||||
case LOGIN_PAM:
|
||||
return LoginUserPAMSource(u, name, passwd, source.ID, source.Cfg.(*PAMConfig), autoRegister)
|
||||
return LoginViaPAM(user, login, password, source.ID, source.Cfg.(*PAMConfig), autoRegister)
|
||||
}
|
||||
|
||||
return nil, ErrUnsupportedLoginType
|
||||
}
|
||||
|
||||
// UserSignIn validates user name and password.
|
||||
func UserSignIn(uname, passwd string) (*User, error) {
|
||||
var u *User
|
||||
if strings.Contains(uname, "@") {
|
||||
u = &User{Email: strings.ToLower(uname)}
|
||||
func UserSignIn(username, password string) (*User, error) {
|
||||
var user *User
|
||||
if strings.Contains(username, "@") {
|
||||
user = &User{Email: strings.ToLower(username)}
|
||||
} else {
|
||||
u = &User{LowerName: strings.ToLower(uname)}
|
||||
user = &User{LowerName: strings.ToLower(username)}
|
||||
}
|
||||
|
||||
userExists, err := x.Get(u)
|
||||
hasUser, err := x.Get(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if userExists {
|
||||
switch u.LoginType {
|
||||
if hasUser {
|
||||
switch user.LoginType {
|
||||
case LOGIN_NOTYPE, LOGIN_PLAIN:
|
||||
if u.ValidatePassword(passwd) {
|
||||
return u, nil
|
||||
if user.ValidatePassword(password) {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
return nil, ErrUserNotExist{u.Id, u.Name}
|
||||
return nil, ErrUserNotExist{user.ID, user.Name}
|
||||
|
||||
default:
|
||||
var source LoginSource
|
||||
hasSource, err := x.Id(u.LoginSource).Get(&source)
|
||||
hasSource, err := x.Id(user.LoginSource).Get(&source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !hasSource {
|
||||
return nil, ErrLoginSourceNotExist
|
||||
return nil, ErrLoginSourceNotExist{user.LoginSource}
|
||||
}
|
||||
|
||||
return ExternalUserLogin(u, u.LoginName, passwd, &source, false)
|
||||
return ExternalUserLogin(user, user.LoginName, password, &source, false)
|
||||
}
|
||||
}
|
||||
|
||||
var sources []LoginSource
|
||||
sources := make([]*LoginSource, 0, 3)
|
||||
if err = x.UseBool().Find(&sources, &LoginSource{IsActived: true}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, source := range sources {
|
||||
u, err := ExternalUserLogin(nil, uname, passwd, &source, true)
|
||||
authUser, err := ExternalUserLogin(nil, username, password, source, true)
|
||||
if err == nil {
|
||||
return u, nil
|
||||
return authUser, nil
|
||||
}
|
||||
|
||||
log.Warn("Failed to login '%s' via '%s': %v", uname, source.Name, err)
|
||||
log.Warn("Failed to login '%s' via '%s': %v", username, source.Name, err)
|
||||
}
|
||||
|
||||
return nil, ErrUserNotExist{u.Id, u.Name}
|
||||
return nil, ErrUserNotExist{user.ID, user.Name}
|
||||
}
|
||||
@@ -13,15 +13,16 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
gouuid "github.com/satori/go.uuid"
|
||||
log "gopkg.in/clog.v1"
|
||||
"gopkg.in/ini.v1"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
gouuid "github.com/gogits/gogs/modules/uuid"
|
||||
)
|
||||
|
||||
const _MIN_DB_VER = 4
|
||||
@@ -50,7 +51,7 @@ func (m *migration) Migrate(x *xorm.Engine) error {
|
||||
|
||||
// The version table. Should have only one row with id==1
|
||||
type Version struct {
|
||||
Id int64
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Version int64
|
||||
}
|
||||
|
||||
@@ -58,13 +59,19 @@ type Version struct {
|
||||
// If you want to "retire" a migration, remove it from the top of the list and
|
||||
// update _MIN_VER_DB accordingly
|
||||
var migrations = []Migration{
|
||||
NewMigration("fix locale file load panic", fixLocaleFileLoadPanic), // V4 -> V5:v0.6.0
|
||||
NewMigration("trim action compare URL prefix", trimCommitActionAppUrlPrefix), // V5 -> V6:v0.6.3
|
||||
NewMigration("generate issue-label from issue", issueToIssueLabel), // V6 -> V7:v0.6.4
|
||||
NewMigration("refactor attachment table", attachmentRefactor), // V7 -> V8:v0.6.4
|
||||
NewMigration("rename pull request fields", renamePullRequestFields), // V8 -> V9:v0.6.16
|
||||
NewMigration("clean up migrate repo info", cleanUpMigrateRepoInfo), // V9 -> V10:v0.6.20
|
||||
NewMigration("generate rands and salt for organizations", generateOrgRandsAndSalt), // V10 -> V11:v0.8.5
|
||||
// v0 -> v4: before 0.6.0 -> 0.7.33
|
||||
NewMigration("fix locale file load panic", fixLocaleFileLoadPanic), // V4 -> V5:v0.6.0
|
||||
NewMigration("trim action compare URL prefix", trimCommitActionAppUrlPrefix), // V5 -> V6:v0.6.3
|
||||
NewMigration("generate issue-label from issue", issueToIssueLabel), // V6 -> V7:v0.6.4
|
||||
NewMigration("refactor attachment table", attachmentRefactor), // V7 -> V8:v0.6.4
|
||||
NewMigration("rename pull request fields", renamePullRequestFields), // V8 -> V9:v0.6.16
|
||||
NewMigration("clean up migrate repo info", cleanUpMigrateRepoInfo), // V9 -> V10:v0.6.20
|
||||
NewMigration("generate rands and salt for organizations", generateOrgRandsAndSalt), // V10 -> V11:v0.8.5
|
||||
NewMigration("convert date to unix timestamp", convertDateToUnix), // V11 -> V12:v0.9.2
|
||||
NewMigration("convert LDAP UseSSL option to SecurityProtocol", ldapUseSSLToSecurityProtocol), // V12 -> V13:v0.9.37
|
||||
|
||||
// v13 -> v14:v0.9.87
|
||||
NewMigration("set comment updated with created", setCommentUpdatedWithCreated),
|
||||
}
|
||||
|
||||
// Migrate database to current version
|
||||
@@ -73,7 +80,7 @@ func Migrate(x *xorm.Engine) error {
|
||||
return fmt.Errorf("sync: %v", err)
|
||||
}
|
||||
|
||||
currentVersion := &Version{Id: 1}
|
||||
currentVersion := &Version{ID: 1}
|
||||
has, err := x.Get(currentVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get: %v", err)
|
||||
@@ -239,7 +246,7 @@ func issueToIssueLabel(x *xorm.Engine) error {
|
||||
}
|
||||
|
||||
if err = sess.Sync2(new(IssueLabel)); err != nil {
|
||||
return fmt.Errorf("sync2: %v", err)
|
||||
return fmt.Errorf("Sync2: %v", err)
|
||||
} else if _, err = sess.Insert(issueLabels); err != nil {
|
||||
return fmt.Errorf("insert issue-labels: %v", err)
|
||||
}
|
||||
@@ -444,8 +451,12 @@ func generateOrgRandsAndSalt(x *xorm.Engine) (err error) {
|
||||
}
|
||||
|
||||
for _, org := range orgs {
|
||||
org.Rands = base.GetRandomString(10)
|
||||
org.Salt = base.GetRandomString(10)
|
||||
if org.Rands, err = base.GetRandomString(10); err != nil {
|
||||
return err
|
||||
}
|
||||
if org.Salt, err = base.GetRandomString(10); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = sess.Id(org.ID).Update(org); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -453,3 +464,222 @@ func generateOrgRandsAndSalt(x *xorm.Engine) (err error) {
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
type TAction struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
func (t *TAction) TableName() string { return "action" }
|
||||
|
||||
type TNotice struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
func (t *TNotice) TableName() string { return "notice" }
|
||||
|
||||
type TComment struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
func (t *TComment) TableName() string { return "comment" }
|
||||
|
||||
type TIssue struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
DeadlineUnix int64
|
||||
CreatedUnix int64
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
func (t *TIssue) TableName() string { return "issue" }
|
||||
|
||||
type TMilestone struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
DeadlineUnix int64
|
||||
ClosedDateUnix int64
|
||||
}
|
||||
|
||||
func (t *TMilestone) TableName() string { return "milestone" }
|
||||
|
||||
type TAttachment struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
func (t *TAttachment) TableName() string { return "attachment" }
|
||||
|
||||
type TLoginSource struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
func (t *TLoginSource) TableName() string { return "login_source" }
|
||||
|
||||
type TPull struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
MergedUnix int64
|
||||
}
|
||||
|
||||
func (t *TPull) TableName() string { return "pull_request" }
|
||||
|
||||
type TRelease struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
func (t *TRelease) TableName() string { return "release" }
|
||||
|
||||
type TRepo struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
func (t *TRepo) TableName() string { return "repository" }
|
||||
|
||||
type TMirror struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UpdatedUnix int64
|
||||
NextUpdateUnix int64
|
||||
}
|
||||
|
||||
func (t *TMirror) TableName() string { return "mirror" }
|
||||
|
||||
type TPublicKey struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
func (t *TPublicKey) TableName() string { return "public_key" }
|
||||
|
||||
type TDeployKey struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
func (t *TDeployKey) TableName() string { return "deploy_key" }
|
||||
|
||||
type TAccessToken struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
func (t *TAccessToken) TableName() string { return "access_token" }
|
||||
|
||||
type TUser struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
func (t *TUser) TableName() string { return "user" }
|
||||
|
||||
type TWebhook struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
CreatedUnix int64
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
func (t *TWebhook) TableName() string { return "webhook" }
|
||||
|
||||
func convertDateToUnix(x *xorm.Engine) (err error) {
|
||||
log.Info("This migration could take up to minutes, please be patient.")
|
||||
type Bean struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
Merged time.Time
|
||||
Deadline time.Time
|
||||
ClosedDate time.Time
|
||||
NextUpdate time.Time
|
||||
}
|
||||
|
||||
var tables = []struct {
|
||||
name string
|
||||
cols []string
|
||||
bean interface{}
|
||||
}{
|
||||
{"action", []string{"created"}, new(TAction)},
|
||||
{"notice", []string{"created"}, new(TNotice)},
|
||||
{"comment", []string{"created"}, new(TComment)},
|
||||
{"issue", []string{"deadline", "created", "updated"}, new(TIssue)},
|
||||
{"milestone", []string{"deadline", "closed_date"}, new(TMilestone)},
|
||||
{"attachment", []string{"created"}, new(TAttachment)},
|
||||
{"login_source", []string{"created", "updated"}, new(TLoginSource)},
|
||||
{"pull_request", []string{"merged"}, new(TPull)},
|
||||
{"release", []string{"created"}, new(TRelease)},
|
||||
{"repository", []string{"created", "updated"}, new(TRepo)},
|
||||
{"mirror", []string{"updated", "next_update"}, new(TMirror)},
|
||||
{"public_key", []string{"created", "updated"}, new(TPublicKey)},
|
||||
{"deploy_key", []string{"created", "updated"}, new(TDeployKey)},
|
||||
{"access_token", []string{"created", "updated"}, new(TAccessToken)},
|
||||
{"user", []string{"created", "updated"}, new(TUser)},
|
||||
{"webhook", []string{"created", "updated"}, new(TWebhook)},
|
||||
}
|
||||
|
||||
for _, table := range tables {
|
||||
log.Info("Converting table: %s", table.name)
|
||||
if err = x.Sync2(table.bean); err != nil {
|
||||
return fmt.Errorf("Sync [table: %s]: %v", table.name, err)
|
||||
}
|
||||
|
||||
offset := 0
|
||||
for {
|
||||
beans := make([]*Bean, 0, 100)
|
||||
if err = x.Sql(fmt.Sprintf("SELECT * FROM `%s` ORDER BY id ASC LIMIT 100 OFFSET %d",
|
||||
table.name, offset)).Find(&beans); err != nil {
|
||||
return fmt.Errorf("select beans [table: %s, offset: %d]: %v", table.name, offset, err)
|
||||
}
|
||||
log.Trace("Table [%s]: offset: %d, beans: %d", table.name, offset, len(beans))
|
||||
if len(beans) == 0 {
|
||||
break
|
||||
}
|
||||
offset += 100
|
||||
|
||||
baseSQL := "UPDATE `" + table.name + "` SET "
|
||||
for _, bean := range beans {
|
||||
valSQLs := make([]string, 0, len(table.cols))
|
||||
for _, col := range table.cols {
|
||||
fieldSQL := ""
|
||||
fieldSQL += col + "_unix = "
|
||||
|
||||
switch col {
|
||||
case "deadline":
|
||||
if bean.Deadline.IsZero() {
|
||||
continue
|
||||
}
|
||||
fieldSQL += com.ToStr(bean.Deadline.Unix())
|
||||
case "created":
|
||||
fieldSQL += com.ToStr(bean.Created.Unix())
|
||||
case "updated":
|
||||
fieldSQL += com.ToStr(bean.Updated.Unix())
|
||||
case "closed_date":
|
||||
fieldSQL += com.ToStr(bean.ClosedDate.Unix())
|
||||
case "merged":
|
||||
fieldSQL += com.ToStr(bean.Merged.Unix())
|
||||
case "next_update":
|
||||
fieldSQL += com.ToStr(bean.NextUpdate.Unix())
|
||||
}
|
||||
|
||||
valSQLs = append(valSQLs, fieldSQL)
|
||||
}
|
||||
|
||||
if len(valSQLs) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, err = x.Exec(baseSQL + strings.Join(valSQLs, ",") + " WHERE id = " + com.ToStr(bean.ID)); err != nil {
|
||||
return fmt.Errorf("update bean [table: %s, id: %d]: %v", table.name, bean.ID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
52
models/migrations/v13.go
Normal file
52
models/migrations/v13.go
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
func ldapUseSSLToSecurityProtocol(x *xorm.Engine) error {
|
||||
results, err := x.Query("SELECT `id`,`cfg` FROM `login_source` WHERE `type` = 2 OR `type` = 5")
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no such column") {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("select LDAP login sources: %v", err)
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, result := range results {
|
||||
cfg := map[string]interface{}{}
|
||||
if err = json.Unmarshal(result["cfg"], &cfg); err != nil {
|
||||
return fmt.Errorf("decode JSON config: %v", err)
|
||||
}
|
||||
if com.ToStr(cfg["UseSSL"]) == "true" {
|
||||
cfg["SecurityProtocol"] = 1 // LDAPS
|
||||
}
|
||||
delete(cfg, "UseSSL")
|
||||
|
||||
data, err := json.Marshal(&cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode JSON config: %v", err)
|
||||
}
|
||||
|
||||
if _, err = sess.Exec("UPDATE `login_source` SET `cfg`=? WHERE `id`=?",
|
||||
string(data), com.StrTo(result["id"]).MustInt64()); err != nil {
|
||||
return fmt.Errorf("update config column: %v", err)
|
||||
}
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
24
models/migrations/v14.go
Normal file
24
models/migrations/v14.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
func setCommentUpdatedWithCreated(x *xorm.Engine) (err error) {
|
||||
type Comment struct {
|
||||
UpdatedUnix int64
|
||||
}
|
||||
|
||||
if err = x.Sync2(new(Comment)); err != nil {
|
||||
return fmt.Errorf("Sync2: %v", err)
|
||||
} else if _, err = x.Exec("UPDATE comment SET updated_unix = created_unix"); err != nil {
|
||||
return fmt.Errorf("set update_unix: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
349
models/milestone.go
Normal file
349
models/milestone.go
Normal file
@@ -0,0 +1,349 @@
|
||||
// Copyright 2017 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
|
||||
api "github.com/gogits/go-gogs-client"
|
||||
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
// Milestone represents a milestone of repository.
|
||||
type Milestone struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX"`
|
||||
Name string
|
||||
Content string `xorm:"TEXT"`
|
||||
RenderedContent string `xorm:"-"`
|
||||
IsClosed bool
|
||||
NumIssues int
|
||||
NumClosedIssues int
|
||||
NumOpenIssues int `xorm:"-"`
|
||||
Completeness int // Percentage(1-100).
|
||||
IsOverDue bool `xorm:"-"`
|
||||
|
||||
DeadlineString string `xorm:"-"`
|
||||
Deadline time.Time `xorm:"-"`
|
||||
DeadlineUnix int64
|
||||
ClosedDate time.Time `xorm:"-"`
|
||||
ClosedDateUnix int64
|
||||
}
|
||||
|
||||
func (m *Milestone) BeforeInsert() {
|
||||
m.DeadlineUnix = m.Deadline.Unix()
|
||||
}
|
||||
|
||||
func (m *Milestone) BeforeUpdate() {
|
||||
if m.NumIssues > 0 {
|
||||
m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
|
||||
} else {
|
||||
m.Completeness = 0
|
||||
}
|
||||
|
||||
m.DeadlineUnix = m.Deadline.Unix()
|
||||
m.ClosedDateUnix = m.ClosedDate.Unix()
|
||||
}
|
||||
|
||||
func (m *Milestone) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "num_closed_issues":
|
||||
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
|
||||
|
||||
case "deadline_unix":
|
||||
m.Deadline = time.Unix(m.DeadlineUnix, 0).Local()
|
||||
if m.Deadline.Year() == 9999 {
|
||||
return
|
||||
}
|
||||
|
||||
m.DeadlineString = m.Deadline.Format("2006-01-02")
|
||||
if time.Now().Local().After(m.Deadline) {
|
||||
m.IsOverDue = true
|
||||
}
|
||||
|
||||
case "closed_date_unix":
|
||||
m.ClosedDate = time.Unix(m.ClosedDateUnix, 0).Local()
|
||||
}
|
||||
}
|
||||
|
||||
// State returns string representation of milestone status.
|
||||
func (m *Milestone) State() api.StateType {
|
||||
if m.IsClosed {
|
||||
return api.STATE_CLOSED
|
||||
}
|
||||
return api.STATE_OPEN
|
||||
}
|
||||
|
||||
func (m *Milestone) ChangeStatus(isClosed bool) error {
|
||||
return ChangeMilestoneStatus(m, isClosed)
|
||||
}
|
||||
|
||||
func (m *Milestone) APIFormat() *api.Milestone {
|
||||
apiMilestone := &api.Milestone{
|
||||
ID: m.ID,
|
||||
State: m.State(),
|
||||
Title: m.Name,
|
||||
Description: m.Content,
|
||||
OpenIssues: m.NumOpenIssues,
|
||||
ClosedIssues: m.NumClosedIssues,
|
||||
}
|
||||
if m.IsClosed {
|
||||
apiMilestone.Closed = &m.ClosedDate
|
||||
}
|
||||
if m.Deadline.Year() < 9999 {
|
||||
apiMilestone.Deadline = &m.Deadline
|
||||
}
|
||||
return apiMilestone
|
||||
}
|
||||
|
||||
// NewMilestone creates new milestone of repository.
|
||||
func NewMilestone(m *Milestone) (err error) {
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.Insert(m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.Exec("UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID); err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func getMilestoneByRepoID(e Engine, repoID, id int64) (*Milestone, error) {
|
||||
m := &Milestone{
|
||||
ID: id,
|
||||
RepoID: repoID,
|
||||
}
|
||||
has, err := e.Get(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrMilestoneNotExist{id, repoID}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GetWebhookByRepoID returns the milestone in a repository.
|
||||
func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) {
|
||||
return getMilestoneByRepoID(x, repoID, id)
|
||||
}
|
||||
|
||||
// GetMilestonesByRepoID returns all milestones of a repository.
|
||||
func GetMilestonesByRepoID(repoID int64) ([]*Milestone, error) {
|
||||
miles := make([]*Milestone, 0, 10)
|
||||
return miles, x.Where("repo_id = ?", repoID).Find(&miles)
|
||||
}
|
||||
|
||||
// GetMilestones returns a list of milestones of given repository and status.
|
||||
func GetMilestones(repoID int64, page int, isClosed bool) ([]*Milestone, error) {
|
||||
miles := make([]*Milestone, 0, setting.UI.IssuePagingNum)
|
||||
sess := x.Where("repo_id = ? AND is_closed = ?", repoID, isClosed)
|
||||
if page > 0 {
|
||||
sess = sess.Limit(setting.UI.IssuePagingNum, (page-1)*setting.UI.IssuePagingNum)
|
||||
}
|
||||
return miles, sess.Find(&miles)
|
||||
}
|
||||
|
||||
func updateMilestone(e Engine, m *Milestone) error {
|
||||
_, err := e.Id(m.ID).AllCols().Update(m)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateMilestone updates information of given milestone.
|
||||
func UpdateMilestone(m *Milestone) error {
|
||||
return updateMilestone(x, m)
|
||||
}
|
||||
|
||||
func countRepoMilestones(e Engine, repoID int64) int64 {
|
||||
count, _ := e.Where("repo_id=?", repoID).Count(new(Milestone))
|
||||
return count
|
||||
}
|
||||
|
||||
// CountRepoMilestones returns number of milestones in given repository.
|
||||
func CountRepoMilestones(repoID int64) int64 {
|
||||
return countRepoMilestones(x, repoID)
|
||||
}
|
||||
|
||||
func countRepoClosedMilestones(e Engine, repoID int64) int64 {
|
||||
closed, _ := e.Where("repo_id=? AND is_closed=?", repoID, true).Count(new(Milestone))
|
||||
return closed
|
||||
}
|
||||
|
||||
// CountRepoClosedMilestones returns number of closed milestones in given repository.
|
||||
func CountRepoClosedMilestones(repoID int64) int64 {
|
||||
return countRepoClosedMilestones(x, repoID)
|
||||
}
|
||||
|
||||
// MilestoneStats returns number of open and closed milestones of given repository.
|
||||
func MilestoneStats(repoID int64) (open int64, closed int64) {
|
||||
open, _ = x.Where("repo_id=? AND is_closed=?", repoID, false).Count(new(Milestone))
|
||||
return open, CountRepoClosedMilestones(repoID)
|
||||
}
|
||||
|
||||
// ChangeMilestoneStatus changes the milestone open/closed status.
|
||||
// If milestone passes with changed values, those values will be
|
||||
// updated to database as well.
|
||||
func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
|
||||
repo, err := GetRepositoryByID(m.RepoID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.IsClosed = isClosed
|
||||
if err = updateMilestone(sess, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repo.NumMilestones = int(countRepoMilestones(sess, repo.ID))
|
||||
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID))
|
||||
if _, err = sess.Id(repo.ID).AllCols().Update(repo); err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func changeMilestoneIssueStats(e *xorm.Session, issue *Issue) error {
|
||||
if issue.MilestoneID == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if issue.IsClosed {
|
||||
m.NumOpenIssues--
|
||||
m.NumClosedIssues++
|
||||
} else {
|
||||
m.NumOpenIssues++
|
||||
m.NumClosedIssues--
|
||||
}
|
||||
|
||||
return updateMilestone(e, m)
|
||||
}
|
||||
|
||||
// ChangeMilestoneIssueStats updates the open/closed issues counter and progress
|
||||
// for the milestone associated with the given issue.
|
||||
func ChangeMilestoneIssueStats(issue *Issue) (err error) {
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = changeMilestoneIssueStats(sess, issue); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64) error {
|
||||
if oldMilestoneID > 0 {
|
||||
m, err := getMilestoneByRepoID(e, issue.RepoID, oldMilestoneID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.NumIssues--
|
||||
if issue.IsClosed {
|
||||
m.NumClosedIssues--
|
||||
}
|
||||
|
||||
if err = updateMilestone(e, m); err != nil {
|
||||
return err
|
||||
} else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE issue_id = ?", issue.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if issue.MilestoneID > 0 {
|
||||
m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.NumIssues++
|
||||
if issue.IsClosed {
|
||||
m.NumClosedIssues++
|
||||
}
|
||||
|
||||
if err = updateMilestone(e, m); err != nil {
|
||||
return err
|
||||
} else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = ? WHERE issue_id = ?", m.ID, issue.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return updateIssue(e, issue)
|
||||
}
|
||||
|
||||
// ChangeMilestoneAssign changes assignment of milestone for issue.
|
||||
func ChangeMilestoneAssign(issue *Issue, oldMilestoneID int64) (err error) {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = changeMilestoneAssign(sess, issue, oldMilestoneID); err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// DeleteMilestoneOfRepoByID deletes a milestone from a repository.
|
||||
func DeleteMilestoneOfRepoByID(repoID, id int64) error {
|
||||
m, err := GetMilestoneByRepoID(repoID, id)
|
||||
if err != nil {
|
||||
if IsErrMilestoneNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
repo, err := GetRepositoryByID(m.RepoID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.Id(m.ID).Delete(new(Milestone)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
repo.NumMilestones = int(countRepoMilestones(sess, repo.ID))
|
||||
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID))
|
||||
if _, err = sess.Id(repo.ID).AllCols().Update(repo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.Exec("UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil {
|
||||
return err
|
||||
} else if _, err = sess.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
264
models/mirror.go
Normal file
264
models/mirror.go
Normal file
@@ -0,0 +1,264 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
log "gopkg.in/clog.v1"
|
||||
"gopkg.in/ini.v1"
|
||||
|
||||
"github.com/gogits/git-module"
|
||||
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/gogits/gogs/modules/sync"
|
||||
)
|
||||
|
||||
var MirrorQueue = sync.NewUniqueQueue(setting.Repository.MirrorQueueLength)
|
||||
|
||||
// Mirror represents mirror information of a repository.
|
||||
type Mirror struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64
|
||||
Repo *Repository `xorm:"-"`
|
||||
Interval int // Hour.
|
||||
EnablePrune bool `xorm:"NOT NULL DEFAULT true"`
|
||||
|
||||
Updated time.Time `xorm:"-"`
|
||||
UpdatedUnix int64
|
||||
NextUpdate time.Time `xorm:"-"`
|
||||
NextUpdateUnix int64
|
||||
|
||||
address string `xorm:"-"`
|
||||
}
|
||||
|
||||
func (m *Mirror) BeforeInsert() {
|
||||
m.UpdatedUnix = time.Now().Unix()
|
||||
m.NextUpdateUnix = m.NextUpdate.Unix()
|
||||
}
|
||||
|
||||
func (m *Mirror) BeforeUpdate() {
|
||||
m.UpdatedUnix = time.Now().Unix()
|
||||
m.NextUpdateUnix = m.NextUpdate.Unix()
|
||||
}
|
||||
|
||||
func (m *Mirror) AfterSet(colName string, _ xorm.Cell) {
|
||||
var err error
|
||||
switch colName {
|
||||
case "repo_id":
|
||||
m.Repo, err = GetRepositoryByID(m.RepoID)
|
||||
if err != nil {
|
||||
log.Error(3, "GetRepositoryByID[%d]: %v", m.ID, err)
|
||||
}
|
||||
case "updated_unix":
|
||||
m.Updated = time.Unix(m.UpdatedUnix, 0).Local()
|
||||
case "next_updated_unix":
|
||||
m.NextUpdate = time.Unix(m.NextUpdateUnix, 0).Local()
|
||||
}
|
||||
}
|
||||
|
||||
// ScheduleNextUpdate calculates and sets next update time.
|
||||
func (m *Mirror) ScheduleNextUpdate() {
|
||||
m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour)
|
||||
}
|
||||
|
||||
func (m *Mirror) readAddress() {
|
||||
if len(m.address) > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
cfg, err := ini.Load(m.Repo.GitConfigPath())
|
||||
if err != nil {
|
||||
log.Error(4, "Load: %v", err)
|
||||
return
|
||||
}
|
||||
m.address = cfg.Section("remote \"origin\"").Key("url").Value()
|
||||
}
|
||||
|
||||
// HandleCloneUserCredentials replaces user credentials from HTTP/HTTPS URL
|
||||
// with placeholder <credentials>.
|
||||
// It will fail for any other forms of clone addresses.
|
||||
func HandleCloneUserCredentials(url string, mosaics bool) string {
|
||||
i := strings.Index(url, "@")
|
||||
if i == -1 {
|
||||
return url
|
||||
}
|
||||
start := strings.Index(url, "://")
|
||||
if start == -1 {
|
||||
return url
|
||||
}
|
||||
if mosaics {
|
||||
return url[:start+3] + "<credentials>" + url[i:]
|
||||
}
|
||||
return url[:start+3] + url[i+1:]
|
||||
}
|
||||
|
||||
// Address returns mirror address from Git repository config without credentials.
|
||||
func (m *Mirror) Address() string {
|
||||
m.readAddress()
|
||||
return HandleCloneUserCredentials(m.address, false)
|
||||
}
|
||||
|
||||
// MosaicsAddress returns mirror address from Git repository config with credentials under mosaics.
|
||||
func (m *Mirror) MosaicsAddress() string {
|
||||
m.readAddress()
|
||||
return HandleCloneUserCredentials(m.address, true)
|
||||
}
|
||||
|
||||
// FullAddress returns mirror address from Git repository config.
|
||||
func (m *Mirror) FullAddress() string {
|
||||
m.readAddress()
|
||||
return m.address
|
||||
}
|
||||
|
||||
// SaveAddress writes new address to Git repository config.
|
||||
func (m *Mirror) SaveAddress(addr string) error {
|
||||
configPath := m.Repo.GitConfigPath()
|
||||
cfg, err := ini.Load(configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Load: %v", err)
|
||||
}
|
||||
|
||||
cfg.Section("remote \"origin\"").Key("url").SetValue(addr)
|
||||
return cfg.SaveToIndent(configPath, "\t")
|
||||
}
|
||||
|
||||
// runSync returns true if sync finished without error.
|
||||
func (m *Mirror) runSync() bool {
|
||||
repoPath := m.Repo.RepoPath()
|
||||
wikiPath := m.Repo.WikiPath()
|
||||
timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second
|
||||
|
||||
// Do a fast-fail testing against on repository URL to ensure it is accessible under
|
||||
// good condition to prevent long blocking on URL resolution without syncing anything.
|
||||
if !git.IsRepoURLAccessible(git.NetworkOptions{
|
||||
URL: m.FullAddress(),
|
||||
Timeout: 10 * time.Second,
|
||||
}) {
|
||||
desc := fmt.Sprintf("Mirror repository URL is not accessible: %s", m.MosaicsAddress())
|
||||
if err := CreateRepositoryNotice(desc); err != nil {
|
||||
log.Error(4, "CreateRepositoryNotice: %v", err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
gitArgs := []string{"remote", "update"}
|
||||
if m.EnablePrune {
|
||||
gitArgs = append(gitArgs, "--prune")
|
||||
}
|
||||
if _, stderr, err := process.ExecDir(
|
||||
timeout, repoPath, fmt.Sprintf("Mirror.runSync: %s", repoPath),
|
||||
"git", gitArgs...); err != nil {
|
||||
desc := fmt.Sprintf("Fail to update mirror repository '%s': %s", repoPath, stderr)
|
||||
log.Error(4, desc)
|
||||
if err = CreateRepositoryNotice(desc); err != nil {
|
||||
log.Error(4, "CreateRepositoryNotice: %v", err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
if m.Repo.HasWiki() {
|
||||
if _, stderr, err := process.ExecDir(
|
||||
timeout, wikiPath, fmt.Sprintf("Mirror.runSync: %s", wikiPath),
|
||||
"git", "remote", "update", "--prune"); err != nil {
|
||||
desc := fmt.Sprintf("Fail to update mirror wiki repository '%s': %s", wikiPath, stderr)
|
||||
log.Error(4, desc)
|
||||
if err = CreateRepositoryNotice(desc); err != nil {
|
||||
log.Error(4, "CreateRepositoryNotice: %v", err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func getMirrorByRepoID(e Engine, repoID int64) (*Mirror, error) {
|
||||
m := &Mirror{RepoID: repoID}
|
||||
has, err := e.Get(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrMirrorNotExist
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GetMirrorByRepoID returns mirror information of a repository.
|
||||
func GetMirrorByRepoID(repoID int64) (*Mirror, error) {
|
||||
return getMirrorByRepoID(x, repoID)
|
||||
}
|
||||
|
||||
func updateMirror(e Engine, m *Mirror) error {
|
||||
_, err := e.Id(m.ID).AllCols().Update(m)
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateMirror(m *Mirror) error {
|
||||
return updateMirror(x, m)
|
||||
}
|
||||
|
||||
func DeleteMirrorByRepoID(repoID int64) error {
|
||||
_, err := x.Delete(&Mirror{RepoID: repoID})
|
||||
return err
|
||||
}
|
||||
|
||||
// MirrorUpdate checks and updates mirror repositories.
|
||||
func MirrorUpdate() {
|
||||
if taskStatusTable.IsRunning(_MIRROR_UPDATE) {
|
||||
return
|
||||
}
|
||||
taskStatusTable.Start(_MIRROR_UPDATE)
|
||||
defer taskStatusTable.Stop(_MIRROR_UPDATE)
|
||||
|
||||
log.Trace("Doing: MirrorUpdate")
|
||||
|
||||
if err := x.Where("next_update_unix<=?", time.Now().Unix()).Iterate(new(Mirror), func(idx int, bean interface{}) error {
|
||||
m := bean.(*Mirror)
|
||||
if m.Repo == nil {
|
||||
log.Error(4, "Disconnected mirror repository found: %d", m.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
MirrorQueue.Add(m.RepoID)
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Error(4, "MirrorUpdate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// SyncMirrors checks and syncs mirrors.
|
||||
// TODO: sync more mirrors at same time.
|
||||
func SyncMirrors() {
|
||||
// Start listening on new sync requests.
|
||||
for repoID := range MirrorQueue.Queue() {
|
||||
log.Trace("SyncMirrors [repo_id: %v]", repoID)
|
||||
MirrorQueue.Remove(repoID)
|
||||
|
||||
m, err := GetMirrorByRepoID(com.StrTo(repoID).MustInt64())
|
||||
if err != nil {
|
||||
log.Error(4, "GetMirrorByRepoID [%s]: %v", repoID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !m.runSync() {
|
||||
continue
|
||||
}
|
||||
|
||||
m.ScheduleNextUpdate()
|
||||
if err = UpdateMirror(m); err != nil {
|
||||
log.Error(4, "UpdateMirror [%s]: %v", repoID, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func InitSyncMirrors() {
|
||||
go SyncMirrors()
|
||||
}
|
||||
108
models/models.go
108
models/models.go
@@ -6,21 +6,19 @@ package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/go-xorm/core"
|
||||
"github.com/go-xorm/xorm"
|
||||
_ "github.com/lib/pq"
|
||||
|
||||
"github.com/gogits/gogs/models/migrations"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
@@ -30,11 +28,14 @@ type Engine interface {
|
||||
Exec(string, ...interface{}) (sql.Result, error)
|
||||
Find(interface{}, ...interface{}) error
|
||||
Get(interface{}) (bool, error)
|
||||
Id(interface{}) *xorm.Session
|
||||
In(string, ...interface{}) *xorm.Session
|
||||
Insert(...interface{}) (int64, error)
|
||||
InsertOne(interface{}) (int64, error)
|
||||
Id(interface{}) *xorm.Session
|
||||
Iterate(interface{}, xorm.IterFunc) error
|
||||
Sql(string, ...interface{}) *xorm.Session
|
||||
Where(string, ...interface{}) *xorm.Session
|
||||
Table(interface{}) *xorm.Session
|
||||
Where(interface{}, ...interface{}) *xorm.Session
|
||||
}
|
||||
|
||||
func sessionRelease(sess *xorm.Session) {
|
||||
@@ -44,27 +45,6 @@ func sessionRelease(sess *xorm.Session) {
|
||||
sess.Close()
|
||||
}
|
||||
|
||||
// Note: get back time.Time from database Go sees it at UTC where they are really Local.
|
||||
// So this function makes correct timezone offset.
|
||||
func regulateTimeZone(t time.Time) time.Time {
|
||||
if !setting.UseMySQL {
|
||||
return t
|
||||
}
|
||||
|
||||
zone := t.Local().Format("-0700")
|
||||
if len(zone) != 5 {
|
||||
log.Error(4, "Unprocessable timezone: %s - %s", t.Local(), zone)
|
||||
return t
|
||||
}
|
||||
hour := com.StrTo(zone[2:3]).MustInt()
|
||||
minutes := com.StrTo(zone[3:5]).MustInt()
|
||||
|
||||
if zone[0] == '-' {
|
||||
return t.Add(time.Duration(hour) * time.Hour).Add(time.Duration(minutes) * time.Minute)
|
||||
}
|
||||
return t.Add(-1 * time.Duration(hour) * time.Hour).Add(-1 * time.Duration(minutes) * time.Minute)
|
||||
}
|
||||
|
||||
var (
|
||||
x *xorm.Engine
|
||||
tables []interface{}
|
||||
@@ -75,13 +55,12 @@ var (
|
||||
}
|
||||
|
||||
EnableSQLite3 bool
|
||||
EnableTidb bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
tables = append(tables,
|
||||
new(User), new(PublicKey), new(AccessToken),
|
||||
new(Repository), new(DeployKey), new(Collaboration), new(Access),
|
||||
new(Repository), new(DeployKey), new(Collaboration), new(Access), new(Upload),
|
||||
new(Watch), new(Star), new(Follow), new(Action),
|
||||
new(Issue), new(PullRequest), new(Comment), new(Attachment), new(IssueUser),
|
||||
new(Label), new(IssueLabel), new(Milestone),
|
||||
@@ -119,48 +98,57 @@ func LoadConfigs() {
|
||||
DbCfg.Path = sec.Key("PATH").MustString("data/gogs.db")
|
||||
}
|
||||
|
||||
// parsePostgreSQLHostPort parses given input in various forms defined in
|
||||
// https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
|
||||
// and returns proper host and port number.
|
||||
func parsePostgreSQLHostPort(info string) (string, string) {
|
||||
host, port := "127.0.0.1", "5432"
|
||||
if strings.Contains(info, ":") && !strings.HasSuffix(info, "]") {
|
||||
idx := strings.LastIndex(info, ":")
|
||||
host = info[:idx]
|
||||
port = info[idx+1:]
|
||||
} else if len(info) > 0 {
|
||||
host = info
|
||||
}
|
||||
return host, port
|
||||
}
|
||||
|
||||
func getEngine() (*xorm.Engine, error) {
|
||||
cnnstr := ""
|
||||
connStr := ""
|
||||
var Param string = "?"
|
||||
if strings.Contains(DbCfg.Name, Param) {
|
||||
Param = "&"
|
||||
}
|
||||
switch DbCfg.Type {
|
||||
case "mysql":
|
||||
if DbCfg.Host[0] == '/' { // looks like a unix socket
|
||||
cnnstr = fmt.Sprintf("%s:%s@unix(%s)/%s?charset=utf8&parseTime=true",
|
||||
DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name)
|
||||
connStr = fmt.Sprintf("%s:%s@unix(%s)/%s%scharset=utf8&parseTime=true",
|
||||
DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name, Param)
|
||||
} else {
|
||||
cnnstr = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=true",
|
||||
DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name)
|
||||
connStr = fmt.Sprintf("%s:%s@tcp(%s)/%s%scharset=utf8&parseTime=true",
|
||||
DbCfg.User, DbCfg.Passwd, DbCfg.Host, DbCfg.Name, Param)
|
||||
}
|
||||
case "postgres":
|
||||
var host, port = "127.0.0.1", "5432"
|
||||
fields := strings.Split(DbCfg.Host, ":")
|
||||
if len(fields) > 0 && len(strings.TrimSpace(fields[0])) > 0 {
|
||||
host = fields[0]
|
||||
host, port := parsePostgreSQLHostPort(DbCfg.Host)
|
||||
if host[0] == '/' { // looks like a unix socket
|
||||
connStr = fmt.Sprintf("postgres://%s:%s@:%s/%s%ssslmode=%s&host=%s",
|
||||
url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Passwd), port, DbCfg.Name, Param, DbCfg.SSLMode, host)
|
||||
} else {
|
||||
connStr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s%ssslmode=%s",
|
||||
url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Passwd), host, port, DbCfg.Name, Param, DbCfg.SSLMode)
|
||||
}
|
||||
if len(fields) > 1 && len(strings.TrimSpace(fields[1])) > 0 {
|
||||
port = fields[1]
|
||||
}
|
||||
cnnstr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=%s",
|
||||
url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Passwd), host, port, DbCfg.Name, DbCfg.SSLMode)
|
||||
case "sqlite3":
|
||||
if !EnableSQLite3 {
|
||||
return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
|
||||
return nil, errors.New("This binary version does not build support for SQLite3.")
|
||||
}
|
||||
if err := os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm); err != nil {
|
||||
return nil, fmt.Errorf("Fail to create directories: %v", err)
|
||||
}
|
||||
cnnstr = "file:" + DbCfg.Path + "?cache=shared&mode=rwc"
|
||||
case "tidb":
|
||||
if !EnableTidb {
|
||||
return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
|
||||
}
|
||||
if err := os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm); err != nil {
|
||||
return nil, fmt.Errorf("Fail to create directories: %v", err)
|
||||
}
|
||||
cnnstr = "goleveldb://" + DbCfg.Path
|
||||
connStr = "file:" + DbCfg.Path + "?cache=shared&mode=rwc"
|
||||
default:
|
||||
return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
|
||||
}
|
||||
return xorm.NewEngine(DbCfg.Type, cnnstr)
|
||||
return xorm.NewEngine(DbCfg.Type, connStr)
|
||||
}
|
||||
|
||||
func NewTestEngine(x *xorm.Engine) (err error) {
|
||||
@@ -190,13 +178,13 @@ func SetEngine() (err error) {
|
||||
if err != nil {
|
||||
return fmt.Errorf("Fail to create xorm.log: %v", err)
|
||||
}
|
||||
x.SetLogger(xorm.NewSimpleLogger(f))
|
||||
|
||||
x.ShowSQL = true
|
||||
x.ShowInfo = true
|
||||
x.ShowDebug = true
|
||||
x.ShowErr = true
|
||||
x.ShowWarn = true
|
||||
if setting.ProdMode {
|
||||
x.SetLogger(xorm.NewSimpleLogger3(f, xorm.DEFAULT_LOG_PREFIX, xorm.DEFAULT_LOG_FLAG, core.LOG_WARNING))
|
||||
} else {
|
||||
x.SetLogger(xorm.NewSimpleLogger(f))
|
||||
}
|
||||
x.ShowSQL(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -231,7 +219,7 @@ func GetStatistic() (stats Statistic) {
|
||||
stats.Counter.User = CountUsers()
|
||||
stats.Counter.Org = CountOrganizations()
|
||||
stats.Counter.PublicKey, _ = x.Count(new(PublicKey))
|
||||
stats.Counter.Repo = CountRepositories()
|
||||
stats.Counter.Repo = CountRepositories(true)
|
||||
stats.Counter.Watch, _ = x.Count(new(Watch))
|
||||
stats.Counter.Star, _ = x.Count(new(Star))
|
||||
stats.Counter.Action, _ = x.Count(new(Action))
|
||||
|
||||
33
models/models_test.go
Normal file
33
models/models_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func Test_parsePostgreSQLHostPort(t *testing.T) {
|
||||
testSuites := []struct {
|
||||
input string
|
||||
host, port string
|
||||
}{
|
||||
{"127.0.0.1:1234", "127.0.0.1", "1234"},
|
||||
{"127.0.0.1", "127.0.0.1", "5432"},
|
||||
{"[::1]:1234", "[::1]", "1234"},
|
||||
{"[::1]", "[::1]", "5432"},
|
||||
{"/tmp/pg.sock:1234", "/tmp/pg.sock", "1234"},
|
||||
{"/tmp/pg.sock", "/tmp/pg.sock", "5432"},
|
||||
}
|
||||
|
||||
Convey("Parse PostgreSQL host and port", t, func() {
|
||||
for _, suite := range testSuites {
|
||||
host, port := parsePostgreSQLHostPort(suite.input)
|
||||
So(host, ShouldEqual, suite.host)
|
||||
So(port, ShouldEqual, suite.port)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// +build tidb go1.4.2
|
||||
|
||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
_ "github.com/go-xorm/tidb"
|
||||
"github.com/ngaut/log"
|
||||
_ "github.com/pingcap/tidb"
|
||||
)
|
||||
|
||||
func init() {
|
||||
EnableTidb = true
|
||||
log.SetLevelByString("error")
|
||||
}
|
||||
852
models/org.go
852
models/org.go
File diff suppressed because it is too large
Load Diff
631
models/org_team.go
Normal file
631
models/org_team.go
Normal file
@@ -0,0 +1,631 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
const OWNER_TEAM = "Owners"
|
||||
|
||||
// Team represents a organization team.
|
||||
type Team struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OrgID int64 `xorm:"INDEX"`
|
||||
LowerName string
|
||||
Name string
|
||||
Description string
|
||||
Authorize AccessMode
|
||||
Repos []*Repository `xorm:"-"`
|
||||
Members []*User `xorm:"-"`
|
||||
NumRepos int
|
||||
NumMembers int
|
||||
}
|
||||
|
||||
func (t *Team) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "num_repos":
|
||||
// LEGACY [0.11]: this is backward compatibility bug fix for https://github.com/gogits/gogs/issues/3671
|
||||
if t.NumRepos < 0 {
|
||||
t.NumRepos = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsOwnerTeam returns true if team is owner team.
|
||||
func (t *Team) IsOwnerTeam() bool {
|
||||
return t.Name == OWNER_TEAM
|
||||
}
|
||||
|
||||
// IsTeamMember returns true if given user is a member of team.
|
||||
func (t *Team) IsMember(uid int64) bool {
|
||||
return IsTeamMember(t.OrgID, t.ID, uid)
|
||||
}
|
||||
|
||||
func (t *Team) getRepositories(e Engine) (err error) {
|
||||
teamRepos := make([]*TeamRepo, 0, t.NumRepos)
|
||||
if err = x.Where("team_id=?", t.ID).Find(&teamRepos); err != nil {
|
||||
return fmt.Errorf("get team-repos: %v", err)
|
||||
}
|
||||
|
||||
t.Repos = make([]*Repository, 0, len(teamRepos))
|
||||
for i := range teamRepos {
|
||||
repo, err := getRepositoryByID(e, teamRepos[i].RepoID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getRepositoryById(%d): %v", teamRepos[i].RepoID, err)
|
||||
}
|
||||
t.Repos = append(t.Repos, repo)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRepositories returns all repositories in team of organization.
|
||||
func (t *Team) GetRepositories() error {
|
||||
return t.getRepositories(x)
|
||||
}
|
||||
|
||||
func (t *Team) getMembers(e Engine) (err error) {
|
||||
t.Members, err = getTeamMembers(e, t.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetMembers returns all members in team of organization.
|
||||
func (t *Team) GetMembers() (err error) {
|
||||
return t.getMembers(x)
|
||||
}
|
||||
|
||||
// AddMember adds new membership of the team to the organization,
|
||||
// the user will have membership to the organization automatically when needed.
|
||||
func (t *Team) AddMember(uid int64) error {
|
||||
return AddTeamMember(t.OrgID, t.ID, uid)
|
||||
}
|
||||
|
||||
// RemoveMember removes member from team of organization.
|
||||
func (t *Team) RemoveMember(uid int64) error {
|
||||
return RemoveTeamMember(t.OrgID, t.ID, uid)
|
||||
}
|
||||
|
||||
func (t *Team) hasRepository(e Engine, repoID int64) bool {
|
||||
return hasTeamRepo(e, t.OrgID, t.ID, repoID)
|
||||
}
|
||||
|
||||
// HasRepository returns true if given repository belong to team.
|
||||
func (t *Team) HasRepository(repoID int64) bool {
|
||||
return t.hasRepository(x, repoID)
|
||||
}
|
||||
|
||||
func (t *Team) addRepository(e Engine, repo *Repository) (err error) {
|
||||
if err = addTeamRepo(e, t.OrgID, t.ID, repo.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.NumRepos++
|
||||
if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
|
||||
return fmt.Errorf("update team: %v", err)
|
||||
}
|
||||
|
||||
if err = repo.recalculateTeamAccesses(e, 0); err != nil {
|
||||
return fmt.Errorf("recalculateAccesses: %v", err)
|
||||
}
|
||||
|
||||
if err = t.getMembers(e); err != nil {
|
||||
return fmt.Errorf("getMembers: %v", err)
|
||||
}
|
||||
for _, u := range t.Members {
|
||||
if err = watchRepo(e, u.ID, repo.ID, true); err != nil {
|
||||
return fmt.Errorf("watchRepo: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRepository adds new repository to team of organization.
|
||||
func (t *Team) AddRepository(repo *Repository) (err error) {
|
||||
if repo.OwnerID != t.OrgID {
|
||||
return errors.New("Repository does not belong to organization")
|
||||
} else if t.HasRepository(repo.ID) {
|
||||
return nil
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = t.addRepository(sess, repo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) {
|
||||
if err = removeTeamRepo(e, t.ID, repo.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.NumRepos--
|
||||
if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Don't need to recalculate when delete a repository from organization.
|
||||
if recalculate {
|
||||
if err = repo.recalculateTeamAccesses(e, t.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = t.getMembers(e); err != nil {
|
||||
return fmt.Errorf("get team members: %v", err)
|
||||
}
|
||||
for _, u := range t.Members {
|
||||
has, err := hasAccess(e, u, repo, ACCESS_MODE_READ)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = watchRepo(e, u.ID, repo.ID, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveRepository removes repository from team of organization.
|
||||
func (t *Team) RemoveRepository(repoID int64) error {
|
||||
if !t.HasRepository(repoID) {
|
||||
return nil
|
||||
}
|
||||
|
||||
repo, err := GetRepositoryByID(repoID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = t.removeRepository(sess, repo, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// NewTeam creates a record of new team.
|
||||
// It's caller's responsibility to assign organization ID.
|
||||
func NewTeam(t *Team) error {
|
||||
if len(t.Name) == 0 {
|
||||
return errors.New("empty team name")
|
||||
}
|
||||
|
||||
has, err := x.Id(t.OrgID).Get(new(User))
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return ErrOrgNotExist
|
||||
}
|
||||
|
||||
t.LowerName = strings.ToLower(t.Name)
|
||||
has, err = x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).Get(new(Team))
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
return ErrTeamAlreadyExist{t.OrgID, t.LowerName}
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.Insert(t); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
// Update organization number of teams.
|
||||
if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func getTeam(e Engine, orgId int64, name string) (*Team, error) {
|
||||
t := &Team{
|
||||
OrgID: orgId,
|
||||
LowerName: strings.ToLower(name),
|
||||
}
|
||||
has, err := e.Get(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrTeamNotExist
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// GetTeam returns team by given team name and organization.
|
||||
func GetTeam(orgId int64, name string) (*Team, error) {
|
||||
return getTeam(x, orgId, name)
|
||||
}
|
||||
|
||||
func getTeamByID(e Engine, teamId int64) (*Team, error) {
|
||||
t := new(Team)
|
||||
has, err := e.Id(teamId).Get(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrTeamNotExist
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// GetTeamByID returns team by given ID.
|
||||
func GetTeamByID(teamId int64) (*Team, error) {
|
||||
return getTeamByID(x, teamId)
|
||||
}
|
||||
|
||||
// UpdateTeam updates information of team.
|
||||
func UpdateTeam(t *Team, authChanged bool) (err error) {
|
||||
if len(t.Name) == 0 {
|
||||
return errors.New("empty team name")
|
||||
}
|
||||
|
||||
if len(t.Description) > 255 {
|
||||
t.Description = t.Description[:255]
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.LowerName = strings.ToLower(t.Name)
|
||||
has, err := x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).And("id!=?", t.ID).Get(new(Team))
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
return ErrTeamAlreadyExist{t.OrgID, t.LowerName}
|
||||
}
|
||||
|
||||
if _, err = sess.Id(t.ID).AllCols().Update(t); err != nil {
|
||||
return fmt.Errorf("update: %v", err)
|
||||
}
|
||||
|
||||
// Update access for team members if needed.
|
||||
if authChanged {
|
||||
if err = t.getRepositories(sess); err != nil {
|
||||
return fmt.Errorf("getRepositories:%v", err)
|
||||
}
|
||||
|
||||
for _, repo := range t.Repos {
|
||||
if err = repo.recalculateTeamAccesses(sess, 0); err != nil {
|
||||
return fmt.Errorf("recalculateTeamAccesses: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// DeleteTeam deletes given team.
|
||||
// It's caller's responsibility to assign organization ID.
|
||||
func DeleteTeam(t *Team) error {
|
||||
if err := t.GetRepositories(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get organization.
|
||||
org, err := GetUserByID(t.OrgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete all accesses.
|
||||
for _, repo := range t.Repos {
|
||||
if err = repo.recalculateTeamAccesses(sess, t.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Delete team-user.
|
||||
if _, err = sess.Where("org_id=?", org.ID).Where("team_id=?", t.ID).Delete(new(TeamUser)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete team.
|
||||
if _, err = sess.Id(t.ID).Delete(new(Team)); err != nil {
|
||||
return err
|
||||
}
|
||||
// Update organization number of teams.
|
||||
if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// ___________ ____ ___
|
||||
// \__ ___/___ _____ _____ | | \______ ___________
|
||||
// | |_/ __ \\__ \ / \| | / ___// __ \_ __ \
|
||||
// | |\ ___/ / __ \| Y Y \ | /\___ \\ ___/| | \/
|
||||
// |____| \___ >____ /__|_| /______//____ >\___ >__|
|
||||
// \/ \/ \/ \/ \/
|
||||
|
||||
// TeamUser represents an team-user relation.
|
||||
type TeamUser struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OrgID int64 `xorm:"INDEX"`
|
||||
TeamID int64 `xorm:"UNIQUE(s)"`
|
||||
UID int64 `xorm:"UNIQUE(s)"`
|
||||
}
|
||||
|
||||
func isTeamMember(e Engine, orgID, teamID, uid int64) bool {
|
||||
has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("uid=?", uid).Get(new(TeamUser))
|
||||
return has
|
||||
}
|
||||
|
||||
// IsTeamMember returns true if given user is a member of team.
|
||||
func IsTeamMember(orgID, teamID, uid int64) bool {
|
||||
return isTeamMember(x, orgID, teamID, uid)
|
||||
}
|
||||
|
||||
func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) {
|
||||
teamUsers := make([]*TeamUser, 0, 10)
|
||||
if err = e.Sql("SELECT `id`, `org_id`, `team_id`, `uid` FROM `team_user` WHERE team_id=?", teamID).
|
||||
Find(&teamUsers); err != nil {
|
||||
return nil, fmt.Errorf("get team-users: %v", err)
|
||||
}
|
||||
members := make([]*User, 0, len(teamUsers))
|
||||
for i := range teamUsers {
|
||||
member := new(User)
|
||||
if _, err = e.Id(teamUsers[i].UID).Get(member); err != nil {
|
||||
return nil, fmt.Errorf("get user '%d': %v", teamUsers[i].UID, err)
|
||||
}
|
||||
members = append(members, member)
|
||||
}
|
||||
return members, nil
|
||||
}
|
||||
|
||||
// GetTeamMembers returns all members in given team of organization.
|
||||
func GetTeamMembers(teamID int64) ([]*User, error) {
|
||||
return getTeamMembers(x, teamID)
|
||||
}
|
||||
|
||||
func getUserTeams(e Engine, orgId, uid int64) ([]*Team, error) {
|
||||
tus := make([]*TeamUser, 0, 5)
|
||||
if err := e.Where("uid=?", uid).And("org_id=?", orgId).Find(&tus); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ts := make([]*Team, len(tus))
|
||||
for i, tu := range tus {
|
||||
t := new(Team)
|
||||
has, err := e.Id(tu.TeamID).Get(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrTeamNotExist
|
||||
}
|
||||
ts[i] = t
|
||||
}
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
// GetUserTeams returns all teams that user belongs to in given organization.
|
||||
func GetUserTeams(orgId, uid int64) ([]*Team, error) {
|
||||
return getUserTeams(x, orgId, uid)
|
||||
}
|
||||
|
||||
// AddTeamMember adds new membership of given team to given organization,
|
||||
// the user will have membership to given organization automatically when needed.
|
||||
func AddTeamMember(orgID, teamID, uid int64) error {
|
||||
if IsTeamMember(orgID, teamID, uid) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := AddOrgUser(orgID, uid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get team and its repositories.
|
||||
t, err := GetTeamByID(teamID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.NumMembers++
|
||||
|
||||
if err = t.GetRepositories(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tu := &TeamUser{
|
||||
UID: uid,
|
||||
OrgID: orgID,
|
||||
TeamID: teamID,
|
||||
}
|
||||
if _, err = sess.Insert(tu); err != nil {
|
||||
return err
|
||||
} else if _, err = sess.Id(t.ID).Update(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Give access to team repositories.
|
||||
for _, repo := range t.Repos {
|
||||
if err = repo.recalculateTeamAccesses(sess, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// We make sure it exists before.
|
||||
ou := new(OrgUser)
|
||||
if _, err = sess.Where("uid = ?", uid).And("org_id = ?", orgID).Get(ou); err != nil {
|
||||
return err
|
||||
}
|
||||
ou.NumTeams++
|
||||
if t.IsOwnerTeam() {
|
||||
ou.IsOwner = true
|
||||
}
|
||||
if _, err = sess.Id(ou.ID).AllCols().Update(ou); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
||||
if !isTeamMember(e, orgID, teamID, uid) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get team and its repositories.
|
||||
t, err := getTeamByID(e, teamID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if the user to delete is the last member in owner team.
|
||||
if t.IsOwnerTeam() && t.NumMembers == 1 {
|
||||
return ErrLastOrgOwner{UID: uid}
|
||||
}
|
||||
|
||||
t.NumMembers--
|
||||
|
||||
if err = t.getRepositories(e); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get organization.
|
||||
org, err := getUserByID(e, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tu := &TeamUser{
|
||||
UID: uid,
|
||||
OrgID: orgID,
|
||||
TeamID: teamID,
|
||||
}
|
||||
if _, err := e.Delete(tu); err != nil {
|
||||
return err
|
||||
} else if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete access to team repositories.
|
||||
for _, repo := range t.Repos {
|
||||
if err = repo.recalculateTeamAccesses(e, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// This must exist.
|
||||
ou := new(OrgUser)
|
||||
_, err = e.Where("uid = ?", uid).And("org_id = ?", org.ID).Get(ou)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ou.NumTeams--
|
||||
if t.IsOwnerTeam() {
|
||||
ou.IsOwner = false
|
||||
}
|
||||
if _, err = e.Id(ou.ID).AllCols().Update(ou); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveTeamMember removes member from given team of given organization.
|
||||
func RemoveTeamMember(orgID, teamID, uid int64) error {
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := removeTeamMember(sess, orgID, teamID, uid); err != nil {
|
||||
return err
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// ___________ __________
|
||||
// \__ ___/___ _____ _____\______ \ ____ ______ ____
|
||||
// | |_/ __ \\__ \ / \| _// __ \\____ \ / _ \
|
||||
// | |\ ___/ / __ \| Y Y \ | \ ___/| |_> > <_> )
|
||||
// |____| \___ >____ /__|_| /____|_ /\___ > __/ \____/
|
||||
// \/ \/ \/ \/ \/|__|
|
||||
|
||||
// TeamRepo represents an team-repository relation.
|
||||
type TeamRepo struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OrgID int64 `xorm:"INDEX"`
|
||||
TeamID int64 `xorm:"UNIQUE(s)"`
|
||||
RepoID int64 `xorm:"UNIQUE(s)"`
|
||||
}
|
||||
|
||||
func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool {
|
||||
has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("repo_id=?", repoID).Get(new(TeamRepo))
|
||||
return has
|
||||
}
|
||||
|
||||
// HasTeamRepo returns true if given repository belongs to team.
|
||||
func HasTeamRepo(orgID, teamID, repoID int64) bool {
|
||||
return hasTeamRepo(x, orgID, teamID, repoID)
|
||||
}
|
||||
|
||||
func addTeamRepo(e Engine, orgID, teamID, repoID int64) error {
|
||||
_, err := e.InsertOne(&TeamRepo{
|
||||
OrgID: orgID,
|
||||
TeamID: teamID,
|
||||
RepoID: repoID,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// AddTeamRepo adds new repository relation to team.
|
||||
func AddTeamRepo(orgID, teamID, repoID int64) error {
|
||||
return addTeamRepo(x, orgID, teamID, repoID)
|
||||
}
|
||||
|
||||
func removeTeamRepo(e Engine, teamID, repoID int64) error {
|
||||
_, err := e.Delete(&TeamRepo{
|
||||
TeamID: teamID,
|
||||
RepoID: repoID,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveTeamRepo deletes repository relation to team.
|
||||
func RemoveTeamRepo(teamID, repoID int64) error {
|
||||
return removeTeamRepo(x, teamID, repoID)
|
||||
}
|
||||
448
models/pull.go
448
models/pull.go
@@ -13,15 +13,18 @@ import (
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
"github.com/gogits/git-module"
|
||||
api "github.com/gogits/go-gogs-client"
|
||||
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
"github.com/gogits/gogs/modules/sync"
|
||||
)
|
||||
|
||||
var PullRequestQueue = sync.NewUniqueQueue(setting.Repository.PullRequestQueueLength)
|
||||
|
||||
type PullRequestType int
|
||||
|
||||
const (
|
||||
@@ -58,60 +61,115 @@ type PullRequest struct {
|
||||
|
||||
HasMerged bool
|
||||
MergedCommitID string `xorm:"VARCHAR(40)"`
|
||||
Merged time.Time
|
||||
MergerID int64
|
||||
Merger *User `xorm:"-"`
|
||||
Merger *User `xorm:"-"`
|
||||
Merged time.Time `xorm:"-"`
|
||||
MergedUnix int64
|
||||
}
|
||||
|
||||
// Note: don't try to get Pull because will end up recursive querying.
|
||||
func (pr *PullRequest) BeforeUpdate() {
|
||||
pr.MergedUnix = pr.Merged.Unix()
|
||||
}
|
||||
|
||||
// Note: don't try to get Issue because will end up recursive querying.
|
||||
func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "merged":
|
||||
case "merged_unix":
|
||||
if !pr.HasMerged {
|
||||
return
|
||||
}
|
||||
|
||||
pr.Merged = regulateTimeZone(pr.Merged)
|
||||
pr.Merged = time.Unix(pr.MergedUnix, 0).Local()
|
||||
}
|
||||
}
|
||||
|
||||
func (pr *PullRequest) getHeadRepo(e Engine) (err error) {
|
||||
pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID)
|
||||
if err != nil && !IsErrRepoNotExist(err) {
|
||||
return fmt.Errorf("getRepositoryByID(head): %v", err)
|
||||
// Note: don't try to get Issue because will end up recursive querying.
|
||||
func (pr *PullRequest) loadAttributes(e Engine) (err error) {
|
||||
if pr.HeadRepo == nil {
|
||||
pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID)
|
||||
if err != nil && !IsErrRepoNotExist(err) {
|
||||
return fmt.Errorf("getRepositoryByID.(HeadRepo) [%d]: %v", pr.HeadRepoID, err)
|
||||
}
|
||||
}
|
||||
|
||||
if pr.BaseRepo == nil {
|
||||
pr.BaseRepo, err = getRepositoryByID(e, pr.BaseRepoID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getRepositoryByID.(BaseRepo) [%d]: %v", pr.BaseRepoID, err)
|
||||
}
|
||||
}
|
||||
|
||||
if pr.HasMerged && pr.Merger == nil {
|
||||
pr.Merger, err = getUserByID(e, pr.MergerID)
|
||||
if IsErrUserNotExist(err) {
|
||||
pr.MergerID = -1
|
||||
pr.Merger = NewGhostUser()
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("getUserByID [%d]: %v", pr.MergerID, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pr *PullRequest) GetHeadRepo() (err error) {
|
||||
return pr.getHeadRepo(x)
|
||||
func (pr *PullRequest) LoadAttributes() error {
|
||||
return pr.loadAttributes(x)
|
||||
}
|
||||
|
||||
func (pr *PullRequest) GetBaseRepo() (err error) {
|
||||
if pr.BaseRepo != nil {
|
||||
func (pr *PullRequest) LoadIssue() (err error) {
|
||||
if pr.Issue != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
pr.BaseRepo, err = GetRepositoryByID(pr.BaseRepoID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetRepositoryByID(base): %v", err)
|
||||
}
|
||||
return nil
|
||||
pr.Issue, err = GetIssueByID(pr.IssueID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (pr *PullRequest) GetMerger() (err error) {
|
||||
if !pr.HasMerged || pr.Merger != nil {
|
||||
return nil
|
||||
// This method assumes following fields have been assigned with valid values:
|
||||
// Required - Issue, BaseRepo
|
||||
// Optional - HeadRepo, Merger
|
||||
func (pr *PullRequest) APIFormat() *api.PullRequest {
|
||||
// In case of head repo has been deleted.
|
||||
var apiHeadRepo *api.Repository
|
||||
if pr.HeadRepo == nil {
|
||||
apiHeadRepo = &api.Repository{
|
||||
Name: "deleted",
|
||||
}
|
||||
} else {
|
||||
apiHeadRepo = pr.HeadRepo.APIFormat(nil)
|
||||
}
|
||||
|
||||
pr.Merger, err = GetUserByID(pr.MergerID)
|
||||
if IsErrUserNotExist(err) {
|
||||
pr.MergerID = -1
|
||||
pr.Merger = NewFakeUser()
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("GetUserByID: %v", err)
|
||||
apiIssue := pr.Issue.APIFormat()
|
||||
apiPullRequest := &api.PullRequest{
|
||||
ID: pr.ID,
|
||||
Index: pr.Index,
|
||||
Poster: apiIssue.Poster,
|
||||
Title: apiIssue.Title,
|
||||
Body: apiIssue.Body,
|
||||
Labels: apiIssue.Labels,
|
||||
Milestone: apiIssue.Milestone,
|
||||
Assignee: apiIssue.Assignee,
|
||||
State: apiIssue.State,
|
||||
Comments: apiIssue.Comments,
|
||||
HeadBranch: pr.HeadBranch,
|
||||
HeadRepo: apiHeadRepo,
|
||||
BaseBranch: pr.BaseBranch,
|
||||
BaseRepo: pr.BaseRepo.APIFormat(nil),
|
||||
HTMLURL: pr.Issue.HTMLURL(),
|
||||
HasMerged: pr.HasMerged,
|
||||
}
|
||||
return nil
|
||||
|
||||
if pr.Status != PULL_REQUEST_STATUS_CHECKING {
|
||||
mergeable := pr.Status != PULL_REQUEST_STATUS_CONFLICT
|
||||
apiPullRequest.Mergeable = &mergeable
|
||||
}
|
||||
if pr.HasMerged {
|
||||
apiPullRequest.Merged = &pr.Merged
|
||||
apiPullRequest.MergedCommitID = &pr.MergedCommitID
|
||||
apiPullRequest.MergedBy = pr.Merger.APIFormat()
|
||||
}
|
||||
|
||||
return apiPullRequest
|
||||
}
|
||||
|
||||
// IsChecking returns true if this pull request is still checking conflict.
|
||||
@@ -125,12 +183,12 @@ func (pr *PullRequest) CanAutoMerge() bool {
|
||||
}
|
||||
|
||||
// Merge merges pull request to base repository.
|
||||
// FIXME: add repoWorkingPull make sure two merges does not happen at same time.
|
||||
func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error) {
|
||||
if err = pr.GetHeadRepo(); err != nil {
|
||||
return fmt.Errorf("GetHeadRepo: %v", err)
|
||||
} else if err = pr.GetBaseRepo(); err != nil {
|
||||
return fmt.Errorf("GetBaseRepo: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
go HookQueue.Add(pr.BaseRepo.ID)
|
||||
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false)
|
||||
}()
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
@@ -138,7 +196,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
|
||||
return err
|
||||
}
|
||||
|
||||
if err = pr.Issue.changeStatus(sess, doer, true); err != nil {
|
||||
if err = pr.Issue.changeStatus(sess, doer, pr.Issue.Repo, true); err != nil {
|
||||
return fmt.Errorf("Issue.changeStatus: %v", err)
|
||||
}
|
||||
|
||||
@@ -147,21 +205,6 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
|
||||
if err != nil {
|
||||
return fmt.Errorf("OpenRepository: %v", err)
|
||||
}
|
||||
pr.MergedCommitID, err = headGitRepo.GetBranchCommitID(pr.HeadBranch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetBranchCommitID: %v", err)
|
||||
}
|
||||
|
||||
if err = mergePullRequestAction(sess, doer, pr.Issue.Repo, pr.Issue); err != nil {
|
||||
return fmt.Errorf("mergePullRequestAction: %v", err)
|
||||
}
|
||||
|
||||
pr.HasMerged = true
|
||||
pr.Merged = time.Now()
|
||||
pr.MergerID = doer.Id
|
||||
if _, err = sess.Id(pr.ID).AllCols().Update(pr); err != nil {
|
||||
return fmt.Errorf("update pull request: %v", err)
|
||||
}
|
||||
|
||||
// Clone base repo.
|
||||
tmpBasePath := path.Join(setting.AppDataPath, "tmp/repos", com.ToStr(time.Now().Nanosecond())+".git")
|
||||
@@ -217,37 +260,72 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error
|
||||
return fmt.Errorf("git push: %s", stderr)
|
||||
}
|
||||
|
||||
pr.MergedCommitID, err = headGitRepo.GetBranchCommitID(pr.HeadBranch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetBranchCommit: %v", err)
|
||||
}
|
||||
|
||||
pr.HasMerged = true
|
||||
pr.Merged = time.Now()
|
||||
pr.MergerID = doer.ID
|
||||
if _, err = sess.Id(pr.ID).AllCols().Update(pr); err != nil {
|
||||
return fmt.Errorf("update pull request: %v", err)
|
||||
}
|
||||
|
||||
if err = sess.Commit(); err != nil {
|
||||
return fmt.Errorf("Commit: %v", err)
|
||||
}
|
||||
|
||||
// Compose commit repository action
|
||||
if err = MergePullRequestAction(doer, pr.Issue.Repo, pr.Issue); err != nil {
|
||||
log.Error(4, "MergePullRequestAction [%d]: %v", pr.ID, err)
|
||||
}
|
||||
|
||||
// Reload pull request information.
|
||||
if err = pr.LoadAttributes(); err != nil {
|
||||
log.Error(4, "LoadAttributes: %v", err)
|
||||
return nil
|
||||
}
|
||||
if err = PrepareWebhooks(pr.Issue.Repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
|
||||
Action: api.HOOK_ISSUE_CLOSED,
|
||||
Index: pr.Index,
|
||||
PullRequest: pr.APIFormat(),
|
||||
Repository: pr.Issue.Repo.APIFormat(nil),
|
||||
Sender: doer.APIFormat(),
|
||||
}); err != nil {
|
||||
log.Error(4, "PrepareWebhooks: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
l, err := headGitRepo.CommitsBetweenIDs(pr.MergedCommitID, pr.MergeBase)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CommitsBetween: %v", err)
|
||||
log.Error(4, "CommitsBetweenIDs: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: when squash commits, no need to append merge commit.
|
||||
// It is possible that head branch is not fully sync with base branch for merge commits,
|
||||
// so we need to get latest head commit and append merge commit manully
|
||||
// to avoid strange diff commits produced.
|
||||
mergeCommit, err := baseGitRepo.GetBranchCommit(pr.BaseBranch)
|
||||
if err != nil {
|
||||
log.Error(4, "GetBranchCommit: %v", err)
|
||||
return nil
|
||||
}
|
||||
l.PushFront(mergeCommit)
|
||||
|
||||
p := &api.PushPayload{
|
||||
Ref: "refs/heads/" + pr.BaseBranch,
|
||||
Ref: git.BRANCH_PREFIX + pr.BaseBranch,
|
||||
Before: pr.MergeBase,
|
||||
After: pr.MergedCommitID,
|
||||
CompareUrl: setting.AppUrl + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
|
||||
Commits: ListToPushCommits(l).ToApiPayloadCommits(pr.BaseRepo.FullRepoLink()),
|
||||
Repo: pr.BaseRepo.ComposePayload(),
|
||||
Pusher: &api.PayloadAuthor{
|
||||
Name: pr.HeadRepo.MustOwner().DisplayName(),
|
||||
Email: pr.HeadRepo.MustOwner().Email,
|
||||
UserName: pr.HeadRepo.MustOwner().Name,
|
||||
},
|
||||
Sender: &api.PayloadUser{
|
||||
UserName: doer.Name,
|
||||
ID: doer.Id,
|
||||
AvatarUrl: setting.AppUrl + doer.RelAvatarLink(),
|
||||
},
|
||||
CompareURL: setting.AppUrl + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
|
||||
Commits: ListToPushCommits(l).ToApiPayloadCommits(pr.BaseRepo.HTMLURL()),
|
||||
Repo: pr.BaseRepo.APIFormat(nil),
|
||||
Pusher: pr.HeadRepo.MustOwner().APIFormat(),
|
||||
Sender: doer.APIFormat(),
|
||||
}
|
||||
if err = PrepareWebhooks(pr.BaseRepo, HOOK_EVENT_PUSH, p); err != nil {
|
||||
return fmt.Errorf("PrepareWebhooks: %v", err)
|
||||
}
|
||||
go HookQueue.Add(pr.BaseRepo.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -256,6 +334,7 @@ var patchConflicts = []string{
|
||||
"patch does not apply",
|
||||
"already exists in working directory",
|
||||
"unrecognized input",
|
||||
"error:",
|
||||
}
|
||||
|
||||
// testPatch checks if patch can be merged to base repository without conflit.
|
||||
@@ -279,28 +358,23 @@ func (pr *PullRequest) testPatch() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Trace("PullRequest[%d].testPatch(patchPath): %s", pr.ID, patchPath)
|
||||
repoWorkingPool.CheckIn(com.ToStr(pr.BaseRepoID))
|
||||
defer repoWorkingPool.CheckOut(com.ToStr(pr.BaseRepoID))
|
||||
|
||||
if err := pr.BaseRepo.UpdateLocalCopy(); err != nil {
|
||||
return fmt.Errorf("UpdateLocalCopy: %v", err)
|
||||
}
|
||||
log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
|
||||
|
||||
// Checkout base branch.
|
||||
_, stderr, err := process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(),
|
||||
fmt.Sprintf("PullRequest.Merge(git checkout): %v", pr.BaseRepo.ID),
|
||||
"git", "checkout", pr.BaseBranch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("git checkout: %s", stderr)
|
||||
if err := pr.BaseRepo.UpdateLocalCopyBranch(pr.BaseBranch); err != nil {
|
||||
return fmt.Errorf("UpdateLocalCopy [%d]: %v", pr.BaseRepoID, err)
|
||||
}
|
||||
|
||||
pr.Status = PULL_REQUEST_STATUS_CHECKING
|
||||
_, stderr, err = process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(),
|
||||
fmt.Sprintf("testPatch(git apply --check): %d", pr.BaseRepo.ID),
|
||||
_, stderr, err := process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(),
|
||||
fmt.Sprintf("testPatch (git apply --check): %d", pr.BaseRepo.ID),
|
||||
"git", "apply", "--check", patchPath)
|
||||
if err != nil {
|
||||
for i := range patchConflicts {
|
||||
if strings.Contains(stderr, patchConflicts[i]) {
|
||||
log.Trace("PullRequest[%d].testPatch(apply): has conflit", pr.ID)
|
||||
log.Trace("PullRequest[%d].testPatch (apply): has conflit", pr.ID)
|
||||
fmt.Println(stderr)
|
||||
pr.Status = PULL_REQUEST_STATUS_CONFLICT
|
||||
return nil
|
||||
@@ -320,26 +394,16 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
||||
return err
|
||||
}
|
||||
|
||||
if err = newIssue(sess, repo, pull, labelIDs, uuids, true); err != nil {
|
||||
if err = newIssue(sess, NewIssueOptions{
|
||||
Repo: repo,
|
||||
Issue: pull,
|
||||
LableIDs: labelIDs,
|
||||
Attachments: uuids,
|
||||
IsPull: true,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("newIssue: %v", err)
|
||||
}
|
||||
|
||||
// Notify watchers.
|
||||
act := &Action{
|
||||
ActUserID: pull.Poster.Id,
|
||||
ActUserName: pull.Poster.Name,
|
||||
ActEmail: pull.Poster.Email,
|
||||
OpType: CREATE_PULL_REQUEST,
|
||||
Content: fmt.Sprintf("%d|%s", pull.Index, pull.Name),
|
||||
RepoID: repo.ID,
|
||||
RepoUserName: repo.Owner.Name,
|
||||
RepoName: repo.Name,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
}
|
||||
if err = notifyWatchers(sess, act); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pr.Index = pull.Index
|
||||
if err = repo.SavePatch(pr.Index, patch); err != nil {
|
||||
return fmt.Errorf("SavePatch: %v", err)
|
||||
@@ -349,6 +413,7 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
||||
if err = pr.testPatch(); err != nil {
|
||||
return fmt.Errorf("testPatch: %v", err)
|
||||
}
|
||||
// No conflict appears after test means mergeable.
|
||||
if pr.Status == PULL_REQUEST_STATUS_CHECKING {
|
||||
pr.Status = PULL_REQUEST_STATUS_MERGEABLE
|
||||
}
|
||||
@@ -358,7 +423,39 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
|
||||
return fmt.Errorf("insert pull repo: %v", err)
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
if err = sess.Commit(); err != nil {
|
||||
return fmt.Errorf("Commit: %v", err)
|
||||
}
|
||||
|
||||
if err = NotifyWatchers(&Action{
|
||||
ActUserID: pull.Poster.ID,
|
||||
ActUserName: pull.Poster.Name,
|
||||
OpType: ACTION_CREATE_PULL_REQUEST,
|
||||
Content: fmt.Sprintf("%d|%s", pull.Index, pull.Title),
|
||||
RepoID: repo.ID,
|
||||
RepoUserName: repo.Owner.Name,
|
||||
RepoName: repo.Name,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
}); err != nil {
|
||||
log.Error(4, "NotifyWatchers: %v", err)
|
||||
} else if err = pull.MailParticipants(); err != nil {
|
||||
log.Error(4, "MailParticipants: %v", err)
|
||||
}
|
||||
|
||||
pr.Issue = pull
|
||||
pull.PullRequest = pr
|
||||
if err = PrepareWebhooks(repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
|
||||
Action: api.HOOK_ISSUE_OPENED,
|
||||
Index: pull.Index,
|
||||
PullRequest: pr.APIFormat(),
|
||||
Repository: repo.APIFormat(nil),
|
||||
Sender: pull.Poster.APIFormat(),
|
||||
}); err != nil {
|
||||
log.Error(4, "PrepareWebhooks: %v", err)
|
||||
}
|
||||
go HookQueue.Add(repo.ID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUnmergedPullRequest returnss a pull request that is open and has not been merged
|
||||
@@ -381,9 +478,9 @@ func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch
|
||||
// by given head information (repo and branch).
|
||||
func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string) ([]*PullRequest, error) {
|
||||
prs := make([]*PullRequest, 0, 2)
|
||||
return prs, x.Where("head_repo_id=? AND head_branch=? AND has_merged=? AND issue.is_closed=?",
|
||||
return prs, x.Where("head_repo_id = ? AND head_branch = ? AND has_merged = ? AND issue.is_closed = ?",
|
||||
repoID, branch, false, false).
|
||||
Join("INNER", "issue", "issue.id=pull_request.issue_id").Find(&prs)
|
||||
Join("INNER", "issue", "issue.id = pull_request.issue_id").Find(&prs)
|
||||
}
|
||||
|
||||
// GetUnmergedPullRequestsByBaseInfo returnss all pull requests that are open and has not been merged
|
||||
@@ -395,30 +492,38 @@ func GetUnmergedPullRequestsByBaseInfo(repoID int64, branch string) ([]*PullRequ
|
||||
Join("INNER", "issue", "issue.id=pull_request.issue_id").Find(&prs)
|
||||
}
|
||||
|
||||
// GetPullRequestByID returns a pull request by given ID.
|
||||
func GetPullRequestByID(id int64) (*PullRequest, error) {
|
||||
func getPullRequestByID(e Engine, id int64) (*PullRequest, error) {
|
||||
pr := new(PullRequest)
|
||||
has, err := x.Id(id).Get(pr)
|
||||
has, err := e.Id(id).Get(pr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrPullRequestNotExist{id, 0, 0, 0, "", ""}
|
||||
}
|
||||
return pr, nil
|
||||
return pr, pr.loadAttributes(e)
|
||||
}
|
||||
|
||||
// GetPullRequestByIssueID returns pull request by given issue ID.
|
||||
func GetPullRequestByIssueID(issueID int64) (*PullRequest, error) {
|
||||
// GetPullRequestByID returns a pull request by given ID.
|
||||
func GetPullRequestByID(id int64) (*PullRequest, error) {
|
||||
return getPullRequestByID(x, id)
|
||||
}
|
||||
|
||||
func getPullRequestByIssueID(e Engine, issueID int64) (*PullRequest, error) {
|
||||
pr := &PullRequest{
|
||||
IssueID: issueID,
|
||||
}
|
||||
has, err := x.Get(pr)
|
||||
has, err := e.Get(pr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrPullRequestNotExist{0, issueID, 0, 0, "", ""}
|
||||
}
|
||||
return pr, nil
|
||||
return pr, pr.loadAttributes(e)
|
||||
}
|
||||
|
||||
// GetPullRequestByIssueID returns pull request by given issue ID.
|
||||
func GetPullRequestByIssueID(issueID int64) (*PullRequest, error) {
|
||||
return getPullRequestByIssueID(x, issueID)
|
||||
}
|
||||
|
||||
// Update updates all fields of pull request.
|
||||
@@ -433,21 +538,13 @@ func (pr *PullRequest) UpdateCols(cols ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var PullRequestQueue = NewUniqueQueue(setting.Repository.PullRequestQueueLength)
|
||||
|
||||
// UpdatePatch generates and saves a new patch.
|
||||
func (pr *PullRequest) UpdatePatch() (err error) {
|
||||
if err = pr.GetHeadRepo(); err != nil {
|
||||
return fmt.Errorf("GetHeadRepo: %v", err)
|
||||
} else if pr.HeadRepo == nil {
|
||||
if pr.HeadRepo == nil {
|
||||
log.Trace("PullRequest[%d].UpdatePatch: ignored cruppted data", pr.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = pr.GetBaseRepo(); err != nil {
|
||||
return fmt.Errorf("GetBaseRepo: %v", err)
|
||||
}
|
||||
|
||||
headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath())
|
||||
if err != nil {
|
||||
return fmt.Errorf("OpenRepository: %v", err)
|
||||
@@ -481,6 +578,37 @@ func (pr *PullRequest) UpdatePatch() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PushToBaseRepo pushes commits from branches of head repository to
|
||||
// corresponding branches of base repository.
|
||||
// FIXME: Only push branches that are actually updates?
|
||||
func (pr *PullRequest) PushToBaseRepo() (err error) {
|
||||
log.Trace("PushToBaseRepo[%d]: pushing commits to base repo 'refs/pull/%d/head'", pr.BaseRepoID, pr.Index)
|
||||
|
||||
headRepoPath := pr.HeadRepo.RepoPath()
|
||||
headGitRepo, err := git.OpenRepository(headRepoPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("OpenRepository: %v", err)
|
||||
}
|
||||
|
||||
tmpRemoteName := fmt.Sprintf("tmp-pull-%d", pr.ID)
|
||||
if err = headGitRepo.AddRemote(tmpRemoteName, pr.BaseRepo.RepoPath(), false); err != nil {
|
||||
return fmt.Errorf("headGitRepo.AddRemote: %v", err)
|
||||
}
|
||||
// Make sure to remove the remote even if the push fails
|
||||
defer headGitRepo.RemoveRemote(tmpRemoteName)
|
||||
|
||||
headFile := fmt.Sprintf("refs/pull/%d/head", pr.Index)
|
||||
|
||||
// Remove head in case there is a conflict.
|
||||
os.Remove(path.Join(pr.BaseRepo.RepoPath(), headFile))
|
||||
|
||||
if err = git.Push(headRepoPath, tmpRemoteName, fmt.Sprintf("%s:%s", pr.HeadBranch, headFile)); err != nil {
|
||||
return fmt.Errorf("Push: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddToTaskQueue adds itself to pull request test task queue.
|
||||
func (pr *PullRequest) AddToTaskQueue() {
|
||||
go PullRequestQueue.AddFunc(pr.ID, func() {
|
||||
@@ -491,12 +619,54 @@ func (pr *PullRequest) AddToTaskQueue() {
|
||||
})
|
||||
}
|
||||
|
||||
type PullRequestList []*PullRequest
|
||||
|
||||
func (prs PullRequestList) loadAttributes(e Engine) (err error) {
|
||||
if len(prs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load issues
|
||||
issueIDs := make([]int64, 0, len(prs))
|
||||
for i := range prs {
|
||||
issueIDs = append(issueIDs, prs[i].IssueID)
|
||||
}
|
||||
issues := make([]*Issue, 0, len(issueIDs))
|
||||
if err = e.Where("id > 0").In("id", issueIDs).Find(&issues); err != nil {
|
||||
return fmt.Errorf("find issues: %v", err)
|
||||
}
|
||||
|
||||
set := make(map[int64]*Issue)
|
||||
for i := range issues {
|
||||
set[issues[i].ID] = issues[i]
|
||||
}
|
||||
for i := range prs {
|
||||
prs[i].Issue = set[prs[i].IssueID]
|
||||
}
|
||||
|
||||
// Load attributes
|
||||
for i := range prs {
|
||||
if err = prs[i].loadAttributes(e); err != nil {
|
||||
return fmt.Errorf("loadAttributes [%d]: %v", prs[i].ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (prs PullRequestList) LoadAttributes() error {
|
||||
return prs.loadAttributes(x)
|
||||
}
|
||||
|
||||
func addHeadRepoTasks(prs []*PullRequest) {
|
||||
for _, pr := range prs {
|
||||
log.Trace("addHeadRepoTasks[%d]: composing new test task", pr.ID)
|
||||
if err := pr.UpdatePatch(); err != nil {
|
||||
log.Error(4, "UpdatePatch: %v", err)
|
||||
continue
|
||||
} else if err := pr.PushToBaseRepo(); err != nil {
|
||||
log.Error(4, "PushToBaseRepo: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
pr.AddToTaskQueue()
|
||||
@@ -505,19 +675,47 @@ func addHeadRepoTasks(prs []*PullRequest) {
|
||||
|
||||
// AddTestPullRequestTask adds new test tasks by given head/base repository and head/base branch,
|
||||
// and generate new patch for testing as needed.
|
||||
func AddTestPullRequestTask(repoID int64, branch string) {
|
||||
log.Trace("AddTestPullRequestTask[head_repo_id: %d, head_branch: %s]: finding pull requests", repoID, branch)
|
||||
func AddTestPullRequestTask(doer *User, repoID int64, branch string, isSync bool) {
|
||||
log.Trace("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: finding pull requests", repoID, branch)
|
||||
prs, err := GetUnmergedPullRequestsByHeadInfo(repoID, branch)
|
||||
if err != nil {
|
||||
log.Error(4, "Find pull requests[head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err)
|
||||
log.Error(4, "Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err)
|
||||
return
|
||||
}
|
||||
|
||||
if isSync {
|
||||
if err = PullRequestList(prs).LoadAttributes(); err != nil {
|
||||
log.Error(4, "PullRequestList.LoadAttributes: %v", err)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
for _, pr := range prs {
|
||||
pr.Issue.PullRequest = pr
|
||||
if err = pr.Issue.LoadAttributes(); err != nil {
|
||||
log.Error(4, "LoadAttributes: %v", err)
|
||||
continue
|
||||
}
|
||||
if err = PrepareWebhooks(pr.Issue.Repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
|
||||
Action: api.HOOK_ISSUE_SYNCHRONIZED,
|
||||
Index: pr.Issue.Index,
|
||||
PullRequest: pr.Issue.PullRequest.APIFormat(),
|
||||
Repository: pr.Issue.Repo.APIFormat(nil),
|
||||
Sender: doer.APIFormat(),
|
||||
}); err != nil {
|
||||
log.Error(4, "PrepareWebhooks [pull_id: %v]: %v", pr.ID, err)
|
||||
continue
|
||||
}
|
||||
go HookQueue.Add(pr.Issue.Repo.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addHeadRepoTasks(prs)
|
||||
|
||||
log.Trace("AddTestPullRequestTask[base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch)
|
||||
log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch)
|
||||
prs, err = GetUnmergedPullRequestsByBaseInfo(repoID, branch)
|
||||
if err != nil {
|
||||
log.Error(4, "Find pull requests[base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err)
|
||||
log.Error(4, "Find pull requests [base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err)
|
||||
return
|
||||
}
|
||||
for _, pr := range prs {
|
||||
@@ -559,8 +757,8 @@ func TestPullRequests() {
|
||||
func(idx int, bean interface{}) error {
|
||||
pr := bean.(*PullRequest)
|
||||
|
||||
if err := pr.GetBaseRepo(); err != nil {
|
||||
log.Error(3, "GetBaseRepo: %v", err)
|
||||
if err := pr.LoadAttributes(); err != nil {
|
||||
log.Error(3, "LoadAttributes: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -584,7 +782,7 @@ func TestPullRequests() {
|
||||
|
||||
pr, err := GetPullRequestByID(com.StrTo(prID).MustInt64())
|
||||
if err != nil {
|
||||
log.Error(4, "GetPullRequestByID[%d]: %v", prID, err)
|
||||
log.Error(4, "GetPullRequestByID[%s]: %v", prID, err)
|
||||
continue
|
||||
} else if err = pr.testPatch(); err != nil {
|
||||
log.Error(4, "testPatch[%d]: %v", pr.ID, err)
|
||||
|
||||
@@ -33,13 +33,21 @@ type Release struct {
|
||||
Note string `xorm:"TEXT"`
|
||||
IsDraft bool `xorm:"NOT NULL DEFAULT false"`
|
||||
IsPrerelease bool
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
|
||||
Created time.Time `xorm:"-"`
|
||||
CreatedUnix int64
|
||||
}
|
||||
|
||||
func (r *Release) BeforeInsert() {
|
||||
if r.CreatedUnix == 0 {
|
||||
r.CreatedUnix = time.Now().Unix()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Release) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created":
|
||||
r.Created = regulateTimeZone(r.Created)
|
||||
case "created_unix":
|
||||
r.Created = time.Unix(r.CreatedUnix, 0).Local()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +69,12 @@ func createTag(gitRepo *git.Repository, rel *Release) error {
|
||||
return fmt.Errorf("GetBranchCommit: %v", err)
|
||||
}
|
||||
|
||||
// Trim '--' prefix to prevent command line argument vulnerability.
|
||||
rel.TagName = strings.TrimPrefix(rel.TagName, "--")
|
||||
if err = gitRepo.CreateTag(rel.TagName, commit.ID.String()); err != nil {
|
||||
if strings.Contains(err.Error(), "is not a valid tag name") {
|
||||
return ErrInvalidTagName{rel.TagName}
|
||||
}
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
@@ -70,6 +83,7 @@ func createTag(gitRepo *git.Repository, rel *Release) error {
|
||||
return fmt.Errorf("GetTagCommit: %v", err)
|
||||
}
|
||||
|
||||
rel.Sha1 = commit.ID.String()
|
||||
rel.NumCommits, err = commit.CommitsCount()
|
||||
if err != nil {
|
||||
return fmt.Errorf("CommitsCount: %v", err)
|
||||
@@ -125,7 +139,7 @@ func GetReleaseByID(id int64) (*Release, error) {
|
||||
|
||||
// GetReleasesByRepoID returns a list of releases of repository.
|
||||
func GetReleasesByRepoID(repoID int64) (rels []*Release, err error) {
|
||||
err = x.Desc("created").Find(&rels, Release{RepoID: repoID})
|
||||
err = x.Desc("created_unix").Find(&rels, Release{RepoID: repoID})
|
||||
return rels, err
|
||||
}
|
||||
|
||||
@@ -164,13 +178,18 @@ func UpdateRelease(gitRepo *git.Repository, rel *Release) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteReleaseByID deletes a release and corresponding Git tag by given ID.
|
||||
func DeleteReleaseByID(id int64) error {
|
||||
// DeleteReleaseOfRepoByID deletes a release and corresponding Git tag by given ID.
|
||||
func DeleteReleaseOfRepoByID(repoID, id int64) error {
|
||||
rel, err := GetReleaseByID(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetReleaseByID: %v", err)
|
||||
}
|
||||
|
||||
// Mark sure the delete operation againsts same repository.
|
||||
if repoID != rel.RepoID {
|
||||
return nil
|
||||
}
|
||||
|
||||
repo, err := GetRepositoryByID(rel.RepoID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetRepositoryByID: %v", err)
|
||||
|
||||
1248
models/repo.go
1248
models/repo.go
File diff suppressed because it is too large
Load Diff
57
models/repo_branch.go
Normal file
57
models/repo_branch.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/gogits/git-module"
|
||||
)
|
||||
|
||||
type Branch struct {
|
||||
Path string
|
||||
Name string
|
||||
}
|
||||
|
||||
func GetBranchesByPath(path string) ([]*Branch, error) {
|
||||
gitRepo, err := git.OpenRepository(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
brs, err := gitRepo.GetBranches()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
branches := make([]*Branch, len(brs))
|
||||
for i := range brs {
|
||||
branches[i] = &Branch{
|
||||
Path: path,
|
||||
Name: brs[i],
|
||||
}
|
||||
}
|
||||
return branches, nil
|
||||
}
|
||||
|
||||
func (repo *Repository) GetBranch(br string) (*Branch, error) {
|
||||
if !git.IsBranchExist(repo.RepoPath(), br) {
|
||||
return nil, &ErrBranchNotExist{br}
|
||||
}
|
||||
return &Branch{
|
||||
Path: repo.RepoPath(),
|
||||
Name: br,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (repo *Repository) GetBranches() ([]*Branch, error) {
|
||||
return GetBranchesByPath(repo.RepoPath())
|
||||
}
|
||||
|
||||
func (br *Branch) GetCommit() (*git.Commit, error) {
|
||||
gitRepo, err := git.OpenRepository(br.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gitRepo.GetBranchCommit(br.Name)
|
||||
}
|
||||
163
models/repo_collaboration.go
Normal file
163
models/repo_collaboration.go
Normal file
@@ -0,0 +1,163 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Collaboration represent the relation between an individual and a repository.
|
||||
type Collaboration struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
UserID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
|
||||
Mode AccessMode `xorm:"DEFAULT 2 NOT NULL"`
|
||||
}
|
||||
|
||||
func (c *Collaboration) ModeI18nKey() string {
|
||||
switch c.Mode {
|
||||
case ACCESS_MODE_READ:
|
||||
return "repo.settings.collaboration.read"
|
||||
case ACCESS_MODE_WRITE:
|
||||
return "repo.settings.collaboration.write"
|
||||
case ACCESS_MODE_ADMIN:
|
||||
return "repo.settings.collaboration.admin"
|
||||
default:
|
||||
return "repo.settings.collaboration.undefined"
|
||||
}
|
||||
}
|
||||
|
||||
// AddCollaborator adds new collaboration to a repository with default access mode.
|
||||
func (repo *Repository) AddCollaborator(u *User) error {
|
||||
collaboration := &Collaboration{
|
||||
RepoID: repo.ID,
|
||||
UserID: u.ID,
|
||||
}
|
||||
|
||||
has, err := x.Get(collaboration)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
return nil
|
||||
}
|
||||
collaboration.Mode = ACCESS_MODE_WRITE
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.InsertOne(collaboration); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if repo.Owner.IsOrganization() {
|
||||
err = repo.recalculateTeamAccesses(sess, 0)
|
||||
} else {
|
||||
err = repo.recalculateAccesses(sess)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("recalculateAccesses 'team=%v': %v", repo.Owner.IsOrganization(), err)
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func (repo *Repository) getCollaborations(e Engine) ([]*Collaboration, error) {
|
||||
collaborations := make([]*Collaboration, 0)
|
||||
return collaborations, e.Find(&collaborations, &Collaboration{RepoID: repo.ID})
|
||||
}
|
||||
|
||||
// Collaborator represents a user with collaboration details.
|
||||
type Collaborator struct {
|
||||
*User
|
||||
Collaboration *Collaboration
|
||||
}
|
||||
|
||||
func (repo *Repository) getCollaborators(e Engine) ([]*Collaborator, error) {
|
||||
collaborations, err := repo.getCollaborations(e)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getCollaborations: %v", err)
|
||||
}
|
||||
|
||||
collaborators := make([]*Collaborator, len(collaborations))
|
||||
for i, c := range collaborations {
|
||||
user, err := getUserByID(e, c.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
collaborators[i] = &Collaborator{
|
||||
User: user,
|
||||
Collaboration: c,
|
||||
}
|
||||
}
|
||||
return collaborators, nil
|
||||
}
|
||||
|
||||
// GetCollaborators returns the collaborators for a repository
|
||||
func (repo *Repository) GetCollaborators() ([]*Collaborator, error) {
|
||||
return repo.getCollaborators(x)
|
||||
}
|
||||
|
||||
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
|
||||
func (repo *Repository) ChangeCollaborationAccessMode(uid int64, mode AccessMode) error {
|
||||
// Discard invalid input
|
||||
if mode <= ACCESS_MODE_NONE || mode > ACCESS_MODE_OWNER {
|
||||
return nil
|
||||
}
|
||||
|
||||
collaboration := &Collaboration{
|
||||
RepoID: repo.ID,
|
||||
UserID: uid,
|
||||
}
|
||||
has, err := x.Get(collaboration)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get collaboration: %v", err)
|
||||
} else if !has {
|
||||
return nil
|
||||
}
|
||||
|
||||
if collaboration.Mode == mode {
|
||||
return nil
|
||||
}
|
||||
collaboration.Mode = mode
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.Id(collaboration.ID).AllCols().Update(collaboration); err != nil {
|
||||
return fmt.Errorf("update collaboration: %v", err)
|
||||
} else if _, err = sess.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, uid, repo.ID); err != nil {
|
||||
return fmt.Errorf("update access table: %v", err)
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// DeleteCollaboration removes collaboration relation between the user and repository.
|
||||
func (repo *Repository) DeleteCollaboration(uid int64) (err error) {
|
||||
collaboration := &Collaboration{
|
||||
RepoID: repo.ID,
|
||||
UserID: uid,
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if has, err := sess.Delete(collaboration); err != nil || has == 0 {
|
||||
return err
|
||||
} else if err = repo.recalculateAccesses(sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
520
models/repo_editor.go
Normal file
520
models/repo_editor.go
Normal file
@@ -0,0 +1,520 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
gouuid "github.com/satori/go.uuid"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
git "github.com/gogits/git-module"
|
||||
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
// ___________ .___.__ __ ___________.__.__
|
||||
// \_ _____/ __| _/|__|/ |_ \_ _____/|__| | ____
|
||||
// | __)_ / __ | | \ __\ | __) | | | _/ __ \
|
||||
// | \/ /_/ | | || | | \ | | |_\ ___/
|
||||
// /_______ /\____ | |__||__| \___ / |__|____/\___ >
|
||||
// \/ \/ \/ \/
|
||||
|
||||
// discardLocalRepoBranchChanges discards local commits/changes of
|
||||
// given branch to make sure it is even to remote branch.
|
||||
func discardLocalRepoBranchChanges(localPath, branch string) error {
|
||||
if !com.IsExist(localPath) {
|
||||
return nil
|
||||
}
|
||||
// No need to check if nothing in the repository.
|
||||
if !git.IsBranchExist(localPath, branch) {
|
||||
return nil
|
||||
}
|
||||
|
||||
refName := "origin/" + branch
|
||||
if err := git.ResetHEAD(localPath, true, refName); err != nil {
|
||||
return fmt.Errorf("git reset --hard %s: %v", refName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *Repository) DiscardLocalRepoBranchChanges(branch string) error {
|
||||
return discardLocalRepoBranchChanges(repo.LocalCopyPath(), branch)
|
||||
}
|
||||
|
||||
// checkoutNewBranch checks out to a new branch from the a branch name.
|
||||
func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error {
|
||||
if err := git.Checkout(localPath, git.CheckoutOptions{
|
||||
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second,
|
||||
Branch: newBranch,
|
||||
OldBranch: oldBranch,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("git checkout -b %s %s: %v", newBranch, oldBranch, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error {
|
||||
return checkoutNewBranch(repo.RepoPath(), repo.LocalCopyPath(), oldBranch, newBranch)
|
||||
}
|
||||
|
||||
type UpdateRepoFileOptions struct {
|
||||
LastCommitID string
|
||||
OldBranch string
|
||||
NewBranch string
|
||||
OldTreeName string
|
||||
NewTreeName string
|
||||
Message string
|
||||
Content string
|
||||
IsNewFile bool
|
||||
}
|
||||
|
||||
// UpdateRepoFile adds or updates a file in repository.
|
||||
func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (err error) {
|
||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||
|
||||
if err = repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil {
|
||||
return fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", opts.OldBranch, err)
|
||||
} else if err = repo.UpdateLocalCopyBranch(opts.OldBranch); err != nil {
|
||||
return fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", opts.OldBranch, err)
|
||||
}
|
||||
|
||||
if opts.OldBranch != opts.NewBranch {
|
||||
if err := repo.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil {
|
||||
return fmt.Errorf("CheckoutNewBranch [old_branch: %s, new_branch: %s]: %v", opts.OldBranch, opts.NewBranch, err)
|
||||
}
|
||||
}
|
||||
|
||||
localPath := repo.LocalCopyPath()
|
||||
oldFilePath := path.Join(localPath, opts.OldTreeName)
|
||||
filePath := path.Join(localPath, opts.NewTreeName)
|
||||
os.MkdirAll(path.Dir(filePath), os.ModePerm)
|
||||
|
||||
// If it's meant to be a new file, make sure it doesn't exist.
|
||||
if opts.IsNewFile {
|
||||
if com.IsExist(filePath) {
|
||||
return ErrRepoFileAlreadyExist{filePath}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore move step if it's a new file under a directory.
|
||||
// Otherwise, move the file when name changed.
|
||||
if com.IsFile(oldFilePath) && opts.OldTreeName != opts.NewTreeName {
|
||||
if err = git.MoveFile(localPath, opts.OldTreeName, opts.NewTreeName); err != nil {
|
||||
return fmt.Errorf("git mv %s %s: %v", opts.OldTreeName, opts.NewTreeName, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = ioutil.WriteFile(filePath, []byte(opts.Content), 0666); err != nil {
|
||||
return fmt.Errorf("WriteFile: %v", err)
|
||||
}
|
||||
|
||||
if err = git.AddChanges(localPath, true); err != nil {
|
||||
return fmt.Errorf("git add --all: %v", err)
|
||||
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
|
||||
Committer: doer.NewGitSig(),
|
||||
Message: opts.Message,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CommitChanges: %v", err)
|
||||
} else if err = git.Push(localPath, "origin", opts.NewBranch); err != nil {
|
||||
return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err)
|
||||
}
|
||||
|
||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
log.Error(4, "OpenRepository: %v", err)
|
||||
return nil
|
||||
}
|
||||
commit, err := gitRepo.GetBranchCommit(opts.NewBranch)
|
||||
if err != nil {
|
||||
log.Error(4, "GetBranchCommit [branch: %s]: %v", opts.NewBranch, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Simulate push event.
|
||||
pushCommits := &PushCommits{
|
||||
Len: 1,
|
||||
Commits: []*PushCommit{CommitToPushCommit(commit)},
|
||||
}
|
||||
oldCommitID := opts.LastCommitID
|
||||
if opts.NewBranch != opts.OldBranch {
|
||||
oldCommitID = git.EMPTY_SHA
|
||||
}
|
||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||
PusherName: doer.Name,
|
||||
RepoOwnerID: repo.MustOwner().ID,
|
||||
RepoName: repo.Name,
|
||||
RefFullName: git.BRANCH_PREFIX + opts.NewBranch,
|
||||
OldCommitID: oldCommitID,
|
||||
NewCommitID: commit.ID.String(),
|
||||
Commits: pushCommits,
|
||||
}); err != nil {
|
||||
log.Error(4, "CommitRepoAction: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDiffPreview produces and returns diff result of a file which is not yet committed.
|
||||
func (repo *Repository) GetDiffPreview(branch, treePath, content string) (diff *Diff, err error) {
|
||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||
|
||||
if err = repo.DiscardLocalRepoBranchChanges(branch); err != nil {
|
||||
return nil, fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", branch, err)
|
||||
} else if err = repo.UpdateLocalCopyBranch(branch); err != nil {
|
||||
return nil, fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", branch, err)
|
||||
}
|
||||
|
||||
localPath := repo.LocalCopyPath()
|
||||
filePath := path.Join(localPath, treePath)
|
||||
os.MkdirAll(filepath.Dir(filePath), os.ModePerm)
|
||||
if err = ioutil.WriteFile(filePath, []byte(content), 0666); err != nil {
|
||||
return nil, fmt.Errorf("WriteFile: %v", err)
|
||||
}
|
||||
|
||||
cmd := exec.Command("git", "diff", treePath)
|
||||
cmd.Dir = localPath
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("StdoutPipe: %v", err)
|
||||
}
|
||||
|
||||
if err = cmd.Start(); err != nil {
|
||||
return nil, fmt.Errorf("Start: %v", err)
|
||||
}
|
||||
|
||||
pid := process.Add(fmt.Sprintf("GetDiffPreview [repo_path: %s]", repo.RepoPath()), cmd)
|
||||
defer process.Remove(pid)
|
||||
|
||||
diff, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ParsePatch: %v", err)
|
||||
}
|
||||
|
||||
if err = cmd.Wait(); err != nil {
|
||||
return nil, fmt.Errorf("Wait: %v", err)
|
||||
}
|
||||
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
// ________ .__ __ ___________.__.__
|
||||
// \______ \ ____ | | _____/ |_ ____ \_ _____/|__| | ____
|
||||
// | | \_/ __ \| | _/ __ \ __\/ __ \ | __) | | | _/ __ \
|
||||
// | ` \ ___/| |_\ ___/| | \ ___/ | \ | | |_\ ___/
|
||||
// /_______ /\___ >____/\___ >__| \___ > \___ / |__|____/\___ >
|
||||
// \/ \/ \/ \/ \/ \/
|
||||
//
|
||||
|
||||
type DeleteRepoFileOptions struct {
|
||||
LastCommitID string
|
||||
OldBranch string
|
||||
NewBranch string
|
||||
TreePath string
|
||||
Message string
|
||||
}
|
||||
|
||||
func (repo *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) (err error) {
|
||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||
|
||||
if err = repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil {
|
||||
return fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", opts.OldBranch, err)
|
||||
} else if err = repo.UpdateLocalCopyBranch(opts.OldBranch); err != nil {
|
||||
return fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", opts.OldBranch, err)
|
||||
}
|
||||
|
||||
if opts.OldBranch != opts.NewBranch {
|
||||
if err := repo.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil {
|
||||
return fmt.Errorf("CheckoutNewBranch [old_branch: %s, new_branch: %s]: %v", opts.OldBranch, opts.NewBranch, err)
|
||||
}
|
||||
}
|
||||
|
||||
localPath := repo.LocalCopyPath()
|
||||
if err = os.Remove(path.Join(localPath, opts.TreePath)); err != nil {
|
||||
return fmt.Errorf("Remove: %v", err)
|
||||
}
|
||||
|
||||
if err = git.AddChanges(localPath, true); err != nil {
|
||||
return fmt.Errorf("git add --all: %v", err)
|
||||
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
|
||||
Committer: doer.NewGitSig(),
|
||||
Message: opts.Message,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CommitChanges: %v", err)
|
||||
} else if err = git.Push(localPath, "origin", opts.NewBranch); err != nil {
|
||||
return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err)
|
||||
}
|
||||
|
||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
log.Error(4, "OpenRepository: %v", err)
|
||||
return nil
|
||||
}
|
||||
commit, err := gitRepo.GetBranchCommit(opts.NewBranch)
|
||||
if err != nil {
|
||||
log.Error(4, "GetBranchCommit [branch: %s]: %v", opts.NewBranch, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Simulate push event.
|
||||
pushCommits := &PushCommits{
|
||||
Len: 1,
|
||||
Commits: []*PushCommit{CommitToPushCommit(commit)},
|
||||
}
|
||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||
PusherName: doer.Name,
|
||||
RepoOwnerID: repo.MustOwner().ID,
|
||||
RepoName: repo.Name,
|
||||
RefFullName: git.BRANCH_PREFIX + opts.NewBranch,
|
||||
OldCommitID: opts.LastCommitID,
|
||||
NewCommitID: commit.ID.String(),
|
||||
Commits: pushCommits,
|
||||
}); err != nil {
|
||||
log.Error(4, "CommitRepoAction: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ____ ___ .__ .___ ___________.___.__
|
||||
// | | \______ | | _________ __| _/ \_ _____/| | | ____ ______
|
||||
// | | /\____ \| | / _ \__ \ / __ | | __) | | | _/ __ \ / ___/
|
||||
// | | / | |_> > |_( <_> ) __ \_/ /_/ | | \ | | |_\ ___/ \___ \
|
||||
// |______/ | __/|____/\____(____ /\____ | \___ / |___|____/\___ >____ >
|
||||
// |__| \/ \/ \/ \/ \/
|
||||
//
|
||||
|
||||
// Upload represent a uploaded file to a repo to be deleted when moved
|
||||
type Upload struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UUID string `xorm:"uuid UNIQUE"`
|
||||
Name string
|
||||
}
|
||||
|
||||
// UploadLocalPath returns where uploads is stored in local file system based on given UUID.
|
||||
func UploadLocalPath(uuid string) string {
|
||||
return path.Join(setting.Repository.Upload.TempPath, uuid[0:1], uuid[1:2], uuid)
|
||||
}
|
||||
|
||||
// LocalPath returns where uploads are temporarily stored in local file system.
|
||||
func (upload *Upload) LocalPath() string {
|
||||
return UploadLocalPath(upload.UUID)
|
||||
}
|
||||
|
||||
// NewUpload creates a new upload object.
|
||||
func NewUpload(name string, buf []byte, file multipart.File) (_ *Upload, err error) {
|
||||
upload := &Upload{
|
||||
UUID: gouuid.NewV4().String(),
|
||||
Name: name,
|
||||
}
|
||||
|
||||
localPath := upload.LocalPath()
|
||||
if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil {
|
||||
return nil, fmt.Errorf("MkdirAll: %v", err)
|
||||
}
|
||||
|
||||
fw, err := os.Create(localPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Create: %v", err)
|
||||
}
|
||||
defer fw.Close()
|
||||
|
||||
if _, err = fw.Write(buf); err != nil {
|
||||
return nil, fmt.Errorf("Write: %v", err)
|
||||
} else if _, err = io.Copy(fw, file); err != nil {
|
||||
return nil, fmt.Errorf("Copy: %v", err)
|
||||
}
|
||||
|
||||
if _, err := x.Insert(upload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return upload, nil
|
||||
}
|
||||
|
||||
func GetUploadByUUID(uuid string) (*Upload, error) {
|
||||
upload := &Upload{UUID: uuid}
|
||||
has, err := x.Get(upload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrUploadNotExist{0, uuid}
|
||||
}
|
||||
return upload, nil
|
||||
}
|
||||
|
||||
func GetUploadsByUUIDs(uuids []string) ([]*Upload, error) {
|
||||
if len(uuids) == 0 {
|
||||
return []*Upload{}, nil
|
||||
}
|
||||
|
||||
// Silently drop invalid uuids.
|
||||
uploads := make([]*Upload, 0, len(uuids))
|
||||
return uploads, x.In("uuid", uuids).Find(&uploads)
|
||||
}
|
||||
|
||||
func DeleteUploads(uploads ...*Upload) (err error) {
|
||||
if len(uploads) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ids := make([]int64, len(uploads))
|
||||
for i := 0; i < len(uploads); i++ {
|
||||
ids[i] = uploads[i].ID
|
||||
}
|
||||
if _, err = sess.In("id", ids).Delete(new(Upload)); err != nil {
|
||||
return fmt.Errorf("delete uploads: %v", err)
|
||||
}
|
||||
|
||||
for _, upload := range uploads {
|
||||
localPath := upload.LocalPath()
|
||||
if !com.IsFile(localPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := os.Remove(localPath); err != nil {
|
||||
return fmt.Errorf("remove upload: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func DeleteUpload(u *Upload) error {
|
||||
return DeleteUploads(u)
|
||||
}
|
||||
|
||||
func DeleteUploadByUUID(uuid string) error {
|
||||
upload, err := GetUploadByUUID(uuid)
|
||||
if err != nil {
|
||||
if IsErrUploadNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("GetUploadByUUID: %v", err)
|
||||
}
|
||||
|
||||
if err := DeleteUpload(upload); err != nil {
|
||||
return fmt.Errorf("DeleteUpload: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type UploadRepoFileOptions struct {
|
||||
LastCommitID string
|
||||
OldBranch string
|
||||
NewBranch string
|
||||
TreePath string
|
||||
Message string
|
||||
Files []string // In UUID format.
|
||||
}
|
||||
|
||||
func (repo *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions) (err error) {
|
||||
if len(opts.Files) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
uploads, err := GetUploadsByUUIDs(opts.Files)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetUploadsByUUIDs [uuids: %v]: %v", opts.Files, err)
|
||||
}
|
||||
|
||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||
|
||||
if err = repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil {
|
||||
return fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", opts.OldBranch, err)
|
||||
} else if err = repo.UpdateLocalCopyBranch(opts.OldBranch); err != nil {
|
||||
return fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", opts.OldBranch, err)
|
||||
}
|
||||
|
||||
if opts.OldBranch != opts.NewBranch {
|
||||
if err = repo.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil {
|
||||
return fmt.Errorf("CheckoutNewBranch [old_branch: %s, new_branch: %s]: %v", opts.OldBranch, opts.NewBranch, err)
|
||||
}
|
||||
}
|
||||
|
||||
localPath := repo.LocalCopyPath()
|
||||
dirPath := path.Join(localPath, opts.TreePath)
|
||||
os.MkdirAll(dirPath, os.ModePerm)
|
||||
|
||||
// Copy uploaded files into repository.
|
||||
for _, upload := range uploads {
|
||||
tmpPath := upload.LocalPath()
|
||||
targetPath := path.Join(dirPath, upload.Name)
|
||||
if !com.IsFile(tmpPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = com.Copy(tmpPath, targetPath); err != nil {
|
||||
return fmt.Errorf("Copy: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = git.AddChanges(localPath, true); err != nil {
|
||||
return fmt.Errorf("git add --all: %v", err)
|
||||
} else if err = git.CommitChanges(localPath, git.CommitChangesOptions{
|
||||
Committer: doer.NewGitSig(),
|
||||
Message: opts.Message,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CommitChanges: %v", err)
|
||||
} else if err = git.Push(localPath, "origin", opts.NewBranch); err != nil {
|
||||
return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err)
|
||||
}
|
||||
|
||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
log.Error(4, "OpenRepository: %v", err)
|
||||
return nil
|
||||
}
|
||||
commit, err := gitRepo.GetBranchCommit(opts.NewBranch)
|
||||
if err != nil {
|
||||
log.Error(4, "GetBranchCommit [branch: %s]: %v", opts.NewBranch, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Simulate push event.
|
||||
pushCommits := &PushCommits{
|
||||
Len: 1,
|
||||
Commits: []*PushCommit{CommitToPushCommit(commit)},
|
||||
}
|
||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||
PusherName: doer.Name,
|
||||
RepoOwnerID: repo.MustOwner().ID,
|
||||
RepoName: repo.Name,
|
||||
RefFullName: git.BRANCH_PREFIX + opts.NewBranch,
|
||||
OldCommitID: opts.LastCommitID,
|
||||
NewCommitID: commit.ID.String(),
|
||||
Commits: pushCommits,
|
||||
}); err != nil {
|
||||
log.Error(4, "CommitRepoAction: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return DeleteUploads(uploads...)
|
||||
}
|
||||
62
models/repo_test.go
Normal file
62
models/repo_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package models_test
|
||||
|
||||
import (
|
||||
. "github.com/gogits/gogs/models"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"testing"
|
||||
|
||||
"github.com/gogits/gogs/modules/markdown"
|
||||
)
|
||||
|
||||
func TestRepo(t *testing.T) {
|
||||
Convey("The metas map", t, func() {
|
||||
var repo = new(Repository)
|
||||
repo.Name = "testrepo"
|
||||
repo.Owner = new(User)
|
||||
repo.Owner.Name = "testuser"
|
||||
repo.ExternalTrackerFormat = "https://someurl.com/{user}/{repo}/{issue}"
|
||||
|
||||
Convey("When no external tracker is configured", func() {
|
||||
Convey("It should be nil", func() {
|
||||
repo.EnableExternalTracker = false
|
||||
So(repo.ComposeMetas(), ShouldEqual, map[string]string(nil))
|
||||
})
|
||||
Convey("It should be nil even if other settings are present", func() {
|
||||
repo.EnableExternalTracker = false
|
||||
repo.ExternalTrackerFormat = "http://someurl.com/{user}/{repo}/{issue}"
|
||||
repo.ExternalTrackerStyle = markdown.ISSUE_NAME_STYLE_NUMERIC
|
||||
So(repo.ComposeMetas(), ShouldEqual, map[string]string(nil))
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When an external issue tracker is configured", func() {
|
||||
repo.EnableExternalTracker = true
|
||||
Convey("It should default to numeric issue style", func() {
|
||||
metas := repo.ComposeMetas()
|
||||
So(metas["style"], ShouldEqual, markdown.ISSUE_NAME_STYLE_NUMERIC)
|
||||
})
|
||||
Convey("It should pass through numeric issue style setting", func() {
|
||||
repo.ExternalTrackerStyle = markdown.ISSUE_NAME_STYLE_NUMERIC
|
||||
metas := repo.ComposeMetas()
|
||||
So(metas["style"], ShouldEqual, markdown.ISSUE_NAME_STYLE_NUMERIC)
|
||||
})
|
||||
Convey("It should pass through alphanumeric issue style setting", func() {
|
||||
repo.ExternalTrackerStyle = markdown.ISSUE_NAME_STYLE_ALPHANUMERIC
|
||||
metas := repo.ComposeMetas()
|
||||
So(metas["style"], ShouldEqual, markdown.ISSUE_NAME_STYLE_ALPHANUMERIC)
|
||||
})
|
||||
Convey("It should contain the user name", func() {
|
||||
metas := repo.ComposeMetas()
|
||||
So(metas["user"], ShouldEqual, "testuser")
|
||||
})
|
||||
Convey("It should contain the repo name", func() {
|
||||
metas := repo.ComposeMetas()
|
||||
So(metas["repo"], ShouldEqual, "testrepo")
|
||||
})
|
||||
Convey("It should contain the URL format", func() {
|
||||
metas := repo.ComposeMetas()
|
||||
So(metas["format"], ShouldEqual, "https://someurl.com/{user}/{repo}/{issue}")
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -5,13 +5,12 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -22,18 +21,18 @@ import (
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
"golang.org/x/crypto/ssh"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/process"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
const (
|
||||
// "### autogenerated by gitgos, DO NOT EDIT\n"
|
||||
_TPL_PUBLICK_KEY = `command="%s serv key-%d --config='%s'",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s` + "\n"
|
||||
)
|
||||
|
||||
var sshOpLocker = sync.Mutex{}
|
||||
var sshOpLocker sync.Mutex
|
||||
|
||||
type KeyType int
|
||||
|
||||
@@ -42,69 +41,96 @@ const (
|
||||
KEY_TYPE_DEPLOY
|
||||
)
|
||||
|
||||
// PublicKey represents a SSH or deploy key.
|
||||
// PublicKey represents a user or deploy SSH public key.
|
||||
type PublicKey struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OwnerID int64 `xorm:"INDEX NOT NULL"`
|
||||
Name string `xorm:"NOT NULL"`
|
||||
Fingerprint string `xorm:"NOT NULL"`
|
||||
Content string `xorm:"TEXT NOT NULL"`
|
||||
Mode AccessMode `xorm:"NOT NULL DEFAULT 2"`
|
||||
Type KeyType `xorm:"NOT NULL DEFAULT 1"`
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
Updated time.Time // Note: Updated must below Created for AfterSet.
|
||||
HasRecentActivity bool `xorm:"-"`
|
||||
HasUsed bool `xorm:"-"`
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
OwnerID int64 `xorm:"INDEX NOT NULL"`
|
||||
Name string `xorm:"NOT NULL"`
|
||||
Fingerprint string `xorm:"NOT NULL"`
|
||||
Content string `xorm:"TEXT NOT NULL"`
|
||||
Mode AccessMode `xorm:"NOT NULL DEFAULT 2"`
|
||||
Type KeyType `xorm:"NOT NULL DEFAULT 1"`
|
||||
|
||||
Created time.Time `xorm:"-"`
|
||||
CreatedUnix int64
|
||||
Updated time.Time `xorm:"-"` // Note: Updated must below Created for AfterSet.
|
||||
UpdatedUnix int64
|
||||
HasRecentActivity bool `xorm:"-"`
|
||||
HasUsed bool `xorm:"-"`
|
||||
}
|
||||
|
||||
func (k *PublicKey) BeforeInsert() {
|
||||
k.CreatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (k *PublicKey) BeforeUpdate() {
|
||||
k.UpdatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (k *PublicKey) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created":
|
||||
case "created_unix":
|
||||
k.Created = time.Unix(k.CreatedUnix, 0).Local()
|
||||
case "updated_unix":
|
||||
k.Updated = time.Unix(k.UpdatedUnix, 0).Local()
|
||||
k.HasUsed = k.Updated.After(k.Created)
|
||||
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
// OmitEmail returns content of public key but without e-mail address.
|
||||
// OmitEmail returns content of public key without email address.
|
||||
func (k *PublicKey) OmitEmail() string {
|
||||
return strings.Join(strings.Split(k.Content, " ")[:2], " ")
|
||||
}
|
||||
|
||||
// GetAuthorizedString generates and returns formatted public key string for authorized_keys file.
|
||||
func (key *PublicKey) GetAuthorizedString() string {
|
||||
return fmt.Sprintf(_TPL_PUBLICK_KEY, setting.AppPath, key.ID, setting.CustomConf, key.Content)
|
||||
// AuthorizedString returns formatted public key string for authorized_keys file.
|
||||
func (k *PublicKey) AuthorizedString() string {
|
||||
return fmt.Sprintf(_TPL_PUBLICK_KEY, setting.AppPath, k.ID, setting.CustomConf, k.Content)
|
||||
}
|
||||
|
||||
// IsDeployKey returns true if the public key is used as deploy key.
|
||||
func (k *PublicKey) IsDeployKey() bool {
|
||||
return k.Type == KEY_TYPE_DEPLOY
|
||||
}
|
||||
|
||||
func extractTypeFromBase64Key(key string) (string, error) {
|
||||
b, err := base64.StdEncoding.DecodeString(key)
|
||||
if err != nil || len(b) < 4 {
|
||||
return "", errors.New("Invalid key format")
|
||||
return "", fmt.Errorf("invalid key format: %v", err)
|
||||
}
|
||||
|
||||
keyLength := int(binary.BigEndian.Uint32(b))
|
||||
|
||||
if len(b) < 4+keyLength {
|
||||
return "", errors.New("Invalid key format")
|
||||
return "", fmt.Errorf("invalid key format: not enough length %d", keyLength)
|
||||
}
|
||||
|
||||
return string(b[4 : 4+keyLength]), nil
|
||||
}
|
||||
|
||||
// parseKeyString parses any key string in openssh or ssh2 format to clean openssh string (rfc4253)
|
||||
// parseKeyString parses any key string in OpenSSH or SSH2 format to clean OpenSSH string (RFC4253).
|
||||
func parseKeyString(content string) (string, error) {
|
||||
// Transform all legal line endings to a single "\n"
|
||||
s := strings.Replace(strings.Replace(strings.TrimSpace(content), "\r\n", "\n", -1), "\r", "\n", -1)
|
||||
|
||||
lines := strings.Split(s, "\n")
|
||||
// Replace all windows full new lines ("\r\n")
|
||||
content = strings.Replace(content, "\r\n", "\n", -1)
|
||||
|
||||
// Replace all windows half new lines ("\r"), if it happen not to match replace above
|
||||
content = strings.Replace(content, "\r", "\n", -1)
|
||||
|
||||
// Replace ending new line as its may cause unwanted behaviour (extra line means not a single line key | OpenSSH key)
|
||||
content = strings.TrimRight(content, "\n")
|
||||
|
||||
// split lines
|
||||
lines := strings.Split(content, "\n")
|
||||
|
||||
var keyType, keyContent, keyComment string
|
||||
|
||||
if len(lines) == 1 {
|
||||
// Parse openssh format
|
||||
// Parse OpenSSH format.
|
||||
parts := strings.SplitN(lines[0], " ", 3)
|
||||
switch len(parts) {
|
||||
case 0:
|
||||
return "", errors.New("Empty key")
|
||||
return "", errors.New("empty key")
|
||||
case 1:
|
||||
keyContent = parts[0]
|
||||
case 2:
|
||||
@@ -116,17 +142,15 @@ func parseKeyString(content string) (string, error) {
|
||||
keyComment = parts[2]
|
||||
}
|
||||
|
||||
// If keyType is not given, extract it from content. If given, validate it
|
||||
// If keyType is not given, extract it from content. If given, validate it.
|
||||
t, err := extractTypeFromBase64Key(keyContent)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("extractTypeFromBase64Key: %v", err)
|
||||
}
|
||||
if len(keyType) == 0 {
|
||||
if t, err := extractTypeFromBase64Key(keyContent); err == nil {
|
||||
keyType = t
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
if t, err := extractTypeFromBase64Key(keyContent); err != nil || keyType != t {
|
||||
return "", err
|
||||
}
|
||||
keyType = t
|
||||
} else if keyType != t {
|
||||
return "", fmt.Errorf("key type and content does not match: %s - %s", keyType, t)
|
||||
}
|
||||
} else {
|
||||
// Parse SSH2 file format.
|
||||
@@ -144,17 +168,123 @@ func parseKeyString(content string) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if t, err := extractTypeFromBase64Key(keyContent); err == nil {
|
||||
keyType = t
|
||||
} else {
|
||||
return "", err
|
||||
t, err := extractTypeFromBase64Key(keyContent)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("extractTypeFromBase64Key: %v", err)
|
||||
}
|
||||
keyType = t
|
||||
}
|
||||
return keyType + " " + keyContent + " " + keyComment, nil
|
||||
}
|
||||
|
||||
// writeTmpKeyFile writes key content to a temporary file
|
||||
// and returns the name of that file, along with any possible errors.
|
||||
func writeTmpKeyFile(content string) (string, error) {
|
||||
tmpFile, err := ioutil.TempFile(setting.SSH.KeyTestPath, "gogs_keytest")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("TempFile: %v", err)
|
||||
}
|
||||
defer tmpFile.Close()
|
||||
|
||||
if _, err = tmpFile.WriteString(content); err != nil {
|
||||
return "", fmt.Errorf("WriteString: %v", err)
|
||||
}
|
||||
return tmpFile.Name(), nil
|
||||
}
|
||||
|
||||
// SSHKeyGenParsePublicKey extracts key type and length using ssh-keygen.
|
||||
func SSHKeyGenParsePublicKey(key string) (string, int, error) {
|
||||
// The ssh-keygen in Windows does not print key type, so no need go further.
|
||||
if setting.IsWindows {
|
||||
return "", 0, nil
|
||||
}
|
||||
|
||||
tmpName, err := writeTmpKeyFile(key)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("writeTmpKeyFile: %v", err)
|
||||
}
|
||||
defer os.Remove(tmpName)
|
||||
|
||||
stdout, stderr, err := process.Exec("SSHKeyGenParsePublicKey", setting.SSH.KeygenPath, "-lf", tmpName)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("fail to parse public key: %s - %s", err, stderr)
|
||||
}
|
||||
if strings.Contains(stdout, "is not a public key file") {
|
||||
return "", 0, ErrKeyUnableVerify{stdout}
|
||||
}
|
||||
|
||||
fields := strings.Split(stdout, " ")
|
||||
if len(fields) < 4 {
|
||||
return "", 0, fmt.Errorf("invalid public key line: %s", stdout)
|
||||
}
|
||||
|
||||
keyType := strings.Trim(fields[len(fields)-1], "()\r\n")
|
||||
return strings.ToLower(keyType), com.StrTo(fields[0]).MustInt(), nil
|
||||
}
|
||||
|
||||
// SSHNativeParsePublicKey extracts the key type and length using the golang SSH library.
|
||||
// NOTE: ed25519 is not supported.
|
||||
func SSHNativeParsePublicKey(keyLine string) (string, int, error) {
|
||||
fields := strings.Fields(keyLine)
|
||||
if len(fields) < 2 {
|
||||
return "", 0, fmt.Errorf("not enough fields in public key line: %s", string(keyLine))
|
||||
}
|
||||
|
||||
raw, err := base64.StdEncoding.DecodeString(fields[1])
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
pkey, err := ssh.ParsePublicKey(raw)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "ssh: unknown key algorithm") {
|
||||
return "", 0, ErrKeyUnableVerify{err.Error()}
|
||||
}
|
||||
return "", 0, fmt.Errorf("ParsePublicKey: %v", err)
|
||||
}
|
||||
|
||||
// The ssh library can parse the key, so next we find out what key exactly we have.
|
||||
switch pkey.Type() {
|
||||
case ssh.KeyAlgoDSA:
|
||||
rawPub := struct {
|
||||
Name string
|
||||
P, Q, G, Y *big.Int
|
||||
}{}
|
||||
if err := ssh.Unmarshal(pkey.Marshal(), &rawPub); err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
// as per https://bugzilla.mindrot.org/show_bug.cgi?id=1647 we should never
|
||||
// see dsa keys != 1024 bit, but as it seems to work, we will not check here
|
||||
return "dsa", rawPub.P.BitLen(), nil // use P as per crypto/dsa/dsa.go (is L)
|
||||
case ssh.KeyAlgoRSA:
|
||||
rawPub := struct {
|
||||
Name string
|
||||
E *big.Int
|
||||
N *big.Int
|
||||
}{}
|
||||
if err := ssh.Unmarshal(pkey.Marshal(), &rawPub); err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
return "rsa", rawPub.N.BitLen(), nil // use N as per crypto/rsa/rsa.go (is bits)
|
||||
case ssh.KeyAlgoECDSA256:
|
||||
return "ecdsa", 256, nil
|
||||
case ssh.KeyAlgoECDSA384:
|
||||
return "ecdsa", 384, nil
|
||||
case ssh.KeyAlgoECDSA521:
|
||||
return "ecdsa", 521, nil
|
||||
case "ssh-ed25519": // TODO: replace with ssh constant when available
|
||||
return "ed25519", 256, nil
|
||||
}
|
||||
return "", 0, fmt.Errorf("unsupported key length detection for type: %s", pkey.Type())
|
||||
}
|
||||
|
||||
// CheckPublicKeyString checks if the given public key string is recognized by SSH.
|
||||
// It returns the actual public key line on success.
|
||||
func CheckPublicKeyString(content string) (_ string, err error) {
|
||||
if setting.SSH.Disabled {
|
||||
return "", errors.New("SSH is disabled")
|
||||
}
|
||||
|
||||
content, err = parseKeyString(content)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -165,43 +295,56 @@ func CheckPublicKeyString(content string) (_ string, err error) {
|
||||
return "", errors.New("only a single line with a single key please")
|
||||
}
|
||||
|
||||
fields := strings.Fields(content)
|
||||
if len(fields) < 2 {
|
||||
return "", errors.New("too less fields")
|
||||
}
|
||||
// remove any unnecessary whitespace now
|
||||
content = strings.TrimSpace(content)
|
||||
|
||||
key, err := base64.StdEncoding.DecodeString(fields[1])
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("StdEncoding.DecodeString: %v", err)
|
||||
var (
|
||||
fnName string
|
||||
keyType string
|
||||
length int
|
||||
)
|
||||
if setting.SSH.StartBuiltinServer {
|
||||
fnName = "SSHNativeParsePublicKey"
|
||||
keyType, length, err = SSHNativeParsePublicKey(content)
|
||||
} else {
|
||||
fnName = "SSHKeyGenParsePublicKey"
|
||||
keyType, length, err = SSHKeyGenParsePublicKey(content)
|
||||
}
|
||||
pkey, err := ssh.ParsePublicKey([]byte(key))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ParsePublicKey: %v", err)
|
||||
return "", fmt.Errorf("%s: %v", fnName, err)
|
||||
}
|
||||
log.Trace("Key type: %s", pkey.Type())
|
||||
log.Trace("Key info [native: %v]: %s-%d", setting.SSH.StartBuiltinServer, keyType, length)
|
||||
|
||||
return content, nil
|
||||
if !setting.SSH.MinimumKeySizeCheck {
|
||||
return content, nil
|
||||
}
|
||||
if minLen, found := setting.SSH.MinimumKeySizes[keyType]; found && length >= minLen {
|
||||
return content, nil
|
||||
} else if found && length < minLen {
|
||||
return "", fmt.Errorf("key length is not enough: got %d, needs %d", length, minLen)
|
||||
}
|
||||
return "", fmt.Errorf("key type is not allowed: %s", keyType)
|
||||
}
|
||||
|
||||
// saveAuthorizedKeyFile writes SSH key content to authorized_keys file.
|
||||
func saveAuthorizedKeyFile(keys ...*PublicKey) error {
|
||||
// appendAuthorizedKeysToFile appends new SSH keys' content to authorized_keys file.
|
||||
func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
|
||||
sshOpLocker.Lock()
|
||||
defer sshOpLocker.Unlock()
|
||||
|
||||
fpath := filepath.Join(setting.SSHRootPath, "authorized_keys")
|
||||
fpath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
|
||||
f, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// FIXME: following command does not support in Windows.
|
||||
// Note: chmod command does not support in Windows.
|
||||
if !setting.IsWindows {
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// .ssh directory should have mode 700, and authorized_keys file should have mode 600.
|
||||
if fi.Mode().Perm() > 0600 {
|
||||
log.Error(4, "authorized_keys file has unusual permission flags: %s - setting to -rw-------", fi.Mode().Perm().String())
|
||||
@@ -212,7 +355,7 @@ func saveAuthorizedKeyFile(keys ...*PublicKey) error {
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
if _, err = f.WriteString(key.GetAuthorizedString()); err != nil {
|
||||
if _, err = f.WriteString(key.AuthorizedString()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -242,9 +385,10 @@ func addKey(e Engine, key *PublicKey) (err error) {
|
||||
if err = ioutil.WriteFile(tmpPath, []byte(key.Content), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
stdout, stderr, err := process.Exec("AddPublicKey", "ssh-keygen", "-lf", tmpPath)
|
||||
|
||||
stdout, stderr, err := process.Exec("AddPublicKey", setting.SSH.KeygenPath, "-lf", tmpPath)
|
||||
if err != nil {
|
||||
return errors.New("ssh-keygen -lf: " + stderr)
|
||||
return fmt.Errorf("fail to parse public key: %s - %s", err, stderr)
|
||||
} else if len(stdout) < 2 {
|
||||
return errors.New("not enough output for calculating fingerprint: " + stdout)
|
||||
}
|
||||
@@ -256,20 +400,21 @@ func addKey(e Engine, key *PublicKey) (err error) {
|
||||
}
|
||||
|
||||
// Don't need to rewrite this file if builtin SSH server is enabled.
|
||||
if setting.StartSSHServer {
|
||||
if setting.SSH.StartBuiltinServer {
|
||||
return nil
|
||||
}
|
||||
return saveAuthorizedKeyFile(key)
|
||||
return appendAuthorizedKeysToFile(key)
|
||||
}
|
||||
|
||||
// AddPublicKey adds new public key to database and authorized_keys file.
|
||||
func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) {
|
||||
log.Trace(content)
|
||||
if err := checkKeyContent(content); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Key name of same user cannot be duplicated.
|
||||
has, err := x.Where("owner_id=? AND name=?", ownerID, name).Get(new(PublicKey))
|
||||
has, err := x.Where("owner_id = ? AND name = ?", ownerID, name).Get(new(PublicKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if has {
|
||||
@@ -324,57 +469,7 @@ func SearchPublicKeyByContent(content string) (*PublicKey, error) {
|
||||
// ListPublicKeys returns a list of public keys belongs to given user.
|
||||
func ListPublicKeys(uid int64) ([]*PublicKey, error) {
|
||||
keys := make([]*PublicKey, 0, 5)
|
||||
return keys, x.Where("owner_id=?", uid).Find(&keys)
|
||||
}
|
||||
|
||||
// rewriteAuthorizedKeys finds and deletes corresponding line in authorized_keys file.
|
||||
func rewriteAuthorizedKeys(key *PublicKey, p, tmpP string) error {
|
||||
fr, err := os.Open(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
fw, err := os.OpenFile(tmpP, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fw.Close()
|
||||
|
||||
isFound := false
|
||||
keyword := fmt.Sprintf("key-%d", key.ID)
|
||||
buf := bufio.NewReader(fr)
|
||||
for {
|
||||
line, errRead := buf.ReadString('\n')
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
if errRead != nil {
|
||||
if errRead != io.EOF {
|
||||
return errRead
|
||||
}
|
||||
|
||||
// Reached end of file, if nothing to read then break,
|
||||
// otherwise handle the last line.
|
||||
if len(line) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Found the line and copy rest of file.
|
||||
if !isFound && strings.Contains(line, keyword) && strings.Contains(line, key.Content) {
|
||||
isFound = true
|
||||
continue
|
||||
}
|
||||
// Still finding the line, copy the line that currently read.
|
||||
if _, err = fw.WriteString(line + "\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if errRead == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return keys, x.Where("owner_id = ?", uid).Find(&keys)
|
||||
}
|
||||
|
||||
// UpdatePublicKey updates given public key.
|
||||
@@ -383,35 +478,14 @@ func UpdatePublicKey(key *PublicKey) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func deletePublicKey(e *xorm.Session, keyID int64) error {
|
||||
sshOpLocker.Lock()
|
||||
defer sshOpLocker.Unlock()
|
||||
|
||||
key := &PublicKey{ID: keyID}
|
||||
has, err := e.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
// deletePublicKeys does the actual key deletion but does not update authorized_keys file.
|
||||
func deletePublicKeys(e *xorm.Session, keyIDs ...int64) error {
|
||||
if len(keyIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err = e.Id(key.ID).Delete(new(PublicKey)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Don't need to rewrite this file if builtin SSH server is enabled.
|
||||
if setting.StartSSHServer {
|
||||
return nil
|
||||
}
|
||||
|
||||
fpath := filepath.Join(setting.SSHRootPath, "authorized_keys")
|
||||
tmpPath := filepath.Join(setting.SSHRootPath, "authorized_keys.tmp")
|
||||
if err = rewriteAuthorizedKeys(key, fpath, tmpPath); err != nil {
|
||||
return err
|
||||
} else if err = os.Remove(fpath); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(tmpPath, fpath)
|
||||
_, err := e.In("id", strings.Join(base.Int64sToStrings(keyIDs), ",")).Delete(new(PublicKey))
|
||||
return err
|
||||
}
|
||||
|
||||
// DeletePublicKey deletes SSH key information both in database and authorized_keys file.
|
||||
@@ -425,8 +499,8 @@ func DeletePublicKey(doer *User, id int64) (err error) {
|
||||
}
|
||||
|
||||
// Check if user has access to delete this key.
|
||||
if !doer.IsAdmin && doer.Id != key.OwnerID {
|
||||
return ErrKeyAccessDenied{doer.Id, key.ID, "public"}
|
||||
if !doer.IsAdmin && doer.ID != key.OwnerID {
|
||||
return ErrKeyAccessDenied{doer.ID, key.ID, "public"}
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
@@ -435,19 +509,26 @@ func DeletePublicKey(doer *User, id int64) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = deletePublicKey(sess, id); err != nil {
|
||||
if err = deletePublicKeys(sess, id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
if err = sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return RewriteAllPublicKeys()
|
||||
}
|
||||
|
||||
// RewriteAllPublicKeys removes any authorized key and rewrite all keys from database again.
|
||||
// Note: x.Iterate does not get latest data after insert/delete, so we have to call this function
|
||||
// outsite any session scope independently.
|
||||
func RewriteAllPublicKeys() error {
|
||||
sshOpLocker.Lock()
|
||||
defer sshOpLocker.Unlock()
|
||||
|
||||
tmpPath := filepath.Join(setting.SSHRootPath, "authorized_keys.tmp")
|
||||
fpath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
|
||||
tmpPath := fpath + ".tmp"
|
||||
f, err := os.OpenFile(tmpPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -455,7 +536,7 @@ func RewriteAllPublicKeys() error {
|
||||
defer os.Remove(tmpPath)
|
||||
|
||||
err = x.Iterate(new(PublicKey), func(idx int, bean interface{}) (err error) {
|
||||
_, err = f.WriteString((bean.(*PublicKey)).GetAuthorizedString())
|
||||
_, err = f.WriteString((bean.(*PublicKey)).AuthorizedString())
|
||||
return err
|
||||
})
|
||||
f.Close()
|
||||
@@ -463,7 +544,6 @@ func RewriteAllPublicKeys() error {
|
||||
return err
|
||||
}
|
||||
|
||||
fpath := filepath.Join(setting.SSHRootPath, "authorized_keys")
|
||||
if com.IsExist(fpath) {
|
||||
if err = os.Remove(fpath); err != nil {
|
||||
return err
|
||||
@@ -485,21 +565,35 @@ func RewriteAllPublicKeys() error {
|
||||
|
||||
// DeployKey represents deploy key information and its relation with repository.
|
||||
type DeployKey struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
KeyID int64 `xorm:"UNIQUE(s) INDEX"`
|
||||
RepoID int64 `xorm:"UNIQUE(s) INDEX"`
|
||||
Name string
|
||||
Fingerprint string
|
||||
Content string `xorm:"-"`
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
Updated time.Time // Note: Updated must below Created for AfterSet.
|
||||
HasRecentActivity bool `xorm:"-"`
|
||||
HasUsed bool `xorm:"-"`
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
KeyID int64 `xorm:"UNIQUE(s) INDEX"`
|
||||
RepoID int64 `xorm:"UNIQUE(s) INDEX"`
|
||||
Name string
|
||||
Fingerprint string
|
||||
Content string `xorm:"-"`
|
||||
|
||||
Created time.Time `xorm:"-"`
|
||||
CreatedUnix int64
|
||||
Updated time.Time `xorm:"-"` // Note: Updated must below Created for AfterSet.
|
||||
UpdatedUnix int64
|
||||
HasRecentActivity bool `xorm:"-"`
|
||||
HasUsed bool `xorm:"-"`
|
||||
}
|
||||
|
||||
func (k *DeployKey) BeforeInsert() {
|
||||
k.CreatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (k *DeployKey) BeforeUpdate() {
|
||||
k.UpdatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (k *DeployKey) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created":
|
||||
case "created_unix":
|
||||
k.Created = time.Unix(k.CreatedUnix, 0).Local()
|
||||
case "updated_unix":
|
||||
k.Updated = time.Unix(k.UpdatedUnix, 0).Local()
|
||||
k.HasUsed = k.Updated.After(k.Created)
|
||||
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(time.Now())
|
||||
}
|
||||
@@ -517,14 +611,14 @@ func (k *DeployKey) GetContent() error {
|
||||
|
||||
func checkDeployKey(e Engine, keyID, repoID int64, name string) error {
|
||||
// Note: We want error detail, not just true or false here.
|
||||
has, err := e.Where("key_id=? AND repo_id=?", keyID, repoID).Get(new(DeployKey))
|
||||
has, err := e.Where("key_id = ? AND repo_id = ?", keyID, repoID).Get(new(DeployKey))
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
return ErrDeployKeyAlreadyExist{keyID, repoID}
|
||||
}
|
||||
|
||||
has, err = e.Where("repo_id=? AND name=?", repoID, name).Get(new(DeployKey))
|
||||
has, err = e.Where("repo_id = ? AND name = ?", repoID, name).Get(new(DeployKey))
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
@@ -552,7 +646,7 @@ func addDeployKey(e *xorm.Session, keyID, repoID int64, name, fingerprint string
|
||||
|
||||
// HasDeployKey returns true if public key is a deploy key of given repository.
|
||||
func HasDeployKey(keyID, repoID int64) bool {
|
||||
has, _ := x.Where("key_id=? AND repo_id=?", keyID, repoID).Get(new(DeployKey))
|
||||
has, _ := x.Where("key_id = ? AND repo_id = ?", keyID, repoID).Get(new(DeployKey))
|
||||
return has
|
||||
}
|
||||
|
||||
@@ -646,7 +740,7 @@ func DeleteDeployKey(doer *User, id int64) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("HasAccess: %v", err)
|
||||
} else if !yes {
|
||||
return ErrKeyAccessDenied{doer.Id, key.ID, "deploy"}
|
||||
return ErrKeyAccessDenied{doer.ID, key.ID, "deploy"}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -657,15 +751,15 @@ func DeleteDeployKey(doer *User, id int64) error {
|
||||
}
|
||||
|
||||
if _, err = sess.Id(key.ID).Delete(new(DeployKey)); err != nil {
|
||||
return fmt.Errorf("delete deploy key[%d]: %v", key.ID, err)
|
||||
return fmt.Errorf("delete deploy key [%d]: %v", key.ID, err)
|
||||
}
|
||||
|
||||
// Check if this is the last reference to same key content.
|
||||
has, err := sess.Where("key_id=?", key.KeyID).Get(new(DeployKey))
|
||||
has, err := sess.Where("key_id = ?", key.KeyID).Get(new(DeployKey))
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
if err = deletePublicKey(sess, key.KeyID); err != nil {
|
||||
if err = deletePublicKeys(sess, key.KeyID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -676,5 +770,5 @@ func DeleteDeployKey(doer *User, id int64) error {
|
||||
// ListDeployKeys returns all deploy keys by given repository ID.
|
||||
func ListDeployKeys(repoID int64) ([]*DeployKey, error) {
|
||||
keys := make([]*DeployKey, 0, 5)
|
||||
return keys, x.Where("repo_id=?", repoID).Find(&keys)
|
||||
return keys, x.Where("repo_id = ?", repoID).Find(&keys)
|
||||
}
|
||||
|
||||
56
models/ssh_key_test.go
Normal file
56
models/ssh_key_test.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
func init() {
|
||||
setting.NewContext()
|
||||
}
|
||||
|
||||
func Test_SSHParsePublicKey(t *testing.T) {
|
||||
testKeys := map[string]struct {
|
||||
typeName string
|
||||
length int
|
||||
content string
|
||||
}{
|
||||
"dsa-1024": {"dsa", 1024, "ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ibZ2OkQ3S0SqDIa0HXSEJ1zaExQdmbO+Ux/wsytWZmCczWOVsaszBZSl90q8UnWlSH6P+/YA+RWJm5SFtuV9PtGIhyZgoNuz5kBQ7K139wuQsecdKktISwTakzAAAAFQCzKsO2JhNKlL+wwwLGOcLffoAmkwAAAIBpK7/3xvduajLBD/9vASqBQIHrgK2J+wiQnIb/Wzy0UsVmvfn8A+udRbBo+csM8xrSnlnlJnjkJS3qiM5g+eTwsLIV1IdKPEwmwB+VcP53Cw6lSyWyJcvhFb0N6s08NZysLzvj0N+ZC/FnhKTLzIyMtkHf/IrPCwlM+pV/M/96YgAAAIEAqQcGn9CKgzgPaguIZooTAOQdvBLMI5y0bQjOW6734XOpqQGf/Kra90wpoasLKZjSYKNPjE+FRUOrStLrxcNs4BeVKhy2PYTRnybfYVk1/dmKgH6P1YSRONsGKvTsH6c5IyCRG0ncCgYeF8tXppyd642982daopE7zQ/NPAnJfag= nocomment"},
|
||||
"rsa-1024": {"rsa", 1024, "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== nocomment\n"},
|
||||
"rsa-2048": {"rsa", 2048, "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMZXh+1OBUwSH9D45wTaxErQIN9IoC9xl7MKJkqvTvv6O5RR9YW/IK9FbfjXgXsppYGhsCZo1hFOOsXHMnfOORqu/xMDx4yPuyvKpw4LePEcg4TDipaDFuxbWOqc/BUZRZcXu41QAWfDLrInwsltWZHSeG7hjhpacl4FrVv9V1pS6Oc5Q1NxxEzTzuNLS/8diZrTm/YAQQ/+B+mzWI3zEtF4miZjjAljWd1LTBPvU23d29DcBmmFahcZ441XZsTeAwGxG/Q6j8NgNXj9WxMeWwxXV2jeAX/EBSpZrCVlCQ1yJswT6xCp8TuBnTiGWYMBNTbOZvPC4e0WI2/yZW/s5F nocomment"},
|
||||
"ecdsa-256": {"ecdsa", 256, "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFQacN3PrOll7PXmN5B/ZNVahiUIqI05nbBlZk1KXsO3d06ktAWqbNflv2vEmA38bTFTfJ2sbn2B5ksT52cDDbA= nocomment"},
|
||||
"ecdsa-384": {"ecdsa", 384, "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBINmioV+XRX1Fm9Qk2ehHXJ2tfVxW30ypUWZw670Zyq5GQfBAH6xjygRsJ5wWsHXBsGYgFUXIHvMKVAG1tpw7s6ax9oA+dJOJ7tj+vhn8joFqT+sg3LYHgZkHrfqryRasQ== nocomment"},
|
||||
// "ecdsa-521": {"ecdsa", 521, "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACGt3UG3EzRwNOI17QR84l6PgiAcvCE7v6aXPj/SC6UWKg4EL8vW9ZBcdYL9wzs4FZXh4MOV8jAzu3KRWNTwb4k2wFNUpGOt7l28MztFFEtH5BDDrtAJSPENPy8pvPLMfnPg5NhvWycqIBzNcHipem5wSJFN5PdpNOC2xMrPWKNqj+ZjQ== nocomment"},
|
||||
}
|
||||
|
||||
Convey("Parse public keys in both native and ssh-keygen", t, func() {
|
||||
for name, key := range testKeys {
|
||||
fmt.Println("\nTesting key:", name)
|
||||
|
||||
keyTypeN, lengthN, errN := SSHNativeParsePublicKey(key.content)
|
||||
So(errN, ShouldBeNil)
|
||||
So(keyTypeN, ShouldEqual, key.typeName)
|
||||
So(lengthN, ShouldEqual, key.length)
|
||||
|
||||
keyTypeK, lengthK, errK := SSHKeyGenParsePublicKey(key.content)
|
||||
if errK != nil {
|
||||
// Some server just does not support ecdsa format.
|
||||
if strings.Contains(errK.Error(), "line 1 too long:") {
|
||||
continue
|
||||
}
|
||||
So(errK, ShouldBeNil)
|
||||
}
|
||||
So(keyTypeK, ShouldEqual, key.typeName)
|
||||
So(lengthK, ShouldEqual, key.length)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -7,31 +7,58 @@ package models
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
gouuid "github.com/satori/go.uuid"
|
||||
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/uuid"
|
||||
)
|
||||
|
||||
// AccessToken represents a personal access token.
|
||||
type AccessToken struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UID int64 `xorm:"INDEX"`
|
||||
Name string
|
||||
Sha1 string `xorm:"UNIQUE VARCHAR(40)"`
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
Updated time.Time
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UID int64 `xorm:"INDEX"`
|
||||
Name string
|
||||
Sha1 string `xorm:"UNIQUE VARCHAR(40)"`
|
||||
|
||||
Created time.Time `xorm:"-"`
|
||||
CreatedUnix int64
|
||||
Updated time.Time `xorm:"-"` // Note: Updated must below Created for AfterSet.
|
||||
UpdatedUnix int64
|
||||
HasRecentActivity bool `xorm:"-"`
|
||||
HasUsed bool `xorm:"-"`
|
||||
}
|
||||
|
||||
func (t *AccessToken) BeforeInsert() {
|
||||
t.CreatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (t *AccessToken) BeforeUpdate() {
|
||||
t.UpdatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (t *AccessToken) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "created_unix":
|
||||
t.Created = time.Unix(t.CreatedUnix, 0).Local()
|
||||
case "updated_unix":
|
||||
t.Updated = time.Unix(t.UpdatedUnix, 0).Local()
|
||||
t.HasUsed = t.Updated.After(t.Created)
|
||||
t.HasRecentActivity = t.Updated.Add(7 * 24 * time.Hour).After(time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
// NewAccessToken creates new access token.
|
||||
func NewAccessToken(t *AccessToken) error {
|
||||
t.Sha1 = base.EncodeSha1(uuid.NewV4().String())
|
||||
t.Sha1 = base.EncodeSha1(gouuid.NewV4().String())
|
||||
_, err := x.Insert(t)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetAccessTokenBySHA returns access token by given sha1.
|
||||
func GetAccessTokenBySHA(sha string) (*AccessToken, error) {
|
||||
if sha == "" {
|
||||
return nil, ErrAccessTokenEmpty{}
|
||||
}
|
||||
t := &AccessToken{Sha1: sha}
|
||||
has, err := x.Get(t)
|
||||
if err != nil {
|
||||
@@ -45,16 +72,7 @@ func GetAccessTokenBySHA(sha string) (*AccessToken, error) {
|
||||
// ListAccessTokens returns a list of access tokens belongs to given user.
|
||||
func ListAccessTokens(uid int64) ([]*AccessToken, error) {
|
||||
tokens := make([]*AccessToken, 0, 5)
|
||||
err := x.Where("uid=?", uid).Desc("id").Find(&tokens)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, t := range tokens {
|
||||
t.HasUsed = t.Updated.After(t.Created)
|
||||
t.HasRecentActivity = t.Updated.Add(7 * 24 * time.Hour).After(time.Now())
|
||||
}
|
||||
return tokens, nil
|
||||
return tokens, x.Where("uid=?", uid).Desc("id").Find(&tokens)
|
||||
}
|
||||
|
||||
// UpdateAccessToken updates information of access token.
|
||||
@@ -63,8 +81,11 @@ func UpdateAccessToken(t *AccessToken) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteAccessTokenByID deletes access token by given ID.
|
||||
func DeleteAccessTokenByID(id int64) error {
|
||||
_, err := x.Id(id).Delete(new(AccessToken))
|
||||
// DeleteAccessTokenOfUserByID deletes access token by given ID.
|
||||
func DeleteAccessTokenOfUserByID(userID, id int64) error {
|
||||
_, err := x.Delete(&AccessToken{
|
||||
ID: id,
|
||||
UID: userID,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
137
models/update.go
137
models/update.go
@@ -10,9 +10,9 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/gogits/git-module"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
git "github.com/gogits/git-module"
|
||||
)
|
||||
|
||||
type UpdateTask struct {
|
||||
@@ -47,6 +47,19 @@ func DeleteUpdateTaskByUUID(uuid string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// CommitToPushCommit transforms a git.Commit to PushCommit type.
|
||||
func CommitToPushCommit(commit *git.Commit) *PushCommit {
|
||||
return &PushCommit{
|
||||
Sha1: commit.ID.String(),
|
||||
Message: commit.Message(),
|
||||
AuthorEmail: commit.Author.Email,
|
||||
AuthorName: commit.Author.Name,
|
||||
CommitterEmail: commit.Committer.Email,
|
||||
CommitterName: commit.Committer.Name,
|
||||
Timestamp: commit.Committer.When,
|
||||
}
|
||||
}
|
||||
|
||||
func ListToPushCommits(l *list.List) *PushCommits {
|
||||
commits := make([]*PushCommit, 0)
|
||||
var actEmail string
|
||||
@@ -55,104 +68,104 @@ func ListToPushCommits(l *list.List) *PushCommits {
|
||||
if actEmail == "" {
|
||||
actEmail = commit.Committer.Email
|
||||
}
|
||||
commits = append(commits,
|
||||
&PushCommit{commit.ID.String(),
|
||||
commit.Message(),
|
||||
commit.Author.Email,
|
||||
commit.Author.Name,
|
||||
})
|
||||
commits = append(commits, CommitToPushCommit(commit))
|
||||
}
|
||||
return &PushCommits{l.Len(), commits, "", nil}
|
||||
}
|
||||
|
||||
func Update(refName, oldCommitID, newCommitID, userName, repoUserName, repoName string, userID int64) error {
|
||||
isNew := strings.HasPrefix(oldCommitID, "0000000")
|
||||
if isNew &&
|
||||
strings.HasPrefix(newCommitID, "0000000") {
|
||||
return fmt.Errorf("old rev and new rev both 000000")
|
||||
type PushUpdateOptions struct {
|
||||
PusherID int64
|
||||
PusherName string
|
||||
RepoUserName string
|
||||
RepoName string
|
||||
RefFullName string
|
||||
OldCommitID string
|
||||
NewCommitID string
|
||||
}
|
||||
|
||||
// PushUpdate must be called for any push actions in order to
|
||||
// generates necessary push action history feeds.
|
||||
func PushUpdate(opts PushUpdateOptions) (err error) {
|
||||
isNewRef := opts.OldCommitID == git.EMPTY_SHA
|
||||
isDelRef := opts.NewCommitID == git.EMPTY_SHA
|
||||
if isNewRef && isDelRef {
|
||||
return fmt.Errorf("Old and new revisions are both %s", git.EMPTY_SHA)
|
||||
}
|
||||
|
||||
f := RepoPath(repoUserName, repoName)
|
||||
repoPath := RepoPath(opts.RepoUserName, opts.RepoName)
|
||||
|
||||
gitUpdate := exec.Command("git", "update-server-info")
|
||||
gitUpdate.Dir = f
|
||||
gitUpdate.Run()
|
||||
gitUpdate.Dir = repoPath
|
||||
if err = gitUpdate.Run(); err != nil {
|
||||
return fmt.Errorf("Fail to call 'git update-server-info': %v", err)
|
||||
}
|
||||
|
||||
isDel := strings.HasPrefix(newCommitID, "0000000")
|
||||
if isDel {
|
||||
log.GitLogger.Info("del rev", refName, "from", userName+"/"+repoName+".git", "by", userID)
|
||||
if isDelRef {
|
||||
log.Trace("Reference '%s' has been deleted from '%s/%s' by %s",
|
||||
opts.RefFullName, opts.RepoUserName, opts.RepoName, opts.PusherName)
|
||||
return nil
|
||||
}
|
||||
|
||||
gitRepo, err := git.OpenRepository(f)
|
||||
gitRepo, err := git.OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("runUpdate.Open repoId: %v", err)
|
||||
return fmt.Errorf("OpenRepository: %v", err)
|
||||
}
|
||||
|
||||
user, err := GetUserByName(repoUserName)
|
||||
owner, err := GetUserByName(opts.RepoUserName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("runUpdate.GetUserByName: %v", err)
|
||||
return fmt.Errorf("GetUserByName: %v", err)
|
||||
}
|
||||
|
||||
repo, err := GetRepositoryByName(user.Id, repoName)
|
||||
repo, err := GetRepositoryByName(owner.ID, opts.RepoName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("runUpdate.GetRepositoryByName userId: %v", err)
|
||||
return fmt.Errorf("GetRepositoryByName: %v", err)
|
||||
}
|
||||
|
||||
// Push tags.
|
||||
if strings.HasPrefix(refName, "refs/tags/") {
|
||||
tagName := git.RefEndName(refName)
|
||||
tag, err := gitRepo.GetTag(tagName)
|
||||
if err != nil {
|
||||
log.GitLogger.Fatal(4, "runUpdate.GetTag: %v", err)
|
||||
if strings.HasPrefix(opts.RefFullName, git.TAG_PREFIX) {
|
||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||
PusherName: opts.PusherName,
|
||||
RepoOwnerID: owner.ID,
|
||||
RepoName: repo.Name,
|
||||
RefFullName: opts.RefFullName,
|
||||
OldCommitID: opts.OldCommitID,
|
||||
NewCommitID: opts.NewCommitID,
|
||||
Commits: &PushCommits{},
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CommitRepoAction (tag): %v", err)
|
||||
}
|
||||
|
||||
var actEmail string
|
||||
if tag.Tagger != nil {
|
||||
actEmail = tag.Tagger.Email
|
||||
} else {
|
||||
cmt, err := tag.Commit()
|
||||
if err != nil {
|
||||
log.GitLogger.Fatal(4, "runUpdate.GetTag Commit: %v", err)
|
||||
}
|
||||
actEmail = cmt.Committer.Email
|
||||
}
|
||||
|
||||
commit := &PushCommits{}
|
||||
|
||||
if err = CommitRepoAction(userID, user.Id, userName, actEmail,
|
||||
repo.ID, repoUserName, repoName, refName, commit, oldCommitID, newCommitID); err != nil {
|
||||
log.GitLogger.Fatal(4, "CommitRepoAction: %s/%s:%v", repoUserName, repoName, err)
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
newCommit, err := gitRepo.GetCommit(newCommitID)
|
||||
newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("runUpdate GetCommit of newCommitId: %v", err)
|
||||
return fmt.Errorf("gitRepo.GetCommit: %v", err)
|
||||
}
|
||||
|
||||
// Push new branch.
|
||||
var l *list.List
|
||||
if isNew {
|
||||
if isNewRef {
|
||||
l, err = newCommit.CommitsBeforeLimit(10)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CommitsBefore: %v", err)
|
||||
return fmt.Errorf("newCommit.CommitsBeforeLimit: %v", err)
|
||||
}
|
||||
} else {
|
||||
l, err = newCommit.CommitsBeforeUntil(oldCommitID)
|
||||
l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CommitsBeforeUntil: %v", err)
|
||||
return fmt.Errorf("newCommit.CommitsBeforeUntil: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("runUpdate.Commit repoId: %v", err)
|
||||
}
|
||||
|
||||
if err = CommitRepoAction(userID, user.Id, userName, user.Email,
|
||||
repo.ID, repoUserName, repoName, refName, ListToPushCommits(l), oldCommitID, newCommitID); err != nil {
|
||||
return fmt.Errorf("runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err)
|
||||
if err := CommitRepoAction(CommitRepoActionOptions{
|
||||
PusherName: opts.PusherName,
|
||||
RepoOwnerID: owner.ID,
|
||||
RepoName: repo.Name,
|
||||
RefFullName: opts.RefFullName,
|
||||
OldCommitID: opts.OldCommitID,
|
||||
NewCommitID: opts.NewCommitID,
|
||||
Commits: ListToPushCommits(l),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CommitRepoAction (branch): %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
563
models/user.go
563
models/user.go
@@ -8,36 +8,39 @@ import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
_ "image/jpeg"
|
||||
"image/png"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/nfnt/resize"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
log "gopkg.in/clog.v1"
|
||||
|
||||
"github.com/gogits/git-module"
|
||||
api "github.com/gogits/go-gogs-client"
|
||||
|
||||
"github.com/gogits/gogs/modules/avatar"
|
||||
"github.com/gogits/gogs/modules/base"
|
||||
"github.com/gogits/gogs/modules/log"
|
||||
"github.com/gogits/gogs/modules/markdown"
|
||||
"github.com/gogits/gogs/modules/setting"
|
||||
)
|
||||
|
||||
type UserType int
|
||||
|
||||
const (
|
||||
INDIVIDUAL UserType = iota // Historic reason to make it starts at 0.
|
||||
ORGANIZATION
|
||||
USER_TYPE_INDIVIDUAL UserType = iota // Historic reason to make it starts at 0.
|
||||
USER_TYPE_ORGANIZATION
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -45,14 +48,13 @@ var (
|
||||
ErrEmailNotExist = errors.New("E-mail does not exist")
|
||||
ErrEmailNotActivated = errors.New("E-mail address has not been activated")
|
||||
ErrUserNameIllegal = errors.New("User name contains illegal characters")
|
||||
ErrLoginSourceNotExist = errors.New("Login source does not exist")
|
||||
ErrLoginSourceNotActived = errors.New("Login source is not actived")
|
||||
ErrUnsupportedLoginType = errors.New("Login source is unknown")
|
||||
)
|
||||
|
||||
// User represents the object of individual and member of organization.
|
||||
type User struct {
|
||||
Id int64
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
LowerName string `xorm:"UNIQUE NOT NULL"`
|
||||
Name string `xorm:"UNIQUE NOT NULL"`
|
||||
FullName string
|
||||
@@ -68,10 +70,13 @@ type User struct {
|
||||
Repos []*Repository `xorm:"-"`
|
||||
Location string
|
||||
Website string
|
||||
Rands string `xorm:"VARCHAR(10)"`
|
||||
Salt string `xorm:"VARCHAR(10)"`
|
||||
Created time.Time `xorm:"CREATED"`
|
||||
Updated time.Time `xorm:"UPDATED"`
|
||||
Rands string `xorm:"VARCHAR(10)"`
|
||||
Salt string `xorm:"VARCHAR(10)"`
|
||||
|
||||
Created time.Time `xorm:"-"`
|
||||
CreatedUnix int64
|
||||
Updated time.Time `xorm:"-"`
|
||||
UpdatedUnix int64
|
||||
|
||||
// Remember visibility choice for convenience, true for private
|
||||
LastRepoVisibility bool
|
||||
@@ -79,10 +84,11 @@ type User struct {
|
||||
MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1"`
|
||||
|
||||
// Permissions
|
||||
IsActive bool
|
||||
IsActive bool // Activate primary email
|
||||
IsAdmin bool
|
||||
AllowGitHook bool
|
||||
AllowImportLocal bool // Allow migrate repository by local path
|
||||
ProhibitLogin bool
|
||||
|
||||
// Avatar
|
||||
Avatar string `xorm:"VARCHAR(2048) NOT NULL"`
|
||||
@@ -103,18 +109,36 @@ type User struct {
|
||||
Members []*User `xorm:"-"`
|
||||
}
|
||||
|
||||
func (u *User) BeforeInsert() {
|
||||
u.CreatedUnix = time.Now().Unix()
|
||||
u.UpdatedUnix = u.CreatedUnix
|
||||
}
|
||||
|
||||
func (u *User) BeforeUpdate() {
|
||||
if u.MaxRepoCreation < -1 {
|
||||
u.MaxRepoCreation = -1
|
||||
}
|
||||
u.UpdatedUnix = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (u *User) AfterSet(colName string, _ xorm.Cell) {
|
||||
switch colName {
|
||||
case "full_name":
|
||||
u.FullName = base.Sanitizer.Sanitize(u.FullName)
|
||||
case "created":
|
||||
u.Created = regulateTimeZone(u.Created)
|
||||
u.FullName = markdown.Sanitizer.Sanitize(u.FullName)
|
||||
case "created_unix":
|
||||
u.Created = time.Unix(u.CreatedUnix, 0).Local()
|
||||
case "updated_unix":
|
||||
u.Updated = time.Unix(u.UpdatedUnix, 0).Local()
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) APIFormat() *api.User {
|
||||
return &api.User{
|
||||
ID: u.ID,
|
||||
UserName: u.Name,
|
||||
FullName: u.FullName,
|
||||
Email: u.Email,
|
||||
AvatarUrl: u.AvatarLink(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +149,7 @@ func (u *User) IsLocal() bool {
|
||||
|
||||
// HasForkedRepo checks if user has already forked a repository with given ID.
|
||||
func (u *User) HasForkedRepo(repoID int64) bool {
|
||||
_, has := HasForkedRepo(u.Id, repoID)
|
||||
_, has := HasForkedRepo(u.ID, repoID)
|
||||
return has
|
||||
}
|
||||
|
||||
@@ -146,6 +170,10 @@ func (u *User) CanCreateRepo() bool {
|
||||
return u.NumRepos < u.MaxRepoCreation
|
||||
}
|
||||
|
||||
func (u *User) CanCreateOrganization() bool {
|
||||
return !setting.Admin.DisableRegularOrgCreation || u.IsAdmin
|
||||
}
|
||||
|
||||
// CanEditGitHook returns true if user can edit Git hooks.
|
||||
func (u *User) CanEditGitHook() bool {
|
||||
return u.IsAdmin || u.AllowGitHook
|
||||
@@ -153,17 +181,7 @@ func (u *User) CanEditGitHook() bool {
|
||||
|
||||
// CanImportLocal returns true if user can migrate repository by local path.
|
||||
func (u *User) CanImportLocal() bool {
|
||||
return u.IsAdmin || u.AllowImportLocal
|
||||
}
|
||||
|
||||
// EmailAdresses is the list of all email addresses of a user. Can contain the
|
||||
// primary email address, but is not obligatory
|
||||
type EmailAddress struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UID int64 `xorm:"INDEX NOT NULL"`
|
||||
Email string `xorm:"UNIQUE NOT NULL"`
|
||||
IsActivated bool
|
||||
IsPrimary bool `xorm:"-"`
|
||||
return setting.Repository.EnableLocalPathMigration && (u.IsAdmin || u.AllowImportLocal)
|
||||
}
|
||||
|
||||
// DashboardLink returns the user dashboard page link.
|
||||
@@ -179,10 +197,14 @@ func (u *User) HomeLink() string {
|
||||
return setting.AppSubUrl + "/" + u.Name
|
||||
}
|
||||
|
||||
func (u *User) HTMLURL() string {
|
||||
return setting.AppUrl + u.Name
|
||||
}
|
||||
|
||||
// GenerateEmailActivateCode generates an activate code based on user information and given e-mail.
|
||||
func (u *User) GenerateEmailActivateCode(email string) string {
|
||||
code := base.CreateTimeLimitCode(
|
||||
com.ToStr(u.Id)+email+u.LowerName+u.Passwd+u.Rands,
|
||||
com.ToStr(u.ID)+email+u.LowerName+u.Passwd+u.Rands,
|
||||
setting.Service.ActiveCodeLives, nil)
|
||||
|
||||
// Add tail hex username
|
||||
@@ -197,7 +219,7 @@ func (u *User) GenerateActivateCode() string {
|
||||
|
||||
// CustomAvatarPath returns user custom avatar file path.
|
||||
func (u *User) CustomAvatarPath() string {
|
||||
return filepath.Join(setting.AvatarUploadPath, com.ToStr(u.Id))
|
||||
return filepath.Join(setting.AvatarUploadPath, com.ToStr(u.ID))
|
||||
}
|
||||
|
||||
// GenerateRandomAvatar generates a random avatar for user.
|
||||
@@ -211,7 +233,7 @@ func (u *User) GenerateRandomAvatar() error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("RandomImage: %v", err)
|
||||
}
|
||||
if err = os.MkdirAll(path.Dir(u.CustomAvatarPath()), os.ModePerm); err != nil {
|
||||
if err = os.MkdirAll(filepath.Dir(u.CustomAvatarPath()), os.ModePerm); err != nil {
|
||||
return fmt.Errorf("MkdirAll: %v", err)
|
||||
}
|
||||
fw, err := os.Create(u.CustomAvatarPath())
|
||||
@@ -220,17 +242,20 @@ func (u *User) GenerateRandomAvatar() error {
|
||||
}
|
||||
defer fw.Close()
|
||||
|
||||
if err = jpeg.Encode(fw, img, nil); err != nil {
|
||||
if err = png.Encode(fw, img); err != nil {
|
||||
return fmt.Errorf("Encode: %v", err)
|
||||
}
|
||||
|
||||
log.Info("New random avatar created: %d", u.Id)
|
||||
log.Info("New random avatar created: %d", u.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RelAvatarLink returns relative avatar link to the site domain,
|
||||
// which includes app sub-url as prefix. However, it is possible
|
||||
// to return full URL if user enables Gravatar-like service.
|
||||
func (u *User) RelAvatarLink() string {
|
||||
defaultImgUrl := "/img/avatar_default.jpg"
|
||||
if u.Id == -1 {
|
||||
defaultImgUrl := setting.AppSubUrl + "/img/avatar_default.png"
|
||||
if u.ID == -1 {
|
||||
return defaultImgUrl
|
||||
}
|
||||
|
||||
@@ -239,7 +264,7 @@ func (u *User) RelAvatarLink() string {
|
||||
if !com.IsExist(u.CustomAvatarPath()) {
|
||||
return defaultImgUrl
|
||||
}
|
||||
return "/avatars/" + com.ToStr(u.Id)
|
||||
return setting.AppSubUrl + "/avatars/" + com.ToStr(u.ID)
|
||||
case setting.DisableGravatar, setting.OfflineMode:
|
||||
if !com.IsExist(u.CustomAvatarPath()) {
|
||||
if err := u.GenerateRandomAvatar(); err != nil {
|
||||
@@ -247,18 +272,16 @@ func (u *User) RelAvatarLink() string {
|
||||
}
|
||||
}
|
||||
|
||||
return "/avatars/" + com.ToStr(u.Id)
|
||||
case setting.Service.EnableCacheAvatar:
|
||||
return "/avatar/" + u.Avatar
|
||||
return setting.AppSubUrl + "/avatars/" + com.ToStr(u.ID)
|
||||
}
|
||||
return setting.GravatarSource + u.Avatar
|
||||
return base.AvatarLink(u.AvatarEmail)
|
||||
}
|
||||
|
||||
// AvatarLink returns user gravatar link.
|
||||
// AvatarLink returns user avatar absolute link.
|
||||
func (u *User) AvatarLink() string {
|
||||
link := u.RelAvatarLink()
|
||||
if link[0] == '/' && link[1] != '/' {
|
||||
return setting.AppSubUrl + link
|
||||
return setting.AppUrl + strings.TrimPrefix(link, setting.AppSubUrl)[1:]
|
||||
}
|
||||
return link
|
||||
}
|
||||
@@ -266,7 +289,7 @@ func (u *User) AvatarLink() string {
|
||||
// User.GetFollwoers returns range of user's followers.
|
||||
func (u *User) GetFollowers(page int) ([]*User, error) {
|
||||
users := make([]*User, 0, ItemsPerPage)
|
||||
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("follow.follow_id=?", u.Id)
|
||||
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("follow.follow_id=?", u.ID)
|
||||
if setting.UsePostgreSQL {
|
||||
sess = sess.Join("LEFT", "follow", `"user".id=follow.user_id`)
|
||||
} else {
|
||||
@@ -276,13 +299,13 @@ func (u *User) GetFollowers(page int) ([]*User, error) {
|
||||
}
|
||||
|
||||
func (u *User) IsFollowing(followID int64) bool {
|
||||
return IsFollowing(u.Id, followID)
|
||||
return IsFollowing(u.ID, followID)
|
||||
}
|
||||
|
||||
// GetFollowing returns range of user's following.
|
||||
func (u *User) GetFollowing(page int) ([]*User, error) {
|
||||
users := make([]*User, 0, ItemsPerPage)
|
||||
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("follow.user_id=?", u.Id)
|
||||
sess := x.Limit(ItemsPerPage, (page-1)*ItemsPerPage).Where("follow.user_id=?", u.ID)
|
||||
if setting.UsePostgreSQL {
|
||||
sess = sess.Join("LEFT", "follow", `"user".id=follow.follow_id`)
|
||||
} else {
|
||||
@@ -294,7 +317,7 @@ func (u *User) GetFollowing(page int) ([]*User, error) {
|
||||
// NewGitSig generates and returns the signature of given user.
|
||||
func (u *User) NewGitSig() *git.Signature {
|
||||
return &git.Signature{
|
||||
Name: u.Name,
|
||||
Name: u.DisplayName(),
|
||||
Email: u.Email,
|
||||
When: time.Now(),
|
||||
}
|
||||
@@ -302,7 +325,7 @@ func (u *User) NewGitSig() *git.Signature {
|
||||
|
||||
// EncodePasswd encodes password to safe format.
|
||||
func (u *User) EncodePasswd() {
|
||||
newPasswd := base.PBKDF2([]byte(u.Passwd), []byte(u.Salt), 10000, 50, sha256.New)
|
||||
newPasswd := pbkdf2.Key([]byte(u.Passwd), []byte(u.Salt), 10000, 50, sha256.New)
|
||||
u.Passwd = fmt.Sprintf("%x", newPasswd)
|
||||
}
|
||||
|
||||
@@ -310,7 +333,7 @@ func (u *User) EncodePasswd() {
|
||||
func (u *User) ValidatePassword(passwd string) bool {
|
||||
newUser := &User{Passwd: passwd, Salt: u.Salt}
|
||||
newUser.EncodePasswd()
|
||||
return u.Passwd == newUser.Passwd
|
||||
return subtle.ConstantTimeCompare([]byte(u.Passwd), []byte(newUser.Passwd)) == 1
|
||||
}
|
||||
|
||||
// UploadAvatar saves custom avatar for user.
|
||||
@@ -321,7 +344,7 @@ func (u *User) UploadAvatar(data []byte) error {
|
||||
return fmt.Errorf("Decode: %v", err)
|
||||
}
|
||||
|
||||
m := resize.Resize(290, 290, img, resize.NearestNeighbor)
|
||||
m := resize.Resize(avatar.AVATAR_SIZE, avatar.AVATAR_SIZE, img, resize.NearestNeighbor)
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
@@ -348,42 +371,53 @@ func (u *User) UploadAvatar(data []byte) error {
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
// DeleteAvatar deletes the user's custom avatar.
|
||||
func (u *User) DeleteAvatar() error {
|
||||
log.Trace("DeleteAvatar[%d]: %s", u.ID, u.CustomAvatarPath())
|
||||
os.Remove(u.CustomAvatarPath())
|
||||
|
||||
u.UseCustomAvatar = false
|
||||
if err := UpdateUser(u); err != nil {
|
||||
return fmt.Errorf("UpdateUser: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsAdminOfRepo returns true if user has admin or higher access of repository.
|
||||
func (u *User) IsAdminOfRepo(repo *Repository) bool {
|
||||
if err := repo.GetOwner(); err != nil {
|
||||
log.Error(3, "GetOwner: %v", err)
|
||||
return false
|
||||
has, err := HasAccess(u, repo, ACCESS_MODE_ADMIN)
|
||||
if err != nil {
|
||||
log.Error(3, "HasAccess: %v", err)
|
||||
}
|
||||
return has
|
||||
}
|
||||
|
||||
if repo.Owner.IsOrganization() {
|
||||
has, err := HasAccess(u, repo, ACCESS_MODE_ADMIN)
|
||||
if err != nil {
|
||||
log.Error(3, "HasAccess: %v", err)
|
||||
return false
|
||||
}
|
||||
return has
|
||||
// IsWriterOfRepo returns true if user has write access to given repository.
|
||||
func (u *User) IsWriterOfRepo(repo *Repository) bool {
|
||||
has, err := HasAccess(u, repo, ACCESS_MODE_WRITE)
|
||||
if err != nil {
|
||||
log.Error(3, "HasAccess: %v", err)
|
||||
}
|
||||
|
||||
return repo.IsOwnedBy(u.Id)
|
||||
return has
|
||||
}
|
||||
|
||||
// IsOrganization returns true if user is actually a organization.
|
||||
func (u *User) IsOrganization() bool {
|
||||
return u.Type == ORGANIZATION
|
||||
return u.Type == USER_TYPE_ORGANIZATION
|
||||
}
|
||||
|
||||
// IsUserOrgOwner returns true if user is in the owner team of given organization.
|
||||
func (u *User) IsUserOrgOwner(orgId int64) bool {
|
||||
return IsOrganizationOwner(orgId, u.Id)
|
||||
return IsOrganizationOwner(orgId, u.ID)
|
||||
}
|
||||
|
||||
// IsPublicMember returns true if user public his/her membership in give organization.
|
||||
func (u *User) IsPublicMember(orgId int64) bool {
|
||||
return IsPublicMembership(orgId, u.Id)
|
||||
return IsPublicMembership(orgId, u.ID)
|
||||
}
|
||||
|
||||
func (u *User) getOrganizationCount(e Engine) (int64, error) {
|
||||
return e.Where("uid=?", u.Id).Count(new(OrgUser))
|
||||
return e.Where("uid=?", u.ID).Count(new(OrgUser))
|
||||
}
|
||||
|
||||
// GetOrganizationCount returns count of membership of organization of user.
|
||||
@@ -391,21 +425,31 @@ func (u *User) GetOrganizationCount() (int64, error) {
|
||||
return u.getOrganizationCount(x)
|
||||
}
|
||||
|
||||
// GetRepositories returns all repositories that user owns, including private repositories.
|
||||
func (u *User) GetRepositories() (err error) {
|
||||
u.Repos, err = GetRepositories(u.Id, true)
|
||||
// GetRepositories returns repositories that user owns, including private repositories.
|
||||
func (u *User) GetRepositories(page, pageSize int) (err error) {
|
||||
u.Repos, err = GetUserRepositories(&UserRepoOptions{
|
||||
UserID: u.ID,
|
||||
Private: true,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetRepositories returns mirror repositories that user owns, including private repositories.
|
||||
func (u *User) GetMirrorRepositories() ([]*Repository, error) {
|
||||
return GetUserMirrorRepositories(u.ID)
|
||||
}
|
||||
|
||||
// GetOwnedOrganizations returns all organizations that user owns.
|
||||
func (u *User) GetOwnedOrganizations() (err error) {
|
||||
u.OwnedOrgs, err = GetOwnedOrgsByUserID(u.Id)
|
||||
u.OwnedOrgs, err = GetOwnedOrgsByUserID(u.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetOrganizations returns all organizations that user belongs to.
|
||||
func (u *User) GetOrganizations(all bool) error {
|
||||
ous, err := GetOrgUsersByUserID(u.Id, all)
|
||||
ous, err := GetOrgUsersByUserID(u.ID, all)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -433,6 +477,12 @@ func (u *User) ShortName(length int) string {
|
||||
return base.EllipsisString(u.Name, length)
|
||||
}
|
||||
|
||||
// IsMailable checks if a user is elegible
|
||||
// to receive emails.
|
||||
func (u *User) IsMailable() bool {
|
||||
return u.IsActive
|
||||
}
|
||||
|
||||
// IsUserExist checks if given user name exist,
|
||||
// the user name should be noncased unique.
|
||||
// If uid is presented, then check will rule out that one,
|
||||
@@ -444,36 +494,57 @@ func IsUserExist(uid int64, name string) (bool, error) {
|
||||
return x.Where("id!=?", uid).Get(&User{LowerName: strings.ToLower(name)})
|
||||
}
|
||||
|
||||
// IsEmailUsed returns true if the e-mail has been used.
|
||||
func IsEmailUsed(email string) (bool, error) {
|
||||
if len(email) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
email = strings.ToLower(email)
|
||||
if has, err := x.Get(&EmailAddress{Email: email}); has || err != nil {
|
||||
return has, err
|
||||
}
|
||||
return x.Get(&User{Email: email})
|
||||
}
|
||||
|
||||
// GetUserSalt returns a ramdom user salt token.
|
||||
func GetUserSalt() string {
|
||||
func GetUserSalt() (string, error) {
|
||||
return base.GetRandomString(10)
|
||||
}
|
||||
|
||||
// NewFakeUser creates and returns a fake user for someone has deleted his/her account.
|
||||
func NewFakeUser() *User {
|
||||
// NewGhostUser creates and returns a fake user for someone has deleted his/her account.
|
||||
func NewGhostUser() *User {
|
||||
return &User{
|
||||
Id: -1,
|
||||
Name: "Someone",
|
||||
LowerName: "someone",
|
||||
ID: -1,
|
||||
Name: "Ghost",
|
||||
LowerName: "ghost",
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
reservedUsernames = []string{"assets", "css", "img", "js", "less", "plugins", "debug", "raw", "install", "api", "avatar", "user", "org", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin", "new", ".", ".."}
|
||||
reservedUserPatterns = []string{"*.keys"}
|
||||
)
|
||||
|
||||
// isUsableName checks if name is reserved or pattern of name is not allowed
|
||||
// based on given reserved names and patterns.
|
||||
// Names are exact match, patterns can be prefix or suffix match with placeholder '*'.
|
||||
func isUsableName(names, patterns []string, name string) error {
|
||||
name = strings.TrimSpace(strings.ToLower(name))
|
||||
if utf8.RuneCountInString(name) == 0 {
|
||||
return ErrNameEmpty
|
||||
}
|
||||
|
||||
for i := range names {
|
||||
if name == names[i] {
|
||||
return ErrNameReserved{name}
|
||||
}
|
||||
}
|
||||
|
||||
for _, pat := range patterns {
|
||||
if pat[0] == '*' && strings.HasSuffix(name, pat[1:]) ||
|
||||
(pat[len(pat)-1] == '*' && strings.HasPrefix(name, pat[:len(pat)-1])) {
|
||||
return ErrNamePatternNotAllowed{pat}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsUsableUsername(name string) error {
|
||||
return isUsableName(reservedUsernames, reservedUserPatterns, name)
|
||||
}
|
||||
|
||||
// CreateUser creates record of a new user.
|
||||
func CreateUser(u *User) (err error) {
|
||||
if err = IsUsableName(u.Name); err != nil {
|
||||
if err = IsUsableUsername(u.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -494,23 +565,25 @@ func CreateUser(u *User) (err error) {
|
||||
|
||||
u.LowerName = strings.ToLower(u.Name)
|
||||
u.AvatarEmail = u.Email
|
||||
u.Avatar = avatar.HashEmail(u.AvatarEmail)
|
||||
u.Rands = GetUserSalt()
|
||||
u.Salt = GetUserSalt()
|
||||
u.Avatar = base.HashEmail(u.AvatarEmail)
|
||||
if u.Rands, err = GetUserSalt(); err != nil {
|
||||
return err
|
||||
}
|
||||
if u.Salt, err = GetUserSalt(); err != nil {
|
||||
return err
|
||||
}
|
||||
u.EncodePasswd()
|
||||
u.MaxRepoCreation = -1
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = sess.Insert(u); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
} else if err = os.MkdirAll(UserPath(u.Name), os.ModePerm); err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -558,7 +631,7 @@ func VerifyUserActiveCode(code string) (user *User) {
|
||||
if user = getVerifyUser(code); user != nil {
|
||||
// time limit code
|
||||
prefix := code[:base.TimeLimitCodeLength]
|
||||
data := com.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands
|
||||
data := com.ToStr(user.ID) + user.Email + user.LowerName + user.Passwd + user.Rands
|
||||
|
||||
if base.VerifyTimeLimitCode(data, minutes, prefix) {
|
||||
return user
|
||||
@@ -574,7 +647,7 @@ func VerifyActiveEmailCode(code, email string) *EmailAddress {
|
||||
if user := getVerifyUser(code); user != nil {
|
||||
// time limit code
|
||||
prefix := code[:base.TimeLimitCodeLength]
|
||||
data := com.ToStr(user.Id) + email + user.LowerName + user.Passwd + user.Rands
|
||||
data := com.ToStr(user.ID) + email + user.LowerName + user.Passwd + user.Rands
|
||||
|
||||
if base.VerifyTimeLimitCode(data, minutes, prefix) {
|
||||
emailAddress := &EmailAddress{Email: email}
|
||||
@@ -588,7 +661,7 @@ func VerifyActiveEmailCode(code, email string) *EmailAddress {
|
||||
|
||||
// ChangeUserName changes all corresponding setting from old user name to new one.
|
||||
func ChangeUserName(u *User, newUserName string) (err error) {
|
||||
if err = IsUsableName(newUserName); err != nil {
|
||||
if err = IsUsableUsername(newUserName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -599,19 +672,27 @@ func ChangeUserName(u *User, newUserName string) (err error) {
|
||||
return ErrUserAlreadyExist{newUserName}
|
||||
}
|
||||
|
||||
err = ChangeUsernameInPullRequests(u.Name, newUserName)
|
||||
if err != nil {
|
||||
if err = ChangeUsernameInPullRequests(u.Name, newUserName); err != nil {
|
||||
return fmt.Errorf("ChangeUsernameInPullRequests: %v", err)
|
||||
}
|
||||
|
||||
// Delete all local copies of repository wiki that user owns.
|
||||
if err = x.Where("owner_id=?", u.ID).Iterate(new(Repository), func(idx int, bean interface{}) error {
|
||||
repo := bean.(*Repository)
|
||||
RemoveAllWithNotice("Delete repository wiki local copy", repo.LocalWikiPath())
|
||||
return nil
|
||||
}); err != nil {
|
||||
return fmt.Errorf("Delete repository wiki local copy: %v", err)
|
||||
}
|
||||
|
||||
return os.Rename(UserPath(u.Name), UserPath(newUserName))
|
||||
}
|
||||
|
||||
func updateUser(e Engine, u *User) error {
|
||||
// Organization does not need e-mail.
|
||||
// Organization does not need email
|
||||
if !u.IsOrganization() {
|
||||
u.Email = strings.ToLower(u.Email)
|
||||
has, err := e.Where("id!=?", u.Id).And("type=?", u.Type).And("email=?", u.Email).Get(new(User))
|
||||
has, err := e.Where("id!=?", u.ID).And("type=?", u.Type).And("email=?", u.Email).Get(new(User))
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
@@ -621,23 +702,16 @@ func updateUser(e Engine, u *User) error {
|
||||
if len(u.AvatarEmail) == 0 {
|
||||
u.AvatarEmail = u.Email
|
||||
}
|
||||
u.Avatar = avatar.HashEmail(u.AvatarEmail)
|
||||
u.Avatar = base.HashEmail(u.AvatarEmail)
|
||||
}
|
||||
|
||||
u.LowerName = strings.ToLower(u.Name)
|
||||
u.Location = base.TruncateString(u.Location, 255)
|
||||
u.Website = base.TruncateString(u.Website, 255)
|
||||
u.Description = base.TruncateString(u.Description, 255)
|
||||
|
||||
if len(u.Location) > 255 {
|
||||
u.Location = u.Location[:255]
|
||||
}
|
||||
if len(u.Website) > 255 {
|
||||
u.Website = u.Website[:255]
|
||||
}
|
||||
if len(u.Description) > 255 {
|
||||
u.Description = u.Description[:255]
|
||||
}
|
||||
|
||||
u.FullName = base.Sanitizer.Sanitize(u.FullName)
|
||||
_, err := e.Id(u.Id).AllCols().Update(u)
|
||||
u.FullName = markdown.Sanitizer.Sanitize(u.FullName)
|
||||
_, err := e.Id(u.ID).AllCols().Update(u)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -666,7 +740,7 @@ func deleteUser(e *xorm.Session, u *User) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetRepositoryCount: %v", err)
|
||||
} else if count > 0 {
|
||||
return ErrUserOwnRepos{UID: u.Id}
|
||||
return ErrUserOwnRepos{UID: u.ID}
|
||||
}
|
||||
|
||||
// Check membership of organization.
|
||||
@@ -674,12 +748,12 @@ func deleteUser(e *xorm.Session, u *User) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetOrganizationCount: %v", err)
|
||||
} else if count > 0 {
|
||||
return ErrUserHasOrgs{UID: u.Id}
|
||||
return ErrUserHasOrgs{UID: u.ID}
|
||||
}
|
||||
|
||||
// ***** START: Watch *****
|
||||
watches := make([]*Watch, 0, 10)
|
||||
if err = e.Find(&watches, &Watch{UserID: u.Id}); err != nil {
|
||||
if err = e.Find(&watches, &Watch{UserID: u.ID}); err != nil {
|
||||
return fmt.Errorf("get all watches: %v", err)
|
||||
}
|
||||
for i := range watches {
|
||||
@@ -691,7 +765,7 @@ func deleteUser(e *xorm.Session, u *User) error {
|
||||
|
||||
// ***** START: Star *****
|
||||
stars := make([]*Star, 0, 10)
|
||||
if err = e.Find(&stars, &Star{UID: u.Id}); err != nil {
|
||||
if err = e.Find(&stars, &Star{UID: u.ID}); err != nil {
|
||||
return fmt.Errorf("get all stars: %v", err)
|
||||
}
|
||||
for i := range stars {
|
||||
@@ -703,7 +777,7 @@ func deleteUser(e *xorm.Session, u *User) error {
|
||||
|
||||
// ***** START: Follow *****
|
||||
followers := make([]*Follow, 0, 10)
|
||||
if err = e.Find(&followers, &Follow{UserID: u.Id}); err != nil {
|
||||
if err = e.Find(&followers, &Follow{UserID: u.ID}); err != nil {
|
||||
return fmt.Errorf("get all followers: %v", err)
|
||||
}
|
||||
for i := range followers {
|
||||
@@ -714,37 +788,40 @@ func deleteUser(e *xorm.Session, u *User) error {
|
||||
// ***** END: Follow *****
|
||||
|
||||
if err = deleteBeans(e,
|
||||
&AccessToken{UID: u.Id},
|
||||
&Collaboration{UserID: u.Id},
|
||||
&Access{UserID: u.Id},
|
||||
&Watch{UserID: u.Id},
|
||||
&Star{UID: u.Id},
|
||||
&Follow{FollowID: u.Id},
|
||||
&Action{UserID: u.Id},
|
||||
&IssueUser{UID: u.Id},
|
||||
&EmailAddress{UID: u.Id},
|
||||
&AccessToken{UID: u.ID},
|
||||
&Collaboration{UserID: u.ID},
|
||||
&Access{UserID: u.ID},
|
||||
&Watch{UserID: u.ID},
|
||||
&Star{UID: u.ID},
|
||||
&Follow{FollowID: u.ID},
|
||||
&Action{UserID: u.ID},
|
||||
&IssueUser{UID: u.ID},
|
||||
&EmailAddress{UID: u.ID},
|
||||
); err != nil {
|
||||
return fmt.Errorf("deleteBeans: %v", err)
|
||||
}
|
||||
|
||||
// ***** START: PublicKey *****
|
||||
keys := make([]*PublicKey, 0, 10)
|
||||
if err = e.Find(&keys, &PublicKey{OwnerID: u.Id}); err != nil {
|
||||
if err = e.Find(&keys, &PublicKey{OwnerID: u.ID}); err != nil {
|
||||
return fmt.Errorf("get all public keys: %v", err)
|
||||
}
|
||||
for _, key := range keys {
|
||||
if err = deletePublicKey(e, key.ID); err != nil {
|
||||
return fmt.Errorf("deletePublicKey: %v", err)
|
||||
}
|
||||
|
||||
keyIDs := make([]int64, len(keys))
|
||||
for i := range keys {
|
||||
keyIDs[i] = keys[i].ID
|
||||
}
|
||||
if err = deletePublicKeys(e, keyIDs...); err != nil {
|
||||
return fmt.Errorf("deletePublicKeys: %v", err)
|
||||
}
|
||||
// ***** END: PublicKey *****
|
||||
|
||||
// Clear assignee.
|
||||
if _, err = e.Exec("UPDATE `issue` SET assignee_id=0 WHERE assignee_id=?", u.Id); err != nil {
|
||||
if _, err = e.Exec("UPDATE `issue` SET assignee_id=0 WHERE assignee_id=?", u.ID); err != nil {
|
||||
return fmt.Errorf("clear assignee: %v", err)
|
||||
}
|
||||
|
||||
if _, err = e.Id(u.Id).Delete(new(User)); err != nil {
|
||||
if _, err = e.Id(u.ID).Delete(new(User)); err != nil {
|
||||
return fmt.Errorf("Delete: %v", err)
|
||||
}
|
||||
|
||||
@@ -752,7 +829,6 @@ func deleteUser(e *xorm.Session, u *User) error {
|
||||
// Note: There are something just cannot be roll back,
|
||||
// so just keep error logs of those operations.
|
||||
|
||||
RewriteAllPublicKeys()
|
||||
os.RemoveAll(UserPath(u.Name))
|
||||
os.Remove(u.CustomAvatarPath())
|
||||
|
||||
@@ -773,15 +849,20 @@ func DeleteUser(u *User) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
if err = sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return RewriteAllPublicKeys()
|
||||
}
|
||||
|
||||
// DeleteInactivateUsers deletes all inactivate users and email addresses.
|
||||
func DeleteInactivateUsers() (err error) {
|
||||
users := make([]*User, 0, 10)
|
||||
if err = x.Where("is_active=?", false).Find(&users); err != nil {
|
||||
if err = x.Where("is_active = ?", false).Find(&users); err != nil {
|
||||
return fmt.Errorf("get all inactive users: %v", err)
|
||||
}
|
||||
// FIXME: should only update authorized_keys file once after all deletions.
|
||||
for _, u := range users {
|
||||
if err = DeleteUser(u); err != nil {
|
||||
// Ignore users that were set inactive by admin.
|
||||
@@ -792,7 +873,7 @@ func DeleteInactivateUsers() (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
_, err = x.Where("is_activated=?", false).Delete(new(EmailAddress))
|
||||
_, err = x.Where("is_activated = ?", false).Delete(new(EmailAddress))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -830,7 +911,7 @@ func GetUserByID(id int64) (*User, error) {
|
||||
|
||||
// GetAssigneeByID returns the user with write access of repository by given ID.
|
||||
func GetAssigneeByID(repo *Repository, userID int64) (*User, error) {
|
||||
has, err := HasAccess(&User{Id: userID}, repo, ACCESS_MODE_WRITE)
|
||||
has, err := HasAccess(&User{ID: userID}, repo, ACCESS_MODE_WRITE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
@@ -862,165 +943,26 @@ func GetUserEmailsByNames(names []string) []string {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
mails = append(mails, u.Email)
|
||||
if u.IsMailable() {
|
||||
mails = append(mails, u.Email)
|
||||
}
|
||||
}
|
||||
return mails
|
||||
}
|
||||
|
||||
// GetUserIdsByNames returns a slice of ids corresponds to names.
|
||||
func GetUserIdsByNames(names []string) []int64 {
|
||||
// GetUserIDsByNames returns a slice of ids corresponds to names.
|
||||
func GetUserIDsByNames(names []string) []int64 {
|
||||
ids := make([]int64, 0, len(names))
|
||||
for _, name := range names {
|
||||
u, err := GetUserByName(name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ids = append(ids, u.Id)
|
||||
ids = append(ids, u.ID)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
// GetEmailAddresses returns all e-mail addresses belongs to given user.
|
||||
func GetEmailAddresses(uid int64) ([]*EmailAddress, error) {
|
||||
emails := make([]*EmailAddress, 0, 5)
|
||||
err := x.Where("uid=?", uid).Find(&emails)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u, err := GetUserByID(uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isPrimaryFound := false
|
||||
for _, email := range emails {
|
||||
if email.Email == u.Email {
|
||||
isPrimaryFound = true
|
||||
email.IsPrimary = true
|
||||
} else {
|
||||
email.IsPrimary = false
|
||||
}
|
||||
}
|
||||
|
||||
// We alway want the primary email address displayed, even if it's not in
|
||||
// the emailaddress table (yet)
|
||||
if !isPrimaryFound {
|
||||
emails = append(emails, &EmailAddress{
|
||||
Email: u.Email,
|
||||
IsActivated: true,
|
||||
IsPrimary: true,
|
||||
})
|
||||
}
|
||||
return emails, nil
|
||||
}
|
||||
|
||||
func AddEmailAddress(email *EmailAddress) error {
|
||||
email.Email = strings.ToLower(strings.TrimSpace(email.Email))
|
||||
used, err := IsEmailUsed(email.Email)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if used {
|
||||
return ErrEmailAlreadyUsed{email.Email}
|
||||
}
|
||||
|
||||
_, err = x.Insert(email)
|
||||
return err
|
||||
}
|
||||
|
||||
func AddEmailAddresses(emails []*EmailAddress) error {
|
||||
if len(emails) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if any of them has been used
|
||||
for i := range emails {
|
||||
emails[i].Email = strings.ToLower(strings.TrimSpace(emails[i].Email))
|
||||
used, err := IsEmailUsed(emails[i].Email)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if used {
|
||||
return ErrEmailAlreadyUsed{emails[i].Email}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := x.Insert(emails); err != nil {
|
||||
return fmt.Errorf("Insert: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (email *EmailAddress) Activate() error {
|
||||
email.IsActivated = true
|
||||
if _, err := x.Id(email.ID).AllCols().Update(email); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user, err := GetUserByID(email.UID); err != nil {
|
||||
return err
|
||||
} else {
|
||||
user.Rands = GetUserSalt()
|
||||
return UpdateUser(user)
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteEmailAddress(email *EmailAddress) (err error) {
|
||||
if email.ID > 0 {
|
||||
_, err = x.Id(email.ID).Delete(new(EmailAddress))
|
||||
} else {
|
||||
_, err = x.Where("email=?", email.Email).Delete(new(EmailAddress))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteEmailAddresses(emails []*EmailAddress) (err error) {
|
||||
for i := range emails {
|
||||
if err = DeleteEmailAddress(emails[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func MakeEmailPrimary(email *EmailAddress) error {
|
||||
has, err := x.Get(email)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return ErrEmailNotExist
|
||||
}
|
||||
|
||||
if !email.IsActivated {
|
||||
return ErrEmailNotActivated
|
||||
}
|
||||
|
||||
user := &User{Id: email.UID}
|
||||
has, err = x.Get(user)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return ErrUserNotExist{email.UID, ""}
|
||||
}
|
||||
|
||||
// Make sure the former primary email doesn't disappear
|
||||
former_primary_email := &EmailAddress{Email: user.Email}
|
||||
has, err = x.Get(former_primary_email)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
former_primary_email.UID = user.Id
|
||||
former_primary_email.IsActivated = user.IsActive
|
||||
x.Insert(former_primary_email)
|
||||
}
|
||||
|
||||
user.Email = email.Email
|
||||
_, err = x.Id(user.Id).AllCols().Update(user)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// UserCommit represents a commit with validation of user.
|
||||
type UserCommit struct {
|
||||
User *User
|
||||
@@ -1093,16 +1035,47 @@ func GetUserByEmail(email string) (*User, error) {
|
||||
return nil, ErrUserNotExist{0, email}
|
||||
}
|
||||
|
||||
// SearchUserByName returns given number of users whose name contains keyword.
|
||||
func SearchUserByName(opt SearchOption) (us []*User, err error) {
|
||||
if len(opt.Keyword) == 0 {
|
||||
return us, nil
|
||||
}
|
||||
opt.Keyword = strings.ToLower(opt.Keyword)
|
||||
type SearchUserOptions struct {
|
||||
Keyword string
|
||||
Type UserType
|
||||
OrderBy string
|
||||
Page int
|
||||
PageSize int // Can be smaller than or equal to setting.UI.ExplorePagingNum
|
||||
}
|
||||
|
||||
us = make([]*User, 0, opt.Limit)
|
||||
err = x.Limit(opt.Limit).Where("type=0").And("lower_name like ?", "%"+opt.Keyword+"%").Find(&us)
|
||||
return us, err
|
||||
// SearchUserByName takes keyword and part of user name to search,
|
||||
// it returns results in given range and number of total results.
|
||||
func SearchUserByName(opts *SearchUserOptions) (users []*User, _ int64, _ error) {
|
||||
if len(opts.Keyword) == 0 {
|
||||
return users, 0, nil
|
||||
}
|
||||
opts.Keyword = strings.ToLower(opts.Keyword)
|
||||
|
||||
if opts.PageSize <= 0 || opts.PageSize > setting.UI.ExplorePagingNum {
|
||||
opts.PageSize = setting.UI.ExplorePagingNum
|
||||
}
|
||||
if opts.Page <= 0 {
|
||||
opts.Page = 1
|
||||
}
|
||||
|
||||
searchQuery := "%" + opts.Keyword + "%"
|
||||
users = make([]*User, 0, opts.PageSize)
|
||||
// Append conditions
|
||||
sess := x.Where("LOWER(lower_name) LIKE ?", searchQuery).
|
||||
Or("LOWER(full_name) LIKE ?", searchQuery).
|
||||
And("type = ?", opts.Type)
|
||||
|
||||
var countSess xorm.Session
|
||||
countSess = *sess
|
||||
count, err := countSess.Count(new(User))
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("Count: %v", err)
|
||||
}
|
||||
|
||||
if len(opts.OrderBy) > 0 {
|
||||
sess.OrderBy(opts.OrderBy)
|
||||
}
|
||||
return users, count, sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&users)
|
||||
}
|
||||
|
||||
// ___________ .__ .__
|
||||
|
||||
208
models/user_mail.go
Normal file
208
models/user_mail.go
Normal file
@@ -0,0 +1,208 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// EmailAdresses is the list of all email addresses of a user. Can contain the
|
||||
// primary email address, but is not obligatory.
|
||||
type EmailAddress struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UID int64 `xorm:"INDEX NOT NULL"`
|
||||
Email string `xorm:"UNIQUE NOT NULL"`
|
||||
IsActivated bool
|
||||
IsPrimary bool `xorm:"-"`
|
||||
}
|
||||
|
||||
// GetEmailAddresses returns all email addresses belongs to given user.
|
||||
func GetEmailAddresses(uid int64) ([]*EmailAddress, error) {
|
||||
emails := make([]*EmailAddress, 0, 5)
|
||||
if err := x.Where("uid=?", uid).Find(&emails); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u, err := GetUserByID(uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isPrimaryFound := false
|
||||
for _, email := range emails {
|
||||
if email.Email == u.Email {
|
||||
isPrimaryFound = true
|
||||
email.IsPrimary = true
|
||||
} else {
|
||||
email.IsPrimary = false
|
||||
}
|
||||
}
|
||||
|
||||
// We alway want the primary email address displayed, even if it's not in
|
||||
// the emailaddress table (yet).
|
||||
if !isPrimaryFound {
|
||||
emails = append(emails, &EmailAddress{
|
||||
Email: u.Email,
|
||||
IsActivated: true,
|
||||
IsPrimary: true,
|
||||
})
|
||||
}
|
||||
return emails, nil
|
||||
}
|
||||
|
||||
func isEmailUsed(e Engine, email string) (bool, error) {
|
||||
if len(email) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
has, err := e.Get(&EmailAddress{Email: email})
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if has {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// We need to check primary email of users as well.
|
||||
return e.Where("type=?", USER_TYPE_INDIVIDUAL).And("email=?", email).Get(new(User))
|
||||
}
|
||||
|
||||
// IsEmailUsed returns true if the email has been used.
|
||||
func IsEmailUsed(email string) (bool, error) {
|
||||
return isEmailUsed(x, email)
|
||||
}
|
||||
|
||||
func addEmailAddress(e Engine, email *EmailAddress) error {
|
||||
email.Email = strings.ToLower(strings.TrimSpace(email.Email))
|
||||
used, err := isEmailUsed(e, email.Email)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if used {
|
||||
return ErrEmailAlreadyUsed{email.Email}
|
||||
}
|
||||
|
||||
_, err = e.Insert(email)
|
||||
return err
|
||||
}
|
||||
|
||||
func AddEmailAddress(email *EmailAddress) error {
|
||||
return addEmailAddress(x, email)
|
||||
}
|
||||
|
||||
func AddEmailAddresses(emails []*EmailAddress) error {
|
||||
if len(emails) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if any of them has been used
|
||||
for i := range emails {
|
||||
emails[i].Email = strings.ToLower(strings.TrimSpace(emails[i].Email))
|
||||
used, err := IsEmailUsed(emails[i].Email)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if used {
|
||||
return ErrEmailAlreadyUsed{emails[i].Email}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := x.Insert(emails); err != nil {
|
||||
return fmt.Errorf("Insert: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (email *EmailAddress) Activate() error {
|
||||
user, err := GetUserByID(email.UID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if user.Rands, err = GetUserSalt(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
email.IsActivated = true
|
||||
if _, err := sess.Id(email.ID).AllCols().Update(email); err != nil {
|
||||
return err
|
||||
} else if err = updateUser(sess, user); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func DeleteEmailAddress(email *EmailAddress) (err error) {
|
||||
if email.ID > 0 {
|
||||
_, err = x.Id(email.ID).Delete(new(EmailAddress))
|
||||
} else {
|
||||
_, err = x.Where("email=?", email.Email).Delete(new(EmailAddress))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteEmailAddresses(emails []*EmailAddress) (err error) {
|
||||
for i := range emails {
|
||||
if err = DeleteEmailAddress(emails[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func MakeEmailPrimary(email *EmailAddress) error {
|
||||
has, err := x.Get(email)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return ErrEmailNotExist
|
||||
}
|
||||
|
||||
if !email.IsActivated {
|
||||
return ErrEmailNotActivated
|
||||
}
|
||||
|
||||
user := &User{ID: email.UID}
|
||||
has, err = x.Get(user)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return ErrUserNotExist{email.UID, ""}
|
||||
}
|
||||
|
||||
// Make sure the former primary email doesn't disappear.
|
||||
formerPrimaryEmail := &EmailAddress{Email: user.Email}
|
||||
has, err = x.Get(formerPrimaryEmail)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sessionRelease(sess)
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !has {
|
||||
formerPrimaryEmail.UID = user.ID
|
||||
formerPrimaryEmail.IsActivated = user.IsActive
|
||||
if _, err = sess.Insert(formerPrimaryEmail); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
user.Email = email.Email
|
||||
if _, err = sess.Id(user.ID).AllCols().Update(user); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sess.Commit()
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user