mirror of
https://framagit.org/fiat-tux/hat-softwares/lutim.git
synced 2026-03-28 17:42:54 +01:00
Compare commits
576 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95c5620178 | ||
|
|
117d619056 | ||
|
|
717b0562c2 | ||
|
|
64492955ff | ||
|
|
54b461617e | ||
|
|
46fcc036e6 | ||
|
|
e14cdd2ca1 | ||
|
|
bd72394835 | ||
|
|
476dcfc4ff | ||
|
|
98326f53b2 | ||
|
|
202c529983 | ||
|
|
e0fc6cfe9f | ||
|
|
3c8d69f746 | ||
|
|
ddbf43d11a | ||
|
|
c72ba813c2 | ||
|
|
9f5bb58eb5 | ||
|
|
f42ab6c532 | ||
|
|
a09c2af956 | ||
|
|
b5c6acaa70 | ||
|
|
037afeaade | ||
|
|
9364601ee9 | ||
|
|
3e73d58325 | ||
|
|
1e93cf9cc5 | ||
|
|
81e3a00f14 | ||
|
|
ac76032745 | ||
|
|
c62bf20a92 | ||
|
|
b862249c0c | ||
|
|
aaa0ae4858 | ||
|
|
8d20b3d934 | ||
|
|
9563e3a701 | ||
|
|
8f5b1df337 | ||
|
|
e77943b1cc | ||
|
|
0617683c63 | ||
|
|
81701b6f60 | ||
|
|
82f57fc72d | ||
|
|
4de01f5f8b | ||
|
|
7e15ca9174 | ||
|
|
5c0504d5d4 | ||
|
|
0080e26333 | ||
|
|
f5867f4fd6 | ||
|
|
03f63e4ec6 | ||
|
|
2a466f0140 | ||
|
|
613ba30752 | ||
|
|
125a7103a0 | ||
|
|
9477e1add1 | ||
|
|
03dbeecac0 | ||
|
|
a76b240ed8 | ||
|
|
5d6f715396 | ||
|
|
5bf1a9d717 | ||
|
|
fff56a89bb | ||
|
|
2cb07f8565 | ||
|
|
ebf9f66e47 | ||
|
|
ed70fbac2e | ||
|
|
af11a1e7ec | ||
|
|
438ce5050e | ||
|
|
d67d66d0fd | ||
|
|
cc03ba6d3f | ||
|
|
5b4f56b9f6 | ||
|
|
8b6766f498 | ||
|
|
68518dd85c | ||
|
|
e7d0821cff | ||
|
|
121c00167c | ||
|
|
c3c3c7a780 | ||
|
|
47a67a428d | ||
|
|
a9b514a30c | ||
|
|
680b3d7057 | ||
|
|
18888c05b3 | ||
|
|
d0e472fe95 | ||
|
|
10f1bd58c5 | ||
|
|
34be40d928 | ||
|
|
e7d87dac3c | ||
|
|
2bff3fcc51 | ||
|
|
c0da308963 | ||
|
|
8515a2b2c7 | ||
|
|
3dc46ebad5 | ||
|
|
c5a69e6128 | ||
|
|
45b8e6c3e2 | ||
|
|
8c40ae36bc | ||
|
|
6b5e52fba3 | ||
|
|
5db403f040 | ||
|
|
3a0f39761c | ||
|
|
691e0c3592 | ||
|
|
8e4f6c7b22 | ||
|
|
21b592cb51 | ||
|
|
c15f6dea68 | ||
|
|
c9a5cfdcd4 | ||
|
|
e4d78e5433 | ||
|
|
bb6b0c30a8 | ||
|
|
a46aab6ffa | ||
|
|
f610608aa1 | ||
|
|
52b436657f | ||
|
|
4486b32da5 | ||
|
|
17c862ae19 | ||
|
|
124ca306b4 | ||
|
|
7d845b9e64 | ||
|
|
3dd1fdca56 | ||
|
|
87efb615bb | ||
|
|
e17a51d8d5 | ||
|
|
c40d22427a | ||
|
|
d10ecc41fb | ||
|
|
4384bd1b9f | ||
|
|
614a561e00 | ||
|
|
09887ed01a | ||
|
|
f68353fa1b | ||
|
|
6aa0c43b55 | ||
|
|
cf3f0e0250 | ||
|
|
31df50d6c9 | ||
|
|
ab6febf2ea | ||
|
|
a6b0a9961a | ||
|
|
d939ec30c7 | ||
|
|
62519948a6 | ||
|
|
6ee0dc022c | ||
|
|
10daa1cd02 | ||
|
|
f4b5a780f6 | ||
|
|
1142006456 | ||
|
|
9d265879a5 | ||
|
|
d296fdcd2c | ||
|
|
33b81c9e40 | ||
|
|
d11a70c7f6 | ||
|
|
456b8cfc67 | ||
|
|
34a9e7b1e8 | ||
|
|
fe286c947b | ||
|
|
1bf33e5d3b | ||
|
|
72a5e2a543 | ||
|
|
2ee0d9d9d1 | ||
|
|
90fd6ee30f | ||
|
|
acc4c5849b | ||
|
|
7a14ad11e8 | ||
|
|
17f0beb7cf | ||
|
|
c12977b0c0 | ||
|
|
e5740c820f | ||
|
|
045f4ae5ea | ||
|
|
521245ce87 | ||
|
|
226cb56b6f | ||
|
|
63d7e1cbe8 | ||
|
|
5a79b4ced2 | ||
|
|
0aafa19cf7 | ||
|
|
b078707ddf | ||
|
|
f0254a9247 | ||
|
|
acf91e799f | ||
|
|
56dea43802 | ||
|
|
08d301688f | ||
|
|
79835941a5 | ||
|
|
1efae05eda | ||
|
|
3a3e42b352 | ||
|
|
dbfaab5f7e | ||
|
|
b7edbc73cd | ||
|
|
a416a8ad07 | ||
|
|
2071a84ff1 | ||
|
|
4ed87a082f | ||
|
|
fcba4f25b5 | ||
|
|
eacce37d88 | ||
|
|
e075543a0b | ||
|
|
4e9169d5f1 | ||
|
|
8d7f3d18e1 | ||
|
|
e1f62f9d46 | ||
|
|
366bd28e5d | ||
|
|
7884280a46 | ||
|
|
678eb86b27 | ||
|
|
891c0fc0c2 | ||
|
|
8701c1a99e | ||
|
|
12901f00d2 | ||
|
|
0973ca335d | ||
|
|
8c2996fb31 | ||
|
|
0ddf81573c | ||
|
|
442c2c35ed | ||
|
|
1d9c1d9d95 | ||
|
|
b701575e03 | ||
|
|
8bc6157325 | ||
|
|
ef8bdb6c83 | ||
|
|
e0feb50dec | ||
|
|
787193a1f3 | ||
|
|
453b476b6a | ||
|
|
420c726258 | ||
|
|
0f31fe9bd1 | ||
|
|
2fa9da7fa7 | ||
|
|
6ba40384bd | ||
|
|
d5d2720719 | ||
|
|
0627a3708d | ||
|
|
ce2af1df23 | ||
|
|
1ebe905e3b | ||
|
|
eed9bccd45 | ||
|
|
87e0e4e28e | ||
|
|
dc3735b31b | ||
|
|
56e0849533 | ||
|
|
a262ed325d | ||
|
|
56ffd65fe8 | ||
|
|
b65f37293b | ||
|
|
4a37e722fe | ||
|
|
55dec02d71 | ||
|
|
78da85115c | ||
|
|
d709a607af | ||
|
|
dee0101496 | ||
|
|
6cdcd9a88d | ||
|
|
8994ceb405 | ||
|
|
ffcb48ba84 | ||
|
|
285e3a0811 | ||
|
|
998db0cb90 | ||
|
|
0cfd5b2533 | ||
|
|
a03e3549a8 | ||
|
|
b8d2defe61 | ||
|
|
c030e371ae | ||
|
|
85d0aabef8 | ||
|
|
da8a0642ec | ||
|
|
9696da7c01 | ||
|
|
4585b7af8f | ||
|
|
3feeeac39d | ||
|
|
f7b81e701f | ||
|
|
8c7a2a75a1 | ||
|
|
e1533a8804 | ||
|
|
f8b28525f6 | ||
|
|
182f4ccf4e | ||
|
|
07344aa0c9 | ||
|
|
b8d74beea3 | ||
|
|
985a781a5f | ||
|
|
ee936c1f79 | ||
|
|
9ea2234f7d | ||
|
|
7c199a5c87 | ||
|
|
4d5fed4d02 | ||
|
|
b088db9c5e | ||
|
|
ea358d4a4c | ||
|
|
47b38daad5 | ||
|
|
46dcb0256f | ||
|
|
8fe738adc4 | ||
|
|
9b90119061 | ||
|
|
78bb3f37c7 | ||
|
|
f2f785e6d4 | ||
|
|
15e587464d | ||
|
|
4ef5e67f4b | ||
|
|
ef090954c3 | ||
|
|
b42bf08363 | ||
|
|
b5ea181bea | ||
|
|
b84420e6dc | ||
|
|
701a78ac94 | ||
|
|
0d1ca8aa98 | ||
|
|
fbe3c3bc13 | ||
|
|
a7f2fd051d | ||
|
|
c18e6c84af | ||
|
|
10eafcfdc3 | ||
|
|
033ff3c54f | ||
|
|
9645c8a2f3 | ||
|
|
4193f03d7a | ||
|
|
8a92ef9567 | ||
|
|
ed47431415 | ||
|
|
247cb41cc5 | ||
|
|
acf0ec75e7 | ||
|
|
83a8fbeeeb | ||
|
|
707e434d2c | ||
|
|
8a412a33c3 | ||
|
|
3eb8db123e | ||
|
|
dd9dc7bd7d | ||
|
|
c91d46bc68 | ||
|
|
8dd2ab87f9 | ||
|
|
b3ec85daf3 | ||
|
|
dc2b17c7af | ||
|
|
4c53669caa | ||
|
|
b5e73e09bf | ||
|
|
e62cb50ab1 | ||
|
|
cf9504bb30 | ||
|
|
36a007d2f7 | ||
|
|
f013359314 | ||
|
|
1fda560056 | ||
|
|
adc2d00552 | ||
|
|
95f4c372e3 | ||
|
|
4f39a86b0a | ||
|
|
09f1492486 | ||
|
|
163a1e2a66 | ||
|
|
f26e7ba5ff | ||
|
|
7d29e3d3dd | ||
|
|
a7bb73b158 | ||
|
|
dc371daf15 | ||
|
|
bb097294e2 | ||
|
|
cfab86c4b4 | ||
|
|
fb562dd9b8 | ||
|
|
0e63bf766e | ||
|
|
8ab966f83c | ||
|
|
86b3652471 | ||
|
|
c29acb8944 | ||
|
|
564a0ef69d | ||
|
|
781d1bc721 | ||
|
|
7cf9f8b6ec | ||
|
|
1405b078c0 | ||
|
|
95db024ab8 | ||
|
|
fe07945af7 | ||
|
|
a59b728aa6 | ||
|
|
f27cf293dd | ||
|
|
85c025f2c3 | ||
|
|
0295ba3666 | ||
|
|
485436aa67 | ||
|
|
0e0e6da677 | ||
|
|
781e96c073 | ||
|
|
fb610e1a80 | ||
|
|
13b7ece5e2 | ||
|
|
7446edba48 | ||
|
|
35eb60a27f | ||
|
|
0b1f21be31 | ||
|
|
b1e1e88af4 | ||
|
|
68d3ddb61a | ||
|
|
b6ac7d2818 | ||
|
|
da36aad520 | ||
|
|
d00f151a2e | ||
|
|
9c79c9a392 | ||
|
|
eb575519ed | ||
|
|
125bda1687 | ||
|
|
b5836d88bb | ||
|
|
8daef83846 | ||
|
|
883004b7b6 | ||
|
|
5a5e629e29 | ||
|
|
6daabaf054 | ||
|
|
f94d72f978 | ||
|
|
41abd109b6 | ||
|
|
91d2be631b | ||
|
|
56b87bb7c7 | ||
|
|
ab48e8976f | ||
|
|
fa5c3e465b | ||
|
|
474680e600 | ||
|
|
b19a3f6005 | ||
|
|
8e7065e714 | ||
|
|
2529234df9 | ||
|
|
5ad1caea68 | ||
|
|
ef1297be7d | ||
|
|
d6b59c45a6 | ||
|
|
59000d53d6 | ||
|
|
4e1d36ce60 | ||
|
|
bcb0710fde | ||
|
|
799124a58d | ||
|
|
c9a6fd2be4 | ||
|
|
9958a9cca9 | ||
|
|
6b012b7382 | ||
|
|
6921496a19 | ||
|
|
6ab6b76c8f | ||
|
|
8b9791b6b4 | ||
|
|
6f43566664 | ||
|
|
e7e02931ac | ||
|
|
210a2a8df2 | ||
|
|
41e5f292eb | ||
|
|
72f0469674 | ||
|
|
a842311304 | ||
|
|
ce84d403df | ||
|
|
ec6b9ce028 | ||
|
|
321b8bbf97 | ||
|
|
78ce5dc69d | ||
|
|
82289ece59 | ||
|
|
32ca358886 | ||
|
|
c5dac2d5e9 | ||
|
|
88b77f91fb | ||
|
|
75b645e6d4 | ||
|
|
9cfb694779 | ||
|
|
37b6f82f32 | ||
|
|
65403d934c | ||
|
|
36bae6e042 | ||
|
|
2a0f2ef4a2 | ||
|
|
54374765e7 | ||
|
|
0cb82fee0b | ||
|
|
b7e799353f | ||
|
|
c5831168af | ||
|
|
8a8e331de9 | ||
|
|
790da8deeb | ||
|
|
c7b15dc952 | ||
|
|
4f8a27a10b | ||
|
|
6014ea4889 | ||
|
|
e389869414 | ||
|
|
c9dda1c720 | ||
|
|
3b1a6af092 | ||
|
|
b7d4ea0a23 | ||
|
|
05dcdc2729 | ||
|
|
b8212e4920 | ||
|
|
3faee2402c | ||
|
|
ef1463f8c9 | ||
|
|
028961113c | ||
|
|
63a7ad74cd | ||
|
|
3f21ddb35a | ||
|
|
a8d38f6ea8 | ||
|
|
efb71654d6 | ||
|
|
8421efc3da | ||
|
|
e0f8ddec64 | ||
|
|
1f03678348 | ||
|
|
9aed9a0f03 | ||
|
|
c2110dc171 | ||
|
|
381f4e934e | ||
|
|
b710c3250b | ||
|
|
6738c49730 | ||
|
|
9318058f2b | ||
|
|
d2cd4b8335 | ||
|
|
b6d7860472 | ||
|
|
9a4a5a5799 | ||
|
|
1a8d2ea171 | ||
|
|
179def2d3e | ||
|
|
0de43b74db | ||
|
|
5dffc402c9 | ||
|
|
b6cfec60e2 | ||
|
|
efec636f2e | ||
|
|
eb6dc4235a | ||
|
|
d1b6c00a55 | ||
|
|
7af0ee5df4 | ||
|
|
1b9dad0f3f | ||
|
|
4c0df8f8b1 | ||
|
|
4629d2ae2d | ||
|
|
f4f25aab4f | ||
|
|
c6ee5408da | ||
|
|
6cc48bb991 | ||
|
|
91174081b5 | ||
|
|
a69e0df68d | ||
|
|
545059cbe5 | ||
|
|
a848d7aef8 | ||
|
|
cc3281fe2f | ||
|
|
48cc398efb | ||
|
|
4d2721f533 | ||
|
|
2923180f0c | ||
|
|
4130a77aa0 | ||
|
|
dd4ca47ac0 | ||
|
|
b63eebbefa | ||
|
|
e767f850bf | ||
|
|
374b99f77d | ||
|
|
c58edbae83 | ||
|
|
c03082c2c1 | ||
|
|
9f872a3f6a | ||
|
|
fd3d9a6e31 | ||
|
|
fbcc133e47 | ||
|
|
e8eb804d3c | ||
|
|
453bc5d2c5 | ||
|
|
c450b27c0d | ||
|
|
1bd82463d1 | ||
|
|
6a4b0910d4 | ||
|
|
ef6509a3bc | ||
|
|
0a318dc4d4 | ||
|
|
58901989dd | ||
|
|
08a07cd93d | ||
|
|
a7884e8785 | ||
|
|
41fef61f7c | ||
|
|
a1ce8618de | ||
|
|
b60a849752 | ||
|
|
b33c7d3d23 | ||
|
|
99e5ea8c75 | ||
|
|
dced03c964 | ||
|
|
fe06675d0b | ||
|
|
1082f42682 | ||
|
|
1dff747b64 | ||
|
|
c33840f3aa | ||
|
|
a9669b5f7b | ||
|
|
86ee5287bf | ||
|
|
e51e8f3d3a | ||
|
|
688f4517c1 | ||
|
|
1cb5aab686 | ||
|
|
0c23c9ba67 | ||
|
|
204df6bf08 | ||
|
|
2f96407f5c | ||
|
|
14943906d3 | ||
|
|
1aa6e9b859 | ||
|
|
a64808f206 | ||
|
|
af9323365c | ||
|
|
7a7eb8f4cc | ||
|
|
170546df64 | ||
|
|
1ad1929a0e | ||
|
|
8b76684fcd | ||
|
|
71576ce749 | ||
|
|
e0b05ff0d1 | ||
|
|
f121a9f807 | ||
|
|
d98f95811f | ||
|
|
7472650b59 | ||
|
|
a1fc2997c7 | ||
|
|
bb9fae43e2 | ||
|
|
bb5c7050bb | ||
|
|
6fab43b15b | ||
|
|
2f7b2f5fbb | ||
|
|
2ca4ba1f0e | ||
|
|
62e82b8224 | ||
|
|
7b9ed1fa4a | ||
|
|
4aa9a649be | ||
|
|
6d260af932 | ||
|
|
5bcd010055 | ||
|
|
0f7d8e745e | ||
|
|
1f0aa046d4 | ||
|
|
b6c3af7bc6 | ||
|
|
c86faf208d | ||
|
|
4fe657d0d7 | ||
|
|
023952648e | ||
|
|
baaf4ab861 | ||
|
|
ae450a9649 | ||
|
|
b4d06a21a0 | ||
|
|
b0a19e3e42 | ||
|
|
e1840e96b0 | ||
|
|
e7138a40ab | ||
|
|
080542fd95 | ||
|
|
774f3ebda9 | ||
|
|
2e8f0eda2a | ||
|
|
a9ea0d35e6 | ||
|
|
a049163646 | ||
|
|
2e5a3c9c0b | ||
|
|
db50470ef8 | ||
|
|
1cb3ec09e3 | ||
|
|
fa47a97be7 | ||
|
|
afb377faca | ||
|
|
339cdf39b2 | ||
|
|
f1c096fd68 | ||
|
|
fc28efa116 | ||
|
|
152b677614 | ||
|
|
9431b60f9b | ||
|
|
9e27e0df59 | ||
|
|
0544a1b346 | ||
|
|
bb1b43e215 | ||
|
|
ad04bdd571 | ||
|
|
23c130750e | ||
|
|
a0b507e48e | ||
|
|
9692504096 | ||
|
|
8386a2f4d2 | ||
|
|
bef9dfaca3 | ||
|
|
b71c25bf54 | ||
|
|
b0e5f771f2 | ||
|
|
2b361fce26 | ||
|
|
64e0db48f2 | ||
|
|
241e8b8ea1 | ||
|
|
e0f13489a4 | ||
|
|
1ca3137dd1 | ||
|
|
b791c609c2 | ||
|
|
388feba7c0 | ||
|
|
542d3efdf5 | ||
|
|
a57815a2ea | ||
|
|
44c0d5a9c9 | ||
|
|
ebb5282579 | ||
|
|
d03ce97c8b | ||
|
|
a4ca866790 | ||
|
|
73b09d28c6 | ||
|
|
5c45be0099 | ||
|
|
150f27bf9b | ||
|
|
d0b388834f | ||
|
|
e951a44324 | ||
|
|
9fcefcdbd4 | ||
|
|
05892366d8 | ||
|
|
989412046b | ||
|
|
f2baf273ca | ||
|
|
420a37ee75 | ||
|
|
940faed363 | ||
|
|
f86b0e71d1 | ||
|
|
7f99d73691 | ||
|
|
4633fdacef | ||
|
|
bf0e2d47b9 | ||
|
|
f044c1e953 | ||
|
|
277053d2a8 | ||
|
|
b450ea7607 | ||
|
|
510bfb3eed | ||
|
|
673d80cec2 | ||
|
|
d8498b89fd | ||
|
|
846d8a1bfe | ||
|
|
75dde62989 | ||
|
|
d9c094ff16 | ||
|
|
37244e866c | ||
|
|
124ccd6ec2 | ||
|
|
3fb3bac3ef | ||
|
|
00ccb56c8a | ||
|
|
8696cd43c4 | ||
|
|
313bae584c | ||
|
|
63651bd277 | ||
|
|
04d38bfdf7 | ||
|
|
d05827102e | ||
|
|
eb0dac2b4b | ||
|
|
9f130e1761 | ||
|
|
1b580a2e76 | ||
|
|
2a7cb615ba | ||
|
|
6e9a8a79c8 | ||
|
|
a7704987a3 | ||
|
|
6d155a57ab | ||
|
|
cc99fba3fa | ||
|
|
cd699dd6e8 | ||
|
|
c84d5cf691 | ||
|
|
ac301e104c | ||
|
|
b0af4ccd75 | ||
|
|
6b71bbaf35 | ||
|
|
0f3b99e617 | ||
|
|
ad5032b720 | ||
|
|
7e60f4876a | ||
|
|
8264356176 | ||
|
|
52b1005b69 | ||
|
|
c7c8a7d786 | ||
|
|
12e0bf093a | ||
|
|
2c4416985b |
8
.dockerignore
Normal file
8
.dockerignore
Normal file
@@ -0,0 +1,8 @@
|
||||
.ash_history
|
||||
.git
|
||||
.cpan
|
||||
.cpanm
|
||||
/files/
|
||||
/local/
|
||||
/lutim.conf
|
||||
/lutim.db
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
zanata.xml merge=ours
|
||||
25
.gitignore
vendored
25
.gitignore
vendored
@@ -1,6 +1,27 @@
|
||||
*.swp
|
||||
lutim.conf
|
||||
lutim.db
|
||||
*.db
|
||||
*.db-shm
|
||||
*.db-wal
|
||||
script/hypnotoad.pid
|
||||
local/*
|
||||
files/*
|
||||
templates/data.html.ep
|
||||
stop-upload
|
||||
tap.xml
|
||||
themes/*
|
||||
!themes/default
|
||||
!themes/default/*
|
||||
themes/default/templates/data.html.ep
|
||||
themes/default/templates/raw.html.ep
|
||||
themes/default/templates/stats.json.ep
|
||||
themes/default/templates/partial/raw.js.ep
|
||||
!themes/korrigan
|
||||
!themes/korrigan/*
|
||||
tmp/*
|
||||
.zanata-cache/*
|
||||
cover_db/*
|
||||
*.passwd
|
||||
.ash_history
|
||||
.vscode/
|
||||
.DS_Store
|
||||
.cpanm/
|
||||
|
||||
154
.gitlab-ci.yml
Normal file
154
.gitlab-ci.yml
Normal file
@@ -0,0 +1,154 @@
|
||||
image: hatsoftwares/lutim-test-ci:latest
|
||||
stages:
|
||||
- create_release
|
||||
- pouet_it
|
||||
- podcheck
|
||||
- carton
|
||||
- carton_bdd
|
||||
- tests
|
||||
- cover
|
||||
before_script:
|
||||
- rm -f *.db
|
||||
variables:
|
||||
POSTGRES_DB: lutim_db
|
||||
POSTGRES_USER: lutim
|
||||
POSTGRES_PASSWORD: lutim_pwd
|
||||
|
||||
### Jobs templates
|
||||
##
|
||||
#
|
||||
.retry: &retry
|
||||
retry: 2
|
||||
except:
|
||||
- tags
|
||||
|
||||
.carton_bdd_template: &carton_bdd_definition
|
||||
<<: *retry
|
||||
stage: carton_bdd
|
||||
artifacts:
|
||||
paths:
|
||||
- local/
|
||||
needs:
|
||||
- carton
|
||||
|
||||
.test_template: &test_definition
|
||||
<<: *retry
|
||||
stage: tests
|
||||
script:
|
||||
- MOJO_CONFIG=t/$CI_JOB_NAME.conf make test
|
||||
- MOJO_CONFIG=t/$CI_JOB_NAME.conf make watch
|
||||
- MOJO_CONFIG=t/$CI_JOB_NAME.conf make cleanbdd
|
||||
- MOJO_CONFIG=t/$CI_JOB_NAME.conf make cleanfiles
|
||||
- MOJO_CONFIG=t/$CI_JOB_NAME.conf make stats
|
||||
- MOJO_CONFIG=t/$CI_JOB_NAME.conf make test-junit-output
|
||||
artifacts:
|
||||
paths:
|
||||
- tap.xml
|
||||
- cover_db/
|
||||
|
||||
.sqlite_template: &sqlite_definition
|
||||
<<: *test_definition
|
||||
needs:
|
||||
- carton_sqlite
|
||||
|
||||
.pg_template: &pg_definition
|
||||
<<: *test_definition
|
||||
needs:
|
||||
- carton_postgresql
|
||||
services:
|
||||
- name: postgres:9.6
|
||||
alias: postgres
|
||||
|
||||
### Publish tag changelog and create a toot
|
||||
##
|
||||
#
|
||||
include:
|
||||
- 'https://framagit.org/fiat-tux/gitlabci-snippets/-/raw/2aac6c1f3dd725d9aed57549da67a92759f9f9ec/create-release-from-ci.gitlab-ci.yml'
|
||||
- 'https://framagit.org/fiat-tux/gitlabci-snippets/-/raw/41345a919d3c927991782f5fd17e0c7b338a3f3a/pouet-it-from-ci.gitlab-ci.yml'
|
||||
|
||||
### Podcheck
|
||||
##
|
||||
#
|
||||
podcheck:
|
||||
<<: *retry
|
||||
stage: podcheck
|
||||
script:
|
||||
- make podcheck
|
||||
|
||||
### Install common dependencies
|
||||
##
|
||||
#
|
||||
carton:
|
||||
<<: *retry
|
||||
stage: carton
|
||||
artifacts:
|
||||
paths:
|
||||
- local/
|
||||
dependencies: []
|
||||
script:
|
||||
- cpanm -l local Devel::Cover~1.29
|
||||
- carton install --deployment --without=sqlite --without=postgresql --without=minion --without=cache --without=memcached
|
||||
when: always
|
||||
|
||||
### Install DB related dependencies
|
||||
##
|
||||
#
|
||||
carton_sqlite:
|
||||
<<: *carton_bdd_definition
|
||||
script:
|
||||
- carton install --deployment --without=postgresql --without=minion --without=cache --without=memcached
|
||||
carton_postgresql:
|
||||
<<: *carton_bdd_definition
|
||||
script:
|
||||
- carton install --deployment --without=sqlite --without=minion --without=cache --without=memcached
|
||||
|
||||
### SQLite tests
|
||||
##
|
||||
#
|
||||
sqlite1:
|
||||
<<: *sqlite_definition
|
||||
before_script:
|
||||
- carton install --deployment --without=postgresql --without=minion --without=cache --without=memcached
|
||||
sqlite2:
|
||||
<<: *sqlite_definition
|
||||
before_script:
|
||||
- carton install --deployment --without=postgresql --without=cache --without=memcached
|
||||
sqlite3:
|
||||
<<: *sqlite_definition
|
||||
services:
|
||||
- name: postgres:9.6
|
||||
alias: postgres
|
||||
before_script:
|
||||
- carton install --deployment --without=cache --without=memcached
|
||||
- export PGPASSWORD=lutim_pwd; echo 'CREATE DATABASE lutim_minion WITH OWNER lutim;' | psql -h postgres -U lutim lutim_db
|
||||
|
||||
### PostgreSQL tests
|
||||
##
|
||||
#
|
||||
postgresql1:
|
||||
<<: *pg_definition
|
||||
before_script:
|
||||
- carton install --deployment --without=sqlite --without=minion --without=cache --without=memcached
|
||||
postgresql2:
|
||||
<<: *pg_definition
|
||||
before_script:
|
||||
- carton install --deployment --without=cache --without=memcached
|
||||
postgresql3:
|
||||
<<: *pg_definition
|
||||
before_script:
|
||||
- carton install --deployment --without=sqlite --without=cache --without=memcached
|
||||
- export PGPASSWORD=lutim_pwd; echo 'CREATE DATABASE lutim_minion WITH OWNER lutim;' | psql -h postgres -U lutim lutim_db
|
||||
|
||||
### Code coverage
|
||||
##
|
||||
#
|
||||
cover:
|
||||
stage: cover
|
||||
script:
|
||||
- make cover
|
||||
coverage: '/Total.* (\d+\.\d+)$/'
|
||||
artifacts:
|
||||
reports:
|
||||
junit: tap.xml
|
||||
except:
|
||||
- tags
|
||||
7
.provision/README.md
Normal file
7
.provision/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## ansible-role-lutim
|
||||
|
||||
An ansible role deploy the application on host machine(Ubuntu 20.04)
|
||||
|
||||
## terraform-aws-lutim
|
||||
|
||||
A terraform plan creates necessary AWS infrastructure and deploy the lutim. This terraform plan uses the `lutim_startup.sh` script to deploy lufi on AWS and also uses above ansible role `ansible-role-lutim` to configure the application on AWS.
|
||||
50
.provision/ansible-role-lutim/README.md
Normal file
50
.provision/ansible-role-lutim/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
Ansible-Role-lutim
|
||||
=========
|
||||
This role installs the and configures lutim on Debian/Ubuntu servers with nginx web server configuration.
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
| Variable name | Value | Description |
|
||||
| ------------- | ----- | ----------- |
|
||||
| `app_dir` | /var/www/lutim | Set the application directory for the best practice |
|
||||
| `lutim_owner` | www-data | Set the application user for the best practice |
|
||||
| `lutim_group` | www-data | Set the application group for the best practice |
|
||||
| `_contact` | contact.example.com | Contact option (mandatory), where you have to put some way for the users to contact you. |
|
||||
| `_secrets` | ffyg7kbkjba | Secrets option (mandotory), which is array of random string. Used by Mojolicious for encrypting session cookies |
|
||||
| `_project_version` | master | We can chose the project version either Master branch, Dev branch or tag based |
|
||||
| `_server_name` | IP address (or) CNAME/FQDN | Mention the Server Name for the Nginx configurations |
|
||||
|
||||
Sample example of use in a playbook
|
||||
--------------
|
||||
|
||||
The following code has been tested with Ubuntu 20.04
|
||||
|
||||
```yaml
|
||||
|
||||
- name: "install lutim"
|
||||
hosts: enter your hosts file
|
||||
become: yes
|
||||
role:
|
||||
- ansible-role-lutim
|
||||
vars:
|
||||
lutim_owner: "www-data"
|
||||
lutim_group: "www-data"
|
||||
contact: "contact.example.com"
|
||||
secrets: "yigavlvlivwe"
|
||||
app_dir: "/var/www/lutim"
|
||||
project_version: "master"
|
||||
servername: "IP address (or) CNAME/FQDN"
|
||||
```
|
||||
|
||||
Contributing
|
||||
------------
|
||||
Don’t hesitate to create a pull request
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
6
.provision/ansible-role-lutim/files/cronjob
Normal file
6
.provision/ansible-role-lutim/files/cronjob
Normal file
@@ -0,0 +1,6 @@
|
||||
#Path of the script
|
||||
PATH=/var/www/lutim
|
||||
|
||||
carton exec script/lutim cron cleanbdd --mode production
|
||||
carton exec script/lutim cron cleanfiles --mode production
|
||||
carton exec script/lutim cron watch --mode production
|
||||
5
.provision/ansible-role-lutim/handlers/main.yml
Normal file
5
.provision/ansible-role-lutim/handlers/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
# handlers file for ansible-role-lutim
|
||||
|
||||
- name: restart nginx
|
||||
service: name=nginx state=restarted
|
||||
23
.provision/ansible-role-lutim/tasks/apprun.yaml
Normal file
23
.provision/ansible-role-lutim/tasks/apprun.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
#apprun.yml
|
||||
---
|
||||
- name: This command will install the postgress module
|
||||
ansible.builtin.shell:
|
||||
cmd: carton install --deployment --without=test --without=sqlite
|
||||
chdir: "{{ app_dir }}"
|
||||
|
||||
- name: Upload application config file
|
||||
ansible.builtin.template:
|
||||
src: ../templates/lutim.conf.j2
|
||||
dest: "{{ app_dir }}/lutim.conf"
|
||||
|
||||
- name: App executes
|
||||
ansible.builtin.shell:
|
||||
cmd: carton exec hypnotoad script/lutim
|
||||
chdir: "{{ app_dir }}"
|
||||
|
||||
- name: Nginx configuration file add
|
||||
ansible.builtin.template:
|
||||
src: ../templates/app.conf
|
||||
dest: /etc/nginx/conf.d/
|
||||
mode: '0644'
|
||||
notify: restart nginx
|
||||
21
.provision/ansible-role-lutim/tasks/cronjob.yaml
Normal file
21
.provision/ansible-role-lutim/tasks/cronjob.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
- name: Copy the cronjob file
|
||||
ansible.builtin.copy:
|
||||
src: ../files/cronjob
|
||||
dest: /etc/cron.d/lutim
|
||||
owner: www-data
|
||||
group: www-data
|
||||
|
||||
- name: "example cronjob"
|
||||
ansible.builtin.cron:
|
||||
name: "cronjob"
|
||||
state: present
|
||||
user: www-data
|
||||
minute: "0"
|
||||
hour: "0"
|
||||
day: "*"
|
||||
month: "*"
|
||||
weekday: "*"
|
||||
job: |
|
||||
carton exec script/lutim cron cleanbdd --mode production; carton exec script/lutim cron cleanfiles --mode production; carton exec script/lutim cron watch --mode production
|
||||
|
||||
23
.provision/ansible-role-lutim/tasks/dependencies.yaml
Normal file
23
.provision/ansible-role-lutim/tasks/dependencies.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
# dependencies.yaml
|
||||
---
|
||||
- name: Lutim | Update apt cache
|
||||
ansible.builtin.apt: update_cache=yes
|
||||
changed_when: no
|
||||
- name: Install Dependencies
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- nginx
|
||||
- carton
|
||||
- build-essential
|
||||
- libssl-dev
|
||||
- libpq-dev
|
||||
- libio-socket-ssl-perl
|
||||
- zlib1g-dev
|
||||
- libmojo-sqlite-perl
|
||||
- shared-mime-info
|
||||
- perlmagick
|
||||
state: present
|
||||
|
||||
|
||||
|
||||
|
||||
18
.provision/ansible-role-lutim/tasks/gitclone.yaml
Normal file
18
.provision/ansible-role-lutim/tasks/gitclone.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
#gitclone
|
||||
---
|
||||
|
||||
- name: clone the repository
|
||||
ansible.builtin.git:
|
||||
repo: 'https://framagit.org/fiat-tux/hat-softwares/lutim.git'
|
||||
dest: "{{ app_dir }}"
|
||||
clone: yes
|
||||
update: yes
|
||||
version: "{{ project_version }}"
|
||||
|
||||
- name: Change the owner
|
||||
ansible.builtin.file:
|
||||
path: "{{ app_dir }}"
|
||||
owner: "{{ lutim_owner }}"
|
||||
group: "{{ lutim_group }}"
|
||||
state: directory
|
||||
recurse: yes
|
||||
7
.provision/ansible-role-lutim/tasks/main.yml
Normal file
7
.provision/ansible-role-lutim/tasks/main.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
# tasks file for ansible-role-lutim
|
||||
|
||||
- include: dependencies.yaml
|
||||
- include: gitclone.yaml
|
||||
- include: apprun.yaml
|
||||
- include: cronjob.yaml
|
||||
43
.provision/ansible-role-lutim/templates/app.conf
Normal file
43
.provision/ansible-role-lutim/templates/app.conf
Normal file
@@ -0,0 +1,43 @@
|
||||
server {
|
||||
listen 80;
|
||||
# No need to have a `root` parameter.
|
||||
server_name {{ _server_name }};
|
||||
# This is important for user's privacy !
|
||||
access_log off;
|
||||
error_log /var/log/nginx/lutim.error.log;
|
||||
# This is important ! Make it OK with your Lutim configuration
|
||||
client_max_body_size 40M;
|
||||
|
||||
location ~* ^/(img|css|font|js)/ {
|
||||
try_files $uri @lutim;
|
||||
add_header Expires "Thu, 31 Dec 2037 23:55:55 GMT";
|
||||
add_header Cache-Control "public, max-age=315360000";
|
||||
|
||||
# HTTPS only header, improves security
|
||||
#add_header Strict-Transport-Security "max-age=15768000";
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri @lutim;
|
||||
|
||||
# HTTPS only header, improves security
|
||||
#add_header Strict-Transport-Security "max-age=15768000";
|
||||
}
|
||||
|
||||
location @lutim {
|
||||
# Adapt this to your configuration
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
# If you want to log the remote port of the image senders, you'll need that
|
||||
proxy_set_header X-Remote-Port $remote_port;
|
||||
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# We expect the downsteam servers to redirect to the right hostname, so don't do any rewrites here.
|
||||
proxy_redirect off;
|
||||
}
|
||||
}
|
||||
317
.provision/ansible-role-lutim/templates/lutim.conf.j2
Normal file
317
.provision/ansible-role-lutim/templates/lutim.conf.j2
Normal file
@@ -0,0 +1,317 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
{
|
||||
####################
|
||||
# Hypnotoad settings
|
||||
####################
|
||||
# see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings
|
||||
hypnotoad => {
|
||||
# array of IP addresses and ports you want to listen to
|
||||
listen => ['http://127.0.0.1:8080'],
|
||||
# if you use Lutim behind a reverse proxy like Nginx, you want to set proxy to 1
|
||||
# if you use Lutim directly, let it commented
|
||||
#proxy => 1,
|
||||
},
|
||||
|
||||
################
|
||||
# Lutim settings
|
||||
################
|
||||
|
||||
# put a way to contact you here and uncomment it
|
||||
# mandatory
|
||||
contact => '{{ _contact }}',
|
||||
|
||||
# random string used to encrypt cookies
|
||||
# mandatory
|
||||
secrets => ['{{ _secrets }}'],
|
||||
|
||||
# choose a theme. See the available themes in `themes` directory
|
||||
# optional, default is 'default'
|
||||
#theme => 'default',
|
||||
|
||||
# length of the images random URL
|
||||
# optional, default is 8
|
||||
#length => 8,
|
||||
|
||||
# length of the encryption key
|
||||
# optional, default is 8
|
||||
#crypto_key_length => 8,
|
||||
|
||||
# how many URLs will be provisioned in a batch ?
|
||||
# optional, default is 5
|
||||
#provis_step => 5,
|
||||
|
||||
# max number of URLs to be provisioned
|
||||
# optional, default is 100
|
||||
#provisioning => 100,
|
||||
|
||||
# anti-flood protection delay, in seconds
|
||||
# users won't be able to ask Lutim to download images more than one per anti_flood_delay seconds
|
||||
# optional, default is 5
|
||||
#anti_flood_delay => 5,
|
||||
|
||||
# twitter account which will appear on twitter cards
|
||||
# see https://dev.twitter.com/docs/cards/validation/validator to register your Lutim instance on twitter
|
||||
# optional, no default
|
||||
#tweet_card_via => '@foo',
|
||||
|
||||
# max image size, in octets
|
||||
# you can write it 10*1024*1024
|
||||
# optional, default is 10485760
|
||||
#max_file_size => 10485760,
|
||||
|
||||
# if you want to have piwik statistics, provide a piwik image tracker
|
||||
# only the image tracker is allowed, no javascript
|
||||
# optional, no default
|
||||
#piwik_img => 'https://piwik.example.org/piwik.php?idsite=1&rec=1',
|
||||
|
||||
# if you want to include something in the right of the screen, put it here
|
||||
# here's an example to put the logo of your hoster
|
||||
# optional, no default
|
||||
#hosted_by => 'My super hoster <img src="http://hoster.example.com" alt="Hoster logo">',
|
||||
|
||||
# DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED
|
||||
# Lutim now checks if the X-Forwarded-Proto header is present and equal to https.
|
||||
# set to 1 if you use Lutim behind a secure web server
|
||||
# optional, default is 0
|
||||
#https => 0,
|
||||
|
||||
# broadcast_message which will displayed on all pages of Lutim (but no in json response)
|
||||
# optional, no default
|
||||
#broadcast_message => 'Maintenance',
|
||||
|
||||
# array of authorized domains for API calls.
|
||||
# if you want to authorize everyone to use the API: ['*']
|
||||
# optional, no domains allowed by default
|
||||
#allowed_domains => ['http://1.example.com', 'http://2.example.com'],
|
||||
|
||||
# default time limit for files
|
||||
# valid values are 0, 1, 7, 30 and 365
|
||||
# optional, default is 0 (no limit)
|
||||
#default_delay => 0,
|
||||
|
||||
# comma-separated values proposed for delays
|
||||
# optional, default is '0,1,7,30,365'
|
||||
#proposed_delays => '0,1,7,30,365',
|
||||
|
||||
# number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay)
|
||||
# a warning message will be displayed on homepage
|
||||
# optional, default is 0 (no limit)
|
||||
#max_delay => 0,
|
||||
|
||||
# if set to 1, all the images will be encrypted and the encryption option will no be displayed
|
||||
# optional, default is 0
|
||||
#always_encrypt => 0,
|
||||
|
||||
# you can allow to use a watermark on the uploaded images (or enforce its use)
|
||||
# define a path to the watermark image (provide an image with alpha channel)
|
||||
# you can define the path relative to lutim directory or set an absolute path
|
||||
# to disable the usage of a watermark, leave it blank or commented
|
||||
# optional, no default
|
||||
#watermark_path => '',
|
||||
|
||||
# the watermark can be a tiling one or a single one
|
||||
# when using a small one, you can choose where to place it
|
||||
# valid values are 'Center', 'North', 'NorthEast', 'East', 'SouthEast', 'South', 'SouthWest', 'West' and 'NorthWest' (case insensitive)
|
||||
# optional, default is 'SouthEast'
|
||||
#watermark_placement => 'SouthEast',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be used by default
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_default => 'none',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be enforced (users will always have a watermark and won’t be able to disable it)
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_enforce => 'none',
|
||||
|
||||
# length of the image's delete token
|
||||
# optional, default is 24
|
||||
#token_length => 24,
|
||||
|
||||
# URL sub-directory in which you want Lutim to be accessible
|
||||
# example: you want to have Lutim under https://example.org/lutim/
|
||||
# => set prefix to '/lutim' or to '/lutim/', it doesn't matter
|
||||
# optional, defaut is /
|
||||
#prefix => '/',
|
||||
|
||||
# choose what database you want to use
|
||||
# valid choices are sqlite and postgresql (all lowercase)
|
||||
# optional, default is sqlite
|
||||
#dbtype => 'sqlite',
|
||||
|
||||
# SQLite ONLY - only used if dbtype is set to sqlite
|
||||
# define a path to the SQLite database
|
||||
# you can define it relative to lutim directory or set an absolute path
|
||||
# remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is lutim.db
|
||||
#db_path => 'lutim.db',
|
||||
|
||||
# PostgreSQL ONLY - only used if dbtype is set to postgresql
|
||||
# these are the credentials to access the PostgreSQL database
|
||||
# mandatory if you choosed postgresql as dbtype
|
||||
pgdb => {
|
||||
database => 'lutim',
|
||||
host => 'localhost',
|
||||
user => 'DBUSER',
|
||||
pwd => 'DBPASSWORD'
|
||||
},
|
||||
|
||||
# use Minion instead of directly increase counters
|
||||
# need to launch a minion worker service if enabled
|
||||
# optional, Minion is disabled by default
|
||||
minion => {
|
||||
enabled => 0,
|
||||
# # Which Minion backend to use?
|
||||
# # valid values are sqlite and postgresql (all lowercase)
|
||||
# # mandatory if Minion is enabled, default is sqlite
|
||||
# dbtype => 'sqlite',
|
||||
# # SQLite ONLY - only used if if you choose sqlite as Minion backend, define the path to the minion database
|
||||
# # you can define it relative to lutim directory or set an absolute path
|
||||
# # remember that it has to be in a directory writable by Lutim user
|
||||
# # optional, default is minion.db
|
||||
db_path => 'minion.db',
|
||||
# # PostgreSQL ONLY - only used if you choose postgresql as Minion backend
|
||||
# # these are the credentials to access the Minion's PostgreSQL database
|
||||
# # mandatory if you choosed postgresql as Minion backend, no default
|
||||
pgdb => {
|
||||
database => 'lutim_minion',
|
||||
host => 'localhost',
|
||||
user => 'DBUSER',
|
||||
pwd => 'DBPASSWORD'
|
||||
}
|
||||
},
|
||||
|
||||
# set `ldap` if you want that only authenticated users can shorten URLs
|
||||
# please note that everybody can still use shortend URLs
|
||||
# optional, no default
|
||||
#ldap => {
|
||||
# uri => 'ldaps://ldap.example.org', # server URI
|
||||
# user_tree => 'ou=users,dc=example,dc=org', # search base DN
|
||||
# bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN
|
||||
# bind_pwd => 'secr3t', # search bind password
|
||||
# user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.)
|
||||
# user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.)
|
||||
#},
|
||||
|
||||
# set `htpasswd` if you want to use an htpasswd file instead of ldap
|
||||
# create the file with `htpasswd -c lutim.passwd user`, update it with `htpasswd lutim.passwd user2`
|
||||
# make sure that lutim can read the file!
|
||||
# optional, no default
|
||||
#htpasswd => 'lutim.passwd',
|
||||
|
||||
# if you've set ldap or htpasswd above, the session will last `session_duration` seconds before
|
||||
# the user needs to reauthenticate
|
||||
# optional, default is 3600
|
||||
#session_duration => 3600,
|
||||
|
||||
# disable counters of images
|
||||
# set to 1 to disable counters
|
||||
# optional, counters are enabled by default
|
||||
#disable_img_stats => 0,
|
||||
|
||||
# define the height of the thumbnails generated at users' will
|
||||
# this is not the height of the thumbnails send after upload,
|
||||
# we're talking about thumbnails generated when someone asked for
|
||||
# https://example.org/lutim/tesrinp?thumb
|
||||
# this works only if you have ImageMagick
|
||||
# optional, default is 100 (pixels)
|
||||
#thumbnail_size => 100,
|
||||
|
||||
# maximum number of files that can be downloaded as a single zip archive
|
||||
# if too many files are asked, it results a timeout, so Lutim split the zip URL
|
||||
# in multiple URLs, each with max_file_size images.
|
||||
# timeout behavior depends heavily on your server ressources (CPU) and if images
|
||||
# are encrypted
|
||||
# optional, default is 15
|
||||
#max_files_in_zip => 15,
|
||||
|
||||
# maximum size (in MB) of memory allowed for the image cache
|
||||
# Lutim has a built-in memory-based image cache to accelerate responses to often-viewed images.
|
||||
# This setting makes the cache remove oldest viewed image if the cache size is over it.
|
||||
# WARNING: a cache is created for each hypnotoad worker, which by default is twice the number of
|
||||
# CPUs you have. See http://mojolicious.org/perldoc/Mojo/Server/Hypnotoad#workers for details
|
||||
# So, if you have 4 workers and set cache_max_size to 100, the real maximum size of RAM used for
|
||||
# cache is 400MB.
|
||||
# If set to 0, the cache is disabled
|
||||
# optional, default is 0
|
||||
#cache_max_size => 0,
|
||||
|
||||
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
|
||||
# If set to [], the use of memcached is disabled.
|
||||
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
|
||||
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
|
||||
# servers.
|
||||
# exemple of valid value: ['127.0.0.1:11211']
|
||||
# optional, default is []
|
||||
#memcached_servers => [],
|
||||
|
||||
# enable or disable Lutim built-in logs
|
||||
# set to 1 to disable logs
|
||||
# optional, default is 0
|
||||
#quiet_logs => 0,
|
||||
|
||||
# Content-Security-Policy header that will be sent by Lstu
|
||||
# Set to '' to disable CSP header
|
||||
# https://content-security-policy.com/ provides a good documentation about CSP.
|
||||
# https://report-uri.com/home/generate provides a tool to generate a CSP header.
|
||||
# optional, default is "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"
|
||||
# NB: unsafe-inline for script-src and style-src are here only because morris,
|
||||
# the graph library used in the stats page requires it
|
||||
# the default value is good for `default` theme
|
||||
#csp => "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'",
|
||||
|
||||
# X-Frame-Options header that will be sent by Lstu
|
||||
# Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/'
|
||||
# Set to '' to disable X-Frame-Options header
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
||||
# Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly
|
||||
# to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors)
|
||||
# optional, default is 'DENY'
|
||||
#x_frame_options => 'DENY',
|
||||
|
||||
# X-Content-Type-Options that will be sent by Lstu
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
|
||||
# Set to '' to disable X-Content-Type-Options header
|
||||
# optional, default is 'nosniff'
|
||||
#x_content_type_options => 'nosniff',
|
||||
|
||||
# X-XSS-Protection that will be sent by Lstu
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
|
||||
# Set to '' to disable X-XSS-Protection header
|
||||
# optional, default is '1; mode=block'
|
||||
#x_xss_protection => '1; mode=block',
|
||||
|
||||
# if set, the uploaded images will use this domain
|
||||
# optional
|
||||
#fixed_domain => 'example.org',
|
||||
|
||||
##########################
|
||||
# Lutim cron jobs settings
|
||||
##########################
|
||||
|
||||
# number of days shown in /stats page (used with script/lutim cron stats)
|
||||
# optional, default is 365
|
||||
stats_day_num => 365,
|
||||
|
||||
# number of days senders' IP addresses are kept in database
|
||||
# after that delay, they will be deleted from database (used with script/lutim cron cleanbdd)
|
||||
# optional, default is 365
|
||||
keep_ip_during => 365,
|
||||
|
||||
# max size of the files directory, in octets
|
||||
# used by script/lutim cron watch to trigger an action
|
||||
# optional, no default
|
||||
max_total_size => 10*1024*1024*1024,
|
||||
|
||||
# default action when files directory is over max_total_size (used with script/lutim cron watch)
|
||||
# valid values are 'warn', 'stop-upload' and 'delete'
|
||||
# please, see readme
|
||||
# optional, default is 'warn'
|
||||
#policy_when_full => 'warn',
|
||||
|
||||
# images which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task
|
||||
# if delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted
|
||||
# optional, no default
|
||||
delete_no_longer_viewed_files => 90
|
||||
};
|
||||
16
.provision/ansible-role-lutim/vars/main.yml
Normal file
16
.provision/ansible-role-lutim/vars/main.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
# vars file for ansible-role-lutim
|
||||
|
||||
lutim_owner: "www-data"
|
||||
|
||||
lutim_group: "www-data"
|
||||
|
||||
app_dir: "/var/www/lutim"
|
||||
|
||||
_contact: ""
|
||||
|
||||
_secrets: ""
|
||||
|
||||
_project_version: ""
|
||||
|
||||
_servername: ""
|
||||
92
.provision/terraform-aws-lutim/README.md
Normal file
92
.provision/terraform-aws-lutim/README.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Terraform-AWS-Deploy
|
||||
|
||||
This terraform plan create the resourcess of EC2 instance
|
||||
|
||||
## Terraform Variables
|
||||
Edit the `vars.tf` file to add the variables as per your need.
|
||||
|
||||
| Variable name | Value | Description |
|
||||
| ------------- | ----- | ----------- |
|
||||
| `aws_region` | us-east-1 | Set the region |
|
||||
| `vpc_cidr` | 10.0.0.0/16 | Set the cidr value for the vpc |
|
||||
| `public_subnet_cidr` | 10.0.2.0/24 | Set the cidr value for the public subnet |
|
||||
| `user` | ubuntu | Set the EC2 instance user name |
|
||||
| `public_key` | /home/user_name/.ssh/id_rsa_pub | Set the publickey value for the ec2 instance from the host machine |
|
||||
| `private_key` | /home/user_name/.ssh/id_rsa | Set the private key value for the ec2 instance from the hostmachine |
|
||||
| `aws_access_key` | AWSACCESSKEY | Enter your aws access key |
|
||||
| `aws_secrete_key` | AWSSECRETEKEY | Enter your aws secrete key |
|
||||
| `instance_name` | lutim_app_instance | Set the name for instance |
|
||||
| `app_dir` | /var/www/lutim | Set the application directory for the best practice |
|
||||
| `lutim_owner` | www-data | Set the application user for the best practice |
|
||||
| `lutim_group` | www-data | Set the application group for the best practice |
|
||||
| `contact` | contact.example.com | Contact option (mandatory), where you have to put some way for the users to contact you. |
|
||||
| `contact_user` | name | Name of the user |
|
||||
| `secrets` | ffyg7kbkjba | Secrets option (mandotory), which is array of random string. Used by Mojolicious for encrypting session cookies |
|
||||
| `app_dir` | /var/www/lutim | Set the application directory for the best practice |
|
||||
| `lutim_owner` | www-data | Set the application user for the best practice |
|
||||
| `lutim_group` | www-data | Set the application group for the best practice |
|
||||
| `contact` | contact.example.com | Contact option (mandatory), where you have to put some way for the users to contact you. |
|
||||
| `contact_user` | name | Name of the user |
|
||||
| `secrets` | ffyg7kbkjba | Secrets option (mandotory), which is array of random string. Used by Mojolicious for encrypting session cookies |
|
||||
|
||||
## Usage of terraform plan with lufi deploy script
|
||||
|
||||
```sh
|
||||
git clone https://framagit.org/fiat-tux/hat-softwares/lutim.git
|
||||
|
||||
cd lutim/.provision/terraform-aws-lutim
|
||||
|
||||
terraform init
|
||||
terraform plan
|
||||
terraform apply
|
||||
```
|
||||
## Usage of terraform plan with ansible role
|
||||
|
||||
- Comment out the below `locals` and `user_data` source in __main.tf__ file
|
||||
|
||||
```hcl
|
||||
locals {
|
||||
user_data_vars = {
|
||||
user = var.lutim_owner
|
||||
group = var.lutim_group
|
||||
directory = var.app_dir
|
||||
contact_user = var.contact_user
|
||||
contact_lutim = var.contact
|
||||
secret_lutim = var.secret
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```hcl
|
||||
user_data = templatefile("${path.module}/lutim_startup.sh", local.user_data_vars)
|
||||
```
|
||||
|
||||
- Add the below provisioner data in __main.tf__ file at the `aws_instance` resource
|
||||
|
||||
```sh
|
||||
connection {
|
||||
agent = false
|
||||
type = "ssh"
|
||||
host = aws_instance.ec2_instance.public_dns
|
||||
private_key = "${file(var.private_key)}"
|
||||
user = "${var.user}"
|
||||
}
|
||||
|
||||
provisioner "remote-exec" {
|
||||
inline = [
|
||||
"sudo apt update -y",
|
||||
"sudo apt install python3.9 -y",
|
||||
]
|
||||
}
|
||||
|
||||
provisioner "local-exec" {
|
||||
command = <<EOT
|
||||
sleep 120 && \
|
||||
> hosts && \
|
||||
echo "[Lutim]" | tee -a hosts && \
|
||||
echo "${aws_instance.ec2_instance.public_ip} ansible_user=${var.user} ansible_ssh_private_key_file=${var.private_key}" | tee -a hosts && \
|
||||
export ANSIBLE_HOST_KEY_CHECKING=False && \
|
||||
ansible-playbook -u ${var.user} --private-key ${var.private_key} -i hosts site.yml
|
||||
EOT
|
||||
}
|
||||
```
|
||||
66
.provision/terraform-aws-lutim/lutim_startup.sh
Normal file
66
.provision/terraform-aws-lutim/lutim_startup.sh
Normal file
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "**********************************************************************"
|
||||
echo " *"
|
||||
echo "Install dependencies *"
|
||||
echo " *"
|
||||
echo "**********************************************************************"
|
||||
|
||||
SUDO=sudo
|
||||
$SUDO apt update
|
||||
$SUDO apt install jq -y
|
||||
$SUDO apt install wget -y
|
||||
$SUDO apt install unzip
|
||||
$SUDO apt install carton -y
|
||||
$SUDO apt install build-essential -y
|
||||
$SUDO apt install nginx -y
|
||||
$SUDO apt install libssl-dev -y
|
||||
$SUDO apt install libio-socket-ssl-perl -y
|
||||
$SUDO apt install liblwp-protocol-https-perl -y
|
||||
$SUDO apt install zlib1g-dev -y
|
||||
$SUDO apt install libmojo-sqlite-perl -y
|
||||
$SUDO apt install libpq-dev -y
|
||||
|
||||
echo "**********************************************************************"
|
||||
echo " *"
|
||||
echo "Configuring the Application *"
|
||||
echo " *"
|
||||
echo "**********************************************************************"
|
||||
|
||||
sleep 10;
|
||||
version=$(curl -s https://framagit.org/api/v4/projects/1/releases | jq '.[]' | jq -r '.name' | head -1)
|
||||
echo $version
|
||||
pushd ${directory}
|
||||
$SUDO wget https://framagit.org/fiat-tux/hat-softwares/lutim/-/archive/$version/lutim-$version.zip
|
||||
$SUDO unzip lutim-$version.zip
|
||||
$SUDO chown ${user} lutim-$version
|
||||
$SUDO chgrp ${group} lutim-$version
|
||||
pushd lutim-$version
|
||||
|
||||
echo "**********************************************************************"
|
||||
echo " *"
|
||||
echo "Install Carton Packages *"
|
||||
echo " *"
|
||||
echo "**********************************************************************"
|
||||
|
||||
$SUDO carton install --deployment --without=test --without=sqlite --without=mysql
|
||||
|
||||
sleep 10;
|
||||
|
||||
$SUDO cp lutim.conf.template lutim.conf
|
||||
|
||||
sed -i 's/127.0.0.1/0.0.0.0/' lutim.conf
|
||||
sed -i 's/#contact/contact/g' lutim.conf
|
||||
sed -i "s/John Doe/${contact_user}/g" lutim.conf
|
||||
sed -i "s/admin[at]example.com/${contact_lutim}/g" lutim.conf
|
||||
sed -i "s/fdjsofjoihrei/${secret_lutim}/g" lutim.conf
|
||||
sed -i '153 , 158 s/#/ /g' lutim.conf
|
||||
|
||||
echo "**********************************************************************"
|
||||
echo " *"
|
||||
echo "Run the Application *"
|
||||
echo " *"
|
||||
echo "**********************************************************************"
|
||||
|
||||
$SUDO carton exec hypnotoad script/lutim
|
||||
|
||||
123
.provision/terraform-aws-lutim/main.tf
Normal file
123
.provision/terraform-aws-lutim/main.tf
Normal file
@@ -0,0 +1,123 @@
|
||||
locals {
|
||||
user_data_vars = {
|
||||
user = var.lutim_owner
|
||||
group = var.lutim_group
|
||||
directory = var.app_dir
|
||||
contact_user = var.contact_user
|
||||
contact_lutim = var.contact
|
||||
secret_lutim = var.secret
|
||||
}
|
||||
}
|
||||
|
||||
#Create the VPC
|
||||
resource "aws_vpc" "vpc" {
|
||||
cidr_block = "${var.vpc_cidr}"
|
||||
enable_dns_hostnames = true
|
||||
enable_dns_support = true
|
||||
instance_tenancy = "default"
|
||||
tags = {
|
||||
Name = "lutim-master-vpc"
|
||||
}
|
||||
}
|
||||
|
||||
# Create InternetGateWay and attach to VPC
|
||||
|
||||
resource "aws_internet_gateway" "IGW" {
|
||||
vpc_id = "${aws_vpc.vpc.id}"
|
||||
tags = {
|
||||
"Name" = "lutim-master-igw"
|
||||
}
|
||||
}
|
||||
|
||||
# Create a public subnet
|
||||
|
||||
resource "aws_subnet" "publicsubnet" {
|
||||
vpc_id = "${aws_vpc.vpc.id}"
|
||||
cidr_block = "${var.public_subnet_cidr}"
|
||||
map_public_ip_on_launch = true
|
||||
tags = {
|
||||
Name = "lutim-master-us-east-1-public"
|
||||
}
|
||||
}
|
||||
|
||||
# Create routeTable
|
||||
resource "aws_route_table" "publicroute" {
|
||||
vpc_id = "${aws_vpc.vpc.id}"
|
||||
route {
|
||||
cidr_block = "0.0.0.0/0"
|
||||
gateway_id = "${aws_internet_gateway.IGW.id}"
|
||||
}
|
||||
|
||||
tags = {
|
||||
Name = "lutim-master-us-east-1-public-rt"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_main_route_table_association" "mainRTB" {
|
||||
vpc_id = "${aws_vpc.vpc.id}"
|
||||
route_table_id = "${aws_route_table.publicroute.id}"
|
||||
}
|
||||
## Create security group
|
||||
resource "aws_security_group" "security" {
|
||||
name = "lutim-master-sg"
|
||||
description = "allow all traffic"
|
||||
vpc_id = "${aws_vpc.vpc.id}"
|
||||
|
||||
ingress {
|
||||
description = "allow all traffic"
|
||||
from_port = "0"
|
||||
to_port = "65535"
|
||||
protocol = "tcp"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
ingress {
|
||||
description = "allow port SSH"
|
||||
from_port = "22"
|
||||
to_port = "22"
|
||||
protocol = "tcp"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
egress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Add ubuntu AMI
|
||||
data "aws_ami" "ubuntu" {
|
||||
most_recent = true
|
||||
owners = ["099720109477"]
|
||||
|
||||
filter {
|
||||
name = "name"
|
||||
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
|
||||
}
|
||||
}
|
||||
|
||||
#Create key_pair for the instance
|
||||
|
||||
resource "aws_key_pair" "genkey" {
|
||||
key_name = "lutim.webapp"
|
||||
public_key = "${file(var.public_key)}"
|
||||
}
|
||||
|
||||
# Craete ec2 instance
|
||||
resource "aws_instance" "ec2_instance" {
|
||||
ami = "${data.aws_ami.ubuntu.id}"
|
||||
instance_type = "t2.medium"
|
||||
associate_public_ip_address = "true"
|
||||
subnet_id = "${aws_subnet.publicsubnet.id}"
|
||||
vpc_security_group_ids = ["${aws_security_group.security.id}"]
|
||||
user_data = templatefile("${path.module}/lutim_startup.sh", local.user_data_vars)
|
||||
key_name = "lutim.webapp"
|
||||
|
||||
tags = {
|
||||
Name = "${var.instance_name}"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
7
.provision/terraform-aws-lutim/output.tf
Normal file
7
.provision/terraform-aws-lutim/output.tf
Normal file
@@ -0,0 +1,7 @@
|
||||
output "public_ip" {
|
||||
value = "${aws_instance.ec2_instance.public_ip}"
|
||||
}
|
||||
|
||||
output "App_running_at" {
|
||||
value = "http://${aws_instance.ec2_instance.public_ip}:8080"
|
||||
}
|
||||
14
.provision/terraform-aws-lutim/provider.tf
Normal file
14
.provision/terraform-aws-lutim/provider.tf
Normal file
@@ -0,0 +1,14 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
access_key = "${var.aws_access_key}"
|
||||
secret_key = "${var.aws_secret_key}"
|
||||
region = "${var.aws_region}"
|
||||
}
|
||||
60
.provision/terraform-aws-lutim/vars.tf
Normal file
60
.provision/terraform-aws-lutim/vars.tf
Normal file
@@ -0,0 +1,60 @@
|
||||
variable "aws_region" {
|
||||
default = "aws_region"
|
||||
}
|
||||
variable "vpc_cidr" {
|
||||
default = "cidr_value"
|
||||
}
|
||||
variable "public_subnet_cidr" {
|
||||
default = "cidr_value"
|
||||
}
|
||||
variable "public_subnet1_cidr" {
|
||||
default = "cidr_value"
|
||||
}
|
||||
|
||||
variable "user" {
|
||||
default = "user_of_instance"
|
||||
}
|
||||
|
||||
variable "public_key" {
|
||||
default = "$PWD_publickey"
|
||||
}
|
||||
variable "private_key" {
|
||||
default = "$PWD_privatekey"
|
||||
}
|
||||
variable "aws_access_key" {
|
||||
default = "aws_access_key"
|
||||
}
|
||||
|
||||
variable "aws_secret_key" {
|
||||
default = "aws_secrete_key"
|
||||
}
|
||||
|
||||
variable "instance_name" {
|
||||
default = "instance_name"
|
||||
}
|
||||
|
||||
variable "lutim_owner" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "lutim_group" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "app_dir" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "contact_user" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "contact" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "secret" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
|
||||
3
.weblate
Normal file
3
.weblate
Normal file
@@ -0,0 +1,3 @@
|
||||
[weblate]
|
||||
url = https://weblate.framasoft.org/api/
|
||||
translation = lutim/development
|
||||
21
AUTHORS.md
Normal file
21
AUTHORS.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Lutim's authors
|
||||
|
||||
## Main developers
|
||||
|
||||
* Luc Didry, aka Sky (<http://www.fiat-tux.fr>), core developer, @framasky on [Mastodon](https://framapiaf.org/@framasky) and on [Diaspora*](https://framasphere.org/public/framasky)
|
||||
* Dattaz (<http://dattaz.fr>), webapp developer, [@dat_taz](https://twitter.com/dat_taz)
|
||||
|
||||
## Contributors
|
||||
|
||||
* Jean-Bernard Marcon, aka Goofy (<https://github.com/goofy-bz>)
|
||||
* Jean-Christophe Bach (<https://github.com/jcb>)
|
||||
* Florian Bigard, aka Chocobozzz (<https://github.com/Chocobozzz>)
|
||||
* Sandro Cazzaniga, aka Kharec (<http://sandrocazzaniga.fr>), [@Kharec](https://twitter.com/Kharec)
|
||||
* Laura Arjona Reina (<https://wiki.debian.org/LauraArjona>), spanish translation
|
||||
* Thor77 (<http://thor77.org>), german translation, among other things
|
||||
* Quentin Pagès, occitan translation
|
||||
* Alexis Clairet (<https://github.com/Turboconnard>), paste image to upload ability
|
||||
* ButterflyOfFire (<https://mastodon.tetaneutral.net/@BoF>), arabic translation
|
||||
* Alexander Sapozhnikov (<http://shoorick.ru>), russian translation
|
||||
* Arnaud de Mouhy, Docker support
|
||||
* Armando Lüscher (<https://noplanman.ch/>)
|
||||
266
CHANGELOG
Normal file
266
CHANGELOG
Normal file
@@ -0,0 +1,266 @@
|
||||
Revision history for Lutim
|
||||
|
||||
0.19.0 ????-??-??
|
||||
|
||||
0.18.0 2025-10-13
|
||||
- 🌐 — Update translations
|
||||
- 📦 — Update alpine version by @ploc
|
||||
- 🔧 — Ability to configure MOJO_TMPDIR via environment by @s7b4
|
||||
|
||||
0.17.0 2023-12-30
|
||||
- 🐛 — AVIF format support (#139)
|
||||
- ✨ — Allow to configure the directory where to store the images (#125)
|
||||
- ✨ — Ask for confirmation before deleting image on index page (#92)
|
||||
- ✨ — Mass delete button in "My images" page (#47)
|
||||
- ♿ — Improve accessibility on index page
|
||||
|
||||
0.16.0 2023-12-29
|
||||
- ⬆️ Update jQuery
|
||||
- 💥 BREAKING CHANGE: no more twitter cards
|
||||
- 🎨 — Use template literals in js
|
||||
- 🐛 — Gallery, zip and random URLs are now updated when closing image dialog box
|
||||
— ➖ — Replace moment.js with Date().toLocaleDateString(…)
|
||||
- 🩹 — Replace old URL of the project
|
||||
|
||||
0.15.0 2023-12-19
|
||||
- ✨ — Add --nuke option to image command
|
||||
|
||||
0.14.0 2023-12-18
|
||||
- ⬆️ Update dependencies
|
||||
- 💥 BREAKING CHANGE: Use `?_format=json` instead of `?format=json`
|
||||
- 💥 BREAKING CHANGE: Use `?_format=json` instead of terminating the URL with `.json`
|
||||
|
||||
0.13.0 2023-04-26
|
||||
- 💄 — Add Korrigan theme (Nicolas Frandeboeuf)
|
||||
- 🔥 — Remove zanata stuff
|
||||
- ✨ — Add a config flag to disable API (@b_b)
|
||||
- 🌐 — Update translations
|
||||
|
||||
0.12.1 2020-10-08
|
||||
- ⬆️ Update jQuery
|
||||
|
||||
0.12.0 2020-04-17
|
||||
- Add watermarking feature (#112)
|
||||
|
||||
0.11.6 2019-11-16
|
||||
- Remove the "Support the author" dropdown
|
||||
- Add a "select all" checkbox on /myfiles (#105)
|
||||
- Update arabic translation
|
||||
- Group button links fixed to make the whole button a link (Armando Lüscher)
|
||||
- Docker support (Arnaud de Mouhy)
|
||||
- Bump toastify-js to version 1.4.0 (Armando Lüscher)
|
||||
|
||||
0.11.5 2019-04-19
|
||||
- Revert catching Image::Magick problems
|
||||
- In gallery, use JS to find image's width and height if not provided by image's infos
|
||||
|
||||
0.11.4 2018-11-18
|
||||
- Catch Image::Magick problems
|
||||
- Update arabic translation
|
||||
- Add italian translation
|
||||
|
||||
0.11.3 2018-07-31
|
||||
- Fix gallery bug
|
||||
|
||||
0.11.2 2018-07-31
|
||||
- Fix stats graph if no upload happens in the time range
|
||||
|
||||
0.11.1 2018-07-30
|
||||
- Add upload_enabled info to server infos endpoint
|
||||
- Add log message when failing to render an image
|
||||
|
||||
0.11.0 2018-07-29
|
||||
- Allow to install only needed deps
|
||||
- Notification when copying to clipboard or deleting images
|
||||
- Allow to use memcached as cache system
|
||||
- Use Mojolicious::Plugin::Chi
|
||||
- Gzip static assets with Mojolicious::Plugin::GzipStatic
|
||||
- Fix scroll-to-top when clicking on delete image
|
||||
- Add CSP header
|
||||
- Add X-Content-Type-Options, X-XSS-Protection, X-Frame-Options headers
|
||||
- Remove @framasky as default tweet_card_via setting
|
||||
- Add a message saying how many images there is in the gallery
|
||||
- Use ISO::639_1 for languages' native names
|
||||
- Add CLI command to print informations about images
|
||||
- Add CLI command to remove images
|
||||
- Add CLI command to search images based on the uploader's IP address
|
||||
- Add .zip file check in the tests
|
||||
- Fix stats files place when using non-default theme
|
||||
- Allow to use a fixed domain
|
||||
- Add localStorage export and import feature
|
||||
- Add link to generate random file in collection
|
||||
- Add modal to modify expiration delay from "myfiles" page
|
||||
- Add optional authentication (LDAP or htpasswd file)
|
||||
|
||||
0.10.4 2018-05-07
|
||||
- Fix bug in cache system that would allow someone to view an image with an incorrect decryption key
|
||||
|
||||
0.10.3 2018-04-26
|
||||
- Fix another bug on zip file creation
|
||||
|
||||
0.10.2 2018-04-24
|
||||
- Fix bug on zip file creation
|
||||
|
||||
0.10.1 2018-04-15
|
||||
- Fix bug on theme creation (#73)
|
||||
|
||||
0.10.0 2018-04-07
|
||||
- PostgreSQL performance improvments
|
||||
- Move some tasks to recurring instead of being in after_dispatch hook
|
||||
- Built-in image cache system \o/
|
||||
- Disable logs option
|
||||
- Disable images' counter option
|
||||
|
||||
0.9.6 2018-03-12
|
||||
- Update translations
|
||||
|
||||
0.9.5 2018-03-11
|
||||
- Add russian translation, thanks to Alexander Sapozhnikov
|
||||
|
||||
0.9.4 2018-03-11
|
||||
- Replace Twitter account URL with Mastodon one
|
||||
|
||||
0.9.3 2018-03-11
|
||||
- Use woff2 format for HennyPenny font
|
||||
|
||||
0.9.2 2018-03-09
|
||||
- Fix langage drop-down
|
||||
- Allow to use HTML in broadcast message
|
||||
- Remove old targets from Makefile
|
||||
|
||||
0.9.1 2018-03-09
|
||||
- Fix default setting bug (db_path)
|
||||
|
||||
0.9.0 2018-03-09
|
||||
- Added partial arabic translation (thx to ButterflyOfFire)
|
||||
- Default theme is now non-fluid (ie don't take all the width of the screen)
|
||||
- Use Photoswipe for the gallery instead of Unite gallery
|
||||
- Use Zanata for translations (https://trad.framasoft.org)
|
||||
- Add an option to personnalize proposed retention delays
|
||||
- Use random Initialization Vector for encryption
|
||||
- Use Mojo::SQLite instead of ORLite
|
||||
- Fix various bugs
|
||||
|
||||
0.8.8 2018-02-07
|
||||
- Fix security issues, thanks to SecuNinja
|
||||
|
||||
0.8.7 2017-12-22
|
||||
- Fix bug if dbtype not configured in lutim.conf
|
||||
|
||||
0.8.6 2017-11-18
|
||||
- Fix bug resulting in no EXIF tags deletion
|
||||
|
||||
0.8.5 2017-07-09
|
||||
- Fix Henny Penny font path in css
|
||||
|
||||
0.8.4 2017-06-24
|
||||
- Mitigate a bug using the same empty record twice
|
||||
|
||||
0.8.3 2017-06-15
|
||||
- Fix the donuts charts in the /stats page.
|
||||
|
||||
0.8.2 2017-06-14
|
||||
- Enforce Mojolicious::Plugin::AssetPack version
|
||||
|
||||
0.8.1 2017-06-13
|
||||
- Fix #46
|
||||
|
||||
0.8 2017-06-13
|
||||
- Improve statistics page
|
||||
- Add database abstraction layer (#42)
|
||||
- Add PostgreSQL support (#42)
|
||||
- Asks for Mojolicious 7.31 minimum (to install it: `carton update`)
|
||||
- Add Liberapay and Tipeee buttons
|
||||
- Remove Flattr button
|
||||
- Handle MOJO_CONFIG env variable (#44)
|
||||
- Fix bug #39
|
||||
- Add gallery constructor to "my files" list (#33)
|
||||
- Handle too much images in zip download URL (#27)
|
||||
- LocalStorage is now updated if an image's delay is modified
|
||||
- Allow user to paste image from clipboard to upload images (#14 and !5)
|
||||
- Fix #40
|
||||
- Add stats in JSON format (GET /stats.json)
|
||||
- Add Cache-control headers for static files
|
||||
- Put almost all js/css stuff outside templates
|
||||
- Allow to use Minion to increment counter (#43)
|
||||
|
||||
0.7.1 2016-06-21
|
||||
- Fix dependency bug
|
||||
|
||||
0.7 2016-06-21
|
||||
- Use .po files for internationalization
|
||||
- Add spanish translation (thx to Laura Arjona Reina)
|
||||
- Allow Lutim to be on a sub-directory (like http://example.org/lutim/) (#46 and #57)
|
||||
- Remove deprecated (due to typo) option "provisionning".
|
||||
- Allow to upload svg images (no thumbnail for now, ImageMagick in Debian Jessie don't work with svg) (#47)
|
||||
- Allow to change location of the database (#48)
|
||||
- Autorotate images
|
||||
- Purge images from their EXIF tags (for more privacy)
|
||||
- Provide markdown syntax at upload for using lutim's hosted images
|
||||
- Now using a wiki: makes README lighter (https://framagit.org/luc/lutim/wikis/home)
|
||||
- Update twitter bootstrap
|
||||
- Add "Copy to clipboard" buttons to each link
|
||||
- Add "Copy all links" button
|
||||
- Add file extension to view link
|
||||
- Add list of user's images in localStorage and a page to list the user's images
|
||||
- Use cryptographically secure random generator
|
||||
- Allow to set the encryption length in lutim.conf
|
||||
- Add the possibility of displaying images in a gallery
|
||||
- Add the possibility to download a zip file
|
||||
- bugfixes
|
||||
|
||||
0.6 2014-10-03
|
||||
- Add OpenGraph tags in social page (ex-twitter page)
|
||||
- Update README.md
|
||||
- Update info page
|
||||
|
||||
0.5 2014-09-24
|
||||
- Add support for animated gif in Twitter cards (#45)
|
||||
- Update README.md with Twitter integration informations
|
||||
- bugfixes
|
||||
|
||||
0.4 2014-07-12
|
||||
- Webapp ! Downloadable directly from the Lutim instance
|
||||
- Configure expiration delay after uploading (#12)
|
||||
- Twitter share button in the "upload success" message (#35)
|
||||
|
||||
0.3 2014-06-01
|
||||
- Add a delete link to images (#28)
|
||||
- Concatenated css and js with Mojolicious::Plugin::AssetPack
|
||||
- Antiflood protection for the "Download by URL" feature (#29)
|
||||
- Stats page improved
|
||||
- Self-documented configuration template
|
||||
- Remote port detection can now use the X-Remote-Port header if set
|
||||
- Lutim now uses the X-Forwarded-Proto header to set the scheme to https if needed
|
||||
The "https" option in configuration file is deprecated and will be removed in 0.4
|
||||
- Optionally delete images that are no longer viewed after a configurable delay in order to avoid saturation (#27)
|
||||
- Provide init script
|
||||
- Update Shutter plugin
|
||||
- Small bugfixes
|
||||
|
||||
0.2 2014-03-07
|
||||
- Server-side encryption available
|
||||
- Thumbnails of uploaded images in response
|
||||
- Bugfixes
|
||||
- HTML validity
|
||||
- Stats (via cron stats command)
|
||||
- Anonymize IP in DB after a delay (via cron cleanbdd command)
|
||||
- Watch files directory size (via cron watch command)
|
||||
- Anonymize logs (log only the senders' IP address)
|
||||
- Favicon and logo
|
||||
- Better MIME type detection
|
||||
- Broadcast message on all pages available
|
||||
- File max size configurable
|
||||
- Progress bar
|
||||
- More options for suppression delay
|
||||
- Updated documentation
|
||||
- Cross-domain API
|
||||
- Upload by image URL
|
||||
- Add HTTP headers Expires and Content-Cache
|
||||
|
||||
0.1 2014-02-15
|
||||
- Image viewing link
|
||||
- Image downloading link
|
||||
- Image twitter card link
|
||||
- Shutter Plugin
|
||||
- Configurable "Hosted by" information
|
||||
3
CONTRIBUTING.md
Normal file
3
CONTRIBUTING.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Contributing
|
||||
|
||||
See how to contribute on the [wiki](https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/contribute).
|
||||
28
Changes
28
Changes
@@ -1,28 +0,0 @@
|
||||
Revision history for LUTIm
|
||||
|
||||
0.2 2014-03-07
|
||||
- Server-side encryption available
|
||||
- Thumbnails of uploaded images in response
|
||||
- Bugfixes
|
||||
- HTML validity
|
||||
- Stats (via cron stats command)
|
||||
- Anonymize IP in DB after a delay (via cron cleanbdd command)
|
||||
- Watch files directory size (via cron watch command)
|
||||
- Anonymize logs (log only the senders' IP address)
|
||||
- Favicon and logo
|
||||
- Better MIME type detection
|
||||
- Broadcast message on all pages available
|
||||
- File max size configurable
|
||||
- Progress bar
|
||||
- More options for suppression delay
|
||||
- Updated documentation
|
||||
- Cross-domain API
|
||||
- Upload by image URL
|
||||
- Add HTTP headers Expires and Content-Cache
|
||||
|
||||
0.1 2014-02-15
|
||||
- Image viewing link
|
||||
- Image downloading link
|
||||
- Image twitter card link
|
||||
- Shutter Plugin
|
||||
- Configurable "Hosted by" information
|
||||
48
Dockerfile
Normal file
48
Dockerfile
Normal file
@@ -0,0 +1,48 @@
|
||||
FROM alpine:3.22
|
||||
|
||||
ARG BUILD_DATE
|
||||
ARG VCS_REF
|
||||
ARG VERSION
|
||||
LABEL org.label-schema.build-date=$BUILD_DATE \
|
||||
org.label-schema.name="Lets Upload That Image" \
|
||||
org.label-schema.url="https://lutim.fiat-tux.fr/" \
|
||||
org.label-schema.vcs-ref=$VCS_REF \
|
||||
org.label-schema.vcs-url="https://framagit.org/fiat-tux/hat-softwares/lutim" \
|
||||
org.label-schema.vendor="Luc Didry" \
|
||||
org.label-schema.version=$VERSION \
|
||||
org.label-schema.schema-version="1.0"
|
||||
|
||||
RUN adduser -D lutim \
|
||||
&& addgroup lutim root
|
||||
|
||||
COPY . /home/lutim
|
||||
RUN chmod -R g+rwX /home/lutim
|
||||
|
||||
WORKDIR /home/lutim
|
||||
RUN apk --no-cache add perl~=5 \
|
||||
libpq~=17 \
|
||||
perl-crypt-rijndael~=1 \
|
||||
perl-io-socket-ssl~=2 \
|
||||
perl-net-ssleay~=1 \
|
||||
su-exec~=0.2 \
|
||||
shared-mime-info~=2 \
|
||||
libretls~=3 \
|
||||
imagemagick~=7 \
|
||||
imagemagick-perlmagick~=7 \
|
||||
bash~=~5 \
|
||||
&& apk --no-cache add --virtual .build-deps build-base~=0.5 \
|
||||
perl-utils~=5 \
|
||||
perl-dev~=5 \
|
||||
postgresql17-dev~=17 \
|
||||
vim~=9 \
|
||||
wget~=1 \
|
||||
zlib-dev~=1 \
|
||||
&& cpan notest Carton Config::FromHash \
|
||||
&& carton install --without test \
|
||||
&& apk del .build-deps \
|
||||
&& rm -rf /var/cache/apk/* /root/.cpan*
|
||||
|
||||
USER lutim
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["/bin/sh", "/home/lutim/docker/entrypoint.sh"]
|
||||
70
Makefile
Normal file
70
Makefile
Normal file
@@ -0,0 +1,70 @@
|
||||
EXTRACTDIR=-D lib -D themes/default/templates
|
||||
POT=themes/default/lib/Lutim/I18N/lutim.pot
|
||||
ENPO=themes/default/lib/Lutim/I18N/en.po
|
||||
XGETTEXT=carton exec local/bin/xgettext.pl -u
|
||||
CARTON=carton exec
|
||||
LUTIM=script/lutim
|
||||
REAL_LUTIM=script/application
|
||||
HARNESS_PERL_SWITCHES=-MDevel::Cover=+ignore,local
|
||||
HEAD := $(shell git rev-parse --abbrev-ref HEAD)
|
||||
|
||||
minify:
|
||||
@echo "CSS concatenation"
|
||||
@cd ./themes/default/public/css/ && cat bootstrap.min.css fontello.css hennypenny.css lutim.css toastify.css | csso > common.min.css
|
||||
@cd ./themes/default/public/css/ && cat animation.css uploader.css markdown.css | csso > not_stats.min.css
|
||||
@cd ./themes/default/public/css/ && cat photoswipe.css default-skin/default-skin.css | csso > gallery.min.css
|
||||
|
||||
locales:
|
||||
$(XGETTEXT) $(EXTRACTDIR) -o $(POT) 2>/dev/null
|
||||
$(XGETTEXT) $(EXTRACTDIR) -o $(ENPO) 2>/dev/null
|
||||
|
||||
podcheck:
|
||||
podchecker lib/Lutim/DB/Image.pm
|
||||
|
||||
check-syntax:
|
||||
find lib/ themes/ -name \*.pm -exec $(CARTON) perl -Ilib -c {} \;
|
||||
find t/ -name \*.t -exec $(CARTON) perl -Ilib -c {} \;
|
||||
|
||||
cover:
|
||||
PERL5OPT='-Ilib' $(CARTON) cover --ignore_re '^local'
|
||||
|
||||
test:
|
||||
@PERL5OPT='-Ilib/' HARNESS_PERL_SWITCHES='$(HARNESS_PERL_SWITCHES)' $(CARTON) -- prove -l --failures
|
||||
|
||||
test-junit-output:
|
||||
@PERL5OPT='-Ilib/' HARNESS_PERL_SWITCHES='$(HARNESS_PERL_SWITCHES)' $(CARTON) -- prove -l --failures --formatter TAP::Formatter::JUnit > tap.xml
|
||||
|
||||
full-test: podcheck just-test
|
||||
|
||||
clean:
|
||||
rm -rf lutim.db files/
|
||||
|
||||
dev: minify
|
||||
$(CARTON) morbo $(LUTIM) --listen http://0.0.0.0:3000 --watch lib/ --watch script/ --watch themes/ --watch lutim.conf
|
||||
|
||||
devlog:
|
||||
multitail log/development.log
|
||||
|
||||
prod:
|
||||
$(CARTON) hypnotoad -f $(LUTIM)
|
||||
|
||||
prodlog:
|
||||
multitail log/production.log
|
||||
|
||||
minion:
|
||||
$(CARTON) $(REAL_LUTIM) minion worker
|
||||
|
||||
create-pg-test-db:
|
||||
sudo -u postgres psql -f t/create-pg-testdb.sql
|
||||
|
||||
stats:
|
||||
$(CARTON) $(LUTIM) cron stats -m production
|
||||
|
||||
watch:
|
||||
$(CARTON) $(LUTIM) cron watch -m production
|
||||
|
||||
cleanfiles:
|
||||
$(CARTON) $(LUTIM) cron cleanfiles -m production
|
||||
|
||||
cleanbdd:
|
||||
$(CARTON) $(LUTIM) cron cleanbdd -m production
|
||||
225
README.md
225
README.md
@@ -1,210 +1,71 @@
|
||||
#LUTIm
|
||||
# Lutim
|
||||
|
||||
##What LUTIm means?
|
||||
## What Lutim means?
|
||||
It means Let's Upload That Image.
|
||||
|
||||
##What does it do?
|
||||
It stores images and allows you to see them or download them.
|
||||
Images are indefinitly stored unless you request that they will be deleted at first view or after 24 hours / one week / one month / one year.
|
||||
## What does it do?
|
||||
It stores images and allows you to see them, download them or share them on social networks.
|
||||
|
||||
##License
|
||||
LUTIm is licensed under the terms of the AGPL. See the LICENSE file.
|
||||
Images are indefinitely stored unless you request that they will be deleted at first view or after 24 hours / one week / one month / one year.
|
||||
|
||||
##Official instance
|
||||
You can see it working at http://lut.im.
|
||||
## License
|
||||
Lutim is licensed under the terms of the AGPL. See the LICENSE file.
|
||||
|
||||
##Logo
|
||||
LUTIm's logo is an adaptation of [Lutin](http://commons.wikimedia.org/wiki/File:Lutin_by_godo.jpg) by [Godo](http://godoillustrateur.wordpress.com/), licensed under the terms of the CC-BY-SA 3.0 license.
|
||||
## Logo
|
||||
Lutim's logo is an adaptation of [Lutin](http://commons.wikimedia.org/wiki/File:Lutin_by_godo.jpg) by [Godo](http://godoillustrateur.wordpress.com/), licensed under the terms of the CC-BY-SA 3.0 license.
|
||||
|
||||

|
||||

|
||||
|
||||
##Dependencies
|
||||
* Carton : Perl dependencies manager, it will get what you need, so don't bother for dependencies (but you can read the file `cpanfile` if you want).
|
||||
## Wiki
|
||||
|
||||
```shell
|
||||
sudo cpan Carton
|
||||
The official wiki contains all you need to know about Lutim (installation, API, etc.). Go to <https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/home> or clone it:
|
||||
|
||||
```
|
||||
git clone https://framagit.org/fiat-tux/hat-softwares/lutim.wiki.git
|
||||
```
|
||||
|
||||
or
|
||||
## Encryption
|
||||
|
||||
```shell
|
||||
sudo apt-get install carton
|
||||
```
|
||||
Lutim does encryption on the server if asked to, but does not store the key.
|
||||
|
||||
###Thumbnails dependancy
|
||||
If you want to provide thumbnails of uploaded images, you have to install the *ImageMagick* image manipulation software (<http://www.imagemagick.org/>) and the Image::Magick CPAN module.
|
||||
The encryption is done on the server since Lutim is made to be usable even without javascript. If you want to add client-side encryption for javascript-enabled browsers, patches are welcome.
|
||||
|
||||
On Debian, you can do:
|
||||
```shell
|
||||
sudo apt-get install perlmagick
|
||||
```
|
||||
## Internationalization
|
||||
|
||||
##Installation
|
||||
After installing Carton :
|
||||
```shell
|
||||
git clone https://github.com/ldidry/lutim.git
|
||||
cd lutim
|
||||
carton install
|
||||
cp lutim.conf.template lutim.conf
|
||||
vi lutim.conf
|
||||
```
|
||||
Lutim comes with English, French and Spanish languages. It will choose the language to display from the browser's settings.
|
||||
|
||||
##Configuration
|
||||
* hypnotoad: listen to listen to, user and group which runs hypnotoad ;
|
||||
* contact: write something which make people able to contact you (contact form URL, email address, whatever) ;
|
||||
* secrets: an array of random string. Used by Mojolicious for encrypting session cookies.
|
||||
* piwik\_img: the Piwik image provides you records of visits without javascript (better privacy than js and cookies) ;
|
||||
* length: length of the random string part of image's URL (default is 8) ;
|
||||
* provis\_step: LUTIm provisions random strings for image's URL per pack of `provis_step` (default is 5) ;
|
||||
* provisioning: number of random strings to provision (default is 100) ;
|
||||
* hosted\_by: if someone hosts your LUTIm instance, you can add some HTML (a logo for example) to make it appear on index page ;
|
||||
* tweet\_card\_via: a Twitter account which will appear on Twitter cards ;
|
||||
* max\_file\_size: well, this is explicit (default is 10Mio = 10485760 octets) ;
|
||||
* https: 1 if you want to provide secure images URLs (default is 0) ;
|
||||
* stats\_day\_num: when you generate statistics with `script/lutim cron stats`, you will have stats for the last `stats_day_num` days (default is 365) ;
|
||||
* keep\_ip\_during: when you delete IP addresses of image's senders with `script/lutim cron cleanbdd`, the IP addresses of images older than `keep_ip_during` days will be deleted (default is 365) ;
|
||||
* broadcast\_message: put some string (not HTML) here and this message will be displayed on all LUTIm pages (not in JSON responses) ;
|
||||
* allowed\_domains: array of authorized domains for API calls. Example: `['http://1.example.com', 'http://2.example.com']`. If you want to authorize everyone to use the API: `['*']`.
|
||||
* default\_delay: what is the default time limit for files? Valid values are 0, 1, 7, 30 and 365;
|
||||
* max\_delay: if defined, the images will be deleted after that delay (in days), even if they were uploaded with "no delay" (or value superior to max\_delay) option and a warning message will be displayed on homepage;
|
||||
# always\_encrypt: if set to 1, all images will be encrypted.
|
||||
## Authors
|
||||
|
||||
##Usage
|
||||
```
|
||||
carton exec hypnotoad script/lutim
|
||||
```
|
||||
See [AUTHORS.md](AUTHORS.md) file.
|
||||
|
||||
##Update
|
||||
```
|
||||
git pull
|
||||
carton install
|
||||
carton exec hypnotoad script/lutim
|
||||
```
|
||||
## Contribute!
|
||||
|
||||
Yup, that's all (Mojolicious magic), it will listen at "http://127.0.0.1:8080".
|
||||
Please consider contributing, either by [reporting issues](https://framagit.org/fiat-tux/hat-softwares/lutim/issues) or by helping the [internationalization](https://weblate.framasoft.org/projects/lutim/). And of course, code contribution are welcome!
|
||||
|
||||
For more options (interfaces, user, etc.), change the configuration in `lutim.conf` (have a look at http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad#SETTINGS for the available options).
|
||||
The details on how to contribute are on the [wiki](https://framagit.org/fiat-tux/hat-softwares/lutim/wikis/contribute).
|
||||
|
||||
##Reverse proxy
|
||||
You can use a reverse proxy like Nginx or Varnish (or Apache with the mod\_proxy module). The web is full of tutos.
|
||||
## Make a donation
|
||||
|
||||
Here's a valid *Varnish* configuration:
|
||||
```
|
||||
backend lutim {
|
||||
.host = "127.0.0.1";
|
||||
.port = "8080";
|
||||
}
|
||||
sub vcl_recv {
|
||||
if (req.restarts == 0) {
|
||||
set req.http.X-Forwarded-For = client.ip;
|
||||
}
|
||||
if (req.http.host == "lut.im") {
|
||||
set req.backend = lutim;
|
||||
}
|
||||
}
|
||||
```
|
||||
You can make a donation to the author on [Tipeee](https://www.tipeee.com/fiat-tux) or on [Liberapay](https://liberapay.com/sky/).
|
||||
|
||||
##Cron jobs
|
||||
LUTIm have commands which can be used in cron jobs.
|
||||
## Others projects dependencies
|
||||
|
||||
To see what commands are available:
|
||||
```shell
|
||||
carton exec script/lutim cron
|
||||
```
|
||||
Lutim is written in Perl with the [Mojolicious](http://mojolicio.us) framework.
|
||||
|
||||
###Statistics
|
||||
To generate statistics which can be viewed at the address `/stats` (we need to reload hypnotoad after the stats generation):
|
||||
```shell
|
||||
carton exec script/lutim cron stats && carton exec hypnotoad script/lutim
|
||||
```
|
||||
It uses:
|
||||
|
||||
###Delete IP adresses from database
|
||||
To automatically delete the IP addresses of image's senders after a configurable delay:
|
||||
```shell
|
||||
carton exec script/lutim cron cleanbdd
|
||||
```
|
||||
* [Twitter bootstrap](http://getbootstrap.com) framework to look not too ugly
|
||||
* [JQuery](http://jquery.com) and [JQuery File Uploader](https://github.com/danielm/uploader/) (slightly modified) to add some modernity
|
||||
* [Raphaël](http://raphaeljs.com/) and [morris.js](https://morrisjs.github.io/morris.js/) for stats graphs
|
||||
* [freezeframe.js](http://freezeframe.chrisantonellis.com/) (slightly modified) to be able to freeze animated gifs in twitter card
|
||||
* [Moment.js](http://momentjs.com/) for displaying real dates instead of unix timestamps.
|
||||
* [Fontello](http://fontello.com/) and the [markdown font](https://github.com/dcurtis/markdown-mark/) for the icons, licenses for the fontello icons fonts are in `public/font/LICENSE.txt`
|
||||
* [Henny Penny](https://www.google.com/fonts/specimen/Henny+Penny) font designed by Olga Umpeleva for [Brownfox](http://brownfox.org)
|
||||
* [PhotoSwipe](http://photoswipe.com/) for the gallery
|
||||
* [JSZip](https://stuk.github.io/jszip/) for generating a zip containing all the images in the gallery
|
||||
* [FileSaver.js](https://github.com/eligrey/FileSaver.js/) for saving the zip
|
||||
* [Toastify JS](https://apvarun.github.io/toastify-js/) for notifications
|
||||
|
||||
###Delete expired files
|
||||
To automatically delete files which availability delay is over (when you choose that your image will be deleted after 24h / one week / etc.)
|
||||
```shell
|
||||
carton exec script/lutim cron cleanfiles
|
||||
```
|
||||
## Deploy lutim
|
||||
|
||||
###Watch the size of the files directory
|
||||
To execute an action when the files directory is heavier than `max_total_size`.
|
||||
The available actions are `warn` and `stop-upload`:
|
||||
* `warn` prints a message on the standard out (which is normally mailed to you by `cron`) ;
|
||||
* `stop-upload` prints a message on the standard out and creates the `stop-upload` file which prevents uploading and put a warn on LUTIm interface ;
|
||||
* **DANGEROUS OPTION!!!** `delete` prints a message on the standard out and delete older images until the files directory goes under quota.
|
||||
|
||||
If the files directory go under quota, the `stop-upload` file is deleted. If you want to manually prevents uploading, create a file named `stop-upload.manual`.
|
||||
|
||||
```shell
|
||||
carton exec script/lutim cron watch
|
||||
```
|
||||
|
||||
##Broadcast message
|
||||
Set a string in the `broadcast_message` option of `lutim.conf` and reload the server with:
|
||||
```shell
|
||||
carton exec hypnotoad script/lutim
|
||||
```
|
||||
|
||||
It may take a few reloads of page before the message is displayed.
|
||||
|
||||
##Encryption
|
||||
LUTIm do encryption on the server if asked to, but does not store the key.
|
||||
|
||||
The encryption is made on the server since LUTIm is made to be usable even without javascript. If you want to add client-side encryption for javascript-enabled browsers, patches are welcome.
|
||||
|
||||
##API
|
||||
You can add images by using the API. Here's the parameters of the `POST` request to `/` adress:.
|
||||
* format: json
|
||||
MANDATORY if you want to get a json response, otherwise it will send a web page
|
||||
* file: the image file
|
||||
MANDATORY
|
||||
* delete-day: number of days you want the image to stay
|
||||
OPTIONAL if 0, it will be available undefinitely
|
||||
* first-view: 1
|
||||
OPTIONAL if not 0, the image will be deleted at first view
|
||||
|
||||
|
||||
Exemple with curl:
|
||||
```shell
|
||||
curl -F "format=json" -F "file=@/tmp/snap0001.jpg" http://lut.im
|
||||
```
|
||||
|
||||
You can allow people to use your instance of LUTIm from other domains.
|
||||
Add the allowed domains as an array in the `allowed_domains` conf option. Put '`[*]`' if you want to allow all domains.
|
||||
|
||||
##Shutter integration
|
||||
See where Shutter (<http://en.wikipedia.org/wiki/Shutter_%28software%29>) keeps its plugins on your computer.
|
||||
On my computer, it's in `/usr/share/shutter/resources/system/upload_plugins/upload`.
|
||||
|
||||
Then:
|
||||
```
|
||||
sudo cp utilities/Shutter.pm /usr/share/shutter/resources/system/upload_plugins/upload/Lutim.pm
|
||||
```
|
||||
|
||||
And restart Shutter if it was running.
|
||||
|
||||
Of course, this plugin is configured for the official instance of LUTIm (<http://lut.im>), feel free to edit it for your own instance.
|
||||
|
||||
##Internationalization
|
||||
LUTIm comes with English and French languages. It will choose the language to display from the browser's settings.
|
||||
|
||||
If you want to add more languages, for example German:
|
||||
```shell
|
||||
cd lib/I18N
|
||||
cp en.pm de.pm
|
||||
vim de.pm
|
||||
```
|
||||
|
||||
There's just a few sentences, so it will be quick to translate. Please consider to send me you language file in order to help the other users :smile:.
|
||||
|
||||
##Others projects dependancies
|
||||
LUTIm is written in Perl with the [Mojolicious](http://mojolicio.us) framework, uses the [Twitter bootstrap](http://getbootstrap.com) framework to look not too ugly, [JQuery](http://jquery.com) and [JQuery File Uploader](https://github.com/danielm/uploader/) (slightly modified) to add some modernity, [Raphaël](http://raphaeljs.com/) and [SimpleGraph](http://benaskins.github.io/simplegraph/) for stats graphs.
|
||||
|
||||
##Contributors
|
||||
* Luc Didry, aka Sky (<http://www.fiat-tux.fr>), main developer
|
||||
* Jean-Bernard Marcon, aka Goofy (<https://github.com/goofy-bz>)
|
||||
* Jean-Christophe Bach (<https://github.com/jcb>)
|
||||
An ansible role and a terraform plan reside under the `.provision` directory. An user could utilize the terraform plan if they chose to deploy lutim on AWS, if that's not the goal, they could simply execute the ansible role in part. Usage docs for both are present in their respective directories.
|
||||
|
||||
56
cpanfile
56
cpanfile
@@ -1,12 +1,60 @@
|
||||
requires 'Mojolicious';
|
||||
requires 'Mojolicious', '>= 7.31';
|
||||
requires 'EV';
|
||||
requires 'IO::Socket::SSL';
|
||||
requires 'Net::SSLeay', '>= 1.81';
|
||||
requires 'Data::Validate::URI';
|
||||
requires 'Net::Domain::TLD', '>= 1.75'; # Must have the last version to handle (at least) .xyz and .link
|
||||
requires 'Mojolicious::Plugin::I18N';
|
||||
requires 'ORLite';
|
||||
requires 'File::Type';
|
||||
requires 'Mojolicious::Plugin::DebugDumperHelper';
|
||||
requires 'Mojolicious::Plugin::StaticCache';
|
||||
requires 'Mojolicious::Plugin::GzipStatic';
|
||||
requires 'Mojolicious::Plugin::CSPHeader';
|
||||
requires 'Text::Unidecode';
|
||||
requires 'DateTime';
|
||||
requires 'Filesys::DiskUsage';
|
||||
requires 'Switch';
|
||||
requires 'Data::Validate::URI';
|
||||
requires 'Crypt::CBC';
|
||||
requires 'Crypt::Blowfish';
|
||||
requires 'Digest::MD5';
|
||||
requires 'Locale::Maketext';
|
||||
requires 'Locale::Maketext::Extract';
|
||||
requires 'File::MimeInfo';
|
||||
requires 'IO::Scalar';
|
||||
requires 'Image::ExifTool';
|
||||
requires 'Data::Entropy';
|
||||
requires 'List::MoreUtils', '> 0.33';
|
||||
requires 'Archive::Zip';
|
||||
requires 'ISO::639_1';
|
||||
|
||||
feature 'postgresql', 'PostgreSQL support' => sub {
|
||||
requires 'Mojo::Pg';
|
||||
requires 'Mojolicious::Plugin::PgURLHelper';
|
||||
};
|
||||
feature 'sqlite', 'SQLite support' => sub {
|
||||
requires 'Mojo::SQLite', '>= 3.000';
|
||||
requires 'Minion::Backend::SQLite', '>= 4.001';
|
||||
requires 'DBD::SQLite', '>= 1.66';
|
||||
};
|
||||
feature 'minion', 'Minion support' => sub {
|
||||
requires 'Minion';
|
||||
};
|
||||
feature 'cache', 'Cache system' => sub {
|
||||
requires 'Mojolicious::Plugin::CHI';
|
||||
requires 'Data::Serializer';
|
||||
};
|
||||
feature 'memcached', 'Cache system using Memcached' => sub {
|
||||
requires 'Mojolicious::Plugin::CHI';
|
||||
requires 'CHI::Driver::Memcached';
|
||||
requires 'Cache::Memcached';
|
||||
};
|
||||
feature 'ldap', 'LDAP authentication support' => sub {
|
||||
requires 'Net::LDAP';
|
||||
requires 'Mojolicious::Plugin::Authentication';
|
||||
};
|
||||
feature 'htpasswd', 'Htpasswd authentication support' => sub {
|
||||
requires 'Apache::Htpasswd';
|
||||
requires 'Mojolicious::Plugin::Authentication';
|
||||
};
|
||||
feature 'test' => sub {
|
||||
requires 'Devel::Cover';
|
||||
};
|
||||
|
||||
4825
cpanfile.snapshot
4825
cpanfile.snapshot
File diff suppressed because it is too large
Load Diff
22
docker-compose.dev.yml
Normal file
22
docker-compose.dev.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
version: '3.3'
|
||||
|
||||
services:
|
||||
app_dev:
|
||||
build: .
|
||||
ports:
|
||||
- 8080:8080
|
||||
volumes:
|
||||
- .:/home/lutim
|
||||
command: dev
|
||||
postgres_dev:
|
||||
image: postgres:11.2-alpine
|
||||
environment:
|
||||
POSTGRES_PASSWORD: password
|
||||
POSTGRES_USER: lutim
|
||||
POSTGRES_DB: lutim
|
||||
memcached:
|
||||
image: memcached:1.5-alpine
|
||||
adminer:
|
||||
image: dehy/adminer
|
||||
ports:
|
||||
- 8081:80
|
||||
28
docker-compose.yml
Normal file
28
docker-compose.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
version: '3.3'
|
||||
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
ports:
|
||||
- 8080:8080
|
||||
volumes:
|
||||
- ./lutim.conf:/home/lutim/lutim.conf:ro
|
||||
db:
|
||||
image: postgres:11.2-alpine
|
||||
environment:
|
||||
POSTGRES_PASSWORD: password
|
||||
POSTGRES_USER: lutim
|
||||
POSTGRES_DB: lutim
|
||||
cache:
|
||||
image: memcached:1.5-alpine
|
||||
minion:
|
||||
build: .
|
||||
command: minion
|
||||
volumes:
|
||||
- ./lutim.conf:/home/lutim/lutim.conf:ro
|
||||
minion_db:
|
||||
image: postgres:11.2-alpine
|
||||
environment:
|
||||
POSTGRES_PASSWORD: password
|
||||
POSTGRES_USER: lutim_minion
|
||||
POSTGRES_DB: lutim_minion
|
||||
39
docker-stack.yml
Normal file
39
docker-stack.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
version: '3.3'
|
||||
|
||||
services:
|
||||
app:
|
||||
image: aquinum/lutim
|
||||
configs:
|
||||
- source: lutim.conf
|
||||
target: /home/lutim/lutim.conf
|
||||
uid: '1000'
|
||||
gid: '1000'
|
||||
mode: 0440
|
||||
deploy:
|
||||
replicas: 1
|
||||
db:
|
||||
image: postgres:11.2-alpine
|
||||
environment:
|
||||
POSTGRES_PASSWORD: <changeme>
|
||||
POSTGRES_USER: lutim
|
||||
POSTGRES_DB: lutim
|
||||
cache:
|
||||
image: memcached:1.5-alpine
|
||||
minion:
|
||||
image: aquinum/lutim
|
||||
command: minion
|
||||
configs:
|
||||
- source: lutim.conf
|
||||
target: /home/lutim/lutim.conf
|
||||
uid: '1000'
|
||||
gid: '1000'
|
||||
mode: 0440
|
||||
minion_db:
|
||||
image: mariadb:10.3
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: <changeme>
|
||||
MYSQL_DATABASE: lutim_minion
|
||||
|
||||
configs:
|
||||
lutim.conf:
|
||||
file: ./lutim.conf
|
||||
44
docker/entrypoint.sh
Executable file
44
docker/entrypoint.sh
Executable file
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
cd ~lutim
|
||||
|
||||
if [ "${1:-}" == "dev" ]
|
||||
then
|
||||
echo ""
|
||||
echo ""
|
||||
echo "Container started in dev mode. Connect to the container with the following command:"
|
||||
echo " docker-compose -f docker-compose.dev.yml exec -u root app_dev sh"
|
||||
echo ""
|
||||
echo ""
|
||||
echo "You can then install the build dependencies with this command"
|
||||
echo " sh ~lutim/docker/install-dev-env.sh"
|
||||
|
||||
tail -f /dev/null
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# If MySQL/PostgreSQL, wait for database to be up
|
||||
DB_TYPE=$(perl utilities/read_conf.pl dbtype sqlite)
|
||||
DB_HOST=
|
||||
DB_PORT=
|
||||
if [ "$DB_TYPE" == "postgresql" ]
|
||||
then
|
||||
DB_HOST=$(perl utilities/read_conf.pl pgdb/host db)
|
||||
DB_PORT=$(perl utilities/read_conf.pl pgdb/port 5432)
|
||||
fi
|
||||
if [ -n "$DB_HOST" ] && [ -n "$DB_PORT" ]
|
||||
then
|
||||
while ! nc -vz "${DB_HOST}" "${DB_PORT}"; do
|
||||
echo "Waiting for database..."
|
||||
sleep 1;
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "${1:-}" == "minion" ]
|
||||
then
|
||||
exec carton exec script/application minion worker
|
||||
fi
|
||||
|
||||
exec carton exec hypnotoad -f script/lutim
|
||||
579
lib/Lutim.pm
579
lib/Lutim.pm
@@ -1,245 +1,147 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim;
|
||||
use Mojo::Base 'Mojolicious';
|
||||
use Mojo::Util qw(quote);
|
||||
use LutimModel;
|
||||
use Crypt::CBC;
|
||||
use Mojo::IOLoop;
|
||||
use Lutim::DB::Image;
|
||||
use Lutim::DefaultConfig qw($default_config);
|
||||
|
||||
$ENV{MOJO_TMPDIR} = 'tmp';
|
||||
use vars qw($im_loaded);
|
||||
BEGIN {
|
||||
eval "use Image::Magick";
|
||||
if ($@) {
|
||||
warn "You don't have Image::Magick installed so you won't have thumbnails.";
|
||||
$im_loaded = 0;
|
||||
} else {
|
||||
$im_loaded = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$ENV{MOJO_TMPDIR} = 'tmp' unless (defined $ENV{MOJO_TMPDIR});
|
||||
mkdir($ENV{MOJO_TMPDIR}, 0700) unless (-d $ENV{MOJO_TMPDIR});
|
||||
# This method will run once at server start
|
||||
sub startup {
|
||||
my $self = shift;
|
||||
|
||||
$self->plugin('I18N');
|
||||
$self->{wait_for_it} = {};
|
||||
|
||||
my $config = $self->plugin('Config');
|
||||
push @{$self->commands->namespaces}, 'Lutim::Command';
|
||||
|
||||
# Default values
|
||||
$config->{provisioning} = 100 unless (defined($config->{provisionning}));
|
||||
$config->{provisioning} = 100 unless (defined($config->{provisioning}));
|
||||
$config->{provis_step} = 5 unless (defined($config->{provis_step}));
|
||||
$config->{length} = 8 unless (defined($config->{length}));
|
||||
$config->{always_encrypt} = 0 unless (defined($config->{always_encrypt}));
|
||||
$self->plugin('DebugDumperHelper');
|
||||
|
||||
my $config = $self->plugin('Config', {
|
||||
default => $default_config
|
||||
});
|
||||
|
||||
if ($config->{watermark_path}) {
|
||||
die sprintf('%s does not exist or is not readable.', $config->{watermark_path}) unless -r $config->{watermark_path};
|
||||
my $valid = {
|
||||
center => 1,
|
||||
north => 1,
|
||||
northeast => 1,
|
||||
east => 1,
|
||||
southeast => 1,
|
||||
south => 1,
|
||||
southwest => 1,
|
||||
west => 1,
|
||||
northwest => 1
|
||||
};
|
||||
die sprintf('%s is not a valid value for watermark_placement.', $config->{watermark_placement}) unless $valid->{lc($config->{watermark_placement})};
|
||||
$valid = {
|
||||
'tiling' => 1,
|
||||
'single' => 1,
|
||||
'none' => 1
|
||||
};
|
||||
die sprintf('%s is not a valid value for watermark_default.', $config->{watermark_default}) unless $valid->{lc($config->{watermark_default})};
|
||||
die sprintf('%s is not a valid value for watermark_enforce.', $config->{watermark_enforce}) unless $valid->{lc($config->{watermark_enforce})};
|
||||
}
|
||||
|
||||
if (scalar(@{$config->{memcached_servers}})) {
|
||||
$self->plugin(CHI => {
|
||||
lutim_images_cache => {
|
||||
driver => 'Memcached',
|
||||
servers => $config->{memcached_servers},
|
||||
expires_in => '1 day',
|
||||
expires_on_backend => 1,
|
||||
}
|
||||
});
|
||||
} elsif ($config->{cache_max_size} != 0) {
|
||||
my $cache_max_size = 8 * 1024 * 1024 * $config->{cache_max_size};
|
||||
$self->plugin(CHI => {
|
||||
lutim_images_cache => {
|
||||
driver => 'Memory',
|
||||
global => 1,
|
||||
is_size_aware => 1,
|
||||
max_size => $cache_max_size,
|
||||
expires_in => '1 day'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
die "You need to provide a contact information in lutim.conf !" unless (defined($config->{contact}));
|
||||
|
||||
$ENV{MOJO_MAX_MESSAGE_SIZE} = $config->{max_file_size} if (defined($config->{max_file_size}));
|
||||
$ENV{MOJO_MAX_MESSAGE_SIZE} = $config->{max_file_size};
|
||||
|
||||
$self->secrets($config->{secrets});
|
||||
|
||||
$self->helper(
|
||||
render_file => sub {
|
||||
my $c = shift;
|
||||
my ($filename, $path, $mediatype, $dl, $expires, $nocache, $key) = @_;
|
||||
# Themes handling
|
||||
shift @{$self->renderer->paths};
|
||||
shift @{$self->static->paths};
|
||||
if ($config->{theme} ne 'default') {
|
||||
my $theme = $self->home->rel_file('themes/'.$config->{theme});
|
||||
push @{$self->renderer->paths}, $theme.'/templates' if -d $theme.'/templates';
|
||||
push @{$self->static->paths}, $theme.'/public' if -d $theme.'/public';
|
||||
}
|
||||
push @{$self->renderer->paths}, $self->home->rel_file('themes/default/templates');
|
||||
push @{$self->static->paths}, $self->home->rel_file('themes/default/public');
|
||||
|
||||
$filename = quote($filename);
|
||||
# Internationalization
|
||||
my $lib = $self->home->rel_file('themes/'.$config->{theme}.'/lib');
|
||||
eval qq(use lib "$lib");
|
||||
$self->plugin('I18N');
|
||||
|
||||
my $asset;
|
||||
unless ( -f $path && -r _ ) {
|
||||
$c->app->log->error("Cannot read file [$path]. error [$!]");
|
||||
$c->flash(
|
||||
msg => $c->l('image_not_found')
|
||||
);
|
||||
return 500;
|
||||
}
|
||||
# Static assets gzipping
|
||||
$self->plugin('GzipStatic');
|
||||
|
||||
$mediatype =~ s/x-//;
|
||||
# Headers
|
||||
$self->plugin('Lutim::Plugin::Headers');
|
||||
|
||||
my $headers = Mojo::Headers->new();
|
||||
if ($nocache) {
|
||||
$headers->add('Cache-Control' => 'no-cache');
|
||||
} else {
|
||||
$headers->add('Expires' => $expires);
|
||||
}
|
||||
$headers->add('Content-Type' => $mediatype.';name='.$filename);
|
||||
$headers->add('Content-Disposition' => $dl.';filename='.$filename);
|
||||
$c->res->content->headers($headers);
|
||||
# Helpers
|
||||
$self->plugin('Lutim::Plugin::Helpers');
|
||||
$self->plugin('Lutim::Plugin::Lang');
|
||||
|
||||
$c->app->log->debug($key);
|
||||
if ($key) {
|
||||
$asset = $c->decrypt($key, $path);
|
||||
} else {
|
||||
$asset = Mojo::Asset::File->new(path => $path);
|
||||
}
|
||||
$c->res->content->asset($asset);
|
||||
$headers->add('Content-Length' => $asset->size);
|
||||
# Create directory if needed
|
||||
mkdir($self->config('upload_dir'), 0700) unless (-d $self->config('upload_dir'));
|
||||
die ('The upload directory ('.$self->config('upload_dir').') is not writable') unless (-w $self->config('upload_dir'));
|
||||
|
||||
return $c->rendered(200);
|
||||
# Minion
|
||||
if ($config->{minion}->{enabled}) {
|
||||
$self->config->{minion}->{dbtype} = 'sqlite' unless defined $config->{minion}->{dbtype};
|
||||
if ($config->{minion}->{dbtype} eq 'sqlite') {
|
||||
$config->{minion}->{db_path} = 'minion.db' unless defined $config->{minion}->{db_path};
|
||||
$self->plugin('Minion' => { SQLite => 'sqlite:'.$config->{minion}->{db_path} });
|
||||
} elsif ($config->{minion}->{dbtype} eq 'postgresql') {
|
||||
$self->plugin('PgURLHelper');
|
||||
$self->plugin('Minion' => { Pg => $self->pg_url($config->{minion}->{'pgdb'}) });
|
||||
}
|
||||
);
|
||||
$self->app->minion->add_task(
|
||||
accessed => sub {
|
||||
my $job = shift;
|
||||
my $short = $job->args->[0];
|
||||
my $time = $job->args->[1];
|
||||
|
||||
$self->helper(
|
||||
ip => sub {
|
||||
my $c = shift;
|
||||
|
||||
my $proxy = '';
|
||||
my @x_forward = $c->req->headers->header('X-Forwarded-For');
|
||||
for my $x (@x_forward) {
|
||||
$proxy .= join(', ', @$x);
|
||||
my $img = Lutim::DB::Image->new(app => $job->app, short => $short);
|
||||
$img->accessed($time) if $img->path;
|
||||
}
|
||||
my $ip = ($proxy) ? $proxy : $c->tx->remote_address;
|
||||
return $ip.' Remote port: '.$c->tx->remote_port;
|
||||
}
|
||||
);
|
||||
|
||||
$self->helper(
|
||||
provisioning => sub {
|
||||
my $c = shift;
|
||||
|
||||
# Create some short patterns for provisioning
|
||||
if (LutimModel::Lutim->count('WHERE path IS NULL') < $c->config->{provisioning}) {
|
||||
for (my $i = 0; $i < $c->config->{provis_step}; $i++) {
|
||||
if (LutimModel->begin) {
|
||||
my $short;
|
||||
do {
|
||||
$short= $c->shortener($c->config->{length});
|
||||
} while (LutimModel::Lutim->count('WHERE short = ?', $short) || $short eq 'about' || $short eq 'stats');
|
||||
|
||||
LutimModel::Lutim->create(
|
||||
short => $short,
|
||||
counter => 0,
|
||||
enabled => 1,
|
||||
delete_at_first_view => 0,
|
||||
delete_at_day => 0
|
||||
);
|
||||
LutimModel->commit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$self->helper(
|
||||
shortener => sub {
|
||||
my $c = shift;
|
||||
my $length = shift;
|
||||
|
||||
my @chars = ('a'..'z','A'..'Z','0'..'9');
|
||||
my $result = '';
|
||||
foreach (1..$length) {
|
||||
$result .= $chars[rand scalar(@chars)];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
);
|
||||
|
||||
$self->helper(
|
||||
stop_upload => sub {
|
||||
my $c = shift;
|
||||
|
||||
if (-f 'stop-upload' || -f 'stop-upload.manual') {
|
||||
$c->stash(
|
||||
stop_upload => $c->l('stop_upload', $config->{contact})
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
);
|
||||
|
||||
$self->helper(
|
||||
max_delay => sub {
|
||||
my $c = shift;
|
||||
|
||||
if (defined($c->config->{max_delay})) {
|
||||
my $delay = $c->config->{max_delay};
|
||||
if ($delay >= 0) {
|
||||
return $delay;
|
||||
} else {
|
||||
warn "max_delay set to a negative value. Default to 0."
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
);
|
||||
|
||||
$self->helper(
|
||||
default_delay => sub {
|
||||
my $c = shift;
|
||||
|
||||
if (defined($c->config->{default_delay})) {
|
||||
my $delay = $c->config->{default_delay};
|
||||
if ($delay >= 0) {
|
||||
return $delay;
|
||||
} else {
|
||||
warn "default_delay set to a negative value. Default to 0."
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
);
|
||||
|
||||
$self->helper(
|
||||
is_selected => sub {
|
||||
my $c = shift;
|
||||
my $num = shift;
|
||||
|
||||
return ($num == $c->default_delay) ? 'selected="selected"' : '';
|
||||
}
|
||||
);
|
||||
|
||||
$self->helper(
|
||||
crypt => sub {
|
||||
my $c = shift;
|
||||
my $upload = shift;
|
||||
my $filename = shift;
|
||||
|
||||
my $key = $c->shortener(8);
|
||||
|
||||
my $cipher = Crypt::CBC->new(
|
||||
-key => $key,
|
||||
-cipher => 'Blowfish',
|
||||
-header => 'none',
|
||||
-iv => 'dupajasi'
|
||||
);
|
||||
|
||||
$cipher->start('encrypting');
|
||||
|
||||
my $crypt_asset = Mojo::Asset::File->new;
|
||||
|
||||
$crypt_asset->add_chunk($cipher->crypt($upload->slurp));
|
||||
$crypt_asset->add_chunk($cipher->finish);
|
||||
|
||||
my $crypt_upload = Mojo::Upload->new;
|
||||
$crypt_upload->filename($filename);
|
||||
$crypt_upload->asset($crypt_asset);
|
||||
|
||||
return ($crypt_upload, $key);
|
||||
}
|
||||
);
|
||||
|
||||
$self->helper(
|
||||
decrypt => sub {
|
||||
my $c = shift;
|
||||
my $key = shift;
|
||||
my $file = shift;
|
||||
|
||||
my $cipher = Crypt::CBC->new(
|
||||
-key => $key,
|
||||
-cipher => 'Blowfish',
|
||||
-header => 'none',
|
||||
-iv => 'dupajasi'
|
||||
);
|
||||
|
||||
$cipher->start('decrypting');
|
||||
|
||||
my $decrypt_asset = Mojo::Asset::File->new;
|
||||
|
||||
open(my $f, "<",$file) or die "Unable to read encrypted file: $!";
|
||||
binmode $f;
|
||||
while (read($f, my $buffer,1024)) {
|
||||
$decrypt_asset->add_chunk($cipher->crypt($buffer));
|
||||
}
|
||||
$decrypt_asset->add_chunk($cipher->finish) ;
|
||||
|
||||
return $decrypt_asset;
|
||||
}
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
# Hooks
|
||||
$self->hook(
|
||||
before_dispatch => sub {
|
||||
my $c = shift;
|
||||
$c->stop_upload();
|
||||
|
||||
# API allowed domains
|
||||
if (defined($c->config->{allowed_domains})) {
|
||||
if ($c->config->{allowed_domains}->[0] eq '*') {
|
||||
$c->res->headers->header('Access-Control-Allow-Origin' => '*');
|
||||
@@ -252,15 +154,124 @@ sub startup {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Scheme detection
|
||||
if ((defined($c->req->headers->header('X-Forwarded-Proto')) && $c->req->headers->header('X-Forwarded-Proto') eq 'https') || $c->config->{https}) {
|
||||
$c->req->url->base->scheme('https');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$self->hook(
|
||||
after_dispatch => sub {
|
||||
shift->provisioning();
|
||||
}
|
||||
);
|
||||
# Recurrent tasks
|
||||
Mojo::IOLoop->recurring(5 => sub {
|
||||
my $loop = shift;
|
||||
|
||||
$self->provisioning();
|
||||
|
||||
# Purge expired anti-flood protection
|
||||
my $wait_for_it = $self->{wait_for_it};
|
||||
delete @{$wait_for_it}{grep { time - $wait_for_it->{$_} > $self->config->{anti_flood_delay} } keys %{$wait_for_it}} if (defined($wait_for_it));
|
||||
});
|
||||
|
||||
# Authentication (if configured)
|
||||
if (defined($config->{ldap}) || defined($config->{htpasswd})) {
|
||||
if (defined($config->{ldap})) {
|
||||
require Net::LDAP;
|
||||
}
|
||||
if (defined($config->{htpasswd})) {
|
||||
require Apache::Htpasswd;
|
||||
}
|
||||
die sprintf('Unable to read %s', $config->{htpasswd}) if (defined($config->{htpasswd}) && !-r $config->{htpasswd});
|
||||
$self->plugin('Authentication' =>
|
||||
{
|
||||
autoload_user => 1,
|
||||
session_key => 'Lutim',
|
||||
load_user => sub {
|
||||
my ($c, $username) = @_;
|
||||
|
||||
return $username;
|
||||
},
|
||||
validate_user => sub {
|
||||
my ($c, $username, $password, $extradata) = @_;
|
||||
|
||||
if (defined($c->config('ldap'))) {
|
||||
my $ldap = Net::LDAP->new($c->config->{ldap}->{uri});
|
||||
|
||||
my $mesg;
|
||||
if (defined($c->config->{ldap}->{bind_dn}) && defined($c->config->{ldap}->{bind_pwd})) {
|
||||
# connect to the ldap server using the bind credentials
|
||||
$mesg = $ldap->bind(
|
||||
$c->config->{ldap}->{bind_dn},
|
||||
password => $c->config->{ldap}->{bind_pwd}
|
||||
);
|
||||
} else {
|
||||
# anonymous bind
|
||||
$mesg = $ldap->bind
|
||||
}
|
||||
|
||||
if ($mesg->code) {
|
||||
$c->app->log->info('[LDAP INFO] Authenticated bind failed - Login: '.$c->config->{ldap}->{bind_dn}) if defined($c->config->{ldap}->{bind_dn});
|
||||
$c->app->log->error('[LDAP ERROR] Error on bind: '.$mesg->error);
|
||||
return undef;
|
||||
}
|
||||
|
||||
my $ldap_user_attr = $c->config->{ldap}->{user_attr};
|
||||
my $ldap_user_filter = $c->config->{ldap}->{user_filter};
|
||||
|
||||
# search the ldap database for the user who is trying to login
|
||||
$mesg = $ldap->search(
|
||||
base => $c->config->{ldap}->{user_tree},
|
||||
filter => "(&($ldap_user_attr=$username)$ldap_user_filter)"
|
||||
);
|
||||
|
||||
if ($mesg->code) {
|
||||
$c->app->log->error('[LDAP ERROR] Error on search: '.$mesg->error);
|
||||
return undef;
|
||||
}
|
||||
|
||||
# check to make sure that the ldap search returned at least one entry
|
||||
my @entries = $mesg->entries;
|
||||
my $entry = $entries[0];
|
||||
|
||||
unless (defined $entry) {
|
||||
$c->app->log->info("[LDAP INFO] Authentication failed - User $username filtered out, IP: ".$c->ip);
|
||||
return undef;
|
||||
}
|
||||
|
||||
# retrieve the first user returned by the search
|
||||
$c->app->log->debug("[LDAP DEBUG] Found user dn: ".$entry->dn);
|
||||
|
||||
# Now we know that the user exists
|
||||
$mesg = $ldap->bind($entry->dn,
|
||||
password => $password
|
||||
);
|
||||
|
||||
if ($mesg->code) {
|
||||
$c->app->log->info("[LDAP INFO] Authentication failed - Login: $username, IP: ".$c->ip);
|
||||
$c->app->log->error('[LDAP ERROR] Authentication failed '.$mesg->error);
|
||||
return undef;
|
||||
}
|
||||
|
||||
$c->app->log->info("[LDAP INFO] Authentication successful - Login: $username, IP: ".$c->ip);
|
||||
} elsif (defined($c->config('htpasswd'))) {
|
||||
my $htpasswd = new Apache::Htpasswd(
|
||||
{
|
||||
passwdFile => $c->config('htpasswd'),
|
||||
ReadOnly => 1
|
||||
}
|
||||
);
|
||||
if (!$htpasswd->htCheckPassword($username, $password)) {
|
||||
return undef;
|
||||
}
|
||||
$c->app->log->info("[Simple authentication successful] login: $username, IP: ".$c->ip);
|
||||
}
|
||||
|
||||
return $username;
|
||||
}
|
||||
}
|
||||
);
|
||||
$self->app->sessions->default_expiration($config->{session_duration});
|
||||
}
|
||||
|
||||
$self->defaults(layout => 'default');
|
||||
|
||||
@@ -269,6 +280,14 @@ sub startup {
|
||||
# Router
|
||||
my $r = $self->routes;
|
||||
|
||||
$r->add_condition(authorized => sub {
|
||||
my ($r, $c, $captures) = @_;
|
||||
|
||||
return 1 unless (defined($config->{ldap}) || defined($config->{htpasswd}));
|
||||
|
||||
return $c->is_user_authenticated;
|
||||
});
|
||||
|
||||
$r->options(sub {
|
||||
my $c = shift;
|
||||
$c->res->headers->allow('POST') if (defined($c->config->{allowed_domains}));
|
||||
@@ -276,27 +295,135 @@ sub startup {
|
||||
});
|
||||
|
||||
$r->get('/')->
|
||||
to('Controller#home')->
|
||||
requires('authorized')->
|
||||
to('Image#home')->
|
||||
name('index');
|
||||
$r->get('/')->
|
||||
to('Authent#index');
|
||||
|
||||
|
||||
if (defined $config->{ldap} || defined $config->{htpasswd}) {
|
||||
# Login page
|
||||
$r->get('/login')
|
||||
->to('Authent#index')
|
||||
->name('login');
|
||||
|
||||
# Authentication
|
||||
$r->post('/login')
|
||||
->to('Authent#login');
|
||||
|
||||
# Logout page
|
||||
$r->get('/logout')
|
||||
->to('Authent#log_out')
|
||||
->name('logout');
|
||||
}
|
||||
|
||||
$r->get('/about')->
|
||||
to('Controller#about')->
|
||||
to('Image#about')->
|
||||
name('about');
|
||||
|
||||
$r->get('/infos')->
|
||||
to('Image#infos')->
|
||||
name('infos');
|
||||
|
||||
$r->get('/stats')->
|
||||
to('Controller#stats')->
|
||||
to('Image#stats')->
|
||||
name('stats');
|
||||
|
||||
$r->post('/')->
|
||||
to('Controller#add')->
|
||||
name('add');
|
||||
$r->get('/lang/:l')->
|
||||
to('Image#change_lang')->
|
||||
name('lang');
|
||||
|
||||
$r->get('/:short')->
|
||||
to('Controller#short')->
|
||||
$r->get('/partial/<:file>.<:f>' => sub {
|
||||
my $c = shift;
|
||||
$c->render(
|
||||
template => 'partial/'.$c->param('file'),
|
||||
format => 'js',
|
||||
layout => undef,
|
||||
d => {
|
||||
delay_0 => $c->l('no time limit'),
|
||||
delay_1 => $c->l('24 hours'),
|
||||
delay_365 => $c->l('1 year')
|
||||
}
|
||||
);
|
||||
})->name('partial');
|
||||
|
||||
$r->get('/gallery' => sub {
|
||||
shift->render(
|
||||
template => 'gallery',
|
||||
);
|
||||
})->name('gallery');
|
||||
|
||||
$r->get('/myfiles')->
|
||||
requires('authorized')->
|
||||
name('myfiles');
|
||||
$r->get('/myfiles')->
|
||||
to('Authent#index');
|
||||
|
||||
$r->get('/manifest.webapp')->
|
||||
to('Image#webapp')->
|
||||
name('manifest.webapp');
|
||||
|
||||
$r->get('/zip')
|
||||
->to('Image#zip')
|
||||
->name('zip');
|
||||
|
||||
$r->get('/random')
|
||||
->to('Image#random')
|
||||
->name('random');
|
||||
|
||||
$r->post('/')->
|
||||
requires('authorized')->
|
||||
to('Image#add')->
|
||||
name('add');
|
||||
$r->post('/')->
|
||||
to('Authent#index');
|
||||
|
||||
$r->get('/d/:short/:token')->
|
||||
requires('authorized')->
|
||||
to('Image#delete')->
|
||||
name('delete');
|
||||
$r->get('/d/:short/:token')->
|
||||
to('Authent#index');
|
||||
|
||||
$r->post('/m/:short/:token')->
|
||||
requires('authorized')->
|
||||
to('Image#modify')->
|
||||
name('modify');
|
||||
$r->post('/m/:short/:token')->
|
||||
to('Authent#index');
|
||||
|
||||
$r->post('/c')->
|
||||
requires('authorized')->
|
||||
to('Image#get_counter')->
|
||||
name('counter');
|
||||
$r->post('/c')->
|
||||
to('Authent#index');
|
||||
|
||||
$r->get('/about/<:short>')->
|
||||
to('Image#about_img')->
|
||||
name('about_img');
|
||||
|
||||
$r->get('/about/<:short>.<:f>')->
|
||||
to('Image#about_img')->
|
||||
name('about_img');
|
||||
|
||||
$r->get('/about/:short/<:key>.<:f>')->
|
||||
to('Image#about_img')->
|
||||
name('about_img');
|
||||
|
||||
$r->get('/<:short>.<:f>')->
|
||||
to('Image#short')->
|
||||
name('short');
|
||||
|
||||
$r->get('/:short')->
|
||||
to('Image#short');
|
||||
|
||||
$r->get('/:short/<:key>.<:f>')->
|
||||
to('Image#short');
|
||||
|
||||
$r->get('/:short/:key')->
|
||||
to('Controller#short');
|
||||
to('Image#short');
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
package Mojolicious::Command::cron;
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim::Command::cron;
|
||||
use Mojo::Base 'Mojolicious::Commands';
|
||||
|
||||
has description => 'Execute tasks.';
|
||||
@@ -7,7 +8,7 @@ has hint => <<EOF;
|
||||
See 'script/lutim cron help TASK' for more information on a specific task.
|
||||
EOF
|
||||
has message => sub { shift->extract_usage . "\nCron tasks:\n" };
|
||||
has namespaces => sub { ['Mojolicious::Command::cron'] };
|
||||
has namespaces => sub { ['Lutim::Command::cron'] };
|
||||
|
||||
sub help { shift->run(@_) }
|
||||
|
||||
@@ -17,7 +18,7 @@ sub help { shift->run(@_) }
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Mojolicious::Command::cron - Cron commands
|
||||
Lutim::Command::cron - Cron commands
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
47
lib/Lutim/Command/cron/cleanbdd.pm
Normal file
47
lib/Lutim/Command/cron/cleanbdd.pm
Normal file
@@ -0,0 +1,47 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim::Command::cron::cleanbdd;
|
||||
use Mojo::Base 'Mojolicious::Command';
|
||||
use Mojo::File;
|
||||
use Lutim::DB::Image;
|
||||
use Lutim::DefaultConfig qw($default_config);
|
||||
use FindBin qw($Bin);
|
||||
use File::Spec qw(catfile);
|
||||
|
||||
has description => 'Delete IP addresses from database after configured delay.';
|
||||
has usage => sub { shift->extract_usage };
|
||||
|
||||
sub run {
|
||||
my $c = shift;
|
||||
|
||||
my $cfile = Mojo::File->new($Bin, '..' , 'lutim.conf');
|
||||
if (defined $ENV{MOJO_CONFIG}) {
|
||||
$cfile = Mojo::File->new($ENV{MOJO_CONFIG});
|
||||
unless (-e $cfile->to_abs) {
|
||||
$cfile = Mojo::File->new($Bin, '..', $ENV{MOJO_CONFIG});
|
||||
}
|
||||
}
|
||||
my $config = $c->app->plugin('Config', {
|
||||
file => $cfile,
|
||||
default => $default_config
|
||||
});
|
||||
|
||||
my $separation = time() - $config->{keep_ip_during} * 86400;
|
||||
|
||||
my $dbi = Lutim::DB::Image->new(app => $c->app);
|
||||
|
||||
$dbi->clean_ips_until($separation);
|
||||
}
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Lutim::Command::cron::cleanbdd - Delete IP addresses from database after configured delay
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage: script/lutim cron cleanbdd
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
63
lib/Lutim/Command/cron/cleanfiles.pm
Normal file
63
lib/Lutim/Command/cron/cleanfiles.pm
Normal file
@@ -0,0 +1,63 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim::Command::cron::cleanfiles;
|
||||
use Mojo::Base 'Mojolicious::Command';
|
||||
use Mojo::File;
|
||||
use Lutim::DB::Image;
|
||||
use Lutim::DefaultConfig qw($default_config);
|
||||
use Lutim;
|
||||
use FindBin qw($Bin);
|
||||
use File::Spec qw(catfile);
|
||||
|
||||
has description => 'Delete expired files.';
|
||||
has usage => sub { shift->extract_usage };
|
||||
|
||||
sub run {
|
||||
my $c = shift;
|
||||
|
||||
my $cfile = Mojo::File->new($Bin, '..' , 'lutim.conf');
|
||||
if (defined $ENV{MOJO_CONFIG}) {
|
||||
$cfile = Mojo::File->new($ENV{MOJO_CONFIG});
|
||||
unless (-e $cfile->to_abs) {
|
||||
$cfile = Mojo::File->new($Bin, '..', $ENV{MOJO_CONFIG});
|
||||
}
|
||||
}
|
||||
my $config = $c->app->plugin('Config', {
|
||||
file => $cfile,
|
||||
default => $default_config
|
||||
});
|
||||
|
||||
my $l = Lutim->new;
|
||||
|
||||
my $dbi = Lutim::DB::Image->new(app => $c->app);
|
||||
|
||||
$dbi->get_images_to_clean()->each(
|
||||
sub {
|
||||
my ($img, $num) = @_;
|
||||
$l->app->delete_image($img);
|
||||
}
|
||||
);
|
||||
|
||||
if (defined($config->{delete_no_longer_viewed_files}) && $config->{delete_no_longer_viewed_files} > 0) {
|
||||
my $time = time() - $config->{delete_no_longer_viewed_files} * 86400;
|
||||
$dbi->get_no_longer_viewed_files($time)->each(
|
||||
sub {
|
||||
my ($img, $num) = @_;
|
||||
$l->app->delete_image($img);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Lutim::Command::cron::cleanfiles - Delete expired files
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage: script/lutim cron cleanfiles
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
220
lib/Lutim/Command/cron/stats.pm
Normal file
220
lib/Lutim/Command/cron/stats.pm
Normal file
@@ -0,0 +1,220 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim::Command::cron::stats;
|
||||
use Mojo::Base 'Mojolicious::Command';
|
||||
use Mojo::DOM;
|
||||
use Mojo::Util qw(encode);
|
||||
use Mojo::File;
|
||||
use Mojo::JSON qw(encode_json);
|
||||
use Lutim::DB::Image;
|
||||
use Lutim::DefaultConfig qw($default_config);
|
||||
use DateTime;
|
||||
use FindBin qw($Bin);
|
||||
use File::Spec qw(catfile);
|
||||
use POSIX;
|
||||
|
||||
has description => 'Generate statistics about Lutim.';
|
||||
has usage => sub { shift->extract_usage };
|
||||
|
||||
sub run {
|
||||
my $c = shift;
|
||||
|
||||
my $cfile = Mojo::File->new($Bin, '..' , 'lutim.conf');
|
||||
if (defined $ENV{MOJO_CONFIG}) {
|
||||
$cfile = Mojo::File->new($ENV{MOJO_CONFIG});
|
||||
unless (-e $cfile->to_abs) {
|
||||
$cfile = Mojo::File->new($Bin, '..', $ENV{MOJO_CONFIG});
|
||||
}
|
||||
}
|
||||
my $config = $c->app->plugin('Config', {
|
||||
file => $cfile,
|
||||
default => $default_config
|
||||
});
|
||||
|
||||
my $template = 'themes/'.$config->{theme}.'/templates/data.html.ep.template';
|
||||
unless (-e $template) {
|
||||
$template = 'themes/default/templates/data.html.ep.template';
|
||||
}
|
||||
|
||||
my $stats = {};
|
||||
|
||||
my $text = Mojo::File->new($template)->slurp;
|
||||
my $dom = Mojo::DOM->new($text);
|
||||
my $thead_tr = $dom->at('table thead tr');
|
||||
my $tbody_tr = $dom->at('table tbody tr');
|
||||
my $tbody_t2 = $tbody_tr->next;
|
||||
|
||||
my $separation = time() - $config->{stats_day_num} * 86400;
|
||||
|
||||
my %data;
|
||||
my $img = Lutim::DB::Image->new(app => $c->app);
|
||||
my $sca = $img->select_created_after($separation);
|
||||
|
||||
$stats->{total} = $img->count_not_empty;
|
||||
$stats->{average} = floor($sca->size / $config->{stats_day_num}) if $config->{stats_day_num};
|
||||
$stats->{for_days} = $config->{stats_day_num};
|
||||
|
||||
$sca->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $time = DateTime->from_epoch(epoch => $e->created_at);
|
||||
my ($year, $month, $day) = ($time->year(), $time->month(), $time->day());
|
||||
|
||||
if (defined($data{$year}->{$month}->{$day})) {
|
||||
$data{$year}->{$month}->{$day} += 1;
|
||||
} else {
|
||||
$data{$year}->{$month}->{$day} = 1;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
my $total = $img->count_created_before($separation);
|
||||
if (scalar(keys %data)) {
|
||||
for my $year (sort {$a <=> $b} keys %data) {
|
||||
for my $month (sort {$a <=> $b} keys %{$data{$year}}) {
|
||||
for my $day (sort {$a <=> $b} keys %{$data{$year}->{$month}}) {
|
||||
$thead_tr->append_content(sprintf("<th>%#.2d/%#.2d/%d</th>\n", $day, $month, $year));
|
||||
$tbody_tr->append_content(sprintf("<td>%d</td>\n", $data{$year}->{$month}->{$day}));
|
||||
$total += $data{$year}->{$month}->{$day};
|
||||
$tbody_t2->append_content(sprintf("<td>%d</td>\n", $total));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
my $dt = DateTime->from_epoch(epoch => $separation);
|
||||
$thead_tr->append_content(sprintf("<th>%#.2d/%#.2d/%d</th>\n", $dt->day(), $dt->month(), $dt->year()));
|
||||
$tbody_tr->append_content("<td>0</td>\n");
|
||||
$tbody_t2->append_content(sprintf("<td>%d</td>\n", $total));
|
||||
|
||||
$dt = DateTime->now();
|
||||
$thead_tr->append_content(sprintf("<th>%#.2d/%#.2d/%d</th>\n", $dt->day(), $dt->month(), $dt->year()));
|
||||
$tbody_tr->append_content("<td>0</td>\n");
|
||||
$tbody_t2->append_content(sprintf("<td>%d</td>\n", $total));
|
||||
}
|
||||
|
||||
my $moy = $total / $config->{stats_day_num};
|
||||
|
||||
# Raw datas
|
||||
my $template2 = 'themes/'.$config->{theme}.'/templates/raw.html.ep.template';
|
||||
unless (-e $template2) {
|
||||
$template2 = 'themes/default/templates/raw.html.ep.template';
|
||||
}
|
||||
my $text2 = Mojo::File->new($template2)->slurp;
|
||||
my $dom2 = Mojo::DOM->new($text2);
|
||||
my $raw = $dom2->at('table tbody');
|
||||
my $raw_foot = $dom2->at('table tfoot');
|
||||
my $unlimited_enabled = $img->count_delete_at_day_endis(0, 1);
|
||||
my $unlimited_disabled = $img->count_delete_at_day_endis(0, 0);
|
||||
my $day_enabled = $img->count_delete_at_day_endis(1, 1);
|
||||
my $day_disabled = $img->count_delete_at_day_endis(1, 0);
|
||||
my $week_enabled = $img->count_delete_at_day_endis(7, 1);
|
||||
my $week_disabled = $img->count_delete_at_day_endis(7, 0);
|
||||
my $month_enabled = $img->count_delete_at_day_endis(30, 1);
|
||||
my $month_disabled = $img->count_delete_at_day_endis(30, 0);
|
||||
my $year_enabled = $img->count_delete_at_day_endis(365, 1);
|
||||
my $year_disabled = $img->count_delete_at_day_endis(365, 0);
|
||||
my $year_disabled_in_month = $img->count_delete_at_day_endis(365, 1, time - 335 * 86400);
|
||||
|
||||
$stats->{unlimited} = {
|
||||
enabled => $unlimited_enabled,
|
||||
disabled => $unlimited_disabled
|
||||
};
|
||||
$stats->{day} = {
|
||||
enabled => $day_enabled,
|
||||
disabled => $day_disabled
|
||||
};
|
||||
$stats->{week} = {
|
||||
enabled => $week_enabled,
|
||||
disabled => $week_disabled
|
||||
};
|
||||
$stats->{month} = {
|
||||
enabled => $month_enabled,
|
||||
disabled => $month_disabled
|
||||
};
|
||||
$stats->{year} = {
|
||||
enabled => $year_enabled,
|
||||
disabled => $year_disabled
|
||||
};
|
||||
|
||||
my $year_disabled_in_month_pct = ($year_enabled != 0) ? " (".sprintf('%.2f', 100*$year_disabled_in_month/$year_enabled)."%)" : '';
|
||||
|
||||
$raw->append_content("\n<tr><td><%= \$raw[4] %></td><td>".$unlimited_enabled."</td><td>".$unlimited_disabled."</td><td>ø</td></tr>\n");
|
||||
$raw->append_content("<tr><td><%= \$raw[5] %></td><td>".$day_enabled."</td><td>".$day_disabled."</td><td>".$day_enabled." (100%)</td></tr>\n");
|
||||
$raw->append_content("<tr><td><%= \$raw[6] %></td><td>".$week_enabled."</td><td>".$week_disabled."</td><td>".$week_enabled." (100%)</td></tr>\n");
|
||||
$raw->append_content("<tr><td><%= \$raw[7] %></td><td>".$month_enabled."</td><td>".$month_disabled."</td><td>".$month_enabled." (100%)</td></tr>\n");
|
||||
$raw->append_content("<tr><td><%= \$raw[8] %></td><td>".$year_enabled."</td><td>".$year_disabled."</td><td>".$year_disabled_in_month.$year_disabled_in_month_pct."</td></tr>\n");
|
||||
|
||||
$raw_foot->append_content("\n<tr><td><%= \$raw[9] %></td><td>".($unlimited_enabled + $day_enabled + $week_enabled + $month_enabled + $year_enabled)."</td><td>".($unlimited_disabled + $day_disabled + $week_disabled + $month_disabled + $year_disabled)."</td><td>".($day_enabled + $week_enabled + $month_enabled + $year_disabled_in_month)."</td></tr>\n");
|
||||
|
||||
$dom2 = <<EOF;
|
||||
% my \@raw = (
|
||||
% l('Image delay'),
|
||||
% l('Active images'),
|
||||
% l('Deleted images'),
|
||||
% l('Deleted images in 30 days'),
|
||||
% l('no time limit'),
|
||||
% l('24 hours'),
|
||||
% l('%1 days', 7),
|
||||
% l('%1 days', 30),
|
||||
% l('1 year'),
|
||||
% l('Total')
|
||||
% );
|
||||
$dom2
|
||||
EOF
|
||||
|
||||
my $js = <<EOF;
|
||||
var enabled_donut = {
|
||||
element: 'raw-enabled-holder',
|
||||
data: [
|
||||
{label: "<%= l('no time limit') %>", value: $unlimited_enabled},
|
||||
{label: "<%= l('24 hours') %>", value: $day_enabled},
|
||||
{label: "<%= l('%1 days', 7) %>", value: $week_enabled},
|
||||
{label: "<%= l('%1 days', 30) %>", value: $month_enabled},
|
||||
{label: "<%= l('1 year') %>", value: $year_enabled},
|
||||
],
|
||||
colors: [
|
||||
'#40b489',
|
||||
'#40b9b1',
|
||||
'#40a1be',
|
||||
'#427dc1',
|
||||
'#455ac3',
|
||||
]
|
||||
};
|
||||
var disabled_donut = {
|
||||
element: 'raw-disabled-holder',
|
||||
data: [
|
||||
{label: "<%= l('no time limit') %>", value: $unlimited_disabled},
|
||||
{label: "<%= l('24 hours') %>", value: $day_disabled},
|
||||
{label: "<%= l('%1 days', 7) %>", value: $week_disabled},
|
||||
{label: "<%= l('%1 days', 30) %>", value: $month_disabled},
|
||||
{label: "<%= l('1 year') %>", value: $year_disabled},
|
||||
],
|
||||
colors: [
|
||||
'#40b489',
|
||||
'#40b9b1',
|
||||
'#40a1be',
|
||||
'#427dc1',
|
||||
'#455ac3',
|
||||
]
|
||||
};
|
||||
EOF
|
||||
|
||||
Mojo::File->new('themes/'.$config->{theme}.'/templates/stats.json.ep')->spew(encode_json($stats));
|
||||
Mojo::File->new('themes/'.$config->{theme}.'/templates/data.html.ep')->spew($dom);
|
||||
Mojo::File->new('themes/'.$config->{theme}.'/templates/raw.html.ep')->spew(encode('UTF-8', $dom2));
|
||||
mkdir 'themes/'.$config->{theme}.'/templates/partial/' unless -d 'themes/'.$config->{theme}.'/templates/partial/';
|
||||
Mojo::File->new('themes/'.$config->{theme}.'/templates/partial/raw.js.ep')->spew(encode('UTF-8', $js));
|
||||
}
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Lutim::Command::cron::stats - Stats generator
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage: script/lutim cron stats
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
82
lib/Lutim/Command/cron/watch.pm
Normal file
82
lib/Lutim/Command/cron/watch.pm
Normal file
@@ -0,0 +1,82 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim::Command::cron::watch;
|
||||
use Mojo::Base 'Mojolicious::Command';
|
||||
use Filesys::DiskUsage qw/du/;
|
||||
use Lutim::DB::Image;
|
||||
use Lutim::DefaultConfig qw($default_config);
|
||||
use Lutim;
|
||||
use Mojo::File;
|
||||
use Switch;
|
||||
use FindBin qw($Bin);
|
||||
use File::Spec qw(catfile);
|
||||
|
||||
has description => 'Watch the files directory and take action when over quota';
|
||||
has usage => sub { shift->extract_usage };
|
||||
|
||||
sub run {
|
||||
my $c = shift;
|
||||
|
||||
my $cfile = Mojo::File->new($Bin, '..' , 'lutim.conf');
|
||||
if (defined $ENV{MOJO_CONFIG}) {
|
||||
$cfile = Mojo::File->new($ENV{MOJO_CONFIG});
|
||||
unless (-e $cfile->to_abs) {
|
||||
$cfile = Mojo::File->new($Bin, '..', $ENV{MOJO_CONFIG});
|
||||
}
|
||||
}
|
||||
my $config = $c->app->plugin('Config', {
|
||||
file => $cfile,
|
||||
default => $default_config
|
||||
});
|
||||
|
||||
if (defined($config->{max_total_size})) {
|
||||
my $total = du(qw/files/);
|
||||
|
||||
if ($total > $config->{max_total_size}) {
|
||||
say "[Lutim cron job watch] Files directory is over quota ($total > ".$config->{max_total_size}.")";
|
||||
switch ($config->{policy_when_full}) {
|
||||
case 'warn' {
|
||||
say "[Lutim cron job watch] Please, delete some files or increase quota (".$config->{max_total_size}.")";
|
||||
}
|
||||
case 'stop-upload' {
|
||||
open (my $fh, '>', 'stop-upload') or die ("Couldn't open stop-upload: $!");
|
||||
close($fh);
|
||||
say '[Lutim cron job watch] Uploads are stopped. Delete some images and the stop-upload file to reallow uploads.';
|
||||
}
|
||||
case 'delete' {
|
||||
say '[Lutim cron job watch] Older files are being deleted';
|
||||
my $dbi = Lutim::DB::Image->new(app => $c->app);
|
||||
my $l = Lutim->new;
|
||||
do {
|
||||
$dbi->get_50_oldest()->each(
|
||||
sub {
|
||||
my ($img, $num) = @_;
|
||||
$l->app->delete_image($img);
|
||||
}
|
||||
);
|
||||
} while (du(qw/files/) > $config->{max_total_size});
|
||||
}
|
||||
else {
|
||||
say '[Lutim cron job watch] Unrecognized policy_when_full option: '.$config->{policy_when_full}.'. Aborting.';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unlink 'stop-upload' if (-f 'stop-upload');
|
||||
}
|
||||
} else {
|
||||
say "[Lutim cron job watch] No max_total_size found in the configuration file. Aborting.";
|
||||
}
|
||||
}
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Lutim::Command::cron::watch - Watch the files directory and take action when over quota
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage: script/lutim cron watch
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
234
lib/Lutim/Command/image.pm
Normal file
234
lib/Lutim/Command/image.pm
Normal file
@@ -0,0 +1,234 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim::Command::image;
|
||||
use Mojo::Base 'Mojolicious::Command';
|
||||
use Mojo::Util qw(getopt);
|
||||
use Mojo::Collection 'c';
|
||||
use Lutim::DB::Image;
|
||||
use Lutim::DefaultConfig qw($default_config);
|
||||
use FindBin qw($Bin);
|
||||
use File::Spec qw(catfile);
|
||||
|
||||
has description => 'Manage stored images';
|
||||
has usage => sub { shift->extract_usage };
|
||||
|
||||
my $csv_header = 0;
|
||||
|
||||
sub run {
|
||||
my $c = shift;
|
||||
my @args = @_;
|
||||
|
||||
my $cfile = Mojo::File->new($Bin, '..' , 'lutim.conf');
|
||||
if (defined $ENV{MOJO_CONFIG}) {
|
||||
$cfile = Mojo::File->new($ENV{MOJO_CONFIG});
|
||||
unless (-e $cfile->to_abs) {
|
||||
$cfile = Mojo::File->new($Bin, '..', $ENV{MOJO_CONFIG});
|
||||
}
|
||||
}
|
||||
my $config = $c->app->plugin('Config', {
|
||||
file => $cfile,
|
||||
default => $default_config
|
||||
});
|
||||
|
||||
if (scalar(@{$config->{memcached_servers}})) {
|
||||
$c->app->plugin(CHI => {
|
||||
lutim_images_cache => {
|
||||
driver => 'Memcached',
|
||||
servers => $config->{memcached_servers},
|
||||
expires_in => '1 day',
|
||||
expires_on_backend => 1,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getopt \@args,
|
||||
'i|info=s{1,}' => \my @info,
|
||||
'c|csv' => \my $csv,
|
||||
'r|remove=s{1,}' => \my @remove,
|
||||
'y|yes' => \my $yes,
|
||||
'q|quiet' => \my $quiet,
|
||||
's|search=s' => \my $ip,
|
||||
'n|nuke=s' => \my $nuke,
|
||||
;
|
||||
|
||||
if (scalar @info) {
|
||||
c(@info)->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = get_short($c, $e);
|
||||
print_infos($i, $csv) if $i;
|
||||
}
|
||||
);
|
||||
}
|
||||
if (scalar @remove) {
|
||||
c(@remove)->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = get_short($c, $e);
|
||||
if ($i) {
|
||||
if ($i->enabled) {
|
||||
print_infos($i, 0) unless $quiet;
|
||||
delete_short($c, $i, $yes);
|
||||
} else {
|
||||
say sprintf('The image %s is already disabled', $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
if ($config->{cache_max_size} && !scalar(@{$config->{memcached_servers}})) {
|
||||
say "\nPlease reload Lutim to be sure that the deleted images are not in the cache anymore.";
|
||||
}
|
||||
}
|
||||
if ($ip) {
|
||||
my $u = Lutim::DB::Image->new(app => $c->app)->search_created_by($ip);
|
||||
my @shorts;
|
||||
$u->each(sub {
|
||||
my ($e, $num) = @_;
|
||||
push @shorts, $e->short;
|
||||
print_infos($e, $csv);
|
||||
});
|
||||
say sprintf('%d matching images', $u->size);
|
||||
say sprintf("If you want to delete those images, please do:\n carton exec script/lutim image --remove %s", join(' ', @shorts)) if @shorts;
|
||||
}
|
||||
if ($nuke) {
|
||||
my $i = get_short($c, $nuke);
|
||||
if ($i && $i->created_by) {
|
||||
my $u = Lutim::DB::Image->new(app => $c->app)->search_exact_created_by($i->created_by);
|
||||
my @shorts;
|
||||
say sprintf('%d images created by the same IP address (%s) than image %s', $u->size, $i->created_by, $nuke);
|
||||
my $confirm = ($yes) ? 'yes' : undef;
|
||||
unless (defined $confirm) {
|
||||
printf('Are you sure you want to remove those %d images? [N/y] ', $u->size);
|
||||
$confirm = <STDIN>;
|
||||
chomp $confirm;
|
||||
}
|
||||
if ($confirm =~ m/^y(es)?$/i) {
|
||||
$u->each(sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = get_short($c, $e->short);
|
||||
if ($i) {
|
||||
print_infos($i, $csv);
|
||||
if ($i->enabled) {
|
||||
delete_short($c, $i, 1);
|
||||
} else {
|
||||
say sprintf('The image %s is already disabled', $e->short);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
say 'Answer was not "y" or "yes". Aborting deletion.';
|
||||
}
|
||||
} elsif (! $i->created_by) {
|
||||
say sprintf('Image %s does not contain its creator’s IP address.', $nuke);
|
||||
} else {
|
||||
say sprintf('Sorry, can’t find image %s', $nuke);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub get_short {
|
||||
my $c = shift;
|
||||
my $short = shift;
|
||||
|
||||
my $i = Lutim::DB::Image->new(app => $c->app, short => $short);
|
||||
if ($i->path) {
|
||||
return $i;
|
||||
} else {
|
||||
say sprintf('Sorry, unable to find an image with short = %s', $short);
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
sub print_infos {
|
||||
my $i = shift;
|
||||
my $csv = shift;
|
||||
|
||||
my $msg;
|
||||
|
||||
if ($i) {
|
||||
if ($csv) {
|
||||
if (!$csv_header) {
|
||||
say 'short,path,footprint,enabled,mediatype,filename,counter,delete_at_first_view,delete_at_day,created_at,created_by,last_access_at,width,height';
|
||||
$csv_header = 1;
|
||||
}
|
||||
$msg = '"%s","%s","%s",%d,"%s","%s",%d,"%s",%d,"%s","%s","%s",%d,%d';
|
||||
} else {
|
||||
$msg = <<EOF;
|
||||
%s
|
||||
path : %s
|
||||
footprint : %s
|
||||
enabled : %d
|
||||
mediatype : %s
|
||||
filename : %s
|
||||
counter : %d
|
||||
delete_at_first_view : %d
|
||||
delete_at_day : %d
|
||||
created_at : %s
|
||||
created_by : %s
|
||||
last_access_at : %s
|
||||
width : %d
|
||||
height : %d
|
||||
EOF
|
||||
}
|
||||
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($i->created_at);
|
||||
my $created_at = sprintf('%d-%d-%d %d:%d:%d GMT', $year + 1900, ++$mon, $mday, $hour, $min, $sec);
|
||||
|
||||
my $last_access_at = '';
|
||||
if ($i->last_access_at) {
|
||||
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($i->last_access_at);
|
||||
$last_access_at = sprintf('%d-%d-%d %d:%d:%d GMT', $year + 1900, ++$mon, $mday, $hour, $min, $sec);
|
||||
}
|
||||
say sprintf($msg,
|
||||
$i->short,
|
||||
$i->path,
|
||||
$i->footprint,
|
||||
$i->enabled,
|
||||
$i->mediatype,
|
||||
$i->filename,
|
||||
$i->counter,
|
||||
$i->delete_at_first_view,
|
||||
$i->delete_at_day,
|
||||
$created_at,
|
||||
$i->created_by,
|
||||
$last_access_at,
|
||||
$i->width,
|
||||
$i->height
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sub delete_short {
|
||||
my $c = shift;
|
||||
my $i = shift;
|
||||
my $y = shift;
|
||||
|
||||
my $confirm = ($y) ? 'yes' : undef;
|
||||
unless (defined $confirm) {
|
||||
printf('Are you sure you want to remove this image (%s)? [N/y] ', $i->short);
|
||||
$confirm = <STDIN>;
|
||||
chomp $confirm;
|
||||
}
|
||||
if ($confirm =~ m/^y(es)?$/i) {
|
||||
$c->app->delete_image($i);
|
||||
} else {
|
||||
say 'Answer was not "y" or "yes". Aborting deletion.';
|
||||
}
|
||||
}
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Lutim::Command::image - Manage URL in Lutim's database
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage:
|
||||
carton exec script/lutim image --info <short> <short> [--csv] Print infos about the space-separated images (--csv creates a CSV output)
|
||||
carton exec script/lutim image --remove <short> <short> [--yes] [--quiet] Delete the space-separated images (--yes disables confirmation, --quiet disables informations printing)
|
||||
carton exec script/lutim image --search <ip> Print infos about the images uploaded by this IP (database LIKE, may include images uploaded by other IPs)
|
||||
carton exec script/lutim image --nuke <short> Delete the image and all images sent by the same IP address and print infos about the deleted images
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
|
||||
124
lib/Lutim/Command/theme.pm
Normal file
124
lib/Lutim/Command/theme.pm
Normal file
@@ -0,0 +1,124 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim::Command::theme;
|
||||
use Mojo::Base 'Mojolicious::Commands';
|
||||
use FindBin qw($Bin);
|
||||
use File::Spec qw(catfile cat dir);
|
||||
use File::Path qw(make_path);
|
||||
|
||||
has description => 'Create new theme skeleton.';
|
||||
has usage => sub { shift->extract_usage };
|
||||
has message => sub { shift->extract_usage . "\nCreate new theme skeleton:\n" };
|
||||
has namespaces => sub { ['Lutim::Command::theme'] };
|
||||
|
||||
sub run {
|
||||
my $c = shift;
|
||||
my $name = shift;
|
||||
|
||||
unless (defined $name) {
|
||||
say $c->extract_usage;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $home = File::Spec->catdir($Bin, '..', 'themes', $name);
|
||||
|
||||
unless (-d $home) {
|
||||
|
||||
# Create skeleton
|
||||
mkdir $home;
|
||||
mkdir File::Spec->catdir($home, 'public');
|
||||
make_path(File::Spec->catdir($home, 'templates', 'layouts'));
|
||||
make_path(File::Spec->catdir($home, 'lib', 'Lutim', 'I18N'));
|
||||
|
||||
my $i18n = <<EOF;
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim::I18N;
|
||||
|
||||
use base 'Locale::Maketext';
|
||||
use File::Basename qw/dirname/;
|
||||
use Locale::Maketext::Lexicon {
|
||||
_auto => 1,
|
||||
_decode => 1,
|
||||
_style => 'gettext',
|
||||
'*' => [
|
||||
Gettext => dirname(__FILE__) . '/I18N/*.po',
|
||||
Gettext => \$app_dir . 'themes/default/lib/Lutim/I18N/*.po',
|
||||
]
|
||||
};
|
||||
|
||||
use vars qw(\$app_dir);
|
||||
BEGIN {
|
||||
use Cwd;
|
||||
my \$app_dir = getcwd;
|
||||
}
|
||||
|
||||
1;
|
||||
EOF
|
||||
|
||||
open my $f, '>', File::Spec->catfile($home, 'lib', 'Lutim', 'I18N.pm') or die "Unable to open $home/lib/Lutim/I18N.pm: $!";
|
||||
print $f $i18n;
|
||||
close $f;
|
||||
|
||||
my $makefile = <<EOF;
|
||||
EN=lib/Lutim/I18N/en.po
|
||||
FR=lib/Lutim/I18N/fr.po
|
||||
DE=lib/Lutim/I18N/de.po
|
||||
ES=lib/Lutim/I18N/es.po
|
||||
OC=lib/Lutim/I18N/oc.po
|
||||
AR=lib/Lutim/I18N/ar.po
|
||||
SEDOPTS=-e "s\@SOME DESCRIPTIVE TITLE\@Lutim language file\@" \\
|
||||
-e "s\@YEAR THE PACKAGE'S COPYRIGHT HOLDER\@2015 Luc Didry\@" \\
|
||||
-e "s\@CHARSET\@utf8\@" \\
|
||||
-e "s\@the PACKAGE package\@the Lutim package\@" \\
|
||||
-e '/^\\#\\. (/{N;/\\n\\#\\. (/{N;/\\n.*\\.\\.\\/default\\//{s/\\#\\..*\\n.*\\#\\./\\#. (/g}}}' \\
|
||||
-e '/^\\#\\. (/{N;/\\n.*\\.\\.\\/default\\//{s/\\n/ /}}'
|
||||
SEDOPTS2=-e '/^\\#.*\\.\\.\\/default\\//,+3d'
|
||||
XGETTEXT=carton exec ../../local/bin/xgettext.pl
|
||||
CARTON=carton exec
|
||||
|
||||
locales:
|
||||
\$(XGETTEXT) -D templates -D ../default/templates -o \$(EN) 2>/dev/null
|
||||
\$(XGETTEXT) -D templates -D ../default/templates -o \$(FR) 2>/dev/null
|
||||
\$(XGETTEXT) -D templates -D ../default/templates -o \$(DE) 2>/dev/null
|
||||
\$(XGETTEXT) -D templates -D ../default/templates -o \$(ES) 2>/dev/null
|
||||
\$(XGETTEXT) -D templates -D ../default/templates -o \$(OC) 2>/dev/null
|
||||
\$(XGETTEXT) -D templates -D ../default/templates -o \$(AR) 2>/dev/null
|
||||
sed \$(SEDOPTS) -i \$(EN)
|
||||
sed \$(SEDOPTS2) -i \$(EN)
|
||||
sed \$(SEDOPTS) -i \$(FR)
|
||||
sed \$(SEDOPTS2) -i \$(FR)
|
||||
sed \$(SEDOPTS) -i \$(DE)
|
||||
sed \$(SEDOPTS2) -i \$(DE)
|
||||
sed \$(SEDOPTS) -i \$(ES)
|
||||
sed \$(SEDOPTS2) -i \$(ES)
|
||||
sed \$(SEDOPTS) -i \$(OC)
|
||||
sed \$(SEDOPTS) -i \$(OC)
|
||||
sed \$(SEDOPTS2) -i \$(AR)
|
||||
sed \$(SEDOPTS2) -i \$(AR)
|
||||
EOF
|
||||
|
||||
open $f, '>', File::Spec->catfile($home, 'Makefile') or die "Unable to open $home/Makefile: $!";
|
||||
print $f $makefile;
|
||||
close $f;
|
||||
open $f, '>', File::Spec->catfile($home, '.gitignore') or die "Unable to open $home/.gitignore: $!";
|
||||
print $f "public/packed/\ntemplates/data.html.ep";
|
||||
close $f;
|
||||
} else {
|
||||
say "$name theme already exists. Aborting.";
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Lutim::Command::theme - Create new theme skeleton.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage: script/lutim theme THEME_NAME
|
||||
|
||||
Your new theme will be available in the themes directory.
|
||||
=cut
|
||||
|
||||
1;
|
||||
@@ -1,321 +0,0 @@
|
||||
# vim:set sw=4 ts=4 sts=4 expandtab:
|
||||
package Lutim::Controller;
|
||||
use Mojo::Base 'Mojolicious::Controller';
|
||||
use Mojo::Util qw(url_unescape b64_encode);
|
||||
use DateTime;
|
||||
use File::Type;
|
||||
use Digest::file qw(digest_file_hex);
|
||||
use Text::Unidecode;
|
||||
use Data::Validate::URI qw(is_http_uri is_https_uri);
|
||||
|
||||
use vars qw($im_loaded);
|
||||
BEGIN {
|
||||
eval "use Image::Magick";
|
||||
if ($@) {
|
||||
warn "You don't have Image::Magick installed so you won't have thumbnails.";
|
||||
$im_loaded = 0;
|
||||
} else {
|
||||
$im_loaded = 1;
|
||||
}
|
||||
}
|
||||
|
||||
sub home {
|
||||
my $c = shift;
|
||||
|
||||
$c->render(
|
||||
template => 'index',
|
||||
max_file_size => $c->req->max_message_size
|
||||
);
|
||||
|
||||
|
||||
$c->on(finish => sub {
|
||||
my $c = shift;
|
||||
$c->app->log->info('[HIT] someone visited site index');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sub about {
|
||||
shift->render(template => 'about');
|
||||
}
|
||||
|
||||
sub stats {
|
||||
shift->render(
|
||||
template => 'stats',
|
||||
total => LutimModel::Lutim->count('WHERE path IS NOT NULL')
|
||||
);
|
||||
}
|
||||
|
||||
sub add {
|
||||
my $c = shift;
|
||||
my $upload = $c->param('file');
|
||||
my $file_url = $c->param('lutim-file-url');
|
||||
|
||||
if(!defined($c->stash('stop_upload'))) {
|
||||
if (defined($file_url) && $file_url) {
|
||||
if (is_http_uri($file_url) || is_https_uri($file_url)) {
|
||||
my $ua = Mojo::UserAgent->new;
|
||||
my $tx = $ua->get($file_url => {DNT => 1});
|
||||
if (my $res = $tx->success) {
|
||||
$file_url = url_unescape $file_url;
|
||||
$file_url =~ m#^.*/([^/]*)$#;
|
||||
my $filename = $1;
|
||||
$filename = 'uploaded.image' unless (defined($filename));
|
||||
$filename .= '.image' if (index($filename, '.') == -1);
|
||||
$upload = Mojo::Upload->new(
|
||||
asset => $tx->res->content->asset,
|
||||
filename => $filename
|
||||
);
|
||||
} else {
|
||||
my $msg = $c->l('download_error');
|
||||
if (defined($c->param('format')) && $c->param('format') eq 'json') {
|
||||
return $c->render(
|
||||
json => {
|
||||
success => Mojo::JSON->false,
|
||||
msg => {
|
||||
filename => $file_url,
|
||||
msg => $msg
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$c->flash(msg => $msg);
|
||||
$c->flash(filename => $file_url);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
my $msg = $c->l('no_valid_url');
|
||||
if (defined($c->param('format')) && $c->param('format') eq 'json') {
|
||||
return $c->render(
|
||||
json => {
|
||||
success => Mojo::JSON->false,
|
||||
msg => {
|
||||
filename => $file_url,
|
||||
msg => $msg
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$c->flash(msg => $msg);
|
||||
$c->flash(filename => $file_url);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $ft = File::Type->new();
|
||||
my $mediatype = $ft->mime_type($upload->slurp());
|
||||
|
||||
my $ip = $c->ip;
|
||||
|
||||
my ($msg, $short, $thumb);
|
||||
# Check file type
|
||||
if (index($mediatype, 'image/') >= 0) {
|
||||
# Create directory if needed
|
||||
mkdir('files', 0700) unless (-d 'files');
|
||||
|
||||
if ($c->req->is_limit_exceeded) {
|
||||
$msg = l('file_too_big', $c->req->max_message_size);
|
||||
if (defined($c->param('format')) && $c->param('format') eq 'json') {
|
||||
return $c->render(
|
||||
json => {
|
||||
success => Mojo::JSON->false,
|
||||
msg => $msg
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$c->flash(msg => $msg);
|
||||
$c->flash(filename => $upload->filename);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
}
|
||||
if(LutimModel->begin) {
|
||||
my @records = LutimModel::Lutim->select('WHERE path IS NULL LIMIT 1');
|
||||
if (scalar(@records)) {
|
||||
# Save file and create record
|
||||
my $filename = unidecode($upload->filename);
|
||||
my $ext = ($filename =~ m/([^.]+)$/)[0];
|
||||
my $path = 'files/'.$records[0]->short.'.'.$ext;
|
||||
if ($im_loaded) {
|
||||
my $im = Image::Magick->new;
|
||||
$im->BlobToImage($upload->slurp);
|
||||
$im->Resize(geometry=>'x85');
|
||||
|
||||
$thumb = 'data:'.$mediatype.';base64,';
|
||||
$thumb .= b64_encode $im->ImageToBlob();
|
||||
}
|
||||
my $key;
|
||||
if ($c->param('crypt') || $c->config->{always_encrypt}) {
|
||||
($upload, $key) = $c->crypt($upload, $filename);
|
||||
}
|
||||
$upload->move_to($path);
|
||||
$records[0]->update(
|
||||
path => $path,
|
||||
filename => $filename,
|
||||
mediatype => $mediatype,
|
||||
footprint => digest_file_hex($path, 'SHA-512'),
|
||||
enabled => 1,
|
||||
delete_at_day => ($c->param('delete-day')) ? $c->param('delete-day') : $c->max_delay,
|
||||
delete_at_first_view => ($c->param('first-view')) ? 1 : 0,
|
||||
created_at => time(),
|
||||
created_by => $ip
|
||||
);
|
||||
|
||||
# Log image creation
|
||||
$c->app->log->info('[CREATION] '.$c->ip.' pushed '.$filename.' (path: '.$path.')');
|
||||
|
||||
# Give url to user
|
||||
$short = $records[0]->short;
|
||||
$short .= '/'.$key if (defined($key));
|
||||
} else {
|
||||
# Houston, we have a problem
|
||||
$msg = $c->l('no_more_short', $c->config->{contact});
|
||||
}
|
||||
}
|
||||
LutimModel->commit;
|
||||
} else {
|
||||
$msg = $c->l('no_valid_file', $upload->filename);
|
||||
}
|
||||
|
||||
if (defined($c->param('format')) && $c->param('format') eq 'json') {
|
||||
if (defined($short)) {
|
||||
$msg = {
|
||||
filename => $upload->filename,
|
||||
short => $short,
|
||||
thumb => $thumb
|
||||
};
|
||||
} else {
|
||||
$msg = {
|
||||
filename => $upload->filename,
|
||||
msg => $msg
|
||||
};
|
||||
}
|
||||
return $c->render(
|
||||
json => {
|
||||
success => (defined($short)) ? Mojo::JSON->true : Mojo::JSON->false,
|
||||
msg => $msg
|
||||
}
|
||||
);
|
||||
} else {
|
||||
if ((defined($msg))) {
|
||||
$c->flash(msg => $msg);
|
||||
$c->flash(filename => $upload->filename);
|
||||
return $c->redirect_to('/');
|
||||
} else {
|
||||
$c->stash(short => $short) if (defined($short));
|
||||
$c->stash(thumb => $thumb);
|
||||
$c->stash(filename => $upload->filename);
|
||||
return $c->render(
|
||||
template => 'index',
|
||||
max_file_size => $c->req->max_message_size
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (defined($c->param('format')) && $c->param('format') eq 'json') {
|
||||
return $c->render(
|
||||
json => {
|
||||
success => Mojo::JSON->false,
|
||||
msg => {
|
||||
filename => $upload->filename,
|
||||
msg => $c->stash('stop_upload')
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$c->flash(msg => $c->stash('stop_upload'));
|
||||
$c->flash(filename => $upload->filename);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub short {
|
||||
my $c = shift;
|
||||
my $short = $c->param('short');
|
||||
my $touit = $c->param('t');
|
||||
my $key = $c->param('key');
|
||||
my $dl = (defined($c->param('dl'))) ? 'attachment' : 'inline';
|
||||
|
||||
my @images = LutimModel::Lutim->select('WHERE short = ? AND ENABLED = 1 AND path IS NOT NULL', $short);
|
||||
|
||||
if (scalar(@images)) {
|
||||
if($images[0]->delete_at_day && $images[0]->created_at + $images[0]->delete_at_day * 86400 <= time()) {
|
||||
# Log deletion
|
||||
$c->app->log->info('[DELETION] someone tried to view '.$images[0]->filename.' but it has been removed by expiration (path: '.$images[0]->path.')');
|
||||
|
||||
# Delete image
|
||||
unlink $images[0]->path();
|
||||
$images[0]->update(enabled => 0);
|
||||
|
||||
# Warn user
|
||||
$c->flash(
|
||||
msg => $c->l('image_not_found')
|
||||
);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
|
||||
my $test;
|
||||
if (defined($touit)) {
|
||||
$test = 1;
|
||||
my $short = $images[0]->short;
|
||||
$short .= '/'.$key if (defined($key));
|
||||
return $c->render(
|
||||
template => 'twitter',
|
||||
layout => undef,
|
||||
short => $short,
|
||||
filename => $images[0]->filename
|
||||
);
|
||||
} else {
|
||||
my $expires = ($images[0]->delete_at_day) ? $images[0]->delete_at_day : 360;
|
||||
my $dt = DateTime->from_epoch( epoch => $expires * 86400 + $images[0]->created_at);
|
||||
$dt->set_time_zone('GMT');
|
||||
$expires = $dt->strftime("%a, %d %b %Y %H:%M:%S GMT");
|
||||
|
||||
$test = $c->render_file($images[0]->filename, $images[0]->path, $images[0]->mediatype, $dl, $expires, $images[0]->delete_at_first_view, $key);
|
||||
}
|
||||
|
||||
if ($test != 500) {
|
||||
# Update counter
|
||||
$c->on(finish => sub {
|
||||
# Log access
|
||||
$c->app->log->info('[VIEW] someone viewed '.$images[0]->filename.' (path: '.$images[0]->path.')');
|
||||
|
||||
# Update record
|
||||
my $counter = $images[0]->counter + 1;
|
||||
$images[0]->update(counter => $counter);
|
||||
|
||||
$images[0]->update(last_access_at => time());
|
||||
|
||||
# Delete image if needed
|
||||
if ($images[0]->delete_at_first_view) {
|
||||
# Log deletion
|
||||
$c->app->log->info('[DELETION] someone made '.$images[0]->filename.' removed (path: '.$images[0]->path.')');
|
||||
|
||||
# Delete image
|
||||
unlink $images[0]->path();
|
||||
$images[0]->update(enabled => 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@images = LutimModel::Lutim->select('WHERE short = ? AND ENABLED = 0 AND path IS NOT NULL', $short);
|
||||
|
||||
if (scalar(@images)) {
|
||||
# Log access try
|
||||
$c->app->log->info('[NOT FOUND] someone tried to view '.$short.' but it does\'nt exist.');
|
||||
|
||||
# Warn user
|
||||
$c->flash(
|
||||
msg => $c->l('image_not_found')
|
||||
);
|
||||
return $c->redirect_to('/');
|
||||
} else {
|
||||
# Image never existed
|
||||
$c->render_not_found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
76
lib/Lutim/Controller/Authent.pm
Normal file
76
lib/Lutim/Controller/Authent.pm
Normal file
@@ -0,0 +1,76 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim::Controller::Authent;
|
||||
use Mojo::Base 'Mojolicious::Controller';
|
||||
|
||||
sub index {
|
||||
my $c = shift;
|
||||
if ($c->is_user_authenticated) {
|
||||
$c->redirect_to('index');
|
||||
} else {
|
||||
$c->render(template => 'login');
|
||||
}
|
||||
}
|
||||
|
||||
sub login {
|
||||
my $c = shift;
|
||||
my $login = $c->param('login');
|
||||
my $pwd = $c->param('password');
|
||||
my $ref = $c->req->headers->referrer;
|
||||
|
||||
if($c->authenticate($login, $pwd)) {
|
||||
$c->respond_to(
|
||||
json => sub {
|
||||
my $c = shift;
|
||||
$c->render(
|
||||
json => {
|
||||
success => Mojo::JSON->true,
|
||||
msg => $c->l('You have been successfully logged in.')
|
||||
}
|
||||
);
|
||||
},
|
||||
any => sub {
|
||||
$c->redirect_to($ref);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
my $msg = $c->l('Please, check your credentials: unable to authenticate.');
|
||||
$c->respond_to(
|
||||
json => sub {
|
||||
my $c = shift;
|
||||
$c->render(
|
||||
json => {
|
||||
success => Mojo::JSON->false,
|
||||
msg => $msg
|
||||
}
|
||||
);
|
||||
},
|
||||
any => sub {
|
||||
$c->stash(msg => $msg);
|
||||
$c->render(template => 'login')
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sub log_out {
|
||||
my $c = shift;
|
||||
if ($c->is_user_authenticated) {
|
||||
$c->logout;
|
||||
}
|
||||
$c->respond_to(
|
||||
json => sub {
|
||||
my $c = shift;
|
||||
$c->render(
|
||||
json => {
|
||||
success => Mojo::JSON->true,
|
||||
msg => $c->l('You have been successfully logged out.')
|
||||
}
|
||||
);
|
||||
},
|
||||
any => sub {
|
||||
$c->render(template => 'logout');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
||||
864
lib/Lutim/Controller/Image.pm
Normal file
864
lib/Lutim/Controller/Image.pm
Normal file
@@ -0,0 +1,864 @@
|
||||
# vim:set sw=4 ts=4 sts=4 expandtab:
|
||||
package Lutim::Controller::Image;
|
||||
use Mojo::Asset::Memory;
|
||||
use Mojo::Base 'Mojolicious::Controller';
|
||||
use Mojo::File qw(path);
|
||||
use Mojo::Util qw(url_escape url_unescape b64_encode encode);
|
||||
use Mojo::JSON qw(true false);
|
||||
use Lutim::DB::Image;
|
||||
use DateTime;
|
||||
use Digest::file qw(digest_file_hex);
|
||||
use Text::Unidecode;
|
||||
use Data::Validate::URI qw(is_http_uri is_https_uri);
|
||||
use File::MimeInfo::Magic qw(mimetype extensions);
|
||||
use IO::Scalar;
|
||||
use Image::ExifTool;
|
||||
use Archive::Zip qw( :ERROR_CODES :CONSTANTS );
|
||||
use Data::Entropy qw(entropy_source);
|
||||
|
||||
use vars qw($im_loaded);
|
||||
BEGIN {
|
||||
eval "use Image::Magick";
|
||||
if ($@) {
|
||||
warn "You don't have Image::Magick installed so you won't have thumbnails.";
|
||||
$im_loaded = 0;
|
||||
} else {
|
||||
$im_loaded = 1;
|
||||
}
|
||||
}
|
||||
|
||||
sub home {
|
||||
my $c = shift;
|
||||
|
||||
$c->render(
|
||||
template => 'index',
|
||||
max_file_size => $c->req->max_message_size
|
||||
);
|
||||
|
||||
|
||||
$c->on(finish => sub {
|
||||
my $c = shift;
|
||||
$c->app->log->info('[HIT] someone visited site index') unless $c->config('quiet_logs');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sub about {
|
||||
shift->render(template => 'about');
|
||||
}
|
||||
|
||||
sub change_lang {
|
||||
my $c = shift;
|
||||
my $l = $c->param('l');
|
||||
|
||||
$c->cookie(lutim_lang => $l, { path => $c->config('prefix') });
|
||||
|
||||
if ($c->req->headers->referrer) {
|
||||
return $c->redirect_to($c->req->headers->referrer);
|
||||
} else {
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
}
|
||||
|
||||
sub stats {
|
||||
my $c = shift;
|
||||
|
||||
my $img = Lutim::DB::Image->new(app => $c);
|
||||
$c->render(
|
||||
template => 'stats',
|
||||
total => $img->count_not_empty
|
||||
);
|
||||
}
|
||||
|
||||
sub infos {
|
||||
my $c = shift;
|
||||
|
||||
$c->render(
|
||||
json => {
|
||||
broadcast_message => $c->config('broadcast_message'),
|
||||
image_magick => ($im_loaded) ? true : false,
|
||||
contact => $c->config('contact'),
|
||||
max_file_size => $c->config('max_file_size'),
|
||||
default_delay => $c->config('default_delay'),
|
||||
max_delay => $c->config('max_delay'),
|
||||
always_encrypt => ($c->config('always_encrypt')) ? true : false,
|
||||
upload_enabled => ($c->app->stop_upload()) ? false : true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sub about_img {
|
||||
my $c = shift;
|
||||
my $short = $c->param('short');
|
||||
|
||||
my $image = Lutim::DB::Image->new(app => $c->app, short => $short);
|
||||
if ($image->enabled && $image->path) {
|
||||
return $c->render(
|
||||
json => {
|
||||
success => true,
|
||||
data => {
|
||||
width => $image->width,
|
||||
height => $image->height,
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
return $c->render(
|
||||
json => {
|
||||
success => false,
|
||||
msg => $c->l('Unable to find the image %1.', $short)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sub webapp {
|
||||
my $c = shift;
|
||||
|
||||
my $headers = Mojo::Headers->new();
|
||||
$headers->add('Content-Type' => 'application/x-web-app-manifest+json');
|
||||
$c->res->content->headers($headers);
|
||||
|
||||
$c->render(
|
||||
template => 'manifest',
|
||||
format => 'webapp'
|
||||
);
|
||||
}
|
||||
|
||||
sub get_counter {
|
||||
my $c = shift;
|
||||
my $short = $c->param('short');
|
||||
my $token = $c->param('token');
|
||||
|
||||
my $img = Lutim::DB::Image->new(app => $c->app, short => $short);
|
||||
if (defined($img->mod_token) && $img->mod_token eq $token) {
|
||||
return $c->render(
|
||||
json => {
|
||||
success => true,
|
||||
counter => $img->counter,
|
||||
enabled => ($img->enabled) ? true : false
|
||||
}
|
||||
);
|
||||
}
|
||||
$c->render(
|
||||
json => {
|
||||
success => false,
|
||||
msg => $c->l('Unable to get counter')
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sub modify {
|
||||
my $c = shift;
|
||||
my $short = $c->param('short');
|
||||
my $token = $c->param('token');
|
||||
my $url = $c->param('url');
|
||||
|
||||
my $image = Lutim::DB::Image->new(app => $c->app, short => $short);
|
||||
if ($image->path) {
|
||||
my $msg;
|
||||
if ($image->mod_token ne $token || $token eq '') {
|
||||
$msg = $c->l('The delete token is invalid.');
|
||||
} else {
|
||||
$c->app->log->info('[MODIFICATION] someone modify '.$image->filename.' with token method (path: '.$image->path.')') unless $c->config('quiet_logs');
|
||||
|
||||
$image->delete_at_day(($c->param('delete-day') && ($c->param('delete-day') <= $c->max_delay || $c->max_delay == 0)) ? $c->param('delete-day') : $c->max_delay);
|
||||
$image->delete_at_first_view(($c->param('first-view')) ? 1 : 0);
|
||||
$image->write;
|
||||
|
||||
$msg = $c->l('The image’s delay has been successfully modified');
|
||||
if (defined($c->param('format')) && $c->param('format') eq 'json') {
|
||||
return $c->render(
|
||||
json => {
|
||||
success => Mojo::JSON->true,
|
||||
msg => $msg
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$msg .= ' (<a href="'.$url.'">'.$url.'</a>)' unless (!defined($url));
|
||||
$c->flash(
|
||||
success => $msg
|
||||
);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
}
|
||||
|
||||
if (defined($c->param('format')) && $c->param('format') eq 'json') {
|
||||
return $c->render(
|
||||
json => {
|
||||
success => Mojo::JSON->false,
|
||||
msg => $msg
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$c->flash(
|
||||
msg => $msg
|
||||
);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
} else {
|
||||
$c->app->log->info('[UNSUCCESSFUL] someone tried to modify '.$short.' but it doesn’t exist.') unless $c->config('quiet_logs');
|
||||
|
||||
# Image never existed
|
||||
my $msg = $c->l('Unable to find the image %1.', $short);
|
||||
if (defined($c->param('format')) && $c->param('format') eq 'json') {
|
||||
return $c->render(
|
||||
json => {
|
||||
success => Mojo::JSON->false,
|
||||
msg => $msg
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$c->flash(
|
||||
msg => $msg
|
||||
);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub delete {
|
||||
my $c = shift;
|
||||
my $short = $c->param('short');
|
||||
my $token = $c->param('token');
|
||||
|
||||
my $image = Lutim::DB::Image->new(app => $c->app, short => $short);
|
||||
if ($image->path) {
|
||||
my $msg;
|
||||
if ($image->mod_token ne $token || $token eq '') {
|
||||
$msg = $c->l('The delete token is invalid.');
|
||||
} elsif ($image->enabled() == 0) {
|
||||
$msg = $c->l('The image %1 has already been deleted.', $image->filename);
|
||||
} else {
|
||||
$c->app->log->info('[DELETION] someone made '.$image->filename.' removed with token method (path: '.$image->path.')') unless $c->config('quiet_logs');
|
||||
|
||||
$c->delete_image($image);
|
||||
return $c->respond_to(
|
||||
json => {
|
||||
json => {
|
||||
success => true,
|
||||
msg => $c->l('The image %1 has been successfully deleted', $image->filename)
|
||||
}
|
||||
},
|
||||
any => sub {
|
||||
$c->flash(
|
||||
success => $c->l('The image %1 has been successfully deleted', $image->filename)
|
||||
);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return $c->respond_to(
|
||||
json => {
|
||||
json => {
|
||||
success => false,
|
||||
msg => $msg
|
||||
}
|
||||
},
|
||||
any => sub {
|
||||
$c->flash(
|
||||
msg => $msg
|
||||
);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$c->app->log->info('[UNSUCCESSFUL] someone tried to delete '.$short.' but it doesn’t exist.') unless $c->config('quiet_logs');
|
||||
|
||||
# Image never existed
|
||||
return $c->respond_to(
|
||||
json => {
|
||||
json => {
|
||||
success => false,
|
||||
msg => $c->l('Unable to find the image %1.', $short)
|
||||
}
|
||||
},
|
||||
any => sub {
|
||||
$c->helpers->reply->not_found;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sub add {
|
||||
my $c = shift;
|
||||
my $upload = $c->param('file');
|
||||
my $file_url = $c->param('lutim-file-url');
|
||||
my $keep_exif = $c->param('keep-exif');
|
||||
my $wm = $c->param('watermark');
|
||||
|
||||
if ($c->config('disable_api')) {
|
||||
my $unauthorized_api = (!defined($c->req->headers->referrer) || Mojo::URL->new($c->req->headers->referrer)->host ne Mojo::URL->new('https://'.$c->req->headers->host)->host);
|
||||
if ($unauthorized_api) {
|
||||
my $msg = $c->l('Sorry, the API is disabled');
|
||||
$c->app->log->info('Blocked API call for '.$c->ip(1));
|
||||
return $c->respond_to(
|
||||
json => { json => { success => Mojo::JSON->false, msg => $msg } },
|
||||
any => sub {
|
||||
shift->render(
|
||||
template => 'index',
|
||||
msg => $msg,
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
if(!defined($c->stash('stop_upload'))) {
|
||||
if (defined($file_url) && $file_url) {
|
||||
if (is_http_uri($file_url) || is_https_uri($file_url)) {
|
||||
# Anti-flood protection
|
||||
my $ip = $c->ip(1);
|
||||
while (defined($c->app->{wait_for_it}->{$ip}) && (time - $c->app->{wait_for_it}->{$ip}) <= $c->config->{anti_flood_delay} ) {
|
||||
sleep($c->config->{anti_flood_delay});
|
||||
}
|
||||
my $ua = Mojo::UserAgent->new;
|
||||
my $res = $ua->get($file_url => {DNT => 1})->result;
|
||||
if ($res->is_success) {
|
||||
$file_url = url_unescape $file_url;
|
||||
$file_url =~ m#^.*/([^/?]*)\??.*$#;
|
||||
my $filename = $1;
|
||||
$filename = 'uploaded.image' unless (defined($filename));
|
||||
$filename .= '.image' if (index($filename, '.') == -1);
|
||||
$upload = Mojo::Upload->new(
|
||||
asset => $res->content->asset,
|
||||
filename => $filename
|
||||
);
|
||||
$c->app->{wait_for_it}->{$ip} = time;
|
||||
} elsif ($res->is_limit_exceeded) {
|
||||
my $msg = $c->l('The file exceed the size limit (%1)', $res->max_message_size);
|
||||
if (defined($c->param('format')) && $c->param('format') eq 'json') {
|
||||
return $c->render(
|
||||
json => {
|
||||
success => Mojo::JSON->false,
|
||||
msg => {
|
||||
filename => $file_url,
|
||||
msg => $msg
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$c->flash(msg => $msg);
|
||||
$c->flash(filename => $upload->filename);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
} else {
|
||||
my $msg = $c->l('An error occured while downloading the image.');
|
||||
$c->app->log->warn('[DOWNLOAD ERROR]'.$c->dumper($res->message));
|
||||
if (defined($c->param('format')) && $c->param('format') eq 'json') {
|
||||
return $c->render(
|
||||
json => {
|
||||
success => Mojo::JSON->false,
|
||||
msg => {
|
||||
filename => $file_url,
|
||||
msg => $msg
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$c->flash(msg => $msg);
|
||||
$c->flash(filename => $file_url);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
my $msg = $c->l('The URL is not valid.');
|
||||
if (defined($c->param('format')) && $c->param('format') eq 'json') {
|
||||
return $c->render(
|
||||
json => {
|
||||
success => Mojo::JSON->false,
|
||||
msg => {
|
||||
filename => $file_url,
|
||||
msg => $msg
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$c->flash(msg => $msg);
|
||||
$c->flash(filename => $file_url);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $io_scalar = new IO::Scalar \$upload->slurp();
|
||||
my $mediatype = mimetype($io_scalar);
|
||||
|
||||
my ($ext) = ($upload->filename =~ m/.*\.(.*)$/);
|
||||
|
||||
my $ip = $c->ip;
|
||||
|
||||
my ($msg, $short, $real_short, $token, $thumb, $limit, $created);
|
||||
# Check file type
|
||||
if (index($mediatype, 'image/') >= 0) {
|
||||
if ($c->req->is_limit_exceeded) {
|
||||
$msg = $c->l('The file exceed the size limit (%1)', $c->req->max_message_size);
|
||||
if (defined($c->param('format')) && $c->param('format') eq 'json') {
|
||||
return $c->render(
|
||||
json => {
|
||||
success => Mojo::JSON->false,
|
||||
msg => $msg
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$c->flash(msg => $msg);
|
||||
$c->flash(filename => $upload->filename);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
}
|
||||
my $record = Lutim::DB::Image->new(app => $c->app)->select_empty;
|
||||
if ($record->short) {
|
||||
# Save file and create record
|
||||
my $filename = unidecode($upload->filename);
|
||||
my $ext = ($filename =~ m/([^.]+)$/)[0];
|
||||
my $path = path($c->config('upload_dir'), $record->short.'.'.$ext)->to_string;
|
||||
|
||||
my ($width, $height);
|
||||
if ($im_loaded && $mediatype ne 'image/svg+xml' # ImageMagick doesn't work with SVG, xcf or avif files
|
||||
&& $mediatype !~ m#image/(x-)?xcf#
|
||||
&& $mediatype ne 'image/avif') {
|
||||
my $im = Image::Magick->new;
|
||||
$im->BlobToImage($upload->slurp);
|
||||
|
||||
# Automatic rotation from EXIF tag
|
||||
$im->AutoOrient();
|
||||
|
||||
# Get dimensions
|
||||
$width = $im->Get('width');
|
||||
$height = $im->Get('height');
|
||||
|
||||
# Optionally add watermark
|
||||
if ($c->config('watermark_path') && (
|
||||
($wm && $wm ne 'none') ||
|
||||
$c->config('watermark_enforce') ne 'none'
|
||||
)) {
|
||||
my $watermarkim = Image::Magick->new;
|
||||
$watermarkim->ReadImage($c->config('watermark_path'));
|
||||
$watermarkim->Evaluate(
|
||||
operator => 'Multiply',
|
||||
value => 0.25,
|
||||
channel => 'Alpha'
|
||||
);
|
||||
if ($height <= 80) {
|
||||
$watermarkim->Resize(geometry => 'x10');
|
||||
} else {
|
||||
$watermarkim->Resize(geometry => 'x80');
|
||||
}
|
||||
|
||||
# Add one watermark or repeat it all over the image?
|
||||
my $tilingw = 1 if ($c->config('watermark_enforce') eq 'tiling' || $wm eq 'tiling');
|
||||
my $singlew = 1 if ($c->config('watermark_enforce') eq 'single' || $wm eq 'single');
|
||||
if ($tilingw) {
|
||||
$im->Composite(
|
||||
image => $watermarkim,
|
||||
compose => 'Dissolve',
|
||||
tile => 'True',
|
||||
gravity => 'Center'
|
||||
);
|
||||
} elsif ($singlew) {
|
||||
$im->Composite(
|
||||
image => $watermarkim,
|
||||
compose => 'Dissolve',
|
||||
tile => 'False',
|
||||
x => '20',
|
||||
y => '20',
|
||||
gravity => $c->config('watermark_placement')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
# Update the uploaded file with it's auto-rotated/watermarked clone
|
||||
my $asset = Mojo::Asset::Memory->new->add_chunk($im->ImageToBlob());
|
||||
$upload->asset($asset);
|
||||
|
||||
# Create the thumbnail
|
||||
$im->Resize(geometry => 'x85');
|
||||
|
||||
$thumb = 'data:'.$mediatype.';base64,';
|
||||
if ($mediatype eq 'image/gif') {
|
||||
$thumb .= b64_encode $im->[0]->ImageToBlob();
|
||||
} else {
|
||||
$thumb .= b64_encode $im->ImageToBlob();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
unless (defined($keep_exif) && $keep_exif) {
|
||||
# Exiftool can’t process SVG or xcf files
|
||||
if ($mediatype ne 'image/svg+xml'
|
||||
&& $mediatype !~ m#image/(x-)?xcf#) {
|
||||
# Remove the EXIF tags
|
||||
my $data = new IO::Scalar \$upload->slurp();
|
||||
my $et = Image::ExifTool->new;
|
||||
|
||||
# Remove all metadata
|
||||
$et->SetNewValue('*');
|
||||
|
||||
# Create a temporary IO::Scalar to write into
|
||||
my $temp;
|
||||
my $a = new IO::Scalar \$temp;
|
||||
$et->WriteInfo($data, $a);
|
||||
|
||||
# Update the uploaded file with it's no-tags clone
|
||||
$data = Mojo::Asset::Memory->new->add_chunk($temp);
|
||||
$upload->asset($data);
|
||||
}
|
||||
}
|
||||
|
||||
my ($key, $iv);
|
||||
if ($c->param('crypt') || $c->config('always_encrypt')) {
|
||||
($upload, $key, $iv) = $c->crypt($upload, $filename);
|
||||
}
|
||||
$upload->move_to($path);
|
||||
|
||||
$record->path($path)
|
||||
->filename($filename)
|
||||
->mediatype($mediatype)
|
||||
->footprint(digest_file_hex($path, 'SHA-512'))
|
||||
->enabled(1)
|
||||
->delete_at_day(($c->param('delete-day') && ($c->param('delete-day') <= $c->max_delay || $c->max_delay == 0)) ? $c->param('delete-day') : $c->max_delay)
|
||||
->delete_at_first_view(($c->param('first-view'))? 1 : 0)
|
||||
->created_at(time())
|
||||
->created_by($ip)
|
||||
->width($width)
|
||||
->height($height)
|
||||
->iv($iv)
|
||||
->write;
|
||||
|
||||
# Log image creation
|
||||
$c->app->log->info('[CREATION] '.$ip.' pushed '.$filename.' (path: '.$path.')') unless $c->config('quiet_logs');
|
||||
|
||||
# Give url to user
|
||||
$short = $record->short;
|
||||
$real_short = $short;
|
||||
if (!defined($record->mod_token)) {
|
||||
$record->mod_token($c->shortener($c->config->{token_length}))->write;
|
||||
}
|
||||
$token = $record->mod_token;
|
||||
$short .= '/'.$key if (defined($key));
|
||||
|
||||
$limit = $record->delete_at_day;
|
||||
$created = $record->created_at;
|
||||
} else {
|
||||
# Houston, we have a problem
|
||||
$msg = $c->l('There is no more available URL. Retry or contact the administrator. %1', $c->config->{contact});
|
||||
}
|
||||
} else {
|
||||
$msg = $c->l('The file %1 is not an image.', $upload->filename);
|
||||
}
|
||||
|
||||
if (defined($c->param('format')) && $c->param('format') eq 'json') {
|
||||
if (defined($short)) {
|
||||
$msg = {
|
||||
filename => $upload->filename,
|
||||
short => $short,
|
||||
real_short => $real_short,
|
||||
token => $token,
|
||||
ext => $ext || extensions($mediatype),
|
||||
thumb => $thumb,
|
||||
del_at_view => ($c->param('first-view')) ? true : false,
|
||||
limit => $limit,
|
||||
created_at => $created
|
||||
};
|
||||
} else {
|
||||
$msg = {
|
||||
filename => $upload->filename,
|
||||
msg => $msg
|
||||
};
|
||||
}
|
||||
return $c->render(
|
||||
json => {
|
||||
success => (defined($short)) ? Mojo::JSON->true : Mojo::JSON->false,
|
||||
msg => $msg
|
||||
}
|
||||
);
|
||||
} else {
|
||||
if ((defined($msg))) {
|
||||
$c->flash(msg => $msg);
|
||||
$c->flash(filename => $upload->filename);
|
||||
return $c->redirect_to('/');
|
||||
} else {
|
||||
$c->stash(short => $short) if (defined($short));
|
||||
$c->stash(real_short => $real_short);
|
||||
$c->stash(token => $token);
|
||||
$c->stash(ext => $ext || extensions($mediatype));
|
||||
$c->stash(thumb => $thumb);
|
||||
$c->stash(filename => $upload->filename);
|
||||
return $c->render(
|
||||
template => 'index',
|
||||
max_file_size => $c->req->max_message_size
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (defined($c->param('format')) && $c->param('format') eq 'json') {
|
||||
return $c->render(
|
||||
json => {
|
||||
success => Mojo::JSON->false,
|
||||
msg => {
|
||||
filename => $upload->filename,
|
||||
msg => $c->stash('stop_upload')
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$c->flash(msg => $c->stash('stop_upload'));
|
||||
$c->flash(filename => $upload->filename);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub short {
|
||||
my $c = shift;
|
||||
my $short = $c->param('short');
|
||||
my $touit = $c->param('t');
|
||||
my $key = $c->param('key');
|
||||
my $thumb;
|
||||
$thumb = '' if defined $c->param('thumb');
|
||||
$thumb = $c->param('width') if defined $c->param('width');
|
||||
my $dl = (defined($c->param('dl'))) ? 'attachment' : 'inline';
|
||||
|
||||
my $image = Lutim::DB::Image->new(app => $c->app, short => $short);
|
||||
if ($image->enabled && $image->path) {
|
||||
if($image->delete_at_day && $image->created_at + $image->delete_at_day * 86400 <= time()) {
|
||||
# Log deletion
|
||||
$c->app->log->info('[DELETION] someone tried to view '.$image->filename.' but it has been removed by expiration (path: '.$image->path.')') unless $c->config('quiet_logs');
|
||||
|
||||
# Delete image
|
||||
$c->delete_image($image);
|
||||
|
||||
# Warn user
|
||||
$c->flash(
|
||||
msg => $c->l('Unable to find the image: it has been deleted.')
|
||||
);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
|
||||
my $test;
|
||||
if (defined($touit) && $image->mediatype !~ m/svg/) {
|
||||
$test = 1;
|
||||
my $short = $image->short;
|
||||
$short .= '/'.$key if (defined($key));
|
||||
my ($width, $height) = (340,340);
|
||||
if ($image->mediatype eq 'image/gif') {
|
||||
if (defined($image->width) && defined($image->height)) {
|
||||
($width, $height) = ($image->width, $image->height);
|
||||
} elsif ($im_loaded && $image->mediatype !~ m/xcf|avif/) {
|
||||
my $upload = $c->decrypt($key, $image->path, $image->iv);
|
||||
my $im = Image::Magick->new;
|
||||
$im->BlobToImage($upload->slurp);
|
||||
$width = $im->Get('width');
|
||||
$height = $im->Get('height');
|
||||
|
||||
$image->width($width)
|
||||
->height($height)
|
||||
->write;
|
||||
}
|
||||
}
|
||||
return $c->render(
|
||||
template => 'share',
|
||||
layout => undef,
|
||||
short => $short,
|
||||
filename => $image->filename,
|
||||
mimetype => $image->mediatype,
|
||||
width => $width,
|
||||
height => $height
|
||||
);
|
||||
} else {
|
||||
# Delete image if needed
|
||||
if ($image->delete_at_first_view && $image->counter >= 1) {
|
||||
# Log deletion
|
||||
$c->app->log->info('[DELETION] someone made '.$image->filename.' removed (path: '.$image->path.')') unless $c->config('quiet_logs');
|
||||
|
||||
# Delete image
|
||||
$c->delete_image($image);
|
||||
|
||||
$c->flash(
|
||||
msg => $c->l('Unable to find the image: it has been deleted.')
|
||||
);
|
||||
return $c->redirect_to('/');
|
||||
} else {
|
||||
$test = $c->render_file($im_loaded, $image, $dl, $key, $thumb);
|
||||
}
|
||||
}
|
||||
|
||||
if ($test != 500) {
|
||||
# Update counter
|
||||
$c->on(finish => sub {
|
||||
# Log access
|
||||
$c->app->log->info('[VIEW] someone viewed '.$image->filename.' (path: '.$image->path.')') unless $c->config('quiet_logs');
|
||||
|
||||
# Update record
|
||||
unless ($c->config('disable_img_stats')) {
|
||||
if ($c->config('minion')->{enabled}) {
|
||||
$c->app->minion->enqueue(accessed => [$image->short, time]);
|
||||
} else {
|
||||
$image->accessed(time);
|
||||
}
|
||||
}
|
||||
|
||||
# Delete image if needed
|
||||
if ($image->delete_at_first_view) {
|
||||
# Log deletion
|
||||
$c->app->log->info('[DELETION] someone made '.$image->filename.' removed (path: '.$image->path.')') unless $c->config('quiet_logs');
|
||||
|
||||
# Delete image
|
||||
$c->delete_image($image);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$c->app->log->error('[ERROR] Can’t render '.$image->short);
|
||||
}
|
||||
} elsif ($image->path && !$image->enabled) {
|
||||
# Log access try
|
||||
$c->app->log->info('[NOT FOUND] someone tried to view '.$short.' but it doesn’t exist anymore.') unless $c->config('quiet_logs');
|
||||
|
||||
# Warn user
|
||||
$c->flash(
|
||||
msg => $c->l('Unable to find the image: it has been deleted.')
|
||||
);
|
||||
return $c->redirect_to('/');
|
||||
} else {
|
||||
# Image never existed
|
||||
$c->helpers->reply->not_found;
|
||||
}
|
||||
}
|
||||
|
||||
sub zip {
|
||||
my $c = shift;
|
||||
my $imgs = $c->every_param('i');
|
||||
|
||||
my $img_nb = scalar(@{$imgs});
|
||||
my $max_zip = $c->config('max_files_in_zip');
|
||||
|
||||
if ($img_nb <= $max_zip) {
|
||||
my $zip = Archive::Zip->new();
|
||||
|
||||
# We HAVE to add a png file at the beginning, otherwise the $zip
|
||||
# could use the mimetype of an SVG file if it's the first file asked.
|
||||
$zip->addFile('themes/default/public/img/favicon.png', 'hosted_with_lutim.png');
|
||||
|
||||
$zip->addDirectory('images/');
|
||||
for my $img (@{$imgs}) {
|
||||
my ($short, $key) = split('/', $img);
|
||||
if (defined $key) {
|
||||
$key =~ s/\.[^.]*//;
|
||||
} else {
|
||||
$short =~ s/\.[^.]*//;
|
||||
}
|
||||
my $image = Lutim::DB::Image->new(app => $c->app, short => $short);
|
||||
|
||||
if ($image->enabled && $image->path) {
|
||||
my $filename = $image->filename;
|
||||
if($image->delete_at_day && $image->created_at + $image->delete_at_day * 86400 <= time()) {
|
||||
# Log deletion
|
||||
$c->app->log->info('[DELETION] someone tried to view '.$image->filename.' but it has been removed by expiration (path: '.$image->path.')') unless $c->config('quiet_logs');
|
||||
|
||||
# Delete image
|
||||
$c->delete_image($image);
|
||||
|
||||
# Warn user
|
||||
$zip->addString(encode('UTF-8', $c->l('Unable to find the image: it has been deleted.')), 'images/'.$filename.'.txt');
|
||||
next;
|
||||
}
|
||||
|
||||
# Delete image if needed
|
||||
if ($image->delete_at_first_view && $image->counter >= 1) {
|
||||
# Log deletion
|
||||
$c->app->log->info('[DELETION] someone made '.$image->filename.' removed (path: '.$image->path.')') unless $c->config('quiet_logs');
|
||||
|
||||
# Delete image
|
||||
$c->delete_image($image);
|
||||
|
||||
$zip->addString(encode('UTF-8', $c->l('Unable to find the image: it has been deleted.')), 'images/'.$filename.'.txt');
|
||||
next;
|
||||
} else {
|
||||
my $expires = ($image->delete_at_day) ? $image->delete_at_day : 360;
|
||||
my $dt = DateTime->from_epoch( epoch => $expires * 86400 + $image->created_at);
|
||||
$dt->set_time_zone('GMT');
|
||||
$expires = $dt->strftime("%a, %d %b %Y %H:%M:%S GMT");
|
||||
|
||||
my $path = $image->path;
|
||||
unless ( -f $path && -r $path ) {
|
||||
$c->app->log->error("Cannot read file [$path]. error [$!]");
|
||||
$zip->addString(encode('UTF-8', $c->l('Unable to find the image: it has been deleted.')), 'images/'.$filename.'.txt');
|
||||
next;
|
||||
}
|
||||
|
||||
if ($key) {
|
||||
$zip->addString($c->decrypt($key, $path, $image->iv), "images/$filename");
|
||||
} else {
|
||||
$zip->addFile($path, "images/$filename");
|
||||
}
|
||||
|
||||
# Log access
|
||||
$c->app->log->info('[VIEW] someone viewed '.$image->filename.' (path: '.$image->path.')') unless $c->config('quiet_logs');
|
||||
|
||||
# Update counter and record
|
||||
unless ($c->config('disable_img_stats')) {
|
||||
if ($c->config('minion')->{enabled}) {
|
||||
$c->app->minion->enqueue(accessed => [$image->short, time]);
|
||||
} else {
|
||||
$image->accessed(time);
|
||||
}
|
||||
}
|
||||
}
|
||||
} elsif ($image->path && !$image->enabled) {
|
||||
# Log access try
|
||||
$c->app->log->info('[NOT FOUND] someone tried to view '.$short.' but it doesn’t exist anymore.') unless $c->config('quiet_logs');
|
||||
|
||||
# Warn user
|
||||
$zip->addString(encode('UTF-8', $c->l('Unable to find the image: it has been deleted.')), 'images/'.$image->filename.'.txt');
|
||||
next;
|
||||
} else {
|
||||
$zip->addString(encode('UTF-8', $c->l('Image not found.')), 'images/'.$short.'.txt');
|
||||
next;
|
||||
}
|
||||
}
|
||||
my ($fh, $zipfile) = Archive::Zip::tempFile();
|
||||
unless ($zip->writeToFileNamed($zipfile) == AZ_OK) {
|
||||
$c->flash(
|
||||
msg => $c->l('Something went wrong when creating the zip file. Try again later or contact the administrator (%1).', $c->config('contact'))
|
||||
);
|
||||
return $c->redirect_to('/');
|
||||
}
|
||||
$c->res->content->headers->content_type('application/zip;name=images.zip');
|
||||
$c->res->content->headers->content_disposition('attachment;filename=images.zip');;
|
||||
|
||||
my $asset = Mojo::Asset::File->new(path => $zipfile);
|
||||
$c->res->content->asset($asset);
|
||||
$c->res->content->headers->content_length($asset->size);
|
||||
|
||||
unlink $zipfile;
|
||||
|
||||
return $c->rendered(200);
|
||||
} else {
|
||||
my $i = -1;
|
||||
my @urls = ();
|
||||
my @esc_imgs = map { my $e = $_; $e = url_escape($e); $e =~ s#%2F#/#g; $e } @{$imgs};
|
||||
while (++$i < $img_nb) {
|
||||
my $stop = ($i + $max_zip - 1 < $img_nb) ? $i + $max_zip - 1 : $img_nb - 1;
|
||||
push @urls, $c->url_for('/zip')->to_abs->to_string.'?i='.join('&i=', @esc_imgs[$i..$stop]);
|
||||
$i = $stop;
|
||||
}
|
||||
$c->render(
|
||||
template => 'zip',
|
||||
urls => \@urls
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sub random {
|
||||
my $c = shift;
|
||||
my $imgs = $c->every_param('i');
|
||||
|
||||
my $img_nb = scalar(@{$imgs});
|
||||
if ($img_nb) {
|
||||
$c->redirect_to($c->prefix.$imgs->[entropy_source->get_int($img_nb)]);
|
||||
} else {
|
||||
$c->render_not_found;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
364
lib/Lutim/DB/Image.pm
Normal file
364
lib/Lutim/DB/Image.pm
Normal file
@@ -0,0 +1,364 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim::DB::Image;
|
||||
use Mojo::Base -base;
|
||||
|
||||
has 'short';
|
||||
has 'path';
|
||||
has 'footprint';
|
||||
has 'enabled';
|
||||
has 'mediatype';
|
||||
has 'filename';
|
||||
has 'counter' => 0;
|
||||
has 'delete_at_first_view';
|
||||
has 'delete_at_day';
|
||||
has 'created_at';
|
||||
has 'created_by';
|
||||
has 'last_access_at';
|
||||
has 'mod_token';
|
||||
has 'width';
|
||||
has 'height';
|
||||
has 'iv';
|
||||
has 'app';
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Lutim::DB::Image - DB abstraction layer for Lutim images
|
||||
|
||||
=head1 Contributing
|
||||
|
||||
When creating a new database accessor, make sure that it provides the following subroutines.
|
||||
After that, modify this file and modify the C<new> subroutine to allow to use your accessor.
|
||||
|
||||
Have a look at Lutim::DB::Image::SQLite's code: it's simple and may be more understandable that this doc.
|
||||
|
||||
=head1 Attributes
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<short> : random string
|
||||
|
||||
=item B<path> : string, path to the image, relative to lutim's installation directory
|
||||
|
||||
=item B<footprint> : string, sha512 checksum of the image
|
||||
|
||||
=item B<enabled> : boolean, is the image accessible?
|
||||
|
||||
=item B<mediatype> : string, mimetype of the image
|
||||
|
||||
=item B<filename> : string
|
||||
|
||||
=item B<counter> : integer
|
||||
|
||||
=item B<delete_at_first_view> : boolean
|
||||
|
||||
=item B<delete_at_day> : integer, number of days from image upload to deletion
|
||||
|
||||
=item B<created_at> : unix timestamp
|
||||
|
||||
=item B<created_by> : unix timestamp
|
||||
|
||||
=item B<last_access_at> : unix timestamp
|
||||
|
||||
=item B<mod_token> : random string
|
||||
|
||||
=item B<width> : integer
|
||||
|
||||
=item B<height> : integer
|
||||
|
||||
=item B<iv> : initialization vector for the file encryption
|
||||
|
||||
=item B<app> : a mojolicious object
|
||||
|
||||
=back
|
||||
|
||||
=head1 Sub routines
|
||||
|
||||
=head2 new
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c = Lutim::DB::Image-E<gt>new(app =E<gt> $self);>
|
||||
|
||||
=item B<Arguments> : any of the attribute above
|
||||
|
||||
=item B<Purpose> : construct a new db accessor object. If the C<short> attribute is provided, it have to load the informations from the database.
|
||||
|
||||
=item B<Returns> : the db accessor object
|
||||
|
||||
=item B<Info> : the app argument is used by Lutim::DB::Image to choose which db accessor will be used, you don't need to use it in new(), but you can use it to access helpers or configuration settings in the other subroutines
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my $c = shift;
|
||||
|
||||
$c = $c->SUPER::new(@_);
|
||||
|
||||
if (ref($c) eq 'Lutim::DB::Image') {
|
||||
my $dbtype = $c->app->config('dbtype');
|
||||
if ($dbtype eq 'sqlite') {
|
||||
use Lutim::DB::Image::SQLite;
|
||||
$c = Lutim::DB::Image::SQLite->new(@_);
|
||||
} elsif ($dbtype eq 'postgresql') {
|
||||
use Lutim::DB::Image::Pg;
|
||||
$c = Lutim::DB::Image::Pg->new(@_);
|
||||
}
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
sub to_hash {
|
||||
my $c = shift;
|
||||
|
||||
return {
|
||||
short => $c->short,
|
||||
path => $c->path,
|
||||
footprint => $c->footprint,
|
||||
enabled => $c->enabled,
|
||||
mediatype => $c->mediatype,
|
||||
filename => $c->filename,
|
||||
counter => $c->counter,
|
||||
delete_at_first_view => $c->delete_at_first_view,
|
||||
delete_at_day => $c->delete_at_day,
|
||||
created_at => $c->created_at,
|
||||
created_by => $c->created_by,
|
||||
last_access_at => $c->last_access_at,
|
||||
mod_token => $c->mod_token,
|
||||
width => $c->width,
|
||||
height => $c->height,
|
||||
iv => $c->iv
|
||||
};
|
||||
}
|
||||
|
||||
=head2 accessed
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>accessed($time)>
|
||||
|
||||
=item B<Arguments> : an unix timestamp
|
||||
|
||||
=item B<Purpose> : increments the counter attribute by one, set the last_access_at attribute to $time and update the database
|
||||
|
||||
=item B<Returns> : the db accessor object
|
||||
|
||||
=back
|
||||
|
||||
=head2 count_delete_at_day_endis
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>count_delete_at_day_endis($delete_at_day, $enabled[, $time])>
|
||||
|
||||
=item B<Arguments> : two mandatory parameters: one integer, the delete_at_day attribute, a boolean (0 or 1), the enabled attribute
|
||||
an optional parameter: an unix timestamp
|
||||
|
||||
=item B<Purpose> : count how many images there are with the given delete_at_day attribute, and enabled or disabled, depending on the given enabled attribute
|
||||
if the optional parameter is given, count only images according to the given mandatory parameters that were created before the timestamp
|
||||
|
||||
=item B<Returns> : integer
|
||||
|
||||
=back
|
||||
|
||||
=head2 count_created_before
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>count_created_before($time)>
|
||||
|
||||
=item B<Arguments> : an unix timestamp
|
||||
|
||||
=item B<Purpose> : count how many images have been created before the given timestamp
|
||||
|
||||
=item B<Returns> : integer
|
||||
|
||||
=back
|
||||
|
||||
=head2 select_created_after
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>select_created_after($time)>
|
||||
|
||||
=item B<Arguments> : an unix timestamp
|
||||
|
||||
=item B<Purpose> : select images created after the given timestamp
|
||||
|
||||
=item B<Returns> : a Mojo::Collection object containing the images created after the given timestamp
|
||||
|
||||
=back
|
||||
|
||||
=head2 select_empty
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>select_empty>
|
||||
|
||||
=item B<Arguments> : none
|
||||
|
||||
=item B<Purpose> : select a ready-to-use empty record
|
||||
|
||||
=item B<Returns> : a db accessor object
|
||||
|
||||
=back
|
||||
|
||||
=head2 write
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>write>
|
||||
|
||||
=item B<Arguments> : none
|
||||
|
||||
=item B<Purpose> : create or update a record in the database, with the values of the object's attributes
|
||||
|
||||
=item B<Returns> : the db accessor object
|
||||
|
||||
=back
|
||||
|
||||
=head2 count_short
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>count_short($short)>
|
||||
|
||||
=item B<Arguments> : a random string, unique image identifier in the database
|
||||
|
||||
=item B<Purpose> : checks that an identifier isn't already used
|
||||
|
||||
=item B<Returns> : integer, number of records having this identifier (should be 0 or 1)
|
||||
|
||||
=back
|
||||
|
||||
=head2 count_empty
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>count_empty>
|
||||
|
||||
=item B<Arguments> : none
|
||||
|
||||
=item B<Purpose> : counts the number of records whose path is null
|
||||
|
||||
=item B<Returns> : integer
|
||||
|
||||
=back
|
||||
|
||||
=head2 count_not_empty
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>count_not_empty>
|
||||
|
||||
=item B<Arguments> : none
|
||||
|
||||
=item B<Purpose> : counts the number of records whose path is not null
|
||||
|
||||
=item B<Returns> : integer
|
||||
|
||||
=back
|
||||
|
||||
=head2 clean_ips_until
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>clean_ips_until($time)>
|
||||
|
||||
=item B<Arguments> : unix timestamp
|
||||
|
||||
=item B<Purpose> : remove the image's sender information on images created before the given timestamp
|
||||
|
||||
=item B<Returns> : the db accessor object
|
||||
|
||||
=back
|
||||
|
||||
=head2 get_no_longer_viewed_files
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>get_no_longer_viewed_files($time)>
|
||||
|
||||
=item B<Arguments> : unix timestamp
|
||||
|
||||
=item B<Purpose> : get images no longer viewed after the given timestamp
|
||||
|
||||
=item B<Returns> : a Mojo::Collection object containing the no longer viewed images as Lutim::DB::Image objects
|
||||
|
||||
=back
|
||||
|
||||
=head2 get_images_to_clean
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>get_images_to_clean>
|
||||
|
||||
=item B<Arguments> : none
|
||||
|
||||
=item B<Purpose> : get images that are expired but not marked as it
|
||||
|
||||
=item B<Returns> : a Mojo::Collection object containing the images to clean as Lutim::DB::Image objects
|
||||
|
||||
=back
|
||||
|
||||
=head2 get_50_oldest
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>get_50_oldest>
|
||||
|
||||
=item B<Arguments> : none
|
||||
|
||||
=item B<Purpose> : get the 50 oldest enabled images
|
||||
|
||||
=item B<Returns> : a Mojo::Collection object containing the 50 oldest enabled images as Lutim::DB::Image objects
|
||||
|
||||
=back
|
||||
|
||||
=head2 disable
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>disable>
|
||||
|
||||
=item B<Arguments> : none
|
||||
|
||||
=item B<Purpose> : change the attribute C<enabled> to false and update the database record
|
||||
|
||||
=item B<Returns> : the db accessor object
|
||||
|
||||
=back
|
||||
|
||||
=head2 search_created_by
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>search_created_by($ip)>
|
||||
|
||||
=item B<Arguments> : an IP address
|
||||
|
||||
=item B<Purpose> : get enabled images that have been uploaded by this IP address (database query: LIKE '$ip%', results may include images uploaded by similar IP addresses)
|
||||
|
||||
=item B<Returns> : a Mojo::Collection object containing the matching images as Lutim::DB::Image objects
|
||||
|
||||
=back
|
||||
|
||||
=head2 search_exact_created_by
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>search_exact_created_by($ip)>
|
||||
|
||||
=item B<Arguments> : an IP address
|
||||
|
||||
=item B<Purpose> : get enabled images that have been uploaded by this IP address
|
||||
|
||||
=item B<Returns> : a Mojo::Collection object containing the matching images as Lutim::DB::Image objects
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
276
lib/Lutim/DB/Image/Pg.pm
Normal file
276
lib/Lutim/DB/Image/Pg.pm
Normal file
@@ -0,0 +1,276 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim::DB::Image::Pg;
|
||||
use Mojo::Base 'Lutim::DB::Image';
|
||||
use Mojo::Collection 'c';
|
||||
|
||||
has 'record' => 0;
|
||||
|
||||
sub new {
|
||||
my $c = shift;
|
||||
|
||||
$c = $c->SUPER::new(@_);
|
||||
$c = $c->_slurp if ($c->short);
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
sub accessed {
|
||||
my $c = shift;
|
||||
my $time = shift;
|
||||
|
||||
my $h = $c->app->pg->db->query('UPDATE lutim SET counter = counter + 1, last_access_at = ? WHERE short = ? RETURNING counter, last_access_at', $time, $c->short)->hashes->first;
|
||||
$c->counter($h->{counter});
|
||||
$c->last_access_at($h->{last_access_at});
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
sub count_delete_at_day_endis {
|
||||
my $c = shift;
|
||||
my $day = shift;
|
||||
my $enabled = shift;
|
||||
my $created = shift;
|
||||
|
||||
if (defined $created) {
|
||||
return $c->app->pg->db->query('SELECT count(short) FROM lutim WHERE path IS NOT NULL AND delete_at_day = ? AND enabled = ? AND created_at < ?', $day, $enabled, $created)->hashes->first->{count};
|
||||
} else {
|
||||
return $c->app->pg->db->query('SELECT count(short) FROM lutim WHERE path IS NOT NULL AND delete_at_day = ? AND enabled = ?', $day, $enabled)->hashes->first->{count};
|
||||
}
|
||||
}
|
||||
|
||||
sub count_created_before {
|
||||
my $c = shift;
|
||||
my $time = shift;
|
||||
|
||||
return $c->app->pg->db->query('SELECT count(short) FROM lutim WHERE path IS NOT NULL AND created_at < ?', $time)->hashes->first->{count};
|
||||
}
|
||||
|
||||
sub select_created_after {
|
||||
my $c = shift;
|
||||
my $time = shift;
|
||||
|
||||
my @images;
|
||||
|
||||
my $records = $c->app->pg->db->query('SELECT * FROM lutim WHERE path IS NOT NULL AND created_at >= ?', $time)->hashes;
|
||||
|
||||
$records->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = Lutim::DB::Image->new(app => $c->app);
|
||||
$i->_slurp($e);
|
||||
|
||||
push @images, $i;
|
||||
}
|
||||
);
|
||||
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub select_empty {
|
||||
my $c = shift;
|
||||
|
||||
my $record = $c->app->pg->db->query('SELECT * FROM lutim WHERE path IS NULL')->hashes->shuffle->first;
|
||||
$c->app->pg->db->query('UPDATE lutim SET path = ? WHERE short = ?', 'used', $record->{short});
|
||||
|
||||
$c = $c->_slurp($record);
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
sub write {
|
||||
my $c = shift;
|
||||
my $provisioning = shift;
|
||||
|
||||
if ($c->record) {
|
||||
$c->app->pg->db->query('UPDATE lutim SET counter = ?, created_at = ?, created_by = ?, delete_at_day = ?, delete_at_first_view = ?, enabled = ?, filename = ?, footprint = ?, height = ?, last_access_at = ?, mediatype = ?, mod_token = ?, path = ?, short = ?, width = ?, iv = ? WHERE short = ?', $c->counter, $c->created_at, $c->created_by, $c->delete_at_day, $c->delete_at_first_view, $c->enabled, $c->filename, $c->footprint, $c->height, $c->last_access_at, $c->mediatype, $c->mod_token, $c->path, $c->short, $c->width, $c->iv, $c->short);
|
||||
} else {
|
||||
my $query = 'INSERT INTO lutim (counter, created_at, created_by, delete_at_day, delete_at_first_view, enabled, filename, footprint, height, last_access_at, mediatype, mod_token, path, short, width, iv) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||||
$query .= ' ON CONFLICT DO NOTHING' if $provisioning;
|
||||
$c->app->pg->db->query($query, $c->counter, $c->created_at, $c->created_by, $c->delete_at_day, $c->delete_at_first_view, $c->enabled, $c->filename, $c->footprint, $c->height, $c->last_access_at, $c->mediatype, $c->mod_token, $c->path, $c->short, $c->width, $c->iv);
|
||||
$c->record(1);
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
sub count_short {
|
||||
my $c = shift;
|
||||
my $short = shift;
|
||||
|
||||
return $c->app->pg->db->query('SELECT count(short) FROM lutim WHERE short = ?', $short)->hashes->first->{count};
|
||||
}
|
||||
|
||||
sub count_empty {
|
||||
my $c = shift;
|
||||
|
||||
return $c->app->pg->db->query('SELECT count(short) FROM lutim WHERE path IS NULL')->hashes->first->{count};
|
||||
}
|
||||
|
||||
sub count_not_empty {
|
||||
my $c = shift;
|
||||
|
||||
return $c->app->pg->db->query('SELECT count(short) FROM lutim WHERE path IS NOT NULL')->hashes->first->{count};
|
||||
}
|
||||
|
||||
sub clean_ips_until {
|
||||
my $c = shift;
|
||||
my $time = shift;
|
||||
|
||||
$c->app->pg->db->query('UPDATE lutim SET created_by = NULL WHERE path IS NOT NULL AND created_at < ?', $time);
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
sub get_no_longer_viewed_files {
|
||||
my $c = shift;
|
||||
my $time = shift;
|
||||
|
||||
my @images;
|
||||
|
||||
my $records = $c->app->pg->db->query('SELECT * FROM lutim WHERE enabled = 1 AND last_access_at < ?', $time)->{hashes};
|
||||
|
||||
$records->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = Lutim::DB::Image->new(app => $c->app);
|
||||
$i->record(1);
|
||||
$i->_slurp($e);
|
||||
|
||||
push @images, $i;
|
||||
}
|
||||
);
|
||||
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub get_images_to_clean {
|
||||
my $c = shift;
|
||||
|
||||
my @images;
|
||||
|
||||
my $records = $c->app->pg->db->query('SELECT * FROM lutim WHERE enabled = 1 AND (delete_at_day * 86400) < (? - created_at) AND delete_at_day != 0', time())->hashes;
|
||||
|
||||
$records->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = Lutim::DB::Image->new(app => $c->app);
|
||||
$i->_slurp($e);
|
||||
|
||||
push @images, $i;
|
||||
}
|
||||
);
|
||||
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub get_50_oldest {
|
||||
my $c = shift;
|
||||
|
||||
my @images;
|
||||
|
||||
my $records = $c->app->pg->db->query('SELECT * FROM lutim WHERE path IS NOT NULL AND enabled = 1 ORDER BY created_at ASC LIMIT 50')->hashes;
|
||||
|
||||
$records->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = Lutim::DB::Image->new(app => $c->app);
|
||||
$i->_slurp($e);
|
||||
|
||||
push @images, $i;
|
||||
}
|
||||
);
|
||||
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub disable {
|
||||
my $c = shift;
|
||||
|
||||
$c->app->pg->db->query('UPDATE lutim SET enabled = 0 WHERE short = ?', $c->short);
|
||||
$c->enabled(0);
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
sub search_created_by {
|
||||
my $c = shift;
|
||||
my $ip = shift;
|
||||
|
||||
my @images;
|
||||
|
||||
my $records = $c->app->pg->db->select('lutim', undef, { enabled => 1, created_by => { -like => $ip.'%' } })->hashes;
|
||||
|
||||
$records->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = Lutim::DB::Image->new(app => $c->app);
|
||||
$i->_slurp($e);
|
||||
|
||||
push @images, $i;
|
||||
}
|
||||
);
|
||||
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub search_exact_created_by {
|
||||
my $c = shift;
|
||||
my $ip = shift;
|
||||
|
||||
my @images;
|
||||
|
||||
my $records = $c->app->pg->db->select('lutim', undef, { enabled => 1, created_by => $ip })->hashes;
|
||||
|
||||
$records->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = Lutim::DB::Image->new(app => $c->app);
|
||||
$i->_slurp($e);
|
||||
|
||||
push @images, $i;
|
||||
}
|
||||
);
|
||||
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub _slurp {
|
||||
my $c = shift;
|
||||
my $r = shift;
|
||||
|
||||
my $image;
|
||||
if (defined $r) {
|
||||
$image = $r;
|
||||
} else {
|
||||
my $images = $c->app->pg->db->query('SELECT * FROM lutim WHERE short = ?', $c->short)->hashes;
|
||||
|
||||
if ($images->size) {
|
||||
$image = $images->first;
|
||||
}
|
||||
}
|
||||
|
||||
if ($image) {
|
||||
$c->short($image->{short});
|
||||
$c->path($image->{path});
|
||||
$c->footprint($image->{footprint});
|
||||
$c->enabled($image->{enabled});
|
||||
$c->mediatype($image->{mediatype});
|
||||
$c->filename($image->{filename});
|
||||
$c->counter($image->{counter});
|
||||
$c->delete_at_first_view($image->{delete_at_first_view});
|
||||
$c->delete_at_day($image->{delete_at_day});
|
||||
$c->created_at($image->{created_at});
|
||||
$c->created_by($image->{created_by});
|
||||
$c->last_access_at($image->{last_access_at});
|
||||
$c->mod_token($image->{mod_token});
|
||||
$c->width($image->{width});
|
||||
$c->height($image->{height});
|
||||
$c->iv($image->{iv});
|
||||
|
||||
$c->record(1);
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
1;
|
||||
277
lib/Lutim/DB/Image/SQLite.pm
Normal file
277
lib/Lutim/DB/Image/SQLite.pm
Normal file
@@ -0,0 +1,277 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim::DB::Image::SQLite;
|
||||
use Mojo::Base 'Lutim::DB::Image';
|
||||
use Mojo::Collection 'c';
|
||||
|
||||
has 'record' => 0;
|
||||
|
||||
sub new {
|
||||
my $c = shift;
|
||||
|
||||
$c = $c->SUPER::new(@_);
|
||||
$c = $c->_slurp if ($c->short);
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
sub accessed {
|
||||
my $c = shift;
|
||||
my $time = shift;
|
||||
|
||||
$c->app->sqlite->db->query('UPDATE lutim SET counter = counter + 1, last_access_at = ? WHERE short = ?', $time, $c->short);
|
||||
my $h = $c->app->sqlite->db->query('SELECT counter, last_access_at FROM lutim WHERE short = ?', $c->short)->hashes->first;
|
||||
$c->counter($h->{counter});
|
||||
$c->last_access_at($h->{last_access_at});
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
sub count_delete_at_day_endis {
|
||||
my $c = shift;
|
||||
my $day = shift;
|
||||
my $enabled = shift;
|
||||
my $created = shift;
|
||||
|
||||
if (defined $created) {
|
||||
return $c->app->sqlite->db->query('SELECT count(short) AS count FROM lutim WHERE path IS NOT NULL AND delete_at_day = ? AND enabled = ? AND created_at < ?', $day, $enabled, $created)->hashes->first->{count};
|
||||
} else {
|
||||
return $c->app->sqlite->db->query('SELECT count(short) AS count FROM lutim WHERE path IS NOT NULL AND delete_at_day = ? AND enabled = ?', $day, $enabled)->hashes->first->{count};
|
||||
}
|
||||
}
|
||||
|
||||
sub count_created_before {
|
||||
my $c = shift;
|
||||
my $time = shift;
|
||||
|
||||
return $c->app->sqlite->db->query('SELECT count(short) AS count FROM lutim WHERE path IS NOT NULL AND created_at < ?', $time)->hashes->first->{count};
|
||||
}
|
||||
|
||||
sub select_created_after {
|
||||
my $c = shift;
|
||||
my $time = shift;
|
||||
|
||||
my @images;
|
||||
|
||||
my $records = $c->app->sqlite->db->query('SELECT * FROM lutim WHERE path IS NOT NULL AND created_at >= ?', $time)->hashes;
|
||||
|
||||
$records->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = Lutim::DB::Image->new(app => $c->app);
|
||||
$i->_slurp($e);
|
||||
|
||||
push @images, $i;
|
||||
}
|
||||
);
|
||||
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub select_empty {
|
||||
my $c = shift;
|
||||
|
||||
my $record = $c->app->sqlite->db->query('SELECT * FROM lutim WHERE path IS NULL')->hashes->shuffle->first;
|
||||
$c->app->sqlite->db->query('UPDATE lutim SET path = ? WHERE short = ?', 'used', $record->{short});
|
||||
|
||||
$c = $c->_slurp($record);
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
sub write {
|
||||
my $c = shift;
|
||||
my $provisioning = shift;
|
||||
|
||||
if ($c->record) {
|
||||
$c->app->sqlite->db->query('UPDATE lutim SET counter = ?, created_at = ?, created_by = ?, delete_at_day = ?, delete_at_first_view = ?, enabled = ?, filename = ?, footprint = ?, height = ?, last_access_at = ?, mediatype = ?, mod_token = ?, path = ?, short = ?, width = ?, iv = ? WHERE short = ?', $c->counter, $c->created_at, $c->created_by, $c->delete_at_day, $c->delete_at_first_view, $c->enabled, $c->filename, $c->footprint, $c->height, $c->last_access_at, $c->mediatype, $c->mod_token, $c->path, $c->short, $c->width, $c->iv, $c->short);
|
||||
} else {
|
||||
my $query = 'INSERT INTO lutim (counter, created_at, created_by, delete_at_day, delete_at_first_view, enabled, filename, footprint, height, last_access_at, mediatype, mod_token, path, short, width, iv) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||||
$query .= ' ON CONFLICT DO NOTHING' if $provisioning;
|
||||
$c->app->sqlite->db->query($query, $c->counter, $c->created_at, $c->created_by, $c->delete_at_day, $c->delete_at_first_view, $c->enabled, $c->filename, $c->footprint, $c->height, $c->last_access_at, $c->mediatype, $c->mod_token, $c->path, $c->short, $c->width, $c->iv);
|
||||
$c->record(1);
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
sub count_short {
|
||||
my $c = shift;
|
||||
my $short = shift;
|
||||
|
||||
return $c->app->sqlite->db->query('SELECT count(short) AS count FROM lutim WHERE short = ?', $short)->hashes->first->{count};
|
||||
}
|
||||
|
||||
sub count_empty {
|
||||
my $c = shift;
|
||||
|
||||
return $c->app->sqlite->db->query('SELECT count(short) AS count FROM lutim WHERE path IS NULL')->hashes->first->{count};
|
||||
}
|
||||
|
||||
sub count_not_empty {
|
||||
my $c = shift;
|
||||
|
||||
return $c->app->sqlite->db->query('SELECT count(short) AS count FROM lutim WHERE path IS NOT NULL')->hashes->first->{count};
|
||||
}
|
||||
|
||||
sub clean_ips_until {
|
||||
my $c = shift;
|
||||
my $time = shift;
|
||||
|
||||
$c->app->sqlite->db->query('UPDATE lutim SET created_by = NULL WHERE path IS NOT NULL AND created_at < ?', $time);
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
sub get_no_longer_viewed_files {
|
||||
my $c = shift;
|
||||
my $time = shift;
|
||||
|
||||
my @images;
|
||||
|
||||
my $records = $c->app->sqlite->db->query('SELECT * FROM lutim WHERE enabled = 1 AND last_access_at < ?', $time)->{hashes};
|
||||
|
||||
$records->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = Lutim::DB::Image->new(app => $c->app);
|
||||
$i->record(1);
|
||||
$i->_slurp($e);
|
||||
|
||||
push @images, $i;
|
||||
}
|
||||
);
|
||||
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub get_images_to_clean {
|
||||
my $c = shift;
|
||||
|
||||
my @images;
|
||||
|
||||
my $records = $c->app->sqlite->db->query('SELECT * FROM lutim WHERE enabled = 1 AND (delete_at_day * 86400) < (? - created_at) AND delete_at_day != 0', time())->hashes;
|
||||
|
||||
$records->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = Lutim::DB::Image->new(app => $c->app);
|
||||
$i->_slurp($e);
|
||||
|
||||
push @images, $i;
|
||||
}
|
||||
);
|
||||
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub get_50_oldest {
|
||||
my $c = shift;
|
||||
|
||||
my @images;
|
||||
|
||||
my $records = $c->app->sqlite->db->query('SELECT * FROM lutim WHERE path IS NOT NULL AND enabled = 1 ORDER BY created_at ASC LIMIT 50')->hashes;
|
||||
|
||||
$records->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = Lutim::DB::Image->new(app => $c->app);
|
||||
$i->_slurp($e);
|
||||
|
||||
push @images, $i;
|
||||
}
|
||||
);
|
||||
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub disable {
|
||||
my $c = shift;
|
||||
|
||||
$c->app->sqlite->db->query('UPDATE lutim SET enabled = 0 WHERE short = ?', $c->short);
|
||||
$c->enabled(0);
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
sub search_created_by {
|
||||
my $c = shift;
|
||||
my $ip = shift;
|
||||
|
||||
my @images;
|
||||
|
||||
my $records = $c->app->sqlite->db->select('lutim', undef, { enabled => 1, created_by => { -like => $ip.'%' } })->hashes;
|
||||
|
||||
$records->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = Lutim::DB::Image->new(app => $c->app);
|
||||
$i->_slurp($e);
|
||||
|
||||
push @images, $i;
|
||||
}
|
||||
);
|
||||
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub search_exact_created_by {
|
||||
my $c = shift;
|
||||
my $ip = shift;
|
||||
|
||||
my @images;
|
||||
|
||||
my $records = $c->app->sqlite->db->select('lutim', undef, { enabled => 1, created_by => $ip })->hashes;
|
||||
|
||||
$records->each(
|
||||
sub {
|
||||
my ($e, $num) = @_;
|
||||
my $i = Lutim::DB::Image->new(app => $c->app);
|
||||
$i->_slurp($e);
|
||||
|
||||
push @images, $i;
|
||||
}
|
||||
);
|
||||
|
||||
return c(@images);
|
||||
}
|
||||
|
||||
sub _slurp {
|
||||
my $c = shift;
|
||||
my $r = shift;
|
||||
|
||||
my $image;
|
||||
if (defined $r) {
|
||||
$image = $r;
|
||||
} else {
|
||||
my $images = $c->app->sqlite->db->query('SELECT * FROM lutim WHERE short = ?', $c->short)->hashes;
|
||||
|
||||
if ($images->size) {
|
||||
$image = $images->first;
|
||||
}
|
||||
}
|
||||
|
||||
if ($image) {
|
||||
$c->short($image->{short});
|
||||
$c->path($image->{path});
|
||||
$c->footprint($image->{footprint});
|
||||
$c->enabled($image->{enabled});
|
||||
$c->mediatype($image->{mediatype});
|
||||
$c->filename($image->{filename});
|
||||
$c->counter($image->{counter});
|
||||
$c->delete_at_first_view($image->{delete_at_first_view});
|
||||
$c->delete_at_day($image->{delete_at_day});
|
||||
$c->created_at($image->{created_at});
|
||||
$c->created_by($image->{created_by});
|
||||
$c->last_access_at($image->{last_access_at});
|
||||
$c->mod_token($image->{mod_token});
|
||||
$c->width($image->{width});
|
||||
$c->height($image->{height});
|
||||
$c->iv($image->{iv});
|
||||
|
||||
$c->record(1);
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
1;
|
||||
49
lib/Lutim/DefaultConfig.pm
Normal file
49
lib/Lutim/DefaultConfig.pm
Normal file
@@ -0,0 +1,49 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim::DefaultConfig;
|
||||
require Exporter;
|
||||
@ISA = qw(Exporter);
|
||||
@EXPORT_OK = qw($default_config);
|
||||
our $default_config = {
|
||||
provisioning => 100,
|
||||
provis_step => 5,
|
||||
length => 8,
|
||||
always_encrypt => 0,
|
||||
anti_flood_delay => 5,
|
||||
max_file_size => 10*1024*1024,
|
||||
https => 0,
|
||||
proposed_delays => '0,1,7,30,365',
|
||||
default_delay => 0,
|
||||
max_delay => 0,
|
||||
token_length => 24,
|
||||
crypto_key_length => 8,
|
||||
thumbnail_size => 100,
|
||||
watermark_path => '',
|
||||
watermark_placement => 'SouthEast',
|
||||
watermark_default => 'none',
|
||||
watermark_enforce => 'none',
|
||||
theme => 'default',
|
||||
disable_api => 0,
|
||||
upload_dir => 'files',
|
||||
dbtype => 'sqlite',
|
||||
db_path => 'lutim.db',
|
||||
max_files_in_zip => 15,
|
||||
prefix => '/',
|
||||
minion => {
|
||||
enabled => 0,
|
||||
dbtype => 'sqlite',
|
||||
db_path => 'minion.db'
|
||||
},
|
||||
session_duration => 3600,
|
||||
cache_max_size => 0,
|
||||
memcached_servers => [],
|
||||
quiet_logs => 0,
|
||||
disable_img_stats => 0,
|
||||
x_frame_options => 'DENY',
|
||||
x_content_type_options => 'nosniff',
|
||||
x_xss_protection => '1; mode=block',
|
||||
stats_day_num => 365,
|
||||
keep_ip_during => 365,
|
||||
policy_when_full => 'warn',
|
||||
};
|
||||
|
||||
1;
|
||||
@@ -1,77 +0,0 @@
|
||||
package Lutim::I18N::en;
|
||||
use Mojo::Base 'Lutim::I18N';
|
||||
|
||||
my $inf_body = <<EOF;
|
||||
<h4>What is LUTIm?</h4>
|
||||
<p>LUTIm is a free (as in free beer) and anonymous image hosting service. It's also the name of the free (as in free speech) software which provides this service.</p>
|
||||
<p>The images you post on LUTIm can be stored indefinitely or be deleted at first view or after 24 hours.</p>
|
||||
<h4>How does it work?</h4>
|
||||
<p>Drag and drop an image in the appropriate area or use the traditional way to send files and LUTIm will provide you two URLs. One to view the image, the other to directly download it.</p>
|
||||
<p>You can, optionally, request that the image(s) posted on LUTIm to be deleted at first view (or download) or after 24 hours.</p>
|
||||
<h4>Is it really free (as in free beer)?</h4>
|
||||
<p>Yes, it is! On the other side, if you want to support the developer, you can do it via <a href="https://flattr.com/submit/auto?user_id=_SKy_&url=[_1]&title=LUTIm&category=software">Flattr</a> or with <a href="bitcoin:1K3n4MXNRSMHk28oTfXEvDunWFthePvd8v?label=lutim">BitCoin</a>.</p>
|
||||
<h4>Is it really anonymous?</h4>
|
||||
<p>Yes, it is! On the other side, for legal reasons, your IP address will be stored when you send an image. Don't panic, it is normally the case of all sites on which you send files!</p>
|
||||
<p>The IP address of the image's sender is permanently retained.</p>
|
||||
<p>If the files are deleted if you ask it while posting it, their SHA512 footprint are retained.</p>
|
||||
<h4>How to report an image?</h4>
|
||||
<p>Please contact the administrator: [_2]</p>
|
||||
<h4>How do you pronounce LUTIm?</h4>
|
||||
<p>Juste like you pronounce the French word <a href="https://fr.wikipedia.org/wiki/Lutin">lutin</a> (/ly.tɛ̃/).</p>
|
||||
<h4>What about the software which provides the service?</h4>
|
||||
<p>The LUTIm software is a <a href="http://en.wikipedia.org/wiki/Free_software">free software</a>, which allows you to download and install it on you own server. Have a look at the <a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL</a> to see what you can do.</p>
|
||||
<p>For more details, see the <a href="https://github.com/ldidry/lutim">Github</a> page of the project.</p>
|
||||
<h4>Contributors</h4>
|
||||
<ul>
|
||||
<li>Luc Didry, aka Sky (<a href="http://www.fiat-tux.fr">http://www.fiat-tux.fr</a>), main developer</li>
|
||||
<li>Jean-Bernard Marcon, aka Goofy (<a href="https://github.com/goofy-bz">https://github.com/goofy-bz</a>)</li>
|
||||
<li>Jean-Christophe Bach (<a href="https://github.com/jcb">https://github.com/jcb</a>)</li>
|
||||
</ul>
|
||||
EOF
|
||||
|
||||
our %Lexicon = (
|
||||
'license' => 'License:',
|
||||
'fork-me' => 'Fork me on Github !',
|
||||
'share-twitter' => 'Share on Twitter',
|
||||
'informations' => 'Informations',
|
||||
'informations-body' => $inf_body,
|
||||
'view-link' => 'View link',
|
||||
'download-link' => 'Download link',
|
||||
'twitter-link' => 'Link for put in a tweet',
|
||||
'some-bad' => 'Something bad happened',
|
||||
'delete-first' => 'Delete at first view?',
|
||||
'delete-day' => 'Delete after 24 hours?',
|
||||
'upload_image' => 'Send an image',
|
||||
'image-only' => 'Only images are allowed',
|
||||
'go' => 'Let\'s go!',
|
||||
'drag-n-drop' => 'Drag & drop images here',
|
||||
'or' => '-or-',
|
||||
'file-browser' => 'Click to open the file browser',
|
||||
'image_not_found' => 'Unable to find the image',
|
||||
'no_more_short' => 'There is no more available URL. Retry or contact the administrator. [_1]',
|
||||
'no_valid_file' => 'The file [_1] is not an image.',
|
||||
'file_too_big' => 'The file exceed the size limit ([_1])',
|
||||
'no_time_limit' => 'No time limit',
|
||||
'24_hours' => '24 hours',
|
||||
'7_days' => '7 days',
|
||||
'30_days' => '30 days',
|
||||
'1_year' => 'One year',
|
||||
'pushed-images' => ' sent images on this instance from beginning.',
|
||||
'graph-data-once-a-day' => 'The graph\'s datas are not updated in real-time.',
|
||||
'lutim-stats' => 'LUTIm\'s statistics',
|
||||
'back-to-index' => 'Back to homepage',
|
||||
'stop_upload' => 'Uploading is currently disabled, please try later or contact the administrator ([_1]).',
|
||||
'download_error' => 'An error occured while downloading the image.',
|
||||
'no_valid_url' => 'The URL is not valid.',
|
||||
'image_url' => 'Image URL',
|
||||
'upload_image_url' => 'Upload an image with its URL',
|
||||
'delay_0' => 'no time limit',
|
||||
'delay_1' => '24 hours',
|
||||
'delay_days' => '[_1] days',
|
||||
'delay_365' => '1 year',
|
||||
'max_delay' => 'Warning! The maximum time limit for an image is [_1] day(s), even if you choose "no time limit".',
|
||||
'crypt_image' => 'Encrypt the image (LUTIm does not keep the key).',
|
||||
'always_encrypt' => 'The images are encrypted on the server (LUTIm does not keep the key).',
|
||||
);
|
||||
|
||||
1;
|
||||
@@ -1,77 +0,0 @@
|
||||
package Lutim::I18N::fr;
|
||||
use Mojo::Base 'Lutim::I18N';
|
||||
|
||||
my $inf_body = <<EOF;
|
||||
<h4>Qu’est-ce que LUTIm ?</h4>
|
||||
<p>LUTIm est un service gratuit et anonyme d’hébergement d’images. Il s’agit aussi du nom du logiciel (libre) qui fournit ce service.</p>
|
||||
<p>Les images déposées sur LUTIm peuvent être stockées indéfiniment, ou s’effacer dès le premier affichage ou au bout de 24h.</p>
|
||||
<h4>Comment ça marche ?</h4>
|
||||
<p>Faites glisser des images dans la zone prévue à cet effet ou sélectionnez un fichier de façon classique et LUTIm vous fournira deux URLs en retour. Une pour afficher l’image, l’autre pour la télécharger directement.</p>
|
||||
<p>Vous pouvez, de façon facultative, demander à ce que la ou les images déposées sur LUTIm soient supprimées après leur premier affichage (ou téléchargement) ou au bout de 24 heures.</p>
|
||||
<h4>C’est vraiment gratuit ?</h4>
|
||||
<p>Oui, ça l’est ! Par contre, si vous avez envie de soutenir le développeur, vous pouvez faire un microdon avec <a href="https://flattr.com/submit/auto?user_id=_SKy_&url=[_1]&title=LUTIm&category=software">Flattr</a> ou en <a href="bitcoin:1K3n4MXNRSMHk28oTfXEvDunWFthePvd8v?label=lutim">BitCoin</a>.</p>
|
||||
<h4>C’est vraiment anonyme ?</h4>
|
||||
<p>Oui, ça l’est ! Par contre, pour des raisons légales, votre adresse IP sera enregistrée lorsque vous enverrez une image. Ne vous affolez pas, c’est de toute façon normalement le cas de tous les sites sur lesquels vous envoyez des fichiers !</p>
|
||||
<p>L’IP de la personne ayant déposé l’image est stockée de manière définitive.</p>
|
||||
<p>Si les fichiers sont bien supprimés si vous en avez exprimé le choix, leur empreinte SHA512 est toutefois conservée.</p>
|
||||
<h4>Comment peut-on faire pour signaler une image ?</h4>
|
||||
<p>Veuillez contacter l’administrateur : [_2]</p>
|
||||
<h4>Comment doit-on prononcer LUTIm ?</h4>
|
||||
<p>Comme on prononce <a href="https://fr.wikipedia.org/wiki/Lutin">lutin</a> !</p>
|
||||
<h4>Et à propos du logiciel qui fournit le service ?</h4>
|
||||
<p>Le logiciel LUTIm est un <a href="https://fr.wikipedia.org/wiki/Logiciel_libre">logiciel libre</a>, ce qui vous permet de le télécharger et de l’installer sur votre propre serveur. Jetez un coup d’œil à l’<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL</a> pour voir quels sont vos droits.</p>
|
||||
<p>Pour plus de détails, consultez la page <a href="https://github.com/ldidry/lutim">Github</a> du projet.</p>
|
||||
<h4>Contributeurs</h4>
|
||||
<ul>
|
||||
<li>Luc Didry, aka Sky (<a href="http://www.fiat-tux.fr">http://www.fiat-tux.fr</a>), développeur principal</li>
|
||||
<li>Jean-Bernard Marcon, aka Goofy (<a href="https://github.com/goofy-bz">https://github.com/goofy-bz</a>)</li>
|
||||
<li>Jean-Christophe Bach (<a href="https://github.com/jcb">https://github.com/jcb</a>)</li>
|
||||
</ul>
|
||||
EOF
|
||||
|
||||
our %Lexicon = (
|
||||
'license' => 'Licence :',
|
||||
'fork-me' => 'Fork me on Github',
|
||||
'share-twitter' => 'Partager sur Twitter',
|
||||
'informations' => 'Informations',
|
||||
'informations-body' => $inf_body,
|
||||
'view-link' => 'Lien d\'affichage',
|
||||
'download-link' => 'Lien de téléchargement',
|
||||
'twitter-link' => 'Lien pour mettre dans un tweet',
|
||||
'some-bad' => 'Un problème est survenu',
|
||||
'delete-first' => 'Supprimer au premier accès ?',
|
||||
'delete-day' => 'Supprimer après 24 heures ?',
|
||||
'upload_image' => 'Envoyez une image',
|
||||
'image-only' => 'Seules les images sont acceptées',
|
||||
'go' => 'Allons-y !',
|
||||
'drag-n-drop' => 'Déposez vos images ici',
|
||||
'or' => '-ou-',
|
||||
'file-browser' => 'Cliquez pour utiliser le navigateur de fichier',
|
||||
'image_not_found' => 'Impossible de trouver l\'image',
|
||||
'no_more_short' => 'Il n\'y a plus d\'URL disponible. Veuillez réessayer ou contactez l\'administrateur. [_1].',
|
||||
'no_valid_file' => 'Le fichier [_1] n\'est pas une image.',
|
||||
'file_too_big' => 'Le fichier dépasse la limite de taille ([_1])',
|
||||
'no_time_limit' => 'Pas de limitation de durée',
|
||||
'24_hours' => '24 heures',
|
||||
'7_days' => '7 jours',
|
||||
'30_days' => '30 jours',
|
||||
'1_year' => 'Un an',
|
||||
'pushed-images' => ' images envoyées sur cette instance depuis le début.',
|
||||
'graph-data-once-a-day' => 'Les données du graphique ne sont pas mises à jour en temps réél.',
|
||||
'lutim-stats' => 'Statistiques de LUTIm',
|
||||
'back-to-index' => 'Retour à la page d\'accueil',
|
||||
'stop_upload' => 'L\'envoi d\'images est actuellement désactivé, veuillez réessayer plus ou contacter l\'administrateur ([_1]).',
|
||||
'download_error' => 'Une erreur est survenue lors du téléchargement de l\'image.',
|
||||
'no_valid_url' => 'l\'URL n\'est pas valide.',
|
||||
'image_url' => 'URL de l\'image',
|
||||
'upload_image_url' => 'Déposer une image par son URL',
|
||||
'delay_0' => 'pas de limitation de durée',
|
||||
'delay_1' => '24 heures',
|
||||
'delay_days' => '[_1] jours',
|
||||
'delay_365' => '1 an',
|
||||
'max_delay' => 'Attention ! Le délai maximal de rétention d\'une image est de [_1] jour(s), même si vous choisissez « pas de limitation de durée ».',
|
||||
'crypt_image' => 'Chiffrer l\'image (LUTIm ne stocke pas la clé).',
|
||||
'always_encrypt' => 'Les images sont chiffrées sur le serveur (LUTIm ne stocke pas la clé).',
|
||||
);
|
||||
|
||||
1;
|
||||
50
lib/Lutim/Plugin/Headers.pm
Normal file
50
lib/Lutim/Plugin/Headers.pm
Normal file
@@ -0,0 +1,50 @@
|
||||
package Lutim::Plugin::Headers;
|
||||
use Mojo::Base 'Mojolicious::Plugin';
|
||||
|
||||
sub register {
|
||||
my ($self, $app) = @_;
|
||||
|
||||
# Assets Cache headers
|
||||
$app->plugin('StaticCache' => { even_in_dev => 1 });
|
||||
|
||||
# Add CSP Header
|
||||
if (!defined($app->config('csp')) || (defined($app->config('csp')) && $app->config('csp') ne '')) {
|
||||
my $directives = {
|
||||
'default-src' => "'none'",
|
||||
'script-src' => "'self' 'unsafe-eval'",
|
||||
'style-src' => "'self' 'unsafe-inline'",
|
||||
'connect-src' => "'self'",
|
||||
'img-src' => "'self' data:",
|
||||
'font-src' => "'self'",
|
||||
'form-action' => "'self'",
|
||||
'base-uri' => "'self'",
|
||||
};
|
||||
|
||||
my $frame_ancestors = '';
|
||||
$frame_ancestors = "'none'" if $app->config('x_frame_options') eq 'DENY';
|
||||
$frame_ancestors = "'self'" if $app->config('x_frame_options') eq 'SAMEORIGIN';
|
||||
if ($app->config('x_frame_options') =~ m#^ALLOW-FROM#) {
|
||||
$frame_ancestors = $app->config('x_frame_options');
|
||||
$frame_ancestors =~ s#ALLOW-FROM +##;
|
||||
}
|
||||
$directives->{'frame-ancestors'} = $frame_ancestors if $frame_ancestors;
|
||||
|
||||
$app->plugin('CSPHeader',
|
||||
csp => $app->config('csp'),
|
||||
directives => $directives
|
||||
);
|
||||
}
|
||||
|
||||
# Add other headers
|
||||
$app->hook(
|
||||
before_dispatch => sub {
|
||||
my $c = shift;
|
||||
|
||||
$c->res->headers->header('X-Frame-Options' => $app->config('x_frame_options')) if $app->config('x_frame_options');
|
||||
$c->res->headers->header('X-Content-Type-Options' => $app->config('x_content_type_options')) if $app->config('x_content_type_options');
|
||||
$c->res->headers->header('X-XSS-Protection' => $app->config('x_xss_protection')) if $app->config('x_xss_protection');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
||||
361
lib/Lutim/Plugin/Helpers.pm
Normal file
361
lib/Lutim/Plugin/Helpers.pm
Normal file
@@ -0,0 +1,361 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim::Plugin::Helpers;
|
||||
use Mojo::Base 'Mojolicious::Plugin';
|
||||
use Mojo::Util qw(quote);
|
||||
use Mojo::File;
|
||||
use Crypt::CBC;
|
||||
use Data::Entropy qw(entropy_source);
|
||||
use DateTime;
|
||||
use Mojo::Util qw(decode);
|
||||
use ISO::639_1;
|
||||
use Digest::MD5 'md5';
|
||||
|
||||
sub register {
|
||||
my ($self, $app) = @_;
|
||||
|
||||
|
||||
if ($app->config('dbtype') eq 'postgresql') {
|
||||
require Mojo::Pg;
|
||||
$app->plugin('PgURLHelper');
|
||||
$app->helper(pg => \&_pg);
|
||||
|
||||
# Database migration
|
||||
my $migrations = Mojo::Pg::Migrations->new(pg => $app->pg);
|
||||
if ($app->mode eq 'development' && $ENV{LUTIM_DEBUG}) {
|
||||
$migrations->from_file('utilities/migrations/postgresql.sql')->migrate(0)->migrate(3);
|
||||
} else {
|
||||
$migrations->from_file('utilities/migrations/postgresql.sql')->migrate(3);
|
||||
}
|
||||
} elsif ($app->config('dbtype') eq 'sqlite') {
|
||||
# SQLite database migration if needed
|
||||
require Mojo::SQLite;
|
||||
$app->helper(sqlite => \&_sqlite);
|
||||
|
||||
my $sql = Mojo::SQLite->new('sqlite:'.$app->config('db_path'));
|
||||
my $migrations = $sql->migrations;
|
||||
if ($app->mode eq 'development' && $ENV{LUTIM_DEBUG}) {
|
||||
$migrations->from_file('utilities/migrations/sqlite.sql')->migrate(0)->migrate(2);
|
||||
} else {
|
||||
$migrations->from_file('utilities/migrations/sqlite.sql')->migrate(2);
|
||||
}
|
||||
}
|
||||
|
||||
$app->helper(render_file => \&_render_file);
|
||||
$app->helper(ip => \&_ip);
|
||||
$app->helper(provisioning => \&_provisioning);
|
||||
$app->helper(shortener => \&_shortener);
|
||||
$app->helper(stop_upload => \&_stop_upload);
|
||||
$app->helper(max_delay => \&_max_delay);
|
||||
$app->helper(default_delay => \&_default_delay);
|
||||
$app->helper(is_selected => \&_is_selected);
|
||||
$app->helper(is_wm_selected => \&_is_wm_selected);
|
||||
$app->helper(crypt => \&_crypt);
|
||||
$app->helper(decrypt => \&_decrypt);
|
||||
$app->helper(delete_image => \&_delete_image);
|
||||
$app->helper(iso639_native_name => \&_iso639_native_name);
|
||||
$app->helper(prefix => \&_prefix);
|
||||
}
|
||||
|
||||
sub _pg {
|
||||
my $c = shift;
|
||||
|
||||
state $pg = Mojo::Pg->new($c->app->pg_url($c->app->config('pgdb')));
|
||||
return $pg;
|
||||
}
|
||||
|
||||
sub _sqlite {
|
||||
my $c = shift;
|
||||
|
||||
state $sqlite = Mojo::SQLite->new('sqlite:'.$c->app->config('db_path'));
|
||||
return $sqlite;
|
||||
}
|
||||
|
||||
sub _render_file {
|
||||
my $c = shift;
|
||||
my ($im_loaded, $img, $dl, $key, $thumb) = @_;
|
||||
|
||||
my ($filename, $path, $iv, $mediatype, $no_cache) = ($img->filename, $img->path, $img->iv, $img->mediatype, $img->delete_at_first_view);
|
||||
|
||||
my $expires = ($img->delete_at_day) ? $img->delete_at_day : 360;
|
||||
my $dt = DateTime->from_epoch( epoch => $expires * 86400 + $img->created_at);
|
||||
$dt->set_time_zone('GMT');
|
||||
$expires = $dt->strftime("%a, %d %b %Y %H:%M:%S GMT");
|
||||
|
||||
$dl = 'attachment' if ($mediatype =~ m/svg/);
|
||||
$filename = quote($filename);
|
||||
|
||||
unless (-f $path && -r $path) {
|
||||
$c->app->log->error("Cannot read file [$path]. error [$!]");
|
||||
$c->flash(
|
||||
msg => $c->l('Unable to find the image: it has been deleted.')
|
||||
);
|
||||
return 500;
|
||||
}
|
||||
|
||||
$mediatype =~ s/x-//;
|
||||
|
||||
my $headers = Mojo::Headers->new();
|
||||
if ($no_cache || defined($thumb)) {
|
||||
$headers->add('Cache-Control' => 'no-cache, no-store, max-age=0, must-revalidate');
|
||||
} else {
|
||||
$headers->add('Expires' => $expires);
|
||||
}
|
||||
$headers->add('Content-Type' => $mediatype.';name='.$filename);
|
||||
$headers->add('Content-Disposition' => $dl.';filename='.$filename);
|
||||
$c->res->content->headers($headers);
|
||||
|
||||
my $cache;
|
||||
if ($c->config('cache_max_size') != 0 || scalar(@{$c->config('memcached_servers')})) {
|
||||
$cache = $c->chi('lutim_images_cache')->compute($img->short, undef, sub {
|
||||
if ($key) {
|
||||
return {
|
||||
asset => $c->decrypt($key, $path, $iv),
|
||||
key => $key
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
asset => Mojo::File->new($path)->slurp,
|
||||
};
|
||||
}
|
||||
});
|
||||
if ($key && $key ne $cache->{key}) {
|
||||
my $tmp = $c->decrypt($key, $path, $iv);
|
||||
$cache->{asset} = $tmp;
|
||||
$c->chi('lutim_images_cache')->replace(
|
||||
$img->short,
|
||||
{
|
||||
asset => $tmp,
|
||||
key => $key
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if ($key) {
|
||||
$cache = {
|
||||
asset => $c->decrypt($key, $path, $iv),
|
||||
};
|
||||
} else {
|
||||
$cache = {
|
||||
asset => Mojo::File->new($path)->slurp,
|
||||
};
|
||||
}
|
||||
}
|
||||
# Extend expiration time
|
||||
my $asset = Mojo::Asset::Memory->new;
|
||||
$asset->add_chunk($cache->{asset});
|
||||
|
||||
if (defined $thumb && $im_loaded && $mediatype ne 'image/svg+xml' && $mediatype !~ m#image/(x-)?xcf# && $mediatype ne 'image/avif') { # ImageMagick don't work in Debian with svg (for now?)
|
||||
my $im = Image::Magick->new;
|
||||
$im->BlobToImage($asset->slurp);
|
||||
|
||||
# Create the thumbnail
|
||||
if ($thumb eq '') {
|
||||
$im->Resize(geometry => 'x'.$c->config('thumbnail_size'));
|
||||
} else {
|
||||
$im->Resize(geometry => $thumb);
|
||||
}
|
||||
|
||||
# Replace the asset with the thumbnail
|
||||
$asset = Mojo::Asset::Memory->new->add_chunk($im->ImageToBlob());
|
||||
}
|
||||
|
||||
$c->res->content->asset($asset);
|
||||
$headers->add('Content-Length' => $asset->size);
|
||||
|
||||
return $c->rendered(200);
|
||||
}
|
||||
|
||||
sub _ip {
|
||||
my $c = shift;
|
||||
my $ip_only = shift || 0;
|
||||
|
||||
my $proxy = $c->req->headers->header('X-Forwarded-For');
|
||||
|
||||
my $ip = ($proxy) ? $proxy : $c->tx->remote_address;
|
||||
|
||||
my $remote_port = (defined($c->req->headers->header('X-Remote-Port'))) ? $c->req->headers->header('X-Remote-Port') : $c->tx->remote_port;
|
||||
|
||||
return ($ip_only) ? $ip : "$ip remote port:$remote_port";
|
||||
}
|
||||
|
||||
sub _provisioning {
|
||||
my $c = shift;
|
||||
|
||||
# Create some short patterns for provisioning
|
||||
my $img = Lutim::DB::Image->new(app => $c->app);
|
||||
if ($img->count_empty < $c->app->config('provisioning')) {
|
||||
for (my $i = 0; $i < $c->app->config('provis_step'); $i++) {
|
||||
my $short;
|
||||
do {
|
||||
$short = $c->shortener($c->app->config('length'));
|
||||
} while ($img->count_short($short) || $short eq 'about' || $short eq 'stats' || $short eq 'd' || $short eq 'm' || $short eq 'gallery' || $short eq 'zip' || $short eq 'infos');
|
||||
|
||||
$img->short($short)
|
||||
->counter(0)
|
||||
->enabled(1)
|
||||
->delete_at_first_view(0)
|
||||
->delete_at_day(0)
|
||||
->mod_token($c->shortener($c->app->config('token_length')))
|
||||
->write('provisioning');
|
||||
|
||||
$img = Lutim::DB::Image->new(app => $c->app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub _shortener {
|
||||
my $c = shift;
|
||||
my $length = shift;
|
||||
|
||||
my @chars = ('a'..'z','A'..'Z','0'..'9');
|
||||
my $result = '';
|
||||
foreach (1..$length) {
|
||||
$result .= $chars[entropy_source->get_int(scalar(@chars))];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub _stop_upload {
|
||||
my $c = shift;
|
||||
|
||||
if (-f 'stop-upload' || -f 'stop-upload.manual') {
|
||||
$c->stash(
|
||||
stop_upload => $c->l('Uploading is currently disabled, please try later or contact the administrator (%1).', $c->app->config('contact'))
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub _max_delay {
|
||||
my $c = shift;
|
||||
|
||||
return $c->app->config('max_delay') if ($c->app->config('max_delay') >= 0);
|
||||
|
||||
warn "max_delay set to a negative value. Default to 0.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub _default_delay {
|
||||
my $c = shift;
|
||||
|
||||
return $c->app->config('default_delay') if ($c->app->config('default_delay') >= 0);
|
||||
|
||||
warn "default_delay set to a negative value. Default to 0.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub _is_selected {
|
||||
my $c = shift;
|
||||
my $num = shift;
|
||||
|
||||
return ($num == $c->default_delay) ? 'selected="selected"' : '';
|
||||
}
|
||||
|
||||
sub _is_wm_selected {
|
||||
my $c = shift;
|
||||
my $wm = shift;
|
||||
|
||||
return ($wm eq $c->config('watermark_default')) ? 'selected="selected"' : '';
|
||||
}
|
||||
|
||||
sub _key_from_key {
|
||||
my $key = shift;
|
||||
|
||||
# Key size for Blowfish is 56
|
||||
my $ks = 56;
|
||||
my $material = md5($key);
|
||||
while (length($material) < $ks) {
|
||||
$material .= md5($material);
|
||||
}
|
||||
return substr($material,0,$ks);
|
||||
}
|
||||
|
||||
sub _crypt {
|
||||
my $c = shift;
|
||||
my $upload = shift;
|
||||
my $filename = shift;
|
||||
|
||||
my $key = $c->shortener($c->config('crypto_key_length'));
|
||||
my $iv = $c->shortener(8);
|
||||
|
||||
my $cipher = Crypt::CBC->new(
|
||||
-key => _key_from_key($key),
|
||||
-cipher => 'Blowfish',
|
||||
-header => 'none',
|
||||
-literal_key => 1,
|
||||
-pbkdf => 'pbkdf2',
|
||||
-iv => $iv
|
||||
);
|
||||
|
||||
$cipher->start('encrypting');
|
||||
|
||||
my $crypt_asset = Mojo::Asset::File->new;
|
||||
|
||||
$crypt_asset->add_chunk($cipher->crypt($upload->slurp));
|
||||
$crypt_asset->add_chunk($cipher->finish);
|
||||
|
||||
my $crypt_upload = Mojo::Upload->new;
|
||||
$crypt_upload->filename($filename);
|
||||
$crypt_upload->asset($crypt_asset);
|
||||
|
||||
return ($crypt_upload, $key, $iv);
|
||||
}
|
||||
|
||||
sub _decrypt {
|
||||
my $c = shift;
|
||||
my $key = _key_from_key(shift);
|
||||
my $file = shift;
|
||||
my $iv = shift;
|
||||
$iv = 'dupajasi' unless $iv;
|
||||
|
||||
my $cipher = Crypt::CBC->new(
|
||||
-key => $key,
|
||||
-cipher => 'Blowfish',
|
||||
-header => 'none',
|
||||
-literal_key => 1,
|
||||
-pbkdf => 'pbkdf2',
|
||||
-iv => $iv
|
||||
);
|
||||
|
||||
$cipher->start('decrypting');
|
||||
|
||||
my $decrypt_asset = Mojo::Asset::File->new;
|
||||
|
||||
open(my $f, "<",$file) or die "Unable to read encrypted file: $!";
|
||||
binmode $f;
|
||||
while (read($f, my $buffer, 1024)) {
|
||||
$decrypt_asset->add_chunk($cipher->crypt($buffer));
|
||||
}
|
||||
$decrypt_asset->add_chunk($cipher->finish) ;
|
||||
|
||||
return $decrypt_asset->slurp;
|
||||
}
|
||||
|
||||
sub _delete_image {
|
||||
my $c = shift;
|
||||
my $img = shift;
|
||||
if ($c->config('cache_max_size') != 0 || scalar(@{$c->config('memcached_servers')})) {
|
||||
$c->chi('lutim_images_cache')->remove($img->short);
|
||||
}
|
||||
unlink $img->path or warn "Could not unlink ".$img->path.": $!";
|
||||
$img->disable();
|
||||
}
|
||||
|
||||
sub _iso639_native_name {
|
||||
my $c = shift;
|
||||
return ucfirst(decode 'UTF-8', get_iso639_1(shift)->{nativeName});
|
||||
}
|
||||
|
||||
sub _prefix {
|
||||
my $c = shift;
|
||||
|
||||
my $prefix = $c->url_for('/')->to_abs;
|
||||
# Forced domain
|
||||
$prefix->host($c->config('fixed_domain')) if (defined($c->config('fixed_domain')) && $c->config('fixed_domain') ne '');
|
||||
# Hack for prefix (subdir) handling
|
||||
$prefix .= '/' unless ($prefix =~ m#/$#);
|
||||
return $prefix;
|
||||
}
|
||||
|
||||
1;
|
||||
38
lib/Lutim/Plugin/Lang.pm
Normal file
38
lib/Lutim/Plugin/Lang.pm
Normal file
@@ -0,0 +1,38 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lutim::Plugin::Lang;
|
||||
use Mojo::Base 'Mojolicious::Plugin';
|
||||
use Mojo::Collection;
|
||||
use Mojo::File;
|
||||
|
||||
sub register {
|
||||
my ($self, $app) = @_;
|
||||
|
||||
$app->helper(available_langs => \&_available_langs);
|
||||
|
||||
$app->hook(
|
||||
before_dispatch => sub {
|
||||
my $c = shift;
|
||||
$c->languages($c->cookie('lutim_lang')) if $c->cookie('lutim_lang');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
sub _available_langs {
|
||||
my $c = shift;
|
||||
|
||||
state $langs = Mojo::Collection->new(
|
||||
glob($c->app->home->rel_file('themes/'.$c->config('theme').'/lib/Lutim/I18N/*po')),
|
||||
glob($c->app->home->rel_file('themes/default/lib/Lutim/I18N/*po'))
|
||||
)->map(
|
||||
sub {
|
||||
Mojo::File->new($_)->basename('.po');
|
||||
}
|
||||
)->uniq->sort(
|
||||
sub {
|
||||
$c->l($a) cmp $c->l($b)
|
||||
}
|
||||
)->to_array;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package LutimModel;
|
||||
|
||||
# Create database
|
||||
use ORLite {
|
||||
file => 'lutim.db',
|
||||
unicode => 1,
|
||||
create => sub {
|
||||
my $dbh = shift;
|
||||
$dbh->do(
|
||||
'CREATE TABLE lutim (
|
||||
short TEXT PRIMARY KEY,
|
||||
path TEXT,
|
||||
footprint TEXT,
|
||||
enabled INTEGER,
|
||||
mediatype TEXT,
|
||||
filename TEXT,
|
||||
counter INTEGER,
|
||||
delete_at_first_view INTEGER,
|
||||
delete_at_day INTEGER,
|
||||
created_at INTEGER,
|
||||
created_by TEXT,
|
||||
last_access_at INTEGER,
|
||||
last_access_by INTEGER)'
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
1;
|
||||
@@ -1,38 +0,0 @@
|
||||
package Mojolicious::Command::cron::cleanbdd;
|
||||
use Mojo::Base 'Mojolicious::Command';
|
||||
use LutimModel;
|
||||
use Mojo::Util qw(slurp decode);
|
||||
use Mojolicious::Plugin::Config;
|
||||
|
||||
has description => 'Delete IP addresses from database after configured delay.';
|
||||
has usage => sub { shift->extract_usage };
|
||||
|
||||
sub run {
|
||||
my $c = shift;
|
||||
|
||||
my $config = Mojolicious::Plugin::Config->parse(decode('UTF-8', slurp 'lutim.conf'), 'lutim.conf');
|
||||
|
||||
$config->{keep_ip_during} = (defined($config->{keep_ip_during})) ? $config->{keep_ip_during} : 365;
|
||||
|
||||
my $separation = time() - $config->{keep_ip_during} * 86400;
|
||||
|
||||
LutimModel->do(
|
||||
'UPDATE lutim SET created_by = "" WHERE path IS NOT NULL AND created_at < ?',
|
||||
{},
|
||||
$separation
|
||||
);
|
||||
}
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Mojolicious::Command::cron::cleanbdd - Delete IP addresses from database after configured delay
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage: script/lutim cron cleanbdd
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
@@ -1,36 +0,0 @@
|
||||
package Mojolicious::Command::cron::cleanfiles;
|
||||
use Mojo::Base 'Mojolicious::Command';
|
||||
use LutimModel;
|
||||
use Mojo::Util qw(slurp decode);
|
||||
use Mojolicious::Plugin::Config;
|
||||
|
||||
has description => 'Delete expired files.';
|
||||
has usage => sub { shift->extract_usage };
|
||||
|
||||
sub run {
|
||||
my $c = shift;
|
||||
|
||||
my $config = Mojolicious::Plugin::Config->parse(decode('UTF-8', slurp 'lutim.conf'), 'lutim.conf');
|
||||
|
||||
my $time = time();
|
||||
my @images = LutimModel::Lutim->select('WHERE enabled = 1 AND (delete_at_day * 86400) < (? - created_at) AND delete_at_day != 0', $time);
|
||||
|
||||
for my $image (@images) {
|
||||
$image->update(enabled => 0);
|
||||
unlink $image->path();
|
||||
}
|
||||
}
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Mojolicious::Command::cron::cleanfiles - Delete expired files
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage: script/lutim cron cleanfiles
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
@@ -1,64 +0,0 @@
|
||||
package Mojolicious::Command::cron::stats;
|
||||
use Mojo::Base 'Mojolicious::Command';
|
||||
use LutimModel;
|
||||
use Mojo::DOM;
|
||||
use Mojo::Util qw(slurp spurt decode);
|
||||
use DateTime;
|
||||
use Mojolicious::Plugin::Config;
|
||||
|
||||
has description => 'Generate statistics about LUTIm.';
|
||||
has usage => sub { shift->extract_usage };
|
||||
|
||||
sub run {
|
||||
my $c = shift;
|
||||
|
||||
my $config = Mojolicious::Plugin::Config->parse(decode('UTF-8', slurp 'lutim.conf'), 'lutim.conf');
|
||||
$config->{stats_day_num} = (defined($config->{stats_day_num})) ? $config->{stats_day_num} : 365;
|
||||
|
||||
my $text = slurp('templates/data.html.ep.template');
|
||||
my $dom = Mojo::DOM->new($text);
|
||||
my $thead_tr = $dom->at('table thead tr');
|
||||
my $tbody_tr = $dom->at('table tbody tr');
|
||||
my $tbody_t2 = $tbody_tr->next;
|
||||
|
||||
my $separation = time() - $config->{stats_day_num} * 86400;
|
||||
|
||||
my %data;
|
||||
for my $img (LutimModel::Lutim->select('WHERE path IS NOT NULL AND created_at >= ?', $separation)) {
|
||||
my $time = DateTime->from_epoch(epoch => $img->created_at);
|
||||
my ($year, $month, $day) = ($time->year(), $time->month(), $time->day());
|
||||
|
||||
if (defined($data{$year}->{$month}->{$day})) {
|
||||
$data{$year}->{$month}->{$day} += 1;
|
||||
} else {
|
||||
$data{$year}->{$month}->{$day} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
my $total = LutimModel::Lutim->count('WHERE path IS NOT NULL AND created_at < ?', $separation);
|
||||
for my $year (sort keys %data) {
|
||||
for my $month (sort keys %{$data{$year}}) {
|
||||
for my $day (sort keys %{$data{$year}->{$month}}) {
|
||||
$thead_tr->append_content('<th>'."$day/$month/$year".'</th>'."\n");
|
||||
$tbody_tr->append_content('<td>'.$data{$year}->{$month}->{$day}.'</td>'."\n");
|
||||
$total += $data{$year}->{$month}->{$day};
|
||||
$tbody_t2->append_content('<td>'.$total.'</td>'."\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
spurt $dom, 'templates/data.html.ep';
|
||||
}
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Mojolicious::Command::cron::stats - Stats generator
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage: script/lutim cron stats
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
@@ -1,68 +0,0 @@
|
||||
package Mojolicious::Command::cron::watch;
|
||||
use Mojo::Base 'Mojolicious::Command';
|
||||
use Mojo::Util qw(slurp decode);
|
||||
use Mojolicious::Plugin::Config;
|
||||
use Filesys::DiskUsage qw/du/;
|
||||
use LutimModel;
|
||||
use Switch;
|
||||
|
||||
has description => 'Watch the files directory and take action when over quota';
|
||||
has usage => sub { shift->extract_usage };
|
||||
|
||||
sub run {
|
||||
my $c = shift;
|
||||
|
||||
my $config = Mojolicious::Plugin::Config->parse(decode('UTF-8', slurp 'lutim.conf'), 'lutim.conf');
|
||||
|
||||
if (defined($config->{max_total_size})) {
|
||||
my $total = du(qw/files/);
|
||||
|
||||
if ($total > $config->{max_total_size}) {
|
||||
if (defined($config->{policy_when_full})) {
|
||||
say "[LUTIm cron job watch] Files directory is over quota ($total > ".$config->{max_total_size}.")";
|
||||
switch ($config->{policy_when_full}) {
|
||||
case 'warn' {
|
||||
say "[LUTIm cron job watch] Please, delete some files or increase quota (".$config->{max_total_size}.")";
|
||||
}
|
||||
case 'stop-upload' {
|
||||
open (my $fh, '>', 'stop-upload') or die ("Couldn't open stop-upload: $!");
|
||||
close($fh);
|
||||
say '[LUTIm cron job watch] Uploads are stopped. Delete some images and the stop-upload file to reallow uploads.';
|
||||
}
|
||||
case 'delete' {
|
||||
say '[LUTIm cron job watch] Older files are being deleted';
|
||||
do {
|
||||
for my $img (LutimModel::Lutim->select('WHERE path IS NOT NULL AND enabled = 1 ORDER BY created_at ASC LIMIT 50')) {
|
||||
unlink $img->path() or warn "Could not unlink ".$img->path.": $!";
|
||||
$img->update(enabled => 0);
|
||||
}
|
||||
} while (du(qw/files/) > $config->{max_total_size});
|
||||
}
|
||||
else {
|
||||
say '[LUTIm cron job watch] Unrecognized policy_when_full option: '.$config->{policy_when_full}.'. Aborting.';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
say "[LUTIm cron job watch] Files directory over quota ($total > ".$config->{max_total_size}.") but no configured policy_when_full option!" ;
|
||||
}
|
||||
} else {
|
||||
unlink 'stop-upload' if (-f 'stop-upload');
|
||||
}
|
||||
} else {
|
||||
say "[LUTIm cron job watch] No max_total_size found in the configuration file. Aborting.";
|
||||
}
|
||||
}
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Mojolicious::Command::cron::watch - Delete IP addresses from database after configured delay
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage: script/lutim cron watch
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
51
lib/Mounter.pm
Normal file
51
lib/Mounter.pm
Normal file
@@ -0,0 +1,51 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Mounter;
|
||||
use Mojo::Base 'Mojolicious';
|
||||
use Mojo::File;
|
||||
use FindBin qw($Bin);
|
||||
use File::Spec qw(catfile);
|
||||
use Lutim::DefaultConfig qw($default_config);
|
||||
|
||||
# This method will run once at server start
|
||||
sub startup {
|
||||
my $self = shift;
|
||||
|
||||
push @{$self->commands->namespaces}, 'Lutim::Command';
|
||||
|
||||
my $cfile = Mojo::File->new($Bin, '..' , 'lutim.conf');
|
||||
if (defined $ENV{MOJO_CONFIG}) {
|
||||
$cfile = Mojo::File->new($ENV{MOJO_CONFIG});
|
||||
unless (-e $cfile->to_abs) {
|
||||
$cfile = Mojo::File->new($Bin, '..', $ENV{MOJO_CONFIG});
|
||||
}
|
||||
}
|
||||
my $config = $self->plugin('Config', {
|
||||
file => $cfile,
|
||||
default => $default_config
|
||||
});
|
||||
|
||||
$config->{prefix} = $config->{url_sub_dir} if (defined($config->{url_sub_dir}) && $config->{prefix} eq '/');
|
||||
|
||||
$self->app->log->warn('"url_sub_dir" configuration option is deprecated. Use "prefix" instead. "url_sub_dir" will be removed in the future') if (defined($config->{url_sub_dir}));
|
||||
|
||||
# Themes handling
|
||||
shift @{$self->static->paths};
|
||||
if ($config->{theme} ne 'default') {
|
||||
my $theme = $self->home->rel_file('themes/'.$config->{theme});
|
||||
push @{$self->static->paths}, $theme.'/public' if -d $theme.'/public';
|
||||
}
|
||||
push @{$self->static->paths}, $self->home->rel_file('themes/default/public');
|
||||
|
||||
# Static assets gzipping
|
||||
$self->plugin('GzipStatic');
|
||||
|
||||
# Headers
|
||||
$self->plugin('Lutim::Plugin::Headers');
|
||||
|
||||
# Helpers
|
||||
$self->plugin('Lutim::Plugin::Helpers');
|
||||
|
||||
$self->plugin('Mount' => {$config->{prefix} => File::Spec->catfile($Bin, '..', 'script', 'application')});
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,28 +1,325 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
# See README.md for more explanations
|
||||
{
|
||||
####################
|
||||
# Hypnotoad settings
|
||||
####################
|
||||
# see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings
|
||||
hypnotoad => {
|
||||
# array of IP addresses and ports you want to listen to
|
||||
listen => ['http://127.0.0.1:8080'],
|
||||
user => 'www-data',
|
||||
group => 'www-data'
|
||||
# if you use Lutim behind a reverse proxy like Nginx, you want to set proxy to 1
|
||||
# if you use Lutim directly, let it commented
|
||||
#proxy => 1,
|
||||
},
|
||||
|
||||
################
|
||||
# Lutim settings
|
||||
################
|
||||
|
||||
# put a way to contact you here and uncomment it
|
||||
# mandatory
|
||||
#contact => 'John Doe, admin[at]example.com',
|
||||
secrets => ['fdjsofjoihrei'], # please provide a random string
|
||||
length => 8, # optional
|
||||
provis_step => 5, # optional
|
||||
provisioning => 100, # optional
|
||||
tweet_card_via => '@framasky', # optional
|
||||
max_file_size => 10485760, # optional, size in octets, you can write it 10*1024*1024
|
||||
#piwik_img => 'https://piwik.example.org/piwik.php?idsite=1&rec=1', # optional, only the piwik image tracker is allowed, no javascript
|
||||
#hosted_by => 'My super hoster <img src="http://hoster.example.com" alt="Hoster logo">', # optional
|
||||
#https => 0, # optional, set to 1 if you use Lutim behind a secure web server
|
||||
#stats_day_num => 365, # optional, number of days shown in /stats page (used with script/lutim cron stats)
|
||||
#keep_ip_during => 365, # optional, number of days, after that delay, the IP addresses of image creators will be deleted (used with script/lutim cron cleanbdd)
|
||||
#max_total_size => 10*1024*1024*1024, # optional, maximum total size of the files directory (used with script/lutim cron watch)
|
||||
#policy_when_full => 'warn', # optional, policy when files directory is over max_total_size. Valid values are 'warn', 'stop-upload' and 'delete'. See README.
|
||||
#broadcast_message => 'Maintenance', #optional, the broadcast_message is displayed on all pages of LUTIm (but no in json response)
|
||||
#allowed_domains => ['http://1.example.com', 'http://2.example.com'], #optional, array of authorized domains for API calls. If you want to authorize everyone to use the API: ['*']
|
||||
#default_delay => 0, #optional: what is the default time limit for files? Valid values are 0, 1, 7, 30 and 365.
|
||||
#max_delay => 0, #optional, if defined, the images will be deleted after that delay (in days), even if they were uploaded with "no delay" (or value superior to max\_delay) option and a warning message will be displayed on homepage.
|
||||
#always_encrypt => 0, #optional, if set to 1, all the images will be encrypted
|
||||
|
||||
# random string used to encrypt cookies
|
||||
# mandatory
|
||||
secrets => ['fdjsofjoihrei'],
|
||||
|
||||
# choose a theme. See the available themes in `themes` directory
|
||||
# optional, default is 'default'
|
||||
#theme => 'default',
|
||||
|
||||
# length of the images random URL
|
||||
# optional, default is 8
|
||||
#length => 8,
|
||||
|
||||
# length of the encryption key
|
||||
# optional, default is 8
|
||||
#crypto_key_length => 8,
|
||||
|
||||
# how many URLs will be provisioned in a batch ?
|
||||
# optional, default is 5
|
||||
#provis_step => 5,
|
||||
|
||||
# max number of URLs to be provisioned
|
||||
# optional, default is 100
|
||||
#provisioning => 100,
|
||||
|
||||
# anti-flood protection delay, in seconds
|
||||
# users won't be able to ask Lutim to download images more than one per anti_flood_delay seconds
|
||||
# optional, default is 5
|
||||
#anti_flood_delay => 5,
|
||||
|
||||
# max image size, in octets
|
||||
# you can write it 10*1024*1024
|
||||
# optional, default is 10485760
|
||||
#max_file_size => 10485760,
|
||||
|
||||
# if you want to have piwik statistics, provide a piwik image tracker
|
||||
# only the image tracker is allowed, no javascript
|
||||
# optional, no default
|
||||
#piwik_img => 'https://piwik.example.org/piwik.php?idsite=1&rec=1',
|
||||
|
||||
# if you want to include something in the right of the screen, put it here
|
||||
# here's an example to put the logo of your hoster
|
||||
# optional, no default
|
||||
#hosted_by => 'My super hoster <img src="http://hoster.example.com" alt="Hoster logo">',
|
||||
|
||||
# DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED
|
||||
# Lutim now checks if the X-Forwarded-Proto header is present and equal to https.
|
||||
# set to 1 if you use Lutim behind a secure web server
|
||||
# optional, default is 0
|
||||
#https => 0,
|
||||
|
||||
# broadcast_message which will displayed on all pages of Lutim (but no in json response)
|
||||
# optional, no default
|
||||
#broadcast_message => 'Maintenance',
|
||||
|
||||
# array of authorized domains for API calls.
|
||||
# if you want to authorize everyone to use the API: ['*']
|
||||
# optional, no domains allowed by default
|
||||
#allowed_domains => ['http://1.example.com', 'http://2.example.com'],
|
||||
|
||||
# default time limit for files
|
||||
# valid values are 0, 1, 7, 30 and 365
|
||||
# optional, default is 0 (no limit)
|
||||
#default_delay => 0,
|
||||
|
||||
# comma-separated values proposed for delays
|
||||
# optional, default is '0,1,7,30,365'
|
||||
#proposed_delays => '0,1,7,30,365',
|
||||
|
||||
# number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay)
|
||||
# a warning message will be displayed on homepage
|
||||
# optional, default is 0 (no limit)
|
||||
#max_delay => 0,
|
||||
|
||||
# if set to 1, all the images will be encrypted and the encryption option will no be displayed
|
||||
# optional, default is 0
|
||||
#always_encrypt => 0,
|
||||
|
||||
# you can allow to use a watermark on the uploaded images (or enforce its use)
|
||||
# define a path to the watermark image (provide an image with alpha channel)
|
||||
# you can define the path relative to lutim directory or set an absolute path
|
||||
# to disable the usage of a watermark, leave it blank or commented
|
||||
# optional, no default
|
||||
#watermark_path => '',
|
||||
|
||||
# the watermark can be a tiling one or a single one
|
||||
# when using a small one, you can choose where to place it
|
||||
# valid values are 'Center', 'North', 'NorthEast', 'East', 'SouthEast', 'South', 'SouthWest', 'West' and 'NorthWest' (case insensitive)
|
||||
# optional, default is 'SouthEast'
|
||||
#watermark_placement => 'SouthEast',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be used by default
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_default => 'none',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be enforced (users will always have a watermark and won’t be able to disable it)
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_enforce => 'none',
|
||||
|
||||
# length of the image's delete token
|
||||
# optional, default is 24
|
||||
#token_length => 24,
|
||||
|
||||
# URL sub-directory in which you want Lutim to be accessible
|
||||
# example: you want to have Lutim under https://example.org/lutim/
|
||||
# => set prefix to '/lutim' or to '/lutim/', it doesn't matter
|
||||
# optional, defaut is /
|
||||
#prefix => '/',
|
||||
|
||||
# if set to 1, Lutim will try to prevent its use without using the web interface
|
||||
# optional, default is 0
|
||||
#disable_api => 0,
|
||||
|
||||
# Define a path to the upload directory, where the uploaded images will be stored.
|
||||
# You can define it relative to lutim directory or set an absolute path.
|
||||
# The path is stored in database for each uploaded file, so you’ll need to do some
|
||||
# SQL commands if you move the images in an other directory (if you keep the old directory
|
||||
# where it was, you have nothing to do).
|
||||
# Remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is 'files'
|
||||
#upload_dir => 'files',
|
||||
|
||||
# choose what database you want to use
|
||||
# valid choices are sqlite and postgresql (all lowercase)
|
||||
# optional, default is sqlite
|
||||
#dbtype => 'sqlite',
|
||||
|
||||
# SQLite ONLY - only used if dbtype is set to sqlite
|
||||
# define a path to the SQLite database
|
||||
# you can define it relative to lutim directory or set an absolute path
|
||||
# remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is lutim.db
|
||||
#db_path => 'lutim.db',
|
||||
|
||||
# PostgreSQL ONLY - only used if dbtype is set to postgresql
|
||||
# these are the credentials to access the PostgreSQL database
|
||||
# mandatory if you choosed postgresql as dbtype
|
||||
#pgdb => {
|
||||
# database => 'lutim',
|
||||
# host => 'localhost',
|
||||
# #user => 'DBUSER',
|
||||
# #pwd => 'DBPASSWORD'
|
||||
#},
|
||||
|
||||
# use Minion instead of directly increase counters
|
||||
# need to launch a minion worker service if enabled
|
||||
# optional, Minion is disabled by default
|
||||
#minion => {
|
||||
# enabled => 0,
|
||||
# # Which Minion backend to use?
|
||||
# # valid values are sqlite and postgresql (all lowercase)
|
||||
# # mandatory if Minion is enabled, default is sqlite
|
||||
# dbtype => 'sqlite',
|
||||
# # SQLite ONLY - only used if if you choose sqlite as Minion backend, define the path to the minion database
|
||||
# # you can define it relative to lutim directory or set an absolute path
|
||||
# # remember that it has to be in a directory writable by Lutim user
|
||||
# # optional, default is minion.db
|
||||
# db_path => 'minion.db',
|
||||
# # PostgreSQL ONLY - only used if you choose postgresql as Minion backend
|
||||
# # these are the credentials to access the Minion's PostgreSQL database
|
||||
# # mandatory if you choosed postgresql as Minion backend, no default
|
||||
# pgdb => {
|
||||
# database => 'lutim_minion',
|
||||
# host => 'localhost',
|
||||
# #user => 'DBUSER',
|
||||
# #pwd => 'DBPASSWORD'
|
||||
# }
|
||||
#},
|
||||
|
||||
# set `ldap` if you want that only authenticated users can shorten URLs
|
||||
# please note that everybody can still use shortend URLs
|
||||
# optional, no default
|
||||
#ldap => {
|
||||
# uri => 'ldaps://ldap.example.org', # server URI
|
||||
# user_tree => 'ou=users,dc=example,dc=org', # search base DN
|
||||
# bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN
|
||||
# bind_pwd => 'secr3t', # search bind password
|
||||
# user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.)
|
||||
# user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.)
|
||||
#},
|
||||
|
||||
# set `htpasswd` if you want to use an htpasswd file instead of ldap
|
||||
# create the file with `htpasswd -c lutim.passwd user`, update it with `htpasswd lutim.passwd user2`
|
||||
# make sure that lutim can read the file!
|
||||
# optional, no default
|
||||
#htpasswd => 'lutim.passwd',
|
||||
|
||||
# if you've set ldap or htpasswd above, the session will last `session_duration` seconds before
|
||||
# the user needs to reauthenticate
|
||||
# optional, default is 3600
|
||||
#session_duration => 3600,
|
||||
|
||||
# disable counters of images
|
||||
# set to 1 to disable counters
|
||||
# optional, counters are enabled by default
|
||||
#disable_img_stats => 0,
|
||||
|
||||
# define the height of the thumbnails generated at users' will
|
||||
# this is not the height of the thumbnails send after upload,
|
||||
# we're talking about thumbnails generated when someone asked for
|
||||
# https://example.org/lutim/tesrinp?thumb
|
||||
# this works only if you have ImageMagick
|
||||
# optional, default is 100 (pixels)
|
||||
#thumbnail_size => 100,
|
||||
|
||||
# maximum number of files that can be downloaded as a single zip archive
|
||||
# if too many files are asked, it results a timeout, so Lutim split the zip URL
|
||||
# in multiple URLs, each with max_file_size images.
|
||||
# timeout behavior depends heavily on your server ressources (CPU) and if images
|
||||
# are encrypted
|
||||
# optional, default is 15
|
||||
#max_files_in_zip => 15,
|
||||
|
||||
# maximum size (in MB) of memory allowed for the image cache
|
||||
# Lutim has a built-in memory-based image cache to accelerate responses to often-viewed images.
|
||||
# This setting makes the cache remove oldest viewed image if the cache size is over it.
|
||||
# WARNING: a cache is created for each hypnotoad worker, which by default is twice the number of
|
||||
# CPUs you have. See http://mojolicious.org/perldoc/Mojo/Server/Hypnotoad#workers for details
|
||||
# So, if you have 4 workers and set cache_max_size to 100, the real maximum size of RAM used for
|
||||
# cache is 400MB.
|
||||
# If set to 0, the cache is disabled
|
||||
# optional, default is 0
|
||||
#cache_max_size => 0,
|
||||
|
||||
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
|
||||
# If set to [], the use of memcached is disabled.
|
||||
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
|
||||
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
|
||||
# servers.
|
||||
# exemple of valid value: ['127.0.0.1:11211']
|
||||
# optional, default is []
|
||||
#memcached_servers => [],
|
||||
|
||||
# enable or disable Lutim built-in logs
|
||||
# set to 1 to disable logs
|
||||
# optional, default is 0
|
||||
#quiet_logs => 0,
|
||||
|
||||
# Content-Security-Policy header that will be sent by Lutim
|
||||
# Set to '' to disable CSP header
|
||||
# https://content-security-policy.com/ provides a good documentation about CSP.
|
||||
# https://report-uri.com/home/generate provides a tool to generate a CSP header.
|
||||
# optional, default is "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"
|
||||
# NB: unsafe-inline for script-src and style-src are here only because morris,
|
||||
# the graph library used in the stats page requires it
|
||||
# the default value is good for `default` theme
|
||||
#csp => "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'",
|
||||
|
||||
# X-Frame-Options header that will be sent by Lutim
|
||||
# Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/'
|
||||
# Set to '' to disable X-Frame-Options header
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
||||
# Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly
|
||||
# to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors)
|
||||
# optional, default is 'DENY'
|
||||
#x_frame_options => 'DENY',
|
||||
|
||||
# X-Content-Type-Options that will be sent by Lutim
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
|
||||
# Set to '' to disable X-Content-Type-Options header
|
||||
# optional, default is 'nosniff'
|
||||
#x_content_type_options => 'nosniff',
|
||||
|
||||
# X-XSS-Protection that will be sent by Lutim
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
|
||||
# Set to '' to disable X-XSS-Protection header
|
||||
# optional, default is '1; mode=block'
|
||||
#x_xss_protection => '1; mode=block',
|
||||
|
||||
# if set, the uploaded images will use this domain
|
||||
# optional
|
||||
#fixed_domain => 'example.org',
|
||||
|
||||
##########################
|
||||
# Lutim cron jobs settings
|
||||
##########################
|
||||
|
||||
# number of days shown in /stats page (used with script/lutim cron stats)
|
||||
# optional, default is 365
|
||||
#stats_day_num => 365,
|
||||
|
||||
# number of days senders' IP addresses are kept in database
|
||||
# after that delay, they will be deleted from database (used with script/lutim cron cleanbdd)
|
||||
# optional, default is 365
|
||||
#keep_ip_during => 365,
|
||||
|
||||
# max size of the files directory, in octets
|
||||
# used by script/lutim cron watch to trigger an action
|
||||
# optional, no default
|
||||
#max_total_size => 10*1024*1024*1024,
|
||||
|
||||
# default action when files directory is over max_total_size (used with script/lutim cron watch)
|
||||
# valid values are 'warn', 'stop-upload' and 'delete'
|
||||
# please, see readme
|
||||
# optional, default is 'warn'
|
||||
#policy_when_full => 'warn',
|
||||
|
||||
# images which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task
|
||||
# if delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted
|
||||
# optional, no default
|
||||
#delete_no_longer_viewed_files => 90
|
||||
};
|
||||
|
||||
7
public/css/bootstrap.min.css
vendored
7
public/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
8
public/css/fontello-codes.css
vendored
8
public/css/fontello-codes.css
vendored
@@ -1,8 +0,0 @@
|
||||
|
||||
.icon-eye:before { content: '\e806'; } /* '' */
|
||||
.icon-download:before { content: '\e804'; } /* '' */
|
||||
.icon-bitcoin:before { content: '\e802'; } /* '' */
|
||||
.icon-spinner:before { content: '\e805'; } /* '' */
|
||||
.icon-github-circled:before { content: '\e800'; } /* '' */
|
||||
.icon-touiteur:before { content: '\e801'; } /* '' */
|
||||
.icon-flattr:before { content: '\e803'; } /* '' */
|
||||
61
public/css/fontello-embedded.css
vendored
61
public/css/fontello-embedded.css
vendored
File diff suppressed because one or more lines are too long
8
public/css/fontello-ie7-codes.css
vendored
8
public/css/fontello-ie7-codes.css
vendored
@@ -1,8 +0,0 @@
|
||||
|
||||
.icon-eye { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-download { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-bitcoin { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-spinner { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-github-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-touiteur { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-flattr { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
19
public/css/fontello-ie7.css
vendored
19
public/css/fontello-ie7.css
vendored
@@ -1,19 +0,0 @@
|
||||
[class^="icon-"], [class*=" icon-"] {
|
||||
font-family: 'fontello';
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
|
||||
/* fix buttons height */
|
||||
line-height: 1em;
|
||||
|
||||
/* you can be more comfortable with increased icons size */
|
||||
/* font-size: 120%; */
|
||||
}
|
||||
|
||||
.icon-eye { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-download { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-bitcoin { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-spinner { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-github-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-touiteur { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
.icon-flattr { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); }
|
||||
59
public/css/fontello.css
vendored
59
public/css/fontello.css
vendored
@@ -1,59 +0,0 @@
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.eot?69050751');
|
||||
src: url('../font/fontello.eot?69050751#iefix') format('embedded-opentype'),
|
||||
url('../font/fontello.woff?69050751') format('woff'),
|
||||
url('../font/fontello.ttf?69050751') format('truetype'),
|
||||
url('../font/fontello.svg?69050751#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
|
||||
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
|
||||
/*
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.svg?69050751#fontello') format('svg');
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
[class^="icon-"]:before, [class*=" icon-"]:before {
|
||||
font-family: "fontello";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
speak: none;
|
||||
|
||||
display: inline-block;
|
||||
text-decoration: inherit;
|
||||
width: 1em;
|
||||
margin-right: .2em;
|
||||
text-align: center;
|
||||
/* opacity: .8; */
|
||||
|
||||
/* For safety - reset parent styles, that can break glyph codes*/
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
|
||||
/* fix buttons height, for twitter bootstrap */
|
||||
line-height: 1em;
|
||||
|
||||
/* Animation center compensation - margins should be symmetric */
|
||||
/* remove if not needed */
|
||||
margin-left: .2em;
|
||||
|
||||
/* you can be more comfortable with increased icons size */
|
||||
/* font-size: 120%; */
|
||||
|
||||
/* Uncomment for 3D effect */
|
||||
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
|
||||
}
|
||||
|
||||
.icon-eye:before { content: '\e806'; } /* '' */
|
||||
.icon-download:before { content: '\e804'; } /* '' */
|
||||
.icon-bitcoin:before { content: '\e802'; } /* '' */
|
||||
.icon-spinner:before { content: '\e805'; } /* '' */
|
||||
.icon-github-circled:before { content: '\e800'; } /* '' */
|
||||
.icon-touiteur:before { content: '\e801'; } /* '' */
|
||||
.icon-flattr:before { content: '\e803'; } /* '' */
|
||||
Binary file not shown.
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Copyright (C) 2014 by original authors @ fontello.com</metadata>
|
||||
<defs>
|
||||
<font id="fontello" horiz-adv-x="1000" >
|
||||
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
|
||||
<missing-glyph horiz-adv-x="1000" />
|
||||
<glyph glyph-name="eye" unicode="" d="m929 314q-85 132-213 197q34-58 34-125q0-104-73-177t-177-73t-177 73t-73 177q0 67 34 125q-128-65-213-197q75-114 187-182t242-68t242 68t187 182z m-402 215q0 11-8 19t-19 7q-70 0-120-50t-50-119q0-12 8-19t19-8t19 8t8 19q0 48 34 82t82 34q11 0 19 8t8 19z m473-215q0-19-11-38q-78-129-210-206t-279-77t-279 77t-210 206q-11 19-11 38t11 39q78 128 210 205t279 78t279-78t210-205q11-20 11-39z" horiz-adv-x="1000" />
|
||||
<glyph glyph-name="download" unicode="" d="m714 100q0 15-10 25t-25 11t-26-11t-10-25t10-25t26-11t25 11t10 25z m143 0q0 15-10 25t-26 11t-25-11t-10-25t10-25t25-11t26 11t10 25z m72 125v-179q0-22-16-37t-38-16h-821q-23 0-38 16t-16 37v179q0 22 16 38t38 16h259l75-76q33-32 76-32t76 32l76 76h259q22 0 38-16t16-38z m-182 318q10-23-8-40l-250-250q-10-10-25-10t-25 10l-250 250q-17 17-8 40q10 21 33 21h143v250q0 15 11 25t25 11h143q14 0 25-11t10-25v-250h143q24 0 33-21z" horiz-adv-x="928.6" />
|
||||
<glyph glyph-name="bitcoin" unicode="" d="m651 493q10-102-73-144q65-16 98-58t25-119q-4-40-18-70t-36-49t-54-33t-68-19t-81-9v-142h-86v140q-45 0-68 1v-141h-86v142q-10 0-30 1t-31 0h-112l18 102h62q27 0 32 28v225h9q-4 0-9 0v160q-7 38-50 38h-62v92l119-1q35 0 54 1v141h86v-138q45 1 68 1v137h86v-141q44-4 78-13t63-25t46-43t20-64z m-120-304q0 20-8 35t-21 26t-32 17t-36 10t-42 5t-38 2t-36 0t-27-1v-189q5 0 21 0t27 0t29 1t33 2t32 5t31 8t26 11t22 17t14 22t5 29z m-39 265q0 19-7 33t-17 23t-27 16t-31 9t-34 5t-33 1t-30 0t-22-1v-171q3 0 20 0t26 0t27 1t31 3t29 6t27 10t21 15t15 22t5 28z" horiz-adv-x="714.3" />
|
||||
<glyph glyph-name="spinner" unicode="" d="m277 100q0-33-24-57t-57-23q-33 0-56 23t-24 57t24 57t56 23q33 0 57-23t24-57z m241-107q0-30-21-51t-51-21t-50 21t-21 51t21 50t50 21t51-21t21-50z m-339 357q0-37-27-63t-63-26t-63 26t-26 63t26 63t63 26t63-26t27-63z m580-250q0-26-18-44t-45-18t-44 18t-18 44t18 44t44 19t45-19t18-44z m-464 500q0-41-29-69t-70-29t-69 29t-29 69t29 69t69 29t70-29t29-69z m259 107q0-45-32-76t-76-31t-75 31t-32 76t32 76t75 31t76-31t32-76z m303-357q0-22-15-38t-38-16t-38 16t-16 38t16 38t38 16t38-16t15-38z m-116 250q0-18-13-32t-32-13t-31 13t-13 32t13 31t31 14t32-14t13-31z" horiz-adv-x="875" />
|
||||
<glyph glyph-name="github-circled" unicode="" d="m857 350q0-140-82-252t-211-155q-15-3-22 4t-7 17v118q0 54-29 79q32 3 57 10t53 22t45 37t30 58t11 84q0 68-44 115q21 51-5 114q-15 5-45-6t-51-25l-21-13q-52 15-107 15t-108-15q-8 6-23 15t-47 22t-48 7q-24-63-4-114q-44-47-44-115q0-47 12-83t29-59t45-37t52-22t57-10q-22-20-27-58q-12-5-25-8t-32-3t-36 12t-31 35q-11 18-27 29t-28 14l-11 1q-12 0-16-2t-3-7t5-8t7-6l4-3q12-6 24-21t18-29l5-13q8-21 25-34t37-17t39-4t31 2l13 3q0-22 0-50t1-30q0-10-8-17t-22-4q-129 43-211 155t-82 252q0 117 58 215t155 156t216 58t215-58t156-156t57-215z" horiz-adv-x="857.1" />
|
||||
<glyph glyph-name="touiteur" unicode="" d="m904 622q-37-54-90-93q0-8 0-23q0-73-21-145t-64-139t-103-117t-144-82t-181-30q-151 0-276 81q19-3 43-3q126 0 224 77q-59 2-105 36t-64 89q19-2 34-2q24 0 48 6q-63 13-104 62t-41 115v2q38-21 82-23q-37 25-59 64t-22 86q0 49 25 91q68-83 164-133t208-55q-5 21-5 41q0 75 53 127t127 53q79 0 132-57q61 12 114 44q-20-64-79-100q52 6 104 28z" horiz-adv-x="928.6" />
|
||||
<glyph glyph-name="flattr" unicode="" d="m180 424l0-210l-180-180l0 414q0 302 278 302l442 0l-312-310q-6-6-10-6q-8 0-12 10l0 128q-98 0-112-2q-94-16-94-146z m440 64l180 180l0-414q0-304-278-304l-440 0l312 312q2 6 8 6q8 0 12-10l0-128q98 0 112 2q94 16 94 146l0 210z" horiz-adv-x="800" />
|
||||
</font>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.8 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 234 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 6.6 KiB |
381
public/js/bootstrap.js
vendored
381
public/js/bootstrap.js
vendored
@@ -1,381 +0,0 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: alert.js v3.1.1
|
||||
* http://getbootstrap.com/javascript/#alerts
|
||||
* ========================================================================
|
||||
* Copyright 2011-2014 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) {
|
||||
'use strict';
|
||||
|
||||
// ALERT CLASS DEFINITION
|
||||
// ======================
|
||||
|
||||
var dismiss = '[data-dismiss="alert"]'
|
||||
var Alert = function (el) {
|
||||
$(el).on('click', dismiss, this.close)
|
||||
}
|
||||
|
||||
Alert.prototype.close = function (e) {
|
||||
var $this = $(this)
|
||||
var selector = $this.attr('data-target')
|
||||
|
||||
if (!selector) {
|
||||
selector = $this.attr('href')
|
||||
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
|
||||
}
|
||||
|
||||
var $parent = $(selector)
|
||||
|
||||
if (e) e.preventDefault()
|
||||
|
||||
if (!$parent.length) {
|
||||
$parent = $this.hasClass('alert') ? $this : $this.parent()
|
||||
}
|
||||
|
||||
$parent.trigger(e = $.Event('close.bs.alert'))
|
||||
|
||||
if (e.isDefaultPrevented()) return
|
||||
|
||||
$parent.removeClass('in')
|
||||
|
||||
function removeElement() {
|
||||
$parent.trigger('closed.bs.alert').remove()
|
||||
}
|
||||
|
||||
$.support.transition && $parent.hasClass('fade') ?
|
||||
$parent
|
||||
.one($.support.transition.end, removeElement)
|
||||
.emulateTransitionEnd(150) :
|
||||
removeElement()
|
||||
}
|
||||
|
||||
|
||||
// ALERT PLUGIN DEFINITION
|
||||
// =======================
|
||||
|
||||
var old = $.fn.alert
|
||||
|
||||
$.fn.alert = function (option) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('bs.alert')
|
||||
|
||||
if (!data) $this.data('bs.alert', (data = new Alert(this)))
|
||||
if (typeof option == 'string') data[option].call($this)
|
||||
})
|
||||
}
|
||||
|
||||
$.fn.alert.Constructor = Alert
|
||||
|
||||
|
||||
// ALERT NO CONFLICT
|
||||
// =================
|
||||
|
||||
$.fn.alert.noConflict = function () {
|
||||
$.fn.alert = old
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
// ALERT DATA-API
|
||||
// ==============
|
||||
|
||||
$(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
|
||||
|
||||
}(jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: modal.js v3.1.1
|
||||
* http://getbootstrap.com/javascript/#modals
|
||||
* ========================================================================
|
||||
* Copyright 2011-2014 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) {
|
||||
'use strict';
|
||||
|
||||
// MODAL CLASS DEFINITION
|
||||
// ======================
|
||||
|
||||
var Modal = function (element, options) {
|
||||
this.options = options
|
||||
this.$element = $(element)
|
||||
this.$backdrop =
|
||||
this.isShown = null
|
||||
|
||||
if (this.options.remote) {
|
||||
this.$element
|
||||
.find('.modal-content')
|
||||
.load(this.options.remote, $.proxy(function () {
|
||||
this.$element.trigger('loaded.bs.modal')
|
||||
}, this))
|
||||
}
|
||||
}
|
||||
|
||||
Modal.DEFAULTS = {
|
||||
backdrop: true,
|
||||
keyboard: true,
|
||||
show: true
|
||||
}
|
||||
|
||||
Modal.prototype.toggle = function (_relatedTarget) {
|
||||
return this[!this.isShown ? 'show' : 'hide'](_relatedTarget)
|
||||
}
|
||||
|
||||
Modal.prototype.show = function (_relatedTarget) {
|
||||
var that = this
|
||||
var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
|
||||
|
||||
this.$element.trigger(e)
|
||||
|
||||
if (this.isShown || e.isDefaultPrevented()) return
|
||||
|
||||
this.isShown = true
|
||||
|
||||
this.escape()
|
||||
|
||||
this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
|
||||
|
||||
this.backdrop(function () {
|
||||
var transition = $.support.transition && that.$element.hasClass('fade')
|
||||
|
||||
if (!that.$element.parent().length) {
|
||||
that.$element.appendTo(document.body) // don't move modals dom position
|
||||
}
|
||||
|
||||
that.$element
|
||||
.show()
|
||||
.scrollTop(0)
|
||||
|
||||
if (transition) {
|
||||
that.$element[0].offsetWidth // force reflow
|
||||
}
|
||||
|
||||
that.$element
|
||||
.addClass('in')
|
||||
.attr('aria-hidden', false)
|
||||
|
||||
that.enforceFocus()
|
||||
|
||||
var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
|
||||
|
||||
transition ?
|
||||
that.$element.find('.modal-dialog') // wait for modal to slide in
|
||||
.one($.support.transition.end, function () {
|
||||
that.$element.focus().trigger(e)
|
||||
})
|
||||
.emulateTransitionEnd(300) :
|
||||
that.$element.focus().trigger(e)
|
||||
})
|
||||
}
|
||||
|
||||
Modal.prototype.hide = function (e) {
|
||||
if (e) e.preventDefault()
|
||||
|
||||
e = $.Event('hide.bs.modal')
|
||||
|
||||
this.$element.trigger(e)
|
||||
|
||||
if (!this.isShown || e.isDefaultPrevented()) return
|
||||
|
||||
this.isShown = false
|
||||
|
||||
this.escape()
|
||||
|
||||
$(document).off('focusin.bs.modal')
|
||||
|
||||
this.$element
|
||||
.removeClass('in')
|
||||
.attr('aria-hidden', true)
|
||||
.off('click.dismiss.bs.modal')
|
||||
|
||||
$.support.transition && this.$element.hasClass('fade') ?
|
||||
this.$element
|
||||
.one($.support.transition.end, $.proxy(this.hideModal, this))
|
||||
.emulateTransitionEnd(300) :
|
||||
this.hideModal()
|
||||
}
|
||||
|
||||
Modal.prototype.enforceFocus = function () {
|
||||
$(document)
|
||||
.off('focusin.bs.modal') // guard against infinite focus loop
|
||||
.on('focusin.bs.modal', $.proxy(function (e) {
|
||||
if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
|
||||
this.$element.focus()
|
||||
}
|
||||
}, this))
|
||||
}
|
||||
|
||||
Modal.prototype.escape = function () {
|
||||
if (this.isShown && this.options.keyboard) {
|
||||
this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {
|
||||
e.which == 27 && this.hide()
|
||||
}, this))
|
||||
} else if (!this.isShown) {
|
||||
this.$element.off('keyup.dismiss.bs.modal')
|
||||
}
|
||||
}
|
||||
|
||||
Modal.prototype.hideModal = function () {
|
||||
var that = this
|
||||
this.$element.hide()
|
||||
this.backdrop(function () {
|
||||
that.removeBackdrop()
|
||||
that.$element.trigger('hidden.bs.modal')
|
||||
})
|
||||
}
|
||||
|
||||
Modal.prototype.removeBackdrop = function () {
|
||||
this.$backdrop && this.$backdrop.remove()
|
||||
this.$backdrop = null
|
||||
}
|
||||
|
||||
Modal.prototype.backdrop = function (callback) {
|
||||
var animate = this.$element.hasClass('fade') ? 'fade' : ''
|
||||
|
||||
if (this.isShown && this.options.backdrop) {
|
||||
var doAnimate = $.support.transition && animate
|
||||
|
||||
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
|
||||
.appendTo(document.body)
|
||||
|
||||
this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
|
||||
if (e.target !== e.currentTarget) return
|
||||
this.options.backdrop == 'static'
|
||||
? this.$element[0].focus.call(this.$element[0])
|
||||
: this.hide.call(this)
|
||||
}, this))
|
||||
|
||||
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
|
||||
|
||||
this.$backdrop.addClass('in')
|
||||
|
||||
if (!callback) return
|
||||
|
||||
doAnimate ?
|
||||
this.$backdrop
|
||||
.one($.support.transition.end, callback)
|
||||
.emulateTransitionEnd(150) :
|
||||
callback()
|
||||
|
||||
} else if (!this.isShown && this.$backdrop) {
|
||||
this.$backdrop.removeClass('in')
|
||||
|
||||
$.support.transition && this.$element.hasClass('fade') ?
|
||||
this.$backdrop
|
||||
.one($.support.transition.end, callback)
|
||||
.emulateTransitionEnd(150) :
|
||||
callback()
|
||||
|
||||
} else if (callback) {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MODAL PLUGIN DEFINITION
|
||||
// =======================
|
||||
|
||||
var old = $.fn.modal
|
||||
|
||||
$.fn.modal = function (option, _relatedTarget) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('bs.modal')
|
||||
var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||
|
||||
if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
|
||||
if (typeof option == 'string') data[option](_relatedTarget)
|
||||
else if (options.show) data.show(_relatedTarget)
|
||||
})
|
||||
}
|
||||
|
||||
$.fn.modal.Constructor = Modal
|
||||
|
||||
|
||||
// MODAL NO CONFLICT
|
||||
// =================
|
||||
|
||||
$.fn.modal.noConflict = function () {
|
||||
$.fn.modal = old
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
// MODAL DATA-API
|
||||
// ==============
|
||||
|
||||
$(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
|
||||
var $this = $(this)
|
||||
var href = $this.attr('href')
|
||||
var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
|
||||
var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
|
||||
|
||||
if ($this.is('a')) e.preventDefault()
|
||||
|
||||
$target
|
||||
.modal(option, this)
|
||||
.one('hide', function () {
|
||||
$this.is(':visible') && $this.focus()
|
||||
})
|
||||
})
|
||||
|
||||
$(document)
|
||||
.on('show.bs.modal', '.modal', function () { $(document.body).addClass('modal-open') })
|
||||
.on('hidden.bs.modal', '.modal', function () { $(document.body).removeClass('modal-open') })
|
||||
|
||||
}(jQuery);
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: transition.js v3.1.1
|
||||
* http://getbootstrap.com/javascript/#transitions
|
||||
* ========================================================================
|
||||
* Copyright 2011-2014 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) {
|
||||
'use strict';
|
||||
|
||||
// CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
|
||||
// ============================================================
|
||||
|
||||
function transitionEnd() {
|
||||
var el = document.createElement('bootstrap')
|
||||
|
||||
var transEndEventNames = {
|
||||
'WebkitTransition' : 'webkitTransitionEnd',
|
||||
'MozTransition' : 'transitionend',
|
||||
'OTransition' : 'oTransitionEnd otransitionend',
|
||||
'transition' : 'transitionend'
|
||||
}
|
||||
|
||||
for (var name in transEndEventNames) {
|
||||
if (el.style[name] !== undefined) {
|
||||
return { end: transEndEventNames[name] }
|
||||
}
|
||||
}
|
||||
|
||||
return false // explicit for ie8 ( ._.)
|
||||
}
|
||||
|
||||
// http://blog.alexmaccaw.com/css-transitions
|
||||
$.fn.emulateTransitionEnd = function (duration) {
|
||||
var called = false, $el = this
|
||||
$(this).one($.support.transition.end, function () { called = true })
|
||||
var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
|
||||
setTimeout(callback, duration)
|
||||
return this
|
||||
}
|
||||
|
||||
$(function () {
|
||||
$.support.transition = transitionEnd()
|
||||
})
|
||||
|
||||
}(jQuery);
|
||||
7
public/js/bootstrap.min.js
vendored
7
public/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
4
public/js/jquery-2.1.0.min.js
vendored
4
public/js/jquery-2.1.0.min.js
vendored
File diff suppressed because one or more lines are too long
11
script/application
Executable file
11
script/application
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
|
||||
|
||||
# Start command line interface for application
|
||||
require Mojolicious::Commands;
|
||||
Mojolicious::Commands->start_app('Lutim');
|
||||
@@ -8,4 +8,4 @@ BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
|
||||
|
||||
# Start command line interface for application
|
||||
require Mojolicious::Commands;
|
||||
Mojolicious::Commands->start_app('Lutim');
|
||||
Mojolicious::Commands->start_app('Mounter');
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
use Mojo::Base -strict;
|
||||
|
||||
use Test::More;
|
||||
use Test::Mojo;
|
||||
|
||||
my $t = Test::Mojo->new('Lutim');
|
||||
$t->get_ok('/')->status_is(200)->content_like(qr/Mojolicious/i);
|
||||
|
||||
done_testing();
|
||||
4
t/create-pg-testdb.sql
Normal file
4
t/create-pg-testdb.sql
Normal file
@@ -0,0 +1,4 @@
|
||||
CREATE USER lutim WITH PASSWORD 'lutim';
|
||||
CREATE DATABASE lutimtest OWNER lutim;
|
||||
CREATE DATABASE lutim_miniontest OWNER lutim;
|
||||
|
||||
324
t/postgresql1.conf
Normal file
324
t/postgresql1.conf
Normal file
@@ -0,0 +1,324 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
{
|
||||
####################
|
||||
# Hypnotoad settings
|
||||
####################
|
||||
# see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings
|
||||
hypnotoad => {
|
||||
# array of IP addresses and ports you want to listen to
|
||||
listen => ['http://127.0.0.1:8080'],
|
||||
# if you use Lutim behind a reverse proxy like Nginx, you want to set proxy to 1
|
||||
# if you use Lutim directly, let it commented
|
||||
#proxy => 1,
|
||||
},
|
||||
|
||||
################
|
||||
# Lutim settings
|
||||
################
|
||||
|
||||
# put a way to contact you here and uncomment it
|
||||
# mandatory
|
||||
contact => 'John Doe, admin[at]example.com',
|
||||
|
||||
# random string used to encrypt cookies
|
||||
# mandatory
|
||||
secrets => ['fdjsofjoihrei'],
|
||||
|
||||
# choose a theme. See the available themes in `themes` directory
|
||||
# optional, default is 'default'
|
||||
#theme => 'default',
|
||||
|
||||
# length of the images random URL
|
||||
# optional, default is 8
|
||||
#length => 8,
|
||||
|
||||
# length of the encryption key
|
||||
# optional, default is 8
|
||||
#crypto_key_length => 8,
|
||||
|
||||
# how many URLs will be provisioned in a batch ?
|
||||
# optional, default is 5
|
||||
#provis_step => 5,
|
||||
|
||||
# max number of URLs to be provisioned
|
||||
# optional, default is 100
|
||||
#provisioning => 100,
|
||||
|
||||
# anti-flood protection delay, in seconds
|
||||
# users won't be able to ask Lutim to download images more than one per anti_flood_delay seconds
|
||||
# optional, default is 5
|
||||
#anti_flood_delay => 5,
|
||||
|
||||
# max image size, in octets
|
||||
# you can write it 10*1024*1024
|
||||
# optional, default is 10485760
|
||||
max_file_size => 1048576,
|
||||
|
||||
# if you want to have piwik statistics, provide a piwik image tracker
|
||||
# only the image tracker is allowed, no javascript
|
||||
# optional, no default
|
||||
#piwik_img => 'https://piwik.example.org/piwik.php?idsite=1&rec=1',
|
||||
|
||||
# if you want to include something in the right of the screen, put it here
|
||||
# here's an example to put the logo of your hoster
|
||||
# optional, no default
|
||||
#hosted_by => 'My super hoster <img src="http://hoster.example.com" alt="Hoster logo">',
|
||||
|
||||
# DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED
|
||||
# Lutim now checks if the X-Forwarded-Proto header is present and equal to https.
|
||||
# set to 1 if you use Lutim behind a secure web server
|
||||
# optional, default is 0
|
||||
#https => 0,
|
||||
|
||||
# broadcast_message which will displayed on all pages of Lutim (but no in json response)
|
||||
# optional, no default
|
||||
broadcast_message => 'test broadcast message',
|
||||
|
||||
# array of authorized domains for API calls.
|
||||
# if you want to authorize everyone to use the API: ['*']
|
||||
# optional, no domains allowed by default
|
||||
#allowed_domains => ['http://1.example.com', 'http://2.example.com'],
|
||||
|
||||
# default time limit for files
|
||||
# valid values are 0, 1, 7, 30 and 365
|
||||
# optional, default is 0 (no limit)
|
||||
default_delay => 30,
|
||||
|
||||
# comma-separated values proposed for delays
|
||||
# optional, default is '0,1,7,30,365'
|
||||
#proposed_delays => '0,1,7,30,365',
|
||||
|
||||
# number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay)
|
||||
# a warning message will be displayed on homepage
|
||||
# optional, default is 0 (no limit)
|
||||
max_delay => 200,
|
||||
|
||||
# if set to 1, all the images will be encrypted and the encryption option will no be displayed
|
||||
# optional, default is 0
|
||||
#always_encrypt => 0,
|
||||
|
||||
# you can allow to use a watermark on the uploaded images (or enforce its use)
|
||||
# define a path to the watermark image (provide an image with alpha channel)
|
||||
# you can define the path relative to lutim directory or set an absolute path
|
||||
# to disable the usage of a watermark, leave it blank or commented
|
||||
# optional, no default
|
||||
#watermark_path => '',
|
||||
|
||||
# the watermark can be a tiling one or a single one
|
||||
# when using a small one, you can choose where to place it
|
||||
# valid values are 'Center', 'North', 'NorthEast', 'East', 'SouthEast', 'South', 'SouthWest', 'West' and 'NorthWest' (case insensitive)
|
||||
# optional, default is 'SouthEast'
|
||||
#watermark_placement => 'SouthEast',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be used by default
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_default => 'none',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be enforced (users will always have a watermark and won’t be able to disable it)
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_enforce => 'none',
|
||||
|
||||
# length of the image's delete token
|
||||
# optional, default is 24
|
||||
#token_length => 24,
|
||||
|
||||
# URL sub-directory in which you want Lutim to be accessible
|
||||
# example: you want to have Lutim under https://example.org/lutim/
|
||||
# => set prefix to '/lutim' or to '/lutim/', it doesn't matter
|
||||
# optional, defaut is /
|
||||
#prefix => '/',
|
||||
|
||||
# if set to 1, Lutim will try to prevent its use without using the web interface
|
||||
# optional, default is 0
|
||||
#disable_api => 0,
|
||||
|
||||
# Define a path to the upload directory, where the uploaded images will be stored
|
||||
# You can define it relative to lutim directory or set an absolute path
|
||||
# The path is stored in database for each uploaded file, so you’ll need to do some
|
||||
# SQL commands if you change the upload_dir after getting images uploaded.
|
||||
# Remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is 'files'
|
||||
#upload_dir => 'files',
|
||||
|
||||
# choose what database you want to use
|
||||
# valid choices are sqlite and postgresql (all lowercase)
|
||||
# optional, default is sqlite
|
||||
dbtype => 'postgresql',
|
||||
|
||||
# SQLite ONLY - only used if dbtype is set to sqlite
|
||||
# define a path to the SQLite database
|
||||
# you can define it relative to lutim directory or set an absolute path
|
||||
# remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is lutim.db
|
||||
#db_path => 'lutim.db',
|
||||
|
||||
# PostgreSQL ONLY - only used if dbtype is set to postgresql
|
||||
# these are the credentials to access the PostgreSQL database
|
||||
# mandatory if you choosed postgresql as dbtype
|
||||
pgdb => {
|
||||
database => 'lutim_db',
|
||||
host => 'postgres',
|
||||
user => 'lutim',
|
||||
pwd => 'lutim_pwd'
|
||||
},
|
||||
|
||||
# use Minion instead of directly increase counters
|
||||
# need to launch a minion worker service if enabled
|
||||
# optional, Minion is disabled by default
|
||||
#minion => {
|
||||
# enabled => 0,
|
||||
# # Which Minion backend to use?
|
||||
# # valid values are sqlite and postgresql (all lowercase)
|
||||
# # mandatory if Minion is enabled, default is sqlite
|
||||
# dbtype => 'sqlite',
|
||||
# # SQLite ONLY - only used if if you choose sqlite as Minion backend, define the path to the minion database
|
||||
# # you can define it relative to lutim directory or set an absolute path
|
||||
# # remember that it has to be in a directory writable by Lutim user
|
||||
# # optional, default is minion.db
|
||||
# db_path => 'minion.db',
|
||||
# # PostgreSQL ONLY - only used if you choose postgresql as Minion backend
|
||||
# # these are the credentials to access the Minion's PostgreSQL database
|
||||
# # mandatory if you choosed postgresql as Minion backend, no default
|
||||
# pgdb => {
|
||||
# database => 'lutim_minion',
|
||||
# host => 'localhost',
|
||||
# #user => 'DBUSER',
|
||||
# #pwd => 'DBPASSWORD'
|
||||
# }
|
||||
#},
|
||||
|
||||
# set `ldap` if you want that only authenticated users can shorten URLs
|
||||
# please note that everybody can still use shortend URLs
|
||||
# optional, no default
|
||||
#ldap => {
|
||||
# uri => 'ldaps://ldap.example.org', # server URI
|
||||
# user_tree => 'ou=users,dc=example,dc=org', # search base DN
|
||||
# bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN
|
||||
# bind_pwd => 'secr3t', # search bind password
|
||||
# user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.)
|
||||
# user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.)
|
||||
#},
|
||||
|
||||
# set `htpasswd` if you want to use an htpasswd file instead of ldap
|
||||
# create the file with `htpasswd -c lutim.passwd user`, update it with `htpasswd lutim.passwd user2`
|
||||
# make sure that lutim can read the file!
|
||||
# optional, no default
|
||||
#htpasswd => 'lutim.passwd',
|
||||
|
||||
# if you've set ldap or htpasswd above, the session will last `session_duration` seconds before
|
||||
# the user needs to reauthenticate
|
||||
# optional, default is 3600
|
||||
#session_duration => 3600,
|
||||
|
||||
# disable counters of images
|
||||
# set to 1 to disable counters
|
||||
# optional, counters are enabled by default
|
||||
#disable_img_stats => 0,
|
||||
|
||||
# define the height of the thumbnails generated at users' will
|
||||
# this is not the height of the thumbnails send after upload,
|
||||
# we're talking about thumbnails generated when someone asked for
|
||||
# https://example.org/lutim/tesrinp?thumb
|
||||
# this works only if you have ImageMagick
|
||||
# optional, default is 100 (pixels)
|
||||
#thumbnail_size => 100,
|
||||
|
||||
# maximum number of files that can be downloaded as a single zip archive
|
||||
# if too many files are asked, it results a timeout, so Lutim split the zip URL
|
||||
# in multiple URLs, each with max_file_size images.
|
||||
# timeout behavior depends heavily on your server ressources (CPU) and if images
|
||||
# are encrypted
|
||||
# optional, default is 15
|
||||
#max_files_in_zip => 15,
|
||||
|
||||
# maximum size (in MB) of memory allowed for the image cache
|
||||
# Lutim has a built-in memory-based image cache to accelerate responses to often-viewed images.
|
||||
# This setting makes the cache remove oldest viewed image if the cache size is over it.
|
||||
# WARNING: a cache is created for each hypnotoad worker, which by default is twice the number of
|
||||
# CPUs you have. See http://mojolicious.org/perldoc/Mojo/Server/Hypnotoad#workers for details
|
||||
# So, if you have 4 workers and set cache_max_size to 100, the real maximum size of RAM used for
|
||||
# cache is 400MB.
|
||||
# If set to 0, the cache is disabled
|
||||
# optional, default is 0
|
||||
#cache_max_size => 0,
|
||||
|
||||
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
|
||||
# If set to [], the use of memcached is disabled.
|
||||
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
|
||||
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
|
||||
# servers.
|
||||
# exemple of valid value: ['127.0.0.1:11211']
|
||||
# optional, default is []
|
||||
#memcached_servers => [],
|
||||
|
||||
# enable or disable Lutim built-in logs
|
||||
# set to 1 to disable logs
|
||||
# optional, default is 0
|
||||
#quiet_logs => 0,
|
||||
|
||||
# Content-Security-Policy header that will be sent by Lutim
|
||||
# Set to '' to disable CSP header
|
||||
# https://content-security-policy.com/ provides a good documentation about CSP.
|
||||
# https://report-uri.com/home/generate provides a tool to generate a CSP header.
|
||||
# optional, default is "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"
|
||||
# NB: unsafe-inline for script-src and style-src are here only because morris,
|
||||
# the graph library used in the stats page requires it
|
||||
# the default value is good for `default` theme
|
||||
#csp => "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'",
|
||||
|
||||
# X-Frame-Options header that will be sent by Lutim
|
||||
# Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/'
|
||||
# Set to '' to disable X-Frame-Options header
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
||||
# Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly
|
||||
# to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors)
|
||||
# optional, default is 'DENY'
|
||||
#x_frame_options => 'DENY',
|
||||
|
||||
# X-Content-Type-Options that will be sent by Lutim
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
|
||||
# Set to '' to disable X-Content-Type-Options header
|
||||
# optional, default is 'nosniff'
|
||||
#x_content_type_options => 'nosniff',
|
||||
|
||||
# X-XSS-Protection that will be sent by Lutim
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
|
||||
# Set to '' to disable X-XSS-Protection header
|
||||
# optional, default is '1; mode=block'
|
||||
#x_xss_protection => '1; mode=block',
|
||||
|
||||
# if set, the uploaded images will use this domain
|
||||
# optional
|
||||
#fixed_domain => 'example.org',
|
||||
|
||||
##########################
|
||||
# Lutim cron jobs settings
|
||||
##########################
|
||||
|
||||
# number of days shown in /stats page (used with script/lutim cron stats)
|
||||
# optional, default is 365
|
||||
#stats_day_num => 365,
|
||||
|
||||
# number of days senders' IP addresses are kept in database
|
||||
# after that delay, they will be deleted from database (used with script/lutim cron cleanbdd)
|
||||
# optional, default is 365
|
||||
#keep_ip_during => 365,
|
||||
|
||||
# max size of the files directory, in octets
|
||||
# used by script/lutim cron watch to trigger an action
|
||||
# optional, no default
|
||||
max_total_size => 10*1024*1024*1024,
|
||||
|
||||
# default action when files directory is over max_total_size (used with script/lutim cron watch)
|
||||
# valid values are 'warn', 'stop-upload' and 'delete'
|
||||
# please, see readme
|
||||
# optional, default is 'warn'
|
||||
#policy_when_full => 'warn',
|
||||
|
||||
# images which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task
|
||||
# if delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted
|
||||
# optional, no default
|
||||
#delete_no_longer_viewed_files => 90
|
||||
};
|
||||
324
t/postgresql2.conf
Normal file
324
t/postgresql2.conf
Normal file
@@ -0,0 +1,324 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
{
|
||||
####################
|
||||
# Hypnotoad settings
|
||||
####################
|
||||
# see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings
|
||||
hypnotoad => {
|
||||
# array of IP addresses and ports you want to listen to
|
||||
listen => ['http://127.0.0.1:8080'],
|
||||
# if you use Lutim behind a reverse proxy like Nginx, you want to set proxy to 1
|
||||
# if you use Lutim directly, let it commented
|
||||
#proxy => 1,
|
||||
},
|
||||
|
||||
################
|
||||
# Lutim settings
|
||||
################
|
||||
|
||||
# put a way to contact you here and uncomment it
|
||||
# mandatory
|
||||
contact => 'John Doe, admin[at]example.com',
|
||||
|
||||
# random string used to encrypt cookies
|
||||
# mandatory
|
||||
secrets => ['fdjsofjoihrei'],
|
||||
|
||||
# choose a theme. See the available themes in `themes` directory
|
||||
# optional, default is 'default'
|
||||
#theme => 'default',
|
||||
|
||||
# length of the images random URL
|
||||
# optional, default is 8
|
||||
#length => 8,
|
||||
|
||||
# length of the encryption key
|
||||
# optional, default is 8
|
||||
#crypto_key_length => 8,
|
||||
|
||||
# how many URLs will be provisioned in a batch ?
|
||||
# optional, default is 5
|
||||
#provis_step => 5,
|
||||
|
||||
# max number of URLs to be provisioned
|
||||
# optional, default is 100
|
||||
#provisioning => 100,
|
||||
|
||||
# anti-flood protection delay, in seconds
|
||||
# users won't be able to ask Lutim to download images more than one per anti_flood_delay seconds
|
||||
# optional, default is 5
|
||||
#anti_flood_delay => 5,
|
||||
|
||||
# max image size, in octets
|
||||
# you can write it 10*1024*1024
|
||||
# optional, default is 10485760
|
||||
max_file_size => 1048576,
|
||||
|
||||
# if you want to have piwik statistics, provide a piwik image tracker
|
||||
# only the image tracker is allowed, no javascript
|
||||
# optional, no default
|
||||
#piwik_img => 'https://piwik.example.org/piwik.php?idsite=1&rec=1',
|
||||
|
||||
# if you want to include something in the right of the screen, put it here
|
||||
# here's an example to put the logo of your hoster
|
||||
# optional, no default
|
||||
#hosted_by => 'My super hoster <img src="http://hoster.example.com" alt="Hoster logo">',
|
||||
|
||||
# DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED
|
||||
# Lutim now checks if the X-Forwarded-Proto header is present and equal to https.
|
||||
# set to 1 if you use Lutim behind a secure web server
|
||||
# optional, default is 0
|
||||
#https => 0,
|
||||
|
||||
# broadcast_message which will displayed on all pages of Lutim (but no in json response)
|
||||
# optional, no default
|
||||
broadcast_message => 'test broadcast message',
|
||||
|
||||
# array of authorized domains for API calls.
|
||||
# if you want to authorize everyone to use the API: ['*']
|
||||
# optional, no domains allowed by default
|
||||
#allowed_domains => ['http://1.example.com', 'http://2.example.com'],
|
||||
|
||||
# default time limit for files
|
||||
# valid values are 0, 1, 7, 30 and 365
|
||||
# optional, default is 0 (no limit)
|
||||
default_delay => 30,
|
||||
|
||||
# comma-separated values proposed for delays
|
||||
# optional, default is '0,1,7,30,365'
|
||||
#proposed_delays => '0,1,7,30,365',
|
||||
|
||||
# number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay)
|
||||
# a warning message will be displayed on homepage
|
||||
# optional, default is 0 (no limit)
|
||||
max_delay => 200,
|
||||
|
||||
# if set to 1, all the images will be encrypted and the encryption option will no be displayed
|
||||
# optional, default is 0
|
||||
#always_encrypt => 0,
|
||||
|
||||
# you can allow to use a watermark on the uploaded images (or enforce its use)
|
||||
# define a path to the watermark image (provide an image with alpha channel)
|
||||
# you can define the path relative to lutim directory or set an absolute path
|
||||
# to disable the usage of a watermark, leave it blank or commented
|
||||
# optional, no default
|
||||
#watermark_path => '',
|
||||
|
||||
# the watermark can be a tiling one or a single one
|
||||
# when using a small one, you can choose where to place it
|
||||
# valid values are 'Center', 'North', 'NorthEast', 'East', 'SouthEast', 'South', 'SouthWest', 'West' and 'NorthWest' (case insensitive)
|
||||
# optional, default is 'SouthEast'
|
||||
#watermark_placement => 'SouthEast',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be used by default
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_default => 'none',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be enforced (users will always have a watermark and won’t be able to disable it)
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_enforce => 'none',
|
||||
|
||||
# length of the image's delete token
|
||||
# optional, default is 24
|
||||
#token_length => 24,
|
||||
|
||||
# URL sub-directory in which you want Lutim to be accessible
|
||||
# example: you want to have Lutim under https://example.org/lutim/
|
||||
# => set prefix to '/lutim' or to '/lutim/', it doesn't matter
|
||||
# optional, defaut is /
|
||||
#prefix => '/',
|
||||
|
||||
# if set to 1, Lutim will try to prevent its use without using the web interface
|
||||
# optional, default is 0
|
||||
#disable_api => 0,
|
||||
|
||||
# Define a path to the upload directory, where the uploaded images will be stored
|
||||
# You can define it relative to lutim directory or set an absolute path
|
||||
# The path is stored in database for each uploaded file, so you’ll need to do some
|
||||
# SQL commands if you change the upload_dir after getting images uploaded.
|
||||
# Remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is 'files'
|
||||
#upload_dir => 'files',
|
||||
|
||||
# choose what database you want to use
|
||||
# valid choices are sqlite and postgresql (all lowercase)
|
||||
# optional, default is sqlite
|
||||
dbtype => 'postgresql',
|
||||
|
||||
# SQLite ONLY - only used if dbtype is set to sqlite
|
||||
# define a path to the SQLite database
|
||||
# you can define it relative to lutim directory or set an absolute path
|
||||
# remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is lutim.db
|
||||
db_path => 'testpg2.db',
|
||||
|
||||
# PostgreSQL ONLY - only used if dbtype is set to postgresql
|
||||
# these are the credentials to access the PostgreSQL database
|
||||
# mandatory if you choosed postgresql as dbtype
|
||||
pgdb => {
|
||||
database => 'lutim_db',
|
||||
host => 'postgres',
|
||||
user => 'lutim',
|
||||
pwd => 'lutim_pwd'
|
||||
},
|
||||
|
||||
# use Minion instead of directly increase counters
|
||||
# need to launch a minion worker service if enabled
|
||||
# optional, Minion is disabled by default
|
||||
minion => {
|
||||
enabled => 1,
|
||||
# # Which Minion backend to use?
|
||||
# # valid values are sqlite and postgresql (all lowercase)
|
||||
# # mandatory if Minion is enabled, default is sqlite
|
||||
# dbtype => 'sqlite',
|
||||
# # SQLite ONLY - only used if if you choose sqlite as Minion backend, define the path to the minion database
|
||||
# # you can define it relative to lutim directory or set an absolute path
|
||||
# # remember that it has to be in a directory writable by Lutim user
|
||||
# # optional, default is minion.db
|
||||
# db_path => 'minion.db',
|
||||
# # PostgreSQL ONLY - only used if you choose postgresql as Minion backend
|
||||
# # these are the credentials to access the Minion's PostgreSQL database
|
||||
# # mandatory if you choosed postgresql as Minion backend, no default
|
||||
# pgdb => {
|
||||
# database => 'lutim_minion',
|
||||
# host => 'localhost',
|
||||
# #user => 'DBUSER',
|
||||
# #pwd => 'DBPASSWORD'
|
||||
# }
|
||||
},
|
||||
|
||||
# set `ldap` if you want that only authenticated users can shorten URLs
|
||||
# please note that everybody can still use shortend URLs
|
||||
# optional, no default
|
||||
#ldap => {
|
||||
# uri => 'ldaps://ldap.example.org', # server URI
|
||||
# user_tree => 'ou=users,dc=example,dc=org', # search base DN
|
||||
# bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN
|
||||
# bind_pwd => 'secr3t', # search bind password
|
||||
# user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.)
|
||||
# user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.)
|
||||
#},
|
||||
|
||||
# set `htpasswd` if you want to use an htpasswd file instead of ldap
|
||||
# create the file with `htpasswd -c lutim.passwd user`, update it with `htpasswd lutim.passwd user2`
|
||||
# make sure that lutim can read the file!
|
||||
# optional, no default
|
||||
#htpasswd => 'lutim.passwd',
|
||||
|
||||
# if you've set ldap or htpasswd above, the session will last `session_duration` seconds before
|
||||
# the user needs to reauthenticate
|
||||
# optional, default is 3600
|
||||
#session_duration => 3600,
|
||||
|
||||
# disable counters of images
|
||||
# set to 1 to disable counters
|
||||
# optional, counters are enabled by default
|
||||
#disable_img_stats => 0,
|
||||
|
||||
# define the height of the thumbnails generated at users' will
|
||||
# this is not the height of the thumbnails send after upload,
|
||||
# we're talking about thumbnails generated when someone asked for
|
||||
# https://example.org/lutim/tesrinp?thumb
|
||||
# this works only if you have ImageMagick
|
||||
# optional, default is 100 (pixels)
|
||||
#thumbnail_size => 100,
|
||||
|
||||
# maximum number of files that can be downloaded as a single zip archive
|
||||
# if too many files are asked, it results a timeout, so Lutim split the zip URL
|
||||
# in multiple URLs, each with max_file_size images.
|
||||
# timeout behavior depends heavily on your server ressources (CPU) and if images
|
||||
# are encrypted
|
||||
# optional, default is 15
|
||||
#max_files_in_zip => 15,
|
||||
|
||||
# maximum size (in MB) of memory allowed for the image cache
|
||||
# Lutim has a built-in memory-based image cache to accelerate responses to often-viewed images.
|
||||
# This setting makes the cache remove oldest viewed image if the cache size is over it.
|
||||
# WARNING: a cache is created for each hypnotoad worker, which by default is twice the number of
|
||||
# CPUs you have. See http://mojolicious.org/perldoc/Mojo/Server/Hypnotoad#workers for details
|
||||
# So, if you have 4 workers and set cache_max_size to 100, the real maximum size of RAM used for
|
||||
# cache is 400MB.
|
||||
# If set to 0, the cache is disabled
|
||||
# optional, default is 0
|
||||
#cache_max_size => 0,
|
||||
|
||||
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
|
||||
# If set to [], the use of memcached is disabled.
|
||||
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
|
||||
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
|
||||
# servers.
|
||||
# exemple of valid value: ['127.0.0.1:11211']
|
||||
# optional, default is []
|
||||
#memcached_servers => [],
|
||||
|
||||
# enable or disable Lutim built-in logs
|
||||
# set to 1 to disable logs
|
||||
# optional, default is 0
|
||||
#quiet_logs => 0,
|
||||
|
||||
# Content-Security-Policy header that will be sent by Lutim
|
||||
# Set to '' to disable CSP header
|
||||
# https://content-security-policy.com/ provides a good documentation about CSP.
|
||||
# https://report-uri.com/home/generate provides a tool to generate a CSP header.
|
||||
# optional, default is "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"
|
||||
# NB: unsafe-inline for script-src and style-src are here only because morris,
|
||||
# the graph library used in the stats page requires it
|
||||
# the default value is good for `default` theme
|
||||
#csp => "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'",
|
||||
|
||||
# X-Frame-Options header that will be sent by Lutim
|
||||
# Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/'
|
||||
# Set to '' to disable X-Frame-Options header
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
||||
# Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly
|
||||
# to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors)
|
||||
# optional, default is 'DENY'
|
||||
#x_frame_options => 'DENY',
|
||||
|
||||
# X-Content-Type-Options that will be sent by Lutim
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
|
||||
# Set to '' to disable X-Content-Type-Options header
|
||||
# optional, default is 'nosniff'
|
||||
#x_content_type_options => 'nosniff',
|
||||
|
||||
# X-XSS-Protection that will be sent by Lutim
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
|
||||
# Set to '' to disable X-XSS-Protection header
|
||||
# optional, default is '1; mode=block'
|
||||
#x_xss_protection => '1; mode=block',
|
||||
|
||||
# if set, the uploaded images will use this domain
|
||||
# optional
|
||||
#fixed_domain => 'example.org',
|
||||
|
||||
##########################
|
||||
# Lutim cron jobs settings
|
||||
##########################
|
||||
|
||||
# number of days shown in /stats page (used with script/lutim cron stats)
|
||||
# optional, default is 365
|
||||
#stats_day_num => 365,
|
||||
|
||||
# number of days senders' IP addresses are kept in database
|
||||
# after that delay, they will be deleted from database (used with script/lutim cron cleanbdd)
|
||||
# optional, default is 365
|
||||
#keep_ip_during => 365,
|
||||
|
||||
# max size of the files directory, in octets
|
||||
# used by script/lutim cron watch to trigger an action
|
||||
# optional, no default
|
||||
max_total_size => 10*1024*1024*1024,
|
||||
|
||||
# default action when files directory is over max_total_size (used with script/lutim cron watch)
|
||||
# valid values are 'warn', 'stop-upload' and 'delete'
|
||||
# please, see readme
|
||||
# optional, default is 'warn'
|
||||
#policy_when_full => 'warn',
|
||||
|
||||
# images which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task
|
||||
# if delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted
|
||||
# optional, no default
|
||||
#delete_no_longer_viewed_files => 90
|
||||
};
|
||||
301
t/postgresql3.conf
Normal file
301
t/postgresql3.conf
Normal file
@@ -0,0 +1,301 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
{
|
||||
####################
|
||||
# Hypnotoad settings
|
||||
####################
|
||||
# see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings
|
||||
hypnotoad => {
|
||||
# array of IP addresses and ports you want to listen to
|
||||
listen => ['http://127.0.0.1:8080'],
|
||||
# if you use Lutim behind a reverse proxy like Nginx, you want to set proxy to 1
|
||||
# if you use Lutim directly, let it commented
|
||||
#proxy => 1,
|
||||
},
|
||||
|
||||
################
|
||||
# Lutim settings
|
||||
################
|
||||
|
||||
# put a way to contact you here and uncomment it
|
||||
# mandatory
|
||||
contact => 'John Doe, admin[at]example.com',
|
||||
|
||||
# random string used to encrypt cookies
|
||||
# mandatory
|
||||
secrets => ['fdjsofjoihrei'],
|
||||
|
||||
# choose a theme. See the available themes in `themes` directory
|
||||
# optional, default is 'default'
|
||||
#theme => 'default',
|
||||
|
||||
# length of the images random URL
|
||||
# optional, default is 8
|
||||
#length => 8,
|
||||
|
||||
# length of the encryption key
|
||||
# optional, default is 8
|
||||
#crypto_key_length => 8,
|
||||
|
||||
# how many URLs will be provisioned in a batch ?
|
||||
# optional, default is 5
|
||||
#provis_step => 5,
|
||||
|
||||
# max number of URLs to be provisioned
|
||||
# optional, default is 100
|
||||
#provisioning => 100,
|
||||
|
||||
# anti-flood protection delay, in seconds
|
||||
# users won't be able to ask Lutim to download images more than one per anti_flood_delay seconds
|
||||
# optional, default is 5
|
||||
#anti_flood_delay => 5,
|
||||
|
||||
# max image size, in octets
|
||||
# you can write it 10*1024*1024
|
||||
# optional, default is 10485760
|
||||
max_file_size => 1048576,
|
||||
|
||||
# if you want to have piwik statistics, provide a piwik image tracker
|
||||
# only the image tracker is allowed, no javascript
|
||||
# optional, no default
|
||||
#piwik_img => 'https://piwik.example.org/piwik.php?idsite=1&rec=1',
|
||||
|
||||
# if you want to include something in the right of the screen, put it here
|
||||
# here's an example to put the logo of your hoster
|
||||
# optional, no default
|
||||
#hosted_by => 'My super hoster <img src="http://hoster.example.com" alt="Hoster logo">',
|
||||
|
||||
# DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED
|
||||
# Lutim now checks if the X-Forwarded-Proto header is present and equal to https.
|
||||
# set to 1 if you use Lutim behind a secure web server
|
||||
# optional, default is 0
|
||||
#https => 0,
|
||||
|
||||
# broadcast_message which will displayed on all pages of Lutim (but no in json response)
|
||||
# optional, no default
|
||||
broadcast_message => 'test broadcast message',
|
||||
|
||||
# array of authorized domains for API calls.
|
||||
# if you want to authorize everyone to use the API: ['*']
|
||||
# optional, no domains allowed by default
|
||||
#allowed_domains => ['http://1.example.com', 'http://2.example.com'],
|
||||
|
||||
# default time limit for files
|
||||
# valid values are 0, 1, 7, 30 and 365
|
||||
# optional, default is 0 (no limit)
|
||||
default_delay => 30,
|
||||
|
||||
# comma-separated values proposed for delays
|
||||
# optional, default is '0,1,7,30,365'
|
||||
#proposed_delays => '0,1,7,30,365',
|
||||
|
||||
# number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay)
|
||||
# a warning message will be displayed on homepage
|
||||
# optional, default is 0 (no limit)
|
||||
max_delay => 200,
|
||||
|
||||
# if set to 1, all the images will be encrypted and the encryption option will no be displayed
|
||||
# optional, default is 0
|
||||
#always_encrypt => 0,
|
||||
|
||||
# you can allow to use a watermark on the uploaded images (or enforce its use)
|
||||
# define a path to the watermark image (provide an image with alpha channel)
|
||||
# you can define the path relative to lutim directory or set an absolute path
|
||||
# to disable the usage of a watermark, leave it blank or commented
|
||||
# optional, no default
|
||||
#watermark_path => '',
|
||||
|
||||
# the watermark can be a tiling one or a single one
|
||||
# when using a small one, you can choose where to place it
|
||||
# valid values are 'Center', 'North', 'NorthEast', 'East', 'SouthEast', 'South', 'SouthWest', 'West' and 'NorthWest' (case insensitive)
|
||||
# optional, default is 'SouthEast'
|
||||
#watermark_placement => 'SouthEast',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be used by default
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_default => 'none',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be enforced (users will always have a watermark and won’t be able to disable it)
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_enforce => 'none',
|
||||
|
||||
# length of the image's delete token
|
||||
# optional, default is 24
|
||||
#token_length => 24,
|
||||
|
||||
# URL sub-directory in which you want Lutim to be accessible
|
||||
# example: you want to have Lutim under https://example.org/lutim/
|
||||
# => set prefix to '/lutim' or to '/lutim/', it doesn't matter
|
||||
# optional, defaut is /
|
||||
#prefix => '/',
|
||||
|
||||
# if set to 1, Lutim will try to prevent its use without using the web interface
|
||||
# optional, default is 0
|
||||
#disable_api => 0,
|
||||
|
||||
# Define a path to the upload directory, where the uploaded images will be stored
|
||||
# You can define it relative to lutim directory or set an absolute path
|
||||
# The path is stored in database for each uploaded file, so you’ll need to do some
|
||||
# SQL commands if you change the upload_dir after getting images uploaded.
|
||||
# Remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is 'files'
|
||||
#upload_dir => 'files',
|
||||
|
||||
# choose what database you want to use
|
||||
# valid choices are sqlite and postgresql (all lowercase)
|
||||
# optional, default is sqlite
|
||||
dbtype => 'postgresql',
|
||||
|
||||
# SQLite ONLY - only used if dbtype is set to sqlite
|
||||
# define a path to the SQLite database
|
||||
# you can define it relative to lutim directory or set an absolute path
|
||||
# remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is lutim.db
|
||||
db_path => 'testpg3.db',
|
||||
|
||||
# PostgreSQL ONLY - only used if dbtype is set to postgresql
|
||||
# these are the credentials to access the PostgreSQL database
|
||||
# mandatory if you choosed postgresql as dbtype
|
||||
pgdb => {
|
||||
database => 'lutim_db',
|
||||
host => 'postgres',
|
||||
user => 'lutim',
|
||||
pwd => 'lutim_pwd'
|
||||
},
|
||||
|
||||
# use Minion instead of directly increase counters
|
||||
# need to launch a minion worker service if enabled
|
||||
# optional, Minion is disabled by default
|
||||
minion => {
|
||||
enabled => 1,
|
||||
# Which Minion backend to use?
|
||||
# valid values are sqlite and postgresql (all lowercase)
|
||||
# mandatory if Minion is enabled, default is sqlite
|
||||
dbtype => 'postgresql',
|
||||
# SQLite ONLY - only used if if you choose sqlite as Minion backend, define the path to the minion database
|
||||
# you can define it relative to lutim directory or set an absolute path
|
||||
# remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is minion.db
|
||||
db_path => 'minion.db',
|
||||
# PostgreSQL ONLY - only used if you choose postgresql as Minion backend
|
||||
# these are the credentials to access the Minion's PostgreSQL database
|
||||
# mandatory if you choosed postgresql as Minion backend, no default
|
||||
pgdb => {
|
||||
database => 'lutim_minion',
|
||||
host => 'postgres',
|
||||
user => 'lutim',
|
||||
pwd => 'lutim_pwd'
|
||||
}
|
||||
},
|
||||
|
||||
# disable counters of images
|
||||
# set to 1 to disable counters
|
||||
# optional, counters are enabled by default
|
||||
#disable_img_stats => 0,
|
||||
|
||||
# define the height of the thumbnails generated at users' will
|
||||
# this is not the height of the thumbnails send after upload,
|
||||
# we're talking about thumbnails generated when someone asked for
|
||||
# https://example.org/lutim/tesrinp?thumb
|
||||
# this works only if you have ImageMagick
|
||||
# optional, default is 100 (pixels)
|
||||
#thumbnail_size => 100,
|
||||
|
||||
# maximum number of files that can be downloaded as a single zip archive
|
||||
# if too many files are asked, it results a timeout, so Lutim split the zip URL
|
||||
# in multiple URLs, each with max_file_size images.
|
||||
# timeout behavior depends heavily on your server ressources (CPU) and if images
|
||||
# are encrypted
|
||||
# optional, default is 15
|
||||
#max_files_in_zip => 15,
|
||||
|
||||
# maximum size (in MB) of memory allowed for the image cache
|
||||
# Lutim has a built-in memory-based image cache to accelerate responses to often-viewed images.
|
||||
# This setting makes the cache remove oldest viewed image if the cache size is over it.
|
||||
# WARNING: a cache is created for each hypnotoad worker, which by default is twice the number of
|
||||
# CPUs you have. See http://mojolicious.org/perldoc/Mojo/Server/Hypnotoad#workers for details
|
||||
# So, if you have 4 workers and set cache_max_size to 100, the real maximum size of RAM used for
|
||||
# cache is 400MB.
|
||||
# If set to 0, the cache is disabled
|
||||
# optional, default is 0
|
||||
#cache_max_size => 0,
|
||||
|
||||
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
|
||||
# If set to [], the use of memcached is disabled.
|
||||
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
|
||||
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
|
||||
# servers.
|
||||
# exemple of valid value: ['127.0.0.1:11211']
|
||||
# optional, default is []
|
||||
#memcached_servers => [],
|
||||
|
||||
# enable or disable Lutim built-in logs
|
||||
# set to 1 to disable logs
|
||||
# optional, default is 0
|
||||
#quiet_logs => 0,
|
||||
|
||||
# Content-Security-Policy header that will be sent by Lutim
|
||||
# Set to '' to disable CSP header
|
||||
# https://content-security-policy.com/ provides a good documentation about CSP.
|
||||
# https://report-uri.com/home/generate provides a tool to generate a CSP header.
|
||||
# optional, default is "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"
|
||||
# NB: unsafe-inline for script-src and style-src are here only because morris,
|
||||
# the graph library used in the stats page requires it
|
||||
# the default value is good for `default` theme
|
||||
#csp => "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'",
|
||||
|
||||
# X-Frame-Options header that will be sent by Lutim
|
||||
# Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/'
|
||||
# Set to '' to disable X-Frame-Options header
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
||||
# Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly
|
||||
# to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors)
|
||||
# optional, default is 'DENY'
|
||||
#x_frame_options => 'DENY',
|
||||
|
||||
# X-Content-Type-Options that will be sent by Lutim
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
|
||||
# Set to '' to disable X-Content-Type-Options header
|
||||
# optional, default is 'nosniff'
|
||||
#x_content_type_options => 'nosniff',
|
||||
|
||||
# X-XSS-Protection that will be sent by Lutim
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
|
||||
# Set to '' to disable X-XSS-Protection header
|
||||
# optional, default is '1; mode=block'
|
||||
#x_xss_protection => '1; mode=block',
|
||||
|
||||
# if set, the uploaded images will use this domain
|
||||
# optional
|
||||
#fixed_domain => 'example.org',
|
||||
|
||||
##########################
|
||||
# Lutim cron jobs settings
|
||||
##########################
|
||||
|
||||
# number of days shown in /stats page (used with script/lutim cron stats)
|
||||
# optional, default is 365
|
||||
#stats_day_num => 365,
|
||||
|
||||
# number of days senders' IP addresses are kept in database
|
||||
# after that delay, they will be deleted from database (used with script/lutim cron cleanbdd)
|
||||
# optional, default is 365
|
||||
#keep_ip_during => 365,
|
||||
|
||||
# max size of the files directory, in octets
|
||||
# used by script/lutim cron watch to trigger an action
|
||||
# optional, no default
|
||||
max_total_size => 10*1024*1024*1024,
|
||||
|
||||
# default action when files directory is over max_total_size (used with script/lutim cron watch)
|
||||
# valid values are 'warn', 'stop-upload' and 'delete'
|
||||
# please, see readme
|
||||
# optional, default is 'warn'
|
||||
#policy_when_full => 'warn',
|
||||
|
||||
# images which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task
|
||||
# if delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted
|
||||
# optional, no default
|
||||
#delete_no_longer_viewed_files => 90
|
||||
};
|
||||
324
t/sqlite1.conf
Normal file
324
t/sqlite1.conf
Normal file
@@ -0,0 +1,324 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
{
|
||||
####################
|
||||
# Hypnotoad settings
|
||||
####################
|
||||
# see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings
|
||||
hypnotoad => {
|
||||
# array of IP addresses and ports you want to listen to
|
||||
listen => ['http://127.0.0.1:8080'],
|
||||
# if you use Lutim behind a reverse proxy like Nginx, you want to set proxy to 1
|
||||
# if you use Lutim directly, let it commented
|
||||
#proxy => 1,
|
||||
},
|
||||
|
||||
################
|
||||
# Lutim settings
|
||||
################
|
||||
|
||||
# put a way to contact you here and uncomment it
|
||||
# mandatory
|
||||
contact => 'John Doe, admin[at]example.com',
|
||||
|
||||
# random string used to encrypt cookies
|
||||
# mandatory
|
||||
secrets => ['fdjsofjoihrei'],
|
||||
|
||||
# choose a theme. See the available themes in `themes` directory
|
||||
# optional, default is 'default'
|
||||
#theme => 'default',
|
||||
|
||||
# length of the images random URL
|
||||
# optional, default is 8
|
||||
#length => 8,
|
||||
|
||||
# length of the encryption key
|
||||
# optional, default is 8
|
||||
#crypto_key_length => 8,
|
||||
|
||||
# how many URLs will be provisioned in a batch ?
|
||||
# optional, default is 5
|
||||
#provis_step => 5,
|
||||
|
||||
# max number of URLs to be provisioned
|
||||
# optional, default is 100
|
||||
#provisioning => 100,
|
||||
|
||||
# anti-flood protection delay, in seconds
|
||||
# users won't be able to ask Lutim to download images more than one per anti_flood_delay seconds
|
||||
# optional, default is 5
|
||||
#anti_flood_delay => 5,
|
||||
|
||||
# max image size, in octets
|
||||
# you can write it 10*1024*1024
|
||||
# optional, default is 10485760
|
||||
max_file_size => 1048576,
|
||||
|
||||
# if you want to have piwik statistics, provide a piwik image tracker
|
||||
# only the image tracker is allowed, no javascript
|
||||
# optional, no default
|
||||
#piwik_img => 'https://piwik.example.org/piwik.php?idsite=1&rec=1',
|
||||
|
||||
# if you want to include something in the right of the screen, put it here
|
||||
# here's an example to put the logo of your hoster
|
||||
# optional, no default
|
||||
#hosted_by => 'My super hoster <img src="http://hoster.example.com" alt="Hoster logo">',
|
||||
|
||||
# DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED
|
||||
# Lutim now checks if the X-Forwarded-Proto header is present and equal to https.
|
||||
# set to 1 if you use Lutim behind a secure web server
|
||||
# optional, default is 0
|
||||
#https => 0,
|
||||
|
||||
# broadcast_message which will displayed on all pages of Lutim (but no in json response)
|
||||
# optional, no default
|
||||
broadcast_message => 'test broadcast message',
|
||||
|
||||
# array of authorized domains for API calls.
|
||||
# if you want to authorize everyone to use the API: ['*']
|
||||
# optional, no domains allowed by default
|
||||
#allowed_domains => ['http://1.example.com', 'http://2.example.com'],
|
||||
|
||||
# default time limit for files
|
||||
# valid values are 0, 1, 7, 30 and 365
|
||||
# optional, default is 0 (no limit)
|
||||
default_delay => 30,
|
||||
|
||||
# comma-separated values proposed for delays
|
||||
# optional, default is '0,1,7,30,365'
|
||||
#proposed_delays => '0,1,7,30,365',
|
||||
|
||||
# number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay)
|
||||
# a warning message will be displayed on homepage
|
||||
# optional, default is 0 (no limit)
|
||||
max_delay => 200,
|
||||
|
||||
# if set to 1, all the images will be encrypted and the encryption option will no be displayed
|
||||
# optional, default is 0
|
||||
#always_encrypt => 0,
|
||||
|
||||
# you can allow to use a watermark on the uploaded images (or enforce its use)
|
||||
# define a path to the watermark image (provide an image with alpha channel)
|
||||
# you can define the path relative to lutim directory or set an absolute path
|
||||
# to disable the usage of a watermark, leave it blank or commented
|
||||
# optional, no default
|
||||
#watermark_path => '',
|
||||
|
||||
# the watermark can be a tiling one or a single one
|
||||
# when using a small one, you can choose where to place it
|
||||
# valid values are 'Center', 'North', 'NorthEast', 'East', 'SouthEast', 'South', 'SouthWest', 'West' and 'NorthWest' (case insensitive)
|
||||
# optional, default is 'SouthEast'
|
||||
#watermark_placement => 'SouthEast',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be used by default
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_default => 'none',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be enforced (users will always have a watermark and won’t be able to disable it)
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_enforce => 'none',
|
||||
|
||||
# length of the image's delete token
|
||||
# optional, default is 24
|
||||
#token_length => 24,
|
||||
|
||||
# URL sub-directory in which you want Lutim to be accessible
|
||||
# example: you want to have Lutim under https://example.org/lutim/
|
||||
# => set prefix to '/lutim' or to '/lutim/', it doesn't matter
|
||||
# optional, defaut is /
|
||||
#prefix => '/',
|
||||
|
||||
# if set to 1, Lutim will try to prevent its use without using the web interface
|
||||
# optional, default is 0
|
||||
#disable_api => 0,
|
||||
|
||||
# Define a path to the upload directory, where the uploaded images will be stored
|
||||
# You can define it relative to lutim directory or set an absolute path
|
||||
# The path is stored in database for each uploaded file, so you’ll need to do some
|
||||
# SQL commands if you change the upload_dir after getting images uploaded.
|
||||
# Remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is 'files'
|
||||
#upload_dir => 'files',
|
||||
|
||||
# choose what database you want to use
|
||||
# valid choices are sqlite and postgresql (all lowercase)
|
||||
# optional, default is sqlite
|
||||
dbtype => 'sqlite',
|
||||
|
||||
# SQLite ONLY - only used if dbtype is set to sqlite
|
||||
# define a path to the SQLite database
|
||||
# you can define it relative to lutim directory or set an absolute path
|
||||
# remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is lutim.db
|
||||
db_path => 'testsqlite1.db',
|
||||
|
||||
# PostgreSQL ONLY - only used if dbtype is set to postgresql
|
||||
# these are the credentials to access the PostgreSQL database
|
||||
# mandatory if you choosed postgresql as dbtype
|
||||
#pgdb => {
|
||||
# database => 'lutim',
|
||||
# host => 'localhost',
|
||||
# #user => 'DBUSER',
|
||||
# #pwd => 'DBPASSWORD'
|
||||
#},
|
||||
|
||||
# use Minion instead of directly increase counters
|
||||
# need to launch a minion worker service if enabled
|
||||
# optional, Minion is disabled by default
|
||||
#minion => {
|
||||
# enabled => 0,
|
||||
# # Which Minion backend to use?
|
||||
# # valid values are sqlite and postgresql (all lowercase)
|
||||
# # mandatory if Minion is enabled, default is sqlite
|
||||
# dbtype => 'sqlite',
|
||||
# # SQLite ONLY - only used if if you choose sqlite as Minion backend, define the path to the minion database
|
||||
# # you can define it relative to lutim directory or set an absolute path
|
||||
# # remember that it has to be in a directory writable by Lutim user
|
||||
# # optional, default is minion.db
|
||||
# db_path => 'minion.db',
|
||||
# # PostgreSQL ONLY - only used if you choose postgresql as Minion backend
|
||||
# # these are the credentials to access the Minion's PostgreSQL database
|
||||
# # mandatory if you choosed postgresql as Minion backend, no default
|
||||
# pgdb => {
|
||||
# database => 'lutim_minion',
|
||||
# host => 'localhost',
|
||||
# #user => 'DBUSER',
|
||||
# #pwd => 'DBPASSWORD'
|
||||
# }
|
||||
#},
|
||||
|
||||
# set `ldap` if you want that only authenticated users can shorten URLs
|
||||
# please note that everybody can still use shortend URLs
|
||||
# optional, no default
|
||||
#ldap => {
|
||||
# uri => 'ldaps://ldap.example.org', # server URI
|
||||
# user_tree => 'ou=users,dc=example,dc=org', # search base DN
|
||||
# bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN
|
||||
# bind_pwd => 'secr3t', # search bind password
|
||||
# user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.)
|
||||
# user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.)
|
||||
#},
|
||||
|
||||
# set `htpasswd` if you want to use an htpasswd file instead of ldap
|
||||
# create the file with `htpasswd -c lutim.passwd user`, update it with `htpasswd lutim.passwd user2`
|
||||
# make sure that lutim can read the file!
|
||||
# optional, no default
|
||||
#htpasswd => 'lutim.passwd',
|
||||
|
||||
# if you've set ldap or htpasswd above, the session will last `session_duration` seconds before
|
||||
# the user needs to reauthenticate
|
||||
# optional, default is 3600
|
||||
#session_duration => 3600,
|
||||
|
||||
# disable counters of images
|
||||
# set to 1 to disable counters
|
||||
# optional, counters are enabled by default
|
||||
#disable_img_stats => 0,
|
||||
|
||||
# define the height of the thumbnails generated at users' will
|
||||
# this is not the height of the thumbnails send after upload,
|
||||
# we're talking about thumbnails generated when someone asked for
|
||||
# https://example.org/lutim/tesrinp?thumb
|
||||
# this works only if you have ImageMagick
|
||||
# optional, default is 100 (pixels)
|
||||
#thumbnail_size => 100,
|
||||
|
||||
# maximum number of files that can be downloaded as a single zip archive
|
||||
# if too many files are asked, it results a timeout, so Lutim split the zip URL
|
||||
# in multiple URLs, each with max_file_size images.
|
||||
# timeout behavior depends heavily on your server ressources (CPU) and if images
|
||||
# are encrypted
|
||||
# optional, default is 15
|
||||
#max_files_in_zip => 15,
|
||||
|
||||
# maximum size (in MB) of memory allowed for the image cache
|
||||
# Lutim has a built-in memory-based image cache to accelerate responses to often-viewed images.
|
||||
# This setting makes the cache remove oldest viewed image if the cache size is over it.
|
||||
# WARNING: a cache is created for each hypnotoad worker, which by default is twice the number of
|
||||
# CPUs you have. See http://mojolicious.org/perldoc/Mojo/Server/Hypnotoad#workers for details
|
||||
# So, if you have 4 workers and set cache_max_size to 100, the real maximum size of RAM used for
|
||||
# cache is 400MB.
|
||||
# If set to 0, the cache is disabled
|
||||
# optional, default is 0
|
||||
#cache_max_size => 0,
|
||||
|
||||
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
|
||||
# If set to [], the use of memcached is disabled.
|
||||
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
|
||||
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
|
||||
# servers.
|
||||
# exemple of valid value: ['127.0.0.1:11211']
|
||||
# optional, default is []
|
||||
#memcached_servers => [],
|
||||
|
||||
# enable or disable Lutim built-in logs
|
||||
# set to 1 to disable logs
|
||||
# optional, default is 0
|
||||
#quiet_logs => 0,
|
||||
|
||||
# Content-Security-Policy header that will be sent by Lutim
|
||||
# Set to '' to disable CSP header
|
||||
# https://content-security-policy.com/ provides a good documentation about CSP.
|
||||
# https://report-uri.com/home/generate provides a tool to generate a CSP header.
|
||||
# optional, default is "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"
|
||||
# NB: unsafe-inline for script-src and style-src are here only because morris,
|
||||
# the graph library used in the stats page requires it
|
||||
# the default value is good for `default` theme
|
||||
#csp => "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'",
|
||||
|
||||
# X-Frame-Options header that will be sent by Lutim
|
||||
# Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/'
|
||||
# Set to '' to disable X-Frame-Options header
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
||||
# Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly
|
||||
# to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors)
|
||||
# optional, default is 'DENY'
|
||||
#x_frame_options => 'DENY',
|
||||
|
||||
# X-Content-Type-Options that will be sent by Lutim
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
|
||||
# Set to '' to disable X-Content-Type-Options header
|
||||
# optional, default is 'nosniff'
|
||||
#x_content_type_options => 'nosniff',
|
||||
|
||||
# X-XSS-Protection that will be sent by Lutim
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
|
||||
# Set to '' to disable X-XSS-Protection header
|
||||
# optional, default is '1; mode=block'
|
||||
#x_xss_protection => '1; mode=block',
|
||||
|
||||
# if set, the uploaded images will use this domain
|
||||
# optional
|
||||
#fixed_domain => 'example.org',
|
||||
|
||||
##########################
|
||||
# Lutim cron jobs settings
|
||||
##########################
|
||||
|
||||
# number of days shown in /stats page (used with script/lutim cron stats)
|
||||
# optional, default is 365
|
||||
#stats_day_num => 365,
|
||||
|
||||
# number of days senders' IP addresses are kept in database
|
||||
# after that delay, they will be deleted from database (used with script/lutim cron cleanbdd)
|
||||
# optional, default is 365
|
||||
#keep_ip_during => 365,
|
||||
|
||||
# max size of the files directory, in octets
|
||||
# used by script/lutim cron watch to trigger an action
|
||||
# optional, no default
|
||||
max_total_size => 10*1024*1024*1024,
|
||||
|
||||
# default action when files directory is over max_total_size (used with script/lutim cron watch)
|
||||
# valid values are 'warn', 'stop-upload' and 'delete'
|
||||
# please, see readme
|
||||
# optional, default is 'warn'
|
||||
#policy_when_full => 'warn',
|
||||
|
||||
# images which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task
|
||||
# if delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted
|
||||
# optional, no default
|
||||
#delete_no_longer_viewed_files => 90
|
||||
};
|
||||
324
t/sqlite2.conf
Normal file
324
t/sqlite2.conf
Normal file
@@ -0,0 +1,324 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
{
|
||||
####################
|
||||
# Hypnotoad settings
|
||||
####################
|
||||
# see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings
|
||||
hypnotoad => {
|
||||
# array of IP addresses and ports you want to listen to
|
||||
listen => ['http://127.0.0.1:8080'],
|
||||
# if you use Lutim behind a reverse proxy like Nginx, you want to set proxy to 1
|
||||
# if you use Lutim directly, let it commented
|
||||
#proxy => 1,
|
||||
},
|
||||
|
||||
################
|
||||
# Lutim settings
|
||||
################
|
||||
|
||||
# put a way to contact you here and uncomment it
|
||||
# mandatory
|
||||
contact => 'John Doe, admin[at]example.com',
|
||||
|
||||
# random string used to encrypt cookies
|
||||
# mandatory
|
||||
secrets => ['fdjsofjoihrei'],
|
||||
|
||||
# choose a theme. See the available themes in `themes` directory
|
||||
# optional, default is 'default'
|
||||
#theme => 'default',
|
||||
|
||||
# length of the images random URL
|
||||
# optional, default is 8
|
||||
#length => 8,
|
||||
|
||||
# length of the encryption key
|
||||
# optional, default is 8
|
||||
#crypto_key_length => 8,
|
||||
|
||||
# how many URLs will be provisioned in a batch ?
|
||||
# optional, default is 5
|
||||
#provis_step => 5,
|
||||
|
||||
# max number of URLs to be provisioned
|
||||
# optional, default is 100
|
||||
#provisioning => 100,
|
||||
|
||||
# anti-flood protection delay, in seconds
|
||||
# users won't be able to ask Lutim to download images more than one per anti_flood_delay seconds
|
||||
# optional, default is 5
|
||||
#anti_flood_delay => 5,
|
||||
|
||||
# max image size, in octets
|
||||
# you can write it 10*1024*1024
|
||||
# optional, default is 10485760
|
||||
max_file_size => 1048576,
|
||||
|
||||
# if you want to have piwik statistics, provide a piwik image tracker
|
||||
# only the image tracker is allowed, no javascript
|
||||
# optional, no default
|
||||
#piwik_img => 'https://piwik.example.org/piwik.php?idsite=1&rec=1',
|
||||
|
||||
# if you want to include something in the right of the screen, put it here
|
||||
# here's an example to put the logo of your hoster
|
||||
# optional, no default
|
||||
#hosted_by => 'My super hoster <img src="http://hoster.example.com" alt="Hoster logo">',
|
||||
|
||||
# DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED
|
||||
# Lutim now checks if the X-Forwarded-Proto header is present and equal to https.
|
||||
# set to 1 if you use Lutim behind a secure web server
|
||||
# optional, default is 0
|
||||
#https => 0,
|
||||
|
||||
# broadcast_message which will displayed on all pages of Lutim (but no in json response)
|
||||
# optional, no default
|
||||
broadcast_message => 'test broadcast message',
|
||||
|
||||
# array of authorized domains for API calls.
|
||||
# if you want to authorize everyone to use the API: ['*']
|
||||
# optional, no domains allowed by default
|
||||
#allowed_domains => ['http://1.example.com', 'http://2.example.com'],
|
||||
|
||||
# default time limit for files
|
||||
# valid values are 0, 1, 7, 30 and 365
|
||||
# optional, default is 0 (no limit)
|
||||
default_delay => 30,
|
||||
|
||||
# comma-separated values proposed for delays
|
||||
# optional, default is '0,1,7,30,365'
|
||||
#proposed_delays => '0,1,7,30,365',
|
||||
|
||||
# number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay)
|
||||
# a warning message will be displayed on homepage
|
||||
# optional, default is 0 (no limit)
|
||||
max_delay => 200,
|
||||
|
||||
# if set to 1, all the images will be encrypted and the encryption option will no be displayed
|
||||
# optional, default is 0
|
||||
#always_encrypt => 0,
|
||||
|
||||
# you can allow to use a watermark on the uploaded images (or enforce its use)
|
||||
# define a path to the watermark image (provide an image with alpha channel)
|
||||
# you can define the path relative to lutim directory or set an absolute path
|
||||
# to disable the usage of a watermark, leave it blank or commented
|
||||
# optional, no default
|
||||
#watermark_path => '',
|
||||
|
||||
# the watermark can be a tiling one or a single one
|
||||
# when using a small one, you can choose where to place it
|
||||
# valid values are 'Center', 'North', 'NorthEast', 'East', 'SouthEast', 'South', 'SouthWest', 'West' and 'NorthWest' (case insensitive)
|
||||
# optional, default is 'SouthEast'
|
||||
#watermark_placement => 'SouthEast',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be used by default
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_default => 'none',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be enforced (users will always have a watermark and won’t be able to disable it)
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_enforce => 'none',
|
||||
|
||||
# length of the image's delete token
|
||||
# optional, default is 24
|
||||
#token_length => 24,
|
||||
|
||||
# URL sub-directory in which you want Lutim to be accessible
|
||||
# example: you want to have Lutim under https://example.org/lutim/
|
||||
# => set prefix to '/lutim' or to '/lutim/', it doesn't matter
|
||||
# optional, defaut is /
|
||||
#prefix => '/',
|
||||
|
||||
# if set to 1, Lutim will try to prevent its use without using the web interface
|
||||
# optional, default is 0
|
||||
#disable_api => 0,
|
||||
|
||||
# Define a path to the upload directory, where the uploaded images will be stored
|
||||
# You can define it relative to lutim directory or set an absolute path
|
||||
# The path is stored in database for each uploaded file, so you’ll need to do some
|
||||
# SQL commands if you change the upload_dir after getting images uploaded.
|
||||
# Remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is 'files'
|
||||
#upload_dir => 'files',
|
||||
|
||||
# choose what database you want to use
|
||||
# valid choices are sqlite and postgresql (all lowercase)
|
||||
# optional, default is sqlite
|
||||
dbtype => 'sqlite',
|
||||
|
||||
# SQLite ONLY - only used if dbtype is set to sqlite
|
||||
# define a path to the SQLite database
|
||||
# you can define it relative to lutim directory or set an absolute path
|
||||
# remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is lutim.db
|
||||
db_path => 'testsqlite2.db',
|
||||
|
||||
# PostgreSQL ONLY - only used if dbtype is set to postgresql
|
||||
# these are the credentials to access the PostgreSQL database
|
||||
# mandatory if you choosed postgresql as dbtype
|
||||
#pgdb => {
|
||||
# database => 'lutim',
|
||||
# host => 'localhost',
|
||||
# #user => 'DBUSER',
|
||||
# #pwd => 'DBPASSWORD'
|
||||
#},
|
||||
|
||||
# use Minion instead of directly increase counters
|
||||
# need to launch a minion worker service if enabled
|
||||
# optional, Minion is disabled by default
|
||||
minion => {
|
||||
enabled => 1,
|
||||
# # Which Minion backend to use?
|
||||
# # valid values are sqlite and postgresql (all lowercase)
|
||||
# # mandatory if Minion is enabled, default is sqlite
|
||||
# dbtype => 'sqlite',
|
||||
# # SQLite ONLY - only used if if you choose sqlite as Minion backend, define the path to the minion database
|
||||
# # you can define it relative to lutim directory or set an absolute path
|
||||
# # remember that it has to be in a directory writable by Lutim user
|
||||
# # optional, default is minion.db
|
||||
# db_path => 'minion.db',
|
||||
# # PostgreSQL ONLY - only used if you choose postgresql as Minion backend
|
||||
# # these are the credentials to access the Minion's PostgreSQL database
|
||||
# # mandatory if you choosed postgresql as Minion backend, no default
|
||||
# pgdb => {
|
||||
# database => 'lutim_minion',
|
||||
# host => 'localhost',
|
||||
# #user => 'DBUSER',
|
||||
# #pwd => 'DBPASSWORD'
|
||||
# }
|
||||
},
|
||||
|
||||
# set `ldap` if you want that only authenticated users can shorten URLs
|
||||
# please note that everybody can still use shortend URLs
|
||||
# optional, no default
|
||||
#ldap => {
|
||||
# uri => 'ldaps://ldap.example.org', # server URI
|
||||
# user_tree => 'ou=users,dc=example,dc=org', # search base DN
|
||||
# bind_dn => 'uid=ldap_user,ou=users,dc=example,dc=org', # search bind DN
|
||||
# bind_pwd => 'secr3t', # search bind password
|
||||
# user_attr => 'uid', # user attribute (uid, mail, sAMAccountName, etc.)
|
||||
# user_filter => '(!(uid=ldap_user))', # user filter (to exclude some users, etc.)
|
||||
#},
|
||||
|
||||
# set `htpasswd` if you want to use an htpasswd file instead of ldap
|
||||
# create the file with `htpasswd -c lutim.passwd user`, update it with `htpasswd lutim.passwd user2`
|
||||
# make sure that lutim can read the file!
|
||||
# optional, no default
|
||||
#htpasswd => 'lutim.passwd',
|
||||
|
||||
# if you've set ldap or htpasswd above, the session will last `session_duration` seconds before
|
||||
# the user needs to reauthenticate
|
||||
# optional, default is 3600
|
||||
#session_duration => 3600,
|
||||
|
||||
# disable counters of images
|
||||
# set to 1 to disable counters
|
||||
# optional, counters are enabled by default
|
||||
#disable_img_stats => 0,
|
||||
|
||||
# define the height of the thumbnails generated at users' will
|
||||
# this is not the height of the thumbnails send after upload,
|
||||
# we're talking about thumbnails generated when someone asked for
|
||||
# https://example.org/lutim/tesrinp?thumb
|
||||
# this works only if you have ImageMagick
|
||||
# optional, default is 100 (pixels)
|
||||
#thumbnail_size => 100,
|
||||
|
||||
# maximum number of files that can be downloaded as a single zip archive
|
||||
# if too many files are asked, it results a timeout, so Lutim split the zip URL
|
||||
# in multiple URLs, each with max_file_size images.
|
||||
# timeout behavior depends heavily on your server ressources (CPU) and if images
|
||||
# are encrypted
|
||||
# optional, default is 15
|
||||
#max_files_in_zip => 15,
|
||||
|
||||
# maximum size (in MB) of memory allowed for the image cache
|
||||
# Lutim has a built-in memory-based image cache to accelerate responses to often-viewed images.
|
||||
# This setting makes the cache remove oldest viewed image if the cache size is over it.
|
||||
# WARNING: a cache is created for each hypnotoad worker, which by default is twice the number of
|
||||
# CPUs you have. See http://mojolicious.org/perldoc/Mojo/Server/Hypnotoad#workers for details
|
||||
# So, if you have 4 workers and set cache_max_size to 100, the real maximum size of RAM used for
|
||||
# cache is 400MB.
|
||||
# If set to 0, the cache is disabled
|
||||
# optional, default is 0
|
||||
#cache_max_size => 0,
|
||||
|
||||
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
|
||||
# If set to [], the use of memcached is disabled.
|
||||
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
|
||||
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
|
||||
# servers.
|
||||
# exemple of valid value: ['127.0.0.1:11211']
|
||||
# optional, default is []
|
||||
#memcached_servers => [],
|
||||
|
||||
# enable or disable Lutim built-in logs
|
||||
# set to 1 to disable logs
|
||||
# optional, default is 0
|
||||
#quiet_logs => 0,
|
||||
|
||||
# Content-Security-Policy header that will be sent by Lutim
|
||||
# Set to '' to disable CSP header
|
||||
# https://content-security-policy.com/ provides a good documentation about CSP.
|
||||
# https://report-uri.com/home/generate provides a tool to generate a CSP header.
|
||||
# optional, default is "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"
|
||||
# NB: unsafe-inline for script-src and style-src are here only because morris,
|
||||
# the graph library used in the stats page requires it
|
||||
# the default value is good for `default` theme
|
||||
#csp => "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'",
|
||||
|
||||
# X-Frame-Options header that will be sent by Lutim
|
||||
# Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/'
|
||||
# Set to '' to disable X-Frame-Options header
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
||||
# Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly
|
||||
# to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors)
|
||||
# optional, default is 'DENY'
|
||||
#x_frame_options => 'DENY',
|
||||
|
||||
# X-Content-Type-Options that will be sent by Lutim
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
|
||||
# Set to '' to disable X-Content-Type-Options header
|
||||
# optional, default is 'nosniff'
|
||||
#x_content_type_options => 'nosniff',
|
||||
|
||||
# X-XSS-Protection that will be sent by Lutim
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
|
||||
# Set to '' to disable X-XSS-Protection header
|
||||
# optional, default is '1; mode=block'
|
||||
#x_xss_protection => '1; mode=block',
|
||||
|
||||
# if set, the uploaded images will use this domain
|
||||
# optional
|
||||
#fixed_domain => 'example.org',
|
||||
|
||||
##########################
|
||||
# Lutim cron jobs settings
|
||||
##########################
|
||||
|
||||
# number of days shown in /stats page (used with script/lutim cron stats)
|
||||
# optional, default is 365
|
||||
#stats_day_num => 365,
|
||||
|
||||
# number of days senders' IP addresses are kept in database
|
||||
# after that delay, they will be deleted from database (used with script/lutim cron cleanbdd)
|
||||
# optional, default is 365
|
||||
#keep_ip_during => 365,
|
||||
|
||||
# max size of the files directory, in octets
|
||||
# used by script/lutim cron watch to trigger an action
|
||||
# optional, no default
|
||||
max_total_size => 10*1024*1024*1024,
|
||||
|
||||
# default action when files directory is over max_total_size (used with script/lutim cron watch)
|
||||
# valid values are 'warn', 'stop-upload' and 'delete'
|
||||
# please, see readme
|
||||
# optional, default is 'warn'
|
||||
#policy_when_full => 'warn',
|
||||
|
||||
# images which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task
|
||||
# if delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted
|
||||
# optional, no default
|
||||
#delete_no_longer_viewed_files => 90
|
||||
};
|
||||
301
t/sqlite3.conf
Normal file
301
t/sqlite3.conf
Normal file
@@ -0,0 +1,301 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
{
|
||||
####################
|
||||
# Hypnotoad settings
|
||||
####################
|
||||
# see http://mojolicio.us/perldoc/Mojo/Server/Hypnotoad for a full list of settings
|
||||
hypnotoad => {
|
||||
# array of IP addresses and ports you want to listen to
|
||||
listen => ['http://127.0.0.1:8080'],
|
||||
# if you use Lutim behind a reverse proxy like Nginx, you want to set proxy to 1
|
||||
# if you use Lutim directly, let it commented
|
||||
#proxy => 1,
|
||||
},
|
||||
|
||||
################
|
||||
# Lutim settings
|
||||
################
|
||||
|
||||
# put a way to contact you here and uncomment it
|
||||
# mandatory
|
||||
contact => 'John Doe, admin[at]example.com',
|
||||
|
||||
# random string used to encrypt cookies
|
||||
# mandatory
|
||||
secrets => ['fdjsofjoihrei'],
|
||||
|
||||
# choose a theme. See the available themes in `themes` directory
|
||||
# optional, default is 'default'
|
||||
#theme => 'default',
|
||||
|
||||
# length of the images random URL
|
||||
# optional, default is 8
|
||||
#length => 8,
|
||||
|
||||
# length of the encryption key
|
||||
# optional, default is 8
|
||||
#crypto_key_length => 8,
|
||||
|
||||
# how many URLs will be provisioned in a batch ?
|
||||
# optional, default is 5
|
||||
#provis_step => 5,
|
||||
|
||||
# max number of URLs to be provisioned
|
||||
# optional, default is 100
|
||||
#provisioning => 100,
|
||||
|
||||
# anti-flood protection delay, in seconds
|
||||
# users won't be able to ask Lutim to download images more than one per anti_flood_delay seconds
|
||||
# optional, default is 5
|
||||
#anti_flood_delay => 5,
|
||||
|
||||
# max image size, in octets
|
||||
# you can write it 10*1024*1024
|
||||
# optional, default is 10485760
|
||||
max_file_size => 1048576,
|
||||
|
||||
# if you want to have piwik statistics, provide a piwik image tracker
|
||||
# only the image tracker is allowed, no javascript
|
||||
# optional, no default
|
||||
#piwik_img => 'https://piwik.example.org/piwik.php?idsite=1&rec=1',
|
||||
|
||||
# if you want to include something in the right of the screen, put it here
|
||||
# here's an example to put the logo of your hoster
|
||||
# optional, no default
|
||||
#hosted_by => 'My super hoster <img src="http://hoster.example.com" alt="Hoster logo">',
|
||||
|
||||
# DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED
|
||||
# Lutim now checks if the X-Forwarded-Proto header is present and equal to https.
|
||||
# set to 1 if you use Lutim behind a secure web server
|
||||
# optional, default is 0
|
||||
#https => 0,
|
||||
|
||||
# broadcast_message which will displayed on all pages of Lutim (but no in json response)
|
||||
# optional, no default
|
||||
broadcast_message => 'test broadcast message',
|
||||
|
||||
# array of authorized domains for API calls.
|
||||
# if you want to authorize everyone to use the API: ['*']
|
||||
# optional, no domains allowed by default
|
||||
#allowed_domains => ['http://1.example.com', 'http://2.example.com'],
|
||||
|
||||
# default time limit for files
|
||||
# valid values are 0, 1, 7, 30 and 365
|
||||
# optional, default is 0 (no limit)
|
||||
default_delay => 30,
|
||||
|
||||
# comma-separated values proposed for delays
|
||||
# optional, default is '0,1,7,30,365'
|
||||
#proposed_delays => '0,1,7,30,365',
|
||||
|
||||
# number of days after which the images will be deleted, even if they were uploaded with "no delay" (or value superior to max_delay)
|
||||
# a warning message will be displayed on homepage
|
||||
# optional, default is 0 (no limit)
|
||||
max_delay => 200,
|
||||
|
||||
# if set to 1, all the images will be encrypted and the encryption option will no be displayed
|
||||
# optional, default is 0
|
||||
#always_encrypt => 0,
|
||||
|
||||
# you can allow to use a watermark on the uploaded images (or enforce its use)
|
||||
# define a path to the watermark image (provide an image with alpha channel)
|
||||
# you can define the path relative to lutim directory or set an absolute path
|
||||
# to disable the usage of a watermark, leave it blank or commented
|
||||
# optional, no default
|
||||
#watermark_path => '',
|
||||
|
||||
# the watermark can be a tiling one or a single one
|
||||
# when using a small one, you can choose where to place it
|
||||
# valid values are 'Center', 'North', 'NorthEast', 'East', 'SouthEast', 'South', 'SouthWest', 'West' and 'NorthWest' (case insensitive)
|
||||
# optional, default is 'SouthEast'
|
||||
#watermark_placement => 'SouthEast',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be used by default
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_default => 'none',
|
||||
|
||||
# choose which watermark (tiling, single or none) should be enforced (users will always have a watermark and won’t be able to disable it)
|
||||
# valid values are 'tiling', 'single' or 'none' (case insensitive)
|
||||
# optional, default is 'none'
|
||||
#watermark_enforce => 'none',
|
||||
|
||||
# length of the image's delete token
|
||||
# optional, default is 24
|
||||
#token_length => 24,
|
||||
|
||||
# URL sub-directory in which you want Lutim to be accessible
|
||||
# example: you want to have Lutim under https://example.org/lutim/
|
||||
# => set prefix to '/lutim' or to '/lutim/', it doesn't matter
|
||||
# optional, defaut is /
|
||||
#prefix => '/',
|
||||
|
||||
# if set to 1, Lutim will try to prevent its use without using the web interface
|
||||
# optional, default is 0
|
||||
#disable_api => 0,
|
||||
|
||||
# Define a path to the upload directory, where the uploaded images will be stored
|
||||
# You can define it relative to lutim directory or set an absolute path
|
||||
# The path is stored in database for each uploaded file, so you’ll need to do some
|
||||
# SQL commands if you change the upload_dir after getting images uploaded.
|
||||
# Remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is 'files'
|
||||
#upload_dir => 'files',
|
||||
|
||||
# choose what database you want to use
|
||||
# valid choices are sqlite and postgresql (all lowercase)
|
||||
# optional, default is sqlite
|
||||
dbtype => 'sqlite',
|
||||
|
||||
# SQLite ONLY - only used if dbtype is set to sqlite
|
||||
# define a path to the SQLite database
|
||||
# you can define it relative to lutim directory or set an absolute path
|
||||
# remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is lutim.db
|
||||
db_path => 'testsqlite3.db',
|
||||
|
||||
# PostgreSQL ONLY - only used if dbtype is set to postgresql
|
||||
# these are the credentials to access the PostgreSQL database
|
||||
# mandatory if you choosed postgresql as dbtype
|
||||
#pgdb => {
|
||||
# database => 'lutim',
|
||||
# host => 'localhost',
|
||||
# #user => 'DBUSER',
|
||||
# #pwd => 'DBPASSWORD'
|
||||
#},
|
||||
|
||||
# use Minion instead of directly increase counters
|
||||
# need to launch a minion worker service if enabled
|
||||
# optional, Minion is disabled by default
|
||||
minion => {
|
||||
enabled => 1,
|
||||
# Which Minion backend to use?
|
||||
# valid values are sqlite and postgresql (all lowercase)
|
||||
# mandatory if Minion is enabled, default is sqlite
|
||||
dbtype => 'postgresql',
|
||||
# SQLite ONLY - only used if if you choose sqlite as Minion backend, define the path to the minion database
|
||||
# you can define it relative to lutim directory or set an absolute path
|
||||
# remember that it has to be in a directory writable by Lutim user
|
||||
# optional, default is minion.db
|
||||
db_path => 'minion.db',
|
||||
# PostgreSQL ONLY - only used if you choose postgresql as Minion backend
|
||||
# these are the credentials to access the Minion's PostgreSQL database
|
||||
# mandatory if you choosed postgresql as Minion backend, no default
|
||||
pgdb => {
|
||||
database => 'lutim_minion',
|
||||
host => 'postgres',
|
||||
user => 'lutim',
|
||||
pwd => 'lutim_pwd'
|
||||
}
|
||||
},
|
||||
|
||||
# disable counters of images
|
||||
# set to 1 to disable counters
|
||||
# optional, counters are enabled by default
|
||||
#disable_img_stats => 0,
|
||||
|
||||
# define the height of the thumbnails generated at users' will
|
||||
# this is not the height of the thumbnails send after upload,
|
||||
# we're talking about thumbnails generated when someone asked for
|
||||
# https://example.org/lutim/tesrinp?thumb
|
||||
# this works only if you have ImageMagick
|
||||
# optional, default is 100 (pixels)
|
||||
#thumbnail_size => 100,
|
||||
|
||||
# maximum number of files that can be downloaded as a single zip archive
|
||||
# if too many files are asked, it results a timeout, so Lutim split the zip URL
|
||||
# in multiple URLs, each with max_file_size images.
|
||||
# timeout behavior depends heavily on your server ressources (CPU) and if images
|
||||
# are encrypted
|
||||
# optional, default is 15
|
||||
#max_files_in_zip => 15,
|
||||
|
||||
# maximum size (in MB) of memory allowed for the image cache
|
||||
# Lutim has a built-in memory-based image cache to accelerate responses to often-viewed images.
|
||||
# This setting makes the cache remove oldest viewed image if the cache size is over it.
|
||||
# WARNING: a cache is created for each hypnotoad worker, which by default is twice the number of
|
||||
# CPUs you have. See http://mojolicious.org/perldoc/Mojo/Server/Hypnotoad#workers for details
|
||||
# So, if you have 4 workers and set cache_max_size to 100, the real maximum size of RAM used for
|
||||
# cache is 400MB.
|
||||
# If set to 0, the cache is disabled
|
||||
# optional, default is 0
|
||||
#cache_max_size => 0,
|
||||
|
||||
# array of memcached servers to cache URL in order to accelerate responses to often-viewed URL.
|
||||
# If set to [], the use of memcached is disabled.
|
||||
# If you use memcached, the internal cache (see cache_max_size option above) will not be used.
|
||||
# Please see https://framagit.org/fiat-tux/hat-softwares/lutim/-/wikis/memcached to know how to configure your memcached
|
||||
# servers.
|
||||
# exemple of valid value: ['127.0.0.1:11211']
|
||||
# optional, default is []
|
||||
#memcached_servers => [],
|
||||
|
||||
# enable or disable Lutim built-in logs
|
||||
# set to 1 to disable logs
|
||||
# optional, default is 0
|
||||
#quiet_logs => 0,
|
||||
|
||||
# Content-Security-Policy header that will be sent by Lutim
|
||||
# Set to '' to disable CSP header
|
||||
# https://content-security-policy.com/ provides a good documentation about CSP.
|
||||
# https://report-uri.com/home/generate provides a tool to generate a CSP header.
|
||||
# optional, default is "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"
|
||||
# NB: unsafe-inline for script-src and style-src are here only because morris,
|
||||
# the graph library used in the stats page requires it
|
||||
# the default value is good for `default` theme
|
||||
#csp => "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'",
|
||||
|
||||
# X-Frame-Options header that will be sent by Lutim
|
||||
# Valid values are: 'DENY', 'SAMEORIGIN', 'ALLOW-FROM https://example.com/'
|
||||
# Set to '' to disable X-Frame-Options header
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
||||
# Please note that this will add a "frame-ancestors" directive to the CSP header (see above) accordingly
|
||||
# to the chosen setting (See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors)
|
||||
# optional, default is 'DENY'
|
||||
#x_frame_options => 'DENY',
|
||||
|
||||
# X-Content-Type-Options that will be sent by Lutim
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
|
||||
# Set to '' to disable X-Content-Type-Options header
|
||||
# optional, default is 'nosniff'
|
||||
#x_content_type_options => 'nosniff',
|
||||
|
||||
# X-XSS-Protection that will be sent by Lutim
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
|
||||
# Set to '' to disable X-XSS-Protection header
|
||||
# optional, default is '1; mode=block'
|
||||
#x_xss_protection => '1; mode=block',
|
||||
|
||||
# if set, the uploaded images will use this domain
|
||||
# optional
|
||||
#fixed_domain => 'example.org',
|
||||
|
||||
##########################
|
||||
# Lutim cron jobs settings
|
||||
##########################
|
||||
|
||||
# number of days shown in /stats page (used with script/lutim cron stats)
|
||||
# optional, default is 365
|
||||
#stats_day_num => 365,
|
||||
|
||||
# number of days senders' IP addresses are kept in database
|
||||
# after that delay, they will be deleted from database (used with script/lutim cron cleanbdd)
|
||||
# optional, default is 365
|
||||
#keep_ip_during => 365,
|
||||
|
||||
# max size of the files directory, in octets
|
||||
# used by script/lutim cron watch to trigger an action
|
||||
# optional, no default
|
||||
max_total_size => 10*1024*1024*1024,
|
||||
|
||||
# default action when files directory is over max_total_size (used with script/lutim cron watch)
|
||||
# valid values are 'warn', 'stop-upload' and 'delete'
|
||||
# please, see readme
|
||||
# optional, default is 'warn'
|
||||
#policy_when_full => 'warn',
|
||||
|
||||
# images which are not viewed since delete_no_longer_viewed_files days will be deleted by the cron cleanfiles task
|
||||
# if delete_no_longer_viewed_files is not set, the no longer viewed files will NOT be deleted
|
||||
# optional, no default
|
||||
#delete_no_longer_viewed_files => 90
|
||||
};
|
||||
166
t/test.t
Normal file
166
t/test.t
Normal file
@@ -0,0 +1,166 @@
|
||||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
use Mojo::Base -strict;
|
||||
use Mojo::File;
|
||||
use Mojo::JSON qw(true false);
|
||||
use Mojolicious;
|
||||
|
||||
use Test::More;
|
||||
use Test::Mojo;
|
||||
|
||||
use FindBin qw($Bin);
|
||||
use Digest::file qw(digest_file_hex);
|
||||
use IO::Uncompress::Unzip qw($UnzipError);
|
||||
|
||||
my ($m, $cfile);
|
||||
|
||||
BEGIN {
|
||||
use lib 'lib';
|
||||
$m = Mojolicious->new;
|
||||
$cfile = Mojo::File->new($Bin, '..' , 'lutim.conf');
|
||||
if (defined $ENV{MOJO_CONFIG}) {
|
||||
$cfile = Mojo::File->new($ENV{MOJO_CONFIG});
|
||||
unless (-e $cfile->to_abs) {
|
||||
$cfile = Mojo::File->new($Bin, '..', $ENV{MOJO_CONFIG});
|
||||
}
|
||||
}
|
||||
my $config = $m->plugin('Config' =>
|
||||
{
|
||||
file => $cfile->to_abs->to_string,
|
||||
default => {
|
||||
provisioning => 100,
|
||||
provis_step => 5,
|
||||
length => 8,
|
||||
always_encrypt => 0,
|
||||
anti_flood_delay => 5,
|
||||
max_file_size => 10*1024*1024,
|
||||
https => 0,
|
||||
proposed_delays => '0,1,7,30,365',
|
||||
default_delay => 0,
|
||||
max_delay => 0,
|
||||
token_length => 24,
|
||||
crypto_key_length => 8,
|
||||
thumbnail_size => 100,
|
||||
theme => 'default',
|
||||
dbtype => 'sqlite',
|
||||
db_path => 'lutim.db',
|
||||
max_files_in_zip => 15,
|
||||
prefix => '/',
|
||||
minion => {
|
||||
enabled => 0,
|
||||
dbtype => 'sqlite',
|
||||
db_path => 'minion.db'
|
||||
},
|
||||
cache_max_size => 0,
|
||||
memcached_servers => [],
|
||||
quiet_logs => 0,
|
||||
disable_img_stats => 0,
|
||||
x_frame_options => 'DENY',
|
||||
x_content_type_options => 'nosniff',
|
||||
x_xss_protection => '1; mode=block',
|
||||
}
|
||||
}
|
||||
);
|
||||
$m->plugin('Lutim::Plugin::Helpers');
|
||||
}
|
||||
|
||||
# Home page
|
||||
my $t = Test::Mojo->new('Lutim');
|
||||
$t->get_ok('/')
|
||||
->status_is(200)
|
||||
->content_like(qr/Let's Upload That IMage/i)
|
||||
->header_is('Content-Security-Policy' => "base-uri 'self'; connect-src 'self'; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' data:; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'")
|
||||
->header_is('X-Frame-Options' => 'DENY')
|
||||
->header_is('X-XSS-Protection' => '1; mode=block')
|
||||
->header_is('X-Content-Type-Options' => 'nosniff');
|
||||
|
||||
# Gzip static assets
|
||||
$t->get_ok('/css/lutim.css')
|
||||
->status_is(200)
|
||||
->header_is(Vary => 'Accept-Encoding');
|
||||
|
||||
# Instance settings informations
|
||||
$t->get_ok('/infos')
|
||||
->status_is(200)
|
||||
->json_has('/image_magick')
|
||||
->json_is(
|
||||
'/always_encrypt' => false,
|
||||
'/broadcast_message' => 'test broadcast message',
|
||||
'/contact' => 'John Doe, admin[at]example.com',
|
||||
'/default_delay' => 30,
|
||||
'/max_delay' => 200,
|
||||
'/max_file_size' => 1048576,
|
||||
'/upload_enabled' => true
|
||||
);
|
||||
|
||||
# Post image
|
||||
my $image = Mojo::File->new($Bin, '..', 'themes', 'default', 'public', 'img', 'Lutim.png')->to_string;
|
||||
$t->post_ok('/' => form => { file => { file => $image }, format => 'json' })
|
||||
->status_is(200)
|
||||
->json_has('/msg', '/success')
|
||||
->json_is('/success' => true, '/msg/filename' => 'Lutim.png')
|
||||
->json_like('/msg/short' => qr#[-_a-zA-Z0-9]{8}#, '/msg/real_short' => qr#[-_a-zA-Z0-9]{8}#, '/msg/token' => qr#[-_a-zA-Z0-9]{24}#);
|
||||
|
||||
# Post delete-at-first-view image
|
||||
my $raw = $t->ua->post('/' => form => { file => { file => $image }, 'first-view' => 1, format => 'json' })->res;
|
||||
my $short = $raw->json('/msg/short');
|
||||
|
||||
$t->get_ok('/'.$short)
|
||||
->status_is(200);
|
||||
|
||||
$t->get_ok('/'.$short)
|
||||
->status_is(302);
|
||||
|
||||
# Get zip file and test it
|
||||
$t->get_ok('/zip?i='.$short)
|
||||
->status_is(200)
|
||||
->header_is('Content-Disposition' => 'attachment;filename=images.zip');
|
||||
|
||||
my $zip = $t->ua->get('/zip?i='.$short)->res->body;
|
||||
my $u = new IO::Uncompress::Unzip \$zip
|
||||
or die "Cannot open zip file: $UnzipError";
|
||||
|
||||
my $status;
|
||||
my @files = qw(hosted_with_lutim.png images/ images/Lutim.png.txt);
|
||||
for ($status = 1; $status > 0; $status = $u->nextStream()) {
|
||||
my $name = $u->getHeaderInfo()->{Name};
|
||||
is($name, shift(@files));
|
||||
my $buff;
|
||||
while (($status = $u->read($buff)) > 0) {
|
||||
}
|
||||
last if $status < 0;
|
||||
}
|
||||
|
||||
# Delete image with token
|
||||
$raw = $t->ua->post('/' => form => { file => { file => $image }, format => 'json' })->res;
|
||||
my $rshort = $raw->json('/msg/real_short');
|
||||
my $token = $raw->json('/msg/token');
|
||||
|
||||
$t->get_ok('/'.$rshort)
|
||||
->status_is(200);
|
||||
|
||||
$t->get_ok('/d/'.$rshort.'/'.$token, form => { _format => 'json' })
|
||||
->status_is('200')
|
||||
->json_is(
|
||||
{
|
||||
success => true,
|
||||
msg => 'The image Lutim.png has been successfully deleted'
|
||||
}
|
||||
);
|
||||
|
||||
$t->get_ok('/'.$rshort)
|
||||
->status_is(302);
|
||||
|
||||
$t->app->minion->perform_jobs if $t->app->config('minion')->{enabled};
|
||||
|
||||
# Get image counter
|
||||
$t->post_ok('/c', form => { short => $rshort, token => $token })
|
||||
->status_is(200)
|
||||
->json_is(
|
||||
{
|
||||
success => true,
|
||||
counter => 1,
|
||||
enabled => false
|
||||
}
|
||||
);
|
||||
|
||||
done_testing();
|
||||
@@ -1,6 +0,0 @@
|
||||
% # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab:
|
||||
% my $scheme = (defined(config('https')) && config('https')) ? 'https' : 'http';
|
||||
<div class="modal-body">
|
||||
<%==l 'informations-body', url_for('/')->base->scheme($scheme)->to_abs().'/', config('contact') %>
|
||||
<%= link_to url_for('index') => ( class => "btn btn-primary btn-lg" ) => begin %><%=l 'back-to-index' %><% end%>
|
||||
</div>
|
||||
@@ -1,235 +0,0 @@
|
||||
% # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab:
|
||||
% my $scheme = (defined(config('https')) && config('https')) ? 'https' : 'http';
|
||||
<div class="messages">
|
||||
% if (config('always_encrypt')) {
|
||||
<p><%=l 'always_encrypt' %></p>
|
||||
% }
|
||||
% if (defined(stash('short'))) {
|
||||
<div class="alert alert-success">
|
||||
% if (defined(stash('short'))) {
|
||||
<img class="pull-left thumbnail" alt="<%= stash('filename') %> thumbnail" src="<%= stash('thumb') %>">
|
||||
% }
|
||||
<div>
|
||||
<strong><%= stash('filename') %></strong>
|
||||
<ul class="list-unstyled">
|
||||
<li><i class="icon icon-eye" title="<%=l 'view-link' %>"></i> <%= link_to url_for('/')->base->scheme($scheme)->to_abs.'/'.stash('short') => begin %><%= url_for('/')->base->scheme($scheme)->to_abs.'/'.stash('short') %><%= end %></li>
|
||||
<li><i class="icon icon-download" title="<%=l 'download-link' %>"></i> <%= link_to url_for('/')->base->scheme($scheme)->to_abs.'/'.stash('short').'?dl' => begin %><%= url_for('/')->base->scheme($scheme)->to_abs.'/'.stash('short').'?dl' %><%= end %></li>
|
||||
<li><i class="icon icon-touiteur" title="<%=l 'twitter-link' %>"></i> <%= link_to url_for('/')->base->scheme($scheme)->to_abs.'/'.stash('short').'?t' => begin %><%= url_for('/')->base->scheme($scheme)->to_abs.'/'.stash('short').'?t' %><%= end %></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
% }
|
||||
% if (defined(flash('msg'))) {
|
||||
<div class="alert alert-danger">
|
||||
<strong><%=l 'some-bad' %></strong><br>
|
||||
<%= flash('filename') %> <%= flash('msg') %>
|
||||
</div>
|
||||
% }
|
||||
</div>
|
||||
|
||||
<noscript>
|
||||
<form class="form" role="form" method="POST" action="<%== url_for('add') %>" enctype="multipart/form-data">
|
||||
<div class="form-group form-inline">
|
||||
<select name="delete-day" class="form-control">
|
||||
% for my $delay (qw/0 1 7 30 365/) {
|
||||
% my $text = ($delay == 7 || $delay == 30) ? l('delay_days', $delay) : l("delay_$delay");
|
||||
% if (config('max_delay')) {
|
||||
% if ($delay) {
|
||||
% if ($delay < config('max_delay')) {
|
||||
<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>
|
||||
% } elsif ($delay == config('max_delay')) {
|
||||
<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>
|
||||
% last;
|
||||
% } else {
|
||||
% my $text = ($delay == 1) ? l('delay_1') : l('delay_days', $delay);
|
||||
<option value="<%= config('max_delay') %>" <%== is_selected(config('max_delay')) %>><%=l('delay_days', config('max_delay')) %></option>
|
||||
% last;
|
||||
% }
|
||||
% }
|
||||
% } else {
|
||||
<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>
|
||||
% }
|
||||
% }
|
||||
</select>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="first-view"> <%=l 'delete-first' %>
|
||||
</label>
|
||||
<label class="always-encrypt">
|
||||
<input type="checkbox" name="crypt"> <%=l 'crypt_image' %>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="lutim-file"><%=l 'upload_image' %></label>
|
||||
<input type="file" name="file" id="lutim-file">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="lutim-file-url"><%=l 'upload_image_url' %></label>
|
||||
<input type="url" name="lutim-file-url" placeholder="<%=l 'image_url' %>">
|
||||
<p class="help-block"><%=l 'image-only' %></p>
|
||||
</div>
|
||||
<%= submit_button l('go'), class => 'btn btn-default btn-primary', id => 'submitbutton' %>
|
||||
</form>
|
||||
</noscript>
|
||||
|
||||
<!-- D&D Zone-->
|
||||
<div class="jsonly">
|
||||
<div class="form-group form-inline">
|
||||
<select id="delete-day" class="form-control">
|
||||
% for my $delay (qw/0 1 7 30 365/) {
|
||||
% my $text = ($delay == 7 || $delay == 30) ? l('delay_days', $delay) : l("delay_$delay");
|
||||
% if (config('max_delay')) {
|
||||
% if ($delay) {
|
||||
% if ($delay < config('max_delay')) {
|
||||
<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>
|
||||
% } elsif ($delay == config('max_delay')) {
|
||||
<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>
|
||||
% last;
|
||||
% } else {
|
||||
% my $text = ($delay == 1) ? l('delay_1') : l('delay_days', $delay);
|
||||
<option value="<%= config('max_delay') %>" <%== is_selected(config('max_delay')) %>><%=l('delay_days', config('max_delay')) %></option>
|
||||
% last;
|
||||
% }
|
||||
% }
|
||||
% } else {
|
||||
<option value="<%= $delay %>" <%== is_selected($delay) %>><%= $text %></option>
|
||||
% }
|
||||
% }
|
||||
</select>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="first-view"> <%=l 'delete-first' %>
|
||||
</label>
|
||||
<label class="always-encrypt">
|
||||
<input type="checkbox" id="crypt"> <%=l 'crypt_image' %>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="drag-and-drop-zone" class="uploader">
|
||||
<div><%=l 'drag-n-drop' %></div>
|
||||
<div class="or"><%=l 'or' %></div>
|
||||
<div class="browser">
|
||||
<label>
|
||||
<span><%=l 'file-browser' %></span>
|
||||
<input type="file" name="files[]" multiple="multiple" title='<%=l 'file-browser' %>'>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help-block"><%=l 'image-only' %></p>
|
||||
<form class="form-horizontal" role="form" method="POST" action="<%== url_for('add') %>">
|
||||
<div class="form-group">
|
||||
<span class="col-sm-3"><span class="hidden-spin" style="font-size:200%; display:none;" > <i class="icon-spinner animate-spin pull-right"></i></span><label for="lutim-file-url" class="control-label pull-right"><%=l 'upload_image_url' %></label></span>
|
||||
<div class="col-sm-9">
|
||||
<input type="url" name="file-url" class="form-control" id="lutim-file-url" placeholder="<%=l 'image_url' %>">
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" class="btn btn-default btn-primary pull-right" id="file-url-button"><%=l 'go' %></a>
|
||||
</form>
|
||||
</div>
|
||||
<!-- /D&D Zone -->
|
||||
|
||||
%= javascript 'js/dmuploader.min.js'
|
||||
%= javascript begin
|
||||
function link(url, dl) {
|
||||
if (dl !== '') {
|
||||
url = url+'?'+dl;
|
||||
}
|
||||
return '<a href="<%== url_for('index')->base->scheme($scheme)->to_abs() %>/'+url+'"><%== url_for('index')->base->scheme($scheme)->to_abs() %>/'+url+'</a>';
|
||||
}
|
||||
function message(success, msg) {
|
||||
if(success) {
|
||||
var thumb = (msg.thumb !== null) ? '<img class="pull-left thumbnail" alt="'+msg.filename+' thumbnail" src="'+msg.thumb+'">' : ''
|
||||
return '<div class="alert alert-success"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>'
|
||||
+thumb
|
||||
+'<div><strong>'
|
||||
+msg.filename
|
||||
+'</strong>'
|
||||
+'<ul class="list-unstyled"><li><i class="icon icon-eye" title="<%=l 'view-link' %>"></i> '
|
||||
+link(msg.short, '')
|
||||
+'</a></li><li><i class="icon icon-download" title="<%=l 'download-link' %>"></i> '
|
||||
+link(msg.short, 'dl')
|
||||
+'</a></li><li><i class="icon icon-touiteur" title="<%=l 'twitter-link' %>"></i> '
|
||||
+link(msg.short, 't')
|
||||
+'</li></ul></div>';
|
||||
} else {
|
||||
return '<div class="alert alert-danger"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button><strong><%=l 'some-bad' %></strong><br>'
|
||||
+msg.filename
|
||||
+"<br>"
|
||||
+msg.msg
|
||||
+'</div>';
|
||||
}
|
||||
}
|
||||
function bindddz(firstview, deleteday) {
|
||||
$('#drag-and-drop-zone').dmUploader({
|
||||
url: '<%== url_for('add') %>',
|
||||
dataType: 'json',
|
||||
allowedTypes: 'image/*',
|
||||
maxFileSize: <%= $max_file_size %>,
|
||||
onNewFile: function(id, file){
|
||||
$(".messages").append('<div id="'+id+'-div">'+file.name+'<br><div class="progress"><div id="'+id+'"class="progress-bar progress-striped active" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"><span id="'+id+'-text" class="pull-left" style="padding-left: 10px;"> 0%</span></div></div></div>');
|
||||
},
|
||||
onUploadProgress: function(id, percent){
|
||||
var percentStr = ' '+percent+'%';
|
||||
$('#'+id).prop('aria-valuenow', percent);
|
||||
$('#'+id).prop('style', 'width: '+percent+'%;');
|
||||
$('#'+id+'-text').html(percentStr);
|
||||
|
||||
},
|
||||
onUploadSuccess: function(id, data){
|
||||
$('#'+id+'-div').remove();
|
||||
$(".messages").append(message(data.success, data.msg));
|
||||
},
|
||||
onUploadError: function(id, message){
|
||||
$(".messages").append(message(false, ''));
|
||||
},
|
||||
onFileSizeError: function(file){
|
||||
$(".messages").append(message(false, { filename: file.name, msg: '<%= l('file_too_big', $max_file_size) %>'}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function upload_url() {
|
||||
var val = $("#lutim-file-url").val();
|
||||
if (val !== undefined && val !== "") {
|
||||
$("#lutim-file-url").prop('disabled', 'disabled');
|
||||
$(".hidden-spin").css('display', 'block');
|
||||
console.log(val);
|
||||
$.ajax({
|
||||
url : '<%== url_for('add') %>',
|
||||
type : "POST",
|
||||
data : {
|
||||
'lutim-file-url' : val,
|
||||
'format' : 'json',
|
||||
'first-view' : ($("#first-view").prop('checked')) ? 1 : 0,
|
||||
'crypt' : ($("#crypt").prop('checked')) ? 1 : 0,
|
||||
'delete-day' : $("#delete-day").val()
|
||||
},
|
||||
success: function(data) {
|
||||
$(".messages").append(message(data.success, data.msg));
|
||||
if (data.success) {
|
||||
$("#lutim-file-url").val('');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$(".messages").append(message(false, ''));
|
||||
},
|
||||
complete: function() {
|
||||
$("#lutim-file-url").prop('disabled', '');
|
||||
$(".hidden-spin").css('display', 'none');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("fhdsjnf");
|
||||
}
|
||||
}
|
||||
|
||||
$('document').ready(function() {
|
||||
var firstview = ($("#first-view").prop('checked')) ? 1 : 0;
|
||||
var deleteday = ($("#delete-day").prop('checked')) ? 1 : 0;
|
||||
|
||||
bindddz(firstview, deleteday);
|
||||
|
||||
$("#file-url-button").on("click", upload_url);
|
||||
});
|
||||
% end
|
||||
@@ -1,110 +0,0 @@
|
||||
% # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab:
|
||||
% use Mojo::Util qw(url_escape);
|
||||
% my $twitter_url = 'https://twitter.com/share';
|
||||
% my $scheme = (defined(config('https')) && config('https')) ? 'https' : 'http';
|
||||
% my $url = url_for('/')->base->scheme($scheme)->to_abs().'/';
|
||||
% $twitter_url .= '?url='.url_escape("$url")
|
||||
% .'&via=framasky'
|
||||
% .'&text=Check out this %23LUTIm instance! ';
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>LUTIm</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" type="image/png" href="<%= url_for('/') %>img/favicon.png">
|
||||
%= stylesheet 'css/bootstrap.min.css', media => 'screen'
|
||||
%= stylesheet 'css/fontello.css'
|
||||
%= stylesheet 'css/animation.css'
|
||||
%= stylesheet 'css/uploader.css'
|
||||
%= stylesheet begin
|
||||
body {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 15px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.jsonly {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
% if (config('always_encrypt')) {
|
||||
label.always-encrypt {
|
||||
display: none;
|
||||
}
|
||||
% }
|
||||
% end
|
||||
%= javascript 'js/jquery-2.1.0.min.js'
|
||||
%= javascript 'js/bootstrap.min.js'
|
||||
%= javascript begin
|
||||
$('document').ready(function() {
|
||||
$('.jsonly').show();
|
||||
});
|
||||
% end
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div>
|
||||
% if (defined(config('hosted_by'))) {
|
||||
<div class="pull-right">
|
||||
<%== config('hosted_by') %>
|
||||
</div>
|
||||
% }
|
||||
<div>
|
||||
<div class="pull-left hidden-xs">
|
||||
<img src="<%= url_for('/') %>img/LUTIm_small.png" alt="LUTIm logo">
|
||||
</div>
|
||||
<h1>Let's Upload That Image!</h1>
|
||||
<p>
|
||||
© 2014 <%= link_to 'http://www.fiat-tux.fr' => begin %>Luc Didry<% end %> —
|
||||
<%=l 'license' %> <%= link_to 'https://www.gnu.org/licenses/agpl-3.0.html' => begin %>AGPL<% end %> —
|
||||
<span class="jsonly"><a data-toggle="modal" href="#myModal"><%=l 'informations' %></a> — </span>
|
||||
<noscript><%= link_to url_for('about') => begin %><%=l 'informations' %><% end %> — </noscript>
|
||||
<%= link_to 'https://github.com/ldidry/lutim' => (title => l 'fork-me') => begin %><i class="lead icon icon-github-circled"></i><% end %>
|
||||
<%= link_to $twitter_url => (title => l 'share-twitter') => begin %><i class="lead icon icon-touiteur"></i><% end %>
|
||||
<%= link_to 'https://flattr.com/submit/auto?user_id=_SKy_&url='.$url.'&title=LUTIm&category=software' => (title => 'Flattr this') => begin %><i class="lead icon icon-flattr"></i><% end %>
|
||||
<%= link_to 'bitcoin:1K3n4MXNRSMHk28oTfXEvDunWFthePvd8v?label=lutim' => (title => 'Give Bitcoins') => begin %><i class="lead icon icon-bitcoin"></i><% end %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
% if (defined(config('broadcast_message'))) {
|
||||
<div class="alert alert-info">
|
||||
<strong><%= config('broadcast_message') %></strong>
|
||||
</div>
|
||||
% }
|
||||
% if (defined(stash('stop_upload'))) {
|
||||
<div class="alert alert-danger">
|
||||
<strong><%= stash('stop_upload') %></strong>
|
||||
</div>
|
||||
% }
|
||||
<%= content %>
|
||||
<div class="modal fade bs-modal-lg" id="myModal" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 class="modal-title">LUTIm</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<%==l 'informations-body', url_for('/')->base->scheme($scheme)->to_abs().'/', config('contact') %>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
</div>
|
||||
% if (defined(config('piwik_img'))) {
|
||||
<img src="<%== config('piwik_img') %>" style="border:0" alt="" />
|
||||
% }
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,64 +0,0 @@
|
||||
% # vim:set sts=4 sw=4 ts=4 ft=html.epl expandtab:
|
||||
%= javascript 'js/raphael.js'
|
||||
%= javascript 'js/raphael.path.methods.js'
|
||||
%= javascript 'js/jquery.simplegraph.js'
|
||||
%= include 'data'
|
||||
<div id="evol-holder"></div>
|
||||
<div id="total-holder"></div>
|
||||
<p><small><%=l 'graph-data-once-a-day' %></small></p>
|
||||
|
||||
<h4><%= $total %><%=l 'pushed-images' %></h4>
|
||||
|
||||
<%= link_to url_for('index') => ( class => "btn btn-primary btn-lg" ) => begin %><%=l 'back-to-index' %><% end%>
|
||||
%= javascript begin
|
||||
function graph(stats_data, stats_labels, stats_total) {
|
||||
// Plot the data
|
||||
// - Temperature Graph - adds colour, fill, and a minimum value for the y axis
|
||||
$("#evol-holder").simplegraph(
|
||||
stats_data,
|
||||
stats_labels,
|
||||
{
|
||||
addHover: true,
|
||||
penColor: "#f00",
|
||||
fillUnderLine: true,
|
||||
drawPoints: true,
|
||||
width: document.body.offsetWidth - 50
|
||||
}
|
||||
)
|
||||
$("#total-holder").simplegraph(
|
||||
stats_total,
|
||||
stats_labels,
|
||||
{
|
||||
addHover: true,
|
||||
penColor: "#00f",
|
||||
fillUnderLine: true,
|
||||
drawPoints: true,
|
||||
width: document.body.offsetWidth - 50
|
||||
}
|
||||
);
|
||||
}
|
||||
$(document).ready(function() {
|
||||
// Get the data
|
||||
var stats_labels = [], stats_data = [], stats_total = [];
|
||||
$("#stats-data thead th").each(function () {
|
||||
stats_labels.push($(this).html());
|
||||
});
|
||||
$("#stats-data tbody tr:first-child td").each(function () {
|
||||
stats_data.push($(this).html());
|
||||
});
|
||||
$("#stats-data tbody tr:nth-child(2) td").each(function () {
|
||||
stats_total.push($(this).html());
|
||||
});
|
||||
|
||||
// Hide the data
|
||||
$("#stats-data").hide();
|
||||
|
||||
graph(stats_data, stats_labels, stats_total);
|
||||
|
||||
$(window).resize(function() {
|
||||
$("#evol-holder").empty();
|
||||
$("#total-holder").empty();
|
||||
graph(stats_data, stats_labels, stats_total);
|
||||
});
|
||||
});
|
||||
% end
|
||||
@@ -1,21 +0,0 @@
|
||||
% # vim:set sw=4 ts=4 sts=4 ft=html.epl expandtab:
|
||||
% my $scheme = (defined(config('https')) && config('https')) ? 'https' : 'http';
|
||||
<!DOCTYPE html>
|
||||
<html style="height:100%;">
|
||||
<head>
|
||||
<title>LUTIm</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" type="image/png" href="<%= url_for('/')->base->scheme($scheme) %>/img/favicon.png">
|
||||
<meta name="twitter:card" content="photo">
|
||||
% if (defined(config('tweet_card_via'))) {
|
||||
<meta name="twitter:site" content="<%= config('tweet_card_via') %>">
|
||||
% }
|
||||
<meta name="twitter:image:src" content="<%= url_for('/')->base->scheme($scheme)->to_abs().'/'.$short %>">
|
||||
</head>
|
||||
<body style="height: 97%;">
|
||||
<img style="max-width:100%; max-height:100%;" src="<%= url_for('/').$short %>" alt="<%= $filename %>">
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
12
themes/default/lib/Lutim/I18N.pm
Normal file
12
themes/default/lib/Lutim/I18N.pm
Normal file
@@ -0,0 +1,12 @@
|
||||
package Lutim::I18N;
|
||||
|
||||
use base 'Locale::Maketext';
|
||||
use File::Basename qw/dirname/;
|
||||
use Locale::Maketext::Lexicon {
|
||||
_auto => 1,
|
||||
_decode => 1,
|
||||
_style => 'gettext',
|
||||
'*' => [Gettext => dirname(__FILE__) . '/I18N/*.po']
|
||||
};
|
||||
|
||||
1;
|
||||
655
themes/default/lib/Lutim/I18N/ar.po
Normal file
655
themes/default/lib/Lutim/I18N/ar.po
Normal file
@@ -0,0 +1,655 @@
|
||||
# Lutim language file
|
||||
# Copyright (C) 2014 Luc Didry
|
||||
# This file is distributed under the same license as the Lutim package.
|
||||
#
|
||||
# Translators:
|
||||
# ButterflyOfFire <butterflyoffire@protonmail.com>, ٢٠١٨, 2018.
|
||||
# ButterflyOfFire <butterflyoffire@protonmail.com>, 2018. #zanata
|
||||
# Luc Didry <luc@framasoft.org>, 2018. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"PO-Revision-Date: 2020-03-09 16:23+0000\n"
|
||||
"Last-Translator: Luc Didry <luc@framasoft.org>\n"
|
||||
"Language-Team: Arabic <https://weblate.framasoft.org/projects/lutim/"
|
||||
"default-theme/ar/>\n"
|
||||
"Language: ar\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
|
||||
"&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n"
|
||||
"X-Generator: Weblate 3.11.2\n"
|
||||
"n%100<=10 ? 3: n%100>=11 ? 4 : 5);\n"
|
||||
|
||||
#. (7)
|
||||
#. (30)
|
||||
#. ($delay)
|
||||
#. (config('max_delay')
|
||||
#: lib/Lutim/Command/cron/stats.pm:159 lib/Lutim/Command/cron/stats.pm:160 lib/Lutim/Command/cron/stats.pm:173 lib/Lutim/Command/cron/stats.pm:174 lib/Lutim/Command/cron/stats.pm:190 lib/Lutim/Command/cron/stats.pm:191 themes/default/templates/myfiles.html.ep:24 themes/default/templates/myfiles.html.ep:33 themes/default/templates/myfiles.html.ep:34 themes/default/templates/partial/for_my_delay.html.ep:13 themes/default/templates/partial/for_my_delay.html.ep:14 themes/default/templates/partial/for_my_delay.html.ep:4 themes/default/templates/partial/lutim.js.ep:140 themes/default/templates/partial/lutim.js.ep:149 themes/default/templates/partial/lutim.js.ep:150 themes/default/templates/partial/raw.js.ep:23 themes/default/templates/partial/raw.js.ep:24 themes/default/templates/partial/raw.js.ep:6 themes/default/templates/partial/raw.js.ep:7 themes/default/templates/raw.html.ep:8 themes/default/templates/raw.html.ep:9
|
||||
msgid "%1 days"
|
||||
msgstr "%1 أيام"
|
||||
|
||||
#. ($total)
|
||||
#: themes/default/templates/stats.html.ep:2
|
||||
msgid "%1 sent images on this instance from beginning."
|
||||
msgstr "تم إرسال %1 صورة على مثيل الخادوم هذا منذ البداية."
|
||||
|
||||
#: themes/default/templates/index.html.ep:208
|
||||
msgid "-or-"
|
||||
msgstr "-أو-"
|
||||
|
||||
#: lib/Lutim.pm:350 lib/Lutim/Command/cron/stats.pm:161 lib/Lutim/Command/cron/stats.pm:175 lib/Lutim/Command/cron/stats.pm:192 themes/default/templates/index.html.ep:5 themes/default/templates/myfiles.html.ep:5 themes/default/templates/partial/raw.js.ep:25 themes/default/templates/partial/raw.js.ep:8 themes/default/templates/raw.html.ep:10
|
||||
msgid "1 year"
|
||||
msgstr "سنة واحدة"
|
||||
|
||||
#: lib/Lutim.pm:349 lib/Lutim/Command/cron/stats.pm:158 lib/Lutim/Command/cron/stats.pm:172 lib/Lutim/Command/cron/stats.pm:189 themes/default/templates/index.html.ep:4 themes/default/templates/myfiles.html.ep:33 themes/default/templates/myfiles.html.ep:4 themes/default/templates/partial/for_my_delay.html.ep:13 themes/default/templates/partial/lutim.js.ep:149 themes/default/templates/partial/raw.js.ep:22 themes/default/templates/partial/raw.js.ep:5 themes/default/templates/raw.html.ep:7
|
||||
msgid "24 hours"
|
||||
msgstr "24 ساعة"
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:180
|
||||
msgid ": Error while trying to get the counter."
|
||||
msgstr ": طرأ هناك خطأ أثناء محاولة إستعادة العداد."
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:77
|
||||
msgid "About"
|
||||
msgstr "عن البرنامج"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:154 themes/default/templates/raw.html.ep:3
|
||||
msgid "Active images"
|
||||
msgstr "الصور النشطة"
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:328
|
||||
msgid "An error occured while downloading the image."
|
||||
msgstr "طرأ هناك خطأ أثناء عملية تنزيل الصورة."
|
||||
|
||||
#: themes/default/templates/zip.html.ep:2
|
||||
msgid "Archives download"
|
||||
msgstr "تنزيل الأرشيف"
|
||||
|
||||
#: themes/default/templates/about.html.ep:44 themes/default/templates/myfiles.html.ep:135 themes/default/templates/stats.html.ep:25
|
||||
msgid "Back to homepage"
|
||||
msgstr "العودة إلى الصفحة الرئيسية"
|
||||
|
||||
#: themes/default/templates/index.html.ep:211 themes/default/templates/index.html.ep:212
|
||||
msgid "Click to open the file browser"
|
||||
msgstr "إضغط لفتح مدير الملفات"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:51
|
||||
msgid "Close"
|
||||
msgstr "إغلاق"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:29
|
||||
msgid "Close (Esc)"
|
||||
msgstr "إغلاق (Esc)"
|
||||
|
||||
#: themes/default/templates/about.html.ep:30
|
||||
msgid "Contributors"
|
||||
msgstr "المساهمون"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:113 themes/default/templates/partial/common.js.ep:93
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "تم نسخه إلى الحافظة"
|
||||
|
||||
#: themes/default/templates/partial/lutim.js.ep:215 themes/default/templates/partial/lutim.js.ep:278 themes/default/templates/partial/lutim.js.ep:364
|
||||
msgid "Copy all view links to clipboard"
|
||||
msgstr "أنسخ كافة روابط العرض إلى الحافظة"
|
||||
|
||||
#: themes/default/templates/index.html.ep:103 themes/default/templates/index.html.ep:111 themes/default/templates/index.html.ep:18 themes/default/templates/index.html.ep:36 themes/default/templates/index.html.ep:54 themes/default/templates/index.html.ep:87 themes/default/templates/index.html.ep:95 themes/default/templates/myfiles.html.ep:104 themes/default/templates/myfiles.html.ep:68 themes/default/templates/myfiles.html.ep:86 themes/default/templates/partial/common.js.ep:186 themes/default/templates/partial/lutim.js.ep:106 themes/default/templates/partial/lutim.js.ep:121 themes/default/templates/partial/lutim.js.ep:80 themes/default/templates/partial/lutim.js.ep:92 themes/default/templates/partial/myfiles.js.ep:142
|
||||
msgid "Copy to clipboard"
|
||||
msgstr "أنسخ إلى الحافظة"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:123
|
||||
msgid "Counter"
|
||||
msgstr "العداد"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:18
|
||||
msgid "Delay repartition chart for disabled images"
|
||||
msgstr ""
|
||||
|
||||
#: themes/default/templates/stats.html.ep:15
|
||||
msgid "Delay repartition chart for enabled images"
|
||||
msgstr "المنحنى البياني للتوزيع الزمني للصور النشطة"
|
||||
|
||||
#: themes/default/templates/index.html.ep:133 themes/default/templates/index.html.ep:165 themes/default/templates/index.html.ep:196 themes/default/templates/myfiles.html.ep:124 themes/default/templates/myfiles.html.ep:45 themes/default/templates/partial/lutim.js.ep:161
|
||||
msgid "Delete at first view?"
|
||||
msgstr "هل تود حذف الصورة مباشرة بعد أول عرض ؟"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:155 themes/default/templates/raw.html.ep:4
|
||||
msgid "Deleted images"
|
||||
msgstr "الصور المحذوفة"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:156 themes/default/templates/raw.html.ep:5
|
||||
msgid "Deleted images in 30 days"
|
||||
msgstr "الصور المحذوفة منذ 30 يوما"
|
||||
|
||||
#: themes/default/templates/index.html.ep:116 themes/default/templates/myfiles.html.ep:127 themes/default/templates/partial/common.js.ep:178 themes/default/templates/partial/common.js.ep:181
|
||||
msgid "Deletion link"
|
||||
msgstr "رابط الحذف"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:10
|
||||
msgid "Download all images"
|
||||
msgstr "تنزيل كافة الصور"
|
||||
|
||||
#: themes/default/templates/index.html.ep:101 themes/default/templates/index.html.ep:99 themes/default/templates/partial/lutim.js.ep:102 themes/default/templates/partial/lutim.js.ep:98
|
||||
msgid "Download link"
|
||||
msgstr "رابط التنزيل"
|
||||
|
||||
#: themes/default/templates/index.html.ep:28 themes/default/templates/index.html.ep:31 themes/default/templates/myfiles.html.ep:78 themes/default/templates/myfiles.html.ep:81
|
||||
msgid "Download zip link"
|
||||
msgstr "رابط التنزيل كحزمة مضغوطة"
|
||||
|
||||
#: themes/default/templates/index.html.ep:207
|
||||
msgid "Drag & drop images here"
|
||||
msgstr "إسحب ثم القي الصور هنا"
|
||||
|
||||
#: themes/default/templates/about.html.ep:7
|
||||
msgid ""
|
||||
"Drag and drop an image in the appropriate area or use the traditional way to "
|
||||
"send files and Lutim will provide you four URLs. One to view the image, an "
|
||||
"other to directly download it, one you can use on social networks and a last "
|
||||
"to delete the image when you want."
|
||||
msgstr ""
|
||||
"إسحب ثم القي صورة ما على المساحة المخصصة لذلك أو قم باستخدام الأسلوب المعتاد "
|
||||
"لإرسال الملفات و سيقوم لوتان بعدها بتوفير أربع روابط. رابط لعرض الصورة و آخر "
|
||||
"لتنزيل الصورة مباشرة و آخر يمكنكم إستخدامه على شبكات التواصل الإجتماعي و "
|
||||
"أخيرا رابط آخر يمكّنكم من حذف الصورة إن شئتم."
|
||||
|
||||
#: themes/default/templates/index.html.ep:168 themes/default/templates/index.html.ep:199
|
||||
msgid "Encrypt the image (Lutim does not keep the key)."
|
||||
msgstr "قم بتعمية و تشفير الصورة (لوتان Lutim لا يحتفظ بالمفتاح)."
|
||||
|
||||
#: themes/default/templates/partial/lutim.js.ep:45 themes/default/templates/partial/myfiles.js.ep:113
|
||||
msgid "Error while trying to modify the image."
|
||||
msgstr "طرأ هناك خطأ أثناء عملية تعديل الصورة."
|
||||
|
||||
#: themes/default/templates/stats.html.ep:10
|
||||
msgid "Evolution of total files"
|
||||
msgstr "تطور العدد الإجمالي للملفات"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:126
|
||||
msgid "Expires at"
|
||||
msgstr "تنتهي مدة صلاحيتها في"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:112
|
||||
msgid "Export localStorage data"
|
||||
msgstr "تصدير بيانات localStorage"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:121
|
||||
msgid "File name"
|
||||
msgstr "إسم الملف"
|
||||
|
||||
#: themes/default/templates/about.html.ep:24
|
||||
msgid ""
|
||||
"For more details, see the <a href=\"https://framagit.org/luc/"
|
||||
"lutim\">homepage of the project</a>."
|
||||
msgstr ""
|
||||
"للمزيد من التفاصيل، قم بزيارة <a href=\"https://framagit.org/luc/"
|
||||
"lutim\">الصفحة الرئيسة للمشروع</a>."
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:80
|
||||
msgid "Fork me!"
|
||||
msgstr "قم بتشعيبي !"
|
||||
|
||||
#: themes/default/templates/index.html.ep:10 themes/default/templates/index.html.ep:13 themes/default/templates/myfiles.html.ep:60 themes/default/templates/myfiles.html.ep:63
|
||||
msgid "Gallery link"
|
||||
msgstr "رابط المعرض"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:116 themes/default/templates/partial/common.js.ep:134
|
||||
msgid "Hit Ctrl+C, then Enter to copy the short link"
|
||||
msgstr "إضغط على CTRL+C ثم على مفتاح الإدخال للقيام بنسخ الرابط القصير"
|
||||
|
||||
#: themes/default/templates/layouts/default.html.ep:47
|
||||
msgid "Homepage"
|
||||
msgstr "الصفحة الرئيسية"
|
||||
|
||||
#: themes/default/templates/about.html.ep:20
|
||||
msgid "How do you pronounce Lutim?"
|
||||
msgstr "كيف يُنطق Lutim ؟"
|
||||
|
||||
#: themes/default/templates/about.html.ep:6
|
||||
msgid "How does it work?"
|
||||
msgstr "كيف يشتغل ؟"
|
||||
|
||||
#: themes/default/templates/about.html.ep:18
|
||||
msgid "How to report an image?"
|
||||
msgstr "كيف يتم الإبلاغ عن صورة ما ؟"
|
||||
|
||||
#: themes/default/templates/about.html.ep:14
|
||||
msgid ""
|
||||
"If the files are deleted if you ask it while posting it, their SHA512 "
|
||||
"footprint are retained."
|
||||
msgstr ""
|
||||
"إذا تم حذف الملفات و إن قمت بطلب ذلك أثناء الإرسال سوف نقوم بالإحتفاظ ببصمات "
|
||||
"SHA512 الخاصة بالصور."
|
||||
|
||||
#: themes/default/templates/index.html.ep:181 themes/default/templates/index.html.ep:221
|
||||
msgid "Image URL"
|
||||
msgstr "رابط الصورة"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:153 themes/default/templates/raw.html.ep:2
|
||||
msgid "Image delay"
|
||||
msgstr "تأجيل الصورة"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:157
|
||||
msgid "Image deleted"
|
||||
msgstr "تم حذف الصورة"
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:756
|
||||
msgid "Image not found."
|
||||
msgstr "الصورة غير موجودة."
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:113
|
||||
msgid "Import localStorage data"
|
||||
msgstr "استيراد بيانات localStorage"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:69
|
||||
msgid "Informations"
|
||||
msgstr "معلومات"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:25
|
||||
msgid "Install webapp"
|
||||
msgstr "نتصيب برنامج الويب"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:21
|
||||
msgid "Instance's statistics"
|
||||
msgstr "إحصائيات مثيل الخادوم"
|
||||
|
||||
#: themes/default/templates/about.html.ep:11
|
||||
msgid "Is it really anonymous?"
|
||||
msgstr "هل يدعم حقا التخفي ؟"
|
||||
|
||||
#: themes/default/templates/about.html.ep:9
|
||||
msgid "Is it really free (as in free beer)?"
|
||||
msgstr "هل هو مجاني (كما في عبارة مشروب مجاني) ؟"
|
||||
|
||||
#: themes/default/templates/about.html.ep:21
|
||||
msgid "The way you pronounce the French word <a href=\"%1\">lutin</a> (/ly.tɛ̃/)."
|
||||
msgstr "يُنطق مِثل الكلمة الفرنسية <a href=\"%1\">lutin</a> (/ly.tɛ̃/)."
|
||||
|
||||
#: themes/default/templates/index.html.ep:171 themes/default/templates/index.html.ep:202
|
||||
msgid "Keep EXIF tags"
|
||||
msgstr "الإبقاء على بيانات EXIF"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:43
|
||||
msgid "Language"
|
||||
msgstr "اللغة"
|
||||
|
||||
#: themes/default/templates/index.html.ep:136 themes/default/templates/index.html.ep:184 themes/default/templates/index.html.ep:224 themes/default/templates/partial/lutim.js.ep:165
|
||||
msgid "Let's go!"
|
||||
msgstr "هيا بنا !"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:74
|
||||
msgid "License:"
|
||||
msgstr "الرخصة :"
|
||||
|
||||
#: themes/default/templates/index.html.ep:107 themes/default/templates/index.html.ep:109 themes/default/templates/partial/lutim.js.ep:112 themes/default/templates/partial/lutim.js.ep:116
|
||||
msgid "Link for share on social networks"
|
||||
msgstr "رابط للمشاركة على شبكات التواصل الإجتماعي"
|
||||
|
||||
#: themes/default/templates/login.html.ep:8
|
||||
msgid "Login"
|
||||
msgstr "تسجيل الدخول"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:33
|
||||
msgid "Logout"
|
||||
msgstr "الخروج"
|
||||
|
||||
#: themes/default/templates/zip.html.ep:7
|
||||
msgid ""
|
||||
"Lutim can't zip so many images at once, so it splitted your demand in "
|
||||
"multiple URLs."
|
||||
msgstr ""
|
||||
"ليس بمقدور لوتان ضغط العديد من الصور في آن واحد، لذا قام بتقطيع طلبك على شكل "
|
||||
"عدةروابط."
|
||||
|
||||
#: themes/default/templates/about.html.ep:4
|
||||
msgid ""
|
||||
"Lutim is a free (as in free beer) and anonymous image hosting service. It's "
|
||||
"also the name of the free (as in free speech) software which provides this "
|
||||
"service."
|
||||
msgstr ""
|
||||
"لوتيم أو Lutim خدمة مجانية ومجهولة لاستضافة الصور. ويعني كذلك اسم البرنامج "
|
||||
"الحر الموفّر لهذه الخدمة."
|
||||
|
||||
#: themes/default/templates/about.html.ep:25
|
||||
msgid "Main developers"
|
||||
msgstr "المطورون الرئيسيون"
|
||||
|
||||
#: themes/default/templates/index.html.ep:91 themes/default/templates/index.html.ep:93 themes/default/templates/partial/lutim.js.ep:86 themes/default/templates/partial/lutim.js.ep:89
|
||||
msgid "Markdown syntax"
|
||||
msgstr "صيغة ماركداون"
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:149
|
||||
msgid "Modify expiration delay"
|
||||
msgstr "تعديل تاريخ نهاية الصلاحية"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:7 themes/default/templates/partial/navbar.html.ep:18
|
||||
msgid "My images"
|
||||
msgstr "صوري"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:45
|
||||
msgid "Next (arrow right)"
|
||||
msgstr "التالي (السهم إلى اليمين)"
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:105 themes/default/templates/partial/myfiles.js.ep:132
|
||||
msgid "No limit"
|
||||
msgstr "غير محدود"
|
||||
|
||||
#: themes/default/templates/index.html.ep:183 themes/default/templates/index.html.ep:216
|
||||
msgid "Only images are allowed"
|
||||
msgstr "لا يُسمح إلّا بإستضافة الصور"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:11
|
||||
msgid ""
|
||||
"Only the images sent with this browser will be listed here. The details are "
|
||||
"stored in localStorage: if you delete your localStorage data, you'll loose "
|
||||
"these details."
|
||||
msgstr ""
|
||||
|
||||
#: themes/default/templates/about.html.ep:16
|
||||
msgid ""
|
||||
"Only the uploader! (well, only if he's the only owner of the images' rights "
|
||||
"before the upload)"
|
||||
msgstr ""
|
||||
|
||||
#: themes/default/templates/login.html.ep:12
|
||||
msgid "Password"
|
||||
msgstr "الكلمة السرية"
|
||||
|
||||
#: themes/default/templates/zip.html.ep:12
|
||||
msgid "Please click on each URL to download the different zip files."
|
||||
msgstr "يُرجى النقر على كل عنوان لتنزيل الملفات المضغوطة zip المختلفة."
|
||||
|
||||
#. (config('contact')
|
||||
#: themes/default/templates/about.html.ep:19
|
||||
msgid "Please contact the administrator: %1"
|
||||
msgstr "يرجى الإتصال بالمدير : %1"
|
||||
|
||||
#: lib/Lutim/Controller/Authent.pm:36
|
||||
msgid "Please, check your credentials: unable to authenticate."
|
||||
msgstr "يرجى التحقق مِن مُعرّفاتك : تعذر الإستيثاق."
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:43
|
||||
msgid "Previous (arrow left)"
|
||||
msgstr ""
|
||||
|
||||
#: themes/default/templates/index.html.ep:46 themes/default/templates/index.html.ep:49 themes/default/templates/myfiles.html.ep:96 themes/default/templates/myfiles.html.ep:99
|
||||
msgid "Random image link"
|
||||
msgstr "رابط إلى صورة عشوائية"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:22
|
||||
msgid "Raw stats"
|
||||
msgstr "الإحصائيات الخامة"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:52
|
||||
msgid "Save changes"
|
||||
msgstr "حفظ التعديلات"
|
||||
|
||||
#: themes/default/templates/index.html.ep:176
|
||||
msgid "Send an image"
|
||||
msgstr "إرسال صورة"
|
||||
|
||||
#: themes/default/templates/login.html.ep:16 themes/default/templates/logout.html.ep:5 themes/default/templates/partial/navbar.html.ep:37
|
||||
msgid "Signin"
|
||||
msgstr "انشاء حساب"
|
||||
|
||||
#: themes/default/templates/index.html.ep:151 themes/default/templates/partial/gallery.js.ep:211 themes/default/templates/partial/lutim.js.ep:176
|
||||
msgid "Something bad happened"
|
||||
msgstr "طرأ هناك خلل"
|
||||
|
||||
#. ($c->config('contact')
|
||||
#: lib/Lutim/Controller/Image.pm:763
|
||||
msgid ""
|
||||
"Something went wrong when creating the zip file. Try again later or contact "
|
||||
"the administrator (%1)."
|
||||
msgstr ""
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:55
|
||||
msgid "Support the author"
|
||||
msgstr "ساند مؤلف البرنامج"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:63
|
||||
msgid "Support the author on Liberapay"
|
||||
msgstr "ساند المؤلف عبر ليبراباي"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:60
|
||||
msgid "Support the author on Tipeee"
|
||||
msgstr "ساند المؤلف عبر تيبي"
|
||||
|
||||
#: themes/default/templates/about.html.ep:13
|
||||
msgid ""
|
||||
"The IP address of the image's sender is retained for a delay which depends "
|
||||
"of the administrator's choice (for the official instance, which is located "
|
||||
"in France, it's one year)."
|
||||
msgstr ""
|
||||
|
||||
#: themes/default/templates/about.html.ep:23
|
||||
msgid ""
|
||||
"The Lutim software is a <a href=\"http://en.wikipedia.org/wiki/"
|
||||
"Free_software\">free software</a>, which allows you to download and install "
|
||||
"it on you own server. Have a look at the <a href=\"https://www.gnu.org/"
|
||||
"licenses/agpl-3.0.html\">AGPL</a> to see what you can do."
|
||||
msgstr ""
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:347
|
||||
msgid "The URL is not valid."
|
||||
msgstr "الرابط غير صالح."
|
||||
|
||||
#: themes/default/templates/zip.html.ep:16
|
||||
msgid ""
|
||||
"The automatic download process will open a tab in your browser for each link."
|
||||
" You need to allow popups for Lutim."
|
||||
msgstr ""
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:32
|
||||
msgid "The data has been successfully imported."
|
||||
msgstr "تم استيراد البيانات بنجاح."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:160 lib/Lutim/Controller/Image.pm:228
|
||||
msgid "The delete token is invalid."
|
||||
msgstr "رمز الحذف غير صالح."
|
||||
|
||||
#. ($upload->filename)
|
||||
#: lib/Lutim/Controller/Image.pm:488
|
||||
msgid "The file %1 is not an image."
|
||||
msgstr "الملف %1 ليس صورة."
|
||||
|
||||
#. ($tx->res->max_message_size)
|
||||
#. ($c->req->max_message_size)
|
||||
#. (config('max_file_size')
|
||||
#: lib/Lutim/Controller/Image.pm:311 lib/Lutim/Controller/Image.pm:380 themes/default/templates/partial/lutim.js.ep:249
|
||||
msgid "The file exceed the size limit (%1)"
|
||||
msgstr "لقد تجاوز حجم الملف حد (%1)"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:12
|
||||
msgid "The graph's datas are not updated in real-time."
|
||||
msgstr "المنحنيات البيانية للبيانات لا يتم تحديثها آنيا."
|
||||
|
||||
#. ($image->filename)
|
||||
#: lib/Lutim/Controller/Image.pm:230
|
||||
msgid "The image %1 has already been deleted."
|
||||
msgstr "لقد تم حذف الصورة %1 مِن قَبل."
|
||||
|
||||
#. ($image->filename)
|
||||
#: lib/Lutim/Controller/Image.pm:239 lib/Lutim/Controller/Image.pm:244
|
||||
msgid "The image %1 has been successfully deleted"
|
||||
msgstr "تم حذف الصورة %1 بنجاح"
|
||||
|
||||
#: themes/default/templates/index.html.ep:63
|
||||
msgid "The images are encrypted on the server (Lutim does not keep the key)."
|
||||
msgstr ""
|
||||
"إنّ الصور مشفّرة ومُعمّاة على الخادم (لا يقوم Lutim بالإحتفاظ بالمفتاح)."
|
||||
|
||||
#: themes/default/templates/about.html.ep:5
|
||||
msgid ""
|
||||
"The images you post on Lutim can be stored indefinitely or be deleted at "
|
||||
"first view or after a delay selected from those proposed."
|
||||
msgstr ""
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:168
|
||||
msgid "The image’s delay has been successfully modified"
|
||||
msgstr "تم تعديل مهلة الصورة بنجاح"
|
||||
|
||||
#: themes/default/templates/partial/gallery.js.ep:277
|
||||
msgid "There is XXXX image(s) in the gallery"
|
||||
msgstr ""
|
||||
|
||||
#. ($c->config->{contact})
|
||||
#: lib/Lutim/Controller/Image.pm:485
|
||||
msgid "There is no more available URL. Retry or contact the administrator. %1"
|
||||
msgstr ""
|
||||
"لم يعد هناك أي رابط متوفّر. الرجاء إعادة المحاولة أو الإتصال بالمدير. %1"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:30
|
||||
msgid "Toggle fullscreen"
|
||||
msgstr "الإنتقال إلى وضع الشاشة الكامل"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:8
|
||||
msgid "Toggle navigation"
|
||||
msgstr "توسيع شريط التصفّح"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:162 themes/default/templates/raw.html.ep:11
|
||||
msgid "Total"
|
||||
msgstr "العدد الإجمالي"
|
||||
|
||||
#: themes/default/templates/index.html.ep:78 themes/default/templates/partial/lutim.js.ep:17
|
||||
msgid "Tweet it!"
|
||||
msgstr "غرِّد الرابط!"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:110 themes/default/templates/partial/common.js.ep:90
|
||||
msgid "Unable to copy to clipboard"
|
||||
msgstr "لم يمكن النسخ إلى الحافطة"
|
||||
|
||||
#. ($short)
|
||||
#: lib/Lutim/Controller/Image.pm:108 lib/Lutim/Controller/Image.pm:202 lib/Lutim/Controller/Image.pm:273
|
||||
msgid "Unable to find the image %1."
|
||||
msgstr "تعذر العثور على الصورة %1."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:574 lib/Lutim/Controller/Image.pm:619 lib/Lutim/Controller/Image.pm:660 lib/Lutim/Controller/Image.pm:703 lib/Lutim/Controller/Image.pm:715 lib/Lutim/Controller/Image.pm:726 lib/Lutim/Controller/Image.pm:753 lib/Lutim/Plugin/Helpers.pm:88
|
||||
msgid "Unable to find the image: it has been deleted."
|
||||
msgstr "تعذر العثور على الصورة : لقد تم حذفها."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:145
|
||||
msgid "Unable to get counter"
|
||||
msgstr "تعذر جلب التعداد"
|
||||
|
||||
#: themes/default/templates/about.html.ep:17
|
||||
msgid ""
|
||||
"Unlike many image sharing services, you don't give us rights on uploaded "
|
||||
"images."
|
||||
msgstr ""
|
||||
|
||||
#: themes/default/templates/index.html.ep:180 themes/default/templates/index.html.ep:219
|
||||
msgid "Upload an image with its URL"
|
||||
msgstr "القيام برفع صورة بواسطة رابطها"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:125
|
||||
msgid "Uploaded at"
|
||||
msgstr "تم رفعها على"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:6
|
||||
msgid "Uploaded files by days"
|
||||
msgstr "الملفات المستضافة بحسب الأيام"
|
||||
|
||||
#. ($c->app->config('contact')
|
||||
#: lib/Lutim/Plugin/Helpers.pm:221
|
||||
msgid ""
|
||||
"Uploading is currently disabled, please try later or contact the "
|
||||
"administrator (%1)."
|
||||
msgstr ""
|
||||
"لقد تمّ تعليق تحميل الصور حاليا ، يرجى إعادة المحاولة لاحقا أو الإتصال "
|
||||
"بالمدير (%1)."
|
||||
|
||||
#: themes/default/templates/index.html.ep:83 themes/default/templates/index.html.ep:85 themes/default/templates/myfiles.html.ep:122 themes/default/templates/partial/lutim.js.ep:72 themes/default/templates/partial/lutim.js.ep:76
|
||||
msgid "View link"
|
||||
msgstr "عرض الرابط"
|
||||
|
||||
#: themes/default/templates/about.html.ep:22
|
||||
msgid "What about the software which provides the service?"
|
||||
msgstr "عن البرنامج الذي يقدم هذه الخدمة؟"
|
||||
|
||||
#: themes/default/templates/about.html.ep:3
|
||||
msgid "What is Lutim?"
|
||||
msgstr "ماذا نقصد بـ Lutim؟"
|
||||
|
||||
#: themes/default/templates/about.html.ep:15
|
||||
msgid "Who owns rights on images uploaded on Lutim?"
|
||||
msgstr "من يمتلك حق ملكية الصور المستضافة على Lutim ؟"
|
||||
|
||||
#: themes/default/templates/about.html.ep:12
|
||||
msgid ""
|
||||
"Yes, it is! On the other side, for legal reasons, your IP address will be "
|
||||
"stored when you send an image. Don't panic, it is normally the case of all "
|
||||
"sites on which you send files!"
|
||||
msgstr ""
|
||||
|
||||
#: themes/default/templates/about.html.ep:10
|
||||
msgid ""
|
||||
"Yes, it is! On the other side, if you want to support the developer, you can "
|
||||
"do it via <a href=\"https://www.tipeee.com/fiat-tux\">Tipeee</a> or via <a "
|
||||
"href=\"https://liberapay.com/sky/\">Liberapay</a>."
|
||||
msgstr ""
|
||||
|
||||
#: themes/default/templates/zip.html.ep:6
|
||||
msgid "You asked to download a zip archive for too much files."
|
||||
msgstr ""
|
||||
|
||||
#: themes/default/templates/about.html.ep:8
|
||||
msgid ""
|
||||
"You can, optionally, request that the image(s) posted on Lutim to be deleted "
|
||||
"at first view (or download) or after the delay selected from those proposed."
|
||||
msgstr ""
|
||||
|
||||
#: lib/Lutim/Controller/Authent.pm:27
|
||||
msgid "You have been successfully logged in."
|
||||
msgstr "تم تسجيل دخولك بنجاح."
|
||||
|
||||
#: lib/Lutim/Controller/Authent.pm:66 themes/default/templates/logout.html.ep:3
|
||||
msgid "You have been successfully logged out."
|
||||
msgstr "تم تسجيل خروجك بنجاح."
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:31
|
||||
msgid "Zoom in/out"
|
||||
msgstr "تكبير/تصغير"
|
||||
|
||||
#: themes/default/templates/about.html.ep:27
|
||||
msgid "and on"
|
||||
msgstr "وكذا على"
|
||||
|
||||
#: themes/default/templates/about.html.ep:40
|
||||
msgid "arabic translation"
|
||||
msgstr "الترجمة إلى العربية"
|
||||
|
||||
#: themes/default/templates/about.html.ep:27
|
||||
msgid "core developer"
|
||||
msgstr "المطور الرئيسي"
|
||||
|
||||
#: lib/Lutim.pm:348 lib/Lutim/Command/cron/stats.pm:157 lib/Lutim/Command/cron/stats.pm:171 lib/Lutim/Command/cron/stats.pm:188 themes/default/templates/index.html.ep:3 themes/default/templates/myfiles.html.ep:3 themes/default/templates/partial/raw.js.ep:21 themes/default/templates/partial/raw.js.ep:4 themes/default/templates/raw.html.ep:6
|
||||
msgid "no time limit"
|
||||
msgstr "من دون قيد زمني"
|
||||
|
||||
#: themes/default/templates/about.html.ep:38
|
||||
msgid "occitan translation"
|
||||
msgstr "الترجمة إلى الأوكستانية"
|
||||
|
||||
#: themes/default/templates/about.html.ep:27
|
||||
msgid "on"
|
||||
msgstr "على"
|
||||
|
||||
#: themes/default/templates/about.html.ep:39
|
||||
msgid "paste image to upload ability"
|
||||
msgstr "إمكانية لصق الصورة قصد إرسالها"
|
||||
|
||||
#: themes/default/templates/about.html.ep:41
|
||||
msgid "russian translation"
|
||||
msgstr "الترجمة إلى الروسية"
|
||||
|
||||
#: themes/default/templates/about.html.ep:36
|
||||
msgid "spanish translation"
|
||||
msgstr "الترجمة إلى الإسبانية"
|
||||
|
||||
#: themes/default/templates/about.html.ep:28
|
||||
msgid "webapp developer"
|
||||
msgstr "مطور تطبيق الويب"
|
||||
707
themes/default/lib/Lutim/I18N/de.po
Normal file
707
themes/default/lib/Lutim/I18N/de.po
Normal file
@@ -0,0 +1,707 @@
|
||||
# Lutim language file
|
||||
# Copyright (C) 2014 Luc Didry
|
||||
# This file is distributed under the same license as the Lutim package.
|
||||
#
|
||||
# Translators:
|
||||
# Translators:
|
||||
# Thor77 <thor77@thor77.org>, 2015
|
||||
# Luc Didry <luc@framasoft.org>, 2018. #zanata
|
||||
# frju365 <abld@abld.info>, 2018. #zanata
|
||||
# Luc Didry <luc@framasoft.org>, 2019. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"PO-Revision-Date: 2022-12-24 17:38+0000\n"
|
||||
"Last-Translator: Vri <vrifox@vrifox.cc>\n"
|
||||
"Language-Team: German <https://weblate.framasoft.org/projects/lutim/"
|
||||
"default-theme/de/>\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.14.1\n"
|
||||
|
||||
#. (7)
|
||||
#. (30)
|
||||
#. ($delay)
|
||||
#. (config('max_delay')
|
||||
#: lib/Lutim/Command/cron/stats.pm:159 lib/Lutim/Command/cron/stats.pm:160 lib/Lutim/Command/cron/stats.pm:173 lib/Lutim/Command/cron/stats.pm:174 lib/Lutim/Command/cron/stats.pm:190 lib/Lutim/Command/cron/stats.pm:191 themes/default/templates/myfiles.html.ep:24 themes/default/templates/myfiles.html.ep:33 themes/default/templates/myfiles.html.ep:34 themes/default/templates/partial/for_my_delay.html.ep:13 themes/default/templates/partial/for_my_delay.html.ep:14 themes/default/templates/partial/for_my_delay.html.ep:4 themes/default/templates/partial/lutim.js.ep:140 themes/default/templates/partial/lutim.js.ep:149 themes/default/templates/partial/lutim.js.ep:150 themes/default/templates/partial/raw.js.ep:23 themes/default/templates/partial/raw.js.ep:24 themes/default/templates/partial/raw.js.ep:6 themes/default/templates/partial/raw.js.ep:7 themes/default/templates/raw.html.ep:8 themes/default/templates/raw.html.ep:9
|
||||
msgid "%1 days"
|
||||
msgstr "%1 Tage"
|
||||
|
||||
#. ($total)
|
||||
#: themes/default/templates/stats.html.ep:2
|
||||
msgid "%1 sent images on this instance from beginning."
|
||||
msgstr "%1 Bilder wurden bisher über diese Instanz versendet."
|
||||
|
||||
#: themes/default/templates/index.html.ep:208
|
||||
msgid "-or-"
|
||||
msgstr "-oder-"
|
||||
|
||||
#: lib/Lutim.pm:350 lib/Lutim/Command/cron/stats.pm:161 lib/Lutim/Command/cron/stats.pm:175 lib/Lutim/Command/cron/stats.pm:192 themes/default/templates/index.html.ep:5 themes/default/templates/myfiles.html.ep:5 themes/default/templates/partial/raw.js.ep:25 themes/default/templates/partial/raw.js.ep:8 themes/default/templates/raw.html.ep:10
|
||||
msgid "1 year"
|
||||
msgstr "1 Jahr"
|
||||
|
||||
#: lib/Lutim.pm:349 lib/Lutim/Command/cron/stats.pm:158 lib/Lutim/Command/cron/stats.pm:172 lib/Lutim/Command/cron/stats.pm:189 themes/default/templates/index.html.ep:4 themes/default/templates/myfiles.html.ep:33 themes/default/templates/myfiles.html.ep:4 themes/default/templates/partial/for_my_delay.html.ep:13 themes/default/templates/partial/lutim.js.ep:149 themes/default/templates/partial/raw.js.ep:22 themes/default/templates/partial/raw.js.ep:5 themes/default/templates/raw.html.ep:7
|
||||
msgid "24 hours"
|
||||
msgstr "24 Stunden"
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:180
|
||||
msgid ": Error while trying to get the counter."
|
||||
msgstr ":Fehler beim Abrufen des Zählers."
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:77
|
||||
msgid "About"
|
||||
msgstr "Über"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:154 themes/default/templates/raw.html.ep:3
|
||||
msgid "Active images"
|
||||
msgstr "Aktive Bilder"
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:328
|
||||
msgid "An error occured while downloading the image."
|
||||
msgstr "Beim Herunterladen des Bildes ist ein Fehler aufgetreten."
|
||||
|
||||
#: themes/default/templates/zip.html.ep:2
|
||||
msgid "Archives download"
|
||||
msgstr "Herunterladen von Archiv"
|
||||
|
||||
#: themes/default/templates/about.html.ep:44 themes/default/templates/myfiles.html.ep:135 themes/default/templates/stats.html.ep:25
|
||||
msgid "Back to homepage"
|
||||
msgstr "Zurück zur Hauptseite"
|
||||
|
||||
#: themes/default/templates/index.html.ep:211 themes/default/templates/index.html.ep:212
|
||||
msgid "Click to open the file browser"
|
||||
msgstr "Klicken um den Dateibrowser zu öffnen"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:51
|
||||
msgid "Close"
|
||||
msgstr "Schließen"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:29
|
||||
msgid "Close (Esc)"
|
||||
msgstr "Schließen (Esc)"
|
||||
|
||||
#: themes/default/templates/about.html.ep:30
|
||||
msgid "Contributors"
|
||||
msgstr "Mitwirkende"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:113 themes/default/templates/partial/common.js.ep:93
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "in die Zwischenablage kopiert"
|
||||
|
||||
#: themes/default/templates/partial/lutim.js.ep:215 themes/default/templates/partial/lutim.js.ep:278 themes/default/templates/partial/lutim.js.ep:364
|
||||
msgid "Copy all view links to clipboard"
|
||||
msgstr "Alle Links zum Anschauen in die Zwischenablage kopieren"
|
||||
|
||||
#: themes/default/templates/index.html.ep:103 themes/default/templates/index.html.ep:111 themes/default/templates/index.html.ep:18 themes/default/templates/index.html.ep:36 themes/default/templates/index.html.ep:54 themes/default/templates/index.html.ep:87 themes/default/templates/index.html.ep:95 themes/default/templates/myfiles.html.ep:104 themes/default/templates/myfiles.html.ep:68 themes/default/templates/myfiles.html.ep:86 themes/default/templates/partial/common.js.ep:186 themes/default/templates/partial/lutim.js.ep:106 themes/default/templates/partial/lutim.js.ep:121 themes/default/templates/partial/lutim.js.ep:80 themes/default/templates/partial/lutim.js.ep:92 themes/default/templates/partial/myfiles.js.ep:142
|
||||
msgid "Copy to clipboard"
|
||||
msgstr "In die Zwischenablage kopieren"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:123
|
||||
msgid "Counter"
|
||||
msgstr "Zähler"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:18
|
||||
msgid "Delay repartition chart for disabled images"
|
||||
msgstr "Diagramm zur Neuaufteilung der Verzögerung für deaktivierte Bilder"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:15
|
||||
msgid "Delay repartition chart for enabled images"
|
||||
msgstr "Diagramm zur Neuaufteilung der Verzögerung für deaktivierte Bilder"
|
||||
|
||||
#: themes/default/templates/index.html.ep:133 themes/default/templates/index.html.ep:165 themes/default/templates/index.html.ep:196 themes/default/templates/myfiles.html.ep:124 themes/default/templates/myfiles.html.ep:45 themes/default/templates/partial/lutim.js.ep:161
|
||||
msgid "Delete at first view?"
|
||||
msgstr "Nach erstem Aufruf löschen?"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:155 themes/default/templates/raw.html.ep:4
|
||||
msgid "Deleted images"
|
||||
msgstr "Gelöschte Bilder"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:156 themes/default/templates/raw.html.ep:5
|
||||
msgid "Deleted images in 30 days"
|
||||
msgstr "Gelöschte Bilder in 30 Tagen"
|
||||
|
||||
#: themes/default/templates/index.html.ep:116 themes/default/templates/myfiles.html.ep:127 themes/default/templates/partial/common.js.ep:178 themes/default/templates/partial/common.js.ep:181
|
||||
msgid "Deletion link"
|
||||
msgstr "Link zum Löschen"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:10
|
||||
msgid "Download all images"
|
||||
msgstr "Laden Sie alle Bilder"
|
||||
|
||||
#: themes/default/templates/index.html.ep:101 themes/default/templates/index.html.ep:99 themes/default/templates/partial/lutim.js.ep:102 themes/default/templates/partial/lutim.js.ep:98
|
||||
msgid "Download link"
|
||||
msgstr "Link zum Herunterladen"
|
||||
|
||||
#: themes/default/templates/index.html.ep:28 themes/default/templates/index.html.ep:31 themes/default/templates/myfiles.html.ep:78 themes/default/templates/myfiles.html.ep:81
|
||||
msgid "Download zip link"
|
||||
msgstr "Link zum Archivbilder"
|
||||
|
||||
#: themes/default/templates/index.html.ep:207
|
||||
msgid "Drag & drop images here"
|
||||
msgstr "Bilder hierher ziehen"
|
||||
|
||||
#: themes/default/templates/about.html.ep:7
|
||||
msgid ""
|
||||
"Drag and drop an image in the appropriate area or use the traditional way to "
|
||||
"send files and Lutim will provide you four URLs. One to view the image, an "
|
||||
"other to directly download it, one you can use on social networks and a last "
|
||||
"to delete the image when you want."
|
||||
msgstr ""
|
||||
"Ziehe Bilder in den dafür vorgesehenen Bereich und Lutim wird vier URLs "
|
||||
"generieren. Eine zum Anschauen, eine zum direkten Herunterladen, eine zum "
|
||||
"Nutzen in sozialen Netzwerken und eine letzte um das Bild zu löschen."
|
||||
|
||||
#: themes/default/templates/index.html.ep:168 themes/default/templates/index.html.ep:199
|
||||
msgid "Encrypt the image (Lutim does not keep the key)."
|
||||
msgstr "Verschlüssle das Bild (Lutim behält den Key nicht)."
|
||||
|
||||
#: themes/default/templates/partial/lutim.js.ep:45 themes/default/templates/partial/myfiles.js.ep:113
|
||||
msgid "Error while trying to modify the image."
|
||||
msgstr "Beim Bearbeiten des Bildes ist ein Fehler aufgetreten."
|
||||
|
||||
#: themes/default/templates/stats.html.ep:10
|
||||
msgid "Evolution of total files"
|
||||
msgstr "Entwicklung der Anzahl an Dateien"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:126
|
||||
msgid "Expires at"
|
||||
msgstr "Läuft ab am"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:112
|
||||
msgid "Export localStorage data"
|
||||
msgstr "localStorage-Daten exportieren"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:121
|
||||
msgid "File name"
|
||||
msgstr "Dateiname"
|
||||
|
||||
#: themes/default/templates/about.html.ep:24
|
||||
msgid ""
|
||||
"For more details, see the <a href=\"https://framagit.org/luc/"
|
||||
"lutim\">homepage of the project</a>."
|
||||
msgstr ""
|
||||
"Besuche für mehr Details die <a href=\"https://framagit.org/luc/"
|
||||
"lutim\">Homepage des Projekts</a>."
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:80
|
||||
msgid "Fork me!"
|
||||
msgstr "Fork mich!"
|
||||
|
||||
#: themes/default/templates/index.html.ep:10 themes/default/templates/index.html.ep:13 themes/default/templates/myfiles.html.ep:60 themes/default/templates/myfiles.html.ep:63
|
||||
msgid "Gallery link"
|
||||
msgstr "Link zur Galerie"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:116 themes/default/templates/partial/common.js.ep:134
|
||||
msgid "Hit Ctrl+C, then Enter to copy the short link"
|
||||
msgstr "Drücke STRG+C und dann Enter um den Kurz-Link zu kopieren"
|
||||
|
||||
#: themes/default/templates/layouts/default.html.ep:47
|
||||
msgid "Homepage"
|
||||
msgstr "Webseite"
|
||||
|
||||
#: themes/default/templates/about.html.ep:20
|
||||
msgid "How do you pronounce Lutim?"
|
||||
msgstr "Wie spricht man Lutim aus?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:6
|
||||
msgid "How does it work?"
|
||||
msgstr "Wie funktionert das?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:18
|
||||
msgid "How to report an image?"
|
||||
msgstr "Wie kann ich ein Bild melden?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:14
|
||||
msgid ""
|
||||
"If the files are deleted if you ask it while posting it, their SHA512 "
|
||||
"footprint are retained."
|
||||
msgstr ""
|
||||
"Wenn du versuchst, ein Bild während dem Hochladen zu löschen, wird die "
|
||||
"SHA512-Summe des Bildes behalten."
|
||||
|
||||
#: themes/default/templates/index.html.ep:181 themes/default/templates/index.html.ep:221
|
||||
msgid "Image URL"
|
||||
msgstr "Bild-URL"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:153 themes/default/templates/raw.html.ep:2
|
||||
msgid "Image delay"
|
||||
msgstr "Bild-Verzögerung"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:157
|
||||
msgid "Image deleted"
|
||||
msgstr "Bild gelöscht"
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:756
|
||||
msgid "Image not found."
|
||||
msgstr "Bild nicht gefunden."
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:113
|
||||
msgid "Import localStorage data"
|
||||
msgstr "localStorage-Daten importieren"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:69
|
||||
msgid "Informations"
|
||||
msgstr "Informationen"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:25
|
||||
msgid "Install webapp"
|
||||
msgstr "Installiere die Webapp"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:21
|
||||
msgid "Instance's statistics"
|
||||
msgstr "Statistik der Instanz"
|
||||
|
||||
#: themes/default/templates/about.html.ep:11
|
||||
msgid "Is it really anonymous?"
|
||||
msgstr "Ist es wirklich anonym?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:9
|
||||
msgid "Is it really free (as in free beer)?"
|
||||
msgstr "Is es wirklich kostenlos?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:21
|
||||
msgid "The way you pronounce the French word <a href=\"%1\">lutin</a> (/ly.tɛ̃/)."
|
||||
msgstr "Genauso wie das französische Wort <a href=\"%1\">lutin</a> (/ly.tɛ̃/)."
|
||||
|
||||
#: themes/default/templates/index.html.ep:171 themes/default/templates/index.html.ep:202
|
||||
msgid "Keep EXIF tags"
|
||||
msgstr "Behalte EXIF-Daten"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:43
|
||||
msgid "Language"
|
||||
msgstr "Sprache"
|
||||
|
||||
#: themes/default/templates/index.html.ep:136 themes/default/templates/index.html.ep:184 themes/default/templates/index.html.ep:224 themes/default/templates/partial/lutim.js.ep:165
|
||||
msgid "Let's go!"
|
||||
msgstr "Los gehts!"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:74
|
||||
msgid "License:"
|
||||
msgstr "Lizenz:"
|
||||
|
||||
#: themes/default/templates/index.html.ep:107 themes/default/templates/index.html.ep:109 themes/default/templates/partial/lutim.js.ep:112 themes/default/templates/partial/lutim.js.ep:116
|
||||
msgid "Link for share on social networks"
|
||||
msgstr "Links zum teilen auf sozialen Netzwerken"
|
||||
|
||||
#: themes/default/templates/login.html.ep:8
|
||||
msgid "Login"
|
||||
msgstr "Anmelden"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:33
|
||||
msgid "Logout"
|
||||
msgstr "Abmelden"
|
||||
|
||||
#: themes/default/templates/zip.html.ep:7
|
||||
msgid ""
|
||||
"Lutim can't zip so many images at once, so it splitted your demand in "
|
||||
"multiple URLs."
|
||||
msgstr ""
|
||||
"Lutim kann nicht so viele Bilder auf einmal zippen, daher hat es Ihren "
|
||||
"Bedarf in mehrere URLs aufgeteilt."
|
||||
|
||||
#: themes/default/templates/about.html.ep:4
|
||||
msgid ""
|
||||
"Lutim is a free (as in free beer) and anonymous image hosting service. It's "
|
||||
"also the name of the free (as in free speech) software which provides this "
|
||||
"service."
|
||||
msgstr ""
|
||||
"Lutim ist ein freier und anonymer Bilder-Upload-Service. Es ist auch der "
|
||||
"Name der freien Software, die diesen Service bietet."
|
||||
|
||||
#: themes/default/templates/about.html.ep:25
|
||||
msgid "Main developers"
|
||||
msgstr "Haupt-Entwickler"
|
||||
|
||||
#: themes/default/templates/index.html.ep:91 themes/default/templates/index.html.ep:93 themes/default/templates/partial/lutim.js.ep:86 themes/default/templates/partial/lutim.js.ep:89
|
||||
msgid "Markdown syntax"
|
||||
msgstr "Markdown Syntax"
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:149
|
||||
msgid "Modify expiration delay"
|
||||
msgstr "Ablaufverzögerung verändern"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:7 themes/default/templates/partial/navbar.html.ep:18
|
||||
msgid "My images"
|
||||
msgstr "Meine Bilder"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:45
|
||||
msgid "Next (arrow right)"
|
||||
msgstr "Nächstes (rechte Pfeiltaste)"
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:105 themes/default/templates/partial/myfiles.js.ep:132
|
||||
msgid "No limit"
|
||||
msgstr "Keine Begrenzung"
|
||||
|
||||
#: themes/default/templates/index.html.ep:183 themes/default/templates/index.html.ep:216
|
||||
msgid "Only images are allowed"
|
||||
msgstr "Es sind nur Bilder erlaubt"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:11
|
||||
msgid ""
|
||||
"Only the images sent with this browser will be listed here. The details are "
|
||||
"stored in localStorage: if you delete your localStorage data, you'll loose "
|
||||
"these details."
|
||||
msgstr ""
|
||||
"Nur die Bilder, die über diesen Browser versendet wurden, werden hier "
|
||||
"angezeigt. Die Details werden im localStorage gespeichert, wenn du diesen "
|
||||
"leerst, sind die Details verloren."
|
||||
|
||||
#: themes/default/templates/about.html.ep:16
|
||||
msgid ""
|
||||
"Only the uploader! (well, only if he's the only owner of the images' rights "
|
||||
"before the upload)"
|
||||
msgstr ""
|
||||
"Nur der Hochladende (natürlich nur, wenn er vorher auch Rechteinhaber des "
|
||||
"Bildes war)"
|
||||
|
||||
#: themes/default/templates/login.html.ep:12
|
||||
msgid "Password"
|
||||
msgstr "Passwort"
|
||||
|
||||
#: themes/default/templates/zip.html.ep:12
|
||||
msgid "Please click on each URL to download the different zip files."
|
||||
msgstr ""
|
||||
"Bitte klicken Sie auf die jeweilige URL, um die verschiedenen Zip-Dateien "
|
||||
"herunterzuladen."
|
||||
|
||||
#. (config('contact')
|
||||
#: themes/default/templates/about.html.ep:19
|
||||
msgid "Please contact the administrator: %1"
|
||||
msgstr "Kontaktiere den Administrator: %1"
|
||||
|
||||
#: lib/Lutim/Controller/Authent.pm:36
|
||||
msgid "Please, check your credentials: unable to authenticate."
|
||||
msgstr ""
|
||||
"Bitte überprüfen Sie Ihre Anmeldeinformationen: Die Anmeldung kann nicht "
|
||||
"erfolgen."
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:43
|
||||
msgid "Previous (arrow left)"
|
||||
msgstr "Vorheriges (linke Pfeiltaste)"
|
||||
|
||||
#: themes/default/templates/index.html.ep:46 themes/default/templates/index.html.ep:49 themes/default/templates/myfiles.html.ep:96 themes/default/templates/myfiles.html.ep:99
|
||||
msgid "Random image link"
|
||||
msgstr "Zufälliger Bildlink"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:22
|
||||
msgid "Raw stats"
|
||||
msgstr "Rohe Statistiken"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:52
|
||||
msgid "Save changes"
|
||||
msgstr "Änderungen speichern"
|
||||
|
||||
#: themes/default/templates/index.html.ep:176
|
||||
msgid "Send an image"
|
||||
msgstr "Sende ein Bild"
|
||||
|
||||
#: themes/default/templates/login.html.ep:16 themes/default/templates/logout.html.ep:5 themes/default/templates/partial/navbar.html.ep:37
|
||||
msgid "Signin"
|
||||
msgstr "Anmelden"
|
||||
|
||||
#: themes/default/templates/index.html.ep:151 themes/default/templates/partial/gallery.js.ep:211 themes/default/templates/partial/lutim.js.ep:176
|
||||
msgid "Something bad happened"
|
||||
msgstr "Es ist ein Fehler aufgetreten"
|
||||
|
||||
#. ($c->config('contact')
|
||||
#: lib/Lutim/Controller/Image.pm:763
|
||||
msgid ""
|
||||
"Something went wrong when creating the zip file. Try again later or contact "
|
||||
"the administrator (%1)."
|
||||
msgstr ""
|
||||
"Es ist ein Fehler aufgetreten. Versuche es erneut oder kontaktiere den "
|
||||
"Administrator (%1)."
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:55
|
||||
msgid "Support the author"
|
||||
msgstr "Unterstützen Sie den Autor"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:63
|
||||
msgid "Support the author on Liberapay"
|
||||
msgstr "Unterstützen den Autor bei Liberapay"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:60
|
||||
msgid "Support the author on Tipeee"
|
||||
msgstr "Unterstützen den Autor bei Tipeee"
|
||||
|
||||
#: themes/default/templates/about.html.ep:13
|
||||
msgid ""
|
||||
"The IP address of the image's sender is retained for a delay which depends "
|
||||
"of the administrator's choice (for the official instance, which is located "
|
||||
"in France, it's one year)."
|
||||
msgstr ""
|
||||
"Die IP-Adresse des Nutzers wird für eine bestimmte Zeit gespeichert. Diese "
|
||||
"kann der Administrator frei wählen (für die offizielle Instanz, die in "
|
||||
"Frankreich gehostet ist, liegt diese Zeit bei einem Jahr)."
|
||||
|
||||
#: themes/default/templates/about.html.ep:23
|
||||
msgid ""
|
||||
"The Lutim software is a <a href=\"http://en.wikipedia.org/wiki/"
|
||||
"Free_software\">free software</a>, which allows you to download and install "
|
||||
"it on you own server. Have a look at the <a href=\"https://www.gnu.org/"
|
||||
"licenses/agpl-3.0.html\">AGPL</a> to see what you can do."
|
||||
msgstr ""
|
||||
"Lutim ist <a href=\"https://de.wikipedia.org/wiki/Freie_Software\">freie "
|
||||
"Software</a>, was dir erlaubt sie herunterzuladen und sie auf deinem eigenem "
|
||||
"Server zu installieren. Schau dir die <a href=\"https://www.gnu.org/licenses/"
|
||||
"agpl-3.0.html\">AGPL</a> an um deine Recht zu sehen."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:347
|
||||
msgid "The URL is not valid."
|
||||
msgstr "Die URL ist nicht gültig."
|
||||
|
||||
#: themes/default/templates/zip.html.ep:16
|
||||
msgid ""
|
||||
"The automatic download process will open a tab in your browser for each link."
|
||||
" You need to allow popups for Lutim."
|
||||
msgstr ""
|
||||
"Das automatische Herunterladen wird für jeden Link einen neuen Tab in deinem "
|
||||
"Browser öffnen. Du musst Pop-Ups für Lutim erlauben."
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:32
|
||||
msgid "The data has been successfully imported."
|
||||
msgstr "Die Daten wurden erfolgreich importiert."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:160 lib/Lutim/Controller/Image.pm:228
|
||||
msgid "The delete token is invalid."
|
||||
msgstr "Das Token zum Löschen ist ungültig."
|
||||
|
||||
#. ($upload->filename)
|
||||
#: lib/Lutim/Controller/Image.pm:488
|
||||
msgid "The file %1 is not an image."
|
||||
msgstr "Die Datei %1 ist kein Bild."
|
||||
|
||||
#. ($tx->res->max_message_size)
|
||||
#. ($c->req->max_message_size)
|
||||
#. (config('max_file_size')
|
||||
#: lib/Lutim/Controller/Image.pm:311 lib/Lutim/Controller/Image.pm:380 themes/default/templates/partial/lutim.js.ep:249
|
||||
msgid "The file exceed the size limit (%1)"
|
||||
msgstr "Die Datei überschreitet die Größenbeschränkung (%1)"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:12
|
||||
msgid "The graph's datas are not updated in real-time."
|
||||
msgstr "Die Daten des Graphs werden nicht in Echtzeit aktualisiert."
|
||||
|
||||
#. ($image->filename)
|
||||
#: lib/Lutim/Controller/Image.pm:230
|
||||
msgid "The image %1 has already been deleted."
|
||||
msgstr "Das Bild %1 wurde schon gelöscht."
|
||||
|
||||
#. ($image->filename)
|
||||
#: lib/Lutim/Controller/Image.pm:239 lib/Lutim/Controller/Image.pm:244
|
||||
msgid "The image %1 has been successfully deleted"
|
||||
msgstr "Das Bild %1 wurde erfolgreich gelöscht"
|
||||
|
||||
#: themes/default/templates/index.html.ep:63
|
||||
msgid "The images are encrypted on the server (Lutim does not keep the key)."
|
||||
msgstr ""
|
||||
"Die Bilder werden auf dem Server verschlüsselt (Lutim behält den Key nicht)."
|
||||
|
||||
#: themes/default/templates/about.html.ep:5
|
||||
msgid ""
|
||||
"The images you post on Lutim can be stored indefinitely or be deleted at "
|
||||
"first view or after a delay selected from those proposed."
|
||||
msgstr ""
|
||||
"Die Bilder, die du auf Lutim hochlädst, können entweder nie, nach dem ersten "
|
||||
"Aufruf oder nach einer bestimmten Zeit gelöscht werden."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:168
|
||||
msgid "The image’s delay has been successfully modified"
|
||||
msgstr "Die Zeit bis zum Löschen des Bildes wurde erfolgreich geändert"
|
||||
|
||||
#: themes/default/templates/partial/gallery.js.ep:277
|
||||
msgid "There is XXXX image(s) in the gallery"
|
||||
msgstr "Es gibt XXXX Bild(er) in der Galerie"
|
||||
|
||||
#. ($c->config->{contact})
|
||||
#: lib/Lutim/Controller/Image.pm:485
|
||||
msgid "There is no more available URL. Retry or contact the administrator. %1"
|
||||
msgstr ""
|
||||
"Es sind keine URLs mehr verfügbar. Versuche es erneut oder kontaktiere den "
|
||||
"Administrator. %1"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:30
|
||||
msgid "Toggle fullscreen"
|
||||
msgstr "Vollbild umschalten"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:8
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Navigation umschalten"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:162 themes/default/templates/raw.html.ep:11
|
||||
msgid "Total"
|
||||
msgstr "Gesamtzahl"
|
||||
|
||||
#: themes/default/templates/index.html.ep:78 themes/default/templates/partial/lutim.js.ep:17
|
||||
msgid "Tweet it!"
|
||||
msgstr "Twittere es!"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:110 themes/default/templates/partial/common.js.ep:90
|
||||
msgid "Unable to copy to clipboard"
|
||||
msgstr "Kopieren in die Zwischenablage nicht möglich"
|
||||
|
||||
#. ($short)
|
||||
#: lib/Lutim/Controller/Image.pm:108 lib/Lutim/Controller/Image.pm:202 lib/Lutim/Controller/Image.pm:273
|
||||
msgid "Unable to find the image %1."
|
||||
msgstr "Konnte das Bild %1 nicht finden."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:574 lib/Lutim/Controller/Image.pm:619 lib/Lutim/Controller/Image.pm:660 lib/Lutim/Controller/Image.pm:703 lib/Lutim/Controller/Image.pm:715 lib/Lutim/Controller/Image.pm:726 lib/Lutim/Controller/Image.pm:753 lib/Lutim/Plugin/Helpers.pm:88
|
||||
msgid "Unable to find the image: it has been deleted."
|
||||
msgstr "Dieses Bild wurde gelöscht."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:145
|
||||
msgid "Unable to get counter"
|
||||
msgstr "Konnte den Zähler nicht abrufen"
|
||||
|
||||
#: themes/default/templates/about.html.ep:17
|
||||
msgid ""
|
||||
"Unlike many image sharing services, you don't give us rights on uploaded "
|
||||
"images."
|
||||
msgstr ""
|
||||
"Im Gegensatz zu anderen Bild-Hosting-Diensten, überträgst du uns nicht die "
|
||||
"Rechte an hochgeladenen Bildern."
|
||||
|
||||
#: themes/default/templates/index.html.ep:180 themes/default/templates/index.html.ep:219
|
||||
msgid "Upload an image with its URL"
|
||||
msgstr "Lade ein Bild über seine URL hoch"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:125
|
||||
msgid "Uploaded at"
|
||||
msgstr "Hochgeladen am"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:6
|
||||
msgid "Uploaded files by days"
|
||||
msgstr "Hochgeladene Bilder pro Tag"
|
||||
|
||||
#. ($c->app->config('contact')
|
||||
#: lib/Lutim/Plugin/Helpers.pm:221
|
||||
msgid ""
|
||||
"Uploading is currently disabled, please try later or contact the "
|
||||
"administrator (%1)."
|
||||
msgstr ""
|
||||
"Hochladen ist momentan deaktiviert. Versuche es später erneut oder "
|
||||
"kontaktiere den Administrator (%1)."
|
||||
|
||||
#: themes/default/templates/index.html.ep:83 themes/default/templates/index.html.ep:85 themes/default/templates/myfiles.html.ep:122 themes/default/templates/partial/lutim.js.ep:72 themes/default/templates/partial/lutim.js.ep:76
|
||||
msgid "View link"
|
||||
msgstr "Link ansehen"
|
||||
|
||||
#: themes/default/templates/about.html.ep:22
|
||||
msgid "What about the software which provides the service?"
|
||||
msgstr "Welche Software stellt diesen Dienst bereit?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:3
|
||||
msgid "What is Lutim?"
|
||||
msgstr "Was ist Lutim?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:15
|
||||
msgid "Who owns rights on images uploaded on Lutim?"
|
||||
msgstr "Wer hat die Rechte an auf Lutim hochgeladenen Bildern?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:12
|
||||
msgid ""
|
||||
"Yes, it is! On the other side, for legal reasons, your IP address will be "
|
||||
"stored when you send an image. Don't panic, it is normally the case of all "
|
||||
"sites on which you send files!"
|
||||
msgstr ""
|
||||
"Ja, ist es! Auf der anderen Seite wird deine IP-Adresse, wegen rechtlichen "
|
||||
"Gründen, beim hochladen gespeichert. Keine Panik, das ist normalerweise der "
|
||||
"Fall für alle Seiten, an die du Dateien sendest!"
|
||||
|
||||
#: themes/default/templates/about.html.ep:10
|
||||
msgid ""
|
||||
"Yes, it is! On the other side, if you want to support the developer, you can "
|
||||
"do it via <a href=\"https://www.tipeee.com/fiat-tux\">Tipeee</a> or via <a "
|
||||
"href=\"https://liberapay.com/sky/\">Liberapay</a>."
|
||||
msgstr ""
|
||||
"Ja, ist es! Jedoch kannst du den Entwickler via <a href=\"https://www.tipeee."
|
||||
"com/fiat-tux\">Tipeee</a> oder <a href=\"https://liberapay.com/sky/\""
|
||||
">Liberapay</a> unterstützen."
|
||||
|
||||
#: themes/default/templates/zip.html.ep:6
|
||||
msgid "You asked to download a zip archive for too much files."
|
||||
msgstr ""
|
||||
"Du hast darum gebeten, ein Zip-Archiv für zu viele Dateien herunterzuladen."
|
||||
|
||||
#: themes/default/templates/about.html.ep:8
|
||||
msgid ""
|
||||
"You can, optionally, request that the image(s) posted on Lutim to be deleted "
|
||||
"at first view (or download) or after the delay selected from those proposed."
|
||||
msgstr ""
|
||||
"Du kannst Bilder, die du auf Lutim hochlädst, entweder nach dem ernsten "
|
||||
"Ansehen (oder Herunterladen) oder nach einem der vorgeschlagenen Zeiten "
|
||||
"löschen lassen."
|
||||
|
||||
#: lib/Lutim/Controller/Authent.pm:27
|
||||
msgid "You have been successfully logged in."
|
||||
msgstr "Sie wurden erfolgreich angemeldet."
|
||||
|
||||
#: lib/Lutim/Controller/Authent.pm:66 themes/default/templates/logout.html.ep:3
|
||||
msgid "You have been successfully logged out."
|
||||
msgstr "Sie wurden erfolgreich abgemeldet."
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:31
|
||||
msgid "Zoom in/out"
|
||||
msgstr "Vergrößern/verkleinern"
|
||||
|
||||
#: themes/default/templates/about.html.ep:27
|
||||
msgid "and on"
|
||||
msgstr "und auf"
|
||||
|
||||
#: themes/default/templates/about.html.ep:40
|
||||
msgid "arabic translation"
|
||||
msgstr "Arabische Übersetzung"
|
||||
|
||||
#: themes/default/templates/about.html.ep:27
|
||||
msgid "core developer"
|
||||
msgstr "Haupt-Entwickler"
|
||||
|
||||
#: lib/Lutim.pm:348 lib/Lutim/Command/cron/stats.pm:157 lib/Lutim/Command/cron/stats.pm:171 lib/Lutim/Command/cron/stats.pm:188 themes/default/templates/index.html.ep:3 themes/default/templates/myfiles.html.ep:3 themes/default/templates/partial/raw.js.ep:21 themes/default/templates/partial/raw.js.ep:4 themes/default/templates/raw.html.ep:6
|
||||
msgid "no time limit"
|
||||
msgstr "keine Zeit-Begrenzung"
|
||||
|
||||
#: themes/default/templates/about.html.ep:38
|
||||
msgid "occitan translation"
|
||||
msgstr "okzitanisch Übersetzung"
|
||||
|
||||
#: themes/default/templates/about.html.ep:27
|
||||
msgid "on"
|
||||
msgstr "auf"
|
||||
|
||||
#: themes/default/templates/about.html.ep:39
|
||||
msgid "paste image to upload ability"
|
||||
msgstr "Bild zum Hochladen einfügen-Funktion"
|
||||
|
||||
#: themes/default/templates/about.html.ep:41
|
||||
msgid "russian translation"
|
||||
msgstr "russische Übersetzung"
|
||||
|
||||
#: themes/default/templates/about.html.ep:36
|
||||
msgid "spanish translation"
|
||||
msgstr "spanische Übersetzung"
|
||||
|
||||
#: themes/default/templates/about.html.ep:28
|
||||
msgid "webapp developer"
|
||||
msgstr "Webapp-Entwickler"
|
||||
|
||||
#: themes/default/templates/about.html.ep:24
|
||||
msgid "For more details, see the <a href=\"https://framagit.org/fiat-tux/hat-softwares/lutim\">homepage of the project</a>."
|
||||
msgstr ""
|
||||
"Für weitere Details besuche die <a href=\"https://framagit.org/fiat-tux/"
|
||||
"hat-softwares/lutim\">Website des Projekts</a>."
|
||||
|
||||
#: themes/default/templates/index.html.ep:174 themes/default/templates/index.html.ep:218
|
||||
msgid "Single watermark"
|
||||
msgstr "Einzelnes Wasserzeichen"
|
||||
|
||||
#: themes/default/templates/index.html.ep:171 themes/default/templates/index.html.ep:215
|
||||
msgid "Tiling watermark"
|
||||
msgstr "Gekacheltes Wasserzeichen"
|
||||
|
||||
#: themes/default/templates/index.html.ep:177 themes/default/templates/index.html.ep:221
|
||||
msgid "No watermark"
|
||||
msgstr "Kein Wasserzeichen"
|
||||
600
themes/default/lib/Lutim/I18N/en.po
Normal file
600
themes/default/lib/Lutim/I18N/en.po
Normal file
@@ -0,0 +1,600 @@
|
||||
# Luc Didry <luc@framasoft.org>, 2018. #zanata
|
||||
# Luc Didry <luc@framasoft.org>, 2019. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2019-05-13 08:43+0000\n"
|
||||
"Last-Translator: Luc Didry <luc@framasoft.org>\n"
|
||||
"Language-Team: English\n"
|
||||
"Language: en\n"
|
||||
"X-Generator: Zanata 4.6.2\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
#. (7)
|
||||
#. (30)
|
||||
#. ($delay)
|
||||
#. (config('max_delay')
|
||||
#: lib/Lutim/Command/cron/stats.pm:156 lib/Lutim/Command/cron/stats.pm:157 lib/Lutim/Command/cron/stats.pm:170 lib/Lutim/Command/cron/stats.pm:171 lib/Lutim/Command/cron/stats.pm:187 lib/Lutim/Command/cron/stats.pm:188 themes/default/templates/myfiles.html.ep:24 themes/default/templates/myfiles.html.ep:33 themes/default/templates/myfiles.html.ep:34 themes/default/templates/partial/for_my_delay.html.ep:13 themes/default/templates/partial/for_my_delay.html.ep:14 themes/default/templates/partial/for_my_delay.html.ep:4 themes/default/templates/partial/lutim.js.ep:129 themes/default/templates/partial/lutim.js.ep:138 themes/default/templates/partial/lutim.js.ep:139
|
||||
msgid "%1 days"
|
||||
msgstr "%1 days"
|
||||
|
||||
#. ($total)
|
||||
#: themes/default/templates/stats.html.ep:2
|
||||
msgid "%1 sent images on this instance from beginning."
|
||||
msgstr "%1 sent images on this instance from beginning."
|
||||
|
||||
#: themes/default/templates/index.html.ep:227
|
||||
msgid "-or-"
|
||||
msgstr "-or-"
|
||||
|
||||
#: lib/Lutim.pm:346 lib/Lutim/Command/cron/stats.pm:158 lib/Lutim/Command/cron/stats.pm:172 lib/Lutim/Command/cron/stats.pm:189 themes/default/templates/index.html.ep:5 themes/default/templates/myfiles.html.ep:5
|
||||
msgid "1 year"
|
||||
msgstr "1 year"
|
||||
|
||||
#: lib/Lutim.pm:345 lib/Lutim/Command/cron/stats.pm:155 lib/Lutim/Command/cron/stats.pm:169 lib/Lutim/Command/cron/stats.pm:186 themes/default/templates/index.html.ep:4 themes/default/templates/myfiles.html.ep:33 themes/default/templates/myfiles.html.ep:4 themes/default/templates/partial/for_my_delay.html.ep:13 themes/default/templates/partial/lutim.js.ep:138
|
||||
msgid "24 hours"
|
||||
msgstr "24 hours"
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:245
|
||||
msgid ": Error while trying to get the counter."
|
||||
msgstr ": Error while trying to get the counter."
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:65
|
||||
msgid "About"
|
||||
msgstr "About"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:151
|
||||
msgid "Active images"
|
||||
msgstr "Active images"
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:346
|
||||
msgid "An error occured while downloading the image."
|
||||
msgstr "An error occured while downloading the image."
|
||||
|
||||
#: themes/default/templates/zip.html.ep:2
|
||||
msgid "Archives download"
|
||||
msgstr "Archives download"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:145
|
||||
msgid "Are you sure you want to delete the image ${short}?"
|
||||
msgstr "Are you sure you want to delete the image ${short}?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:44 themes/default/templates/myfiles.html.ep:130 themes/default/templates/stats.html.ep:25
|
||||
msgid "Back to homepage"
|
||||
msgstr "Back to homepage"
|
||||
|
||||
#: themes/default/templates/index.html.ep:230 themes/default/templates/index.html.ep:231
|
||||
msgid "Click to open the file browser"
|
||||
msgstr "Click to open the file browser"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:51
|
||||
msgid "Close"
|
||||
msgstr "Close"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:29
|
||||
msgid "Close (Esc)"
|
||||
msgstr "Close (Esc)"
|
||||
|
||||
#: themes/default/templates/about.html.ep:30
|
||||
msgid "Contributors"
|
||||
msgstr "Contributors"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:113 themes/default/templates/partial/common.js.ep:93
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "Copied to clipboard"
|
||||
|
||||
#: themes/default/templates/partial/lutim.js.ep:206 themes/default/templates/partial/lutim.js.ep:267 themes/default/templates/partial/lutim.js.ep:354
|
||||
msgid "Copy all view links to clipboard"
|
||||
msgstr "Copy all view links to clipboard"
|
||||
|
||||
#: themes/default/templates/index.html.ep:104 themes/default/templates/index.html.ep:16 themes/default/templates/index.html.ep:32 themes/default/templates/index.html.ep:48 themes/default/templates/index.html.ep:80 themes/default/templates/index.html.ep:88 themes/default/templates/index.html.ep:96 themes/default/templates/myfiles.html.ep:66 themes/default/templates/myfiles.html.ep:82 themes/default/templates/myfiles.html.ep:98 themes/default/templates/partial/common.js.ep:209 themes/default/templates/partial/lutim.js.ep:110 themes/default/templates/partial/lutim.js.ep:70 themes/default/templates/partial/lutim.js.ep:82 themes/default/templates/partial/lutim.js.ep:96 themes/default/templates/partial/myfiles.js.ep:183
|
||||
msgid "Copy to clipboard"
|
||||
msgstr "Copy to clipboard"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:118
|
||||
msgid "Counter"
|
||||
msgstr "Counter"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:18
|
||||
msgid "Delay repartition chart for disabled images"
|
||||
msgstr "Delay repartition chart for disabled images"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:15
|
||||
msgid "Delay repartition chart for enabled images"
|
||||
msgstr "Delay repartition chart for enabled images"
|
||||
|
||||
#: themes/default/templates/index.html.ep:126 themes/default/templates/index.html.ep:158 themes/default/templates/index.html.ep:202 themes/default/templates/myfiles.html.ep:119 themes/default/templates/myfiles.html.ep:45 themes/default/templates/partial/lutim.js.ep:150
|
||||
msgid "Delete at first view?"
|
||||
msgstr "Delete at first view?"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:109
|
||||
msgid "Delete selected images"
|
||||
msgstr "Delete selected images"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:152
|
||||
msgid "Deleted images"
|
||||
msgstr "Deleted images"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:153
|
||||
msgid "Deleted images in 30 days"
|
||||
msgstr "Deleted images in 30 days"
|
||||
|
||||
#: themes/default/templates/index.html.ep:109 themes/default/templates/myfiles.html.ep:122 themes/default/templates/partial/common.js.ep:195 themes/default/templates/partial/common.js.ep:200
|
||||
msgid "Deletion link"
|
||||
msgstr "Deletion link"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:10
|
||||
msgid "Download all images"
|
||||
msgstr "Download all images"
|
||||
|
||||
#: themes/default/templates/index.html.ep:92 themes/default/templates/index.html.ep:94 themes/default/templates/partial/lutim.js.ep:88 themes/default/templates/partial/lutim.js.ep:92
|
||||
msgid "Download link"
|
||||
msgstr "Download link"
|
||||
|
||||
#: themes/default/templates/index.html.ep:26 themes/default/templates/index.html.ep:28 themes/default/templates/myfiles.html.ep:76 themes/default/templates/myfiles.html.ep:78
|
||||
msgid "Download zip link"
|
||||
msgstr "Download zip link"
|
||||
|
||||
#: themes/default/templates/index.html.ep:226
|
||||
msgid "Drag & drop images here"
|
||||
msgstr "Drag & drop images here"
|
||||
|
||||
#: themes/default/templates/about.html.ep:7
|
||||
msgid "Drag and drop an image in the appropriate area or use the traditional way to send files and Lutim will provide you four URLs. One to view the image, an other to directly download it, one you can use on social networks and a last to delete the image when you want."
|
||||
msgstr "Drag and drop an image in the appropriate area or use the traditional way to send files and Lutim will provide you four URLs. One to view the image, an other to directly download it, one you can use on social networks and a last to delete the image when you want."
|
||||
|
||||
#: themes/default/templates/index.html.ep:161 themes/default/templates/index.html.ep:205
|
||||
msgid "Encrypt the image (Lutim does not keep the key)."
|
||||
msgstr "Encrypt the image (Lutim does not keep the key)."
|
||||
|
||||
#: themes/default/templates/partial/lutim.js.ep:38 themes/default/templates/partial/myfiles.js.ep:129
|
||||
msgid "Error while trying to modify the image."
|
||||
msgstr "Error while trying to modify the image."
|
||||
|
||||
#: themes/default/templates/stats.html.ep:10
|
||||
msgid "Evolution of total files"
|
||||
msgstr "Evolution of total files"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:121
|
||||
msgid "Expires at"
|
||||
msgstr "Expires at"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:106
|
||||
msgid "Export localStorage data"
|
||||
msgstr "Export localStorage data"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:116
|
||||
msgid "File name"
|
||||
msgstr "File name"
|
||||
|
||||
#: themes/default/templates/about.html.ep:24
|
||||
msgid "For more details, see the <a href=\"https://framagit.org/fiat-tux/hat-softwares/lutim\">homepage of the project</a>."
|
||||
msgstr "For more details, see the <a href=\"https://framagit.org/fiat-tux/hat-softwares/lutim\">homepage of the project</a>."
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:68
|
||||
msgid "Fork me!"
|
||||
msgstr "Fork me!"
|
||||
|
||||
#: themes/default/templates/index.html.ep:10 themes/default/templates/index.html.ep:12 themes/default/templates/myfiles.html.ep:60 themes/default/templates/myfiles.html.ep:62
|
||||
msgid "Gallery link"
|
||||
msgstr "Gallery link"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:116 themes/default/templates/partial/common.js.ep:134
|
||||
msgid "Hit Ctrl+C, then Enter to copy the short link"
|
||||
msgstr "Hit Ctrl+C, then Enter to copy the short link"
|
||||
|
||||
#: themes/default/templates/layouts/default.html.ep:46
|
||||
msgid "Homepage"
|
||||
msgstr "Homepage"
|
||||
|
||||
#: themes/default/templates/about.html.ep:20
|
||||
msgid "How do you pronounce Lutim?"
|
||||
msgstr "How do you pronounce Lutim?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:6
|
||||
msgid "How does it work?"
|
||||
msgstr "How does it work?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:18
|
||||
msgid "How to report an image?"
|
||||
msgstr "How to report an image?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:14
|
||||
msgid "If the files are deleted if you ask it while posting it, their SHA512 footprint are retained."
|
||||
msgstr "If the files are deleted if you ask it while posting it, their SHA512 footprint are retained."
|
||||
|
||||
#: themes/default/templates/index.html.ep:187 themes/default/templates/index.html.ep:240
|
||||
msgid "Image URL"
|
||||
msgstr "Image URL"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:150
|
||||
msgid "Image delay"
|
||||
msgstr "Image delay"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:172
|
||||
msgid "Image deleted"
|
||||
msgstr "Image deleted"
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:815
|
||||
msgid "Image not found."
|
||||
msgstr "Image not found."
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:107
|
||||
msgid "Import localStorage data"
|
||||
msgstr "Import localStorage data"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:57
|
||||
msgid "Informations"
|
||||
msgstr "Informations"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:25
|
||||
msgid "Install webapp"
|
||||
msgstr "Install webapp"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:21
|
||||
msgid "Instance's statistics"
|
||||
msgstr "Instance's statistics"
|
||||
|
||||
#: themes/default/templates/about.html.ep:11
|
||||
msgid "Is it really anonymous?"
|
||||
msgstr "Is it really anonymous?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:9
|
||||
msgid "Is it really free (as in free beer)?"
|
||||
msgstr "Is it really free (as in free beer)?"
|
||||
|
||||
#: themes/default/templates/index.html.ep:164 themes/default/templates/index.html.ep:208
|
||||
msgid "Keep EXIF tags"
|
||||
msgstr "Keep EXIF tags"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:43 themes/default/templates/partial/navbar.html.ep:44
|
||||
msgid "Language"
|
||||
msgstr "Language"
|
||||
|
||||
#: themes/default/templates/index.html.ep:129 themes/default/templates/index.html.ep:190 themes/default/templates/index.html.ep:243 themes/default/templates/partial/lutim.js.ep:157
|
||||
msgid "Let's go!"
|
||||
msgstr "Let's go!"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:62
|
||||
msgid "License:"
|
||||
msgstr "License:"
|
||||
|
||||
#: themes/default/templates/index.html.ep:100 themes/default/templates/index.html.ep:102 themes/default/templates/partial/lutim.js.ep:102 themes/default/templates/partial/lutim.js.ep:106
|
||||
msgid "Link for share on social networks"
|
||||
msgstr "Link for share on social networks"
|
||||
|
||||
#: themes/default/templates/login.html.ep:8
|
||||
msgid "Login"
|
||||
msgstr "Login"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:33
|
||||
msgid "Logout"
|
||||
msgstr "Logout"
|
||||
|
||||
#: themes/default/templates/zip.html.ep:7
|
||||
msgid "Lutim can't zip so many images at once, so it splitted your demand in multiple URLs."
|
||||
msgstr "Lutim can't zip so many images at once, so it splitted your demand in multiple URLs."
|
||||
|
||||
#: themes/default/templates/about.html.ep:4
|
||||
msgid "Lutim is a free (as in free beer) and anonymous image hosting service. It's also the name of the free (as in free speech) software which provides this service."
|
||||
msgstr "Lutim is a free (as in free beer) and anonymous image hosting service. It's also the name of the free (as in free speech) software which provides this service."
|
||||
|
||||
#: themes/default/templates/about.html.ep:25
|
||||
msgid "Main developers"
|
||||
msgstr "Main developers"
|
||||
|
||||
#: themes/default/templates/index.html.ep:84 themes/default/templates/index.html.ep:86 themes/default/templates/partial/lutim.js.ep:76 themes/default/templates/partial/lutim.js.ep:79
|
||||
msgid "Markdown syntax"
|
||||
msgstr "Markdown syntax"
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:200
|
||||
msgid "Modify expiration delay"
|
||||
msgstr "Modify expiration delay"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:7 themes/default/templates/partial/navbar.html.ep:18
|
||||
msgid "My images"
|
||||
msgstr "My images"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:45
|
||||
msgid "Next (arrow right)"
|
||||
msgstr "Next (arrow right)"
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:121 themes/default/templates/partial/myfiles.js.ep:163
|
||||
msgid "No limit"
|
||||
msgstr "No limit"
|
||||
|
||||
#: themes/default/templates/index.html.ep:176 themes/default/templates/index.html.ep:220
|
||||
msgid "No watermark"
|
||||
msgstr "No watermark"
|
||||
|
||||
#: themes/default/templates/index.html.ep:189 themes/default/templates/index.html.ep:235
|
||||
msgid "Only images are allowed"
|
||||
msgstr "Only images are allowed"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:11
|
||||
msgid "Only the images sent with this browser will be listed here. The details are stored in localStorage: if you delete your localStorage data, you'll loose these details."
|
||||
msgstr "Only the images sent with this browser will be listed here. The details are stored in localStorage: if you delete your localStorage data, you'll loose these details."
|
||||
|
||||
#: themes/default/templates/about.html.ep:16
|
||||
msgid "Only the uploader! (well, only if he's the only owner of the images' rights before the upload)"
|
||||
msgstr "Only the uploader! (well, only if he's the only owner of the images' rights before the upload)"
|
||||
|
||||
#: themes/default/templates/login.html.ep:12
|
||||
msgid "Password"
|
||||
msgstr "Password"
|
||||
|
||||
#: themes/default/templates/zip.html.ep:12
|
||||
msgid "Please click on each URL to download the different zip files."
|
||||
msgstr "Please click on each URL to download the different zip files."
|
||||
|
||||
#. (config('contact')
|
||||
#: themes/default/templates/about.html.ep:19
|
||||
msgid "Please contact the administrator: %1"
|
||||
msgstr "Please contact the administrator: %1"
|
||||
|
||||
#: lib/Lutim/Controller/Authent.pm:36
|
||||
msgid "Please, check your credentials: unable to authenticate."
|
||||
msgstr "Please, check your credentials: unable to authenticate."
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:43
|
||||
msgid "Previous (arrow left)"
|
||||
msgstr "Previous (arrow left)"
|
||||
|
||||
#: themes/default/templates/index.html.ep:42 themes/default/templates/index.html.ep:44 themes/default/templates/myfiles.html.ep:92 themes/default/templates/myfiles.html.ep:94
|
||||
msgid "Random image link"
|
||||
msgstr "Random image link"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:22
|
||||
msgid "Raw stats"
|
||||
msgstr "Raw stats"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:52
|
||||
msgid "Save changes"
|
||||
msgstr "Save changes"
|
||||
|
||||
#: themes/default/templates/index.html.ep:182
|
||||
msgid "Send an image"
|
||||
msgstr "Send an image"
|
||||
|
||||
#: themes/default/templates/login.html.ep:16 themes/default/templates/logout.html.ep:5 themes/default/templates/partial/navbar.html.ep:37
|
||||
msgid "Signin"
|
||||
msgstr "Signin"
|
||||
|
||||
#: themes/default/templates/index.html.ep:173 themes/default/templates/index.html.ep:217
|
||||
msgid "Single watermark"
|
||||
msgstr "Single watermark"
|
||||
|
||||
#: themes/default/templates/index.html.ep:144 themes/default/templates/partial/gallery.js.ep:210 themes/default/templates/partial/lutim.js.ep:166
|
||||
msgid "Something bad happened"
|
||||
msgstr "Something bad happened"
|
||||
|
||||
#. ($c->config('contact')
|
||||
#: lib/Lutim/Controller/Image.pm:822
|
||||
msgid "Something went wrong when creating the zip file. Try again later or contact the administrator (%1)."
|
||||
msgstr "Something went wrong when creating the zip file. Try again later or contact the administrator (%1)."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:294
|
||||
msgid "Sorry, the API is disabled"
|
||||
msgstr "Sorry, the API is disabled"
|
||||
|
||||
#: themes/default/templates/about.html.ep:13
|
||||
msgid "The IP address of the image's sender is retained for a delay which depends of the administrator's choice (for the official instance, which is located in France, it's one year)."
|
||||
msgstr "The IP address of the image's sender is retained for a delay which depends of the administrator's choice (for the official instance, which is located in France, it's one year)."
|
||||
|
||||
#: themes/default/templates/about.html.ep:23
|
||||
msgid "The Lutim software is a <a href=\"http://en.wikipedia.org/wiki/Free_software\">free software</a>, which allows you to download and install it on you own server. Have a look at the <a href=\"https://www.gnu.org/licenses/agpl-3.0.html\">AGPL</a> to see what you can do."
|
||||
msgstr "The Lutim software is a <a href=\"http://en.wikipedia.org/wiki/Free_software\">free software</a>, which allows you to download and install it on you own server. Have a look at the <a href=\"https://www.gnu.org/licenses/agpl-3.0.html\">AGPL</a> to see what you can do."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:365
|
||||
msgid "The URL is not valid."
|
||||
msgstr "The URL is not valid."
|
||||
|
||||
#: themes/default/templates/zip.html.ep:16
|
||||
msgid "The automatic download process will open a tab in your browser for each link. You need to allow popups for Lutim."
|
||||
msgstr "The automatic download process will open a tab in your browser for each link. You need to allow popups for Lutim."
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:32
|
||||
msgid "The data has been successfully imported."
|
||||
msgstr "The data has been successfully imported."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:161 lib/Lutim/Controller/Image.pm:229
|
||||
msgid "The delete token is invalid."
|
||||
msgstr "The delete token is invalid."
|
||||
|
||||
#. ($upload->filename)
|
||||
#: lib/Lutim/Controller/Image.pm:547
|
||||
msgid "The file %1 is not an image."
|
||||
msgstr "The file %1 is not an image."
|
||||
|
||||
#. ($res->max_message_size)
|
||||
#. ($c->req->max_message_size)
|
||||
#. (config('max_file_size')
|
||||
#: lib/Lutim/Controller/Image.pm:329 lib/Lutim/Controller/Image.pm:395 themes/default/templates/partial/lutim.js.ep:239
|
||||
msgid "The file exceed the size limit (%1)"
|
||||
msgstr "The file exceed the size limit (%1)"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:12
|
||||
msgid "The graph's datas are not updated in real-time."
|
||||
msgstr "The graph's datas are not updated in real-time."
|
||||
|
||||
#. ($image->filename)
|
||||
#: lib/Lutim/Controller/Image.pm:231
|
||||
msgid "The image %1 has already been deleted."
|
||||
msgstr "The image %1 has already been deleted."
|
||||
|
||||
#. ($image->filename)
|
||||
#: lib/Lutim/Controller/Image.pm:240 lib/Lutim/Controller/Image.pm:245
|
||||
msgid "The image %1 has been successfully deleted"
|
||||
msgstr "The image %1 has been successfully deleted"
|
||||
|
||||
#: themes/default/templates/index.html.ep:57
|
||||
msgid "The images are encrypted on the server (Lutim does not keep the key)."
|
||||
msgstr "The images are encrypted on the server (Lutim does not keep the key)."
|
||||
|
||||
#: themes/default/templates/about.html.ep:5
|
||||
msgid "The images you post on Lutim can be stored indefinitely or be deleted at first view or after a delay selected from those proposed."
|
||||
msgstr "The images you post on Lutim can be stored indefinitely or be deleted at first view or after a delay selected from those proposed."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:169
|
||||
msgid "The image’s delay has been successfully modified"
|
||||
msgstr "The image’s delay has been successfully modified"
|
||||
|
||||
#. ('https://fr.wikipedia.org/wiki/Lutin')
|
||||
#: themes/default/templates/about.html.ep:21
|
||||
msgid "The way you pronounce the French word <a href=\"%1\">lutin</a> (/ly.tɛ̃/)."
|
||||
msgstr "The way you pronounce the French word <a href=\"%1\">lutin</a> (/ly.tɛ̃/)."
|
||||
|
||||
#: themes/default/templates/partial/gallery.js.ep:271
|
||||
msgid "There is XXXX image(s) in the gallery"
|
||||
msgstr "There is XXXX image(s) in the gallery"
|
||||
|
||||
#. ($c->config->{contact})
|
||||
#: lib/Lutim/Controller/Image.pm:544
|
||||
msgid "There is no more available URL. Retry or contact the administrator. %1"
|
||||
msgstr "There is no more available URL. Retry or contact the administrator. %1"
|
||||
|
||||
#: themes/default/templates/index.html.ep:170 themes/default/templates/index.html.ep:214
|
||||
msgid "Tiling watermark"
|
||||
msgstr "Tiling watermark"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:30
|
||||
msgid "Toggle fullscreen"
|
||||
msgstr "Toggle fullscreen"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:8
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Toggle navigation"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:159
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#:
|
||||
msgid "Tweet it!"
|
||||
msgstr "Tweet it!"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:110 themes/default/templates/partial/common.js.ep:90
|
||||
msgid "Unable to copy to clipboard"
|
||||
msgstr "Unable to copy to clipboard"
|
||||
|
||||
#. ($short)
|
||||
#: lib/Lutim/Controller/Image.pm:109 lib/Lutim/Controller/Image.pm:203 lib/Lutim/Controller/Image.pm:274
|
||||
msgid "Unable to find the image %1."
|
||||
msgstr "Unable to find the image %1."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:633 lib/Lutim/Controller/Image.pm:678 lib/Lutim/Controller/Image.pm:719 lib/Lutim/Controller/Image.pm:762 lib/Lutim/Controller/Image.pm:774 lib/Lutim/Controller/Image.pm:785 lib/Lutim/Controller/Image.pm:812 lib/Lutim/Plugin/Helpers.pm:90
|
||||
msgid "Unable to find the image: it has been deleted."
|
||||
msgstr "Unable to find the image: it has been deleted."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:146
|
||||
msgid "Unable to get counter"
|
||||
msgstr "Unable to get counter"
|
||||
|
||||
#: themes/default/templates/about.html.ep:17
|
||||
msgid "Unlike many image sharing services, you don't give us rights on uploaded images."
|
||||
msgstr "Unlike many image sharing services, you don't give us rights on uploaded images."
|
||||
|
||||
#: themes/default/templates/index.html.ep:186 themes/default/templates/index.html.ep:238
|
||||
msgid "Upload an image with its URL"
|
||||
msgstr "Upload an image with its URL"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:120
|
||||
msgid "Uploaded at"
|
||||
msgstr "Uploaded at"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:6
|
||||
msgid "Uploaded files by days"
|
||||
msgstr "Uploaded files by days"
|
||||
|
||||
#. ($c->app->config('contact')
|
||||
#: lib/Lutim/Plugin/Helpers.pm:223
|
||||
msgid "Uploading is currently disabled, please try later or contact the administrator (%1)."
|
||||
msgstr "Uploading is currently disabled, please try later or contact the administrator (%1)."
|
||||
|
||||
#: themes/default/templates/index.html.ep:76 themes/default/templates/index.html.ep:78 themes/default/templates/myfiles.html.ep:117 themes/default/templates/partial/lutim.js.ep:62 themes/default/templates/partial/lutim.js.ep:66
|
||||
msgid "View link"
|
||||
msgstr "View link"
|
||||
|
||||
#: themes/default/templates/about.html.ep:22
|
||||
msgid "What about the software which provides the service?"
|
||||
msgstr "What about the software which provides the service?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:3
|
||||
msgid "What is Lutim?"
|
||||
msgstr "What is Lutim?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:15
|
||||
msgid "Who owns rights on images uploaded on Lutim?"
|
||||
msgstr "Who owns rights on images uploaded on Lutim?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:12
|
||||
msgid "Yes, it is! On the other side, for legal reasons, your IP address will be stored when you send an image. Don't panic, it is normally the case of all sites on which you send files!"
|
||||
msgstr "Yes, it is! On the other side, for legal reasons, your IP address will be stored when you send an image. Don't panic, it is normally the case of all sites on which you send files!"
|
||||
|
||||
#: themes/default/templates/about.html.ep:10
|
||||
msgid "Yes, it is! On the other side, if you want to support the developer, you can do it via <a href=\"https://www.tipeee.com/fiat-tux\">Tipeee</a> or via <a href=\"https://liberapay.com/sky/\">Liberapay</a>."
|
||||
msgstr "Yes, it is! On the other side, if you want to support the developer, you can do it via <a href=\"https://www.tipeee.com/fiat-tux\">Tipeee</a> or via <a href=\"https://liberapay.com/sky/\">Liberapay</a>."
|
||||
|
||||
#: themes/default/templates/zip.html.ep:6
|
||||
msgid "You asked to download a zip archive for too much files."
|
||||
msgstr "You asked to download a zip archive for too much files."
|
||||
|
||||
#: themes/default/templates/about.html.ep:8
|
||||
msgid "You can, optionally, request that the image(s) posted on Lutim to be deleted at first view (or download) or after the delay selected from those proposed."
|
||||
msgstr "You can, optionally, request that the image(s) posted on Lutim to be deleted at first view (or download) or after the delay selected from those proposed."
|
||||
|
||||
#: lib/Lutim/Controller/Authent.pm:27
|
||||
msgid "You have been successfully logged in."
|
||||
msgstr "You have been successfully logged in."
|
||||
|
||||
#: lib/Lutim/Controller/Authent.pm:66 themes/default/templates/logout.html.ep:3
|
||||
msgid "You have been successfully logged out."
|
||||
msgstr "You have been successfully logged out."
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:31
|
||||
msgid "Zoom in/out"
|
||||
msgstr "Zoom in/out"
|
||||
|
||||
#: themes/default/templates/about.html.ep:27
|
||||
msgid "and on"
|
||||
msgstr "and on"
|
||||
|
||||
#: themes/default/templates/about.html.ep:40
|
||||
msgid "arabic translation"
|
||||
msgstr "arabic translation"
|
||||
|
||||
#: themes/default/templates/about.html.ep:27
|
||||
msgid "core developer"
|
||||
msgstr "core developer"
|
||||
|
||||
#: lib/Lutim.pm:344 lib/Lutim/Command/cron/stats.pm:154 lib/Lutim/Command/cron/stats.pm:168 lib/Lutim/Command/cron/stats.pm:185 themes/default/templates/index.html.ep:3 themes/default/templates/myfiles.html.ep:3
|
||||
msgid "no time limit"
|
||||
msgstr "no time limit"
|
||||
|
||||
#: themes/default/templates/about.html.ep:38
|
||||
msgid "occitan translation"
|
||||
msgstr "occitan translation"
|
||||
|
||||
#: themes/default/templates/about.html.ep:27
|
||||
msgid "on"
|
||||
msgstr "on"
|
||||
|
||||
#: themes/default/templates/about.html.ep:39
|
||||
msgid "paste image to upload ability"
|
||||
msgstr "paste image to upload ability"
|
||||
|
||||
#: themes/default/templates/about.html.ep:41
|
||||
msgid "russian translation"
|
||||
msgstr "russian translation"
|
||||
|
||||
#: themes/default/templates/about.html.ep:36
|
||||
msgid "spanish translation"
|
||||
msgstr "spanish translation"
|
||||
|
||||
#: themes/default/templates/about.html.ep:28
|
||||
msgid "webapp developer"
|
||||
msgstr "webapp developer"
|
||||
695
themes/default/lib/Lutim/I18N/es.po
Normal file
695
themes/default/lib/Lutim/I18N/es.po
Normal file
@@ -0,0 +1,695 @@
|
||||
# Lutim language file
|
||||
# Copyright (C) 2014 Luc Didry
|
||||
# This file is distributed under the same license as the Lutim package.
|
||||
#
|
||||
# Translators:
|
||||
# Translators:
|
||||
# Luc Didry <luc@didry.org>, 2015
|
||||
# Luc Didry <luc@framasoft.org>, 2018. #zanata
|
||||
# Luc Didry <luc@framasoft.org>, 2019. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"PO-Revision-Date: 2021-04-24 11:14+0000\n"
|
||||
"Last-Translator: Berto Te <ateira@3fpj.com>\n"
|
||||
"Language-Team: Spanish <https://weblate.framasoft.org/projects/lutim/"
|
||||
"default-theme/es/>\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.5.1\n"
|
||||
|
||||
#. (7)
|
||||
#. (30)
|
||||
#. ($delay)
|
||||
#. (config('max_delay')
|
||||
#: lib/Lutim/Command/cron/stats.pm:159 lib/Lutim/Command/cron/stats.pm:160 lib/Lutim/Command/cron/stats.pm:173 lib/Lutim/Command/cron/stats.pm:174 lib/Lutim/Command/cron/stats.pm:190 lib/Lutim/Command/cron/stats.pm:191 themes/default/templates/myfiles.html.ep:24 themes/default/templates/myfiles.html.ep:33 themes/default/templates/myfiles.html.ep:34 themes/default/templates/partial/for_my_delay.html.ep:13 themes/default/templates/partial/for_my_delay.html.ep:14 themes/default/templates/partial/for_my_delay.html.ep:4 themes/default/templates/partial/lutim.js.ep:140 themes/default/templates/partial/lutim.js.ep:149 themes/default/templates/partial/lutim.js.ep:150 themes/default/templates/partial/raw.js.ep:23 themes/default/templates/partial/raw.js.ep:24 themes/default/templates/partial/raw.js.ep:6 themes/default/templates/partial/raw.js.ep:7 themes/default/templates/raw.html.ep:8 themes/default/templates/raw.html.ep:9
|
||||
msgid "%1 days"
|
||||
msgstr "%1 días"
|
||||
|
||||
#. ($total)
|
||||
#: themes/default/templates/stats.html.ep:2
|
||||
msgid "%1 sent images on this instance from beginning."
|
||||
msgstr "%1 imágenes enviadas a esta instancia desde el inicio."
|
||||
|
||||
#: themes/default/templates/index.html.ep:208
|
||||
msgid "-or-"
|
||||
msgstr "-o-"
|
||||
|
||||
#: lib/Lutim.pm:350 lib/Lutim/Command/cron/stats.pm:161 lib/Lutim/Command/cron/stats.pm:175 lib/Lutim/Command/cron/stats.pm:192 themes/default/templates/index.html.ep:5 themes/default/templates/myfiles.html.ep:5 themes/default/templates/partial/raw.js.ep:25 themes/default/templates/partial/raw.js.ep:8 themes/default/templates/raw.html.ep:10
|
||||
msgid "1 year"
|
||||
msgstr "1 año"
|
||||
|
||||
#: lib/Lutim.pm:349 lib/Lutim/Command/cron/stats.pm:158 lib/Lutim/Command/cron/stats.pm:172 lib/Lutim/Command/cron/stats.pm:189 themes/default/templates/index.html.ep:4 themes/default/templates/myfiles.html.ep:33 themes/default/templates/myfiles.html.ep:4 themes/default/templates/partial/for_my_delay.html.ep:13 themes/default/templates/partial/lutim.js.ep:149 themes/default/templates/partial/raw.js.ep:22 themes/default/templates/partial/raw.js.ep:5 themes/default/templates/raw.html.ep:7
|
||||
msgid "24 hours"
|
||||
msgstr "24 horas"
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:180
|
||||
msgid ": Error while trying to get the counter."
|
||||
msgstr ": Error al intentar obtener el contador."
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:77
|
||||
msgid "About"
|
||||
msgstr "Acerca de"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:154 themes/default/templates/raw.html.ep:3
|
||||
msgid "Active images"
|
||||
msgstr "Imágenes activas"
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:328
|
||||
msgid "An error occured while downloading the image."
|
||||
msgstr "Error al intentar modificar la imagen."
|
||||
|
||||
#: themes/default/templates/zip.html.ep:2
|
||||
msgid "Archives download"
|
||||
msgstr "Descarga de archivos"
|
||||
|
||||
#: themes/default/templates/about.html.ep:44 themes/default/templates/myfiles.html.ep:135 themes/default/templates/stats.html.ep:25
|
||||
msgid "Back to homepage"
|
||||
msgstr "Volver a la página inicial"
|
||||
|
||||
#: themes/default/templates/index.html.ep:211 themes/default/templates/index.html.ep:212
|
||||
msgid "Click to open the file browser"
|
||||
msgstr "Clic para abrir el explorador de archivos"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:51
|
||||
msgid "Close"
|
||||
msgstr "Cerrar"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:29
|
||||
msgid "Close (Esc)"
|
||||
msgstr "Cerrar (Esc)"
|
||||
|
||||
#: themes/default/templates/about.html.ep:30
|
||||
msgid "Contributors"
|
||||
msgstr "Contribuidores"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:113 themes/default/templates/partial/common.js.ep:93
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "Copiado al portapapeles"
|
||||
|
||||
#: themes/default/templates/partial/lutim.js.ep:215 themes/default/templates/partial/lutim.js.ep:278 themes/default/templates/partial/lutim.js.ep:364
|
||||
msgid "Copy all view links to clipboard"
|
||||
msgstr "Copiar todos los enlaces de visualización al portapapeles"
|
||||
|
||||
#: themes/default/templates/index.html.ep:103 themes/default/templates/index.html.ep:111 themes/default/templates/index.html.ep:18 themes/default/templates/index.html.ep:36 themes/default/templates/index.html.ep:54 themes/default/templates/index.html.ep:87 themes/default/templates/index.html.ep:95 themes/default/templates/myfiles.html.ep:104 themes/default/templates/myfiles.html.ep:68 themes/default/templates/myfiles.html.ep:86 themes/default/templates/partial/common.js.ep:186 themes/default/templates/partial/lutim.js.ep:106 themes/default/templates/partial/lutim.js.ep:121 themes/default/templates/partial/lutim.js.ep:80 themes/default/templates/partial/lutim.js.ep:92 themes/default/templates/partial/myfiles.js.ep:142
|
||||
msgid "Copy to clipboard"
|
||||
msgstr "Copiar al portapapeles"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:123
|
||||
msgid "Counter"
|
||||
msgstr "Contador"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:18
|
||||
msgid "Delay repartition chart for disabled images"
|
||||
msgstr "Tabla de reparto de retardos para imágenes deshabilitadas"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:15
|
||||
msgid "Delay repartition chart for enabled images"
|
||||
msgstr "Tabla de reparto de retardos para imágenes habilitadas"
|
||||
|
||||
#: themes/default/templates/index.html.ep:133 themes/default/templates/index.html.ep:165 themes/default/templates/index.html.ep:196 themes/default/templates/myfiles.html.ep:124 themes/default/templates/myfiles.html.ep:45 themes/default/templates/partial/lutim.js.ep:161
|
||||
msgid "Delete at first view?"
|
||||
msgstr "¿Borrar en la primera vista?"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:155 themes/default/templates/raw.html.ep:4
|
||||
msgid "Deleted images"
|
||||
msgstr "Imágenes borradas"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:156 themes/default/templates/raw.html.ep:5
|
||||
msgid "Deleted images in 30 days"
|
||||
msgstr "Imágenes borradas en 30 días"
|
||||
|
||||
#: themes/default/templates/index.html.ep:116 themes/default/templates/myfiles.html.ep:127 themes/default/templates/partial/common.js.ep:178 themes/default/templates/partial/common.js.ep:181
|
||||
msgid "Deletion link"
|
||||
msgstr "Enlace para borrar"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:10
|
||||
msgid "Download all images"
|
||||
msgstr "Descargar todas las imágenes"
|
||||
|
||||
#: themes/default/templates/index.html.ep:101 themes/default/templates/index.html.ep:99 themes/default/templates/partial/lutim.js.ep:102 themes/default/templates/partial/lutim.js.ep:98
|
||||
msgid "Download link"
|
||||
msgstr "Enlace de descarga"
|
||||
|
||||
#: themes/default/templates/index.html.ep:28 themes/default/templates/index.html.ep:31 themes/default/templates/myfiles.html.ep:78 themes/default/templates/myfiles.html.ep:81
|
||||
msgid "Download zip link"
|
||||
msgstr "Enlace de descarga del archivo de las imágenes"
|
||||
|
||||
#: themes/default/templates/index.html.ep:207
|
||||
msgid "Drag & drop images here"
|
||||
msgstr "Arrastre y suelte imágenes aquí"
|
||||
|
||||
#: themes/default/templates/about.html.ep:7
|
||||
msgid ""
|
||||
"Drag and drop an image in the appropriate area or use the traditional way to "
|
||||
"send files and Lutim will provide you four URLs. One to view the image, an "
|
||||
"other to directly download it, one you can use on social networks and a last "
|
||||
"to delete the image when you want."
|
||||
msgstr ""
|
||||
"Arrastre y suelte una imagen en el área apropiada, o use el método "
|
||||
"tradicional para enviar ficheros, y Lutim proporcionará cuatro URLs. Una "
|
||||
"para ver la imagen, otra para descargarla directamente, una que upede usar "
|
||||
"en redes sociales, y una última para borrar la imagen cuando lo desee."
|
||||
|
||||
#: themes/default/templates/index.html.ep:168 themes/default/templates/index.html.ep:199
|
||||
msgid "Encrypt the image (Lutim does not keep the key)."
|
||||
msgstr "Las imágenes se cifran en el servidor (Lutim no guarda la clave)."
|
||||
|
||||
#: themes/default/templates/partial/lutim.js.ep:45 themes/default/templates/partial/myfiles.js.ep:113
|
||||
msgid "Error while trying to modify the image."
|
||||
msgstr "Error al intentar modificar la imagen."
|
||||
|
||||
#: themes/default/templates/stats.html.ep:10
|
||||
msgid "Evolution of total files"
|
||||
msgstr "Evolución de archivos en total"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:126
|
||||
msgid "Expires at"
|
||||
msgstr "Expira"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:112
|
||||
msgid "Export localStorage data"
|
||||
msgstr "Exportar datos de almacenamiento local"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:121
|
||||
msgid "File name"
|
||||
msgstr "Nombre de archivo"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:80
|
||||
msgid "Fork me!"
|
||||
msgstr "¡Clóname!"
|
||||
|
||||
#: themes/default/templates/index.html.ep:10 themes/default/templates/index.html.ep:13 themes/default/templates/myfiles.html.ep:60 themes/default/templates/myfiles.html.ep:63
|
||||
msgid "Gallery link"
|
||||
msgstr "Enlace a la galería"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:116 themes/default/templates/partial/common.js.ep:134
|
||||
msgid "Hit Ctrl+C, then Enter to copy the short link"
|
||||
msgstr "Presione Ctrl + C, entonces Ingresar para copiar el enlace"
|
||||
|
||||
#: themes/default/templates/layouts/default.html.ep:47
|
||||
msgid "Homepage"
|
||||
msgstr "Página inicial"
|
||||
|
||||
#: themes/default/templates/about.html.ep:20
|
||||
msgid "How do you pronounce Lutim?"
|
||||
msgstr "¿Cómo se pronuncia Lutim?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:6
|
||||
msgid "How does it work?"
|
||||
msgstr "¿Cómo funciona?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:18
|
||||
msgid "How to report an image?"
|
||||
msgstr "¿Cómo informar sobre una imagen?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:14
|
||||
msgid ""
|
||||
"If the files are deleted if you ask it while posting it, their SHA512 "
|
||||
"footprint are retained."
|
||||
msgstr ""
|
||||
"Si los ficheros se borran por haberlo solicitado al enviarlos, se retiene su "
|
||||
"huella digital SHA512."
|
||||
|
||||
#: themes/default/templates/index.html.ep:181 themes/default/templates/index.html.ep:221
|
||||
msgid "Image URL"
|
||||
msgstr "URL de la imagen"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:153 themes/default/templates/raw.html.ep:2
|
||||
msgid "Image delay"
|
||||
msgstr "Retardo de imagen"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:157
|
||||
msgid "Image deleted"
|
||||
msgstr "Imagen eliminada"
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:756
|
||||
msgid "Image not found."
|
||||
msgstr "Imagen no encontrada."
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:113
|
||||
msgid "Import localStorage data"
|
||||
msgstr "Importar datos de almacenamiento local"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:69
|
||||
msgid "Informations"
|
||||
msgstr "Informaciones"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:25
|
||||
msgid "Install webapp"
|
||||
msgstr "Instalar webapp"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:21
|
||||
msgid "Instance's statistics"
|
||||
msgstr "Estadísticas de la instancia"
|
||||
|
||||
#: themes/default/templates/about.html.ep:11
|
||||
msgid "Is it really anonymous?"
|
||||
msgstr "¿Es realmente anónimo?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:9
|
||||
msgid "Is it really free (as in free beer)?"
|
||||
msgstr "¿Es realmente gratis?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:21
|
||||
msgid "The way you pronounce the French word <a href=\"%1\">lutin</a> (/ly.tɛ̃/)."
|
||||
msgstr "Tal y como se pronuncia la palabra francesa <a href=\"%1\">lutin</a> (/ly.tɛ̃/)."
|
||||
|
||||
#: themes/default/templates/index.html.ep:171 themes/default/templates/index.html.ep:202
|
||||
msgid "Keep EXIF tags"
|
||||
msgstr "Mantener las etiquetas EXIF"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:43
|
||||
msgid "Language"
|
||||
msgstr "Lengua"
|
||||
|
||||
#: themes/default/templates/index.html.ep:136 themes/default/templates/index.html.ep:184 themes/default/templates/index.html.ep:224 themes/default/templates/partial/lutim.js.ep:165
|
||||
msgid "Let's go!"
|
||||
msgstr "¡Vamos allá!"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:74
|
||||
msgid "License:"
|
||||
msgstr "Licencia:"
|
||||
|
||||
#: themes/default/templates/index.html.ep:107 themes/default/templates/index.html.ep:109 themes/default/templates/partial/lutim.js.ep:112 themes/default/templates/partial/lutim.js.ep:116
|
||||
msgid "Link for share on social networks"
|
||||
msgstr "Enlace para compartir en redes sociales"
|
||||
|
||||
#: themes/default/templates/login.html.ep:8
|
||||
msgid "Login"
|
||||
msgstr "Login"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:33
|
||||
msgid "Logout"
|
||||
msgstr "Salir"
|
||||
|
||||
#: themes/default/templates/zip.html.ep:7
|
||||
msgid ""
|
||||
"Lutim can't zip so many images at once, so it splitted your demand in "
|
||||
"multiple URLs."
|
||||
msgstr ""
|
||||
"Lutim no puede comprimir tantas imágenes a la vez, por lo que dividió tu "
|
||||
"solicitud en varias URL."
|
||||
|
||||
#: themes/default/templates/about.html.ep:4
|
||||
msgid ""
|
||||
"Lutim is a free (as in free beer) and anonymous image hosting service. It's "
|
||||
"also the name of the free (as in free speech) software which provides this "
|
||||
"service."
|
||||
msgstr ""
|
||||
"Lutim es un servicio de alojamiento de imágenes anónimo y gratuito. También "
|
||||
"es el nombre del software libre que proporciona este servicio."
|
||||
|
||||
#: themes/default/templates/about.html.ep:25
|
||||
msgid "Main developers"
|
||||
msgstr "Desarrolladores principales"
|
||||
|
||||
#: themes/default/templates/index.html.ep:91 themes/default/templates/index.html.ep:93 themes/default/templates/partial/lutim.js.ep:86 themes/default/templates/partial/lutim.js.ep:89
|
||||
msgid "Markdown syntax"
|
||||
msgstr "Sintaxis de Markdown"
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:149
|
||||
msgid "Modify expiration delay"
|
||||
msgstr "Modificar el retardo de expiración"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:7 themes/default/templates/partial/navbar.html.ep:18
|
||||
msgid "My images"
|
||||
msgstr "Mis Imágenes"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:45
|
||||
msgid "Next (arrow right)"
|
||||
msgstr "Siguiente (flecha derecha)"
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:105 themes/default/templates/partial/myfiles.js.ep:132
|
||||
msgid "No limit"
|
||||
msgstr "Sin fecha de caducidad"
|
||||
|
||||
#: themes/default/templates/index.html.ep:183 themes/default/templates/index.html.ep:216
|
||||
msgid "Only images are allowed"
|
||||
msgstr "Sólo se admiten imágenes"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:11
|
||||
msgid ""
|
||||
"Only the images sent with this browser will be listed here. The details are "
|
||||
"stored in localStorage: if you delete your localStorage data, you'll loose "
|
||||
"these details."
|
||||
msgstr ""
|
||||
"Sólo se enumeran aquí las imágenes enviadas con este navegador. Los detalles "
|
||||
"se almacenan en localStorage: si elimina sus datos localStorage, perderás "
|
||||
"estos detalles."
|
||||
|
||||
#: themes/default/templates/about.html.ep:16
|
||||
msgid ""
|
||||
"Only the uploader! (well, only if he's the only owner of the images' rights "
|
||||
"before the upload)"
|
||||
msgstr ""
|
||||
"¡Sólo el usuario! (bueno, sólo si él/ela es el único titular de los derechos "
|
||||
"de las imágenes antes de subirlas)"
|
||||
|
||||
#: themes/default/templates/login.html.ep:12
|
||||
msgid "Password"
|
||||
msgstr "Contraseña"
|
||||
|
||||
#: themes/default/templates/zip.html.ep:12
|
||||
msgid "Please click on each URL to download the different zip files."
|
||||
msgstr "Haga clic en cada URL para descargar los diferentes archivos zip."
|
||||
|
||||
#. (config('contact')
|
||||
#: themes/default/templates/about.html.ep:19
|
||||
msgid "Please contact the administrator: %1"
|
||||
msgstr "Por favor, contacte con el administrador: %1"
|
||||
|
||||
#: lib/Lutim/Controller/Authent.pm:36
|
||||
msgid "Please, check your credentials: unable to authenticate."
|
||||
msgstr "Por favor, verifique sus credenciales: no se puede autenticar."
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:43
|
||||
msgid "Previous (arrow left)"
|
||||
msgstr "Anterior (flecha izquierda)"
|
||||
|
||||
#: themes/default/templates/index.html.ep:46 themes/default/templates/index.html.ep:49 themes/default/templates/myfiles.html.ep:96 themes/default/templates/myfiles.html.ep:99
|
||||
msgid "Random image link"
|
||||
msgstr "Enlace de imagen aleatoria"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:22
|
||||
msgid "Raw stats"
|
||||
msgstr "Estadísticas sin procesar"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:52
|
||||
msgid "Save changes"
|
||||
msgstr "Guardar los cambios"
|
||||
|
||||
#: themes/default/templates/index.html.ep:176
|
||||
msgid "Send an image"
|
||||
msgstr "Enviar una imagen"
|
||||
|
||||
#: themes/default/templates/login.html.ep:16 themes/default/templates/logout.html.ep:5 themes/default/templates/partial/navbar.html.ep:37
|
||||
msgid "Signin"
|
||||
msgstr "Acceder"
|
||||
|
||||
#: themes/default/templates/index.html.ep:151 themes/default/templates/partial/gallery.js.ep:211 themes/default/templates/partial/lutim.js.ep:176
|
||||
msgid "Something bad happened"
|
||||
msgstr "Algo malo ha pasado"
|
||||
|
||||
#. ($c->config('contact')
|
||||
#: lib/Lutim/Controller/Image.pm:763
|
||||
msgid ""
|
||||
"Something went wrong when creating the zip file. Try again later or contact "
|
||||
"the administrator (%1)."
|
||||
msgstr ""
|
||||
"Algo malo ha pasado. Inténtelo de nuevo más tarde o contacte con el "
|
||||
"administrador (%1)."
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:55
|
||||
msgid "Support the author"
|
||||
msgstr "Apoya al autor"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:63
|
||||
msgid "Support the author on Liberapay"
|
||||
msgstr "Apoya al autor en Liberapay"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:60
|
||||
msgid "Support the author on Tipeee"
|
||||
msgstr "Apoya al autor en Tipeee"
|
||||
|
||||
#: themes/default/templates/about.html.ep:13
|
||||
msgid ""
|
||||
"The IP address of the image's sender is retained for a delay which depends "
|
||||
"of the administrator's choice (for the official instance, which is located "
|
||||
"in France, it's one year)."
|
||||
msgstr ""
|
||||
"La dirección IP del remitente de la imagen se retiene durante un tiempo, que "
|
||||
"depende de lo que elija el administrador (para la instancia oficial, que "
|
||||
"está localizada en Francia, es un año)."
|
||||
|
||||
#: themes/default/templates/about.html.ep:23
|
||||
msgid ""
|
||||
"The Lutim software is a <a href=\"http://en.wikipedia.org/wiki/"
|
||||
"Free_software\">free software</a>, which allows you to download and install "
|
||||
"it on you own server. Have a look at the <a href=\"https://www.gnu.org/"
|
||||
"licenses/agpl-3.0.html\">AGPL</a> to see what you can do."
|
||||
msgstr ""
|
||||
"El software Lutim es <a href=\"http://es.wikipedia.org/wiki/"
|
||||
"Software_libre\">software libre</a>, lo que le permite descargarlo e "
|
||||
"instalarlo en su propio servidor. Eche un vistazo a la licencia <a href="
|
||||
"\"https://www.gnu.org/licenses/agpl-3.0.html\">AGPL</a> para ver qué puede "
|
||||
"hacer."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:347
|
||||
msgid "The URL is not valid."
|
||||
msgstr "La URL no es válida."
|
||||
|
||||
#: themes/default/templates/zip.html.ep:16
|
||||
msgid ""
|
||||
"The automatic download process will open a tab in your browser for each link."
|
||||
" You need to allow popups for Lutim."
|
||||
msgstr ""
|
||||
"El proceso de descarga automática abrirá una pestaña en su navegador para "
|
||||
"cada enlace. Debe permitir ventanas emergentes para Lutim."
|
||||
|
||||
#: themes/default/templates/partial/myfiles.js.ep:32
|
||||
msgid "The data has been successfully imported."
|
||||
msgstr "Los datos se han importado correctamente."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:160 lib/Lutim/Controller/Image.pm:228
|
||||
msgid "The delete token is invalid."
|
||||
msgstr "El código de borrado no es válido."
|
||||
|
||||
#. ($upload->filename)
|
||||
#: lib/Lutim/Controller/Image.pm:488
|
||||
msgid "The file %1 is not an image."
|
||||
msgstr "El archivo %1 no es una imagen."
|
||||
|
||||
#. ($tx->res->max_message_size)
|
||||
#. ($c->req->max_message_size)
|
||||
#. (config('max_file_size')
|
||||
#: lib/Lutim/Controller/Image.pm:311 lib/Lutim/Controller/Image.pm:380 themes/default/templates/partial/lutim.js.ep:249
|
||||
msgid "The file exceed the size limit (%1)"
|
||||
msgstr "El archivo supera el límite de tamaño (%1)"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:12
|
||||
msgid "The graph's datas are not updated in real-time."
|
||||
msgstr "Los datos del gráfico no se actualizan en tiempo real."
|
||||
|
||||
#. ($image->filename)
|
||||
#: lib/Lutim/Controller/Image.pm:230
|
||||
msgid "The image %1 has already been deleted."
|
||||
msgstr "La imagen %1 ya se ha borrado."
|
||||
|
||||
#. ($image->filename)
|
||||
#: lib/Lutim/Controller/Image.pm:239 lib/Lutim/Controller/Image.pm:244
|
||||
msgid "The image %1 has been successfully deleted"
|
||||
msgstr "La imagen %1 se ha borrado correctamente"
|
||||
|
||||
#: themes/default/templates/index.html.ep:63
|
||||
msgid "The images are encrypted on the server (Lutim does not keep the key)."
|
||||
msgstr "Las imágenes se cifran en el servidor (Lutim no guarda la clave)."
|
||||
|
||||
#: themes/default/templates/about.html.ep:5
|
||||
msgid ""
|
||||
"The images you post on Lutim can be stored indefinitely or be deleted at "
|
||||
"first view or after a delay selected from those proposed."
|
||||
msgstr ""
|
||||
"Puede, opcionalmente, solicitar que la imagen publicada en Lutim se elimine "
|
||||
"con la primera vista (o descarga) o tras un tiempo seleccionado de entre "
|
||||
"varios propuestos."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:168
|
||||
msgid "The image’s delay has been successfully modified"
|
||||
msgstr "Se ha modificado correctamente el tiempo de la imagen"
|
||||
|
||||
#: themes/default/templates/partial/gallery.js.ep:277
|
||||
msgid "There is XXXX image(s) in the gallery"
|
||||
msgstr "Hay imágenes XXXX en la galería"
|
||||
|
||||
#. ($c->config->{contact})
|
||||
#: lib/Lutim/Controller/Image.pm:485
|
||||
msgid "There is no more available URL. Retry or contact the administrator. %1"
|
||||
msgstr ""
|
||||
"No más URL disponibles. Inténtelo de nuevo o contacte con el administrador. "
|
||||
"%1"
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:30
|
||||
msgid "Toggle fullscreen"
|
||||
msgstr "Bascular la pantalla completa"
|
||||
|
||||
#: themes/default/templates/partial/navbar.html.ep:8
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Extiender la barra de navegación"
|
||||
|
||||
#: lib/Lutim/Command/cron/stats.pm:162 themes/default/templates/raw.html.ep:11
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: themes/default/templates/index.html.ep:78 themes/default/templates/partial/lutim.js.ep:17
|
||||
msgid "Tweet it!"
|
||||
msgstr "¡Tuitéalo!"
|
||||
|
||||
#: themes/default/templates/partial/common.js.ep:110 themes/default/templates/partial/common.js.ep:90
|
||||
msgid "Unable to copy to clipboard"
|
||||
msgstr "imposible copiar al portapapeles"
|
||||
|
||||
#. ($short)
|
||||
#: lib/Lutim/Controller/Image.pm:108 lib/Lutim/Controller/Image.pm:202 lib/Lutim/Controller/Image.pm:273
|
||||
msgid "Unable to find the image %1."
|
||||
msgstr "No se ha podido encontrar la imagen %1."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:574 lib/Lutim/Controller/Image.pm:619 lib/Lutim/Controller/Image.pm:660 lib/Lutim/Controller/Image.pm:703 lib/Lutim/Controller/Image.pm:715 lib/Lutim/Controller/Image.pm:726 lib/Lutim/Controller/Image.pm:753 lib/Lutim/Plugin/Helpers.pm:88
|
||||
msgid "Unable to find the image: it has been deleted."
|
||||
msgstr "No se ha podido encontrar la imagen: ha sido borrada."
|
||||
|
||||
#: lib/Lutim/Controller/Image.pm:145
|
||||
msgid "Unable to get counter"
|
||||
msgstr "Imposible recuperar el contador"
|
||||
|
||||
#: themes/default/templates/about.html.ep:17
|
||||
msgid ""
|
||||
"Unlike many image sharing services, you don't give us rights on uploaded "
|
||||
"images."
|
||||
msgstr ""
|
||||
"A diferencia de muchos servicios de compartición de imágenes, usted no cede "
|
||||
"los derechos de las imágenes que sube."
|
||||
|
||||
#: themes/default/templates/index.html.ep:180 themes/default/templates/index.html.ep:219
|
||||
msgid "Upload an image with its URL"
|
||||
msgstr "Subir una imagen con la URL"
|
||||
|
||||
#: themes/default/templates/myfiles.html.ep:125
|
||||
msgid "Uploaded at"
|
||||
msgstr "Enviado el"
|
||||
|
||||
#: themes/default/templates/stats.html.ep:6
|
||||
msgid "Uploaded files by days"
|
||||
msgstr "Archivos enviados por día"
|
||||
|
||||
#. ($c->app->config('contact')
|
||||
#: lib/Lutim/Plugin/Helpers.pm:221
|
||||
msgid ""
|
||||
"Uploading is currently disabled, please try later or contact the "
|
||||
"administrator (%1)."
|
||||
msgstr ""
|
||||
"La carga está deshabilitada en estos momentos, por favor inténtelo más tarde "
|
||||
"o contacte con el administrador (%1)."
|
||||
|
||||
#: themes/default/templates/index.html.ep:83 themes/default/templates/index.html.ep:85 themes/default/templates/myfiles.html.ep:122 themes/default/templates/partial/lutim.js.ep:72 themes/default/templates/partial/lutim.js.ep:76
|
||||
msgid "View link"
|
||||
msgstr "Enlace de visualización"
|
||||
|
||||
#: themes/default/templates/about.html.ep:22
|
||||
msgid "What about the software which provides the service?"
|
||||
msgstr "¿Y qué hay sobre el software que proporciona el servicio?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:3
|
||||
msgid "What is Lutim?"
|
||||
msgstr "¿Qué es Lutim?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:15
|
||||
msgid "Who owns rights on images uploaded on Lutim?"
|
||||
msgstr "¿Quién posee los derechos de las imágenes que se suben a Lutim?"
|
||||
|
||||
#: themes/default/templates/about.html.ep:12
|
||||
msgid ""
|
||||
"Yes, it is! On the other side, for legal reasons, your IP address will be "
|
||||
"stored when you send an image. Don't panic, it is normally the case of all "
|
||||
"sites on which you send files!"
|
||||
msgstr ""
|
||||
"¡Sí, lo es! Por otro lado, por razones legales, se almacena la dirección IP "
|
||||
"cuando se envía una imagen. Que no cunda el pánico, ¡es el caso habitual "
|
||||
"para todos los sitios a los cuales se envían ficheros!"
|
||||
|
||||
#: themes/default/templates/about.html.ep:10
|
||||
msgid ""
|
||||
"Yes, it is! On the other side, if you want to support the developer, you can "
|
||||
"do it via <a href=\"https://www.tipeee.com/fiat-tux\">Tipeee</a> or via <a "
|
||||
"href=\"https://liberapay.com/sky/\">Liberapay</a>."
|
||||
msgstr ""
|
||||
"¡Sí, lo es! Por otro lado, si quiere ayudar a apoyar al desarrollador, puede "
|
||||
"hacerlo vía <a href=\"https://www.tipeee.com/fiat-tux\">Tipeee</a> o con <a "
|
||||
"href=\"https://liberapay.com/sky/\">Liberapay</a>."
|
||||
|
||||
#: themes/default/templates/zip.html.ep:6
|
||||
msgid "You asked to download a zip archive for too much files."
|
||||
msgstr "Solicitó descargar un archivo zip para demasiados archivos."
|
||||
|
||||
#: themes/default/templates/about.html.ep:8
|
||||
msgid ""
|
||||
"You can, optionally, request that the image(s) posted on Lutim to be deleted "
|
||||
"at first view (or download) or after the delay selected from those proposed."
|
||||
msgstr ""
|
||||
"Puede, opcionalmente, solicitar que la imagen publicada en Lutim se elimine "
|
||||
"con la primera vista (o descarga) o tras un tiempo seleccionado de entre "
|
||||
"varios propuestos."
|
||||
|
||||
#: lib/Lutim/Controller/Authent.pm:27
|
||||
msgid "You have been successfully logged in."
|
||||
msgstr "Te has identificado con éxito."
|
||||
|
||||
#: lib/Lutim/Controller/Authent.pm:66 themes/default/templates/logout.html.ep:3
|
||||
msgid "You have been successfully logged out."
|
||||
msgstr "Ha sido desconectado con éxito."
|
||||
|
||||
#: themes/default/templates/gallery.html.ep:31
|
||||
msgid "Zoom in/out"
|
||||
msgstr "Zoom aumentar / reducir"
|
||||
|
||||
#: themes/default/templates/about.html.ep:27
|
||||
msgid "and on"
|
||||
msgstr "y en"
|
||||
|
||||
#: themes/default/templates/about.html.ep:40
|
||||
msgid "arabic translation"
|
||||
msgstr "Traducción al árabe"
|
||||
|
||||
#: themes/default/templates/about.html.ep:27
|
||||
msgid "core developer"
|
||||
msgstr "desarrollador principal"
|
||||
|
||||
#: lib/Lutim.pm:348 lib/Lutim/Command/cron/stats.pm:157 lib/Lutim/Command/cron/stats.pm:171 lib/Lutim/Command/cron/stats.pm:188 themes/default/templates/index.html.ep:3 themes/default/templates/myfiles.html.ep:3 themes/default/templates/partial/raw.js.ep:21 themes/default/templates/partial/raw.js.ep:4 themes/default/templates/raw.html.ep:6
|
||||
msgid "no time limit"
|
||||
msgstr "Sin tiempo límite"
|
||||
|
||||
#: themes/default/templates/about.html.ep:38
|
||||
msgid "occitan translation"
|
||||
msgstr "traducción al occitano"
|
||||
|
||||
#: themes/default/templates/about.html.ep:27
|
||||
msgid "on"
|
||||
msgstr "en"
|
||||
|
||||
#: themes/default/templates/about.html.ep:39
|
||||
msgid "paste image to upload ability"
|
||||
msgstr "posibilidad de pegar una imagen para subirla (upload)"
|
||||
|
||||
#: themes/default/templates/about.html.ep:41
|
||||
msgid "russian translation"
|
||||
msgstr "traducción rusa"
|
||||
|
||||
#: themes/default/templates/about.html.ep:36
|
||||
msgid "spanish translation"
|
||||
msgstr "traducción al español"
|
||||
|
||||
#: themes/default/templates/about.html.ep:28
|
||||
msgid "webapp developer"
|
||||
msgstr "desarrollador de la webapp"
|
||||
|
||||
#: themes/default/templates/about.html.ep:24
|
||||
msgid "For more details, see the <a href=\"https://framagit.org/fiat-tux/hat-softwares/lutim\">homepage of the project</a>."
|
||||
msgstr ""
|
||||
"Para más detalles, vea la <a href=\"https://framagit.org/fiat-tux/"
|
||||
"hat-softwares/lutim\">página del proyecto</a>."
|
||||
|
||||
#: themes/default/templates/index.html.ep:171 themes/default/templates/index.html.ep:215
|
||||
msgid "Tiling watermark"
|
||||
msgstr "Marca de agua en mosaico"
|
||||
|
||||
#: themes/default/templates/index.html.ep:174 themes/default/templates/index.html.ep:218
|
||||
msgid "Single watermark"
|
||||
msgstr "Marca de agua única"
|
||||
|
||||
#: themes/default/templates/index.html.ep:177 themes/default/templates/index.html.ep:221
|
||||
msgid "No watermark"
|
||||
msgstr "Sin marca de agua"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user