Compare commits

...

122 Commits

Author SHA1 Message Date
Henrique Dias
121c695c1a Version 1.4.3
Former-commit-id: 105f6e3a206963eb810ed178f5a3d05e4c05693f [formerly 95a0eef30242460e822aef2f93ec770f6d82702d] [formerly bb726ba819c7f01330db8dc7503b2f869ace40c0 [formerly 74f6ea6897]]
Former-commit-id: 9db1edc8d09521068b70bc7b81e5d2586ef949a8 [formerly e9e78572d9b296f160b5c311e57822a0e960d35a]
Former-commit-id: 4513e831114a23e30822734d62191a7a49d50555
2018-01-05 10:10:40 +00:00
Henrique Dias
6a1f9bbf69 [ci skip] auto: setting untracked version
Former-commit-id: 500c3e1726327fca7ca8421d70f46eebe76702bf [formerly 09a3811e6a1b87b159fe6ab562f968aef1eeef20] [formerly e85343d82a8de4c9b93f1d5e65e8654e85b67f5b [formerly 13c12b9130]]
Former-commit-id: 26e2c13658759ee83e6d2ec7d851a1cfc2b485c7 [formerly 4eb961b41d768ff23b4d00163f4c0e9512829fbd]
Former-commit-id: d6a362c2611bf9ef6267be25a08e47a8d7f58b1e
2018-01-05 10:02:58 +00:00
Henrique Dias
ab54bc3c57 Version 1.4.3
Former-commit-id: f47b39a9b2817c603578b2623919403e30d3af22 [formerly a8b23dd12fba701c0cbe91049f263318d33a8be9] [formerly 7de9b5f8f6d1c6a962b89c540625b68cd4cbac0a [formerly ac3f1482c6]]
Former-commit-id: 56c305fb8ea1a017daf051ce8f630c2ffddebb7b [formerly bcd1d113a852499dee8701bd64e49df8513f8538]
Former-commit-id: 95ca9218c8b98432f9c5e0019fe619c0dd417e34
2018-01-05 10:02:49 +00:00
Henrique Dias
8a10baa815 Update .goreleaser.yml
Former-commit-id: dce13b52b52d980f11ef90f08b229c99ef74548c [formerly 45b6e868899a14fad61e61b5603da5dbdf1704fc] [formerly 90aabc167231a2d164afee7ab966a465146afe63 [formerly d8f057757e]]
Former-commit-id: 43bae9e293b2e3cf0b8608c344dadff88de47380 [formerly beff2d7f954169e917950549a353f5efa033f906]
Former-commit-id: e77e853b8e818863bff7164b169b411eaa6732fe
2018-01-04 14:04:09 +00:00
Henrique Dias
98c22ece8d [ci skip] auto: setting untracked version
Former-commit-id: 8ddd21218dd4fb6e00cf083a80199d2a4339af2f [formerly 3406ebe12f6112ed751092e5f831c77ce459c6e0] [formerly 143f9d23f5fa5e5545fd63e6f44ae9228e6c093c [formerly 5bd60ce05a]]
Former-commit-id: d7362d24d7d872cb722d462fab45d2a48b37b1d5 [formerly c7c1cfd6d43642315e9a7956211ede7eb0a6b5e3]
Former-commit-id: c42269de8c2b7ecbdacaca0cf383c8a29dfd239b
2018-01-04 13:50:31 +00:00
Henrique Dias
63247f6e8c Version 1.4.2
Former-commit-id: 488d6575d81275f6c5e1c04280a5def6aa700117 [formerly fb198ae1767fc52ed8492657f33f5bd91d4d4283] [formerly b100a438db05a237c2e7a081fbdaed3a5ed6ebae [formerly e7b372f548]]
Former-commit-id: 3c418fc25a996e829e35be8f49eec0c43bf1829d [formerly 6568a1dd89e6a5640a2ad9fde224303813f219d0]
Former-commit-id: a1355be166395d2bc0d0a4a5a7170b36c73df094
2018-01-04 13:50:19 +00:00
Henrique Dias
58dd812008 fix #269
Former-commit-id: a0b8ed91061292e0f155cee9a7865609daacf836 [formerly e6bb9eaa82b5704f3fe29bdec50f6bb4f55304b2] [formerly b443cbf5c6d7946ff3cfa44493449b5f0d75d830 [formerly 02df167434]]
Former-commit-id: c193caff6faaf78a16375960893be9e253869d3c [formerly 6c84d234c9e1db432027bc159dbeeb77e9c35031]
Former-commit-id: a48c3851511472efc47bce07ee2509fdd8a44c4a
2018-01-04 13:49:41 +00:00
Henrique Dias
1575fafec8 [ci skip] auto: setting untracked version
Former-commit-id: c497913d120be4a71f7d2305600e2785190f0e2b [formerly 0126f289afd78bc006e5e0fe383670dbc9df4fd4] [formerly 12899a40ac7b8a94c3930dcdb3101d121c56cbc2 [formerly 9da06a7010]]
Former-commit-id: 866590120ccd5ddc189d36e352c637cabc82f81f [formerly af0b154bf6db52c01e9d0b10f3d6678319727bca]
Former-commit-id: ff3c428b12cc8028b21d8585a9927e826ebdf4cb
2018-01-04 12:41:34 +00:00
Henrique Dias
8c935eb51d Version 1.4.1
Former-commit-id: 77ee0108106777f2de0719eb6ea50bcad12f8635 [formerly 4dc3acf5b7eaaa687ccfced7fc463e2b7e7104ea] [formerly 3ec2bd1fe0b8e364621204b9aa1732cbc7828199 [formerly 44d6730073]]
Former-commit-id: efd15158f953966190c8a8e9310801a89049ea4e [formerly 56a49a89f919d70486b6db126e84e5997bd6d261]
Former-commit-id: 969b87ac24214cb7dfc9429253724c68aa0d46bc
2018-01-04 12:41:23 +00:00
Henrique Dias
7f3a26e1c0 fix: error when user have no commands; close #293
Former-commit-id: 9e1ebf686f12d76a202bf8a3b0b16f14f8eb713d [formerly bfc761cc181d6d8880fd86396e69bf5894806f6c] [formerly 774e09b49a2dff07becff48bf048b3b0bd8353ed [formerly 65d3c0fb9b]]
Former-commit-id: 8ab97c0103cde1a9bcf69c1e9af994241fb9d124 [formerly a25dc7feb0755122c7329e3a3bfa61f74418467b]
Former-commit-id: 92e21139ffe04ab23406352e0a5b6131e8cad084
2018-01-04 12:40:45 +00:00
Henrique Dias
fe834aa244 remove file
Former-commit-id: 95f665f00dc03c130afb5fc45e8076a869c30222 [formerly acae04da4a0d58e689b704ff405a749c4fa43c05] [formerly 482666a8335cc216fa706d5175c8a75cbdb073df [formerly 0781b961da]]
Former-commit-id: 57a2f201668aad631fead876207b2d5620e202dc [formerly 9f1344480aa6589652ce34dff2496dc36b9d631c]
Former-commit-id: 2760b12b251f53c4421b80d5d5122520eb92cf40
2017-12-29 17:31:21 +00:00
Henrique Dias
6620a1bf41 [ci skip] auto: setting untracked version
Former-commit-id: 0b2303648bf1c82d04b20f439a76d2ce37cfbc4c [formerly 8aedb0827d0538efa6ec0d88082e3df2e85b6e46] [formerly 91dc35e45674e83e44ecb66e7da2bb1d7634efb4 [formerly d431f5f0e4]]
Former-commit-id: 551cf4271e33f7893ecbd7290074d0d031827a94 [formerly 034d533f73603df46d2c7efccaddd17bb0f37814]
Former-commit-id: 1cf3cbcb484e5128ac9564516992f5fbdaddfc28
2017-12-29 16:46:22 +00:00
Henrique Dias
dab08325b7 Version 1.4.0
Former-commit-id: 368d4ecf114fb68ca038e4baf3d840afb2636ba0 [formerly 42d7e7c749edda5dc525f86e775c999b69dd0343] [formerly edb264480cef71783fa0ec799c667710b48c0e76 [formerly 0848d5b135]]
Former-commit-id: db6356cdcaf40265c31e4aa08c88614d2aa1f886 [formerly a52ba320deffe84af53facdba3606b9d032118ea]
Former-commit-id: 6ee806954d2456421fe737541dfee136df93f54e
2017-12-29 16:46:12 +00:00
Henrique Dias
ddb4685257 Update .goreleaser.yml
Former-commit-id: 66c518285185e0a94bd1ab865676e3e5f409b76f [formerly 906313ef1a1747ce137bcbe7d54a20244b04ced9] [formerly 4724da443d609b6ef27851c2b79eadb9d7cefa4f [formerly 2fc342a3e9]]
Former-commit-id: 90b7247bd50078d5321c9dc1c51149f88c63989e [formerly ba9587d799234e3cd1c2888841fa6fc12227806c]
Former-commit-id: 9b9383f2cbb64775944dc68d882454d73b406246
2017-12-29 16:45:21 +00:00
Henrique Dias
39dbca3b15 [ci skip] auto: setting untracked version
Former-commit-id: ea04ce9843fafcc33961d3aacd5f31b5f93d1a08 [formerly d97fa13252b9201ea17bb9aa188b7f93b03d3f6a] [formerly c647d2080414bccf957ffb713cf029219780f606 [formerly 2b9cf83feb]]
Former-commit-id: 82f4a48d20d439257a70e49726bc26ddbd259c58 [formerly 8d3787cabd875c3ee84044051e5f97fbf1922585]
Former-commit-id: 45a6d0ca105b6b9591928d69db81c7c7777b62df
2017-12-29 16:35:29 +00:00
Henrique Dias
a02bdfc8d5 Version 1.4.0
Former-commit-id: 3a8e072916c7ce76104d67e805c202cb95568c41 [formerly 6c77d93905851193699f1651653de748fc8f0806] [formerly 8563da03483dfdc77cb65736deb60084ae76df40 [formerly d98b58e0f0]]
Former-commit-id: 745caf9deb45ed766f088d7a52aa992191960a40 [formerly d2e6c7c774dc6b58b9406081358730f4dc5c573d]
Former-commit-id: 37ba109c46b21bdff083ee63451cb54bc3243e3e
2017-12-29 16:33:50 +00:00
Henrique Dias
f88721cc83 chore: add more build platforms
Former-commit-id: d3442dce2a31d700cc0778229819ee3821bdcc19 [formerly 218c9d7fb7fa28546797c50d03956362829407a2] [formerly 6abbb1b814a037c106526506ee1ca999dae54914 [formerly 3217751cc3]]
Former-commit-id: 4fb0fecd3b19b145369d751f454b5c079163c9f4 [formerly c428f4e11ce84ca60ed7c2d968da9c71c1f23d11]
Former-commit-id: f22d06e1d43a9eb68845aa2490c72b187eba0018
2017-12-29 16:32:32 +00:00
Henrique Dias
95544bfed5 fix default view mode on caddy plugins
Former-commit-id: 41e6e855b611449829b4aaab2381685da8b94bb0 [formerly cfcd8802666a8869a2e2f8229d86fee05e088d4e] [formerly 72b0f44e1bf1ab0b27cf327cfac6977049e2dacf [formerly 258557fb57]]
Former-commit-id: e17e5ed75665cd53ec8a20b2833268de5ffa5e47 [formerly 5b8571fd62e0517f1daa72fbb95d7c99ba120c6b]
Former-commit-id: 39ebfa5155c60f71e9f565de875594c3b07b8bac
2017-12-29 08:55:03 +00:00
Henrique Dias
999ee1c2f5 [ci skip] auto: setting untracked version
Former-commit-id: c555833bd28140ffed26dd56d5d1f03ccdefe8f9 [formerly 19be792ade9e4df3c4cc0765d819be35269803b9] [formerly 190d7e277e3d94b22af59288b0403c1e6055ca58 [formerly dc1f51cfa9]]
Former-commit-id: d270e36617e94f506828700bab84310aa6199750 [formerly c414ab0a208bc6fa5a4236bd269d42c2fab3ca8c]
Former-commit-id: a82ebba0e51012db7689b41f81c6cbcfdac9accb
2017-12-27 16:20:03 +00:00
Henrique Dias
a61ade62e5 Version 1.3.12
Former-commit-id: cebe84ae6a9b87fec49fd1b2966f58174f49b616 [formerly d8fc91407b0a3cc7616532f14e5ae9b8af49ca11] [formerly d52eac19847da77e49817878f2930c8f438af49a [formerly cec257787b]]
Former-commit-id: c8913636186c289c615ee29d0b36998484d39023 [formerly 67b4f850b4452112f63f2b10e6ae9f9e8848d26b]
Former-commit-id: 45593be374fb4c925a93c26e7ee1288e53c3abf4
2017-12-27 16:19:51 +00:00
Henrique Dias
08de5efeb4 fix: stream file instead of putting it all in memory (#303)
Former-commit-id: 2dc1092f4111870eda6fd51f06cf6dadcac27c78 [formerly 6b21e8ca91b69a70aa07a60099e6919cf1f29fff] [formerly 5860c903ca50aa9a29f91c98d144e859fa223977 [formerly e6a8e3349e]]
Former-commit-id: f8923f5d3fa5d638170ccd870ed4d38abee42d2e [formerly 1a1b27bb606ea012bb5eb3ee42ab6f1d585e1490]
Former-commit-id: f7a238d6ed3e61b925fa20e7ecca145b1f903dba
2017-12-27 16:18:27 +00:00
Henrique Dias
74f690a71b fix: allow user not to write .md or .markdown when creating file based on archetype #288
Former-commit-id: e2bc376cc78905e9d85596bcb281b1f52647b4fb [formerly d4c44f8625bda57d5659366d2651d8021776c9d2] [formerly 766fc98ad54327498076992a110415750d059bd2 [formerly 390fe1d797]]
Former-commit-id: 6c30496fab170a7a39eacba39827e054089f64ad [formerly 8b3aac4b83339ec9722ddadc3fe225e3753afd94]
Former-commit-id: 85e39aa259af1e628e77d488fbd9c7ea5d207eb7
2017-12-27 16:02:36 +00:00
Henrique Dias
1d4a3005ff fix: hugo full path in URL #288 (#306)
Former-commit-id: 56143acfb6b2817df49998c96f9b70174327c31c [formerly b763216262fd57a06d7f343a96891cc62f5ba358] [formerly 8a09d84e73ce9e3865bcd65a8071f2e5cb48333f [formerly 68c4072bd1]]
Former-commit-id: cbec5adf02b6d83b27c7286546a7a291b2344256 [formerly 94dbfbb4aaf51abcd809f3562a4a8a53945782f5]
Former-commit-id: 3f11f9c8be4f7e9b063789fca73212ca838ce1ad
2017-12-27 16:00:16 +00:00
Henrique Dias
7447a530ee Close #289
Former-commit-id: 6145bac21a69ec5af8b0c9d6f5a343ab66c32c74 [formerly 13a045c4cc6852e88af60b359653032590c8cb48] [formerly 1b279454432546ba44702b792ec83eec00f63a1f [formerly b1c36ec71f]]
Former-commit-id: ef359bdf00c3c64d4877f02de3d8cb505bd6448c [formerly b4f09c805cd6a0f5300a1e608d024ce7af6b60db]
Former-commit-id: 009fb85389f234e1d5f7fc726e1d983fed3b5832
2017-12-06 22:30:49 +00:00
Henrique Dias
fa9396f0f4 [ci skip] auto: setting untracked version
Former-commit-id: b508f843299648b61849e33273041df46c2ed31e [formerly 9e153dd2ff31f0bb769e35f70c2bdba656bf449f] [formerly 864e40ed1299bf717242ddc1556d3f3ccbfc2f89 [formerly 6856716adb]]
Former-commit-id: 88b556096518c23d2db8a26c9c448400172d30b7 [formerly 9b9d9539c8ebf8c4ff5a2288ca04ade387a84351]
Former-commit-id: 625b4ab5af200087a85db6e1402c9bc4a0295d83
2017-12-04 16:10:27 +00:00
Henrique Dias
172bbb1828 Version 1.3.11
Former-commit-id: 4a93c06ece303164400a26def8508b415ce8c906 [formerly cb7e56f73da93ce59e63b74de61a384d49335991] [formerly ee5c57615221b3b91660df2146845b413c972975 [formerly 506570530f]]
Former-commit-id: 3b120ed2a0b36e84dc258862663421240866601a [formerly 641b5b688bb500b711cc4b14f4eec48c10c23d6c]
Former-commit-id: 965b1316ab20445fda0d3b64c45229c98496534f
2017-12-04 16:10:15 +00:00
Henrique Dias
410aa5d9da Set correct indentation
Former-commit-id: 0088ec263439da995dd6f42d0e2ad9b0b42c7c98 [formerly c5232aa97864225d432f04d5d83e0215edff18c9] [formerly d038a6f63e089003de83bbb054d76d3953cab33b [formerly 0dac28fbb0]]
Former-commit-id: 44d9d716ddd2eed8fcfa7bcb92f5f46475ab0afc [formerly 9f9d3a6c0bec92f75b6ad6932e6b1eebb9ddefdf]
Former-commit-id: 9e2919024770313c9ed94c1bc9060f2e45871f77
2017-12-04 16:08:24 +00:00
Iñigo
736cef7127 Add Spanish translation (#291)
* Add Spanish

* Add Spanish


Former-commit-id: 52479d923ca4a91d3cd8b627246de8b70c3a5152 [formerly 53bb5278073dd5abdd96d04b9423811ae78637e9] [formerly fe82800194e49c99d278413e79f8c007bf4dfab8 [formerly 78c68ce2f3]]
Former-commit-id: e1314f8d7a5ae78cfbd3727339016d6b322c4aa5 [formerly a0a6d84f266f512c5f9eaa9c92d0f7b57f447367]
Former-commit-id: 6a557d9ab0e82332b201534276e1c032e682714e
2017-12-04 15:49:56 +00:00
Henrique Dias
f4ceb7163e Update file.go
Former-commit-id: 713c809c9314b3db947fd33e5fae69bbe0058865 [formerly 4dd6de8cddb93b1553a6d768db426ec3eb1f0b53] [formerly c5033b6ff486b786afd6b75e41ff35c3a8c69e47 [formerly a34422de57]]
Former-commit-id: 1028ebd6474ce1e257ef1f81c9f805c74f64994e [formerly 5f2db5f3060c457ff2af82e0e0be89a25d1cbc4d]
Former-commit-id: 609be343302e0a49190ef6f37ee677116e9baa04
2017-11-30 18:22:16 +00:00
Henrique Dias
7bd4fbc0cb Add Badge and Update Deps
Former-commit-id: 8d3cf568a2f901ec8370a5f7aaaf0aae95425353 [formerly af950ac5e3ddc57c599b62937ed8872bb653f426] [formerly 3b9e1a7c8d9ea35b10eec9a82fad1221830ae5b6 [formerly 1f62c26e81]]
Former-commit-id: 31e0e2de46b44cb85f4423be7064f4d6d9552ef9 [formerly 431ed7d219cfec8b98fbe4423ee66c23e7849b74]
Former-commit-id: a851d7f44f290616d042542edbcadf35f856d73a
2017-11-21 19:17:19 +00:00
Henrique Dias
2ed4658369 Update assets
Former-commit-id: 921770105b3b607f2681112d204130a0655248b9 [formerly cb7d152432980c20f5d5cda743992b6a0684ed0e] [formerly e63e514df0f701934dad6744d79c7b7746c372e8 [formerly ec30606dcc]]
Former-commit-id: 725b6820ac82bfab769306f1fc92acb3f690f366 [formerly 56b2b925d513261885fbdc7cc0d3662f9576d56a]
Former-commit-id: aeaeb164f0c2c7579778ae1bd587806b69868c3c
2017-11-19 11:22:54 +00:00
Henrique Dias
735312982c Update Dependencies
Former-commit-id: 76bedea1c27cf4e87cb9255786de3d73e6abd29e [formerly 8c21d31b2c5a22d96f3a7a9042dcd58077d7dcef] [formerly b335f0ac2e16fdc8042255ef1d0dbb421c4b09d4 [formerly 02b2bb6215]]
Former-commit-id: dbc8cfd8755959d7d7f40976c2641969e08cdb5a [formerly 29b617fd9461a72b0d0ab729a6a1d7247c5ec74e]
Former-commit-id: 23a7b57755f5fea3e0d62450e6466267227fb865
2017-11-19 11:05:05 +00:00
Henrique Dias
4ddb3f5a34 Improve file extension checking performance
Former-commit-id: 30aaff2f0bee9dd9743d4acd6a68aafc90addef9 [formerly 967fc0b47a5fb0d1b5e79a8b926f4fdd61b8477a] [formerly 5eaecd069cd3de9ebb5e3e6d0feb04844fc473c3 [formerly c76c69c256]]
Former-commit-id: 11d0c1ae276859eccb7949d96dedbd0cd3204ffd [formerly 63de350fea7eb0a6d621b029c9c8866866462d31]
Former-commit-id: 422edd1298a7ba86dd958dcc7a513a0a3e6d83a3
2017-11-15 21:03:50 +00:00
Henrique Dias
c947228ac4 Whitelist yml and tml extensions
Former-commit-id: aed7606c12fa705954e650ba948d891329d66dff [formerly efd4415075000737c5dc7bb5a38a78605989e05c] [formerly 5656bb7bc09285720f094b3054c70994b51aa658 [formerly 8f5eaee952]]
Former-commit-id: 9d774b10c85ae9456a324030f9b7b0c06e600db4 [formerly 9c7919b5e027d829364fcb658b6b9579caad51a3]
Former-commit-id: 3196b0ea7f550057e411410e083a8c4f8e748240
2017-11-15 20:38:30 +00:00
Henrique Dias
505af7d9d7 Update auth.go
Former-commit-id: e2a955c377393341ac3ccf08d2fba9e3359e72b0 [formerly aaf783e1c2aec83c5e0a4a016f98b9b17ece09c2] [formerly a3be55338e9d4403ae3266cba5ac2451aff507fc [formerly 1f946bd94e]]
Former-commit-id: 53e720dcdc8745aa5de632a5d2676ea1c0648445 [formerly 7069f0e163c34e1890d344066ba570dde674a414]
Former-commit-id: 2aee9f0b0ba181bde240bdcf098f3914c3f512b0
2017-11-15 09:09:09 +00:00
Henrique Dias
2c13ac4ac1 Remove fmt.println from code
Former-commit-id: 6bdb55590fb58a6edc64a3ca946a13ee35f3ad60 [formerly 60939e561228d83df8c588942f6773e5cb508fc6] [formerly 0764f9f8de88d1adbb668c48dfee17c43d59b5f4 [formerly d3eafec864]]
Former-commit-id: c09a0c84177f6f8053269cf6396b64cc17bf257c [formerly fc5b21e08ca0f2ebab30991a4c7f2ee0eb0d6a40]
Former-commit-id: cb8338d463ec7014de5e2cb09009ac872ade06b8
2017-11-15 08:59:23 +00:00
Henrique Dias
b5fbab2072 [ci skip] auto: setting untracked version
Former-commit-id: a15ba929646b6a49aad1c0a1f447c8417490bd82 [formerly 2140e8ebdbe698e68fa7cb80c879749af21a67ac] [formerly 8c2f7dc49612073e633affb68f437b8c2241b609 [formerly 927a1aea6c]]
Former-commit-id: a1daf8141f756ad4352bd26697d073c2468b1163 [formerly da39c59b7c0187c61b80fa4fb4867795f2b60b17]
Former-commit-id: 7b1fc0b045cd1e1d59fb49bb9b6db819e670fd94
2017-11-14 07:25:09 +00:00
Henrique Dias
005b184d59 Version 1.3.10
Former-commit-id: f9c2dd8d2b77b8d5e887562032777a108cc75c31 [formerly 659e0be49e23fed2271489b14db6fc51a25b7e0d] [formerly 57d196da7a6f46d3accb40ed471adbed67ebe5bf [formerly 0c657c3019]]
Former-commit-id: 40ae9fb71bcc1989f00c20cf05b54ebf3db3f0ff [formerly a51a873c5262d30736274109f7d779e5cc45e423]
Former-commit-id: 45946d34eca1ae4198ea113bc886d43f12322a65
2017-11-14 07:24:57 +00:00
Henrique Dias
372bd813d9 Merge branch 'master' of https://github.com/hacdias/filemanager
Former-commit-id: 7b068c22b37003acf5dd60806a828ed9f828fc95 [formerly 10918882c8f3150b57a5cd142e7443150caff282] [formerly be0e1f9cd900feead555280dbff11e024055cd7f [formerly b8fdb487d9]]
Former-commit-id: cf6f5b27bb327bfb00d01e249f8db457543407f6 [formerly e5e0ceee98ceadd4061f2443b6c4a53249e838a4]
Former-commit-id: 2187a74e21e29e8cf5b13bcca6724ced2aae47c9
2017-11-14 07:23:08 +00:00
OopsMonk
52599314b0 fix symbolic links issue on Linux. (#281)
Former-commit-id: 84a15d1ff70cbdcb91aaa7a40cb86604f036e349 [formerly 60e5a702fd98686193c5ee7bfb66698453de05ae] [formerly b863bd64cfc4e953c005f71ccc3fd936456b8a5e [formerly d269e239bf]]
Former-commit-id: 24b6c15a5c3a54a866106eff843bea0d7401cf15 [formerly 6b6c3036a90776bb16d93a4b7e864e4f433f5531]
Former-commit-id: 5b7aacabf2ceb1a5303fd303d5132bbdd711362f
2017-11-14 07:21:26 +00:00
Henrique Dias
f61e71e44f Update Dockerfile
Former-commit-id: 657785108f5de159eebb842d402ef98d512f96f7 [formerly addc47263d93e2895204151aab13539307533430] [formerly 91081acc00772f9fba15eec3362d94cbf9513145 [formerly 6f39464130]]
Former-commit-id: 54dc3af451a3bb3e1fdd114b88e0dac7c1a8802e [formerly 49d5afc124292884245cacdd951c5f9670cb552a]
Former-commit-id: f331a53412d10708e0ac19db04d94f0b99e63b40
2017-11-11 07:08:12 +00:00
Henrique Dias
e5265b6632 Update Dockerfile
Former-commit-id: 2b02497070be694a121f6ae8f7567ee521d2039f [formerly 6c301dfe46f6e1ebb9d92fa5251c23aa6a5c9f64] [formerly befd05696772a79c19330a4d3f0b4651c35802a4 [formerly 44d68a4868]]
Former-commit-id: 9f8661bb18ac3230d4f65fc4c5c8ccf31122daf6 [formerly 683bb4fdbfc8c95f1e69382322a8f1132c3de457]
Former-commit-id: a7af592420975962f5556ba47801f9cd4f009fcc
2017-11-11 07:06:23 +00:00
Henrique Dias
003f361956 Add /tmp folder #274
Former-commit-id: 938bde70202335858fe569949b65c04e7ffc4146 [formerly 37c4c3ac2743f7be8e4a8eec92565e8c3b4303e2] [formerly 74b276e4616904471b27d3478b3a58c2ba09fe42 [formerly 00da35b91d]]
Former-commit-id: 7be322ae95ea19b352cb4799b8927353b69286f5 [formerly c662fbd6eb0dc63624daae038897bcede3f28ed7]
Former-commit-id: c8ef89c3ea2239cdf715ad9f1555c1f28167e4f2
2017-11-10 22:29:15 +00:00
Davor Kapsa
cc8369d83e travis: update go version (#276)
Former-commit-id: cb189a867e029cd6dd57e6abcd70f74949e770a7 [formerly 92b3401b09d16673356c16321a1fd2938a8002fe] [formerly 5118e5894f9dec2ae5ae337e300c033f763be0fe [formerly 0be294a502]]
Former-commit-id: 516ccab1cfd5c713eb79a5ab4907e13d1bda98e2 [formerly 586b5951af545993c9ee4bff07625bfc26a3e439]
Former-commit-id: 77e2897c8571704617bd721e46a50a46f6f9acd9
2017-11-09 20:58:34 +00:00
Henrique Dias
aeb583a0bc Improve readability.
Former-commit-id: 684d5ef09624509fb041e47539dc0a568d1e8ac8 [formerly 3d0b8b2e61c5a60174a1fd8b06f8d1e8a55120d5] [formerly fc6cb1f278dbaa90d7db1c4c96cb3c1cbff06c5f [formerly ac88aab3eb]]
Former-commit-id: 80493535e416cad297aed47e8a8dd018d2fd2346 [formerly 3522c56e45bc8c0e2c89177f96c7f812f6c9a6ef]
Former-commit-id: 3096eb9e8abeee11f2ea447aeacfa681464a1252
2017-11-04 09:23:55 +00:00
pandarong
41f07c64eb fix npm run build error (#272)
Former-commit-id: bb2a5ea8b5949e11629d344c319e4cb0c5d48f0e [formerly c6b89a2ab4c66a1e9f16116906dead677e03a5ec] [formerly eb2a1b5b8840883ee18af987f6b7cf16a9d70075 [formerly 8703094109]]
Former-commit-id: 4208e88d8263785a5cc5bacd1127ce8460df960a [formerly 7d45d39e5913b04ca12998ce622188f3c4b975f9]
Former-commit-id: 6020a51629bbdd71ddc131c8ab9c6b0db5eb0f56
2017-11-02 21:21:16 +00:00
Henrique Dias
98588a66a7 Remove unused commentary
Former-commit-id: 6c214b9e0e640036cb012e1000c6a7506a48fa5f [formerly ba4b53ecb41176513699bc883be694b1be3d586b] [formerly bf971d7279fcdb0a734f0f515edd02c405ad86bc [formerly 45a6fd563f]]
Former-commit-id: c50f55e67204964a33fd324b4c948b164420e1df [formerly c00391a35a2aa77a4f5ca2aa5325ac763da263be]
Former-commit-id: 4548263d313aae263f97d2786112a65dd7e056c2
2017-11-02 09:57:47 +00:00
Henrique Dias
49f32f876d [ci skip] auto: setting untracked version
Former-commit-id: ab889938f319abc51aa1eab32ba12d4773b82e23 [formerly 56bf07aeec065255b34e9de3024a1116abd90e1d] [formerly 51aa29d909009fca0b83c71e8bf106ae403091b0 [formerly 2ebc7708a7]]
Former-commit-id: 22662250fd4e0e82ab7dda0f201823c68d437398 [formerly 7c4865e1c894d538d0627413454145d33df6b57e]
Former-commit-id: b3313117d95195075241fd082380de05a582f958
2017-10-31 19:20:50 +00:00
Henrique Dias
aeb0f37e20 Version 1.3.9
Former-commit-id: 9c7d15644bcab9e3babc8d645615e50d99187871 [formerly f0a0bf08c328b801cc6ed5add830732c99f92047] [formerly 250bc983264deb841b2115a03610b8e6f473f1ca [formerly b903356b94]]
Former-commit-id: 56bb31f4e44ad39a65f1d1eaa0a8e493287fad7f [formerly e120e69e05cc67f377a83f19f4106b7be945780f]
Former-commit-id: 36fb7783992200295b2f9811b97c2312977023a4
2017-10-31 19:20:36 +00:00
Henrique Dias
59a0daa293 Fix ViewMode related bugs:
- The user will no longer lost their 'ViewMode' option after being updated in the settings.
- The console will not output errors due tot he scroll function when Mosaic mode is on.


Former-commit-id: 97aa6abdc8b864dc7a55dbf03a2e58895ea7613f [formerly ff9e6ff0898f32bd106b644b2e9002b5de45281c] [formerly 556cc12bd5ff1d91776c81f48dd1dceb8bb627b4 [formerly 51104c5ee7]]
Former-commit-id: dd63b2b818a7bd4960a7243866d6b2829f4c03a8 [formerly 45855d70eaa9a2b060d3a89cb70388040ea8e6d9]
Former-commit-id: 6e0ebf10d7fe3e2c234abc9c0ffd447cfbdd7455
2017-10-31 18:23:31 +00:00
Henrique Dias
c716d126eb [ci skip] auto: setting untracked version
Former-commit-id: 88d8cc322b27fc1165a53779e7226db8fd6f90ab [formerly 7f0e5a193dba73cb133bd4d72cd0d81bdc354736] [formerly da79e8c40409e5b5995749556155cccd8a0325cd [formerly cc5f3ac1dd]]
Former-commit-id: 47f0176b53e3a5b2eb402eba29a07f51b7d0c7a7 [formerly c1d2d78eb8827f3386080b0309e6cef66bfed9b1]
Former-commit-id: 64c1ec091ce58eb6b38b0281b7a728b2ca07fe1a
2017-10-30 16:24:48 +00:00
Henrique Dias
5dc210c000 Version 1.3.8
Former-commit-id: f69ca91fc441142e07dfe8dd5cb92c419a1cd44a [formerly 679e456d16586b10e00c5aceb1cea75eb62e10dc] [formerly 50f4889e1eb2797c0c899af48acb1305fa33b671 [formerly 1e700ef167]]
Former-commit-id: ba0df3d330275d2fa1a5e02c608051f3f1422124 [formerly 77d49e18d479776cf670eb008878364fd4c7292a]
Former-commit-id: 2bfe39e4b6371996559557eeb00a15a895e4321a
2017-10-30 16:24:27 +00:00
Henrique Dias
3bb3fdb956 GOFMT
Former-commit-id: bbc11e551ed79fc41af61d67bfb9a882dfc8e655 [formerly 4ef42ee3251bdf613b7e24b50692629fbf8965ed] [formerly 10abcd7f0e34753069a80a683df94084f03fdb4b [formerly 1d4cbd622c]]
Former-commit-id: 7ac0c18ad61cd491f22b906e1efd87892d729cb2 [formerly b45c7e312058490ae90c8162e6cc1264613a9126]
Former-commit-id: 1c7e8ebd2f1b1dc5bd25df9f30a77c9b73ccad4d
2017-10-30 15:47:59 +00:00
Henrique Dias
b96a4a63c0 whitelist .htaccess to be edited; close #258
Former-commit-id: 1114b97a9b0cf236d96ce283bbf8d482cc85170f [formerly 6419fbd11f28ce1dc5ebdfcde0d34adabd016052] [formerly c1135ecdb720db11f4e1182c5456c484f5f58ed1 [formerly 72ee240ce3]]
Former-commit-id: 09fc62ffc432b822ea495b19bc5daafbd3ed8912 [formerly c1eb12242b30a1d90299427c387e0b2a25ce0f24]
Former-commit-id: e097526998a0d9c6382bd9d97af3bf161e8fcd0c
2017-10-30 15:36:40 +00:00
Henrique Dias
3d54b2bd90 fix #260
Former-commit-id: 0d8742754bb756ad3a83599850dae5f477282430 [formerly 5cb7d75b695d8400fc2af87edd551d6450e7365f] [formerly a6a814c40a5ff4f195c4ab470d4fccc92bd8c1c8 [formerly 99c8c92c6c]]
Former-commit-id: 45eba5ff05f8e64fbf33d9d670e19a0cf4880656 [formerly 88dc856045b9d51596f36ce387b1c4f3e85a7d3c]
Former-commit-id: 1eadaef460060da8ae71df3c66f242c844992725
2017-10-30 15:24:06 +00:00
Henrique Dias
95d43a344c [ci skip] auto: setting untracked version
Former-commit-id: 4f9b713ad6cd1f90e7569c9eb083b2740b2f6a60 [formerly ffa130fe00c78b2431ceb16bf1e18345a98826a3] [formerly 5aecd62f16317a53810677fc49a40af297d6bd91 [formerly f4f7147717]]
Former-commit-id: cd5e3739e28213efeaa59cb1fe68b25b35068d46 [formerly 10edbd82feb4d35296c1edf1ab977860b7504470]
Former-commit-id: 4067d711f14eb26232204a89a6263b989ee0494f
2017-10-29 13:33:42 +00:00
Henrique Dias
1077d5cd6b Version 1.3.7
Former-commit-id: c76a13cb8d0bf0a360c63c18cbfac378f8888425 [formerly e58788aa3de9e8fb54894a72bdd567c3846bf943] [formerly 77cd4f2feb8ef317cfeeff84d85ffb7eff4d899f [formerly 3cddb26572]]
Former-commit-id: af62db2d663287f662990539be428ae84f1974cf [formerly eaae8679ea0b9daeeba5cd29dd981d60d66e6c1e]
Former-commit-id: 886b61f0af26b85625cf7092ce5e7d4c5e44ed59
2017-10-29 13:33:31 +00:00
Henrique Dias
57cc174b3d [ci skip] auto: setting untracked version
Former-commit-id: ed15673452af4cd3ccf1a1f316efd6a733e6bd43 [formerly 0a9637c464bbae4c5c351f7e6f65dd24197a9968] [formerly e7388628831533d4476ba69c8d0cfac3ae18be14 [formerly 08bfeea103]]
Former-commit-id: 412e1b3bfcdef0037292b7ab3f5a5eeb4e9523f3 [formerly 2ca352a5af95eedadc91ea207833bdb670f1481a]
Former-commit-id: b359f15e105d55a9761aa9bf43c331bad113d561
2017-10-29 13:32:10 +00:00
Henrique Dias
cf61baa273 Version 1.3.4
Former-commit-id: 6d70d81a1967f171a41cecbdad8664fc65d1111a [formerly e273e78de0030dcb2eec730a00f248f1e8dbd9f7] [formerly 37b0ede2385e153153a854112ffab1065bcdd44d [formerly f89b86939e]]
Former-commit-id: 84fc2f9742c2e0011254ee762faf6f087986c6fa [formerly f22c175e96173bf25aa08067533937041152fdb3]
Former-commit-id: 32981c2a1211608d2d5c059264aa5eb434099400
2017-10-29 13:32:02 +00:00
Henrique Dias
2d5cd2d1d3 Fix Travis
Former-commit-id: 435a3bebf2ac1bbae2f1ed94d596956227141358 [formerly 46e69c9783cb07a9ca6b7bd428a1ab54cff2005b] [formerly 36ed5ea293a4d39d161bffed5b18a1d7f1a0d5c1 [formerly 3434090a7c]]
Former-commit-id: d5bc8f96b631af8d440438c1a089e710ae09a47a [formerly f447c0c7031af05129bf7c45b6c87168a365738d]
Former-commit-id: 627ac93b54b236e20d552486d125cef813eaf9e5
2017-10-29 13:12:54 +00:00
Henrique Dias
9472aad877 Fix #262 #266
Former-commit-id: 0acca700ed0a2ced8bf6b5b019aa6ff4774b8ea8 [formerly 0ff934dd107e56f4b7349f1363dfb1b6cbbc7916] [formerly f8b60e60087d754b46ca4dad49a151bec299febd [formerly ae1702c4e2]]
Former-commit-id: 824abe0c6cb5d32c061e8efd4a7f1d3354b3dbfe [formerly 655809d5c670c9de352c473de1fe08303a4c5506]
Former-commit-id: 6ca6f383602241b8a50c13cce5b066cd5f2cf990
2017-10-29 13:07:48 +00:00
Henrique Dias
dd0f3ef144 [ci skip] auto: setting untracked version
Former-commit-id: 7abc56816c4aee81b91bccce14251ed71b02ac0e [formerly 5d1acb37bf487778cb740299a0f25846f49f0ad4] [formerly b97420d631839e9a7a74961403113986d493638f [formerly 26e1e44d60]]
Former-commit-id: c196dfa47127e87efcec64c4cda2e7013e5cdc4d [formerly 5647ffd57b1e049a3e11f6ec37fd2344abfc591a]
Former-commit-id: 3cc55fc8d38f553f0405972dd39da2b0bbb52740
2017-10-15 08:28:41 +01:00
Henrique Dias
44e492160b Version 1.3.6
Former-commit-id: 28c72f8ee9f7cf548dd5f2c2cc564b31ba13ada8 [formerly cd087b92826cb418d4d17ac26a8cfeb408a54919] [formerly bf241b663bdcfc00737f5bb429ef4a669c1a6c2b [formerly 3e9c3ed912]]
Former-commit-id: 2b74d089de06c4fa5d4933e0c11ff8b1afcd2f46 [formerly 9ac185e1ea76c1beec895f6c51109fd137547e60]
Former-commit-id: a27b4e08dc726769b4731abb5865d2e908387774
2017-10-15 08:28:28 +01:00
Henrique Dias
0137b03887 Fix Portuguese language reference and DRY
Former-commit-id: 3954a92011b3c1699236b32373688ce80f720179 [formerly 67bca685bac4e893f836b21f85f4369444f3e708] [formerly 9817c40128b6660be30ea48b5317d07d21b84ca0 [formerly 77df2034f5]]
Former-commit-id: 2681c1f068e8094f0f9593a4c21fc6f84d8a6353 [formerly 26986c2af1193a31e3abec64b829cb29aa97011c]
Former-commit-id: c2813afc8de2e22ff9f3de0f55fbb26b3b954619
2017-10-15 08:26:22 +01:00
Equim
92c9b134c3 i18n: auto detect locale from browser (#253)
* i18n: detect locale from browser

* fix regex for locale matching

* remove debug code


Former-commit-id: 17e550af54ff213d5e2b60f83b374cd962052b5b [formerly 62fb089a7a45092b3818135dd68fac218067ef67] [formerly 733c463d2332307dafd40da5e77c6c9558239283 [formerly 4b84492a11]]
Former-commit-id: 2e117c9e060ac5cd9f80a0de2a4582eef74df6b8 [formerly 5fb6fc086bb2ebeb49a578042240a26a7879a4b8]
Former-commit-id: 7692a4fbb2889acbd7c6ee09ccd01a234998616a
2017-10-15 08:12:40 +01:00
Geno
1737702c7c typo in logging (#255)
Former-commit-id: fd0b7d6887e14b77ab97b323f4e8d1bb51ecda23 [formerly fb4fab794ca27e8f77e0dec71483cec51e4a8fce] [formerly df9da803a4301a8b1e5ed93f1f3fd121b8a3bf49 [formerly b93aed98b1]]
Former-commit-id: d17f1d4be63659781b4254b9ec36737d2f794008 [formerly 27cc8277db82e0ae123d2b1ffe95bed297c6095d]
Former-commit-id: 8137dd86f6f3caef4636416f6370bd686773df3a
2017-10-15 08:06:53 +01:00
Equim
ece52ecf7c Don't expose error (#254)
* share: remove share link when it has been moved

* http: don't expose error


Former-commit-id: c7f1d28117c770006132c75e5950d73aa9d87a12 [formerly f29a0260622644a79ff401263ba4efb143dea23a] [formerly fdd741679f09c72a121076e0a62a0d2a6eafefe4 [formerly 538b99ee77]]
Former-commit-id: 5ec9b62254b0cbc233673e7e196a5a4ece53a3f4 [formerly d26d45418267d11a1a211ba90a8b68b5b9fad714]
Former-commit-id: 57333d74cbac7088c6c527a4fe757af427dedea8
2017-10-14 10:07:01 +01:00
Henrique Dias
a7d6a72718 Build Assets
Former-commit-id: aa40a922e2dbd104bed161fd6564343c64f2fb34 [formerly 16a73ea595678b00ab9690338015746a8f5e5f02] [formerly cf4ef0a6fe12d211a1e905047a3a2cdba12e9c84 [formerly 7f7d536c5e]]
Former-commit-id: cb509a9c9961b4c8d2c6f8f67b37f30b2732eef8 [formerly f7465805945f0c08312c31bb067e3d9546d883bd]
Former-commit-id: cab59e68c27cd7c180a8964889c759efbbff7616
2017-10-09 08:28:49 +01:00
Henrique Dias
3fa9286238 ViewMode constants; default view modes
Former-commit-id: d51ad4b2671c76c3a493daf93fd4fb94a76826ed [formerly 576cfa8ebe9c66c9dbc1c8f79ab8ba7fecbe8845] [formerly 0969e87e3c070763d6e58f1d09f815b772814e4d [formerly 8633e9677b]]
Former-commit-id: 4dad4cf6bb3858122cd4d4c6a574e691788ef9ae [formerly 0b356f24a005c8886bd2165884e363f277456d8f]
Former-commit-id: cadb7589dad74e9bdb47bf3664f98ac672940fe5
2017-10-09 08:26:05 +01:00
Henrique Dias
ad5ff4cfe0 Close #248
Former-commit-id: af79c3515a4a6a6d5b72da1193b888b7cd03b286 [formerly bb0e1f8e966e238feacf3013eccf9b5e711f7061] [formerly 0f38f0df6f964f88310fde0e59f6b3a79b9c103c [formerly 90c2de44c3]]
Former-commit-id: ac03a781bce6530b0512b014c38e8d8c77d8ec01 [formerly 5469e2322432fd1991229f909e69d70966fe8f9e]
Former-commit-id: d17517418e68671237ad845df93b62840038d8d7
2017-10-09 08:14:17 +01:00
Henrique Dias
9aee1ebd2a [ci skip] auto: setting untracked version
Former-commit-id: 89e071320621710d9f24bf4dde8827f159c64d31 [formerly 6d44fe3f3e56b82762bacce64c1682262d4d1a90] [formerly 8e0f92b3205067d4644bf2febdc160d84696a37a [formerly 769bdad12a]]
Former-commit-id: 9e00c24fdca0e1ebe71aceb84624ea0b9fb85afa [formerly a04f2e37805000e19c7dcdb7daafe2396cb8269e]
Former-commit-id: 827a695fce5d7731e58052dbbf4f5bd0e3d6f5c7
2017-10-08 23:48:24 +01:00
Henrique Dias
f8ed1b41d6 Version 1.3.5
Former-commit-id: e064282645a50d6eeeb293577da260c586d4bb45 [formerly 2a9ac206ab79a8f115ed25a3afb595663f18fcb4] [formerly 2ea943c96653266a9533d9e95776955821706ea4 [formerly dd59e3c62b]]
Former-commit-id: 4ec9b9738ee48943830b9045ae40e7c305010bf3 [formerly 7748b70d5109e788ad146b466b3430a822037570]
Former-commit-id: 0981b89cb1d7bc0da43d47ff8900eb1c67301982
2017-10-08 23:47:56 +01:00
Equim
bb9b0dfd2b i18n: Fix typo (#250)
Former-commit-id: c27f105374233923e7691327f7e6f6b9a52077ce [formerly c34be33394c74229a1029edf21d9c83d5c9dd2a9] [formerly 3f5bd5a8457310d034b7fe761beb915caf35ae65 [formerly f0609757f7]]
Former-commit-id: 82d69494e5c421a245ceff0df033529ef488e419 [formerly 910438160faa1e853c91252845443514a066b3f9]
Former-commit-id: 01874c63eb13dc39471c3c2cd06a9b2e99394f46
2017-10-08 23:42:51 +01:00
Henrique Dias
cc2ce884fc [ci skip] auto: setting untracked version
Former-commit-id: d68d28c1e0000f0308b1e84a912f00b8a91f7fbf [formerly 15e0131f25aa82b8bb340e2d58d09c35a4a91754] [formerly 282b0c8d07c82de35fb87927c9da29182efaceff [formerly 9d27c38097]]
Former-commit-id: 9cc06bb0c230f2acac72fc6b4d00e4d8446a5f88 [formerly 5634a96f7b01d6f08a6a46f24dab7a3f03668fd6]
Former-commit-id: c9629add111d645e386461016771ebd1b14b98b5
2017-09-11 09:49:54 +01:00
Henrique Dias
e7e7679002 Version 1.3.4
Former-commit-id: f7727c37ed227e062a34de8b67a5bdeb95caab27 [formerly 9202314bd4a0a0288e3b95c179aaeaaf38997f7f] [formerly c57ba427c5e85f4aff34ccc5476d2c374c93a6d4 [formerly d41715c0c2]]
Former-commit-id: 7aa4c2ee81fa7dd451fef64198335f7a6ee78173 [formerly 4d1553375b1a3c86e9f8de45ef658ff8fe64aba4]
Former-commit-id: 85340361dc98b2ce775d1c41291ff3af165f99ee
2017-09-11 09:49:42 +01:00
Henrique Dias
9a829fd594 fix #241
Former-commit-id: 8578bf0b790ea4b8b5c5da4876fbccd2ead42d3c [formerly b67d35502fb0c9a3c57226b812dd2b869c5fcae1] [formerly 506eb279c974a86b232be57f87a11ec283b3b742 [formerly f658394dd7]]
Former-commit-id: e9565ce6a4ef229943f132fb6e05c5ec853447ed [formerly d0b2c24f6df2a74c403ded829cfd0746659e7d5f]
Former-commit-id: 2f4afe92081915821dd5b2fe745faf1492bdcabf
2017-09-11 09:46:17 +01:00
Henrique Dias
624d61930c Add ReCaptcha to main
Former-commit-id: 06bc7079f6d939e5531a3d9600052f979adac86d [formerly e6d8fa4418ccfa8f0163530647099432a936d4ee] [formerly dbd0cfc3770972afdf7aae8121d3af50681d55eb [formerly 879ad7b518]]
Former-commit-id: e6706456ba300c501ae66664596b5709e45d87df [formerly 98238eb61781a545ee4bac512a4f02257f4cf165]
Former-commit-id: 30bfb2b201ecbbd1ac2cf58cbadbe82daea793cb
2017-09-11 09:23:59 +01:00
Henrique Dias
ee30e7711f implement recaptcha on login
Former-commit-id: d7495b6fff4a99a8d155a3be87b15535a74a1305 [formerly 5b3a544447cca0d1cdcb6c87ca94f450a5493506] [formerly b4de1a4f5d4dd295c98366ede2b87bf2cb7918f9 [formerly 002f8066c7]]
Former-commit-id: c0e5d38111a99f8e3e71fb5db86e19b7ba44ec48 [formerly 1b5e454263ba64ced95c6d4b51f5f32e66f74758]
Former-commit-id: cfb17a53fc86d0071fba91503502444f5f10a0c7
2017-09-11 09:00:59 +01:00
Henrique Dias
6e5116aa27 [ci skip] auto: setting untracked version
Former-commit-id: 4f0cecd21f2f1fec680773c6242d6ab9687384f3 [formerly 1dec18820a98ad1ffc39eddff12167b4d1564254] [formerly ffdde8ede3a4d934d492224a046b654518cffbe9 [formerly a61329843c]]
Former-commit-id: 865c60a764807ea8d6781ac4bc95a4c8ddd9ef56 [formerly c9799373cf5e9637c78faffb94117c355a3a4f8c]
Former-commit-id: 070a498ca4c9d6da545c9b10d60e0f84e7e56889
2017-09-10 10:14:35 +01:00
Henrique Dias
34acffbb7b Version 1.3.3
Former-commit-id: d3126ad3137ddb4270199227c7ac4a0aa248b117 [formerly d8de0edac44991fb25fc3d58bc5fbf34ca51e14c] [formerly 65f8bd6a9c9a15f2182f7d9d0d0317df8a7a636c [formerly 53dedb7f5e]]
Former-commit-id: 473f94e0b181bfe06eaf73648775af5e89b03032 [formerly 9efded6125dc4ef0384f441933139847513fde51]
Former-commit-id: 8ada7c0571decba2abb0e721650401f040a6fb86
2017-09-10 10:14:22 +01:00
Henrique Dias
eb6f26c191 Add view mode on users created on previous versions
Former-commit-id: 6c3e320b2004beb8172e966e1a7f1885e33ed20b [formerly 829930444312dcfa1e144b3631c56632f6094d01] [formerly 68ae2ee9df5b3908fb30a07094f7d464818a0d52 [formerly b651d03ea9]]
Former-commit-id: 3af8ad9075641ca34504e33a2eb363eff7c6c63d [formerly e2873038181f9cc6f8d1c465aa5544cf98eb735d]
Former-commit-id: 584b5c3452545d2a6361ade02e093fe5596a52c3
2017-09-10 10:13:10 +01:00
Henrique Dias
06f3e9744a [ci skip] auto: setting untracked version
Former-commit-id: c7af338241ab6cac5e5e6f5683a8394e30a1ff6b [formerly a28f7d51e97810acd69616c22eb578f2d65e8528] [formerly 6b372592bf387d73b10e42a29ab75a5b99647d65 [formerly 750862b17d]]
Former-commit-id: a6a9cedfc2f7507080b39c665d97e8b586a746de [formerly ceeb7a7fc060245d21d169eb22b6b099fc4c384d]
Former-commit-id: 5dcf4b4c5a1caf3b482baf7f0f8aecb1b69a9263
2017-09-07 18:28:22 +01:00
Henrique Dias
cac5413424 Version 1.3.2
Former-commit-id: 90d5ad658b45bedcba302eb5ba31bd419742b6da [formerly 7fbf1df2fdf4319d6079a844d03fc68d0e6272bf] [formerly 8c9bc269c306ba49250a959200b86f1035ff5565 [formerly 6fce037280]]
Former-commit-id: 6da196d1aa360654f7fb61f02d448e6d278d52ff [formerly 272a9fd5dc6092af922aac72040fa0fbe4ecfc52]
Former-commit-id: 07fe419998104c357feb2376e61a9d3c3a65273b
2017-09-07 18:28:10 +01:00
Henrique Dias
037efc2eb8 bugfixes
Former-commit-id: 38f7bdc4de8a29be11376e2321aaedb027b84a57 [formerly 72daa72c1b48182002bf81ac374b54dad2bae465] [formerly 8498a21191a043432e0a857cbc4aae49761bfdbf [formerly 2ad805d793]]
Former-commit-id: 068d502148b08cfd8dc627739847888f018f4417 [formerly 882e3aff3891329a62374c448e839ba41edebb45]
Former-commit-id: 7b71062e2c6e86c5e1ce36daaeb265972c941ca5
2017-09-07 18:27:30 +01:00
Henrique Dias
7de22b53b8 Settings styles; close #228
Former-commit-id: b564ba4e357b2d4b18c7f9407395894eb5e18159 [formerly 84ef220a2b10b8ed501c4499ef03d99acc148546] [formerly 00056633e5c2e947201a2dd1ad3bb937821edf61 [formerly e3212cd076]]
Former-commit-id: 866b84d788a5f8a9767affdef17806dbac984db6 [formerly a22d95ba5726dd5ea757410249bef37654213a68]
Former-commit-id: 4600591829c0a41c5de9defc3e30fbac28815e25
2017-09-07 18:19:29 +01:00
Henrique Dias
ae19731015 Merge branch 'master' of https://github.com/hacdias/filemanager
Former-commit-id: cda66995085b2fe15ba1327f5f21d6cb1e7fbad9 [formerly 1e91b652c2c9ac12f7148a5fcee6826823f861ae] [formerly 6e9c1d23074a4ef51a8342014600a64421e37411 [formerly 35742fe91f]]
Former-commit-id: aaf02c9e8fae32fe0507d4375bce87a97637ee0e [formerly 4b2b1e3a10edbe38434ba663d6888cc462afe749]
Former-commit-id: 02ad43301dbc1d5dcfa4e9dd6aca07689d7df378
2017-09-07 16:39:03 +01:00
Henrique Dias
8aa0797019 build assets
Former-commit-id: 8ac0fd77c313bc4527531ba8fcfbab0723108d4e [formerly 83f9d8ce20bacf6259216c0d902957e4c2b9d798] [formerly 04e9fd8ecdf88a44f562da449ff08bb16c108f5f [formerly fcfc28d09e]]
Former-commit-id: 36785e0d78ac8f7af84f4a1071d4bf226eac2366 [formerly 874b8426ee0aa485fe29b962c7ad6e01d90758ad]
Former-commit-id: ed6bd42a0bf059265816edb0085abbc9fbfda4dc
2017-09-07 16:38:58 +01:00
Maxime Daniel
17b3a403a5 Add new triggers and improve environment variables (#238)
* Add copy/rename trigger and more environment variables

* Add after_upload and after_delete trigger

* Fix scope since merge with master

* Add new before triggers


Former-commit-id: 65f7d47840980e5e9f330ae29053b37f2b98bcdb [formerly 9ec3d017d7f072d1bce2492c3a3e9839aab679a0] [formerly 2359047e3b51594ec705f2baf30b73f3bfc4c6cd [formerly 51295f999d]]
Former-commit-id: f128ee9a69ee280f0488aeaa6c8d86337427bc80 [formerly 311dd0b0207a58427d51a07244c3b0ff310dfc6d]
Former-commit-id: 734321b412e9ec7a5d848f9579e134ebce58145f
2017-09-07 16:38:32 +01:00
Henrique Dias
819d511690 materialdesignish
Former-commit-id: 5624723eeb939734902eeaa6c2f132c4beffa911 [formerly a83456d3eda5175e2941b2deeb58b5da323ff678] [formerly a6b3ac7942dbf48d7d9b3f8db5e5041a93143f19 [formerly 4d6b54c63e]]
Former-commit-id: d03621c16b2c892701d678361b6c0a7d5dbec620 [formerly d818b6751e035f283e1c8390d7993a33a459a7dd]
Former-commit-id: 3539ee68532135467daa2cc175482769b1efb592
2017-09-07 16:37:11 +01:00
Henrique Dias
30cfd06e3d change location of database docker
Former-commit-id: f4b3c8ffe4d6772abed3064173ba0ea8efaa806a [formerly 4792bef0a22c9a132ef1a8d239e92bfcb319c38e] [formerly b0de425e325346a805635a3907f3441556ece17b [formerly 6d2fab77e8]]
Former-commit-id: be1ed94475486cf7d44b339f11b232e284efe8b9 [formerly 167c46931b5751dc7455551cadcc075f646742a5]
Former-commit-id: e8ccb7b598697255949abdcfd6ea760fd2e401f5
2017-09-07 14:19:05 +01:00
Henrique Dias
c5558e6e41 Merge branch 'master' of https://github.com/hacdias/filemanager
Former-commit-id: 068b3d896b21bf786e9a2b7758eece8fa5071058 [formerly 7d80cfb73a7481dee4c95c0f3c39a95d13fd2f7c] [formerly 2b4adf5432b9fd7bb49b244955676db4ba6c5fdd [formerly e7bdba8948]]
Former-commit-id: 4f37bb0dc50a5acbf8956f0ef19d4ecebc64a534 [formerly 4eb5ba6be4bc857fe05862485d76e060bcf37fe6]
Former-commit-id: 98af155a74f74287f36fa30d29b8036c73fa1302
2017-09-07 14:18:10 +01:00
Henrique Dias
c3b3099ebb 1st phase - Global CSS
Former-commit-id: 508c4ab746f994bb3f4f5e86ff1ca5e6dc873f3a [formerly 8a29c22f817f54abefe21116e46f21b24306b6b8] [formerly e23c1a85571f61877b67656ef361f1c15acfcb3d [formerly 67fb6f8a78]]
Former-commit-id: 71e9024cf9107ef4e40f95bd388068fe052ea4f0 [formerly 7861ffe8d20f0777ae48c4f159efd7e32b2204d9]
Former-commit-id: 7db04ac5016d182f13ac56911d0a10871fb261f3
2017-09-07 14:17:56 +01:00
Ricardo Gonçalves
b1d47daa69 Docker image built from scratch (#237)
* Docker image built from scratch

* ldflags aren't need anymore


Former-commit-id: c527464833492254cdd14ced0f182fd78fbf4dda [formerly 5d99678c525e565d5b39f3bd645906b4e997752c] [formerly 80463ad3ae8e60456d5725277125c800bb2d6f50 [formerly 55789107e0]]
Former-commit-id: cea3a7799a9bd767008cb1b854314f6414892aff [formerly e682f6a01994537199f8c178de7526b961591d2b]
Former-commit-id: e76349c3eef778dc5913fb7128ff2b69907e9433
2017-09-07 14:15:25 +01:00
Henrique Dias
f51e2d5ba1 fix #234
Former-commit-id: 7e0b0a321a0bf352eb3530fc8d1250ba04499c87 [formerly a431ecc1f8f4d79b6ef98529ca38b7a00ba332d2] [formerly cc09c86c0b996c5c01bd4b7e65671e545e3c1828 [formerly dd7cd110db]]
Former-commit-id: 7bd178d355de3228aa03ec3cbb814e52588ea7df [formerly 35095c2b6728944bea8173c3ea1904c632ab30b9]
Former-commit-id: f2e8e8539a5ffce98ea186f42fd183710bc2f9a8
2017-09-07 11:42:17 +01:00
Henrique Dias
386974657e Build assets
Former-commit-id: 3ac594e7a4f3e806f56ed23790a1a195c87d941e [formerly 6e6b55d1430bddce55b701c04da53c1d746b4168] [formerly e3b0d8c63cf8bacb88c90d2f4e1c5163a448e473 [formerly 59b99ee298]]
Former-commit-id: 5d32e7939ed0941e60ec56f5a7f21fd78216706c [formerly 9c8c8a52a0633de356322fbdc0c54a9f5395daaa]
Former-commit-id: dbd51a43c6e8bc3df50ae57aa98e60bb611ab066
2017-09-07 10:43:32 +01:00
Henrique Dias
f7858cd719 Default view mode
Former-commit-id: 9212e217514046246a2bde10319763b05998b223 [formerly 8643e0ba3b4246ac0320b5d96dee4ba95e74a346] [formerly 77adb69159dce9cc89b631107340812916a2e279 [formerly 5daacf0298]]
Former-commit-id: 1a264e8a297e2a5146db53cd6cc40b8baba778de [formerly 607685afb5b6c89703c0b6d82b3dcec07eb22bef]
Former-commit-id: 1732f02d1641236402976ae182ee03f7b936d0c1
2017-09-07 10:41:01 +01:00
Henrique Dias
b355a5c058 Close #232
Former-commit-id: ec2f7562e0830ebb98bc7b4d997d74f2e6d685a6 [formerly 281a652559131d195b76feefef5cf0303d312b1e] [formerly 11a192c2d6f5d9667c55fdcf3f028391f70f6793 [formerly d3d3cb3d4f]]
Former-commit-id: a55ef038e404c2e9b97a802bfbf562fac02a98cc [formerly 033ded413b4f7bd21b7ff60feced6b590203cc16]
Former-commit-id: 6fc7fedc5fc087790102a07c3db3060e82400411
2017-09-07 10:29:19 +01:00
Henrique Dias
50758b53f4 Autoplay music and videos #233
Former-commit-id: e6305724cc9b53b764ffc199cb0b114e4fa63984 [formerly 36e7978a12f653f156c01ee11c2f91d24f7a7ca8] [formerly 9927ed89f8c30a24141865a3a7a74ffa79180853 [formerly d555cc44a0]]
Former-commit-id: 42efd044dc45d99a8fcd16c29a1b903b54f99a9b [formerly 92f6b43d0420b782defa61c34d2eb5ae9e9244bf]
Former-commit-id: 8fa9ecdbd6cc57a0513c9a72b5ab0b029839af41
2017-09-07 10:07:24 +01:00
Kyle Bai
f9902d2bdb Update zh-tw translation (#236)
Former-commit-id: e2e8b607eaacbcea36a8d3389dd4502158d2ac8a [formerly d47475c6aab73ad3c0ddb08e90664a0d3d2d9547] [formerly d347499f05216a64cd8af1075525ca6b3b7c3c41 [formerly 4d1b2cbb14]]
Former-commit-id: 229b92d7ce1143d6f9768ef8f52b18f5ead937b8 [formerly 673c0452c0d7a677a7c6fbbef941e47701bafe47]
Former-commit-id: 7e8d93b3114d545139cb2f436cf16bdd481713c7
2017-09-07 09:41:03 +01:00
Henrique Dias
b1512d2b66 remove served with from translations
Former-commit-id: 8fd3db2a2303fd77e51230e4ca9336013021d55d [formerly 3a6d99cad26160eda920508962ffa4f23fe2acd4] [formerly 969fef4feac9234ac7d165e4b8c2d572cf41e493 [formerly cbba245f84]]
Former-commit-id: 85af99ac3e6c48967e1c5d11e0fc6403492dfb0d [formerly ef7e5d4f0b655ca32783c21a6678f5e2c6ff8a1d]
Former-commit-id: 88024e6c39480df5799d892f1a7517b5345b7e22
2017-09-03 10:54:48 +01:00
Henrique Dias
1cfd31756d untracked version Sun, Sep 3, 2017 10:51:45 AM
Former-commit-id: 3f9a066d3456fd0435b5df19a6071f403761c363 [formerly 7ed9f0a686c30d97af32dfb7fa35b1f4dbbcc9ca] [formerly 8f473d98da1799211e7e7fd4e26dc9481297ea7d [formerly c60e7b9c0a]]
Former-commit-id: 732ae3300efa4444b06406743f9b8cce813e6b99 [formerly 465966ff48d99ca33496ff9dcf2d51e375985d0f]
Former-commit-id: 8c61fc2054cbecd2f0b7354884634f4b73f6e77f
2017-09-03 10:51:45 +01:00
Henrique Dias
9152c77543 Version 1.3.1
Former-commit-id: f16b67a8bc80baf5ed8e6e3e4764c0219429c681 [formerly 1d264f27c55881673c8cf7597c49859975e05312] [formerly 4246582d7a7292c037733c0c0f946312241e4bfb [formerly 17cb3dc1f1]]
Former-commit-id: 61269ea3228995bcd72b721529b25ad0cc94bd36 [formerly 3cce1019691228714433106d6e6c83490f36217e]
Former-commit-id: 6d51d1908e6db75ed0e614e273ac108aa9774d79
2017-09-03 10:51:33 +01:00
Henrique Dias
c236db329f Add FM version to interface; close #221
Former-commit-id: 0d8cdc850ea970ecb73bb6ce5af85ca517dabba2 [formerly 9adaffca71250d6e350914e90965ea07198be2f0] [formerly 4c982e090fe2809829c35d574b75023ec384ba26 [formerly 59bd6e97bf]]
Former-commit-id: 959919b2c265757a9dde9f34ae06373468ebae0a [formerly 120c5d413404a70885ea597d93469cc3b93bf26a]
Former-commit-id: ebf8abfcf0003babcad11185da96dede783d25a9
2017-09-03 10:50:49 +01:00
Henrique Dias
bf6e0abd96 untracked version Sun, Sep 3, 2017 10:45:00 AM
Former-commit-id: b27d1ac3dd03bc3c6e4bafc9947d57ec8ff447b8 [formerly cc8278a69ea0ff56f370fda951fbb504b13d9938] [formerly 2553bd5a8c7f8c621080d7d50a89befb1aeff4b3 [formerly 011d5aee46]]
Former-commit-id: 5e67474dbcc85cf6fccefbbc8d566a3aaa4477b9 [formerly 63293531e894499121f1a845311877f62c5addfd]
Former-commit-id: c315ae84528e06a285e01d0cca740cec071d4a87
2017-09-03 10:45:00 +01:00
Henrique Dias
8d8c756233 Version 1.3.0
Former-commit-id: 77f5b5b6d73cf979c074d4390a55b14dd9fb8c39 [formerly 4e77a8d13ec4233f1b38ec9b26e7a42bc6f4a9c5] [formerly 5d9600a5fd5b8044ea0a624570be5881740aa3f0 [formerly a4584e7a55]]
Former-commit-id: b8832f925ca7d7daa162a46b87c03b2fe32f67cf [formerly e109aaf234b60cb046c9c6dea2d96089581066a0]
Former-commit-id: 8916c596e729dc1d887da2135fe7cb7e997b03a5
2017-09-03 10:44:49 +01:00
Henrique Dias
d5f0471ab7 untracked version Sun, Sep 3, 2017 10:42:09 AM
Former-commit-id: 682aeae0dcaeac554de9fe24e50a42e53011cd76 [formerly f6147413fe2dc33d356fedd50644d2471e139871] [formerly 1d90a78470bca90eba6c0221fb06af2a10b7cfad [formerly a3f06b526a]]
Former-commit-id: ba046d6e696866a4c32fb33fa4de545aae29d91e [formerly b2e130a7078e4ac95dbdb37911ab6514f36e565c]
Former-commit-id: e13622c99901155e1d2c4fc67b75b320bd3ab1db
2017-09-03 10:42:09 +01:00
Henrique Dias
f7aaae3f63 Version 1.3.0
Former-commit-id: 474be62198116810d28ffda109669b686b66d2c3 [formerly a2c2f34340b407679035f03a41cf75f6b75f80ec] [formerly bd04b4c765993602859a3eb5e72f9761ec16901f [formerly 6b14dc336e]]
Former-commit-id: efcb629aeecb7d61f4db93be32ae9b574d9385bf [formerly 951ea5271bfad7526aec3da1ef4afdaa7004fe58]
Former-commit-id: 1e0c3c13dc7ea0247c0a2909b79f9417e1cbe59a
2017-09-03 10:41:56 +01:00
Henrique Dias
2515819026 Add version stuff on root
Former-commit-id: 4dcf3117e274fe2b656af6fd2ee98db386143620 [formerly df3bd216ef1020e9ca6a0ba57ef1c4bad5a6e04d] [formerly e5eded7248098655cd60112a6ec5fec06fd99ff2 [formerly 044575d2af]]
Former-commit-id: 8fe1a523af24c490eef9d7cd2b1bc3de656002de [formerly 73aea3062fa487a5c6c89f8f04bd86c177c783f3]
Former-commit-id: 6fd9e5260feacfe6b9d96515a7c3ab28c31f17db
2017-09-03 10:41:14 +01:00
Henrique Dias
7ad727d27d Fix #227
Former-commit-id: f1bc07c44f53df9cd1d67b870c5bb477e341abbb [formerly 4d7dba923461b33e4fab40c912458901d2295f08] [formerly 574b5b5b364719ea1939ee232e598f12697fea2f [formerly 96a5226076]]
Former-commit-id: a2262d2112a5d1ccd094d9077233d043660a9100 [formerly 73d2cf6e74cc635bf40bc4b89204c754d8c21da2]
Former-commit-id: 219e96d7c4b1da662643e48dbfa6e97468d9c0a9
2017-09-03 10:06:25 +01:00
Henrique Dias
adbff03274 Add tabs on settings. Close #220
Former-commit-id: ceb274e0e526378d5ab449470c122f11789200dd [formerly 63e043d75c173243116fb35ba389141e323e7606] [formerly da9fc0747c65a3fbdeb032a5e7e3b9e146f409e4 [formerly 9a9b41dca8]]
Former-commit-id: 2a287ab5251869f0e2e2fb836268fb2b1682b81d [formerly d7201a4b07230d32422940250bf0e01b430dde9c]
Former-commit-id: f8f4013fe9eb03ce233738e51ec2c967753b29d7
2017-09-03 09:54:51 +01:00
Henrique Dias
6b8a65382f fix bug
Former-commit-id: acb5afb206fb81d285be03b7fa93dc4e8b32fafb [formerly ff9425ef412822c13f78ddb04bae862ef0f4254d] [formerly 78a8707fa8eef88ddfca44ea61d1409326dc2183 [formerly 359e311da1]]
Former-commit-id: fa4a813c9bddc8b4ef88c3cd24b9f3fa84cb5f8f [formerly d18bd2c49ad25547fb2bb66519221b6d7fc72849]
Former-commit-id: 0a6e241fd1ef05bdbed545aeef76b442468710a7
2017-09-01 18:18:00 +01:00
Henrique Dias
4f7e6cbb52 prefix url as a flag
Former-commit-id: 837bddec0e006860889cebfa74e476aab7f1b04f [formerly 681933b995c0d65da501270f677c6dec358da62d] [formerly 7db51254915e0eeaf7e7098fb5ee79425aae9514 [formerly 3fde3c716b]]
Former-commit-id: 45535dfac3fbb2cf605d107bcb1887bfc3809cf2 [formerly ee5d2e0d7df8606566988854b0de22303a32748d]
Former-commit-id: 51378e5b5a6325938d00416c200053f772ca0b61
2017-09-01 17:48:15 +01:00
Henrique Dias
40fe081962 fix error
Former-commit-id: 6f9d7c0b4ee819f737395ec1b721a88be1e6c5e6 [formerly 0489f9f724d1165bf63c45df84398f9631ed9833] [formerly 5a3ffb16e3a801dd811b3d05ce853413d1da2ae6 [formerly 5c2166bc15]]
Former-commit-id: 4672f5e091c847482af0ae4741bcb809134a1559 [formerly 96ecde75a3d61b9cc1e77eae6254166ec53b902c]
Former-commit-id: 683218b398c699cd0f6ce15bebb6385d20eee390
2017-09-01 13:26:04 +01:00
Henrique Dias
ebf1325126 build assets
Former-commit-id: 5fd962f1a14d288995f97fdfd22480c0af54cf80 [formerly 1642bbafb9bdb91a58a5fb1532b1168894b5574f] [formerly ad423b6250b27f4eed0d9cbdbe3f09155e343ef0 [formerly 0ffb1fbe39]]
Former-commit-id: c3c687df2340c41f44356cd844a0b5ca0d779f71 [formerly 7238fdf7c28d58a734d4d6d8d71bd47456064dbc]
Former-commit-id: 5be42b6cb65f638508133c56fc6775d71e1dacca
2017-09-01 13:22:35 +01:00
Henrique Dias
ea1b9febb7 Merge branch 'master' of https://github.com/hacdias/filemanager
Former-commit-id: 79aec32eb9ac9b7e75ad00689990a865d5fed5c7 [formerly 96b947bf637adadc04409075b715ee85e4c7376b] [formerly 384abd27c0d7d22e67fd70b57c764b2dcbe69611 [formerly 349b3d02e6]]
Former-commit-id: b58b2e6bc0a7e90a3a77054c686c98f2cfb96978 [formerly 8a6ddadf8af01a29380dad9fa6ea4ca035aa8043]
Former-commit-id: 31856ee921c1fb7d77ba7704ea0af5e2d58ff952
2017-09-01 13:20:11 +01:00
Henrique Dias
f3edf63fb2 fix #226
Former-commit-id: 7a2d0e67b2320af58b04da5e5834c0ed6a0ec137 [formerly 468e34a7bbadffdfc268c072b4460c99beb066d9] [formerly 7a962e989a213218868b02cfdd7c289ca0b6831e [formerly 2a5aa21b8d]]
Former-commit-id: 2f443bbdf9518e66136ab97cf8c2f7e434a65f7b [formerly abcf5d2dc1aaa8b034bbe7b958cdec14feaef1b4]
Former-commit-id: 22cf31bc4973b51fbf9849f537e0191c65bf327a
2017-09-01 13:17:10 +01:00
Equim
f13e7ba940 Update locale (#219)
* i18n: update locale

* i18n: update locale


Former-commit-id: f2e21d0882f071f86bc3c225067313e54bcab1f4 [formerly 8cf56e48e3c40cb7bc1f9df58daa14ebc1d4d459] [formerly 48628588159737a35937da4511c1680600c97f51 [formerly e910f8580a]]
Former-commit-id: 2b0669041ae6f9c48d8163f3e20f69636a9ff291 [formerly 0a23672f8298176a8a03564a20d978225ef1bb6b]
Former-commit-id: 6d9a6057a6c67d645edf3fd617663afa1f42f977
2017-08-28 19:12:35 +01:00
Henrique Dias
3ebe219e96 Option to lock user's password #215
Former-commit-id: e4f0afef51c437bd55c58f12f2f45ce8e8c84bb0 [formerly 549235edf9d6c43d1454a8a00d7b6f832bb8a3ca] [formerly 12d099aa44bff7d995b05680e405d8040f1e1850 [formerly fc9ca4f6a4]]
Former-commit-id: e9666db20e2b473095f21c03d59f2a8fbf07929e [formerly 87ee9eb83daed5180c6a3714c0ddc861668d747b]
Former-commit-id: 5a15b05320c1eb28324e50cad7ca980d3eebcb02
2017-08-24 14:44:53 +01:00
Henrique Dias
d838856711 Popup on copy link.
Former-commit-id: e483df4402733b102d11b10436ff74aad11dfa7c [formerly 6d761c2ee838a9766f755b6c54cdc2ca388b5934] [formerly 1365e9e067af021ad0c680bae3af963dc4a90b28 [formerly 889871ec0a]]
Former-commit-id: ba443a90fded4501c0a6872eb293c14b2923c627 [formerly d21c6b9ab41869d2b10aa99853bc5b6931b63d96]
Former-commit-id: 7c19b231861797c62dc35c1e8a28f4ceeb8761c7
2017-08-24 14:13:29 +01:00
Henrique Dias
610d55c26f Fix override.
Former-commit-id: 515874171ed00bf70db40b2fc302d479d18fe93a [formerly ea9e47252daae3fb211150e2c3ab67ac97067a32] [formerly 2b0378600c957da5e2610f3cf8e82dd079d11f65 [formerly e285195fe1]]
Former-commit-id: 05d03bb250d4bc6bc4b3dbaf388f79a682f09841 [formerly 4ef3ceb2dd3862011a09ae39fbf4a78b889100e4]
Former-commit-id: 448224f282d6b14678f8d5d6629cbbe44ed19644
2017-08-24 12:33:54 +01:00
Henrique Dias
ac044016ee Implement SHIFT+Click to select range of files. #206
Former-commit-id: 15da15e0588d5977b42f71b8c96980248db73889 [formerly 95e464410bb619f4ff70b21d846e937bf9355e8a] [formerly cae63a6f6de24229ff04392fe16260c0d9045e8c [formerly 297a52e606]]
Former-commit-id: de9d291dba6c2e2d750c519f1fcbd59c34538ab1 [formerly 4fb3fcf8ba5726f6dde56fcbd8c1457799539a8e]
Former-commit-id: ad1568f33fa927dc750515856950b95b588d9f68
2017-08-24 09:14:26 +01:00
Henrique Dias
58af3461a8 Fix #216
Former-commit-id: 6d4b0674327746ed46fa4397f2d3ab01b8579bc6 [formerly eafa0d3effab4921e1a57de23f5a189bb75dced9] [formerly db40d686eabe4b78d9a5e8a6b1c20b623880d986 [formerly 9301966eb7]]
Former-commit-id: a148dc60ea64d054567f6d9b5d0c383893851da0 [formerly 02af0aeb73dd88cc157e5e21a9e9dd3658316d2a]
Former-commit-id: 1d4e9c50e20d3689f39224308bfadbe6097f5664
2017-08-23 11:05:54 +01:00
122 changed files with 4990 additions and 13216 deletions

View File

@@ -1,13 +1,13 @@
{
"presets": [
["env", { "modules": false }],
"stage-2"
],
"plugins": ["transform-runtime"],
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": [ "istanbul" ]
}
}
}
{
"presets": [
["env", { "modules": false }],
"stage-2"
],
"plugins": ["transform-runtime"],
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": [ "istanbul" ]
}
}
}

View File

@@ -1,4 +1,4 @@
assets/
testdata/
caddy/
.github/
assets/
testdata/
caddy/
.github/

View File

@@ -1,14 +1,14 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
# 4 space indentation
[*.go]
indent_style = tab
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
# 4 space indentation
[*.go]
indent_style = tab
indent_size = 4

View File

@@ -1,2 +1,2 @@
build/*.js
config/*.js
build/*.js
config/*.js

View File

@@ -1,27 +1,27 @@
// http://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module'
},
env: {
browser: true,
},
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
extends: 'standard',
// required to lint *.vue files
plugins: [
'html'
],
// add your custom rules here
'rules': {
// allow paren-less arrow functions
'arrow-parens': 0,
// allow async-await
'generator-star-spacing': 0,
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
}
}
// http://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module'
},
env: {
browser: true,
},
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
extends: 'standard',
// required to lint *.vue files
plugins: [
'html'
],
// add your custom rules here
'rules': {
// allow paren-less arrow functions
'arrow-parens': 0,
// allow async-await
'generator-star-spacing': 0,
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
}
}

View File

@@ -1,24 +1,24 @@
### Instructions (remove before submitting):
1. Are you asking for help with using Caddy or File Manager? Please use our forum instead: https://forum.caddyserver.com.
2. If you are filing a bug report, please answer the following questions.
3. If your issue is not a bug report, you do not need to use this template.
4. If not using with Caddy, ignore questions 1 and 2.
### 1. Have you downloaded File Manager from caddyserver.com? If yes, when have you done that? If no, and you are running a custom build, which is the revision of File Manager's repository?
### 2. What is your entire Caddyfile?
```text
(Put Caddyfile here)
```
### 3. What are you trying to do?
### 4. What did you expect to see?
### 5. What did you see instead (give full error messages and/or log)?
### 6. How can someone who is starting from scratch reproduce this behaviour as minimally as possible?
### Instructions (remove before submitting):
1. Are you asking for help with using Caddy or File Manager? Please use our forum instead: https://forum.caddyserver.com.
2. If you are filing a bug report, please answer the following questions.
3. If your issue is not a bug report, you do not need to use this template.
4. If not using with Caddy, ignore questions 1 and 2.
### 1. Have you downloaded File Manager from caddyserver.com? If yes, when have you done that? If no, and you are running a custom build, which is the revision of File Manager's repository?
### 2. What is your entire Caddyfile?
```text
(Put Caddyfile here)
```
### 3. What are you trying to do?
### 4. What did you expect to see?
### 5. What did you see instead (give full error messages and/or log)?
### 6. How can someone who is starting from scratch reproduce this behaviour as minimally as possible?

4
.gitignore vendored
View File

@@ -6,3 +6,7 @@ node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.idea
.vscode
package-lock.json
yarn.lock

View File

@@ -1,29 +1,37 @@
build:
main: cmd/filemanager/main.go
binary: filemanager
goos:
- darwin
- linux
- windows
- freebsd
- netbsd
- openbsd
goarch:
- amd64
- 386
- arm
- arm64
ignore:
- goos: openbsd
goarch: arm
goarm: 6
- goos: freebsd
goarch: arm
goarm: 6
archive:
name_template: "{{.Os}}-{{.Arch}}-{{ .ProjectName }}"
format: tar.gz
format_overrides:
- goos: windows
format: zip
build:
main: cmd/filemanager/main.go
binary: filemanager
goos:
- darwin
- linux
- windows
- freebsd
- netbsd
- openbsd
- dragonfly
- solaris
goarch:
- amd64
- 386
- arm
- arm64
- mips
- mips64
- mipsle
- mips64le
ignore:
- goos: openbsd
goarch: arm
goarm: 6
- goos: freebsd
goarch: arm
goarm: 6
- goos: linux
goarch: arm64
archive:
name_template: "{{.Os}}-{{.Arch}}-{{ .ProjectName }}"
format: tar.gz
format_overrides:
- goos: windows
format: zip

View File

@@ -1,6 +1,6 @@
language: go
go: 1.8.3
go: 1.x
env:
- "PATH=/home/travis/gopath/bin:$PATH"
@@ -15,7 +15,7 @@ install:
- go get github.com/tsenart/deadcode
script:
- gometalinter --disable-all -E vet -E gofmt -E misspell -E ineffassign -E goimports -E deadcode --exclude="rice-box.go" --tests ./...
- gometalinter --disable-all -E gofmt -E misspell -E ineffassign -E goimports -E deadcode --exclude="rice-box.go" --tests ./...
- go test ./... -timeout 30s
after_success:

View File

@@ -1,46 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hacdias@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hacdias@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -1,14 +1,14 @@
# Contributing
If you want to contribute or want to build the code from source, you will need to have the most recent version of Go and, if you want to change the static assets (JS, CSS, ...), Node.js installed on your computer. To start developing, you just need to do the following:
1. `go get github.com/hacdias/filemanager`
2. `cd $GOPATH/src/github.com/hacdias/filemanager`
3. `npm install`
4. `npm run dev` - regenerates the static assets automatically
5. `go install github.com/hacdias/filemanager/cmd/filemanager`
6. Execute `$GOPATH/bin/filemanager`
The steps 3 and 4 are only required **if you want to develop the front-end**. Otherwise, you can ignore them. Before pulling, if you made any change on assets folder, you must run the `build.sh` script on the root of this repository.
If you are using this as a Caddy plugin, you should use its [official instructions for plugins](https://github.com/mholt/caddy/wiki/Extending-Caddy#2-plug-in-your-plugin) and import `github.com/hacdias/filemanager/caddy/filemanager`.
# Contributing
If you want to contribute or want to build the code from source, you will need to have the most recent version of Go and, if you want to change the static assets (JS, CSS, ...), Node.js installed on your computer. To start developing, you just need to do the following:
1. `go get github.com/hacdias/filemanager/cmd/filemanager`
2. `cd $GOPATH/src/github.com/hacdias/filemanager`
3. `npm install`
4. `npm run dev` - regenerates the static assets automatically
5. `go install github.com/hacdias/filemanager/cmd/filemanager`
6. Execute `$GOPATH/bin/filemanager`
The steps 3 and 4 are only required **if you want to develop the front-end**. Otherwise, you can ignore them. Before pulling, if you made any change on assets folder, you must run the `build.sh` script on the root of this repository.
If you are using this as a Caddy plugin, you should use its [official instructions for plugins](https://github.com/mholt/caddy/wiki/Extending-Caddy#2-plug-in-your-plugin) and import `github.com/hacdias/filemanager/caddy/filemanager`.

View File

@@ -1,7 +1,7 @@
{
"port": 80,
"address": "",
"database": "/etc/database.db",
"database": "/database.db",
"scope": "/srv",
"allowCommands": true,
"allowEdit": true,

View File

@@ -1,22 +1,23 @@
FROM golang:alpine
COPY . /go/src/github.com/hacdias/filemanager
WORKDIR /go/src/github.com/hacdias/filemanager
RUN apk add --no-cache git
RUN go get ./...
WORKDIR /go/src/github.com/hacdias/filemanager/cmd/filemanager
RUN go build -ldflags "-X main.version=$(git tag -l --points-at HEAD)"
RUN mv filemanager /go/bin/filemanager
FROM alpine:latest
COPY --from=0 /go/bin/filemanager /usr/local/bin/filemanager
VOLUME /srv
EXPOSE 80
COPY Docker.json /etc/config.json
ENTRYPOINT ["/usr/local/bin/filemanager"]
CMD ["--config", "/etc/config.json"]
FROM golang:alpine
COPY . /go/src/github.com/hacdias/filemanager
WORKDIR /go/src/github.com/hacdias/filemanager
RUN apk add --no-cache git
RUN go get ./...
WORKDIR /go/src/github.com/hacdias/filemanager/cmd/filemanager
RUN CGO_ENABLED=0 go build -a
RUN mv filemanager /go/bin/filemanager
FROM scratch
COPY --from=0 /go/bin/filemanager /filemanager
VOLUME /tmp
VOLUME /srv
EXPOSE 80
COPY Docker.json /config.json
ENTRYPOINT ["/filemanager"]
CMD ["--config", "/config.json"]

View File

@@ -1,201 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

157
README.md
View File

@@ -1,78 +1,79 @@
![Preview](https://user-images.githubusercontent.com/5447088/28537288-39be4288-70a2-11e7-8ce9-0813d59f46b7.gif)
# filemanager
[![Build](https://img.shields.io/travis/hacdias/filemanager.svg?style=flat-square)](https://travis-ci.org/hacdias/filemanager)
[![Go Report Card](https://goreportcard.com/badge/github.com/hacdias/filemanager?style=flat-square)](https://goreportcard.com/report/hacdias/filemanager)
[![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](http://godoc.org/github.com/hacdias/filemanager)
filemanager provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app or as a middleware.
# Table of contents
+ [Getting started](#getting-started)
+ [Features](#features)
- [Users](#users)
- [Search](#search)
+ [Contributing](#contributing)
+ [Donate](#donate)
# Getting started
You can find the Getting Started guide on the [documentation](https://henriquedias.com/filemanager/quick-start/).
# Features
Easy login system.
![Login Page](https://user-images.githubusercontent.com/5447088/28432382-975493dc-6d7f-11e7-9190-23f8037159dc.jpg)
Listings of your files, available in two styles: mosaic and list. You can delete, move, rename, upload and create new files, as well as directories. Single files can be downloaded directly, and multiple files as *.zip*, *.tar*, *.tar.gz*, *.tar.bz2* or *.tar.xz*.
![Mosaic Listing](https://user-images.githubusercontent.com/5447088/28432384-9771bb4c-6d7f-11e7-8564-3a9bd6a3ce3a.jpg)
File Manager editor is powered by [Codemirror](https://codemirror.net/) and if you're working with markdown files with metadata, both parts will be separated from each other so you can focus on the content.
![Markdown Editor](https://user-images.githubusercontent.com/5447088/28432383-9756fdac-6d7f-11e7-8e58-fec49470d15f.jpg)
On the settings page, a regular user can set its own custom CSS to personalize the experience and change its password. For admins, they can manage the permissions of each user, set commands which can be executed when certain events are triggered (such as before saving and after saving) and change plugin's settings.
![Settings](https://user-images.githubusercontent.com/5447088/28432385-9776ec66-6d7f-11e7-90a5-891bacd4d02f.jpg)
We also allow the users to search in the directories and execute commands if allowed.
## Users
We support multiple users and each user can have its own scope and custom stylesheet. The administrator is able to choose which permissions should be given to the users, as well as the commands they can execute. Each user also have a set of rules, in which he can be prevented or allowed to access some directories (regular expressions included!).
![Users](https://user-images.githubusercontent.com/5447088/28432386-977f388a-6d7f-11e7-9006-87d16f05f1f8.jpg)
## Search
FileManager allows you to search through your files and it has some options. By default, your search will be something like this:
```
this are keywords
```
If you search for that it will look at every file that contains "this", "are" or "keywords" on their name. If you want to search for an exact term, you should surround your search by double quotes:
```
"this is the name"
```
That will search for any file that contains "this is the name" on its name. It won't search for each separated term this time.
By default, every search will be case sensitive. Although, you can make a case insensitive search by adding `case:insensitive` to the search terms, like this:
```
this are keywords case:insensitive
```
# Contributing
The contributing guidelines can be found [here](https://github.com/hacdias/filemanager/blob/master/CONTRIBUTING.md).
# Donate
Enjoying this project? You can [donate to its creator](https://henriquedias.com/donate/). He will appreciate.
![Preview](https://user-images.githubusercontent.com/5447088/28537288-39be4288-70a2-11e7-8ce9-0813d59f46b7.gif)
# filemanager
[![Build](https://img.shields.io/travis/hacdias/filemanager.svg?style=flat-square)](https://travis-ci.org/hacdias/filemanager)
[![Go Report Card](https://goreportcard.com/badge/github.com/hacdias/filemanager?style=flat-square)](https://goreportcard.com/report/hacdias/filemanager)
[![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](http://godoc.org/github.com/hacdias/filemanager)
[![Version](https://img.shields.io/github/release/hacdias/filemanager.svg?style=flat-square)](https://github.com/hacdias/filemanager/releases/latest)
filemanager provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app or as a middleware.
# Table of contents
+ [Getting started](#getting-started)
+ [Features](#features)
- [Users](#users)
- [Search](#search)
+ [Contributing](#contributing)
+ [Donate](#donate)
# Getting started
You can find the Getting Started guide on the [documentation](https://henriquedias.com/filemanager/quick-start/).
# Features
Easy login system.
![Login Page](https://user-images.githubusercontent.com/5447088/28432382-975493dc-6d7f-11e7-9190-23f8037159dc.jpg)
Listings of your files, available in two styles: mosaic and list. You can delete, move, rename, upload and create new files, as well as directories. Single files can be downloaded directly, and multiple files as *.zip*, *.tar*, *.tar.gz*, *.tar.bz2* or *.tar.xz*.
![Mosaic Listing](https://user-images.githubusercontent.com/5447088/28432384-9771bb4c-6d7f-11e7-8564-3a9bd6a3ce3a.jpg)
File Manager editor is powered by [Codemirror](https://codemirror.net/) and if you're working with markdown files with metadata, both parts will be separated from each other so you can focus on the content.
![Markdown Editor](https://user-images.githubusercontent.com/5447088/28432383-9756fdac-6d7f-11e7-8e58-fec49470d15f.jpg)
On the settings page, a regular user can set its own custom CSS to personalize the experience and change its password. For admins, they can manage the permissions of each user, set commands which can be executed when certain events are triggered (such as before saving and after saving) and change plugin's settings.
![Settings](https://user-images.githubusercontent.com/5447088/28432385-9776ec66-6d7f-11e7-90a5-891bacd4d02f.jpg)
We also allow the users to search in the directories and execute commands if allowed.
## Users
We support multiple users and each user can have its own scope and custom stylesheet. The administrator is able to choose which permissions should be given to the users, as well as the commands they can execute. Each user also have a set of rules, in which he can be prevented or allowed to access some directories (regular expressions included!).
![Users](https://user-images.githubusercontent.com/5447088/28432386-977f388a-6d7f-11e7-9006-87d16f05f1f8.jpg)
## Search
FileManager allows you to search through your files and it has some options. By default, your search will be something like this:
```
this are keywords
```
If you search for that it will look at every file that contains "this", "are" or "keywords" on their name. If you want to search for an exact term, you should surround your search by double quotes:
```
"this is the name"
```
That will search for any file that contains "this is the name" on its name. It won't search for each separated term this time.
By default, every search will be case sensitive. Although, you can make a case insensitive search by adding `case:insensitive` to the search terms, like this:
```
this are keywords case:insensitive
```
# Contributing
The contributing guidelines can be found [here](https://github.com/hacdias/filemanager/blob/master/CONTRIBUTING.md).
# Donate
Enjoying this project? You can [donate to its creator](https://henriquedias.com/donate/). He will appreciate.

View File

@@ -1,31 +1,31 @@
require('./check-versions')()
process.env.NODE_ENV = 'production'
var ora = require('ora')
var rm = require('rimraf')
var path = require('path')
var chalk = require('chalk')
var webpack = require('webpack')
var config = require('./config')
var webpackConfig = require('./webpack.prod.conf')
var spinner = ora('building for production...')
spinner.start()
rm(path.join(config.assetsRoot, config.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, function (err, stats) {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n\n')
console.log(chalk.cyan(' Build complete.\n'))
})
})
require('./check-versions')()
process.env.NODE_ENV = 'production'
var ora = require('ora')
var rm = require('rimraf')
var path = require('path')
var chalk = require('chalk')
var webpack = require('webpack')
var config = require('./config')
var webpackConfig = require('./webpack.prod.conf')
var spinner = ora('building for production...')
spinner.start()
rm(path.join(config.assetsRoot, config.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, function (err, stats) {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n\n')
console.log(chalk.cyan(' Build complete.\n'))
})
})

View File

@@ -1,48 +1,48 @@
var chalk = require('chalk')
var semver = require('semver')
var packageConfig = require('../../package.json')
var shell = require('shelljs')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
var versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
var warnings = []
for (var i = 0; i < versionRequirements.length; i++) {
var mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (var i = 0; i < warnings.length; i++) {
var warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}
var chalk = require('chalk')
var semver = require('semver')
var packageConfig = require('../../package.json')
var shell = require('shelljs')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
var versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
var warnings = []
for (var i = 0; i < versionRequirements.length; i++) {
var mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (var i = 0; i < warnings.length; i++) {
var warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}

View File

@@ -1,26 +1,26 @@
// see http://vuejs-templates.github.io/webpack for documentation.
var path = require('path')
module.exports = {
index: path.resolve(__dirname, '../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '{{ .BaseURL }}/',
build: {
env: {
NODE_ENV: '"production"'
},
productionSourceMap: true,
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
},
dev: {
env: {
NODE_ENV: '"development"'
},
produceSourceMap: true
}
}
// see http://vuejs-templates.github.io/webpack for documentation.
var path = require('path')
module.exports = {
index: path.resolve(__dirname, '../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '{{ .BaseURL }}/',
build: {
env: {
NODE_ENV: '"production"'
},
productionSourceMap: true,
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
},
dev: {
env: {
NODE_ENV: '"development"'
},
produceSourceMap: true
}
}

View File

@@ -1,17 +1,17 @@
// This service worker file is effectively a 'no-op' that will reset any
// previous service worker registered for the same host:port combination.
// In the production build, this file is replaced with an actual service worker
// file that will precache your site's local assets.
// See https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432
self.addEventListener('install', () => self.skipWaiting());
self.addEventListener('activate', () => {
self.clients.matchAll({ type: 'window' }).then(windowClients => {
for (let windowClient of windowClients) {
// Force open pages to refresh, so that they have a chance to load the
// fresh navigation response from the local dev server.
windowClient.navigate(windowClient.url);
}
});
// This service worker file is effectively a 'no-op' that will reset any
// previous service worker registered for the same host:port combination.
// In the production build, this file is replaced with an actual service worker
// file that will precache your site's local assets.
// See https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432
self.addEventListener('install', () => self.skipWaiting());
self.addEventListener('activate', () => {
self.clients.matchAll({ type: 'window' }).then(windowClients => {
for (let windowClient of windowClients) {
// Force open pages to refresh, so that they have a chance to load the
// fresh navigation response from the local dev server.
windowClient.navigate(windowClient.url);
}
});
});

View File

@@ -1,55 +1,55 @@
(function() {
'use strict';
// Check to make sure service workers are supported in the current browser,
// and that the current page is accessed from a secure origin. Using a
// service worker from an insecure origin will trigger JS console errors.
const isLocalhost = Boolean(window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
window.addEventListener('load', function() {
if ('serviceWorker' in navigator &&
(window.location.protocol === 'https:' || isLocalhost)) {
navigator.serviceWorker.register('{{ .BaseURL }}/sw.js')
.then(function(registration) {
// updatefound is fired if service-worker.js changes.
registration.onupdatefound = function() {
// updatefound is also fired the very first time the SW is installed,
// and there's no need to prompt for a reload at that point.
// So check here to see if the page is already controlled,
// i.e. whether there's an existing service worker.
if (navigator.serviceWorker.controller) {
// The updatefound event implies that registration.installing is set
const installingWorker = registration.installing;
installingWorker.onstatechange = function() {
switch (installingWorker.state) {
case 'installed':
// At this point, the old content will have been purged and the
// fresh content will have been added to the cache.
// It's the perfect time to display a "New content is
// available; please refresh." message in the page's interface.
break;
case 'redundant':
throw new Error('The installing ' +
'service worker became redundant.');
default:
// Ignore
}
};
}
};
}).catch(function(e) {
console.error('Error during service worker registration:', e);
});
}
});
})();
(function() {
'use strict';
// Check to make sure service workers are supported in the current browser,
// and that the current page is accessed from a secure origin. Using a
// service worker from an insecure origin will trigger JS console errors.
const isLocalhost = Boolean(window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
window.addEventListener('load', function() {
if ('serviceWorker' in navigator &&
(window.location.protocol === 'https:' || isLocalhost)) {
navigator.serviceWorker.register('{{ .BaseURL }}/sw.js')
.then(function(registration) {
// updatefound is fired if service-worker.js changes.
registration.onupdatefound = function() {
// updatefound is also fired the very first time the SW is installed,
// and there's no need to prompt for a reload at that point.
// So check here to see if the page is already controlled,
// i.e. whether there's an existing service worker.
if (navigator.serviceWorker.controller) {
// The updatefound event implies that registration.installing is set
const installingWorker = registration.installing;
installingWorker.onstatechange = function() {
switch (installingWorker.state) {
case 'installed':
// At this point, the old content will have been purged and the
// fresh content will have been added to the cache.
// It's the perfect time to display a "New content is
// available; please refresh." message in the page's interface.
break;
case 'redundant':
throw new Error('The installing ' +
'service worker became redundant.');
default:
// Ignore
}
};
}
};
}).catch(function(e) {
console.error('Error during service worker registration:', e);
});
}
});
})();

View File

@@ -1,70 +1,70 @@
var path = require('path')
var config = require('./config')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
exports.assetsPath = function (_path) {
var assetsSubDirectory = config.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
var cssLoader = {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
var loaders = [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
var output = []
var loaders = exports.cssLoaders(options)
for (var extension in loaders) {
var loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
var path = require('path')
var config = require('./config')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
exports.assetsPath = function (_path) {
var assetsSubDirectory = config.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
var cssLoader = {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
var loaders = [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
var output = []
var loaders = exports.cssLoaders(options)
for (var extension in loaders) {
var loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}

View File

@@ -1,12 +1,12 @@
var utils = require('./utils')
var config = require('./config')
var isProduction = process.env.NODE_ENV === 'production'
module.exports = {
loaders: utils.cssLoaders({
sourceMap: isProduction
? config.build.productionSourceMap
: config.dev.produceSourceMap,
extract: isProduction
})
}
var utils = require('./utils')
var config = require('./config')
var isProduction = process.env.NODE_ENV === 'production'
module.exports = {
loaders: utils.cssLoaders({
sourceMap: isProduction
? config.build.productionSourceMap
: config.dev.produceSourceMap,
extract: isProduction
})
}

View File

@@ -1,69 +1,69 @@
var path = require('path')
var utils = require('./utils')
var config = require('./config')
var vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
entry: {
app: './assets/src/main.js'
},
output: {
path: config.assetsRoot,
filename: '[name].js',
publicPath: config.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src')
}
},
module: {
rules: [
{
test: /\.(yml|yaml)$/,
loader: 'yml-loader'
},
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter')
}
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
// limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
}
}
var path = require('path')
var utils = require('./utils')
var config = require('./config')
var vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
entry: {
app: './assets/src/main.js'
},
output: {
path: config.assetsRoot,
filename: '[name].js',
publicPath: config.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src')
}
},
module: {
rules: [
{
test: /\.(yml|yaml)$/,
loader: 'yml-loader'
},
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter')
}
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
// limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
}
}

View File

@@ -1,81 +1,81 @@
var fs = require('fs')
var path = require('path')
var utils = require('./utils')
var webpack = require('webpack')
var config = require('./config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
var CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = merge(baseWebpackConfig, {
watch: true,
module: {
rules: utils.styleLoaders({
sourceMap: config.dev.produceSourceMap,
extract: true
})
},
devtool: '#cheap-module-eval-source-map',
output: {
path: config.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
new FriendlyErrorsPlugin(),
new webpack.DefinePlugin({
'process.env': config.dev.env
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css')
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.index,
template: 'assets/index.html',
inject: true,
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency',
serviceWorkerLoader: `<script>${fs.readFileSync(path.join(__dirname,
'./service-worker-dev.js'), 'utf-8')}</script>`
}),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module, count) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
chunks: ['vendor']
}),
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.assetsSubDirectory,
ignore: ['.*']
},
{
from: path.resolve(__dirname, '../../node_modules/codemirror/mode/*/*'),
to: path.join(config.assetsSubDirectory, 'js/codemirror/mode/[name]/[name].js')
}
])
]
})
var fs = require('fs')
var path = require('path')
var utils = require('./utils')
var webpack = require('webpack')
var config = require('./config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
var CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = merge(baseWebpackConfig, {
watch: true,
module: {
rules: utils.styleLoaders({
sourceMap: config.dev.produceSourceMap,
extract: true
})
},
devtool: '#cheap-module-eval-source-map',
output: {
path: config.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
new FriendlyErrorsPlugin(),
new webpack.DefinePlugin({
'process.env': config.dev.env
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css')
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.index,
template: 'assets/index.html',
inject: true,
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency',
serviceWorkerLoader: `<script>${fs.readFileSync(path.join(__dirname,
'./service-worker-dev.js'), 'utf-8')}</script>`
}),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module, count) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
chunks: ['vendor']
}),
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.assetsSubDirectory,
ignore: ['.*']
},
{
from: path.resolve(__dirname, '../../node_modules/codemirror/mode/*/*'),
to: path.join(config.assetsSubDirectory, 'js/codemirror/mode/[name]/[name].js')
}
])
]
})

View File

@@ -7,6 +7,8 @@
<meta name="base" content="{{ .BaseURL }}">
<meta name="staticgen" content="{{ .StaticGen }}">
<meta name="noauth" content="{{ .NoAuth }}">
<meta name="version" content="{{ .Version }}">
<meta name="recaptcha" content="{{ .ReCaptchaKey }}">
<title>File Manager</title>
<link rel="icon" type="image/png" sizes="32x32" href="{{ .BaseURL }}/static/img/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="{{ .BaseURL }}/static/img/icons/favicon-16x16.png">
@@ -23,6 +25,13 @@
<!-- Add to home screen for Windows -->
<meta name="msapplication-TileImage" content="{{ .BaseURL }}/static/img/icons/msapplication-icon-144x144.png">
<meta name="msapplication-TileColor" content="#2979ff">
<script>CSS = "{{ .CSS }}"</script>
{{ if .ReCaptcha -}}
<script src='https://www.google.com/recaptcha/api.js?render=explicit'></script>
{{ end }}
<% for (var chunk of webpack.chunks) {
for (var file of chunk.files) {
if (file.match(/\.(js|css)$/)) { %>

View File

@@ -1,18 +1,74 @@
<template>
<router-view></router-view>
<router-view :dependencies="loaded" @update:css="updateCSS" @clean:css="cleanCSS"></router-view>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'app',
mounted: function () {
// Remove loading animation.
let loading = document.getElementById('loading')
loading.classList.add('done')
computed: mapState(['recaptcha']),
data () {
return {
loaded: false
}
},
mounted () {
if (this.recaptcha.length === 0) {
this.unload()
return
}
setTimeout(function () {
loading.parentNode.removeChild(loading)
}, 200)
let check = () => {
if (typeof window.grecaptcha === 'undefined') {
setTimeout(check, 100)
return
}
this.unload()
}
check()
},
methods: {
unload () {
this.loaded = true
// Remove loading animation.
let loading = document.getElementById('loading')
loading.classList.add('done')
setTimeout(function () {
loading.parentNode.removeChild(loading)
}, 200)
this.updateCSS()
},
updateCSS (global = false) {
let css = this.$store.state.css
if (typeof this.$store.state.user.css === 'string' && !global) {
css += '\n' + this.$store.state.user.css
}
this.removeCSS()
let style = document.createElement('style')
style.title = 'custom-css'
style.type = 'text/css'
style.appendChild(document.createTextNode(css))
document.head.appendChild(style)
},
removeCSS () {
let style = document.querySelector('style[title="custom-css"]')
if (style === undefined || style === null) {
return
}
style.parentElement.removeChild(style)
},
cleanCSS () {
this.updateCSS(true)
}
}
}
</script>

View File

@@ -1,5 +1,5 @@
<svg id="content" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144 144">
<circle cx="72" cy="72" r="72" fill="#2979ff"/>
<circle cx="72" cy="72" r="48" fill="#40c4ff"/>
<circle cx="72" cy="72" r="24" fill="#fff"/>
<svg id="content" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144 144">
<circle cx="72" cy="72" r="72" fill="#2979ff"/>
<circle cx="72" cy="72" r="48" fill="#40c4ff"/>
<circle cx="72" cy="72" r="24" fill="#fff"/>
</svg>

Before

Width:  |  Height:  |  Size: 235 B

After

Width:  |  Height:  |  Size: 239 B

View File

@@ -1,22 +1,23 @@
<template>
<select v-on:change="change" :value="selected">
<option value="en">{{ $t('languages.en') }}</option>
<option value="fr">{{ $t('languages.fr') }}</option>
<option value="pt">{{ $t('languages.pt') }}</option>
<option value="ja">{{ $t('languages.ja') }}</option>
<option value="zh-cn">{{ $t('languages.zhCN') }}</option>
<option value="zh-tw">{{ $t('languages.zhTW') }}</option>
</select>
</template>
<script>
export default {
name: 'languages',
props: [ 'selected' ],
methods: {
change (event) {
this.$emit('update:selected', event.target.value)
}
}
}
</script>
<template>
<select v-on:change="change" :value="selected">
<option value="en">{{ $t('languages.en') }}</option>
<option value="fr">{{ $t('languages.fr') }}</option>
<option value="pt">{{ $t('languages.pt') }}</option>
<option value="ja">{{ $t('languages.ja') }}</option>
<option value="zh-cn">{{ $t('languages.zhCN') }}</option>
<option value="zh-tw">{{ $t('languages.zhTW') }}</option>
<option value="es">{{ $t('languages.es') }}</option>
</select>
</template>
<script>
export default {
name: 'languages',
props: [ 'selected' ],
methods: {
change (event) {
this.$emit('update:selected', event.target.value)
}
}
}
</script>

View File

@@ -1,265 +1,265 @@
<template>
<div id="search" @click="open" v-bind:class="{ active , ongoing }">
<div id="input">
<button v-if="active" class="action" @click="close" :aria-label="$t('buttons.close')" :title="$t('buttons.close')">
<i class="material-icons">arrow_back</i>
</button>
<i v-else class="material-icons">search</i>
<input type="text"
@keyup="keyup"
@keyup.enter="submit"
ref="input"
:autofocus="active"
v-model.trim="value"
:aria-label="$t('search.writeToSearch')"
:placeholder="placeholder">
</div>
<div id="result">
<div>
<template v-if="search.length === 0 && commands.length === 0">
<p>{{ text }}</p>
<template v-if="value.length === 0">
<div class="boxes">
<h3>{{ $t('search.types') }}</h3>
<div>
<div tabindex="0"
role="button"
@click="init('type:image')"
:aria-label="$t('search.images')">
<i class="material-icons">insert_photo</i>
<p>{{ $t('search.images') }}</p>
</div>
<div tabindex="0"
role="button"
@click="init('type:audio')"
:aria-label="$t('search.music')">
<i class="material-icons">volume_up</i>
<p>{{ $t('search.music') }}</p>
</div>
<div tabindex="0"
role="button"
@click="init('type:video')"
:aria-label="$t('search.video')">
<i class="material-icons">movie</i>
<p>{{ $t('search.video') }}</p>
</div>
<div tabindex="0"
role="button"
@click="init('type:pdf')"
:aria-label="$t('search.pdf')">
<i class="material-icons">picture_as_pdf</i>
<p>{{ $t('search.pdf') }}</p>
</div>
</div>
</div>
</template>
</template>
<ul v-else-if="search.length > 0">
<li v-for="s in search">
<router-link @click.native="close" :to="'./' + s.path">
<i v-if="s.dir" class="material-icons">folder</i>
<i v-else class="material-icons">insert_drive_file</i>
<span>./{{ s.path }}</span>
</router-link>
</li>
</ul>
<pre v-else-if="commands.length > 0">
<template v-for="c in commands">{{ c }}</template>
</pre>
</div>
<p id="renew"><i class="material-icons spin">autorenew</i></p>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
import url from '@/utils/url'
import * as api from '@/utils/api'
export default {
name: 'search',
data: function () {
return {
value: '',
active: false,
ongoing: false,
scrollable: null,
search: [],
commands: [],
reload: false
}
},
watch: {
show (val, old) {
this.active = (val === 'search')
// If the hover was search and now it's something else
// we should blur the input.
if (old === 'search' && val !== 'search') {
if (this.reload) {
this.$store.commit('setReload', true)
}
document.body.style.overflow = 'auto'
this.reset()
this.$refs.input.blur()
}
// If we are starting to show the search box, we should
// focus the input.
if (val === 'search') {
this.reload = false
this.$refs.input.focus()
document.body.style.overflow = 'hidden'
}
}
},
computed: {
...mapState(['user', 'show']),
// Placeholder value.
placeholder: function () {
if (this.user.allowCommands && this.user.commands.length > 0) {
return this.$t('search.searchOrCommand')
}
return this.$t('search.search')
},
// The text that is shown on the results' box while
// there is no search result or command output to show.
text: function () {
if (this.ongoing) {
return ''
}
if (this.value.length === 0) {
if (this.user.allowCommands && this.user.commands.length > 0) {
return `${this.$t('search.searchOrSupportedCommand')} ${this.user.commands.join(', ')}.`
}
this.$t('search.type')
}
if (!this.supported() || !this.user.allowCommands) {
return this.$t('search.pressToSearch')
} else {
return this.$t('search.pressToExecute')
}
}
},
mounted: function () {
// Gets the result div which will be scrollable.
this.scrollable = document.querySelector('#search #result')
// Adds the keydown event on window for the ESC key, so
// when it's pressed, it closes the search window.
window.addEventListener('keydown', (event) => {
if (event.keyCode === 27) {
this.$store.commit('closeHovers')
}
})
},
methods: {
// Sets the search to active.
open (event) {
this.$store.commit('showHover', 'search')
},
// Closes the search and prevents the event
// of propagating so it doesn't trigger the
// click event on #search.
close (event) {
event.stopPropagation()
event.preventDefault()
this.$store.commit('closeHovers')
},
// Checks if the current input is a supported command.
supported () {
let pieces = this.value.split(' ')
for (let i = 0; i < this.user.commands.length; i++) {
if (pieces[0] === this.user.commands[i]) {
return true
}
}
return false
},
// Initializes the search with a default value.
init (string) {
this.value = string + ' '
this.$refs.input.focus()
},
// Resets the search box value.
reset () {
this.value = ''
this.active = false
this.ongoing = false
this.search = []
this.commands = []
},
// When the user presses a key, if it is ESC
// then it will close the search box. Otherwise,
// it will set the search box to active and clean
// the search results, as well as commands'.
keyup (event) {
if (event.keyCode === 27) {
this.close(event)
return
}
this.search.length = 0
this.commands.length = 0
},
// Submits the input to the server and sets ongoing to true.
submit (event) {
this.ongoing = true
let path = this.$route.path
if (this.$store.state.req.kind !== 'listing') {
path = url.removeLastDir(path) + '/'
}
// In case of being a command.
if (this.supported() && this.user.allowCommands) {
api.command(path, this.value,
(event) => {
this.commands.push(event.data)
this.scrollable.scrollTop = this.scrollable.scrollHeight
},
(event) => {
this.reload = true
this.ongoing = false
this.scrollable.scrollTop = this.scrollable.scrollHeight
}
)
return
}
// In case of being a search.
api.search(path, this.value,
(event) => {
let response = JSON.parse(event.data)
if (response.path[0] === '/') {
response.path = response.path.substring(1)
}
this.search.push(response)
this.scrollable.scrollTop = this.scrollable.scrollHeight
},
(event) => {
this.ongoing = false
this.scrollable.scrollTop = this.scrollable.scrollHeight
}
)
}
}
}
</script>
<template>
<div id="search" @click="open" v-bind:class="{ active , ongoing }">
<div id="input">
<button v-if="active" class="action" @click="close" :aria-label="$t('buttons.close')" :title="$t('buttons.close')">
<i class="material-icons">arrow_back</i>
</button>
<i v-else class="material-icons">search</i>
<input type="text"
@keyup="keyup"
@keyup.enter="submit"
ref="input"
:autofocus="active"
v-model.trim="value"
:aria-label="$t('search.writeToSearch')"
:placeholder="placeholder">
</div>
<div id="result">
<div>
<template v-if="search.length === 0 && commands.length === 0">
<p>{{ text }}</p>
<template v-if="value.length === 0">
<div class="boxes">
<h3>{{ $t('search.types') }}</h3>
<div>
<div tabindex="0"
role="button"
@click="init('type:image')"
:aria-label="$t('search.images')">
<i class="material-icons">insert_photo</i>
<p>{{ $t('search.images') }}</p>
</div>
<div tabindex="0"
role="button"
@click="init('type:audio')"
:aria-label="$t('search.music')">
<i class="material-icons">volume_up</i>
<p>{{ $t('search.music') }}</p>
</div>
<div tabindex="0"
role="button"
@click="init('type:video')"
:aria-label="$t('search.video')">
<i class="material-icons">movie</i>
<p>{{ $t('search.video') }}</p>
</div>
<div tabindex="0"
role="button"
@click="init('type:pdf')"
:aria-label="$t('search.pdf')">
<i class="material-icons">picture_as_pdf</i>
<p>{{ $t('search.pdf') }}</p>
</div>
</div>
</div>
</template>
</template>
<ul v-else-if="search.length > 0">
<li v-for="s in search">
<router-link @click.native="close" :to="'./' + s.path">
<i v-if="s.dir" class="material-icons">folder</i>
<i v-else class="material-icons">insert_drive_file</i>
<span>./{{ s.path }}</span>
</router-link>
</li>
</ul>
<pre v-else-if="commands.length > 0">
<template v-for="c in commands">{{ c }}</template>
</pre>
</div>
<p id="renew"><i class="material-icons spin">autorenew</i></p>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
import url from '@/utils/url'
import * as api from '@/utils/api'
export default {
name: 'search',
data: function () {
return {
value: '',
active: false,
ongoing: false,
scrollable: null,
search: [],
commands: [],
reload: false
}
},
watch: {
show (val, old) {
this.active = (val === 'search')
// If the hover was search and now it's something else
// we should blur the input.
if (old === 'search' && val !== 'search') {
if (this.reload) {
this.$store.commit('setReload', true)
}
document.body.style.overflow = 'auto'
this.reset()
this.$refs.input.blur()
}
// If we are starting to show the search box, we should
// focus the input.
if (val === 'search') {
this.reload = false
this.$refs.input.focus()
document.body.style.overflow = 'hidden'
}
}
},
computed: {
...mapState(['user', 'show']),
// Placeholder value.
placeholder: function () {
if (this.user.allowCommands && this.user.commands.length > 0) {
return this.$t('search.searchOrCommand')
}
return this.$t('search.search')
},
// The text that is shown on the results' box while
// there is no search result or command output to show.
text: function () {
if (this.ongoing) {
return ''
}
if (this.value.length === 0) {
if (this.user.allowCommands && this.user.commands.length > 0) {
return `${this.$t('search.searchOrSupportedCommand')} ${this.user.commands.join(', ')}.`
}
this.$t('search.type')
}
if (!this.supported() || !this.user.allowCommands) {
return this.$t('search.pressToSearch')
} else {
return this.$t('search.pressToExecute')
}
}
},
mounted: function () {
// Gets the result div which will be scrollable.
this.scrollable = document.querySelector('#search #result')
// Adds the keydown event on window for the ESC key, so
// when it's pressed, it closes the search window.
window.addEventListener('keydown', (event) => {
if (event.keyCode === 27) {
this.$store.commit('closeHovers')
}
})
},
methods: {
// Sets the search to active.
open (event) {
this.$store.commit('showHover', 'search')
},
// Closes the search and prevents the event
// of propagating so it doesn't trigger the
// click event on #search.
close (event) {
event.stopPropagation()
event.preventDefault()
this.$store.commit('closeHovers')
},
// Checks if the current input is a supported command.
supported () {
let pieces = this.value.split(' ')
for (let i = 0; i < this.user.commands.length; i++) {
if (pieces[0] === this.user.commands[i]) {
return true
}
}
return false
},
// Initializes the search with a default value.
init (string) {
this.value = string + ' '
this.$refs.input.focus()
},
// Resets the search box value.
reset () {
this.value = ''
this.active = false
this.ongoing = false
this.search = []
this.commands = []
},
// When the user presses a key, if it is ESC
// then it will close the search box. Otherwise,
// it will set the search box to active and clean
// the search results, as well as commands'.
keyup (event) {
if (event.keyCode === 27) {
this.close(event)
return
}
this.search.length = 0
this.commands.length = 0
},
// Submits the input to the server and sets ongoing to true.
submit (event) {
this.ongoing = true
let path = this.$route.path
if (this.$store.state.req.kind !== 'listing') {
path = url.removeLastDir(path) + '/'
}
// In case of being a command.
if (this.supported() && this.user.allowCommands) {
api.command(path, this.value,
(event) => {
this.commands.push(event.data)
this.scrollable.scrollTop = this.scrollable.scrollHeight
},
(event) => {
this.reload = true
this.ongoing = false
this.scrollable.scrollTop = this.scrollable.scrollHeight
}
)
return
}
// In case of being a search.
api.search(path, this.value,
(event) => {
let response = JSON.parse(event.data)
if (response.path[0] === '/') {
response.path = response.path.substring(1)
}
this.search.push(response)
this.scrollable.scrollTop = this.scrollable.scrollHeight
},
(event) => {
this.ongoing = false
this.scrollable.scrollTop = this.scrollable.scrollHeight
}
)
}
}
}
</script>

View File

@@ -59,7 +59,7 @@
</div>
<p class="credits">
<span>{{ $t('sidebar.servedWith') }} <a rel="noopener noreferrer" href="https://github.com/hacdias/filemanager">File Manager</a>.</span>
<span><a rel="noopener noreferrer" href="https://github.com/hacdias/filemanager">File Manager</a> v{{ version }}</span>
<span><a @click="help">{{ $t('sidebar.help') }}</a></span>
</p>
</nav>
@@ -72,7 +72,7 @@ import auth from '@/utils/auth'
export default {
name: 'sidebar',
computed: {
...mapState(['user', 'staticGen']),
...mapState(['user', 'staticGen', 'version']),
active () {
return this.$store.state.show === 'sidebar'
}

View File

@@ -1,17 +1,17 @@
<template>
<button @click="show" :aria-label="$t('buttons.copy')" :title="$t('buttons.copy')" class="action" id="copy-button">
<i class="material-icons">content_copy</i>
<span>{{ $t('buttons.copyFile') }}</span>
</button>
</template>
<script>
export default {
name: 'copy-button',
methods: {
show: function (event) {
this.$store.commit('showHover', 'copy')
}
}
}
</script>
<template>
<button @click="show" :aria-label="$t('buttons.copy')" :title="$t('buttons.copy')" class="action" id="copy-button">
<i class="material-icons">content_copy</i>
<span>{{ $t('buttons.copyFile') }}</span>
</button>
</template>
<script>
export default {
name: 'copy-button',
methods: {
show: function (event) {
this.$store.commit('showHover', 'copy')
}
}
}
</script>

View File

@@ -1,17 +1,17 @@
<template>
<button @click="show" :aria-label="$t('buttons.delete')" :title="$t('buttons.delete')" class="action" id="delete-button">
<i class="material-icons">delete</i>
<span>{{ $t('buttons.delete') }}</span>
</button>
</template>
<script>
export default {
name: 'delete-button',
methods: {
show: function (event) {
this.$store.commit('showHover', 'delete')
}
}
}
</script>
<template>
<button @click="show" :aria-label="$t('buttons.delete')" :title="$t('buttons.delete')" class="action" id="delete-button">
<i class="material-icons">delete</i>
<span>{{ $t('buttons.delete') }}</span>
</button>
</template>
<script>
export default {
name: 'delete-button',
methods: {
show: function (event) {
this.$store.commit('showHover', 'delete')
}
}
}
</script>

View File

@@ -1,39 +1,39 @@
<template>
<button @click="download" :aria-label="$t('buttons.download')" :title="$t('buttons.download')" id="download-button" class="action">
<i class="material-icons">file_download</i>
<span>{{ $t('buttons.download') }}</span>
<span v-if="selectedCount > 0" class="counter">{{ selectedCount }}</span>
</button>
</template>
<script>
import {mapGetters, mapState} from 'vuex'
import * as api from '@/utils/api'
export default {
name: 'download-button',
computed: {
...mapState(['req', 'selected']),
...mapGetters(['selectedCount'])
},
methods: {
download: function (event) {
// If we are not on a listing, download the current file.
if (this.req.kind !== 'listing') {
api.download(null, this.$route.path)
return
}
// If we are on a listing and there is one element selected,
// download it.
if (this.selectedCount === 1 && !this.req.items[this.selected[0]].isDir) {
api.download(null, this.req.items[this.selected[0]].url)
return
}
// Otherwise show the prompt to choose the formt of the download.
this.$store.commit('showHover', 'download')
}
}
}
</script>
<template>
<button @click="download" :aria-label="$t('buttons.download')" :title="$t('buttons.download')" id="download-button" class="action">
<i class="material-icons">file_download</i>
<span>{{ $t('buttons.download') }}</span>
<span v-if="selectedCount > 0" class="counter">{{ selectedCount }}</span>
</button>
</template>
<script>
import {mapGetters, mapState} from 'vuex'
import * as api from '@/utils/api'
export default {
name: 'download-button',
computed: {
...mapState(['req', 'selected']),
...mapGetters(['selectedCount'])
},
methods: {
download: function (event) {
// If we are not on a listing, download the current file.
if (this.req.kind !== 'listing') {
api.download(null, this.$route.path)
return
}
// If we are on a listing and there is one element selected,
// download it.
if (this.selectedCount === 1 && !this.req.items[this.selected[0]].isDir) {
api.download(null, this.req.items[this.selected[0]].url)
return
}
// Otherwise show the prompt to choose the formt of the download.
this.$store.commit('showHover', 'download')
}
}
}
</script>

View File

@@ -1,17 +1,17 @@
<template>
<button :title="$t('buttons.info')" :aria-label="$t('buttons.info')" class="action" @click="show">
<i class="material-icons">info</i>
<span>{{ $t('buttons.info') }}</span>
</button>
</template>
<script>
export default {
name: 'info-button',
methods: {
show: function (event) {
this.$store.commit('showHover', 'info')
}
}
}
</script>
<template>
<button :title="$t('buttons.info')" :aria-label="$t('buttons.info')" class="action" @click="show">
<i class="material-icons">info</i>
<span>{{ $t('buttons.info') }}</span>
</button>
</template>
<script>
export default {
name: 'info-button',
methods: {
show: function (event) {
this.$store.commit('showHover', 'info')
}
}
}
</script>

View File

@@ -1,17 +1,17 @@
<template>
<button @click="show" :aria-label="$t('buttons.move')" :title="$t('buttons.move')" class="action" id="move-button">
<i class="material-icons">forward</i>
<span>{{ $t('buttons.moveFile') }}</span>
</button>
</template>
<script>
export default {
name: 'move-button',
methods: {
show: function (event) {
this.$store.commit('showHover', 'move')
}
}
}
</script>
<template>
<button @click="show" :aria-label="$t('buttons.move')" :title="$t('buttons.move')" class="action" id="move-button">
<i class="material-icons">forward</i>
<span>{{ $t('buttons.moveFile') }}</span>
</button>
</template>
<script>
export default {
name: 'move-button',
methods: {
show: function (event) {
this.$store.commit('showHover', 'move')
}
}
}
</script>

View File

@@ -1,17 +1,17 @@
<template>
<button @click="show" :aria-label="$t('buttons.rename')" :title="$t('buttons.rename')" class="action" id="rename-button">
<i class="material-icons">mode_edit</i>
<span>{{ $t('buttons.rename') }}</span>
</button>
</template>
<script>
export default {
name: 'rename-button',
methods: {
show: function (event) {
this.$store.commit('showHover', 'rename')
}
}
}
</script>
<template>
<button @click="show" :aria-label="$t('buttons.rename')" :title="$t('buttons.rename')" class="action" id="rename-button">
<i class="material-icons">mode_edit</i>
<span>{{ $t('buttons.rename') }}</span>
</button>
</template>
<script>
export default {
name: 'rename-button',
methods: {
show: function (event) {
this.$store.commit('showHover', 'rename')
}
}
}
</script>

View File

@@ -1,21 +1,21 @@
<template>
<button @click="show"
:aria-label="$t('buttons.schedule')"
:title="$t('buttons.schedule')"
id="schedule-button"
class="action">
<i class="material-icons">alarm</i>
<span>{{ $t('buttons.schedule') }}</span>
</button>
</template>
<script>
export default {
name: 'schedule-button',
methods: {
show: function (event) {
this.$store.commit('showHover', 'schedule')
}
}
}
</script>
<template>
<button @click="show"
:aria-label="$t('buttons.schedule')"
:title="$t('buttons.schedule')"
id="schedule-button"
class="action">
<i class="material-icons">alarm</i>
<span>{{ $t('buttons.schedule') }}</span>
</button>
</template>
<script>
export default {
name: 'schedule-button',
methods: {
show: function (event) {
this.$store.commit('showHover', 'schedule')
}
}
}
</script>

View File

@@ -1,32 +1,35 @@
<template>
<button @click="change" :aria-label="$t('buttons.switchView')" :title="$t('buttons.switchView')" class="action" id="switch-view-button">
<i class="material-icons">{{ icon() }}</i>
<i class="material-icons">{{ icon }}</i>
<span>{{ $t('buttons.switchView') }}</span>
</button>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
import { updateUser } from '@/utils/api'
export default {
name: 'switch-button',
computed: {
...mapState(['user']),
icon: function () {
if (this.user.viewMode === 'mosaic') return 'view_list'
return 'view_module'
}
},
methods: {
...mapMutations(['updateUser']),
change: function (event) {
// If we are on mobile we should close the dropdown.
this.$store.commit('closeHovers')
let display = 'mosaic'
let user = {...this.user}
user.viewMode = (this.icon === 'view_list') ? 'list' : 'mosaic'
if (this.$store.state.req.display === 'mosaic') {
display = 'list'
}
this.$store.commit('listingDisplay', display)
let path = this.$store.state.baseURL
if (path === '') path = '/'
document.cookie = `display=${display}; max-age=31536000; path=${path}`
},
icon: function () {
if (this.$store.state.req.display === 'mosaic') return 'view_list'
return 'view_module'
updateUser(user, 'partial').then(() => {
this.updateUser({ viewMode: user.viewMode })
}).catch(this.$showError)
}
}
}

View File

@@ -1,17 +1,17 @@
<template>
<button @click="upload" :aria-label="$t('buttons.upload')" :title="$t('buttons.upload')" class="action" id="upload-button">
<i class="material-icons">file_upload</i>
<span>{{ $t('buttons.upload') }}</span>
</button>
</template>
<script>
export default {
name: 'upload-button',
methods: {
upload: function (event) {
document.getElementById('upload-input').click()
}
}
}
</script>
<template>
<button @click="upload" :aria-label="$t('buttons.upload')" :title="$t('buttons.upload')" class="action" id="upload-button">
<i class="material-icons">file_upload</i>
<span>{{ $t('buttons.upload') }}</span>
</button>
</template>
<script>
export default {
name: 'upload-button',
methods: {
upload: function (event) {
document.getElementById('upload-input').click()
}
}
}
</script>

View File

@@ -134,7 +134,7 @@ export default {
})
.catch(error => {
buttons.done(button)
this.$store.commit('showError', error)
this.$showError(error)
this.$store.commit('setSchedule', '')
})
}

View File

@@ -7,7 +7,7 @@
<input style="display:none" type="file" id="upload-input" @change="uploadInput($event)" multiple>
</div>
<div v-else id="listing"
:class="req.display"
:class="user.viewMode"
@dragenter="dragEnter"
@dragend="dragEnd">
<div>
@@ -98,7 +98,7 @@ export default {
name: 'listing',
components: { Item },
computed: {
...mapState(['req', 'selected']),
...mapState(['req', 'selected', 'user']),
nameSorted () {
return (this.req.sort === 'name')
},
@@ -196,6 +196,10 @@ export default {
})
},
paste (event) {
if (event.target.tagName.toLowerCase() === 'input') {
return
}
event.preventDefault()
let items = []
@@ -210,17 +214,13 @@ export default {
if (this.$store.state.clipboard.key === 'x') {
api.move(items).then(() => {
this.$store.commit('setReload', true)
}).catch(error => {
this.$store.commit('showError', error)
})
}).catch(this.$showError)
return
}
api.copy(items).then(() => {
this.$store.commit('setReload', true)
}).catch(error => {
this.$store.commit('showError', error)
})
}).catch(this.$showError)
},
resizeEvent () {
// Update the columns size based on the window width.
@@ -267,7 +267,7 @@ export default {
.then(req => {
this.checkConflict(files, req.items, base)
})
.catch(error => { console.log(error) })
.catch(this.$showError)
return
}
@@ -348,7 +348,7 @@ export default {
})
.catch(error => {
finish()
this.$store.commit('showError', error)
this.$showError(error)
})
return false

View File

@@ -109,21 +109,36 @@ export default {
.then(() => {
this.$store.commit('setReload', true)
})
.catch(error => {
this.$store.commit('showError', error)
})
.catch(this.$showError)
},
click: function (event) {
if (this.selectedCount !== 0) event.preventDefault()
if (this.$store.state.selected.indexOf(this.index) === -1) {
if (!event.ctrlKey && !this.$store.state.multiple) this.resetSelected()
this.addSelected(this.index)
} else {
if (this.$store.state.selected.indexOf(this.index) !== -1) {
this.removeSelected(this.index)
return
}
return false
if (event.shiftKey && this.selected.length === 1) {
let fi = 0
let la = 0
if (this.index > this.selected[0]) {
fi = this.selected[0] + 1
la = this.index
} else {
fi = this.index
la = this.selected[0] - 1
}
for (; fi <= la; fi++) {
this.addSelected(fi)
}
return
}
if (!event.ctrlKey && !this.$store.state.multiple) this.resetSelected()
this.addSelected(this.index)
},
touchstart (event) {
setTimeout(() => {

View File

@@ -20,8 +20,8 @@
<div class="preview">
<img v-if="req.type == 'image'" :src="raw()">
<audio v-else-if="req.type == 'audio'" :src="raw()" controls></audio>
<video v-else-if="req.type == 'video'" :src="raw()" controls>
<audio v-else-if="req.type == 'audio'" :src="raw()" autoplay controls></audio>
<video v-else-if="req.type == 'video'" :src="raw()" autoplay controls>
Sorry, your browser doesn't support embedded videos,
but don't worry, you can <a :href="download()">download it</a>
and watch it with your favorite video player!
@@ -75,7 +75,7 @@ export default {
this.listing = req
this.updateLinks()
})
.catch(error => { console.log(error) })
.catch(this.$showError)
},
beforeDestroy () {
window.removeEventListener('keyup', this.key)

View File

@@ -1,19 +1,24 @@
<template>
<div class="prompt">
<h3>{{ $t('prompts.copy') }}</h3>
<p>{{ $t('prompts.copyMessage') }}</p>
<div class="card floating">
<div class="card-title">
<h2>{{ $t('prompts.copy') }}</h2>
</div>
<file-list @update:selected="val => dest = val"></file-list>
<div class="card-content">
<p>{{ $t('prompts.copyMessage') }}</p>
<file-list @update:selected="val => dest = val"></file-list>
</div>
<div>
<button class="ok"
@click="copy"
:aria-label="$t('buttons.copy')"
:title="$t('buttons.copy')">{{ $t('buttons.copy') }}</button>
<button class="cancel"
<div class="card-action">
<button class="cancel flat"
@click="$store.commit('closeHovers')"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
<button class="flat"
@click="copy"
:disabled="$route.path === dest"
:aria-label="$t('buttons.copy')"
:title="$t('buttons.copy')">{{ $t('buttons.copy') }}</button>
</div>
</div>
</template>
@@ -56,7 +61,7 @@ export default {
})
.catch(error => {
buttons.done('copy')
this.$store.commit('showError', error)
this.$showError(error)
})
}
}

View File

@@ -1,16 +1,18 @@
<template>
<div class="prompt">
<h3>{{ $t('prompts.deleteTitle') }}</h3>
<p v-show="req.kind !== 'listing'">{{ $t('prompts.deleteMessageSingle') }}</p>
<p v-show="req.kind === 'listing'">{{ $t('prompts.deleteMessageMultiple', { count: selectedCount}) }}</p>
<div>
<button @click="submit"
:aria-label="$t('buttons.delete')"
:title="$t('buttons.delete')">{{ $t('buttons.delete') }}</button>
<button class="cancel"
@click="$store.commit('closeHovers')"
<div class="card floating">
<div class="card-content">
<p v-if="req.kind !== 'listing'">{{ $t('prompts.deleteMessageSingle') }}</p>
<p v-else>{{ $t('prompts.deleteMessageMultiple', { count: selectedCount}) }}</p>
</div>
<div class="card-action">
<button @click="$store.commit('closeHovers')"
class="flat cancel"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
<button @click="submit"
class="flat"
:aria-label="$t('buttons.delete')"
:title="$t('buttons.delete')">{{ $t('buttons.delete') }}</button>
</div>
</div>
</template>
@@ -43,7 +45,7 @@ export default {
})
.catch(error => {
buttons.done('delete')
this.$store.commit('showError', error)
this.$showError(error)
})
return
@@ -70,7 +72,7 @@ export default {
.catch(error => {
buttons.done('delete')
this.$store.commit('setReload', true)
this.$store.commit('showError', error)
this.$showError(error)
})
}
}

View File

@@ -1,13 +1,18 @@
<template>
<div class="prompt" id="download">
<h3>{{ $t('prompts.download') }}</h3>
<p>{{ $t('prompts.downloadMessage') }}</p>
<div class="card floating" id="download">
<div class="card-title">
<h2>{{ $t('prompts.download') }}</h2>
</div>
<button @click="download('zip')" autofocus>zip</button>
<button @click="download('tar')" autofocus>tar</button>
<button @click="download('targz')" autofocus>tar.gz</button>
<button @click="download('tarbz2')" autofocus>tar.bz2</button>
<button @click="download('tarxz')" autofocus>tar.xz</button>
<div class="card-content">
<p>{{ $t('prompts.downloadMessage') }}</p>
<button class="block cancel" @click="download('zip')" autofocus>zip</button>
<button class="block cancel" @click="download('tar')" autofocus>tar</button>
<button class="block cancel" @click="download('targz')" autofocus>tar.gz</button>
<button class="block cancel" @click="download('tarbz2')" autofocus>tar.bz2</button>
<button class="block cancel" @click="download('tarxz')" autofocus>tar.xz</button>
</div>
</div>
</template>

View File

@@ -1,31 +0,0 @@
<template>
<div class="prompt error">
<i class="material-icons">error_outline</i>
<h3>{{ $t('prompts.error') }}</h3>
<pre>{{ $store.state.showMessage }}</pre>
<div>
<button @click="close"
autofocus
:aria-label="$t('buttons.close')"
:title="$t('buttons.close')">{{ $t('buttons.close') }}</button>
<button @click="reportIssue"
class="cancel"
:aria-label="$t('buttons.reportIssue')"
:title="$t('buttons.reportIssue')">{{ $t('buttons.reportIssue') }}</button>
</div>
</div>
</template>
<script>
export default {
name: 'error',
methods: {
reportIssue () {
window.open('https://github.com/hacdias/filemanager/issues/new')
},
close () {
this.$store.commit('closeHovers')
}
}
}
</script>

View File

@@ -53,7 +53,7 @@ export default {
// so we fetch the data from the previous directory.
api.fetch(url.removeLastDir(this.$route.path))
.then(this.fillOptions)
.catch(this.showError)
.catch(this.$showError)
},
methods: {
fillOptions (req) {
@@ -96,7 +96,7 @@ export default {
api.fetch(uri)
.then(this.fillOptions)
.catch(this.showError)
.catch(this.$showError)
},
touchstart (event) {
let url = event.currentTarget.dataset.url

View File

@@ -1,23 +1,27 @@
<template>
<div class="prompt help">
<h3>{{ $t('help.help') }}</h3>
<div class="card floating help">
<div class="card-title">
<h2>{{ $t('help.help') }}</h2>
</div>
<ul>
<li><strong>F1</strong> - {{ $t('help.f1') }}</li>
<li><strong>F2</strong> - {{ $t('help.f2') }}</li>
<li><strong>DEL</strong> - {{ $t('help.del') }}</li>
<li><strong>ESC</strong> - {{ $t('help.esc') }}</li>
<li><strong>CTRL + S</strong> - {{ $t('help.ctrl.s') }}</li>
<li><strong>CTRL + F</strong> - {{ $t('help.ctrl.f') }}</li>
<li><strong>CTRL + Click</strong> - {{ $t('help.ctrl.click') }}</li>
<li><strong>Click</strong> - {{ $t('help.click') }}</li>
<li><strong>Double click</strong> - {{ $t('help.doubleClick') }}</li>
</ul>
<div class="card-content">
<ul>
<li><strong>F1</strong> - {{ $t('help.f1') }}</li>
<li><strong>F2</strong> - {{ $t('help.f2') }}</li>
<li><strong>DEL</strong> - {{ $t('help.del') }}</li>
<li><strong>ESC</strong> - {{ $t('help.esc') }}</li>
<li><strong>CTRL + S</strong> - {{ $t('help.ctrl.s') }}</li>
<li><strong>CTRL + F</strong> - {{ $t('help.ctrl.f') }}</li>
<li><strong>CTRL + Click</strong> - {{ $t('help.ctrl.click') }}</li>
<li><strong>Click</strong> - {{ $t('help.click') }}</li>
<li><strong>Double click</strong> - {{ $t('help.doubleClick') }}</li>
</ul>
</div>
<div>
<div class="card-action">
<button type="submit"
@click="$store.commit('closeHovers')"
class="ok"
class="flat"
:aria-label="$t('buttons.ok')"
:title="$t('buttons.ok')">{{ $t('buttons.ok') }}</button>
</div>

View File

@@ -1,29 +1,33 @@
<template>
<div class="prompt">
<h3>{{ $t('prompts.fileInfo') }}</h3>
<div class="card floating">
<div class="card-title">
<h2>{{ $t('prompts.fileInfo') }}</h2>
</div>
<p v-show="selected.length > 1">{{ $t('prompts.filesSelected', { count: selected.length }) }}</p>
<div class="card-content">
<p v-if="selected.length > 1">{{ $t('prompts.filesSelected', { count: selected.length }) }}</p>
<p v-show="selected.length < 2"><strong>{{ $t('prompts.displayName') }}</strong> {{ name() }}</p>
<p><strong>{{ $t('prompts.size') }}:</strong> <span id="content_length"></span>{{ humanSize() }}</p>
<p v-show="selected.length < 2"><strong>{{ $t('prompts.lastModified') }}:</strong> {{ humanTime() }}</p>
<p v-if="selected.length < 2"><strong>{{ $t('prompts.displayName') }}</strong> {{ name() }}</p>
<p><strong>{{ $t('prompts.size') }}:</strong> <span id="content_length"></span>{{ humanSize() }}</p>
<p v-if="selected.length < 2"><strong>{{ $t('prompts.lastModified') }}:</strong> {{ humanTime() }}</p>
<section v-show="dir() && selected.length === 0">
<p><strong>{{ $t('prompts.numberFiles') }}:</strong> {{ req.numFiles }}</p>
<p><strong>{{ $t('prompts.numberDirs') }}:</strong> {{ req.numDirs }}</p>
</section>
<template v-if="dir() && selected.length === 0">
<p><strong>{{ $t('prompts.numberFiles') }}:</strong> {{ req.numFiles }}</p>
<p><strong>{{ $t('prompts.numberDirs') }}:</strong> {{ req.numDirs }}</p>
</template>
<section v-show="!dir()">
<p><strong>MD5:</strong> <code><a @click="checksum($event, 'md5')">{{ $t('prompts.show') }}</a></code></p>
<p><strong>SHA1:</strong> <code><a @click="checksum($event, 'sha1')">{{ $t('prompts.show') }}</a></code></p>
<p><strong>SHA256:</strong> <code><a @click="checksum($event, 'sha256')">{{ $t('prompts.show') }}</a></code></p>
<p><strong>SHA512:</strong> <code><a @click="checksum($event, 'sha512')">{{ $t('prompts.show') }}</a></code></p>
</section>
<template v-if="!dir()">
<p><strong>MD5:</strong> <code><a @click="checksum($event, 'md5')">{{ $t('prompts.show') }}</a></code></p>
<p><strong>SHA1:</strong> <code><a @click="checksum($event, 'sha1')">{{ $t('prompts.show') }}</a></code></p>
<p><strong>SHA256:</strong> <code><a @click="checksum($event, 'sha256')">{{ $t('prompts.show') }}</a></code></p>
<p><strong>SHA512:</strong> <code><a @click="checksum($event, 'sha512')">{{ $t('prompts.show') }}</a></code></p>
</template>
</div>
<div>
<div class="card-action">
<button type="submit"
@click="$store.commit('closeHovers')"
class="ok"
class="flat"
:aria-label="$t('buttons.ok')"
:title="$t('buttons.ok')">{{ $t('buttons.ok') }}</button>
</div>
@@ -111,7 +115,7 @@ export default {
api.checksum(link, hash)
.then((hash) => { event.target.innerHTML = hash })
.catch(error => { this.$store.commit('showError', error) })
.catch(this.$showError)
}
}
}

View File

@@ -1,19 +1,23 @@
<template>
<div class="prompt">
<h3>{{ $t('prompts.move') }}</h3>
<p>{{ $t('prompts.moveMessage') }}</p>
<div class="card floating">
<div class="card-title">
<h2>{{ $t('prompts.move') }}</h2>
</div>
<file-list @update:selected="val => dest = val"></file-list>
<div class="card-content">
<file-list @update:selected="val => dest = val"></file-list>
</div>
<div>
<button class="ok"
@click="move"
:aria-label="$t('buttons.move')"
:title="$t('buttons.move')">{{ $t('buttons.move') }}</button>
<button class="cancel"
<div class="card-action">
<button class="flat cancel"
@click="$store.commit('closeHovers')"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
<button class="flat"
@click="move"
:disabled="$route.path === dest"
:aria-label="$t('buttons.move')"
:title="$t('buttons.move')">{{ $t('buttons.move') }}</button>
</div>
</div>
</template>
@@ -56,7 +60,7 @@ export default {
})
.catch(error => {
buttons.done('move')
this.$store.commit('showError', error)
this.$showError(error)
})
event.preventDefault()

View File

@@ -1,18 +1,24 @@
<template>
<div class="prompt">
<h3>{{ $t('prompts.newFile') }}</h3>
<p>{{ $t('prompts.newArchetype') }}</p>
<input autofocus type="text" @keyup.enter="submit" v-model.trim="name">
<input type="text" @keyup.enter="submit" v-model.trim="archetype">
<div>
<button class="ok"
@click="submit"
:aria-label="$t('buttons.create')"
:title="$t('buttons.create')">{{ $t('buttons.create') }}</button>
<button class="cancel"
<div class="card floating">
<div class="card-title">
<h2>{{ $t('prompts.newFile') }}</h2>
</div>
<div class="card-content">
<p>{{ $t('prompts.newArchetype') }}</p>
<input autofocus type="text" @keyup.enter="submit" v-model.trim="name">
<input type="text" @keyup.enter="submit" v-model.trim="archetype">
</div>
<div class="card-action">
<button class="flat cancel"
@click="$store.commit('closeHovers')"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
<button class="flat"
@click="submit"
:aria-label="$t('buttons.create')"
:title="$t('buttons.create')">{{ $t('buttons.create') }}</button>
</div>
</div>
</template>
@@ -37,13 +43,15 @@ export default {
.then((url) => {
this.$router.push({ path: url })
})
.catch(error => {
this.$store.commit('showError', error)
})
.catch(this.$showError)
},
new (url, type) {
url = removePrefix(url)
if (!url.endsWith('.md') && !url.endsWith('.markdown')) {
url += '.markdown'
}
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('POST', `${this.$store.state.baseURL}/api/resource${url}`, true)

View File

@@ -1,17 +1,23 @@
<template>
<div class="prompt">
<h3>{{ $t('prompts.newDir') }}</h3>
<p>{{ $t('prompts.newDirMessage') }}</p>
<input autofocus type="text" @keyup.enter="submit" v-model.trim="name">
<div>
<button class="ok"
:aria-label="$t('buttons.create')"
:title="$t('buttons.create')"
@click="submit">{{ $t('buttons.create') }}</button>
<button class="cancel"
<div class="card floating">
<div class="card-title">
<h2>{{ $t('prompts.newDir') }}</h2>
</div>
<div class="card-content">
<p>{{ $t('prompts.newDirMessage') }}</p>
<input autofocus type="text" @keyup.enter="submit" v-model.trim="name">
</div>
<div class="card-action">
<button class="cancel flat"
@click="$store.commit('closeHovers')"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
<button class="flat"
:aria-label="$t('buttons.create')"
:title="$t('buttons.create')"
@click="submit">{{ $t('buttons.create') }}</button>
</div>
</div>
</template>
@@ -43,7 +49,7 @@ export default {
api.post(uri)
.then(() => { this.$router.push({ path: uri }) })
.catch(error => { this.$store.commit('showError', error) })
.catch(this.$showError)
// Close the prompt
this.$store.commit('closeHovers')

View File

@@ -1,17 +1,23 @@
<template>
<div class="prompt">
<h3>{{ $t('prompts.newFile') }}</h3>
<p>{{ $t('prompts.newFileMessage') }}</p>
<input autofocus type="text" @keyup.enter="submit" v-model.trim="name">
<div>
<button class="ok"
@click="submit"
:aria-label="$t('buttons.create')"
:title="$t('buttons.create')">{{ $t('buttons.create') }}</button>
<button class="cancel"
<div class="card floating">
<div class="card-title">
<h2>{{ $t('prompts.newFile') }}</h2>
</div>
<div class="card-content">
<p>{{ $t('prompts.newFileMessage') }}</p>
<input autofocus type="text" @keyup.enter="submit" v-model.trim="name">
</div>
<div class="card-action">
<button class="cancel flat"
@click="$store.commit('closeHovers')"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
<button class="flat"
@click="submit"
:aria-label="$t('buttons.create')"
:title="$t('buttons.create')">{{ $t('buttons.create') }}</button>
</div>
</div>
</template>
@@ -44,7 +50,7 @@ export default {
// Create the new file.
api.post(uri)
.then(() => { this.$router.push({ path: uri }) })
.catch(error => { this.$store.commit('showError', error) })
.catch(this.$showError)
// Close the prompt.
this.$store.commit('closeHovers')

View File

@@ -9,8 +9,6 @@
<info v-else-if="showInfo"></info>
<move v-else-if="showMove"></move>
<copy v-else-if="showCopy"></copy>
<error v-else-if="showError"></error>
<success v-else-if="showSuccess"></success>
<replace v-else-if="showReplace"></replace>
<schedule v-else-if="show === 'schedule'"></schedule>
<new-archetype v-else-if="show === 'new-archetype'"></new-archetype>
@@ -27,8 +25,6 @@ import Rename from './Rename'
import Download from './Download'
import Move from './Move'
import Copy from './Copy'
import Error from './Error'
import Success from './Success'
import NewFile from './NewFile'
import NewDir from './NewDir'
import NewArchetype from './NewArchetype'
@@ -47,9 +43,7 @@ export default {
NewArchetype,
Schedule,
Rename,
Error,
Download,
Success,
Move,
Copy,
Share,
@@ -70,8 +64,6 @@ export default {
},
computed: {
...mapState(['show', 'plugins']),
showError: function () { return this.show === 'error' },
showSuccess: function () { return this.show === 'success' },
showInfo: function () { return this.show === 'info' },
showHelp: function () { return this.show === 'help' },
showDelete: function () { return this.show === 'delete' },

View File

@@ -1,78 +1,84 @@
<template>
<div class="prompt">
<h3>{{ $t('prompts.rename') }}</h3>
<p>{{ $t('prompts.renameMessage') }} <code>{{ oldName() }}</code>:</p>
<input autofocus type="text" @keyup.enter="submit" v-model.trim="name">
<div>
<button @click="submit"
type="submit"
:aria-label="$t('buttons.rename')"
:title="$t('buttons.rename')">{{ $t('buttons.rename') }}</button>
<button class="cancel"
@click="$store.commit('closeHovers')"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
import url from '@/utils/url'
import * as api from '@/utils/api'
export default {
name: 'rename',
data: function () {
return {
name: ''
}
},
computed: mapState(['req', 'selected', 'selectedCount']),
methods: {
cancel: function (event) {
this.$store.commit('closeHovers')
},
oldName: function () {
// Get the current name of the file we are editing.
if (this.req.kind !== 'listing') {
return this.req.name
}
if (this.selectedCount === 0 || this.selectedCount > 1) {
// This shouldn't happen.
return
}
return this.req.items[this.selected[0]].name
},
submit: function (event) {
let oldLink = ''
let newLink = ''
if (this.req.kind !== 'listing') {
oldLink = this.req.url
} else {
oldLink = this.req.items[this.selected[0]].url
}
this.name = encodeURIComponent(this.name)
newLink = url.removeLastDir(oldLink) + '/' + this.name
api.move([{ from: oldLink, to: newLink }])
.then(() => {
if (this.req.kind !== 'listing') {
this.$router.push({ path: newLink })
return
}
this.$store.commit('setReload', true)
}).catch(error => {
this.$store.commit('showError', error)
})
this.$store.commit('closeHovers')
}
}
}
</script>
<template>
<div class="card floating">
<div class="card-title">
<h2>{{ $t('prompts.rename') }}</h2>
</div>
<div class="card-content">
<p>{{ $t('prompts.renameMessage') }} <code>{{ oldName() }}</code>:</p>
<input autofocus type="text" @keyup.enter="submit" v-model.trim="name">
</div>
<div class="card-action">
<button class="cancel flat"
@click="$store.commit('closeHovers')"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
<button @click="submit"
class="flat"
type="submit"
:aria-label="$t('buttons.rename')"
:title="$t('buttons.rename')">{{ $t('buttons.rename') }}</button>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
import url from '@/utils/url'
import * as api from '@/utils/api'
export default {
name: 'rename',
data: function () {
return {
name: ''
}
},
computed: mapState(['req', 'selected', 'selectedCount']),
methods: {
cancel: function (event) {
this.$store.commit('closeHovers')
},
oldName: function () {
// Get the current name of the file we are editing.
if (this.req.kind !== 'listing') {
return this.req.name
}
if (this.selectedCount === 0 || this.selectedCount > 1) {
// This shouldn't happen.
return
}
return this.req.items[this.selected[0]].name
},
submit: function (event) {
let oldLink = ''
let newLink = ''
if (this.req.kind !== 'listing') {
oldLink = this.req.url
} else {
oldLink = this.req.items[this.selected[0]].url
}
this.name = encodeURIComponent(this.name)
newLink = url.removeLastDir(oldLink) + '/' + this.name
api.move([{ from: oldLink, to: newLink }])
.then(() => {
if (this.req.kind !== 'listing') {
this.$router.push({ path: newLink })
return
}
this.$store.commit('setReload', true)
}).catch(error => {
this.$showError(error)
})
this.$store.commit('closeHovers')
}
}
}
</script>

View File

@@ -1,17 +1,22 @@
<template>
<div class="prompt">
<h3>{{ $t('prompts.replace') }}</h3>
<p>{{ $t('prompts.replaceMessage') }}</p>
<div class="card floating">
<div class="card-title">
<h2>{{ $t('prompts.replace') }}</h2>
</div>
<div>
<button class="ok"
@click="showConfirm"
:aria-label="$t('buttons.replace')"
:title="$t('buttons.replace')">{{ $t('buttons.replace') }}</button>
<button class="cancel"
<div class="card-content">
<p>{{ $t('prompts.replaceMessage') }}</p>
</div>
<div class="card-action">
<button class="flat cancel"
@click="$store.commit('closeHovers')"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
<button class="flat"
@click="showConfirm"
:aria-label="$t('buttons.replace')"
:title="$t('buttons.replace')">{{ $t('buttons.replace') }}</button>
</div>
</div>
</template>

View File

@@ -1,17 +1,23 @@
<template>
<div class="prompt">
<h3>{{ $t('prompts.schedule') }}</h3>
<p>{{ $t('prompts.scheduleMessage') }}</p>
<input autofocus type="datetime-local" v-model="date">
<div>
<button class="ok"
@click="submit"
:aria-label="$t('buttons.schedule')"
:title="$t('buttons.schedule')">{{ $t('buttons.schedule') }}</button>
<button class="cancel"
<div class="card floating">
<div class="card-title">
<h2>{{ $t('prompts.schedule') }}</h2>
</div>
<div class="card-content">
<p>{{ $t('prompts.scheduleMessage') }}</p>
<input autofocus type="datetime-local" v-model="date">
</div>
<div class="card-action">
<button class="cancel flat"
@click="close"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
<button class="falt"
@click="submit"
:aria-label="$t('buttons.schedule')"
:title="$t('buttons.schedule')">{{ $t('buttons.schedule') }}</button>
</div>
</div>
</template>

View File

@@ -1,51 +1,55 @@
<template>
<div class="prompt" id="share">
<h3>{{ $t('buttons.share') }}</h3>
<p></p>
<ul>
<li v-if="!hasPermanent">
<a @click="getPermalink" :aria-label="$t('buttons.permalink')">{{ $t('buttons.permalink') }}</a>
</li>
<div class="card floating" id="share">
<div class="card-title">
<h2>{{ $t('buttons.share') }}</h2>
</div>
<li v-for="link in links" :key="link.hash">
<a :href="buildLink(link.hash)" target="_blank">
<template v-if="link.expires">{{ humanTime(link.expireDate) }}</template>
<template v-else>{{ $t('permanent') }}</template>
</a>
<div class="card-content">
<ul>
<li v-if="!hasPermanent">
<a @click="getPermalink" :aria-label="$t('buttons.permalink')">{{ $t('buttons.permalink') }}</a>
</li>
<button class="action"
@click="deleteLink($event, link)"
:aria-label="$t('buttons.delete')"
:title="$t('buttons.delete')"><i class="material-icons">delete</i></button>
<li v-for="link in links" :key="link.hash">
<a :href="buildLink(link.hash)" target="_blank">
<template v-if="link.expires">{{ humanTime(link.expireDate) }}</template>
<template v-else>{{ $t('permanent') }}</template>
</a>
<button class="action copy"
:data-clipboard-text="buildLink(link.hash)"
:aria-label="$t('buttons.copyToClipboard')"
:title="$t('buttons.copyToClipboard')"><i class="material-icons">content_paste</i></button>
</li>
<button class="action"
@click="deleteLink($event, link)"
:aria-label="$t('buttons.delete')"
:title="$t('buttons.delete')"><i class="material-icons">delete</i></button>
<li>
<input autofocus
type="number"
max="2147483647"
min="0"
@keyup.enter="submit"
v-model.trim="time">
<select v-model="unit" :aria-label="$t('time.unit')">
<option value="seconds">{{ $t('time.seconds') }}</option>
<option value="minutes">{{ $t('time.minutes') }}</option>
<option value="hours">{{ $t('time.hours') }}</option>
<option value="days">{{ $t('time.days') }}</option>
</select>
<button class="action"
@click="submit"
:aria-label="$t('buttons.create')"
:title="$t('buttons.create')"><i class="material-icons">add</i></button>
</li>
</ul>
<button class="action copy-clipboard"
:data-clipboard-text="buildLink(link.hash)"
:aria-label="$t('buttons.copyToClipboard')"
:title="$t('buttons.copyToClipboard')"><i class="material-icons">content_paste</i></button>
</li>
<div>
<button class="cancel"
<li>
<input autofocus
type="number"
max="2147483647"
min="0"
@keyup.enter="submit"
v-model.trim="time">
<select v-model="unit" :aria-label="$t('time.unit')">
<option value="seconds">{{ $t('time.seconds') }}</option>
<option value="minutes">{{ $t('time.minutes') }}</option>
<option value="hours">{{ $t('time.hours') }}</option>
<option value="days">{{ $t('time.days') }}</option>
</select>
<button class="action"
@click="submit"
:aria-label="$t('buttons.create')"
:title="$t('buttons.create')"><i class="material-icons">add</i></button>
</li>
</ul>
</div>
<div class="card-action">
<button class="flat"
@click="$store.commit('closeHovers')"
:aria-label="$t('buttons.close')"
:title="$t('buttons.close')">{{ $t('buttons.close') }}</button>
@@ -54,7 +58,7 @@
</template>
<script>
import { mapState, mapMutations } from 'vuex'
import { mapState } from 'vuex'
import { getShare, deleteShare, share } from '@/utils/api'
import moment from 'moment'
import Clipboard from 'clipboard'
@@ -101,20 +105,25 @@ export default {
})
.catch(error => {
if (error === 404) return
this.showError(error)
this.$showError(error)
})
},
mounted () {
this.clip = new Clipboard('.copy')
this.clip = new Clipboard('.copy-clipboard')
this.clip.on('success', (e) => {
this.$showSuccess(this.$t('success.linkCopied'))
})
},
beforeDestroy () {
this.clip.destroy()
},
methods: {
...mapMutations([ 'showError' ]),
submit: function (event) {
if (!this.time) return
share(this.url, this.time, this.unit)
.then(result => { this.links.push(result); this.sort() })
.catch(error => { this.showError(error) })
.catch(this.$showError)
},
getPermalink (event) {
share(this.url)
@@ -123,7 +132,7 @@ export default {
this.sort()
this.hasPermanent = true
})
.catch(error => { this.showError(error) })
.catch(this.$showError)
},
deleteLink (event, link) {
event.preventDefault()
@@ -132,7 +141,7 @@ export default {
if (!link.expires) this.hasPermanent = false
this.links = this.links.filter(item => item.hash !== link.hash)
})
.catch(error => { this.showError(error) })
.catch(this.$showError)
},
humanTime (time) {
return moment(time).fromNow()

View File

@@ -1,23 +0,0 @@
<template>
<div class="prompt success">
<i class="material-icons">done</i>
<h3>{{ $store.state.showMessage }}</h3>
<div>
<button @click="close"
:aria-label="$t('buttons.ok')"
:title="$t('buttons.ok')"
autofocus>{{ $t('buttons.ok') }}</button>
</div>
</div>
</template>
<script>
export default {
name: 'success',
methods: {
close () {
this.$store.commit('closeHovers')
}
}
}
</script>

View File

@@ -1,9 +1,8 @@
body {
font-family: 'Roboto', sans-serif;
padding-top: 4em;
background-color: #f8f8f8;
user-select: none;
color: #212121;
background-color: #fafafa;
color: #333333;
}
* {
@@ -66,6 +65,58 @@ button:hover {
background-color: #1E88E5;
}
input[type="submit"].block,
button.block {
display: block;
width: 100%;
margin: 0 0 1em;
}
button.delete {
background: #F44336;
}
button.delete:hover {
background: #D32F2F;
}
button.cancel {
background-color: #ECEFF1;
color: #37474F;
}
button.cancel:hover {
background-color: #e9eaeb;
}
button.flat,
input[type="submit"].flat {
color: #1E88E5;
background: transparent;
box-shadow: 0 0 0;
border: 0;
margin-left: 0;
text-transform: uppercase;
}
button.flat:hover,
input[type="submit"].flat:hover {
background: rgba(0,0,0,0.05)
}
button.flat.delete {
color: #F44336;
}
button.flat.cancel {
color: #ccc;
}
button.flat[disabled] {
color: #ccc;
cursor: not-allowed;
}
.mobile-only {
display: none !important;
}

View File

@@ -1,64 +1,34 @@
.dashboard {
max-width: 600px;
box-shadow: rgba(0, 0, 0, 0.06) 0px 1px 3px, rgba(0, 0, 0, 0.12) 0px 1px 2px;
border-radius: .5em;
background: #fff;
padding: 1em;
margin: 1em 0;
}
.dashboard a {
a {
color: inherit
}
.dashboard h1 button {
font-size: 0.5em;
float: right;
}
.dashboard table {
width: 100%;
}
.dashboard table th {
font-weight: 500;
color: #757575;
text-align: left;
}
.dashboard table th,
.dashboard table td {
padding: .5em 0;
}
.dashboard table td:last-child {
width: 1em
}
.dashboard > h1:first-of-type {
margin-top: 0;
}
.dashboard form > p:last-child,
form.dashboard > p:last-child {
text-align: right
}
.dashboard > *:last-child {
margin-bottom: 0;
}
.dashboard select,
.dashboard textarea,
.dashboard input[type="text"],
.dashboard input[type="password"] {
padding: 0;
line-height: 1.7;
select,
textarea,
input[type="text"],
input[type="password"] {
padding: 0.5em 0;
line-height: 1;
display: block;
border: 0;
border-bottom: 1px solid #dddddd;
transition: .2s ease border;
width: 100%;
background: transparent;
}
textarea {
line-height: 1.15;
padding: .5em;
border: 1px solid #ddd;
font-family: monospace;
min-height: 10em;
resize: none;
border-radius: 2px;
}
.dashboard #locale,
@@ -69,49 +39,32 @@ form.dashboard > p:last-child {
}
.dashboard #locale {
border: 1px solid #dddddd;
margin-top: .5em;
}
.dashboard textarea:focus,
.dashboard textarea:hover,
.dashboard input[type="text"]:focus,
.dashboard input[type="password"]:focus,
.dashboard input[type="text"]:hover,
.dashboard input[type="password"]:hover {
textarea:focus,
textarea:hover,
input[type="text"]:focus,
input[type="password"]:focus,
input[type="text"]:hover,
input[type="password"]:hover {
border-color: #2979ff;
}
.dashboard input.red {
input.red {
border-color: red;
}
.dashboard input.green {
input.green {
border-color: green;
}
.dashboard button.delete {
background: #F44336;
}
.dashboard button.delete:hover {
background: #D32F2F;
}
.dashboard textarea {
line-height: 1.15;
padding: .5em;
border: 1px solid #ddd;
font-family: monospace;
min-height: 10em;
resize: vertical;
}
.dashboard p label {
margin-bottom: .2em;
display: block;
font-size: .8em;
font-weight: bold;
font-weight: 500;
color: rgba(0, 0, 0, 0.57);
}
li code,
@@ -131,21 +84,337 @@ p code {
display: flex;
color: rgb(84, 110, 122);
font-weight: 500;
padding: 0 0 1em;
margin: 0 0 1em;
font-size: .8em;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
text-align: center;
justify-content: space-between;
padding: 0;
}
.dashboard #nav li {
width: 100%;
padding: 0 0 1em;
border-bottom: 2px solid rgba(0, 0, 0, 0.05);
}
.dashboard #nav li:last-child {
text-align: right
.dashboard #nav li.active {
border-color: #2196f3
}
.dashboard #nav i {
font-size: 1em;
vertical-align: middle;
}
table {
border-collapse: collapse;
width: 100%;
}
table tr {
border-bottom: 1px solid #ccc;
}
table tr:last-child {
border: 0;
}
table th {
font-weight: 500;
color: #757575;
text-align: left;
}
table th,
table td {
padding: .5em 0;
}
table td.small {
width: 1em;
}
table tr>*:first-child {
padding-left: 1em;
}
table tr>*:last-child {
padding-right: 1em;
}
.card {
position: relative;
margin: .5rem 0 1rem 0;
background-color: #fff;
border-radius: 2px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);
}
.card.floating {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 99999;
max-width: 25em;
width: 90%;
max-height: 95%;
z-index: 99999;
animation: .1s show forwards;
}
.card>*>*:first-child {
margin-top: 0;
}
.card>*>*:last-child {
margin-bottom: 0;
}
.card .card-title {
padding: 1.5em 1em 1em;
display: flex;
}
.card .card-title>*:first-child {
margin-right: auto;
}
.card>div {
padding: 1em 1em;
}
.card>div:first-child {
padding-top: 1.5em;
}
.card>div:last-child {
padding-bottom: 1.5em;
}
.card .card-title * {
margin: 0;
}
.card .card-action {
text-align: right;
}
.card .card-content.full {
padding-bottom: 0;
}
.card h2 {
font-weight: 500;
}
.card h3 {
color: rgba(0, 0, 0, 0.53);
font-size: 1em;
font-weight: 500;
margin: 2em 0 1em;
}
.card-content table {
margin: 0 -1em;
width: calc(100% + 2em);
}
.card code {
word-wrap: break-word;
}
.card#download {
max-width: 15em;
}
.card#share ul {
list-style: none;
padding: 0;
margin: 0;
}
.card#share ul li {
display: flex;
justify-content: space-between;
align-items: center;
}
.card#share ul li a {
color: #2196F3;
cursor: pointer;
margin-right: auto;
}
.card#share ul li .action i {
font-size: 1em;
}
.card#share ul li input,
.card#share ul li select {
padding: .2em;
margin-right: .5em;
border: 1px solid #dadada;
}
.card#share .action.copy-clipboard::after {
content: 'Copied!';
position: absolute;
left: -25%;
width: 150%;
font-size: .6em;
text-align: center;
background: #44a6f5;
color: #fff;
padding: .5em .2em;
border-radius: .4em;
top: -2em;
transition: .1s ease opacity;
opacity: 0;
}
.card#share .action.copy-clipboard.active::after {
opacity: 1;
}
.overlay {
background-color: rgba(0, 0, 0, 0.5);
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
z-index: 9999;
animation: .1s show forwards;
}
/* * * * * * * * * * * * * * * *
* PROMPT - MOVE *
* * * * * * * * * * * * * * * */
.file-list {
max-height: 50vh;
overflow: auto;
list-style: none;
margin: 0;
padding: 0;
width: 100%;
}
.file-list li {
width: 100%;
user-select: none;
border-radius: .2em;
padding: .3em;
}
.file-list li[aria-selected=true] {
background: #2196f3 !important;
color: #fff !important;
transition: .1s ease all;
}
.file-list li:hover {
background-color: #e9eaeb;
cursor: pointer;
}
.file-list li:before {
content: "folder";
color: #6f6f6f;
vertical-align: middle;
line-height: 1.4;
font-family: 'Material Icons';
font-size: 1.75em;
margin-right: .25em;
}
.file-list li[aria-selected=true]:before {
color: white;
}
.help {
max-width: 24em;
}
.help ul {
padding: 0;
margin: 1em 0;
list-style: none;
}
@keyframes show {
0% {
display: none;
opacity: 0;
}
1% {
display: block;
opacity: 0;
}
100% {
display: block;
opacity: 1;
}
}
.collapsible {
border-top: 1px solid rgba(0,0,0,0.1);
}
.collapsible:last-of-type {
border-bottom: 1px solid rgba(0,0,0,0.1);
}
.collapsible > input {
display: none;
}
.collapsible > label {
padding: 1em 0;
cursor: pointer;
border-right: 0;
border-left: 0;
display: flex;
justify-content: space-between;
}
.collapsible > label * {
margin: 0;
color: rgba(0,0,0,0.57);
}
.collapsible > label i {
transition: .2s ease transform;
user-select: none;
}
.collapsible .collapse {
max-height: 0;
overflow: hidden;
transition: .2s ease all;
}
.collapsible > input:checked ~ .collapse {
padding-top: 1em;
padding-bottom: 1em;
max-height: 20em;
}
.collapsible > input:checked ~ label i {
transform: rotate(180deg)
}
.card .collapsible {
width: calc(100% + 2em);
margin: 0 -1em;
}
.card .collapsible > label {
padding: 1em;
}
.card .collapsible .collapse {
padding: 0 1em;
}

View File

@@ -1,184 +1,184 @@
@import "~codemirror/lib/codemirror.css";
@import "~codemirror/theme/ttcn.css";
#editor {
max-width: 800px;
margin: 0 auto;
}
#editor .CodeMirror {
box-shadow: rgba(0, 0, 0, 0.06) 0px 1px 3px, rgba(0, 0, 0, 0.12) 0px 1px 2px;
margin: 2em 0;
border-radius: .5em;
}
#editor h2 {
color: rgba(0, 0, 0, 0.3);
font-weight: 500;
}
.CodeMirror {
height: auto;
}
.markdown .CodeMirror {
padding: .75em;
}
.cm-s-markdown .CodeMirror-gutter {
border-right: 1px solid #eff3f5;
padding-right: 5px;
margin-right: 15px;
min-width: 2.5em;
padding-bottom: 30px;
}
.cm-s-markdown .CodeMirror-cursor {
border-right: 2px solid #667880;
}
.cm-s-markdown .CodeMirror-lines {
margin: 0;
}
.cm-s-markdown {
color: #3D494E;
}
.cm-s-markdown span.cm-header {
color: #3D494E;
font-weight: bold;
}
.cm-s-markdown span.cm-variable-2 {
color: #3D494E;
}
.cm-s-markdown span.cm-meta {
color: #516066;
}
.cm-s-markdown span.cm-hr {
color: #516066;
}
.cm-s-markdown span.cm-comment {
color: #868f93;
}
.cm-s-markdown span.cm-qualifier {
color: #868f93;
}
.cm-s-markdown span.cm-number {
color: #197987;
}
.cm-s-markdown span.cm-variable {
color: #197987;
}
.cm-s-markdown span.cm-builtin {
color: #197987;
}
.cm-s-markdown span.cm-link {
color: #197987;
text-decoration: underline;
}
.cm-s-markdown span.cm-tag {
color: #197987;
}
.cm-s-markdown span.cm-string {
color: #48abb9;
}
.cm-s-markdown span.cm-string-2 {
color: #48abb9;
}
.cm-s-markdown span.cm-quote {
color: #48abb9;
}
.cm-s-markdown span.cm-atom {
color: #48abb9;
}
.cm-s-markdown span.cm-property {
color: #82a367;
}
.cm-s-markdown span.cm-operator {
color: #82a367;
}
.cm-s-markdown span.cm-variable-3 {
color: #82a367;
}
.cm-s-markdown span.cm-attribute {
color: #90bb74;
}
.cm-s-markdown span.cm-def {
color: #90bb74;
}
.cm-s-markdown span.cm-keyword {
color: #ec6c45;
}
.cm-s-markdown span.cm-bracket {
color: #ec6c45;
}
.cm-s-markdown span.cm-error {
color: #e45346;
}
.cm-s-markdown span.cm-em {
font-style: italic;
}
.cm-s-markdown span.cm-strong {
font-weight: bold;
}
.cm-s-markdown .cm-header-1 {
font-size: 200%;
line-height: 200%;
}
.cm-s-markdown .cm-header-2 {
font-size: 160%;
line-height: 160%;
}
.cm-s-markdown .cm-header-3 {
font-size: 125%;
line-height: 125%;
}
.cm-s-markdown .cm-header-4 {
font-size: 110%;
line-height: 110%;
}
.cm-s-markdown .cm-comment {
background: rgba(0, 0, 0, .05);
border-radius: 2px;
}
.cm-s-markdown .cm-link {
color: #7f8c8d;
}
.cm-s-markdown .cm-url {
color: #aab2b3;
}
.cm-s-markdown .cm-strikethrough {
text-decoration: line-through;
}
@import "~codemirror/lib/codemirror.css";
@import "~codemirror/theme/ttcn.css";
#editor {
max-width: 800px;
margin: 0 auto;
}
#editor .CodeMirror {
box-shadow: rgba(0, 0, 0, 0.06) 0px 1px 3px, rgba(0, 0, 0, 0.12) 0px 1px 2px;
margin: 2em 0;
border-radius: .5em;
}
#editor h2 {
color: rgba(0, 0, 0, 0.3);
font-weight: 500;
}
.CodeMirror {
height: auto;
}
.markdown .CodeMirror {
padding: .75em;
}
.cm-s-markdown .CodeMirror-gutter {
border-right: 1px solid #eff3f5;
padding-right: 5px;
margin-right: 15px;
min-width: 2.5em;
padding-bottom: 30px;
}
.cm-s-markdown .CodeMirror-cursor {
border-right: 2px solid #667880;
}
.cm-s-markdown .CodeMirror-lines {
margin: 0;
}
.cm-s-markdown {
color: #3D494E;
}
.cm-s-markdown span.cm-header {
color: #3D494E;
font-weight: bold;
}
.cm-s-markdown span.cm-variable-2 {
color: #3D494E;
}
.cm-s-markdown span.cm-meta {
color: #516066;
}
.cm-s-markdown span.cm-hr {
color: #516066;
}
.cm-s-markdown span.cm-comment {
color: #868f93;
}
.cm-s-markdown span.cm-qualifier {
color: #868f93;
}
.cm-s-markdown span.cm-number {
color: #197987;
}
.cm-s-markdown span.cm-variable {
color: #197987;
}
.cm-s-markdown span.cm-builtin {
color: #197987;
}
.cm-s-markdown span.cm-link {
color: #197987;
text-decoration: underline;
}
.cm-s-markdown span.cm-tag {
color: #197987;
}
.cm-s-markdown span.cm-string {
color: #48abb9;
}
.cm-s-markdown span.cm-string-2 {
color: #48abb9;
}
.cm-s-markdown span.cm-quote {
color: #48abb9;
}
.cm-s-markdown span.cm-atom {
color: #48abb9;
}
.cm-s-markdown span.cm-property {
color: #82a367;
}
.cm-s-markdown span.cm-operator {
color: #82a367;
}
.cm-s-markdown span.cm-variable-3 {
color: #82a367;
}
.cm-s-markdown span.cm-attribute {
color: #90bb74;
}
.cm-s-markdown span.cm-def {
color: #90bb74;
}
.cm-s-markdown span.cm-keyword {
color: #ec6c45;
}
.cm-s-markdown span.cm-bracket {
color: #ec6c45;
}
.cm-s-markdown span.cm-error {
color: #e45346;
}
.cm-s-markdown span.cm-em {
font-style: italic;
}
.cm-s-markdown span.cm-strong {
font-weight: bold;
}
.cm-s-markdown .cm-header-1 {
font-size: 200%;
line-height: 200%;
}
.cm-s-markdown .cm-header-2 {
font-size: 160%;
line-height: 160%;
}
.cm-s-markdown .cm-header-3 {
font-size: 125%;
line-height: 125%;
}
.cm-s-markdown .cm-header-4 {
font-size: 110%;
line-height: 110%;
}
.cm-s-markdown .cm-comment {
background: rgba(0, 0, 0, .05);
border-radius: 2px;
}
.cm-s-markdown .cm-link {
color: #7f8c8d;
}
.cm-s-markdown .cm-url {
color: #aab2b3;
}
.cm-s-markdown .cm-strikethrough {
text-decoration: line-through;
}

View File

@@ -1,137 +1,137 @@
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-cyrillic-ext.woff2) format('woff2');
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-cyrillic.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-greek-ext.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-greek.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-vietnamese.woff2) format('woff2');
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-latin-ext.woff2) format('woff2');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-cyrillic-ext.woff2) format('woff2');
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-cyrillic.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-greek-ext.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-greek.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-vietnamese.woff2) format('woff2');
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-latin-ext.woff2) format('woff2');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: local('Material Icons'), local('MaterialIcons-Regular'), url(../assets/fonts/material/icons.woff2) format('woff2');
}
.prompt .file-list ul li:before,
.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
-moz-osx-font-smoothing: grayscale;
font-feature-settings: 'liga';
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-cyrillic-ext.woff2) format('woff2');
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-cyrillic.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-greek-ext.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-greek.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-vietnamese.woff2) format('woff2');
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-latin-ext.woff2) format('woff2');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-cyrillic-ext.woff2) format('woff2');
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-cyrillic.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-greek-ext.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-greek.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-vietnamese.woff2) format('woff2');
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-latin-ext.woff2) format('woff2');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: local('Material Icons'), local('MaterialIcons-Regular'), url(../assets/fonts/material/icons.woff2) format('woff2');
}
.prompt .file-list ul li:before,
.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
-moz-osx-font-smoothing: grayscale;
font-feature-settings: 'liga';
}

View File

@@ -122,8 +122,9 @@ header .search-button {
#search input {
width: 100%;
border: 0;
outline: 0;
background-color: transparent;
line-height: 0;
padding: 0;
}
#search #result {

View File

@@ -159,7 +159,7 @@
#listing.list .item.header {
display: flex !important;
background: #f8f8f8;
background: #fafafa;
position: fixed;
width: calc(100% - 19em);
top: 7em;

View File

@@ -29,6 +29,14 @@
width: 90%;
}
#login.recaptcha form {
min-width: 304px;
}
#login #recaptcha {
margin: .5em 0 0;
}
#login input {
width: 100%;
width: 100%;

View File

@@ -1,113 +1,113 @@
@media (max-width: 1024px) {
nav {
width: 10em
}
}
@media (max-width: 1024px) {
#listing.list .item.header,
main {
width: calc(100% - 13em)
}
}
@media (max-width: 736px) {
#more {
display: inherit
}
header .overlay {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.1);
}
#dropdown {
position: fixed;
top: 1em;
right: 1em;
display: block;
background-color: #fff;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
transform: scale(0);
transition: .1s ease-in-out transform;
transform-origin: top right;
z-index: 99999;
}
#dropdown > div {
display: block;
}
#dropdown.active {
transform: scale(1);
}
#dropdown .action {
display: flex;
align-items: center;
border-radius: 0;
width: 100%;
}
#dropdown .action span:not(.counter) {
display: inline-block;
padding: .4em;
}
#dropdown .counter {
left: 2.25em;
}
#file-selection {
position: fixed;
bottom: 1em;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
background: #fff;
box-shadow: rgba(0, 0, 0, 0.06) 0px 1px 3px, rgba(0, 0, 0, 0.12) 0px 1px 2px;
width: 95%;
max-width: 20em;
}
#file-selection .action {
border-radius: 50%;
width: auto;
}
#file-selection > span {
display: inline-block;
margin-left: 1em;
color: #6f6f6f;
margin-right: auto;
}
nav {
top: 0;
z-index: 99999;
background: #fff;
height: 100%;
width: 16em;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
transition: .1s ease left;
left: -17em;
}
nav.active {
left: 0;
}
header .search-button,
header>div:first-child>.action {
display: inherit;
}
header img {
display: none;
}
#listing {
margin-bottom: 5em;
}
#listing.list .item.header,
main {
width: calc(100% - 2em);
}
main {
margin: 0 1em;
width: calc(100% - 2em);
}
#search {
display: none;
}
#search.active {
display: block;
}
}
@media (max-width: 1024px) {
nav {
width: 10em
}
}
@media (max-width: 1024px) {
#listing.list .item.header,
main {
width: calc(100% - 13em)
}
}
@media (max-width: 736px) {
#more {
display: inherit
}
header .overlay {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.1);
}
#dropdown {
position: fixed;
top: 1em;
right: 1em;
display: block;
background-color: #fff;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
transform: scale(0);
transition: .1s ease-in-out transform;
transform-origin: top right;
z-index: 99999;
}
#dropdown > div {
display: block;
}
#dropdown.active {
transform: scale(1);
}
#dropdown .action {
display: flex;
align-items: center;
border-radius: 0;
width: 100%;
}
#dropdown .action span:not(.counter) {
display: inline-block;
padding: .4em;
}
#dropdown .counter {
left: 2.25em;
}
#file-selection {
position: fixed;
bottom: 1em;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
background: #fff;
box-shadow: rgba(0, 0, 0, 0.06) 0px 1px 3px, rgba(0, 0, 0, 0.12) 0px 1px 2px;
width: 95%;
max-width: 20em;
}
#file-selection .action {
border-radius: 50%;
width: auto;
}
#file-selection > span {
display: inline-block;
margin-left: 1em;
color: #6f6f6f;
margin-right: auto;
}
nav {
top: 0;
z-index: 99999;
background: #fff;
height: 100%;
width: 16em;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
transition: .1s ease left;
left: -17em;
}
nav.active {
left: 0;
}
header .search-button,
header>div:first-child>.action {
display: inherit;
}
header img {
display: none;
}
#listing {
margin-bottom: 5em;
}
#listing.list .item.header,
main {
width: calc(100% - 2em);
}
main {
margin: 0 1em;
width: calc(100% - 2em);
}
#search {
display: none;
}
#search.active {
display: block;
}
}

View File

@@ -1,208 +0,0 @@
.prompt {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #fff;
border: 1px solid rgba(0, 0, 0, 0.075);
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
padding: 2em;
max-width: 25em;
width: 90%;
max-height: 95%;
z-index: 99999;
animation: .1s show forwards;
}
.overlay {
background-color: rgba(0, 0, 0, 0.5);
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
z-index: 9999;
animation: .1s show forwards;
}
.prompt h3 {
margin: 0;
font-weight: 500;
font-size: 1.5em;
}
.prompt p {
font-size: .9em;
color: rgba(0, 0, 0, 0.8);
margin: .5em 0 1em;
}
.prompt input:not([type="submit"]) {
width: 100%;
border: 1px solid #dadada;
line-height: 1;
padding: .3em;
margin: .3em 0;
}
.prompt code {
word-wrap: break-word;
}
.prompt div:last-of-type {
margin-top: 1em;
display: flex;
justify-content: flex-start;
flex-direction: row-reverse;
}
.prompt .cancel {
background-color: #ECEFF1;
color: #37474F;
}
.prompt .cancel:hover {
background-color: #e9eaeb;
}
.prompt.success i,
.prompt.error i {
color: #F44336;
display: block;
margin: 0 auto .15em;
text-align: center;
font-size: 5em;
}
.prompt.success h3,
.prompt.error h3 {
text-align: center;
}
.prompt.error button:not(.cancel) {
background-color: #F44336
}
.prompt.success i {
color: #8BC34A;
}
.prompt.success button {
background-color: #8BC34A;
}
/* * * * * * * * * * * * * * * *
* PROMPT - MOVE *
* * * * * * * * * * * * * * * */
.file-list {
max-height: 50vh;
overflow: auto;
list-style: none;
margin: 0;
padding: 0;
width: 100%;
}
.file-list li {
width: 100%;
user-select: none;
border-radius: .2em;
padding: .3em;
}
.file-list li[aria-selected=true] {
background: #2196f3 !important;
color: #fff !important;
transition: .1s ease all;
}
.file-list li:hover {
background-color: #e9eaeb;
cursor: pointer;
}
.file-list li:before {
content: "folder";
color: #6f6f6f;
vertical-align: middle;
line-height: 1.4;
font-family: 'Material Icons';
font-size: 1.75em;
margin-right: .25em;
}
.file-list li[aria-selected=true]:before {
color: white;
}
.prompt#download {
max-width: 15em;
}
.prompt#download button {
width: 100%;
display: block;
margin: 0 0 1em;
background-color: #ECEFF1;
color: #37474F;
}
.prompt#download button:last-of-type {
margin-bottom: 0;
}
.help {
max-width: 24em;
}
.help ul {
padding: 0;
margin: 1em 0;
list-style: none;
}
@keyframes show {
0% {
display: none;
opacity: 0;
}
1% {
display: block;
opacity: 0;
}
100% {
display: block;
opacity: 1;
}
}
.prompt#share ul {
list-style: none;
padding: 0;
margin: 0;
}
.prompt#share ul li {
display: flex;
justify-content: space-between;
align-items: center;
}
.prompt#share ul li a {
color: #2196F3;
cursor: pointer;
margin-right: auto;
}
.prompt#share ul li .action i {
font-size: 1em;
}
.prompt#share ul li input,
.prompt#share ul li select {
padding: .2em;
margin-right: .5em;
border: 1px solid #dadada;
}

View File

@@ -1,8 +1,8 @@
@import "~normalize.css/normalize.css";
@import "~noty/lib/noty.css";
@import "./fonts.css";
@import "./base.css";
@import "./header.css";
@import "./prompts.css";
@import "./listing.css";
@import "./editor.css";
@import "./dashboard.css";
@@ -180,6 +180,17 @@
* PROMPT *
* * * * * * * * * * * * * * * */
.noty_buttons {
text-align: right;
padding: 0 10px 10px !important;
}
.noty_buttons button {
background: rgba(0, 0, 0, 0.05);
border: 1px solid rgba(0,0,0,0.1);
box-shadow: 0 0 0 0;
font-size: 14px;
}
/* * * * * * * * * * * * * * * *
* FOOTER *

View File

@@ -26,11 +26,13 @@ buttons:
publish: Publish
selectMultiple: Select multiple
schedule: Schedule
switchView: Swicth view
switchView: Switch view
toggleSidebar: Toggle sidebar
update: Update
upload: Upload
permalink: Get Permanent Link
success:
linkCopied: Link copied!
errors:
forbidden: You're not welcome here.
internal: Something really went wrong.
@@ -115,13 +117,14 @@ settings:
commandsHelp: >
Here you can set commands that are executed in the named events. You
write one command per line. If the event is related to files, such as before and
after saving, the environment variable "file" will be available with the path
after saving, the environment variable "FILE" will be available with the path
of the file.
commandsUpdated: Commands updated!
customStylesheet: Custom Stylesheet
examples: Examples
globalSettings: Global Settings
language: Language
lockPassword: Prevent the user from changing the password
newPassword: Your new password
newPasswordConfirm: Confirm your new password
newUser: New User
@@ -165,7 +168,6 @@ sidebar:
myFiles: My files
newFile: New file
newFolder: New folder
servedWith: Served with
settings: Settings
siteSettings: Site Settings
hugoNew: Hugo New
@@ -190,6 +192,7 @@ languages:
ja: 日本語
zhCN: 中文 (简体)
zhTW: 中文 (繁體)
es: Español
time:
unit: Time Unit
seconds: Seconds

202
assets/src/i18n/es.yaml Normal file
View File

@@ -0,0 +1,202 @@
permanent: Permanente
buttons:
cancel: Cancelar
close: Cerrar
copy: Copiar
copyFile: Copiar archivo
copyToClipboard: Copiar al portapapeles
create: Crear
delete: Borrar
download: Descargar
info: Info
more: Más
move: Mover
moveFile: Mover archivo
new: Nuevo
next: Siguiente
ok: OK
replace: Reemplazar
previous: Anterior
rename: Renombrar
reportIssue: Reportar problema
save: Guardar
search: Buscar
select: Seleccionar
share: Compartir
publish: Publicar
selectMultiple: Selección múltiple
schedule: Programar
switchView: Cambiar vista
toggleSidebar: Mostrar/Ocultar menú
update: Actualizar
upload: Subir
permalink: Link permanente
success:
linkCopied: ¡Link copiado!
errors:
forbidden: No eres bienvenido aquí.
internal: La verdad es que algo ha ido mal.
notFound: No se puede acceder a este lugar.
files:
folders: Carpetas
files: Archivos
body: Cuerpo
clear: Limpiar
closePreview: Cerrar vista previa
home: Inicio
lastModified: Última modificación
loading: Cargando...
lonely: Uno se siente muy sólo aquí...
metadata: Metadatos
multipleSelectionEnabled: Selección múltiple activada
name: Nombre
size: Tamaño
sortByName: Ordenar por nombre
sortBySize: Ordenar por tamaño
sortByLastModified: Ordenar por última modificación
help:
click: seleccionar archivo o carpeta
ctrl:
click: seleccionar múltiples archivos o carpetas
f: abre la búsqueda
s: guarda un archivo o lo descarga a la carpeta en la que estás
del: elimina los items seleccionados
doubleClick: abre un archivo o carpeta
esc: limpia la selección y/o cierra la ventana
f1: esta información
f2: renombrar archivo
help: Ayuda
login:
password: Contraseña
submit: Iniciar sesión
username: Usuario
wrongCredentials: Usuario y/o contraseña incorrectos
prompts:
copy: Copiar
copyMessage: 'Elige el lugar donde quieres copiar tus archivos:'
currentlyNavigating: 'Actualmente estás en:'
deleteMessageMultiple: ¿Estás seguro que quieres eliminar {count} archivo(s)?
deleteMessageSingle: ¿Estás seguro que quieres eliminar este archivo/carpeta?
deleteTitle: Borrar archivos
displayName: 'Nombre:'
download: Descargar archivos
downloadMessage: Elige el formato de descarga.
error: Algo ha fallado
fileInfo: Información del archivo
filesSelected: "{count} archivos seleccionados."
lastModified: Última modificación
move: Mover
moveMessage: 'Elige una nueva casa para tus archivo(s)/carpeta(s):'
newDir: Nueva carpeta
newDirMessage: Escribe el nombre de la nueva carpeta.
newFile: Nuevo archivo
newFileMessage: Escribe el nombre del nuevo archivo.
numberDirs: Número de carpetas
numberFiles: Número de archivos
replace: Reemplazar
replaceMessage: >
Uno de los archivos ue intentas subir está creando conflicto por su nombre.
¿Quieres cambiar el nombre del ya existente?
rename: Renombrar
renameMessage: Escribe el nuevo nombre para
show: Mostrar
size: Tamaño
schedule: Programar
scheduleMessage: Elige una hora y fecha para programar la publicación de este post.
newArchetype: Crea un nuevo post basado en un arquetipo. Tu archivo será creado en la carpeta de contenido.
settings:
admin: Admin
administrator: Administrador
allowCommands: Ejecutar comandos
allowEdit: Editar, renombrar y borrar archivos o carpetas
allowNew: Crear nuevos archivos y carpetas
allowPublish: Publicar nuevos posts y páginas
avoidChanges: "(dejar en blanco para evitar cambios)"
changePassword: Cambiar contraseña
commands: Comandos
commandsHelp: >
Aquí puedes crear comandos que serán ejecutados en los eventos. Debes
escribir un comando por linea. Si el evento está relacionado con archivos, como
por ejemplo, antes y después de guardar, la variable de entorno "FILE" estará
disponible en la ruta del archivo.
commandsUpdated: ¡Comandos actualizados!
customStylesheet: Modificar hoja de estilos
examples: Ejemplos
globalSettings: Ajustes globales
language: Idioma
lockPassword: Evitar que el usuario cambie la contraseña
newPassword: Tu nueva contraseña
newPasswordConfirm: Confirma tu contraseña
newUser: Nuevo usuario
password: Contraseña
passwordUpdated: ¡Contraseña actualizada!
permissions: Permisos
permissionsHelp: >
Puedes nombrar al usuario como administrador o elegir los permisos
individualmente. Si seleccionas "Administrador", todas las otras opciones
serán activadas automáticamente. La administración de usuarios es un privilegio de administrador.
profileSettings: Ajustes del perfil
ruleExample1: >
previene el acceso a una extensión de archivo (Como .git) en
cada carpeta.
ruleExample2: bloquea el acceso al archivo llamado Caddyfile en la carpeta raíz.
rules: Reglas
rulesHelp1: >
Aquí puedes definir un conjunto de reglas de permisos para este usuario
específico. Los archivos bloqueados no se mostrarán en las listas y no serán accesibles
por el usuario. Puedes utilizar regex y rutas relativas a la raíz del usuario.
rulesHelp2: >
Cada regla va en una línea diferente, y debe comenzar con la palabra clave
{0} or {1}. Entonces, debes escribir {2} si estás usando una expresión regular (REGEX) y
luego la expresión o la ruta.
scope: Raíz
settingsUpdated: ¡Ajustes actualizados!
user: Usuario
userCommands: Comandos
userCommandsHelp: >
Una lista separada por espacios con los comandos permitidos para este usuario.
Ejemplo:
userCreated: ¡Usuario creado!
userDeleted: ¡Usuario eliminado!
userManagement: Administración de usuarios
username: Usuario
users: Usuarios
userUpdated: ¡Usuario actualizado!
sidebar:
help: Ayuda
logout: Cerrar sesión
myFiles: Mis archivos
newFile: Nuevo archivo
newFolder: Nueva carpeta
settings: Ajustes
siteSettings: Ajustes del sitio
hugoNew: Nuevo Hugo
preview: Vista previa
search:
images: Images
music: Música
pdf: PDF
pressToExecute: Presiona enter para ejecutar.
pressToSearch: Presiona enter para buscar.
search: Buscar...
searchOrCommand: Buscar o ejecutar un comando...
searchOrSupportedCommand: 'Buscar o ejecutar uno de los comandos soportados:'
type: Escribe y presiona enter para buscar.
types: Tipos
video: Vídeo
writeToSearch: Escribe aquí para buscar
languages:
en: English
fr: Français
pt: Português
es: Español
ja: 日本語
zhCN: 中文 (简体)
zhTW: 中文 (繁體)
time:
unit: Unidad
seconds: Segundos
minutes: Minutos
hours: Horas
days: Días

View File

@@ -115,7 +115,7 @@ settings:
commandsHelp: >
Ici vous pouvez définir des commandes qui seront exécutées lors de l'évènement correspondant.
Vous devez indiquer une commande par ligne. Si l'évènement est en rapport avec des fichiers,
par exemple avant et après enregistrement, la variable d'environement "file" sera disponible
par exemple avant et après enregistrement, la variable d'environement "FILE" sera disponible
et contiendra le chemin d'accès vers le fichier.
commandsUpdated: Commandes mises à jour !
customStylesheet: Feuille de style personnalisée
@@ -161,7 +161,6 @@ sidebar:
myFiles: Mes fichiers
newFile: Nouveau fichier
newFolder: Nouveau dossier
servedWith: Géré avec
settings: Paramètres
siteSettings: Paramètres du site
hugoNew: Nouveau Hugo
@@ -186,6 +185,7 @@ languages:
ja: 日本語
zhCN: 中文 (简体)
zhTW: 中文 (繁體)
es: Español
time:
unit: Unité de temps
seconds: Secondes

View File

@@ -6,11 +6,46 @@ import pt from './pt.yaml'
import ja from './ja.yaml'
import zhCN from './zh-cn.yaml'
import zhTW from './zh-tw.yaml'
import es from './es.yaml'
Vue.use(VueI18n)
export function detectLocale () {
let locale = (navigator.language || navigator.browserLangugae).toLowerCase()
switch (true) {
case /^en.*/i.test(locale):
locale = 'en'
break
case /^fr.*/i.test(locale):
locale = 'fr'
break
case /^pt.*/i.test(locale):
locale = 'pt'
break
case /^ja.*/i.test(locale):
locale = 'ja'
break
case /^zh-CN/i.test(locale):
locale = 'zh-cn'
break
case /^zh-TW/i.test(locale):
locale = 'zh-tw'
break
case /^zh.*/i.test(locale):
locale = 'zh-cn'
break
case /^es.*/i.test(locale):
locale = 'es'
break
default:
locale = 'en'
}
return locale
}
const i18n = new VueI18n({
locale: 'en',
locale: detectLocale(),
fallbackLocale: 'en',
messages: {
'en': en,
@@ -18,7 +53,8 @@ const i18n = new VueI18n({
'pt': pt,
'ja': ja,
'zh-cn': zhCN,
'zh-tw': zhTW
'zh-tw': zhTW,
'es': es
}
})

View File

@@ -1,198 +1,201 @@
permanent: 永久
buttons:
cancel: キャンセル
close: 閉じる
copy: コピー
copyFile: ファイルをコピー
copyToClipboard: クリップボードにコピー
create: 作成
delete: 削除
download: ダウンロード
info: 情報
more: More
move: 移動
moveFile: ファイルを移動
new: 新規
next:
ok: OK
replace: 置き換える
previous:
rename: 名前を変更
reportIssue: 問題を報告
save: 保存
search: 検索
select: 選択
share: シェア
publish: 発表
selectMultiple: 複数選択
schedule: スケジュール
switchView: 表示を切り替わる
toggleSidebar: サイドバーを表示する
update: 更新
upload: アップロード
permalink: 固定リンク
errors:
forbidden: アクセスが拒否されました
internal: 内部エラーが発生しました。
notFound: リソースが見つからなりませんでした。
files:
folders: フォルダ
files: ファイル
body: 本文
clear: クリアー
closePreview: プレビューを閉じる
home: ホーム
lastModified: 最終変更
loading: ローディング...
lonely: ここには何もない...
metadata: メタデータ
multipleSelectionEnabled: 複数選択有効
name: 名前
size: サイズ
sortByName: 名前によるソート
sortBySize: サイズによるソート
sortByLastModified: 最終変更日付によるソート
help:
click: ファイルやディレクトリを選択
ctrl:
click: 複数のファイルやディレクトリを選択
f: 検索を有効にする
s: ファイルを保存またはカレントディレクトリをダウンロード
del: 選択した項目を削除
doubleClick: ファイルディレクトリをオープン
esc: 選択をクリアーまたはプロンプトを閉じる
f1: このヘルプを表示
f2: ファイルの名前を変更
help: ヘルプ
login:
password: パスワード
submit: ログイン
username: ユーザ名
wrongCredentials: ユーザ名またはパスワードが間違っています。
prompts:
copy: コピー
copyMessage: コピーの目標ディレクトリを選択してください:
currentlyNavigating: 現在閲覧しているディレクトリ:
deleteMessageMultiple: '{count} つのファイルを本当に削除してよろしいですか。'
deleteMessageSingle: このファイル/フォルダを本当に削除してよろしいですか。
deleteTitle: ファイルを削除
displayName: 名前:
download: ファイルをダウンロード
downloadMessage: 圧縮形式を選択してください。
error: あるエラーが発生しました。
fileInfo: ファイル情報
filesSelected: '{count} つのファイルは選択されました。'
lastModified: 最終変更
move: 移動
moveMessage: 移動の目標ディレクトリを選択してください:
newDir: 新しいディレクトリを作成
newDirMessage: 新しいディレクトリの名前を入力してください
newFile: 新しいファイルを作成
newFileMessage: 新しいファイルの名前を入力してください。
numberDirs: ディレクトリ個数
numberFiles: ファイル個数
replace: 置き換える
replaceMessage: >
アップロードするファイルの中でかち合う名前が一つあります。
既存のファイルを置き換えりませんか。
rename: 名前を変更
renameMessage: 名前を変更しようファイルは:
show: 表示
size: サイズ
schedule: スケジュール
scheduleMessage: このポストの発表日付をスケジュールしてください。
newArchetype: ある元型に基づいて新しいポストを作成します。ファイルは コンテンツフォルダに作成されます。
settings:
admin: 管理者
administrator: 管理者
allowCommands: コマンドの実行
allowEdit: ファイルやディレクトリの編集、名前変更と削除
allowNew: ファイルとディレクトリの作成
allowPublish: ポストとぺーじの発表
avoidChanges: "(変更を避けるために空白にしてください)"
changePassword: パスワードを変更
commands: コマンド
commandsHelp: "\
ここで、名前付きイベントに実行するコマンドを設定することができます。\
一行にコマンド一つを入力してください。\
イベントはファイルに関連する場合、例えばファイル保存の前にまたは後で、\
環境変数 file はファイルのパスに割り当てられます。"
commandsUpdated: コマンドは更新されました!
customStylesheet: カスタムスタイルシ ート
examples:
globalSettings: グローバル設定
language: 言語
newPassword: 新しいパスワード
newPasswordConfirm: 新しいパスワードを確認します
newUser: 新しいユーザー
password: パスワード
passwordUpdated: パスワードは更新されました!
permissions: 権限
permissionsHelp: "\
あなたはユーザーを管理者に設定し、または権限を個々に設定しできます。\
\"管理者\"を選択する場合、その他のすべての選択肢は自動的に設定されます。\
ユーザーの管理は管理者の権限として保留されました。"
profileSettings: プロファイル設定
ruleExample1: "\
各フォルダに名前はドットで始まるファイル(例えば、.git、.gitignore\
へのアクセスを制限します。"
ruleExample2: 範囲のルートパスに名前は Caddyfile のファイルへのアクセスを制限します。
rules: 規則
rulesHelp1: "\
ここに、あなたはこのユーザーの許可または拒否規則を設定できます。\
ブロックされたファイルはリストに表示されません、それではアクセスも制限されます。\
正規表現(regex)のサポートと範囲に相対のパスが提供されています。"
rulesHelp2: "\
一行に規則一つを入力してください、\
その間に規則はキーワード {0} や {1} で始める必要があります。\
そして正規表現を使う場合、{2} と入力し、表現やパスを入力してください。"
scope: 範囲
settingsUpdated: 設定は更新されました!
user: ユーザー
userCommands: ユーザーのコマンド
userCommandsHelp: "\
空白区切りの有効のコマンドのリストを指定してください。\
例:"
userCreated: ユーザーは作成されました!
userDeleted: ユーザーは削除されました!
userManagement: ユーザー管理
username: ユーザー
users: ユーザー
userUpdated: ユーザーは更新されました!
sidebar:
help: ヘルプ
logout: ログアウト
myFiles: 私のファイル
newFile: 新しいファイルを作成
newFolder: 新しいフォルダを作成
servedWith: サービス提供者
settings: 設定
siteSettings: サイト設定
hugoNew: Hugo New
preview: プレビュー
search:
images: 画像
music: 音楽
pdf: PDF
pressToExecute: Enter を押して実行します。
pressToSearch: Enter を押して検索します。
search: 検索...
searchOrCommand: コマンドを検索または実行します。
searchOrSupportedCommand: サポートしているコマンドを検索または実行します:
type: キーワードを入力し、Enter を押して検索します。
types: 種類
video: ビデオ
writeToSearch: ここにキーワードを入力してください
languages:
en: English
fr: Français
pt: Português
ja: 日本語
zhCN: 中文 (简体)
zhTW: 中文 (繁體)
time:
unit: 時間単位
seconds:
minutes:
hours: 時間
days:
permanent: 永久
buttons:
cancel: キャンセル
close: 閉じる
copy: コピー
copyFile: ファイルをコピー
copyToClipboard: クリップボードにコピー
create: 作成
delete: 削除
download: ダウンロード
info: 情報
more: More
move: 移動
moveFile: ファイルを移動
new: 新規
next:
ok: OK
replace: 置き換える
previous:
rename: 名前を変更
reportIssue: 問題を報告
save: 保存
search: 検索
select: 選択
share: シェア
publish: 発表
selectMultiple: 複数選択
schedule: スケジュール
switchView: 表示を切り替わる
toggleSidebar: サイドバーを表示する
update: 更新
upload: アップロード
permalink: 固定リンク
success:
linkCopied: リンクがコピーされました
errors:
forbidden: アクセスが拒否されました。
internal: 内部エラーが発生しました。
notFound: リソースが見つからなりませんでした。
files:
folders: フォルダ
files: ファイル
body: 本文
clear: クリアー
closePreview: プレビューを閉じる
home: ホーム
lastModified: 最終変更
loading: ローディング...
lonely: ここには何もない...
metadata: メタデータ
multipleSelectionEnabled: 複数選択有効
name: 名前
size: サイズ
sortByName: 名前によるソート
sortBySize: サイズによるソート
sortByLastModified: 最終変更日付によるソート
help:
click: ファイルやディレクトリを選択
ctrl:
click: 複数のファイルディレクトリを選択
f: 検索を有効にする
s: ファイルを保存またはカレントディレクトリをダウンロード
del: 選択した項目を削除
doubleClick: ファイルやディレクトリをオープン
esc: 選択をクリアーまたはプロンプトを閉じる
f1: このヘルプを表示
f2: ファイルの名前を変更
help: ヘルプ
login:
password: パスワード
submit: ログイン
username: ユーザ名
wrongCredentials: ユーザ名またはパスワードが間違っています。
prompts:
copy: コピー
copyMessage: コピーの目標ディレクトリを選択してください:
currentlyNavigating: 現在閲覧しているディレクトリ:
deleteMessageMultiple: '{count} つのファイルを本当に削除してよろしいですか。'
deleteMessageSingle: このファイル/フォルダを本当に削除してよろしいですか。
deleteTitle: ファイルを削除
displayName: 名前:
download: ファイルをダウンロード
downloadMessage: 圧縮形式を選択してください。
error: あるエラーが発生しました。
fileInfo: ファイル情報
filesSelected: '{count} つのファイルは選択されました。'
lastModified: 最終変更
move: 移動
moveMessage: 移動の目標ディレクトリを選択してください
newDir: 新しいディレクトリを作成
newDirMessage: 新しいディレクトリの名前を入力してください。
newFile: 新しいファイルを作成
newFileMessage: 新しいファイルの名前を入力してください。
numberDirs: ディレクトリ個数
numberFiles: ファイル個数
replace: 置き換える
replaceMessage: >
アップロードするファイルの中でかち合う名前が一つあります。
既存のファイルを置き換えりませんか。
rename: 名前を変更
renameMessage: 名前を変更しようファイルは:
show: 表示
size: サイズ
schedule: スケジュール
scheduleMessage: このポストの発表日付をスケジュールしてください。
newArchetype: ある元型に基づいて新しいポストを作成します。ファイルは コンテンツフォルダに作成されます。
settings:
admin: 管理者
administrator: 管理者
allowCommands: コマンドの実行
allowEdit: ファイルやディレクトリの編集、名前変更と削除
allowNew: ファイルとディレクトリの作成
allowPublish: ポストとぺーじの発表
avoidChanges: "(変更を避けるために空白にしてください)"
changePassword: パスワードを変更
commands: コマンド
commandsHelp: "\
ここで、名前付きイベントに実行するコマンドを設定することができます。\
一行にコマンド一つを入力してください。\
イベントはファイルに関連する場合、例えばファイル保存の前にまたは後で、\
環境変数 FILE はファイルのパスに割り当てられます。"
commandsUpdated: コマンドは更新されました!
customStylesheet: カスタムスタイルシ ート
examples:
globalSettings: グローバル設定
language: 言語
lockPassword: 新しいパスワードを変更に禁止
newPassword: 新しいパスワード
newPasswordConfirm: 新しいパスワードを確認します
newUser: 新しいユーザー
password: パスワード
passwordUpdated: パスワードは更新されました!
permissions: 権限
permissionsHelp: "\
あなたはユーザーを管理者に設定し、または権限を個々に設定しできます。\
\"管理者\"を選択する場合、その他のすべての選択肢は自動的に設定されます。\
ユーザーの管理は管理者の権限として保留されました。"
profileSettings: プロファイル設定
ruleExample1: "\
各フォルダに名前はドットで始まるファイル(例えば、.git、.gitignore\
へのアクセスを制限します。"
ruleExample2: 範囲のルートパスに名前は Caddyfile のファイルへのアクセスを制限します。
rules: 規則
rulesHelp1: "\
ここに、あなたはこのユーザーの許可または拒否規則を設定できます。\
ブロックされたファイルはリストに表示されません、それではアクセスも制限されます。\
正規表現(regex)のサポートと範囲に相対のパスが提供されています。"
rulesHelp2: "\
一行に規則一つを入力してください、\
その間に規則はキーワード {0} や {1} で始める必要があります。\
そして正規表現を使う場合、{2} と入力し、表現やパスを入力してください。"
scope: 範囲
settingsUpdated: 設定は更新されました!
user: ユーザー
userCommands: ユーザーのコマンド
userCommandsHelp: "\
空白区切りの有効のコマンドのリストを指定してください。\
例:"
userCreated: ユーザーは作成されました!
userDeleted: ユーザーは削除されました!
userManagement: ユーザー管理
username: ユーザー名
users: ユーザー
userUpdated: ユーザーは更新されました!
sidebar:
help: ヘルプ
logout: ログアウト
myFiles: 私のファイル
newFile: 新しいファイルを作成
newFolder: 新しいフォルダを作成
settings: 設定
siteSettings: サイト設定
hugoNew: Hugo New
preview: プレビュー
search:
images: 画像
music: 音楽
pdf: PDF
pressToExecute: Enter を押して実行します。
pressToSearch: Enter を押して検索します。
search: 検索...
searchOrCommand: コマンドを検索または実行します。
searchOrSupportedCommand: サポートしているコマンドを検索または実行します:
type: キーワードを入力し、Enter を押して検索します。
types: 種類
video: ビデオ
writeToSearch: ここにキーワードを入力してください
languages:
en: English
fr: Français
pt: Português
ja: 日本語
zhCN: 中文 (简体)
zhTW: 中文 (繁體)
es: Español
time:
unit: 時間単位
seconds:
minutes:
hours: 時間
days:

View File

@@ -31,6 +31,8 @@ buttons:
update: Atualizar
upload: Enviar
permalink: Obter link permanente
success:
linkCopied: Link copiado!
errors:
forbidden: Tu não és bem-vindo aqui.
internal: Algo correu bastante mal.
@@ -71,6 +73,7 @@ languages:
ja: 日本語
zhCN: 中文 (简体)
zhTW: 中文 (繁體)
es: Español
login:
password: Palavra-passe
submit: Login
@@ -137,12 +140,13 @@ settings:
Pode definir um conjunto de comandos a executar em determiandos eventos.
Deve escrever um comando por linha. Se o evento estiver relacionado com ficheiros,
como antes e depois de guardar, irá existir uma variável de ambiente denominada
"file" com o caminho do ficheiro.
"FILE" com o caminho do ficheiro.
commandsUpdated: Comandos atualizados!
customStylesheet: Estilos Personalizados
examples: Exemplos
globalSettings: Configurações Globais
language: Linguagem
lockPassword: Não permitir que o utilizador altere a palavra-passe
newPassword: Nova palavra-passe
newPasswordConfirm: Confirme a nova palavra-passe
newUser: Novo Utilizador
@@ -190,7 +194,6 @@ sidebar:
newFile: Novo ficheiro
newFolder: Nova pasta
preview: Pré-visualizar
servedWith: Servido com
settings: Configurações
siteSettings: Configurações do Site
time:

View File

@@ -1,196 +1,199 @@
permanent: 永久
buttons:
cancel: 取消
close: 关闭
copy: 复制
copyFile: 复制文件
copyToClipboard: 复制到剪贴板
create: 创建
delete: 删除
download: 下载
info: 信息
more: 更多
move: 移动
moveFile: 移动文件
new:
next: 下一个
ok: 确定
replace: 替换
previous: 上一个
rename: 重命名
reportIssue: 报告问题
save: 保存
search: 搜索
select: 选择
share: 分享
publish: 发布
selectMultiple: 选择多个
schedule: 计划
switchView: 切换显示方式
toggleSidebar: 切换侧边栏
update: 更新
upload: 上传
permalink: 获取永久链接
errors:
forbidden: 你被禁止访问。
internal: 内部出现麻烦了。
notFound: 找不到文件
files:
folders: 文件
files: 文件
body: Body
clear: 清空
closePreview: 关闭预览
home: 主页
lastModified: 最后修改
loading: 加载中...
lonely: 这里没有任何文件...
metadata: 元数据
multipleSelectionEnabled: 多选模式已开启
name: 名称
size: 大小
sortByName: 名称排序
sortBySize: 大小排序
sortByLastModified: 最后修改时间排序
help:
click: 选择文件或目录
ctrl:
click: 选择多个文件或目录
f: 打开搜索框
s: 保存文件或下载当前文件夹
del: 删除所选的文件/文件夹
doubleClick: 打开文件/文件夹
esc: 清除已选项或关闭提示信息
f1: 显示该帮助信息
f2: 重命名文件/文件夹
help: 帮助
login:
password: 密码
submit: 登录
username: 用户名
wrongCredentials: 用户名或密码错误
prompts:
copy: 复制
copyMessage: 请选择欲复制至的目录:
currentlyNavigating: 当前目录:
deleteMessageMultiple: 你确定要删除这 {count} 个文件吗?
deleteMessageSingle: 你确定要删除这个文件/文件夹吗?
deleteTitle: 删除文件
displayName: 名称:
download: 下载文件
downloadMessage: 请选择要下载的压缩格式。
error: 出了一点问题...
fileInfo: 文件信息
filesSelected: 已选择 {count} 个文件。
lastModified: 最后修改
move: 移动
moveMessage: 请选择欲移动至的目录:
newDir: 新建目录
newDirMessage: 输入新目录的名称。
newFile: 新建文件
newFileMessage: 请输入新文件的名称。
numberDirs: 目录数
numberFiles: 文件数
replace: 替换
replaceMessage: "\
您尝试上传的文件中有一个与现有文件的名称存在冲突。\
是否替换现有的同名文件?"
rename: 重命名
renameMessage: 请输入新名称,旧名称为:
show: 揭示
size: 大小
schedule: 计划
scheduleMessage: 请选择发布这篇帖子的日期。
newArchetype: 创建一个基于原型的新帖子。您的文件将会创建在内容文件夹中。
settings:
admin: 管理员
administrator: 管理员
allowCommands: 执行命令(Linux 代码)
allowEdit: 编辑、重命名或删除文件/目录
allowNew: 创建新文件和目录
allowPublish: 发布新的帖子与页面
avoidChanges: '(留空以避免更改)'
changePassword: 更改密码
commands: 命令(linux 代码)
commandsHelp: "\
在这里,您可以设置在指定事件下执行的命令,一行一条。\
若事件与文件相关,如“在保存文件前”,\
则文件的路径会被赋值给环境变量 \"file\"。"
commandsUpdated: 命令已更新!
customStylesheet: 自定义样式表
examples: 例子
globalSettings: 全局设置
language: 语言
newPassword: 您的新密码
newPasswordConfirm: 重输一遍新密码
newUser: 新建用户
password: 密码
passwordUpdated: 密码已更新!
permissions: 权限
permissionsHelp: "\
您可以将该用户设置为管理员,也可以单独选择各项权限。\
如果选择了“管理员”,则其他的选项会被自动勾上,\
同时该用户可以管理其他用户。"
profileSettings: 配置文件设置
ruleExample1: "\
阻止用户访问所有文件夹下任何以 . 开头的文件\
(隐藏文件, 例如: .git, .gitignore)。"
ruleExample2: 阻止用户访问其目录范围的根目录下名为 Caddyfile 的文件。
rules: 规则
rulesHelp1: "\
您可以为该用户制定一组黑名单或白名单式的规则,\
被屏蔽的文件将不会显示在列表中,用户也无权限访问,\
支持相对于目录范围的路径。"
rulesHelp2: "\
每行一条规则,且必须以关键词 {0} 或 {1} 开头。\
如要使用正则表达式,请在加上 {2} 之后再附上表达式或路径。"
scope: 目录范围
settingsUpdated: 设置已更新!
user: 用户
userCommands: 用户命令(Linux 代码)
userCommandsHelp: "\
指定该用户可以执行的命令(Linux 代码),用空格分隔。\
例如:"
userCreated: 用户已创建!
userDeleted: 用户已删除!
userManagement: 用户管理
username: 用户
users: 用户
userUpdated: 用户已更新!
sidebar:
help: 帮助
logout: 登出
myFiles: 我的文件
newFile: 新建文件
newFolder: 新建文件夹
servedWith: '服务提供者:'
settings: 设置
siteSettings: 网站设置
hugoNew: Hugo New
preview: 预览
search:
images: 图像
music: 音乐
pdf: PDF
pressToExecute: 按回车键执行。
pressToSearch: 按回车键搜索。
search: 搜索...
searchOrCommand: 搜索或者执行命令(Linux 代码)...
searchOrSupportedCommand: 搜索或使用您可以使用的命令(一次只能执行一个命令)
type: 键入并按回车键进行搜索。
types: 类型
video: 视频
writeToSearch: 请输入要搜索的内容
languages:
en: English
fr: Français
pt: Português
ja: 日本語
zhCN: 中文 (简体)
zhTW: 中文 (繁體)
time:
unit: 时间单位
seconds:
minutes: 分钟
hours:
days:
permanent: 永久
buttons:
cancel: 取消
close: 关闭
copy: 复制
copyFile: 复制文件
copyToClipboard: 复制到剪贴板
create: 创建
delete: 删除
download: 下载
info: 信息
more: 更多
move: 移动
moveFile: 移动文件
new:
next: 下一个
ok: 确定
replace: 替换
previous: 上一个
rename: 重命名
reportIssue: 报告问题
save: 保存
search: 搜索
select: 选择
share: 分享
publish: 发布
selectMultiple: 选择多个
schedule: 计划
switchView: 切换显示方式
toggleSidebar: 切换侧边栏
update: 更新
upload: 上传
permalink: 获取永久链接
success:
linkCopied: 链接已复制!
errors:
forbidden: 你被禁止访问
internal: 内部出现麻烦了。
notFound: 找不到文件
files:
folders: 文件夹
files: 文件
body: Body
clear: 清空
closePreview: 关闭预览
home: 主页
lastModified: 最后修改
loading: 加载中...
lonely: 这里没有任何文件...
metadata: 元数据
multipleSelectionEnabled: 多选模式已开启
name: 名称
size: 大小
sortByName: 名称排序
sortBySize: 按大小排序
sortByLastModified: 按最后修改时间排序
help:
click: 选择文件或目录
ctrl:
click: 选择多个文件或目录
f: 打开搜索框
s: 保存文件或下载当前文件夹
del: 删除所选的文件/文件夹
doubleClick: 打开文件/文件夹
esc: 清除已选项或关闭提示信息
f1: 显示该帮助信息
f2: 重命名文件/文件夹
help: 帮助
login:
password: 密码
submit: 登录
username: 用户名
wrongCredentials: 用户名或密码错误
prompts:
copy: 复制
copyMessage: 请选择欲复制至的目录:
currentlyNavigating: 当前目录:
deleteMessageMultiple: 你确定要删除这 {count} 个文件吗?
deleteMessageSingle: 你确定要删除这个文件/文件夹吗?
deleteTitle: 删除文件
displayName: 名称:
download: 下载文件
downloadMessage: 请选择要下载的压缩格式。
error: 出了一点问题...
fileInfo: 文件信息
filesSelected: 已选择 {count} 个文件。
lastModified: 最后修改
move: 移动
moveMessage: 选择欲移动至的目录:
newDir: 新建目录
newDirMessage: 请输入新目录的名称。
newFile: 新建文件
newFileMessage: 请输入新文件的名称。
numberDirs: 目录数
numberFiles: 文件数
replace: 替换
replaceMessage: "\
您尝试上传的文件中有一个与现有文件的名称存在冲突。\
是否替换现有的同名文件?"
rename: 重命名
renameMessage: 请输入新名称,旧名称为:
show: 揭示
size: 大小
schedule: 计划
scheduleMessage: 请选择发布这篇帖子的日期。
newArchetype: 创建一个基于原型的新帖子。您的文件将会创建在内容文件夹中。
settings:
admin: 管理员
administrator: 管理员
allowCommands: 执行命令(Linux 代码)
allowEdit: 编辑、重命名或删除文件/目录
allowNew: 创建新文件和目录
allowPublish: 发布新的帖子与页面
avoidChanges: '(留空以避免更改)'
changePassword: 更改密码
commands: 命令(linux 代码)
commandsHelp: "\
在这里,您可以设置在指定事件下执行的命令,一行一条。\
若事件与文件相关,如“在保存文件前”,\
则文件的路径会被赋值给环境变量 \"FILE\"。"
commandsUpdated: 命令已更新!
customStylesheet: 自定义样式表
examples: 例子
globalSettings: 全局设置
language: 语言
lockPassword: 禁止用户修改密码
newPassword: 您的新密码
newPasswordConfirm: 重输一遍新密码
newUser: 新建用户
password: 密码
passwordUpdated: 密码已更新!
permissions: 权限
permissionsHelp: "\
您可以将该用户设置为管理员,也可以单独选择各项权限。\
如果选择了“管理员”,则其他的选项会被自动勾上,\
同时该用户可以管理其他用户。"
profileSettings: 配置文件设置
ruleExample1: "\
阻止用户访问所有文件夹下任何以 . 开头的文件\
(隐藏文件, 例如: .git, .gitignore)。"
ruleExample2: 阻止用户访问其目录范围的根目录下名为 Caddyfile 的文件。
rules: 规则
rulesHelp1: "\
您可以为该用户制定一组黑名单或白名单式的规则,\
被屏蔽的文件将不会显示在列表中,用户也无权限访问,\
支持相对于目录范围的路径。"
rulesHelp2: "\
每行一条规则,且必须以关键词 {0} 或 {1} 开头。\
如要使用正则表达式,请在加上 {2} 之后再附上表达式或路径。"
scope: 目录范围
settingsUpdated: 设置已更新!
user: 用户
userCommands: 用户命令(Linux 代码)
userCommandsHelp: "\
指定该用户可以执行的命令(Linux 代码),用空格分隔。\
例如:"
userCreated: 用户已创建!
userDeleted: 用户已删除!
userManagement: 用户管理
username: 用户名
users: 用户
userUpdated: 用户已更新!
sidebar:
help: 帮助
logout: 登出
myFiles: 我的文件
newFile: 新建文件
newFolder: 新建文件夹
settings: 设置
siteSettings: 网站设置
hugoNew: Hugo New
preview: 预览
search:
images: 图像
music: 音乐
pdf: PDF
pressToExecute: 按回车键执行。
pressToSearch: 按回车键搜索。
search: 搜索...
searchOrCommand: 搜索或者执行命令(Linux 代码)...
searchOrSupportedCommand: 搜索或使用您可以使用的命令(一次只能执行一个命令)
type: 键入并按回车键进行搜索。
types: 类型
video: 视频
writeToSearch: 请输入要搜索的内容
languages:
en: English
fr: Français
pt: Português
ja: 日本語
zhCN: 中文 (简体)
zhTW: 中文 (繁體)
es: Español
time:
unit: 间单位
seconds:
minutes: 分钟
hours: 小时
days:

View File

@@ -4,7 +4,7 @@ buttons:
close: 關閉
copy: 複製
copyFile: 複製檔案
copyToClipboard: 複製到剪貼
copyToClipboard: 複製到剪貼簿
create: 建立
delete: 刪除
download: 下載
@@ -31,8 +31,10 @@ buttons:
update: 更新
upload: 上傳
permalink: 獲取永久連結
success:
linkCopied: 連結已複製!
errors:
forbidden: 你被禁止訪問
forbidden: 你被禁止存取
internal: 內部出現麻煩了。
notFound: 找不到檔案。
files:
@@ -45,7 +47,7 @@ files:
lastModified: 最後修改
loading: 讀取中...
lonely: 這裡沒有任何檔案...
metadata: 中繼資料
metadata: 詮釋資料
multipleSelectionEnabled: 多選模式已開啟
name: 名稱
size: 大小
@@ -100,70 +102,70 @@ prompts:
show: 顯示
size: 大小
schedule: 計畫
scheduleMessage: 請選擇發佈這篇帖子的日期。
newArchetype: 建立一個基於原型的新帖子。您的檔案將會建立在內容資料夾中。
scheduleMessage: 請選擇發佈這篇貼文的日期。
newArchetype: 建立一個基於原型的新貼文。您的檔案將會建立在內容資料夾中。
settings:
admin: 管理員
administrator: 管理員
allowCommands: 執行命令
allowEdit: 編輯、重命名或刪除檔案/目錄
allowNew: 創建新檔案和目錄
allowPublish: 發佈新的帖子與頁面
allowPublish: 發佈新的貼文與頁面
avoidChanges: '(留空以避免更改)'
changePassword: 更改密碼
commands: 命令
commandsHelp: "\
在這裡,您可以設定在指定事件下執行的命令,一行一條。\
若事件與檔案相關,如“在保存檔案前”,\
則檔案的路徑會被賦值給環境變數 \"file\"。"
則檔案的路徑會被賦值給環境變數 \"FILE\"。"
commandsUpdated: 命令已更新!
customStylesheet: 自定義樣式表
examples:
examples:
globalSettings: 全域設定
language: 語言
lockPassword: 禁止使用者修改密碼
newPassword: 您的新密碼
newPasswordConfirm: 重輸一遍新密碼
newUser: 建立用戶
newUser: 建立使用者
password: 密碼
passwordUpdated: 密碼已更新!
permissions: 權限
permissionsHelp: "\
您可以將該用戶設置為管理員,也可以單獨選擇各項權限。\
您可以將該使用者設置為管理員,也可以單獨選擇各項權限。\
如果選擇了“管理員”,則其他的選項會被自動勾上,\
同時該用戶可以管理其他用戶。"
同時該使用者可以管理其他使用者。"
profileSettings: 設定檔設定
ruleExample1: "\
封鎖用戶訪問所有資料夾下任何以 . 開頭的檔案\
封鎖使用者存取所有資料夾下任何以 . 開頭的檔案\
(隱藏文件, 例如: .git, .gitignore)。"
ruleExample2: 封鎖用戶訪問其目錄範圍的根目錄下名為 Caddyfile 的檔案。
ruleExample2: 封鎖使用者存取其目錄範圍的根目錄下名為 Caddyfile 的檔案。
rules: 規則
rulesHelp1: "\
您可以為該用戶製定一組黑名單或白名單式的規則,\
被屏蔽的檔案將不會顯示在清單中,用戶也無權限訪問,\
您可以為該使用者製定一組黑名單或白名單式的規則,\
被屏蔽的檔案將不會顯示在清單中,使用者也無權限存取,\
支持相對於目錄範圍的路徑。"
rulesHelp2: "\
每行一條規則,且必須以關鍵字 {0} 或 {1} 開頭。\
如要使用規則運算式,請在加上 {2} 之後再附上運算式或路徑。"
scope: 目錄範圍
settingsUpdated: 設定已更新!
user: 用戶
userCommands: 用戶命令
user: 使用者
userCommands: 使用者命令
userCommandsHelp: "\
指定該用戶可以執行的命令,用空格分隔。\
指定該使用者可以執行的命令,用空格分隔。\
例如:"
userCreated: 用戶已建立!
userDeleted: 用戶已刪除!
userManagement: 用戶管理
username: 用戶名
users: 用戶
userUpdated: 用戶已更新!
userCreated: 使用者已建立!
userDeleted: 使用者已刪除!
userManagement: 使用者管理
username: 使用者名稱
users: 使用者
userUpdated: 使用者已更新!
sidebar:
help: 幫助
logout: 登出
myFiles: 我的檔案
newFile: 建立檔案
newFolder: 建立資料夾
servedWith: '服務提供者:'
settings: 設定
siteSettings: 網站設定
hugoNew: Hugo New
@@ -187,9 +189,11 @@ languages:
pt: Português
ja: 日本語
zhCN: 中文 (简体)
zhTW: 中文 (繁體)
es: Español
time:
unit: 時間單位
seconds:
minutes: 分鐘
hours: 小時
days:
days:

View File

@@ -3,9 +3,46 @@ import App from './App'
import store from './store'
import router from './router'
import i18n from './i18n'
import Noty from 'noty'
Vue.config.productionTip = true
const notyDefault = {
type: 'info',
layout: 'bottomRight',
timeout: 1000,
progressBar: true
}
Vue.prototype.$noty = function (opts) {
new Noty(Object.assign({}, notyDefault, opts)).show()
}
Vue.prototype.$showSuccess = function (message) {
new Noty(Object.assign({}, notyDefault, {
text: message,
type: 'success'
})).show()
}
Vue.prototype.$showError = function (error) {
let n = new Noty(Object.assign({}, notyDefault, {
text: error,
type: 'error',
timeout: null,
buttons: [
Noty.button(i18n.t('buttons.reportIssue'), '', function () {
window.open('https://github.com/hacdias/filemanager/issues/new')
}),
Noty.button(i18n.t('buttons.close'), '', function () {
n.close()
})
]
}))
n.show()
}
/* eslint-disable no-new */
new Vue({
el: '#app',

View File

@@ -3,14 +3,15 @@ import Router from 'vue-router'
import Login from '@/views/Login'
import Layout from '@/views/Layout'
import Files from '@/views/Files'
import Users from '@/views/Users'
import User from '@/views/User'
import GlobalSettings from '@/views/GlobalSettings'
import ProfileSettings from '@/views/ProfileSettings'
import Users from '@/views/settings/Users'
import User from '@/views/settings/User'
import Settings from '@/views/Settings'
import GlobalSettings from '@/views/settings/Global'
import ProfileSettings from '@/views/settings/Profile'
import Error403 from '@/views/errors/403'
import Error404 from '@/views/errors/404'
import Error500 from '@/views/errors/500'
import auth from '@/utils/auth.js'
import auth from '@/utils/auth'
import store from '@/store'
Vue.use(Router)
@@ -49,22 +50,44 @@ const router = new Router({
{
path: '/settings',
name: 'Settings',
component: Settings,
redirect: {
path: '/settings/profile'
}
},
{
path: '/settings/profile',
name: 'Profile Settings',
component: ProfileSettings
},
{
path: '/settings/global',
name: 'Global Settings',
component: GlobalSettings,
},
meta: {
requiresAdmin: true
}
disableOnNoAuth: true
},
children: [
{
path: '/settings/profile',
name: 'Profile Settings',
component: ProfileSettings
},
{
path: '/settings/global',
name: 'Global Settings',
component: GlobalSettings,
meta: {
requiresAdmin: true
}
},
{
path: '/settings/users',
name: 'Users',
component: Users,
meta: {
requiresAdmin: true
}
},
{
path: '/settings/users/*',
name: 'User',
component: User,
meta: {
requiresAdmin: true
}
}
]
},
{
path: '/403',
@@ -81,22 +104,6 @@ const router = new Router({
name: 'Internal Server Error',
component: Error500
},
{
path: '/users',
name: 'Users',
component: Users,
meta: {
requiresAdmin: true
}
},
{
path: '/users/*',
name: 'User',
component: User,
meta: {
requiresAdmin: true
}
},
{
path: '/files',
redirect: {
@@ -123,16 +130,17 @@ router.beforeEach((to, from, next) => {
auth.loggedIn()
.then(() => {
if (to.matched.some(record => record.meta.requiresAdmin)) {
if (store.state.user.admin) {
next()
if (!store.state.user.admin) {
next({ path: '/403' })
return
}
}
next({
path: '/403'
})
return
if (to.matched.some(record => record.meta.disableOnNoAuth)) {
if (store.state.noAuth) {
next({ path: '/403' })
return
}
}
next()

View File

@@ -12,9 +12,16 @@ const state = {
key: '',
items: []
},
css: (() => {
let css = window.CSS
window.CSS = null
return css
})(),
recaptcha: document.querySelector('meta[name="recaptcha"]').getAttribute('content'),
staticGen: document.querySelector('meta[name="staticgen"]').getAttribute('content'),
baseURL: document.querySelector('meta[name="base"]').getAttribute('content'),
noAuth: (document.querySelector('meta[name="noauth"]').getAttribute('content') === 'true'),
version: document.querySelector('meta[name="version"]').getAttribute('content'),
jwt: '',
progress: 0,
schedule: '',

View File

@@ -1,4 +1,4 @@
import i18n from '@/i18n'
import * as i18n from '@/i18n'
import moment from 'moment'
const mutations = {
@@ -27,10 +27,17 @@ const mutations = {
setLoading: (state, value) => { state.loading = value },
setReload: (state, value) => { state.reload = value },
setUser: (state, value) => {
moment.locale(value.locale)
i18n.locale = value.locale
let locale = value.locale
if (locale === '') {
locale = i18n.detectLocale()
}
moment.locale(locale)
i18n.default.locale = locale
state.user = value
},
setCSS: (state, value) => (state.css = value),
setJWT: (state, value) => (state.jwt = value),
multiple: (state, value) => (state.multiple = value),
addSelected: (state, value) => (state.selected.push(value)),
@@ -45,8 +52,12 @@ const mutations = {
resetSelected: (state) => {
state.selected = []
},
listingDisplay: (state, value) => {
state.req.display = value
updateUser: (state, value) => {
if (typeof value !== 'object') return
for (let field in value) {
state.user[field] = value[field]
}
},
updateRequest: (state, value) => {
state.req = value

View File

@@ -1,13 +1,18 @@
import cookie from './cookie'
import store from '@/store'
import router from '@/router'
import { Base64 } from 'js-base64'
function parseToken (token) {
let path = store.state.baseURL
if (path === '') path = '/'
document.cookie = `auth=${token}; max-age=86400; path=${path}`
let res = token.split('.')
let user = JSON.parse(window.atob(res[1]))
let user = JSON.parse(Base64.decode(res[1]))
if (!user.commands) {
user.commands = []
}
store.commit('setJWT', token)
store.commit('setUser', user)
}
@@ -31,8 +36,8 @@ function loggedIn () {
})
}
function login (user, password) {
let data = {username: user, password: password}
function login (user, password, captcha) {
let data = {username: user, password: password, recaptcha: captcha}
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('POST', `${store.state.baseURL}/api/auth/get`, true)

View File

@@ -1,60 +1,60 @@
// Most of the code from this file comes from:
// https://github.com/codemirror/CodeMirror/blob/master/addon/mode/loadmode.js
import * as CodeMirror from 'codemirror'
import store from '@/store'
// Make CodeMirror available globally so the modes' can register themselves.
window.CodeMirror = CodeMirror
CodeMirror.modeURL = store.state.baseURL + '/static/js/codemirror/mode/%N/%N.js'
var loading = {}
function splitCallback (cont, n) {
var countDown = n
return function () {
if (--countDown === 0) cont()
}
}
function ensureDeps (mode, cont) {
var deps = CodeMirror.modes[mode].dependencies
if (!deps) return cont()
var missing = []
for (var i = 0; i < deps.length; ++i) {
if (!CodeMirror.modes.hasOwnProperty(deps[i])) missing.push(deps[i])
}
if (!missing.length) return cont()
var split = splitCallback(cont, missing.length)
for (i = 0; i < missing.length; ++i) CodeMirror.requireMode(missing[i], split)
}
CodeMirror.requireMode = function (mode, cont) {
if (typeof mode !== 'string') mode = mode.name
if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont)
if (loading.hasOwnProperty(mode)) return loading[mode].push(cont)
var file = CodeMirror.modeURL.replace(/%N/g, mode)
var script = document.createElement('script')
script.src = file
var others = document.getElementsByTagName('script')[0]
var list = loading[mode] = [cont]
CodeMirror.on(script, 'load', function () {
ensureDeps(mode, function () {
for (var i = 0; i < list.length; ++i) list[i]()
})
})
others.parentNode.insertBefore(script, others)
}
CodeMirror.autoLoadMode = function (instance, mode) {
if (CodeMirror.modes.hasOwnProperty(mode)) return
CodeMirror.requireMode(mode, function () {
instance.setOption('mode', mode)
})
}
export default CodeMirror
// Most of the code from this file comes from:
// https://github.com/codemirror/CodeMirror/blob/master/addon/mode/loadmode.js
import * as CodeMirror from 'codemirror'
import store from '@/store'
// Make CodeMirror available globally so the modes' can register themselves.
window.CodeMirror = CodeMirror
CodeMirror.modeURL = store.state.baseURL + '/static/js/codemirror/mode/%N/%N.js'
var loading = {}
function splitCallback (cont, n) {
var countDown = n
return function () {
if (--countDown === 0) cont()
}
}
function ensureDeps (mode, cont) {
var deps = CodeMirror.modes[mode].dependencies
if (!deps) return cont()
var missing = []
for (var i = 0; i < deps.length; ++i) {
if (!CodeMirror.modes.hasOwnProperty(deps[i])) missing.push(deps[i])
}
if (!missing.length) return cont()
var split = splitCallback(cont, missing.length)
for (i = 0; i < missing.length; ++i) CodeMirror.requireMode(missing[i], split)
}
CodeMirror.requireMode = function (mode, cont) {
if (typeof mode !== 'string') mode = mode.name
if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont)
if (loading.hasOwnProperty(mode)) return loading[mode].push(cont)
var file = CodeMirror.modeURL.replace(/%N/g, mode)
var script = document.createElement('script')
script.src = file
var others = document.getElementsByTagName('script')[0]
var list = loading[mode] = [cont]
CodeMirror.on(script, 'load', function () {
ensureDeps(mode, function () {
for (var i = 0; i < list.length; ++i) list[i]()
})
})
others.parentNode.insertBefore(script, others)
}
CodeMirror.autoLoadMode = function (instance, mode) {
if (CodeMirror.modes.hasOwnProperty(mode)) return
CodeMirror.requireMode(mode, function () {
instance.setOption('mode', mode)
})
}
export default CodeMirror

View File

@@ -1,4 +1,4 @@
export default function (name) {
let re = new RegExp('(?:(?:^|.*;\\s*)' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$')
return document.cookie.replace(re, '$1')
}
export default function (name) {
let re = new RegExp('(?:(?:^|.*;\\s*)' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$')
return document.cookie.replace(re, '$1')
}

View File

@@ -1,28 +1,28 @@
export default function getRule (rules) {
for (let i = 0; i < rules.length; i++) {
rules[i] = rules[i].toLowerCase()
}
let result = null
let find = Array.prototype.find
find.call(document.styleSheets, styleSheet => {
result = find.call(styleSheet.cssRules, cssRule => {
let found = false
if (cssRule instanceof window.CSSStyleRule) {
for (let i = 0; i < rules.length; i++) {
if (cssRule.selectorText.toLowerCase() === rules[i]) {
found = true
}
}
}
return found
})
return result != null
})
return result
}
export default function getRule (rules) {
for (let i = 0; i < rules.length; i++) {
rules[i] = rules[i].toLowerCase()
}
let result = null
let find = Array.prototype.find
find.call(document.styleSheets, styleSheet => {
result = find.call(styleSheet.cssRules, cssRule => {
let found = false
if (cssRule instanceof window.CSSStyleRule) {
for (let i = 0; i < rules.length; i++) {
if (cssRule.selectorText.toLowerCase() === rules[i]) {
found = true
}
}
}
return found
})
return result != null
})
return result
}

View File

@@ -1,12 +1,12 @@
function removeLastDir (url) {
var arr = url.split('/')
if (arr.pop() === '') {
arr.pop()
}
return arr.join('/')
}
export default {
removeLastDir: removeLastDir
}
function removeLastDir (url) {
var arr = url.split('/')
if (arr.pop() === '') {
arr.pop()
}
return arr.join('/')
}
export default {
removeLastDir: removeLastDir
}

View File

@@ -210,7 +210,7 @@ export default {
}
},
scroll (event) {
if (this.req.kind !== 'listing' || this.$store.state.req.display === 'mosaic') return
if (this.req.kind !== 'listing' || this.$store.state.user.viewMode === 'mosaic') return
let top = 112 - window.scrollY

View File

@@ -1,162 +0,0 @@
<template>
<div class="dashboard">
<ul id="nav">
<li>
<router-link to="/settings/profile">
<i class="material-icons">keyboard_arrow_left</i> {{ $t('settings.profileSettings') }}
</router-link>
</li>
<li>
<router-link to="/users">
{{ $t('settings.userManagement') }} <i class="material-icons">keyboard_arrow_right</i>
</router-link>
</li>
</ul>
<h1>{{ $t('settings.globalSettings') }}</h1>
<form @submit="saveStaticGen" v-if="$store.state.staticGen.length > 0">
<h2>{{ capitalize($store.state.staticGen) }}</h2>
<p v-for="field in staticGen" :key="field.variable">
<label v-if="field.type !== 'checkbox'">{{ field.name }}</label>
<input v-if="field.type === 'text'" type="text" v-model.trim="field.value">
<input v-else-if="field.type === 'checkbox'" type="checkbox" v-model.trim="field.value">
<template v-if="field.type === 'checkbox'">{{ capitalize(field.name, 'caps') }}</template>
</p>
<p><input type="submit" value="Save"></p>
</form>
<form @submit="saveCommands">
<h2>{{ $t('settings.commands') }}</h2>
<p class="small">{{ $t('settings.commandsHelp') }}</p>
<template v-for="command in commands">
<h3>{{ capitalize(command.name) }}</h3>
<textarea v-model.trim="command.value"></textarea>
</template>
<p><input type="submit" value="Save"></p>
</form>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
import { getSettings, updateSettings } from '@/utils/api'
export default {
name: 'settings',
data: function () {
return {
commands: [],
staticGen: []
}
},
computed: {
...mapState([ 'user' ])
},
created () {
getSettings()
.then(settings => {
if (this.$store.state.staticGen.length > 0) {
this.parseStaticGen(settings.staticGen)
}
for (let key in settings.commands) {
this.commands.push({
name: key,
value: settings.commands[key].join('\n')
})
}
})
.catch(error => { this.showError(error) })
},
methods: {
...mapMutations([ 'showSuccess', 'showError' ]),
capitalize (name, where = '_') {
if (where === 'caps') where = /(?=[A-Z])/
let splitted = name.split(where)
name = ''
for (let i = 0; i < splitted.length; i++) {
name += splitted[i].charAt(0).toUpperCase() + splitted[i].slice(1) + ' '
}
return name.slice(0, -1)
},
saveCommands (event) {
event.preventDefault()
let commands = {}
for (let command of this.commands) {
let value = command.value.split('\n')
if (value.length === 1 && value[0] === '') {
value = []
}
commands[command.name] = value
}
updateSettings(commands, 'commands')
.then(() => { this.showSuccess(this.$t('settings.commandsUpdated')) })
.catch(error => { this.showError(error) })
},
saveStaticGen (event) {
event.preventDefault()
let staticGen = {}
for (let field of this.staticGen) {
staticGen[field.variable] = field.value
if (field.original === 'array') {
let val = field.value.split(' ')
if (val[0] === '') {
val.shift()
}
staticGen[field.variable] = val
}
}
updateSettings(staticGen, 'staticGen')
.then(() => { this.showSuccess(this.$t('settings.settingsUpdated')) })
.catch(error => { this.showError(error) })
},
parseStaticGen (staticgen) {
for (let option of staticgen) {
let value = option.value
let field = {
name: option.name,
variable: option.variable,
type: 'text',
original: 'text',
value: value
}
if (Array.isArray(value)) {
field.original = 'array'
field.value = value.join(' ')
this.staticGen.push(field)
continue
}
switch (typeof value) {
case 'boolean':
field.type = 'checkbox'
field.original = 'boolean'
break
}
this.staticGen.push(field)
}
}
}
}
</script>

View File

@@ -6,7 +6,7 @@
<site-header></site-header>
<sidebar></sidebar>
<main>
<router-view v-on:css-updated="updateCSS"></router-view>
<router-view @css="$emit('update:css')"></router-view>
</main>
<prompts></prompts>
</div>
@@ -34,23 +34,10 @@ export default {
}
},
mounted () {
this.updateCSS()
this.$emit('update:css')
},
methods: {
updateCSS () {
let css = this.$store.state.user.css
let style = document.querySelector('style[title="user-css"]')
if (style !== undefined && style !== null) {
style.parentElement.removeChild(style)
}
style = document.createElement('style')
style.title = 'user-css'
style.type = 'text/css'
style.appendChild(document.createTextNode(css))
document.head.appendChild(style)
}
beforeDestroy () {
this.$emit('clean:css')
}
}
</script>

View File

@@ -1,11 +1,12 @@
<template>
<div id="login">
<div id="login" :class="{ recaptcha: recaptcha.length > 0 }">
<form @submit="submit">
<img src="../assets/logo.svg" alt="File Manager">
<h1>File Manager</h1>
<div v-if="wrong" class="wrong">{{ $t("login.wrongCredentials") }}</div>
<input type="text" v-model="username" :placeholder="$t('login.username')">
<input type="password" v-model="password" :placeholder="$t('login.password')">
<div v-if="recaptcha.length" id="recaptcha"></div>
<input type="submit" :value="$t('login.submit')">
</form>
</div>
@@ -13,9 +14,12 @@
<script>
import auth from '@/utils/auth'
import { mapState } from 'vuex'
export default {
name: 'login',
props: ['dependencies'],
computed: mapState(['recaptcha']),
data: function () {
return {
wrong: false,
@@ -23,8 +27,23 @@ export default {
password: ''
}
},
mounted () {
if (this.dependencies) this.setup()
},
watch: {
dependencies: function (val) {
if (val) this.setup()
}
},
methods: {
submit: function (event) {
setup () {
if (this.recaptcha.length === 0) return
window.grecaptcha.render('recaptcha', {
sitekey: this.recaptcha
})
},
submit (event) {
event.preventDefault()
event.stopPropagation()
@@ -33,7 +52,17 @@ export default {
redirect = '/files/'
}
auth.login(this.username, this.password)
let captcha = ''
if (this.recaptcha.length > 0) {
captcha = window.grecaptcha.getResponse()
if (captcha === '') {
this.wrong = true
return
}
}
auth.login(this.username, this.password, captcha)
.then(() => { this.$router.push({ path: redirect }) })
.catch(() => { this.wrong = true })
}

View File

@@ -1,103 +0,0 @@
<template>
<div class="dashboard">
<ul id="nav" v-if="user.admin">
<li>
<router-link to="/settings/global">
{{ $t('settings.globalSettings') }} <i class="material-icons">keyboard_arrow_right</i>
</router-link>
</li>
</ul>
<h1>{{ $t('settings.profileSettings') }}</h1>
<form @submit="updateSettings">
<h3>{{ $t('settings.language') }}</h3>
<p><languages id="locale" :selected.sync="locale"></languages></p>
<h3>{{ $t('settings.customStylesheet') }}</h3>
<textarea v-model="css" name="css"></textarea>
<p><input type="submit" :value="$t('buttons.update')"></p>
</form>
<form @submit="updatePassword">
<h3>{{ $t('settings.changePassword') }}</h3>
<p><input :class="passwordClass" type="password" :placeholder="$t('settings.newPassword')" v-model="password" name="password"></p>
<p><input :class="passwordClass" type="password" :placeholder="$t('settings.newPasswordConfirm')" v-model="passwordConf" name="password"></p>
<p><input type="submit" :value="$t('buttons.update')"></p>
</form>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
import { updateUser } from '@/utils/api'
import Languages from '@/components/Languages'
export default {
name: 'settings',
components: {
Languages
},
data: function () {
return {
password: '',
passwordConf: '',
css: '',
locale: ''
}
},
computed: {
...mapState([ 'user' ]),
passwordClass () {
if (this.password === '' && this.passwordConf === '') {
return ''
}
if (this.password === this.passwordConf) {
return 'green'
}
return 'red'
}
},
created () {
this.css = this.user.css
this.locale = this.user.locale
},
methods: {
...mapMutations([ 'showSuccess' ]),
updatePassword (event) {
event.preventDefault()
if (this.password !== this.passwordConf) {
return
}
let user = {
ID: this.$store.state.user.ID,
password: this.password
}
updateUser(user, 'password').then(location => {
this.showSuccess(this.$t('settings.passwordUpdated'))
}).catch(e => {
this.$store.commit('showError', e)
})
},
updateSettings (event) {
event.preventDefault()
let user = {...this.$store.state.user}
user.css = this.css
user.locale = this.locale
updateUser(user, 'partial').then(location => {
this.$store.commit('setUser', user)
this.$emit('css-updated')
this.showSuccess(this.$t('settings.settingsUpdated'))
}).catch(e => {
this.$store.commit('showError', e)
})
}
}
}
</script>

View File

@@ -0,0 +1,20 @@
<template>
<div class="dashboard">
<ul id="nav" v-if="user.admin">
<li :class="{ active: $route.path === '/settings/profile' }"><router-link to="/settings/profile">{{ $t('settings.profileSettings') }}</router-link></li>
<li :class="{ active: $route.path === '/settings/global' }"><router-link to="/settings/global">{{ $t('settings.globalSettings') }}</router-link></li>
<li :class="{ active: $route.path === '/settings/users' }"><router-link to="/settings/users">{{ $t('settings.userManagement') }}</router-link></li>
</ul>
<router-view @css="$emit('css')"></router-view>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'settings',
computed: mapState([ 'user' ])
}
</script>

View File

@@ -1,296 +0,0 @@
<template>
<div>
<form @submit="save" class="dashboard">
<ul id="nav">
<li>
<router-link to="/users">
<i class="material-icons">keyboard_arrow_left</i> {{ $t('settings.userManagement') }}
</router-link>
</li>
<li></li>
</ul>
<h1 v-if="id === 0">{{ $t('settings.newUser') }}</h1>
<h1 v-else>{{ $t('settings.user') }} {{ username }}</h1>
<p><label for="username">{{ $t('settings.username') }}</label><input type="text" v-model="username" id="username"></p>
<p><label for="password">{{ $t('settings.password') }}</label><input type="password" :placeholder="passwordPlaceholder" v-model="password" id="password"></p>
<p><label for="scope">{{ $t('settings.scope') }}</label><input type="text" v-model="filesystem" id="scope"></p>
<p>
<label for="locale">{{ $t('settings.language') }}</label>
<languages id="locale" :selected.sync="locale"></languages>
</p>
<h2>{{ $t('settings.permissions') }}</h2>
<p class="small">{{ $t('settings.permissionsHelp') }}</p>
<p><input type="checkbox" v-model="admin"> {{ $t('settings.administrator') }}</p>
<p><input type="checkbox" :disabled="admin" v-model="allowNew"> {{ $t('settings.allowNew') }}</p>
<p><input type="checkbox" :disabled="admin" v-model="allowEdit"> {{ $t('settings.allowEdit') }}</p>
<p><input type="checkbox" :disabled="admin" v-model="allowCommands"> {{ $t('settings.allowCommands') }}</p>
<p v-show="$store.state.staticGen.length"><input type="checkbox" :disabled="admin" v-model="allowPublish"> {{ $t('settings.allowPublish') }}</p>
<h3>{{ $t('settings.userCommands') }}</h3>
<p class="small">{{ $t('settings.userCommandsHelp') }} <i>git svn hg</i>.</p>
<input type="text" v-model.trim="commands">
<h2>{{ $t('settings.rules') }}</h2>
<p class="small">{{ $t('settings.rulesHelp1') }}</p>
<i18n path="settings.rulesHelp2" tag="p" class="small">
<code>allow</code><code>disallow</code><code>regex</code>
</i18n>
<p class="small"><strong>{{ $t('settings.examples') }}</strong></p>
<ul class="small">
<li><code>disallow regex \\/\\..+</code> - {{ $t('settings.ruleExample1') }}</li>
<li><code>disallow /Caddyfile</code> - {{ $t('settings.ruleExample2') }}</li>
</ul>
<textarea v-model.trim="rules"></textarea>
<h2>{{ $t('settings.customStylesheet') }}</h2>
<textarea name="css"></textarea>
<p>
<button v-if="id !== 0" @click.prevent="deletePrompt" type="button" class="delete" :aria-label="$t('buttons.delete')" :title="$t('buttons.delete')">{{ $t('buttons.delete') }}</button>
<input type="submit" :value="$t('buttons.save')">
</p>
</form>
<div v-if="$store.state.show === 'deleteUser'" class="prompt">
<h3>Delete User</h3>
<p>Are you sure you want to delete this user?</p>
<div>
<button @click="deleteUser" autofocus>{{ $t('buttons.delete') }}</button>
<button class="cancel"
@click="closeHovers"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')">
{{ $t('buttons.cancel') }}
</button>
</div>
</div>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import { getUser, newUser, updateUser, deleteUser } from '@/utils/api'
import Languages from '@/components/Languages'
export default {
name: 'user',
components: { Languages },
data: () => {
return {
id: 0,
admin: false,
allowNew: false,
allowEdit: false,
allowCommands: false,
allowPublish: false,
permissions: {},
password: '',
username: '',
filesystem: '',
rules: '',
locale: '',
css: '',
commands: ''
}
},
computed: {
passwordPlaceholder () {
if (this.$route.path === '/users/new') return ''
return this.$t('settings.avoidChanges')
}
},
created () {
this.fetchData()
},
watch: {
'$route': 'fetchData',
admin: function () {
if (!this.admin) return
this.allowCommands = true
this.allowEdit = true
this.allowNew = true
this.allowPublish = true
for (let key in this.permissions) {
this.permissions[key] = true
}
}
},
methods: {
...mapMutations(['closeHovers']),
fetchData () {
let user = this.$route.params[0]
if (this.$route.path === '/users/new') {
user = 'base'
}
getUser(user).then(user => {
this.id = user.ID
this.admin = user.admin
this.allowCommands = user.allowCommands
this.allowNew = user.allowNew
this.allowEdit = user.allowEdit
this.allowPublish = user.allowPublish
this.filesystem = user.filesystem
this.username = user.username
this.commands = user.commands.join(' ')
this.css = user.css
this.permissions = user.permissions
this.locale = user.locale
for (let rule of user.rules) {
if (rule.allow) {
this.rules += 'allow '
} else {
this.rules += 'disallow '
}
if (rule.regex) {
this.rules += 'regex ' + rule.regexp.raw
} else {
this.rules += rule.path
}
this.rules += '\n'
}
this.rules = this.rules.trim()
}).catch(() => {
this.$router.push({ path: '/users/new' })
})
},
capitalize (name) {
let splitted = name.split(/(?=[A-Z])/)
name = ''
for (let i = 0; i < splitted.length; i++) {
name += splitted[i].charAt(0).toUpperCase() + splitted[i].slice(1) + ' '
}
return name.slice(0, -1)
},
reset () {
this.id = 0
this.admin = false
this.allowNew = false
this.allowEdit = false
this.allowPublish = false
this.permissins = {}
this.allowCommands = false
this.password = ''
this.username = ''
this.filesystem = ''
this.rules = ''
this.locale = ''
this.css = ''
this.commands = ''
},
deletePrompt (event) {
this.$store.commit('showHover', 'deleteUser')
},
deleteUser (event) {
event.preventDefault()
deleteUser(this.id).then(location => {
this.$router.push({ path: '/users' })
this.$store.commit('showSuccess', this.$t('settings.userDeleted'))
}).catch(e => {
this.$store.commit('showError', e)
})
},
save (event) {
event.preventDefault()
let user = this.parseForm()
if (this.$route.path === '/users/new') {
newUser(user).then(location => {
this.$router.push({ path: location })
this.$store.commit('showSuccess', this.$t('settings.userCreated'))
}).catch(e => {
this.$store.commit('showError', e)
})
return
}
updateUser(user).then(location => {
if (user.ID === this.$store.state.user.ID) {
this.$store.commit('setUser', user)
}
this.$store.commit('showSuccess', this.$t('settings.userUpdated'))
}).catch(e => {
this.$store.commit('showError', e)
})
},
parseForm () {
let user = {
ID: this.id,
username: this.username,
password: this.password,
filesystem: this.filesystem,
admin: this.admin,
allowCommands: this.allowCommands,
allowNew: this.allowNew,
allowEdit: this.allowEdit,
allowPublish: this.allowPublish,
permissions: this.permissions,
css: this.css,
locale: this.locale,
commands: this.commands.split(' '),
rules: []
}
let rules = this.rules.split('\n')
for (let rawRule of rules) {
let rule = {
allow: true,
path: '',
regex: false,
regexp: {
raw: ''
}
}
rawRule = rawRule.split(' ')
// Skip a malformed rule
if (rawRule.length < 2) {
continue
}
// Skip a malformed rule
if (rawRule[0] !== 'allow' && rawRule[0] !== 'disallow') {
continue
}
rule.allow = (rawRule[0] === 'allow')
rawRule.shift()
if (rawRule[0] === 'regex') {
rule.regex = true
rawRule.shift()
rule.regexp.raw = rawRule.join(' ')
} else {
rule.path = rawRule.join(' ')
}
user.rules.push(rule)
}
return user
}
}
}
</script>

View File

@@ -1,51 +0,0 @@
<template>
<div class="dashboard">
<ul id="nav">
<li>
<router-link to="/settings/global">
<i class="material-icons">keyboard_arrow_left</i> {{ $t('settings.globalSettings') }}
</router-link>
</li>
<li></li>
</ul>
<h1>{{ $t('settings.users') }} <router-link to="/users/new"><button>{{ $t('buttons.new') }}</button></router-link></h1>
<table>
<tr>
<th>{{ $t('settings.username') }}</th>
<th>{{ $t('settings.admin') }}</th>
<th>{{ $t('settings.scope') }}</th>
<th></th>
</tr>
<tr v-for="user in users">
<td>{{ user.username }}</td>
<td><i v-if="user.admin" class="material-icons">done</i><i v-else class="material-icons">close</i></td>
<td>{{ user.filesystem }}</td>
<td><router-link :to="'/users/' + user.ID"><i class="material-icons">mode_edit</i></router-link></td>
</tr>
</table>
</div>
</template>
<script>
import * as api from '@/utils/api'
export default {
name: 'users',
data: function () {
return {
users: []
}
},
created () {
api.getUsers().then(users => {
this.users = users
}).catch(error => {
this.$store.commit('showError', error)
})
}
}
</script>

View File

@@ -1,13 +1,13 @@
<template>
<div>
<h2 class="message">
<i class="material-icons">error</i>
<span>{{ $t('errors.forbidden') }}</span>
</h2>
</div>
</template>
<script>
export default {name: 'forbidden'}
</script>
<template>
<div>
<h2 class="message">
<i class="material-icons">error</i>
<span>{{ $t('errors.forbidden') }}</span>
</h2>
</div>
</template>
<script>
export default {name: 'forbidden'}
</script>

View File

@@ -1,13 +1,13 @@
<template>
<div>
<h2 class="message">
<i class="material-icons">gps_off</i>
<span>{{ $t('errors.notFound') }}</span>
</h2>
</div>
</template>
<script>
export default {name: 'not-found'}
</script>
<template>
<div>
<h2 class="message">
<i class="material-icons">gps_off</i>
<span>{{ $t('errors.notFound') }}</span>
</h2>
</div>
</template>
<script>
export default {name: 'not-found'}
</script>

View File

@@ -1,13 +1,13 @@
<template>
<div>
<h2 class="message">
<i class="material-icons">error_outline</i>
<span>{{ $t('errors.internal') }}</span>
</h2>
</div>
</template>
<script>
export default {name: 'internal-error'}
</script>
<template>
<div>
<h2 class="message">
<i class="material-icons">error_outline</i>
<span>{{ $t('errors.internal') }}</span>
</h2>
</div>
</template>
<script>
export default {name: 'internal-error'}
</script>

View File

@@ -0,0 +1,187 @@
<template>
<div class="dashboard">
<form class="card" v-if="staticGen.length" @submit.prevent="saveStaticGen">
<div class="card-title">
<h2>{{ capitalize($store.state.staticGen) }}</h2>
</div>
<div class="card-content">
<p v-for="field in staticGen" :key="field.variable">
<label v-if="field.type !== 'checkbox'">{{ field.name }}</label>
<input v-if="field.type === 'text'" type="text" v-model.trim="field.value">
<input v-else-if="field.type === 'checkbox'" type="checkbox" v-model.trim="field.value">
<template v-if="field.type === 'checkbox'">{{ capitalize(field.name, 'caps') }}</template>
</p>
</div>
<div class="card-action">
<input class="flat" type="submit" :value="$t('buttons.update')">
</div>
</form>
<form class="card" @submit.prevent="saveCSS">
<div class="card-title">
<h2>{{ $t('settings.customStylesheet') }}</h2>
</div>
<div class="card-content">
<textarea v-model="css"></textarea>
</div>
<div class="card-action">
<input class="flat" type="submit" :value="$t('buttons.update')">
</div>
</form>
<form class="card" @submit.prevent="saveCommands">
<div class="card-title">
<h2>{{ $t('settings.commands') }}</h2>
</div>
<div class="card-content">
<p class="small">{{ $t('settings.commandsHelp') }}</p>
<div v-for="command in commands" :key="command.name" class="collapsible">
<input :id="command.name" type="checkbox">
<label :for="command.name">
<p>{{ capitalize(command.name) }}</p>
<i class="material-icons">arrow_drop_down</i>
</label>
<div class="collapse">
<textarea v-model.trim="command.value"></textarea>
</div>
</div>
</div>
<div class="card-action">
<input class="flat" type="submit" :value="$t('buttons.update')">
</div>
</form>
</div>
</template>
<script>
import { mapState } from 'vuex'
import { getSettings, updateSettings } from '@/utils/api'
export default {
name: 'settings',
data: function () {
return {
commands: [],
staticGen: [],
css: ''
}
},
computed: {
...mapState([ 'user' ])
},
created () {
getSettings()
.then(settings => {
if (this.$store.state.staticGen.length > 0) {
this.parseStaticGen(settings.staticGen)
}
for (let key in settings.commands) {
this.commands.push({
name: key,
value: settings.commands[key].join('\n')
})
}
this.css = settings.css
})
.catch(this.$showError)
},
methods: {
capitalize (name, where = '_') {
if (where === 'caps') where = /(?=[A-Z])/
let splitted = name.split(where)
name = ''
for (let i = 0; i < splitted.length; i++) {
name += splitted[i].charAt(0).toUpperCase() + splitted[i].slice(1) + ' '
}
return name.slice(0, -1)
},
saveCommands (event) {
let commands = {}
for (let command of this.commands) {
let value = command.value.split('\n')
if (value.length === 1 && value[0] === '') {
value = []
}
commands[command.name] = value
}
updateSettings(commands, 'commands')
.then(() => { this.$showSuccess(this.$t('settings.commandsUpdated')) })
.catch(this.$showError)
},
saveCSS (event) {
updateSettings(this.css, 'css')
.then(() => {
this.$showSuccess(this.$t('settings.settingsUpdated'))
this.$store.commit('setCSS', this.css)
this.$emit('css')
})
.catch(this.$showError)
},
saveStaticGen (event) {
let staticGen = {}
for (let field of this.staticGen) {
staticGen[field.variable] = field.value
if (field.original === 'array') {
let val = field.value.split(' ')
if (val[0] === '') {
val.shift()
}
staticGen[field.variable] = val
}
}
updateSettings(staticGen, 'staticGen')
.then(() => { this.$showSuccess(this.$t('settings.settingsUpdated')) })
.catch(this.$showError)
},
parseStaticGen (staticgen) {
for (let option of staticgen) {
let value = option.value
let field = {
name: option.name,
variable: option.variable,
type: 'text',
original: 'text',
value: value
}
if (Array.isArray(value)) {
field.original = 'array'
field.value = value.join(' ')
this.staticGen.push(field)
continue
}
switch (typeof value) {
case 'boolean':
field.type = 'checkbox'
field.original = 'boolean'
break
}
this.staticGen.push(field)
}
}
}
}
</script>

View File

@@ -0,0 +1,109 @@
<template>
<div class="dashboard">
<form class="card" @submit="updateSettings">
<div class="card-title">
<h2>{{ $t('settings.profileSettings') }}</h2>
</div>
<div class="card-content">
<h3>{{ $t('settings.language') }}</h3>
<p><languages id="locale" :selected.sync="locale"></languages></p>
<h3>{{ $t('settings.customStylesheet') }}</h3>
<textarea v-model="css" name="css"></textarea>
</div>
<div class="card-action">
<input class="flat" type="submit" :value="$t('buttons.update')">
</div>
</form>
<form class="card" v-if="!user.lockPassword" @submit="updatePassword">
<div class="card-title">
<h2>{{ $t('settings.changePassword') }}</h2>
</div>
<div class="card-content">
<p><input :class="passwordClass" type="password" :placeholder="$t('settings.newPassword')" v-model="password" name="password"></p>
<p><input :class="passwordClass" type="password" :placeholder="$t('settings.newPasswordConfirm')" v-model="passwordConf" name="password"></p>
</div>
<div class="card-action">
<input class="flat" type="submit" :value="$t('buttons.update')">
</div>
</form>
</div>
</template>
<script>
import { mapState } from 'vuex'
import { updateUser } from '@/utils/api'
import Languages from '@/components/Languages'
export default {
name: 'settings',
components: {
Languages
},
data: function () {
return {
password: '',
passwordConf: '',
css: '',
locale: ''
}
},
computed: {
...mapState([ 'user' ]),
passwordClass () {
if (this.password === '' && this.passwordConf === '') {
return ''
}
if (this.password === this.passwordConf) {
return 'green'
}
return 'red'
}
},
created () {
this.css = this.user.css
this.locale = this.user.locale
},
methods: {
updatePassword (event) {
event.preventDefault()
if (this.password !== this.passwordConf) {
return
}
let user = {
ID: this.$store.state.user.ID,
password: this.password
}
updateUser(user, 'password').then(location => {
this.$showSuccess(this.$t('settings.passwordUpdated'))
}).catch(e => {
this.$showError(e)
})
},
updateSettings (event) {
event.preventDefault()
let user = {...this.$store.state.user}
user.css = this.css
user.locale = this.locale
updateUser(user, 'partial').then(location => {
this.$store.commit('setUser', user)
this.$emit('css')
this.$showSuccess(this.$t('settings.settingsUpdated'))
}).catch(e => {
this.$showError(e)
})
}
}
}
</script>

Some files were not shown because too many files have changed in this diff Show More