Compare commits

...

144 Commits

Author SHA1 Message Date
Henrique Dias
06200775d5 Version 1.4.2
Former-commit-id: 6dab389efe4eed59f8f39d76e60130ccc74a13ea [formerly 8e106da6d1e25e04a26654435bb180793cc87c3d] [formerly cf02028f5fb706a296a8320fa1d54f222d02f78f [formerly 02f6bb81ccf9c39ea7b1174aa79bd9d2881e0245]]
Former-commit-id: b903fc7a2bf4fdad3a8c89a67b34d99fb3273c35 [formerly 6ea119c7468502d2244108600f0389eb05b502a3]
Former-commit-id: 96de8cafcd6c1ce94f05854a9fb2e9aa3d99eafb
2018-01-04 14:05:19 +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
Henrique Dias
4191a6f9e0 Fix login issues regarding basicauth+noauth #214 #204
Former-commit-id: 5c150c622724e7d2c9a9630f60325a5a246b7526 [formerly 6ff5856dc0ab40dbb9a9759c0c23de74a23d2531] [formerly b41c082f0631a33648bc50e81f38d7dbba612d8c [formerly c95b1aaace]]
Former-commit-id: ead207a0e5405ccc0641b3705875b8846846060d [formerly 7c1221485698d524ef679d09166cff7549f7b00a]
Former-commit-id: b01bdc787983c2c48394ef9e3eabed9137c1f31c
2017-08-22 15:25:54 +01:00
Henrique Dias
c18ca4702d Fix #213
Former-commit-id: 57732db98608e0b8fbe0e18f6d77d69b7cdc8807 [formerly 647558cec7e30388a3dc80c002bf2787f27edbf7] [formerly 03295cda86a4ce43a734c4617d7d4429ffeb7570 [formerly 1aa68b0dd0]]
Former-commit-id: 52bc1fdecc89456f7dc32f5ce64a40727070d06c [formerly 8a1a04c67bba6988b448f9a4c612ba62aeaf7e62]
Former-commit-id: 9b4565efca09847c31f6f856eae1aa488461046d
2017-08-21 07:33:16 +01:00
Henrique Dias
73b1094602 build assets with french transl
Former-commit-id: c3153457b2d0b1011820bf8903bf70026625ac1b [formerly 99d342f928dd31b770a37de1e9f3b743b972a13b] [formerly 2a8ddc01230f98a71b1a929856b2967472e11188 [formerly d53b5ef445]]
Former-commit-id: 9e7799705ad69806841b7366ccfb4ae7433c248b [formerly 4eb717f55bf191a36e35af721f11e2ed242668fa]
Former-commit-id: 7134a11b00ca44865a735f2e8bdb1563318f391c
2017-08-20 16:52:26 +01:00
Corentin Potron
5d026ac15d Add french language (#212)
* add french language

* add french language file fr.yaml


Former-commit-id: 294d7ec69dfe6c81895256b503a44f14bd4ba879 [formerly 0680c3461994cd62a6e1ba5886668490a37165dd] [formerly ca03271d879cc8e9520d8cf5954539e6470bf283 [formerly 791607932f]]
Former-commit-id: 8f12483066aa0519054195b69a30a91659f6a8d2 [formerly d9f47b9e50c125e5c81f4932c28570611a879162]
Former-commit-id: c8f593c505937d017eb2e0ecf3af3a958d6de5f9
2017-08-20 16:49:19 +01:00
Henrique Dias
21156bf24f Merge branch 'spl'
Former-commit-id: 22d8f06e8a8f25af502d28a913fe83fa8aaa3970 [formerly f93cb05d08b4ca935aad07262f3898bf2fce87ef] [formerly 3562ac890aa6a5ab7f5514b5f9c5617bcf16c844 [formerly bbffd10e0b]]
Former-commit-id: 301bbb736a3003ef4b9ac0398e6c3b311a9281bc [formerly 9ed7c705279356410e9861c2100b0d0911acc54f]
Former-commit-id: df715dab654dc2116dfba8bbea3365d940ceecb8
2017-08-20 10:27:09 +01:00
Henrique Dias
edaf6d27aa solve conflict
Former-commit-id: dff18181a89dac3406aa57f5b599b3cc8d25dcb8 [formerly 3aaa6e4e972c5615fa802475379ad5f9d052cc0a] [formerly c0fe3b069670959a1dcca83ce22f4b1656ff3352 [formerly 76bb197dac]]
Former-commit-id: 573869a30349cb42e65dff6945ae08cde2715b36 [formerly f1d63a9fcd70df30cecfd1ffb85f80a06508675f]
Former-commit-id: 21b95aba2cadceee0a75db85671706429adee228
2017-08-20 10:26:49 +01:00
Henrique Dias
816cfb2a6e Fix one bug
Former-commit-id: 3886c36ab7a1a078f0f740caa7ca986490f50f8a [formerly f71de42494da68fac79b6618e03aa44ea8cf7d5b] [formerly af6208163a5bf54fa06171990cc8adb8f90f4834 [formerly 70751c48d2]]
Former-commit-id: 57015ba46af265788d5e1189e7f981aa3e9c2c2d [formerly b0a2660aaed889c9f36e3ba4e10f4a703e8763a1]
Former-commit-id: 33279da11c9ce00ebb77feef419d6f6d82d9572a
2017-08-20 10:23:04 +01:00
Henrique Dias
269ec9ea4b Update docs
Former-commit-id: 70685b598a006623f25a6774fe6084f57cfbe1bf [formerly ccbb5f53ed5d72472604c68e47aa07761c0206f7] [formerly 0b34f9e46762e5b6d940f66f6f1d3a1855d56915 [formerly 154bfb19d5]]
Former-commit-id: 9b20eb1064d9ca96d5100c3ea77b52e8675a393d [formerly 3ae727e5bc065815b79a9df4ccbe4b39b0a6a4e4]
Former-commit-id: 7e5bd07d8310d5df84a0490c7cb75c94e5fa3026
2017-08-20 10:19:04 +01:00
Henrique Dias
9186c1f36c remove old test files
Former-commit-id: f84bd9945948105adbad9e1adec4cc072fc3a083 [formerly f6f884ae5c22aa3f51ffffe7828a892a09b8c285] [formerly edac9defd87c75efb7fbb86a5fd6aaa2084bbd10 [formerly 0117cff0e6]]
Former-commit-id: e5093a9010603aff9432dcc332e7de3507b8ec38 [formerly 95474b25e1c766a94c32ec69136f261bd0d908b1]
Former-commit-id: 0029467a0059d12c2dbf19ac0339a4bc6b1185b6
2017-08-20 10:11:31 +01:00
Henrique Dias
d5cefa20b3 build assets
Former-commit-id: 695d0f7584c1ee104eee182bd9fc909128d76d64 [formerly 97fcfda710cb87cf9c30e3c7bdda343b4676bc60] [formerly 732c84c6355d9585957fe54b11b882bd42168b12 [formerly 0ec21987ed]]
Former-commit-id: b0741bfb46793a3907b260c55ce524ca68389ec3 [formerly b80ea86d170af278d273a7e0225e4b43cd7d874a]
Former-commit-id: ab876ba5754fda34ba3933d2b8659bb75cf08324
2017-08-20 10:11:12 +01:00
Henrique Dias
1be7d7d256 Close #207
Former-commit-id: e7a0fe724406f2284d872e1b49451e8c85924bf4 [formerly 3e0f7f027d117dcb825b326e880ffa05cd5a7ce6] [formerly 70ddacbae6288e5aac8cc9d5cbb250814fe07f7c [formerly 4bf34124a3]]
Former-commit-id: d9118bd6c1689df00c53219d5910892ec68892de [formerly 1e191afcd6f7d73069b6fc79e9b7a5cdd14d2ecb]
Former-commit-id: d908f459e005c204ad8eb0ef58f2e782f9b4c942
2017-08-20 10:09:10 +01:00
Henrique Dias
1b0f67c0f6 Some bug fixes
Former-commit-id: 54fc2a2869dd625e55881818e0022f3c4ae45bd1 [formerly ddba8f0dc58999a6f483fe61fda9391da251d49b] [formerly f76423c629f671538e6c008365c8d6dc1a5460d7 [formerly 82b161cfb2]]
Former-commit-id: 34601615e2beb773bf266cdb503e3c9fd8ead09b [formerly 0f39bbd2d66c789219785b4a726297a7c00a7f1e]
Former-commit-id: 1c2e33c56af3f57f8e8751b4c43b05967f87c587
2017-08-20 09:55:45 +01:00
Henrique Dias
67dbf88eb6 Working Caddy
Former-commit-id: c463c6e5708b2cd10e7de37285cddf0c4898b59b [formerly 615fbb71576801762e831e00489c30bff189c7d2] [formerly cdd9f708fac1163bb79e619368ddd05e4b581be3 [formerly e4d345b7e5]]
Former-commit-id: cfb19f435c5d08cbb38e50ba970fc2d9474ffb0c [formerly 6e1aac15e1da1c06e41d87dafa262332b018d701]
Former-commit-id: 78cebd321e5a840388e6d4eca09e2357469ec546
2017-08-20 09:31:24 +01:00
Henrique Dias
98bea91edf Add Comments
Former-commit-id: ea1761b1e1bd9ff6eb80e06ff378d8263de86064 [formerly a4cefe6cf2f1da416ad34175bcea96ac5262d766] [formerly cc4c6afca638a66e223e64a6bffde563a48b1990 [formerly c6e6b08305]]
Former-commit-id: 56c1574b23fefd33b41d848f47201a24e75d9e6b [formerly b326f699ef7dde08a4e81b4a3a7db22902270634]
Former-commit-id: 078a180adea8d3f3f02177caf78cbeea22145d4c
2017-08-20 09:27:03 +01:00
Henrique Dias
7747fa8ec3 Fix some stuff
Former-commit-id: a820779e24a9e41ae3ef07ad6c49b3b441192b82 [formerly 04d8b699793d2f662cfd7ebbbf1118239deb0ec7] [formerly 94e538cb9690c186fc113e27c008f894d719d453 [formerly 921cc2f930]]
Former-commit-id: 9ddc40cc471fb3ea3fd0fcf811907f102fa13bc2 [formerly a5ea3e36f6b362f8910874d5d1ed087187e05dcc]
Former-commit-id: c4c4cc4c1505145d9b1c1058d94e6dc80a3cd5de
2017-08-20 09:23:02 +01:00
Henrique Dias
d0cf6c08e8 FS as an interface, close #205
Former-commit-id: 9bfcbeaf9c407044fb8eb3142f2eca65f42623d1 [formerly 2fb1a0292e825b5b86e506c70b548bc823050f7d] [formerly 8f70cdc0db6d4328d61769685bb39806999f475b [formerly 20818dca93]]
Former-commit-id: ec8e8d96356d56863d8a330451dc78dbf838a7ef [formerly 216fffad3e0d2cdc4632e8de9299666a74e44375]
Former-commit-id: 230c7bc974a86225064211178250ec072c0a525a
2017-08-20 09:21:36 +01:00
Henrique Dias
600723c224 More updates :)
Former-commit-id: cc9953eaa75e9c68abc19b40cf1c4391f1c5fe24 [formerly b553106eb6a4f00474c7c79f74c02ae475e9601c] [formerly 39ca855c5e0788008f5c671164fb6e404a31aaa0 [formerly 76de8e5940]]
Former-commit-id: 0c1a5f9cc633e40506f54d9da1420ae7c183bc88 [formerly 061569610df0c6e41830bf27516ef567c3a83c55]
Former-commit-id: b400af585254eba659078ea1f5f48609930c4ea6
2017-08-20 08:49:09 +01:00
Henrique Dias
44ab20964c Almost working!
Former-commit-id: b996f4f14f3ffd92fae77d86e92d077b35ea080c [formerly e4b74308ab158ad24bd6b3dc1ce615265f972e6c] [formerly 1ea38eac2569ba58e864f1edceb56daabff5e53d [formerly 5b619337df]]
Former-commit-id: 9117f9eeff1bbc259164b20f0561790b3c393319 [formerly c3c7b1c100c54a5ec0af528806e28b31c67da0ca]
Former-commit-id: 0d95a7f55f6f3ab9f89e1c5b34db927e5763c98d
2017-08-20 08:42:38 +01:00
Henrique Dias
764289e52f DB Updates :)
Former-commit-id: e9795cfca39aab57f4a4b604c65633958ff22e46 [formerly 7dd93e46eb6915a387dc64500a3fe7f6f955643b] [formerly ffa277f1605e46bb8c914464b1223fe029d579d8 [formerly a04ff87bf9]]
Former-commit-id: ffa472fd3b1534f64a1c343864564bbc0290714b [formerly 7074f824d7d7dd3cd74b884c9f3e96834f662394]
Former-commit-id: 4b1e0324de6065fd4daec25d463d3756588b92d8
2017-08-19 12:35:44 +01:00
Henrique Dias
741e5c84ea Fix #209
Former-commit-id: e11caa24e68b811c028762c3c89a1b55b241822d [formerly 2f77506d8eeff4578c328092eb17c98e02a12c09] [formerly 2bf12e36c1d29466a9ba97fc5820691f588f7e7f [formerly e7b8da0bd6]]
Former-commit-id: 22ba33d51961658a991a3fe5098c2923b4ca7633 [formerly 383193414db6aace9d46245b95630872f890bd7f]
Former-commit-id: 13ea3c87d98c9f00654251d5d954added2dbc0ea
2017-08-18 09:03:52 +01:00
Henrique Dias
4b602be5e3 updates
Former-commit-id: 54b88552d11f2151a165dba9debb4657dfa56cf8 [formerly 0ce53651a8e9660f9d5f977295f553b5b1d1e93a] [formerly 7ebca3a8896222091c95af86a9cf1d12550b8b76 [formerly 174330929a]]
Former-commit-id: 993d0cdb239f9969587d13a11ee8469fa8b91287 [formerly c22c911f944dd8d6597ab95589842d3c68d34869]
Former-commit-id: 44ed259fe50a085e8bcace3f1f14caafec97ce66
2017-08-18 09:00:32 +01:00
Henrique Dias
e4144ad2b2 Progress bar. Close #199
Former-commit-id: cf8ec044a2531b295b89da915cee439eba6ccd0b [formerly 6d77a48968796dc6db51602486c07d6f2ecc00e6] [formerly 4aa44181ea637eeaba8e7756a76bbf5cd47b6928 [formerly ec190d28a8]]
Former-commit-id: 5e8e25fee1ae1119d5aaf5bf25e83f258ad13d2a [formerly 574db23a689aaf5bbe533d2e322ae98fc0b6d1de]
Former-commit-id: 5b5dfebfb692b62188c216f0cb22f8cdefdd82ab
2017-08-15 11:08:58 +01:00
Henrique Dias
4071a58107 close #203
Former-commit-id: e2d0b723963d3a82ac0f9042280885e800b1132a [formerly e1ae3f4da43f0481a57c39f11735c36b33fda857] [formerly 02ab5f5fb5f4b812cf413ebc923b853d4f0b4afb [formerly 83bc555094]]
Former-commit-id: bee2ec30c9aa9619a69eaa6320822f8525a41535 [formerly 1af59077494b5e8674af547d0361f50a2ecf8f26]
Former-commit-id: 573870f4a6bffcee3e48fbc0b8349402eaeba407
2017-08-14 18:35:25 +01:00
Henrique Dias
04b6ca6015 Fix #202
Former-commit-id: 4bfdc41566b255452ba1179f98523f51200cafd2 [formerly 194853322977723818d2fcfe860d0fa54117f9dc] [formerly 512370c90e08f58f729ab1a9d03e00ab72b1da69 [formerly 0a11c52a89]]
Former-commit-id: 518476f3f715724ca9b4cfd65aa234006d3a3677 [formerly 7717d5bff8169c8ae5e31d3a70de35ffcd59cd72]
Former-commit-id: 0414a3e51602ea9d51257036c620cfe6853103b3
2017-08-13 10:12:13 +01:00
135 changed files with 6572 additions and 5403 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,24 +1,24 @@
### Instructions (remove before submitting): ### 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. 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. 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. 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. 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? ### 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? ### 2. What is your entire Caddyfile?
```text ```text
(Put Caddyfile here) (Put Caddyfile here)
``` ```
### 3. What are you trying to do? ### 3. What are you trying to do?
### 4. What did you expect to see? ### 4. What did you expect to see?
### 5. What did you see instead (give full error messages and/or log)? ### 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? ### 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* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
.idea
.vscode
package-lock.json
yarn.lock

View File

@@ -1,29 +1,37 @@
build: build:
main: cmd/filemanager/main.go main: cmd/filemanager/main.go
binary: filemanager binary: filemanager
goos: goos:
- darwin - darwin
- linux - linux
- windows - windows
- freebsd - freebsd
- netbsd - netbsd
- openbsd - openbsd
goarch: - dragonfly
- amd64 - solaris
- 386 goarch:
- arm - amd64
- arm64 - 386
ignore: - arm
- goos: openbsd - arm64
goarch: arm - mips
goarm: 6 - mips64
- goos: freebsd - mipsle
goarch: arm - mips64le
goarm: 6 ignore:
- goos: openbsd
archive: goarch: arm
name_template: "{{.Os}}-{{.Arch}}-{{ .ProjectName }}" goarm: 6
format: tar.gz - goos: freebsd
format_overrides: goarch: arm
- goos: windows goarm: 6
format: zip - 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 language: go
go: 1.8.3 go: 1.x
env: env:
- "PATH=/home/travis/gopath/bin:$PATH" - "PATH=/home/travis/gopath/bin:$PATH"
@@ -15,7 +15,7 @@ install:
- go get github.com/tsenart/deadcode - go get github.com/tsenart/deadcode
script: 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 - go test ./... -timeout 30s
after_success: after_success:

View File

@@ -1,46 +1,46 @@
# Contributor Covenant Code of Conduct # Contributor Covenant Code of Conduct
## Our Pledge ## 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. 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 ## Our Standards
Examples of behavior that contributes to creating a positive environment include: Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language * Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences * Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism * Gracefully accepting constructive criticism
* Focusing on what is best for the community * Focusing on what is best for the community
* Showing empathy towards other community members * Showing empathy towards other community members
Examples of unacceptable behavior by participants include: Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances * The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks * Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment * Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission * 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 * Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities ## 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 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. 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 ## 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. 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 ## 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. 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. 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 ## 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] 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 [homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/ [version]: http://contributor-covenant.org/version/1/4/

View File

@@ -1,14 +1,14 @@
# Contributing # 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: 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` 1. `go get github.com/hacdias/filemanager/cmd/filemanager`
2. `cd $GOPATH/src/github.com/hacdias/filemanager` 2. `cd $GOPATH/src/github.com/hacdias/filemanager`
3. `npm install` 3. `npm install`
4. `npm run dev` - regenerates the static assets automatically 4. `npm run dev` - regenerates the static assets automatically
5. `go install github.com/hacdias/filemanager/cmd/filemanager` 5. `go install github.com/hacdias/filemanager/cmd/filemanager`
6. Execute `$GOPATH/bin/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. 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`. 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, "port": 80,
"address": "", "address": "",
"database": "/etc/database.db", "database": "/database.db",
"scope": "/srv", "scope": "/srv",
"allowCommands": true, "allowCommands": true,
"allowEdit": true, "allowEdit": true,

View File

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

View File

@@ -1,201 +1,201 @@
Apache License Apache License
Version 2.0, January 2004 Version 2.0, January 2004
http://www.apache.org/licenses/ http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions. 1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, "License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document. and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by "Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License. the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all "Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition, control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the "control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity. outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity "You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License. exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, "Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation including but not limited to software source code, documentation
source, and configuration files. source, and configuration files.
"Object" form shall mean any form resulting from mechanical "Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation, not limited to compiled object code, generated documentation,
and conversions to other media types. and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or "Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work copyright notice that is included in or attached to the work
(an example is provided in the Appendix below). (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object "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 form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of, separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof. the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including "Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner 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 or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted" the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution." designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity "Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work. subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of 2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual, this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of, copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form. Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of 3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual, this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made, (except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work, use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s) Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate granted to You under this License for that Work shall terminate
as of the date such litigation is filed. as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the 4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You modifications, and in Source or Object form, provided that You
meet the following conditions: meet the following conditions:
(a) You must give any other recipients of the Work or (a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices (b) You must cause any modified files to carry prominent notices
stating that You changed the files; and stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works (c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work, attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of excluding those notices that do not pertain to any part of
the Derivative Works; and the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its (d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or, documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed that such additional attribution notices cannot be construed
as modifying the License. as modifying the License.
You may add Your own copyright statement to Your modifications and You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use, for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License. the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, 5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions. this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions. with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade 6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor, names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file. origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or 7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS, Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License. risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, 8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise, whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the 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 (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages. has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing 9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer, the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity, and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify, of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability. of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work. APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}" boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright {yyyy} {name of copyright owner} Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. 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) ![Preview](https://user-images.githubusercontent.com/5447088/28537288-39be4288-70a2-11e7-8ce9-0813d59f46b7.gif)
# filemanager # filemanager
[![Build](https://img.shields.io/travis/hacdias/filemanager.svg?style=flat-square)](https://travis-ci.org/hacdias/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) [![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) [![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.
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
# Table of contents
+ [Getting started](#getting-started)
+ [Features](#features) + [Getting started](#getting-started)
- [Users](#users) + [Features](#features)
- [Search](#search) - [Users](#users)
+ [Contributing](#contributing) - [Search](#search)
+ [Donate](#donate) + [Contributing](#contributing)
+ [Donate](#donate)
# Getting started
# Getting started
You can find the Getting Started guide on the [documentation](https://henriquedias.com/filemanager/quick-start/).
You can find the Getting Started guide on the [documentation](https://henriquedias.com/filemanager/quick-start/).
# Features
# Features
Easy login system.
Easy login system.
![Login Page](https://user-images.githubusercontent.com/5447088/28432382-975493dc-6d7f-11e7-9190-23f8037159dc.jpg)
![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*.
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)
![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.
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)
![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.
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)
![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.
We also allow the users to search in the directories and execute commands if allowed.
## Users
## 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!).
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)
![Users](https://user-images.githubusercontent.com/5447088/28432386-977f388a-6d7f-11e7-9006-87d16f05f1f8.jpg)
## Search
## Search
FileManager allows you to search through your files and it has some options. By default, your search will be something like this:
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 ```
``` 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:
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" ```
``` "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.
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:
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 ```
``` this are keywords case:insensitive
```
# Contributing
# Contributing
The contributing guidelines can be found [here](https://github.com/hacdias/filemanager/blob/master/CONTRIBUTING.md).
The contributing guidelines can be found [here](https://github.com/hacdias/filemanager/blob/master/CONTRIBUTING.md).
# Donate
# Donate
Enjoying this project? You can [donate to its creator](https://henriquedias.com/donate/). He will appreciate.
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')() require('./check-versions')()
process.env.NODE_ENV = 'production' process.env.NODE_ENV = 'production'
var ora = require('ora') var ora = require('ora')
var rm = require('rimraf') var rm = require('rimraf')
var path = require('path') var path = require('path')
var chalk = require('chalk') var chalk = require('chalk')
var webpack = require('webpack') var webpack = require('webpack')
var config = require('./config') var config = require('./config')
var webpackConfig = require('./webpack.prod.conf') var webpackConfig = require('./webpack.prod.conf')
var spinner = ora('building for production...') var spinner = ora('building for production...')
spinner.start() spinner.start()
rm(path.join(config.assetsRoot, config.assetsSubDirectory), err => { rm(path.join(config.assetsRoot, config.assetsSubDirectory), err => {
if (err) throw err if (err) throw err
webpack(webpackConfig, function (err, stats) { webpack(webpackConfig, function (err, stats) {
spinner.stop() spinner.stop()
if (err) throw err if (err) throw err
process.stdout.write(stats.toString({ process.stdout.write(stats.toString({
colors: true, colors: true,
modules: false, modules: false,
children: false, children: false,
chunks: false, chunks: false,
chunkModules: false chunkModules: false
}) + '\n\n') }) + '\n\n')
console.log(chalk.cyan(' Build complete.\n')) console.log(chalk.cyan(' Build complete.\n'))
}) })
}) })

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,6 +6,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<meta name="base" content="{{ .BaseURL }}"> <meta name="base" content="{{ .BaseURL }}">
<meta name="staticgen" content="{{ .StaticGen }}"> <meta name="staticgen" content="{{ .StaticGen }}">
<meta name="noauth" content="{{ .NoAuth }}">
<meta name="version" content="{{ .Version }}">
<meta name="recaptcha" content="{{ .ReCaptchaKey }}">
<title>File Manager</title> <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="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"> <link rel="icon" type="image/png" sizes="16x16" href="{{ .BaseURL }}/static/img/icons/favicon-16x16.png">
@@ -22,7 +25,14 @@
<!-- Add to home screen for Windows --> <!-- Add to home screen for Windows -->
<meta name="msapplication-TileImage" content="{{ .BaseURL }}/static/img/icons/msapplication-icon-144x144.png"> <meta name="msapplication-TileImage" content="{{ .BaseURL }}/static/img/icons/msapplication-icon-144x144.png">
<meta name="msapplication-TileColor" content="#2979ff"> <meta name="msapplication-TileColor" content="#2979ff">
<% for (var chunk of webpack.compilation.chunks) {
<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) { for (var file of chunk.files) {
if (file.match(/\.(js|css)$/)) { %> if (file.match(/\.(js|css)$/)) { %>
<link rel="preload" href="{{ .BaseURL }}/<%= file %>" as="<%= file.match(/\.css$/)?'style':'script' %>"><% }}} %> <link rel="preload" href="{{ .BaseURL }}/<%= file %>" as="<%= file.match(/\.css$/)?'style':'script' %>"><% }}} %>

View File

@@ -1,18 +1,74 @@
<template> <template>
<router-view></router-view> <router-view :dependencies="loaded" @update:css="updateCSS" @clean:css="cleanCSS"></router-view>
</template> </template>
<script> <script>
import { mapState } from 'vuex'
export default { export default {
name: 'app', name: 'app',
mounted: function () { computed: mapState(['recaptcha']),
// Remove loading animation. data () {
let loading = document.getElementById('loading') return {
loading.classList.add('done') loaded: false
}
},
mounted () {
if (this.recaptcha.length === 0) {
this.unload()
return
}
setTimeout(function () { let check = () => {
loading.parentNode.removeChild(loading) if (typeof window.grecaptcha === 'undefined') {
}, 200) 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> </script>

View File

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

Before

Width:  |  Height:  |  Size: 235 B

After

Width:  |  Height:  |  Size: 239 B

View File

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

View File

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

View File

@@ -46,7 +46,7 @@
</button> </button>
</div> </div>
<div v-if="!$store.state.user.noAuth"> <div v-if="!$store.state.noAuth">
<router-link class="action" to="/settings" :aria-label="$t('sidebar.settings')" :title="$t('sidebar.settings')"> <router-link class="action" to="/settings" :aria-label="$t('sidebar.settings')" :title="$t('sidebar.settings')">
<i class="material-icons">settings_applications</i> <i class="material-icons">settings_applications</i>
<span>{{ $t('sidebar.settings') }}</span> <span>{{ $t('sidebar.settings') }}</span>
@@ -59,7 +59,7 @@
</div> </div>
<p class="credits"> <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> <span><a @click="help">{{ $t('sidebar.help') }}</a></span>
</p> </p>
</nav> </nav>
@@ -72,7 +72,7 @@ import auth from '@/utils/auth'
export default { export default {
name: 'sidebar', name: 'sidebar',
computed: { computed: {
...mapState(['user', 'staticGen']), ...mapState(['user', 'staticGen', 'version']),
active () { active () {
return this.$store.state.show === 'sidebar' return this.$store.state.show === 'sidebar'
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,32 +1,35 @@
<template> <template>
<button @click="change" :aria-label="$t('buttons.switchView')" :title="$t('buttons.switchView')" class="action" id="switch-view-button"> <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> <span>{{ $t('buttons.switchView') }}</span>
</button> </button>
</template> </template>
<script> <script>
import { mapState, mapMutations } from 'vuex'
import { updateUser } from '@/utils/api'
export default { export default {
name: 'switch-button', name: 'switch-button',
computed: {
...mapState(['user']),
icon: function () {
if (this.user.viewMode === 'mosaic') return 'view_list'
return 'view_module'
}
},
methods: { methods: {
...mapMutations(['updateUser']),
change: function (event) { change: function (event) {
// If we are on mobile we should close the dropdown. // If we are on mobile we should close the dropdown.
this.$store.commit('closeHovers') 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') { updateUser(user, 'partial').then(() => {
display = 'list' this.updateUser({ viewMode: user.viewMode })
} }).catch(this.$showError)
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'
} }
} }
} }

View File

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

View File

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

View File

@@ -7,7 +7,7 @@
<input style="display:none" type="file" id="upload-input" @change="uploadInput($event)" multiple> <input style="display:none" type="file" id="upload-input" @change="uploadInput($event)" multiple>
</div> </div>
<div v-else id="listing" <div v-else id="listing"
:class="req.display" :class="user.viewMode"
@dragenter="dragEnter" @dragenter="dragEnter"
@dragend="dragEnd"> @dragend="dragEnd">
<div> <div>
@@ -98,7 +98,7 @@ export default {
name: 'listing', name: 'listing',
components: { Item }, components: { Item },
computed: { computed: {
...mapState(['req', 'selected']), ...mapState(['req', 'selected', 'user']),
nameSorted () { nameSorted () {
return (this.req.sort === 'name') return (this.req.sort === 'name')
}, },
@@ -196,6 +196,10 @@ export default {
}) })
}, },
paste (event) { paste (event) {
if (event.target.tagName.toLowerCase() === 'input') {
return
}
event.preventDefault() event.preventDefault()
let items = [] let items = []
@@ -210,17 +214,13 @@ export default {
if (this.$store.state.clipboard.key === 'x') { if (this.$store.state.clipboard.key === 'x') {
api.move(items).then(() => { api.move(items).then(() => {
this.$store.commit('setReload', true) this.$store.commit('setReload', true)
}).catch(error => { }).catch(this.$showError)
this.$store.commit('showError', error)
})
return return
} }
api.copy(items).then(() => { api.copy(items).then(() => {
this.$store.commit('setReload', true) this.$store.commit('setReload', true)
}).catch(error => { }).catch(this.$showError)
this.$store.commit('showError', error)
})
}, },
resizeEvent () { resizeEvent () {
// Update the columns size based on the window width. // Update the columns size based on the window width.
@@ -267,7 +267,7 @@ export default {
.then(req => { .then(req => {
this.checkConflict(files, req.items, base) this.checkConflict(files, req.items, base)
}) })
.catch(error => { console.log(error) }) .catch(this.$showError)
return return
} }
@@ -318,19 +318,37 @@ export default {
handleFiles (files, base, overwrite = false) { handleFiles (files, base, overwrite = false) {
buttons.loading('upload') buttons.loading('upload')
let promises = [] let promises = []
let progress = new Array(files.length).fill(0)
for (let file of files) { let onupload = (id) => (event) => {
promises.push(api.post(this.$route.path + base + file.name, file, overwrite)) progress[id] = (event.loaded / event.total) * 100
let sum = 0
for (let i = 0; i < progress.length; i++) {
sum += progress[i]
}
this.$store.commit('setProgress', Math.ceil(sum / progress.length))
}
for (let i = 0; i < files.length; i++) {
let file = files[i]
promises.push(api.post(this.$route.path + base + file.name, file, overwrite, onupload(i)))
}
let finish = () => {
buttons.success('upload')
this.$store.commit('setProgress', 0)
} }
Promise.all(promises) Promise.all(promises)
.then(() => { .then(() => {
buttons.success('upload') finish()
this.$store.commit('setReload', true) this.$store.commit('setReload', true)
}) })
.catch(error => { .catch(error => {
buttons.done('upload') finish()
this.$store.commit('showError', error) this.$showError(error)
}) })
return false return false

View File

@@ -109,21 +109,36 @@ export default {
.then(() => { .then(() => {
this.$store.commit('setReload', true) this.$store.commit('setReload', true)
}) })
.catch(error => { .catch(this.$showError)
this.$store.commit('showError', error)
})
}, },
click: function (event) { click: function (event) {
if (this.selectedCount !== 0) event.preventDefault() if (this.selectedCount !== 0) event.preventDefault()
if (this.$store.state.selected.indexOf(this.index) === -1) { if (this.$store.state.selected.indexOf(this.index) !== -1) {
if (!event.ctrlKey && !this.$store.state.multiple) this.resetSelected()
this.addSelected(this.index)
} else {
this.removeSelected(this.index) 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) { touchstart (event) {
setTimeout(() => { setTimeout(() => {

View File

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

View File

@@ -1,19 +1,24 @@
<template> <template>
<div class="prompt"> <div class="card floating">
<h3>{{ $t('prompts.copy') }}</h3> <div class="card-title">
<p>{{ $t('prompts.copyMessage') }}</p> <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> <div class="card-action">
<button class="ok" <button class="cancel flat"
@click="copy"
:aria-label="$t('buttons.copy')"
:title="$t('buttons.copy')">{{ $t('buttons.copy') }}</button>
<button class="cancel"
@click="$store.commit('closeHovers')" @click="$store.commit('closeHovers')"
:aria-label="$t('buttons.cancel')" :aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button> :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>
</div> </div>
</template> </template>
@@ -56,7 +61,7 @@ export default {
}) })
.catch(error => { .catch(error => {
buttons.done('copy') buttons.done('copy')
this.$store.commit('showError', error) this.$showError(error)
}) })
} }
} }

View File

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

View File

@@ -1,13 +1,18 @@
<template> <template>
<div class="prompt" id="download"> <div class="card floating" id="download">
<h3>{{ $t('prompts.download') }}</h3> <div class="card-title">
<p>{{ $t('prompts.downloadMessage') }}</p> <h2>{{ $t('prompts.download') }}</h2>
</div>
<button @click="download('zip')" autofocus>zip</button> <div class="card-content">
<button @click="download('tar')" autofocus>tar</button> <p>{{ $t('prompts.downloadMessage') }}</p>
<button @click="download('targz')" autofocus>tar.gz</button>
<button @click="download('tarbz2')" autofocus>tar.bz2</button> <button class="block cancel" @click="download('zip')" autofocus>zip</button>
<button @click="download('tarxz')" autofocus>tar.xz</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> </div>
</template> </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. // so we fetch the data from the previous directory.
api.fetch(url.removeLastDir(this.$route.path)) api.fetch(url.removeLastDir(this.$route.path))
.then(this.fillOptions) .then(this.fillOptions)
.catch(this.showError) .catch(this.$showError)
}, },
methods: { methods: {
fillOptions (req) { fillOptions (req) {
@@ -96,7 +96,7 @@ export default {
api.fetch(uri) api.fetch(uri)
.then(this.fillOptions) .then(this.fillOptions)
.catch(this.showError) .catch(this.$showError)
}, },
touchstart (event) { touchstart (event) {
let url = event.currentTarget.dataset.url let url = event.currentTarget.dataset.url

View File

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

View File

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

View File

@@ -1,19 +1,23 @@
<template> <template>
<div class="prompt"> <div class="card floating">
<h3>{{ $t('prompts.move') }}</h3> <div class="card-title">
<p>{{ $t('prompts.moveMessage') }}</p> <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> <div class="card-action">
<button class="ok" <button class="flat cancel"
@click="move"
:aria-label="$t('buttons.move')"
:title="$t('buttons.move')">{{ $t('buttons.move') }}</button>
<button class="cancel"
@click="$store.commit('closeHovers')" @click="$store.commit('closeHovers')"
:aria-label="$t('buttons.cancel')" :aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button> :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>
</div> </div>
</template> </template>
@@ -56,7 +60,7 @@ export default {
}) })
.catch(error => { .catch(error => {
buttons.done('move') buttons.done('move')
this.$store.commit('showError', error) this.$showError(error)
}) })
event.preventDefault() event.preventDefault()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,51 +1,55 @@
<template> <template>
<div class="prompt" id="share"> <div class="card floating" id="share">
<h3>{{ $t('buttons.share') }}</h3> <div class="card-title">
<p></p> <h2>{{ $t('buttons.share') }}</h2>
<ul> </div>
<li v-if="!hasPermanent">
<a @click="getPermalink" :aria-label="$t('buttons.permalink')">{{ $t('buttons.permalink') }}</a>
</li>
<li v-for="link in links" :key="link.hash"> <div class="card-content">
<a :href="buildLink(link.hash)" target="_blank"> <ul>
<template v-if="link.expires">{{ humanTime(link.expireDate) }}</template> <li v-if="!hasPermanent">
<template v-else>{{ $t('permanent') }}</template> <a @click="getPermalink" :aria-label="$t('buttons.permalink')">{{ $t('buttons.permalink') }}</a>
</a> </li>
<button class="action" <li v-for="link in links" :key="link.hash">
@click="deleteLink($event, link)" <a :href="buildLink(link.hash)" target="_blank">
:aria-label="$t('buttons.delete')" <template v-if="link.expires">{{ humanTime(link.expireDate) }}</template>
:title="$t('buttons.delete')"><i class="material-icons">delete</i></button> <template v-else>{{ $t('permanent') }}</template>
</a>
<button class="action copy" <button class="action"
:data-clipboard-text="buildLink(link.hash)" @click="deleteLink($event, link)"
:aria-label="$t('buttons.copyToClipboard')" :aria-label="$t('buttons.delete')"
:title="$t('buttons.copyToClipboard')"><i class="material-icons">content_paste</i></button> :title="$t('buttons.delete')"><i class="material-icons">delete</i></button>
</li>
<li> <button class="action copy-clipboard"
<input autofocus :data-clipboard-text="buildLink(link.hash)"
type="number" :aria-label="$t('buttons.copyToClipboard')"
max="2147483647" :title="$t('buttons.copyToClipboard')"><i class="material-icons">content_paste</i></button>
min="0" </li>
@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> <li>
<button class="cancel" <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')" @click="$store.commit('closeHovers')"
:aria-label="$t('buttons.close')" :aria-label="$t('buttons.close')"
:title="$t('buttons.close')">{{ $t('buttons.close') }}</button> :title="$t('buttons.close')">{{ $t('buttons.close') }}</button>
@@ -54,7 +58,7 @@
</template> </template>
<script> <script>
import { mapState, mapMutations } from 'vuex' import { mapState } from 'vuex'
import { getShare, deleteShare, share } from '@/utils/api' import { getShare, deleteShare, share } from '@/utils/api'
import moment from 'moment' import moment from 'moment'
import Clipboard from 'clipboard' import Clipboard from 'clipboard'
@@ -101,20 +105,25 @@ export default {
}) })
.catch(error => { .catch(error => {
if (error === 404) return if (error === 404) return
this.showError(error) this.$showError(error)
}) })
}, },
mounted () { 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: { methods: {
...mapMutations([ 'showError' ]),
submit: function (event) { submit: function (event) {
if (!this.time) return if (!this.time) return
share(this.url, this.time, this.unit) share(this.url, this.time, this.unit)
.then(result => { this.links.push(result); this.sort() }) .then(result => { this.links.push(result); this.sort() })
.catch(error => { this.showError(error) }) .catch(this.$showError)
}, },
getPermalink (event) { getPermalink (event) {
share(this.url) share(this.url)
@@ -123,7 +132,7 @@ export default {
this.sort() this.sort()
this.hasPermanent = true this.hasPermanent = true
}) })
.catch(error => { this.showError(error) }) .catch(this.$showError)
}, },
deleteLink (event, link) { deleteLink (event, link) {
event.preventDefault() event.preventDefault()
@@ -132,7 +141,7 @@ export default {
if (!link.expires) this.hasPermanent = false if (!link.expires) this.hasPermanent = false
this.links = this.links.filter(item => item.hash !== link.hash) this.links = this.links.filter(item => item.hash !== link.hash)
}) })
.catch(error => { this.showError(error) }) .catch(this.$showError)
}, },
humanTime (time) { humanTime (time) {
return moment(time).fromNow() 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 { body {
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
padding-top: 4em; padding-top: 4em;
background-color: #f8f8f8; background-color: #fafafa;
user-select: none; color: #333333;
color: #212121;
} }
* { * {
@@ -66,6 +65,58 @@ button:hover {
background-color: #1E88E5; 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 { .mobile-only {
display: none !important; display: none !important;
} }
@@ -145,3 +196,19 @@ main {
#breadcrumbs span a { #breadcrumbs span a {
padding: .2em; padding: .2em;
} }
#progress {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 3px;
z-index: 9999999999;
}
#progress div {
height: 100%;
background-color: #40c4ff;
width: 0;
transition: .2s ease width;
}

View File

@@ -1,64 +1,34 @@
.dashboard { .dashboard {
max-width: 600px; 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; margin: 1em 0;
} }
.dashboard a { a {
color: inherit color: inherit
} }
.dashboard h1 button { select,
font-size: 0.5em; textarea,
float: right; input[type="text"],
} input[type="password"] {
padding: 0.5em 0;
.dashboard table { line-height: 1;
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;
display: block; display: block;
border: 0; border: 0;
border-bottom: 1px solid #dddddd; border-bottom: 1px solid #dddddd;
transition: .2s ease border; transition: .2s ease border;
width: 100%; 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, .dashboard #locale,
@@ -69,49 +39,32 @@ form.dashboard > p:last-child {
} }
.dashboard #locale { .dashboard #locale {
border: 1px solid #dddddd;
margin-top: .5em; margin-top: .5em;
} }
.dashboard textarea:focus, textarea:focus,
.dashboard textarea:hover, textarea:hover,
.dashboard input[type="text"]:focus, input[type="text"]:focus,
.dashboard input[type="password"]:focus, input[type="password"]:focus,
.dashboard input[type="text"]:hover, input[type="text"]:hover,
.dashboard input[type="password"]:hover { input[type="password"]:hover {
border-color: #2979ff; border-color: #2979ff;
} }
.dashboard input.red { input.red {
border-color: red; border-color: red;
} }
.dashboard input.green { input.green {
border-color: 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 { .dashboard p label {
margin-bottom: .2em; margin-bottom: .2em;
display: block; display: block;
font-size: .8em; font-size: .8em;
font-weight: bold; font-weight: 500;
color: rgba(0, 0, 0, 0.57);
} }
li code, li code,
@@ -131,21 +84,337 @@ p code {
display: flex; display: flex;
color: rgb(84, 110, 122); color: rgb(84, 110, 122);
font-weight: 500; font-weight: 500;
padding: 0 0 1em;
margin: 0 0 1em; margin: 0 0 1em;
font-size: .8em; 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 { .dashboard #nav li {
width: 100%; width: 100%;
padding: 0 0 1em;
border-bottom: 2px solid rgba(0, 0, 0, 0.05);
} }
.dashboard #nav li:last-child { .dashboard #nav li.active {
text-align: right border-color: #2196f3
} }
.dashboard #nav i { .dashboard #nav i {
font-size: 1em; font-size: 1em;
vertical-align: middle; 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/lib/codemirror.css";
@import "~codemirror/theme/ttcn.css"; @import "~codemirror/theme/ttcn.css";
#editor { #editor {
max-width: 800px; max-width: 800px;
margin: 0 auto; margin: 0 auto;
} }
#editor .CodeMirror { #editor .CodeMirror {
box-shadow: rgba(0, 0, 0, 0.06) 0px 1px 3px, rgba(0, 0, 0, 0.12) 0px 1px 2px; box-shadow: rgba(0, 0, 0, 0.06) 0px 1px 3px, rgba(0, 0, 0, 0.12) 0px 1px 2px;
margin: 2em 0; margin: 2em 0;
border-radius: .5em; border-radius: .5em;
} }
#editor h2 { #editor h2 {
color: rgba(0, 0, 0, 0.3); color: rgba(0, 0, 0, 0.3);
font-weight: 500; font-weight: 500;
} }
.CodeMirror { .CodeMirror {
height: auto; height: auto;
} }
.markdown .CodeMirror { .markdown .CodeMirror {
padding: .75em; padding: .75em;
} }
.cm-s-markdown .CodeMirror-gutter { .cm-s-markdown .CodeMirror-gutter {
border-right: 1px solid #eff3f5; border-right: 1px solid #eff3f5;
padding-right: 5px; padding-right: 5px;
margin-right: 15px; margin-right: 15px;
min-width: 2.5em; min-width: 2.5em;
padding-bottom: 30px; padding-bottom: 30px;
} }
.cm-s-markdown .CodeMirror-cursor { .cm-s-markdown .CodeMirror-cursor {
border-right: 2px solid #667880; border-right: 2px solid #667880;
} }
.cm-s-markdown .CodeMirror-lines { .cm-s-markdown .CodeMirror-lines {
margin: 0; margin: 0;
} }
.cm-s-markdown { .cm-s-markdown {
color: #3D494E; color: #3D494E;
} }
.cm-s-markdown span.cm-header { .cm-s-markdown span.cm-header {
color: #3D494E; color: #3D494E;
font-weight: bold; font-weight: bold;
} }
.cm-s-markdown span.cm-variable-2 { .cm-s-markdown span.cm-variable-2 {
color: #3D494E; color: #3D494E;
} }
.cm-s-markdown span.cm-meta { .cm-s-markdown span.cm-meta {
color: #516066; color: #516066;
} }
.cm-s-markdown span.cm-hr { .cm-s-markdown span.cm-hr {
color: #516066; color: #516066;
} }
.cm-s-markdown span.cm-comment { .cm-s-markdown span.cm-comment {
color: #868f93; color: #868f93;
} }
.cm-s-markdown span.cm-qualifier { .cm-s-markdown span.cm-qualifier {
color: #868f93; color: #868f93;
} }
.cm-s-markdown span.cm-number { .cm-s-markdown span.cm-number {
color: #197987; color: #197987;
} }
.cm-s-markdown span.cm-variable { .cm-s-markdown span.cm-variable {
color: #197987; color: #197987;
} }
.cm-s-markdown span.cm-builtin { .cm-s-markdown span.cm-builtin {
color: #197987; color: #197987;
} }
.cm-s-markdown span.cm-link { .cm-s-markdown span.cm-link {
color: #197987; color: #197987;
text-decoration: underline; text-decoration: underline;
} }
.cm-s-markdown span.cm-tag { .cm-s-markdown span.cm-tag {
color: #197987; color: #197987;
} }
.cm-s-markdown span.cm-string { .cm-s-markdown span.cm-string {
color: #48abb9; color: #48abb9;
} }
.cm-s-markdown span.cm-string-2 { .cm-s-markdown span.cm-string-2 {
color: #48abb9; color: #48abb9;
} }
.cm-s-markdown span.cm-quote { .cm-s-markdown span.cm-quote {
color: #48abb9; color: #48abb9;
} }
.cm-s-markdown span.cm-atom { .cm-s-markdown span.cm-atom {
color: #48abb9; color: #48abb9;
} }
.cm-s-markdown span.cm-property { .cm-s-markdown span.cm-property {
color: #82a367; color: #82a367;
} }
.cm-s-markdown span.cm-operator { .cm-s-markdown span.cm-operator {
color: #82a367; color: #82a367;
} }
.cm-s-markdown span.cm-variable-3 { .cm-s-markdown span.cm-variable-3 {
color: #82a367; color: #82a367;
} }
.cm-s-markdown span.cm-attribute { .cm-s-markdown span.cm-attribute {
color: #90bb74; color: #90bb74;
} }
.cm-s-markdown span.cm-def { .cm-s-markdown span.cm-def {
color: #90bb74; color: #90bb74;
} }
.cm-s-markdown span.cm-keyword { .cm-s-markdown span.cm-keyword {
color: #ec6c45; color: #ec6c45;
} }
.cm-s-markdown span.cm-bracket { .cm-s-markdown span.cm-bracket {
color: #ec6c45; color: #ec6c45;
} }
.cm-s-markdown span.cm-error { .cm-s-markdown span.cm-error {
color: #e45346; color: #e45346;
} }
.cm-s-markdown span.cm-em { .cm-s-markdown span.cm-em {
font-style: italic; font-style: italic;
} }
.cm-s-markdown span.cm-strong { .cm-s-markdown span.cm-strong {
font-weight: bold; font-weight: bold;
} }
.cm-s-markdown .cm-header-1 { .cm-s-markdown .cm-header-1 {
font-size: 200%; font-size: 200%;
line-height: 200%; line-height: 200%;
} }
.cm-s-markdown .cm-header-2 { .cm-s-markdown .cm-header-2 {
font-size: 160%; font-size: 160%;
line-height: 160%; line-height: 160%;
} }
.cm-s-markdown .cm-header-3 { .cm-s-markdown .cm-header-3 {
font-size: 125%; font-size: 125%;
line-height: 125%; line-height: 125%;
} }
.cm-s-markdown .cm-header-4 { .cm-s-markdown .cm-header-4 {
font-size: 110%; font-size: 110%;
line-height: 110%; line-height: 110%;
} }
.cm-s-markdown .cm-comment { .cm-s-markdown .cm-comment {
background: rgba(0, 0, 0, .05); background: rgba(0, 0, 0, .05);
border-radius: 2px; border-radius: 2px;
} }
.cm-s-markdown .cm-link { .cm-s-markdown .cm-link {
color: #7f8c8d; color: #7f8c8d;
} }
.cm-s-markdown .cm-url { .cm-s-markdown .cm-url {
color: #aab2b3; color: #aab2b3;
} }
.cm-s-markdown .cm-strikethrough { .cm-s-markdown .cm-strikethrough {
text-decoration: line-through; text-decoration: line-through;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -26,11 +26,13 @@ buttons:
publish: Publish publish: Publish
selectMultiple: Select multiple selectMultiple: Select multiple
schedule: Schedule schedule: Schedule
switchView: Swicth view switchView: Switch view
toggleSidebar: Toggle sidebar toggleSidebar: Toggle sidebar
update: Update update: Update
upload: Upload upload: Upload
permalink: Get Permanent Link permalink: Get Permanent Link
success:
linkCopied: Link copied!
errors: errors:
forbidden: You're not welcome here. forbidden: You're not welcome here.
internal: Something really went wrong. internal: Something really went wrong.
@@ -115,13 +117,14 @@ settings:
commandsHelp: > commandsHelp: >
Here you can set commands that are executed in the named events. You 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 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. of the file.
commandsUpdated: Commands updated! commandsUpdated: Commands updated!
customStylesheet: Custom Stylesheet customStylesheet: Custom Stylesheet
examples: Examples examples: Examples
globalSettings: Global Settings globalSettings: Global Settings
language: Language language: Language
lockPassword: Prevent the user from changing the password
newPassword: Your new password newPassword: Your new password
newPasswordConfirm: Confirm your new password newPasswordConfirm: Confirm your new password
newUser: New User newUser: New User
@@ -165,7 +168,6 @@ sidebar:
myFiles: My files myFiles: My files
newFile: New file newFile: New file
newFolder: New folder newFolder: New folder
servedWith: Served with
settings: Settings settings: Settings
siteSettings: Site Settings siteSettings: Site Settings
hugoNew: Hugo New hugoNew: Hugo New
@@ -185,10 +187,12 @@ search:
writeToSearch: Write here to search writeToSearch: Write here to search
languages: languages:
en: English en: English
fr: Français
pt: Português pt: Português
ja: 日本語 ja: 日本語
zhCN: 中文 (简体) zhCN: 中文 (简体)
zhTW: 中文 (繁體) zhTW: 中文 (繁體)
es: Español
time: time:
unit: Time Unit unit: Time Unit
seconds: Seconds 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

194
assets/src/i18n/fr.yaml Normal file
View File

@@ -0,0 +1,194 @@
permanent: Permanent
buttons:
cancel: Annuler
close: Fermer
copy: Copier
copyFile: Copier le fichier
copyToClipboard: Copier dans le presse-papier
create: Créer
delete: Supprimer
download: Télécharger
info: Info
more: Plus
move: Déplacer
moveFile: Déplacer le fichier
new: Nouveau
next: Suivant
ok: OK
replace: Remplacer
previous: Précédent
rename: Renommer
reportIssue: Rapport d'erreur
save: Enregistrer
search: Chercher
select: Sélectionner
share: Partager
publish: Publier
selectMultiple: Sélection multiple
schedule: Fixer la date
switchView: Changer le mode d'affichage
toggleSidebar: Afficher/Masquer la barre latérale
update: Mettre à jour
upload: Importer
permalink: Obtenir un lien permanent
errors:
forbidden: Vous n'êtes pas autorisé à être ici.
internal: Aïe ! Quelque chose s'est mal passé.
notFound: Impossible d'accéder à cet emplacement.
files:
folders: Dossiers
files: Fichiers
body: Corps
clear: Fermer
closePreview: Fermer la prévisualisation
home: Accueil
lastModified: Dernière modification
loading: Chargement...
lonely: Il semble qu'il n'y ai rien par ici...
metadata: Metadonnées
multipleSelectionEnabled: Sélection multiple activée
name: Nom
size: Taille
sortByName: Trier par nom
sortBySize: Trier par taille
sortByLastModified: Trier par date de dernière modification
help:
click: Sélectionner un élément
ctrl:
click: Sélectionner plusieurs éléments
f: Ouvrir l'invité de recherche
s: Télécharger l'élément actuel
del: Supprimer les éléments sélectionnés
doubleClick: Ouvrir un élément
esc: Désélectionner et/ou fermer la boîte de dialogue
f1: Ouvrir l'aide
f2: Renommer le fichier
help: Aide
login:
password: Mot de passe
submit: Se connecter
username: Utilisateur
wrongCredentials: Identifiants incorrects !
prompts:
copy: Copier
copyMessage: 'Choisissez l''emplacement où copier la sélection :'
currentlyNavigating: 'Dossier courant :'
deleteMessageMultiple: Etes-vous sûr de vouloir supprimer ces {count} élément(s) ?
deleteMessageSingle: Etes-vous sûr de vouloir supprimer cet élément ?
deleteTitle: Supprimer
displayName: 'Nom :'
download: Télécharger
downloadMessage: 'Choisissez le format de téléchargement :'
error: Quelque chose s'est mal passé
fileInfo: Informations
filesSelected: "{count} éléments sélectionnés"
lastModified: Dernière modification
move: Déplacer
moveMessage: 'Choisissez l''emplacement où déplacer la sélection :'
newDir: Nouveau dossier
newDirMessage: 'Nom du nouveau dossier :'
newFile: Nouveau fichier
newFileMessage: 'Nom du nouveau fichier :'
numberDirs: Nombre de dossiers
numberFiles: Nombre de fichiers
replace: Remplacer
replaceMessage: >
Un des fichiers que vous êtes en train d'importer a le même nom qu'un autre déjà présent.
Voulez-vous remplacer le fichier actuel par le nouveau ?
rename: Renommer
renameMessage: Nouveau nom pour
show: Montrer
size: Taille
schedule: Fixer la date
scheduleMessage: Choisissez une date pour planifier la publication de ce post
newArchetype: Créer un nouveau post basé sur un archétype. Votre fichier sera créé dans le dossier de contenu.
settings:
admin: Admin
administrator: Administrateur
allowCommands: Exécuter des commandes
allowEdit: Editer, renommer et supprimer des fichiers ou des dossiers
allowNew: Créer de nouveaux fichiers et dossiers
allowPublish: Publier de nouveaux posts et pages
avoidChanges: "(Laisser vide pour conserver l'actuel)"
changePassword: Modifier le mot de passe
commands: Commandes
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
et contiendra le chemin d'accès vers le fichier.
commandsUpdated: Commandes mises à jour !
customStylesheet: Feuille de style personnalisée
examples: Exemples
globalSettings: Paramètres généraux
language: Langue
newPassword: Votre nouveau mot de passe
newPasswordConfirm: Confirmation du nouveau mot de passe
newUser: Nouvel Utilisateur
password: Mot de passe
passwordUpdated: Mot de passe mis à jour !
permissions: Permissions
permissionsHelp: >
Vous pouvez définir l'utilisateur comme étant un administrateur ou encore choisir les
permissions individuellement. Si vous sélectionnez "Administrateur", toutes les autres
options seront automatiquement activées. La gestion des utilisateurs est un privilège que
seul l'administrateur possède.
profileSettings: Paramètres du profil
ruleExample1: Bloque l'accès à tous les fichiers commençant par un point (comme par exemple .git, .gitignore) dans tous les dossiers
ruleExample2: Bloque l'accès au fichier nommé "Caddyfile" à la racine du dossier utilisateur
rules: Règles
rulesHelp1: >
Vous pouvez définir ici un ensemble de règles pour cet utilisateur.
Les fichiers bloqués ne seront pas affichés et ne seront pas accessibles par l'utilisateur.
Les expressions régulières sont supportées et les chemins d'accès sont relatifs par rapport au dossier de l'utilisateur.
rulesHelp2: >
Chaque règle est définie sur une ligne différente et doit commencer par le mot clé {0} ou {1}.
Vous devez ensuite ajouter {2} si vous utilisez une expression régulière puis l'expression en question ou bien seulement le chemin d'accès.
scope: Portée du dossier utilisateur
settingsUpdated: Les paramètres ont été mis à jour !
user: Utilisateur
userCommands: Commandes
userCommandsHelp: 'Une liste séparée par des espaces des commandes permises pour l''utilisateur. Exemple :'
userCreated: Utilisateur créé !
userDeleted: Utilisateur supprimé !
userManagement: Gestion des utilisateurs
username: Nom d'utilisateur
users: Utilisateurs
userUpdated: Utilisateur mis à jour !
sidebar:
help: Aide
logout: Se déconnecter
myFiles: Mes fichiers
newFile: Nouveau fichier
newFolder: Nouveau dossier
settings: Paramètres
siteSettings: Paramètres du site
hugoNew: Nouveau Hugo
preview: Prévisualiser
search:
images: Images
music: Musique
pdf: PDF
pressToExecute: Appuyez sur Entrée pour exécuter
pressToSearch: Appuyez sur Entrée pour lancer la recherche
search: Recherche en cours...
searchOrCommand: Rechercher ou exécuter une commande...
searchOrSupportedCommand: 'Lancez une recherche ou exécutez une commande parmis les suivantes :'
type: Tapez votre recherche et appuyez sur Entrée
types: Types
video: Video
writeToSearch: Ecrivez ici pour lancer une recherche
languages:
en: English
fr: Français
pt: Português
ja: 日本語
zhCN: 中文 (简体)
zhTW: 中文 (繁體)
es: Español
time:
unit: Unité de temps
seconds: Secondes
minutes: Minutes
hours: Heures
days: Jours

View File

@@ -1,22 +1,60 @@
import Vue from 'vue' import Vue from 'vue'
import VueI18n from 'vue-i18n' import VueI18n from 'vue-i18n'
import en from './en.yaml' import en from './en.yaml'
import fr from './fr.yaml'
import pt from './pt.yaml' import pt from './pt.yaml'
import ja from './ja.yaml' import ja from './ja.yaml'
import zhCN from './zh-cn.yaml' import zhCN from './zh-cn.yaml'
import zhTW from './zh-tw.yaml' import zhTW from './zh-tw.yaml'
import es from './es.yaml'
Vue.use(VueI18n) 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({ const i18n = new VueI18n({
locale: 'en', locale: detectLocale(),
fallbackLocale: 'en', fallbackLocale: 'en',
messages: { messages: {
'en': en, 'en': en,
'fr': fr,
'pt': pt, 'pt': pt,
'ja': ja, 'ja': ja,
'zh-cn': zhCN, 'zh-cn': zhCN,
'zh-tw': zhTW 'zh-tw': zhTW,
'es': es
} }
}) })

View File

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

View File

@@ -31,6 +31,8 @@ buttons:
update: Atualizar update: Atualizar
upload: Enviar upload: Enviar
permalink: Obter link permanente permalink: Obter link permanente
success:
linkCopied: Link copiado!
errors: errors:
forbidden: Tu não és bem-vindo aqui. forbidden: Tu não és bem-vindo aqui.
internal: Algo correu bastante mal. internal: Algo correu bastante mal.
@@ -66,10 +68,12 @@ help:
help: Ajuda help: Ajuda
languages: languages:
en: English en: English
fr: Français
pt: Português pt: Português
ja: 日本語 ja: 日本語
zhCN: 中文 (简体) zhCN: 中文 (简体)
zhTW: 中文 (繁體) zhTW: 中文 (繁體)
es: Español
login: login:
password: Palavra-passe password: Palavra-passe
submit: Login submit: Login
@@ -136,12 +140,13 @@ settings:
Pode definir um conjunto de comandos a executar em determiandos eventos. Pode definir um conjunto de comandos a executar em determiandos eventos.
Deve escrever um comando por linha. Se o evento estiver relacionado com ficheiros, 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 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! commandsUpdated: Comandos atualizados!
customStylesheet: Estilos Personalizados customStylesheet: Estilos Personalizados
examples: Exemplos examples: Exemplos
globalSettings: Configurações Globais globalSettings: Configurações Globais
language: Linguagem language: Linguagem
lockPassword: Não permitir que o utilizador altere a palavra-passe
newPassword: Nova palavra-passe newPassword: Nova palavra-passe
newPasswordConfirm: Confirme a nova palavra-passe newPasswordConfirm: Confirme a nova palavra-passe
newUser: Novo Utilizador newUser: Novo Utilizador
@@ -189,7 +194,6 @@ sidebar:
newFile: Novo ficheiro newFile: Novo ficheiro
newFolder: Nova pasta newFolder: Nova pasta
preview: Pré-visualizar preview: Pré-visualizar
servedWith: Servido com
settings: Configurações settings: Configurações
siteSettings: Configurações do Site siteSettings: Configurações do Site
time: time:

View File

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

View File

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

View File

@@ -3,9 +3,46 @@ import App from './App'
import store from './store' import store from './store'
import router from './router' import router from './router'
import i18n from './i18n' import i18n from './i18n'
import Noty from 'noty'
Vue.config.productionTip = true 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 */ /* eslint-disable no-new */
new Vue({ new Vue({
el: '#app', el: '#app',

View File

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

View File

@@ -12,9 +12,18 @@ const state = {
key: '', key: '',
items: [] 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'), staticGen: document.querySelector('meta[name="staticgen"]').getAttribute('content'),
baseURL: document.querySelector('meta[name="base"]').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: '', jwt: '',
progress: 0,
schedule: '', schedule: '',
loading: false, loading: false,
reload: false, reload: false,

View File

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

View File

@@ -18,7 +18,7 @@ export function fetch (url) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/resource${url}`, true) request.open('GET', `${store.state.baseURL}/api/resource${url}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => { request.onload = () => {
switch (request.status) { switch (request.status) {
@@ -41,7 +41,7 @@ export function remove (url) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('DELETE', `${store.state.baseURL}/api/resource${url}`, true) request.open('DELETE', `${store.state.baseURL}/api/resource${url}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => { request.onload = () => {
if (request.status === 200) { if (request.status === 200) {
@@ -56,13 +56,17 @@ export function remove (url) {
}) })
} }
export function post (url, content = '', overwrite = false) { export function post (url, content = '', overwrite = false, onupload) {
url = removePrefix(url) url = removePrefix(url)
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('POST', `${store.state.baseURL}/api/resource${url}`, true) request.open('POST', `${store.state.baseURL}/api/resource${url}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
if (typeof onupload === 'function') {
request.upload.onprogress = onupload
}
if (overwrite) { if (overwrite) {
request.setRequestHeader('Action', `override`) request.setRequestHeader('Action', `override`)
@@ -91,7 +95,7 @@ export function put (url, content = '', publish = false, date = '') {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('PUT', `${store.state.baseURL}/api/resource${url}`, true) request.open('PUT', `${store.state.baseURL}/api/resource${url}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.setRequestHeader('Publish', publish) request.setRequestHeader('Publish', publish)
if (date !== '') { if (date !== '') {
@@ -121,7 +125,7 @@ function moveCopy (items, copy = false) {
promises.push(new Promise((resolve, reject) => { promises.push(new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('PATCH', `${store.state.baseURL}/api/resource${from}`, true) request.open('PATCH', `${store.state.baseURL}/api/resource${from}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.setRequestHeader('Destination', to) request.setRequestHeader('Destination', to)
if (copy) { if (copy) {
@@ -158,7 +162,7 @@ export function checksum (url, algo) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/checksum${url}?algo=${algo}`, true) request.open('GET', `${store.state.baseURL}/api/checksum${url}?algo=${algo}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => { request.onload = () => {
if (request.status === 200) { if (request.status === 200) {
@@ -222,7 +226,7 @@ export function getSettings () {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/settings/`, true) request.open('GET', `${store.state.baseURL}/api/settings/`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => { request.onload = () => {
switch (request.status) { switch (request.status) {
@@ -251,7 +255,7 @@ export function updateSettings (param, which) {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('PUT', `${store.state.baseURL}/api/settings/`, true) request.open('PUT', `${store.state.baseURL}/api/settings/`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => { request.onload = () => {
switch (request.status) { switch (request.status) {
@@ -274,7 +278,7 @@ export function getUsers () {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/users/`, true) request.open('GET', `${store.state.baseURL}/api/users/`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => { request.onload = () => {
switch (request.status) { switch (request.status) {
@@ -295,7 +299,7 @@ export function getUser (id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/users/${id}`, true) request.open('GET', `${store.state.baseURL}/api/users/${id}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => { request.onload = () => {
switch (request.status) { switch (request.status) {
@@ -316,7 +320,7 @@ export function newUser (user) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('POST', `${store.state.baseURL}/api/users/`, true) request.open('POST', `${store.state.baseURL}/api/users/`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => { request.onload = () => {
switch (request.status) { switch (request.status) {
@@ -341,7 +345,7 @@ export function updateUser (user, which) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('PUT', `${store.state.baseURL}/api/users/${user.ID}`, true) request.open('PUT', `${store.state.baseURL}/api/users/${user.ID}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => { request.onload = () => {
switch (request.status) { switch (request.status) {
@@ -366,7 +370,7 @@ export function deleteUser (id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('DELETE', `${store.state.baseURL}/api/users/${id}`, true) request.open('DELETE', `${store.state.baseURL}/api/users/${id}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => { request.onload = () => {
switch (request.status) { switch (request.status) {
@@ -391,7 +395,7 @@ export function getShare (url) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/share${url}`, true) request.open('GET', `${store.state.baseURL}/api/share${url}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => { request.onload = () => {
if (request.status === 200) { if (request.status === 200) {
@@ -410,7 +414,7 @@ export function deleteShare (hash) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('DELETE', `${store.state.baseURL}/api/share/${hash}`, true) request.open('DELETE', `${store.state.baseURL}/api/share/${hash}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => { request.onload = () => {
if (request.status === 200) { if (request.status === 200) {
@@ -435,7 +439,7 @@ export function share (url, expires = '', unit = 'hours') {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('POST', url, true) request.open('POST', url, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => { request.onload = () => {
if (request.status === 200) { if (request.status === 200) {

View File

@@ -1,13 +1,18 @@
import cookie from './cookie' import cookie from './cookie'
import store from '@/store' import store from '@/store'
import router from '@/router' import router from '@/router'
import { Base64 } from 'js-base64'
function parseToken (token) { function parseToken (token) {
let path = store.state.baseURL let path = store.state.baseURL
if (path === '') path = '/' if (path === '') path = '/'
document.cookie = `auth=${token}; max-age=86400; path=${path}` document.cookie = `auth=${token}; max-age=86400; path=${path}`
let res = token.split('.') 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('setJWT', token)
store.commit('setUser', user) store.commit('setUser', user)
} }
@@ -16,7 +21,7 @@ function loggedIn () {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/auth/renew`, true) request.open('GET', `${store.state.baseURL}/api/auth/renew`, true)
request.setRequestHeader('Authorization', `Bearer ${cookie('auth')}`) if (!store.state.noAuth) request.setRequestHeader('Authorization', `Bearer ${cookie('auth')}`)
request.onload = () => { request.onload = () => {
if (request.status === 200) { if (request.status === 200) {
@@ -31,8 +36,8 @@ function loggedIn () {
}) })
} }
function login (user, password) { function login (user, password, captcha) {
let data = {username: user, password: password} let data = {username: user, password: password, recaptcha: captcha}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest() let request = new window.XMLHttpRequest()
request.open('POST', `${store.state.baseURL}/api/auth/get`, true) 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: // Most of the code from this file comes from:
// https://github.com/codemirror/CodeMirror/blob/master/addon/mode/loadmode.js // https://github.com/codemirror/CodeMirror/blob/master/addon/mode/loadmode.js
import * as CodeMirror from 'codemirror' import * as CodeMirror from 'codemirror'
import store from '@/store' import store from '@/store'
// Make CodeMirror available globally so the modes' can register themselves. // Make CodeMirror available globally so the modes' can register themselves.
window.CodeMirror = CodeMirror window.CodeMirror = CodeMirror
CodeMirror.modeURL = store.state.baseURL + '/static/js/codemirror/mode/%N/%N.js' CodeMirror.modeURL = store.state.baseURL + '/static/js/codemirror/mode/%N/%N.js'
var loading = {} var loading = {}
function splitCallback (cont, n) { function splitCallback (cont, n) {
var countDown = n var countDown = n
return function () { return function () {
if (--countDown === 0) cont() if (--countDown === 0) cont()
} }
} }
function ensureDeps (mode, cont) { function ensureDeps (mode, cont) {
var deps = CodeMirror.modes[mode].dependencies var deps = CodeMirror.modes[mode].dependencies
if (!deps) return cont() if (!deps) return cont()
var missing = [] var missing = []
for (var i = 0; i < deps.length; ++i) { for (var i = 0; i < deps.length; ++i) {
if (!CodeMirror.modes.hasOwnProperty(deps[i])) missing.push(deps[i]) if (!CodeMirror.modes.hasOwnProperty(deps[i])) missing.push(deps[i])
} }
if (!missing.length) return cont() if (!missing.length) return cont()
var split = splitCallback(cont, missing.length) var split = splitCallback(cont, missing.length)
for (i = 0; i < missing.length; ++i) CodeMirror.requireMode(missing[i], split) for (i = 0; i < missing.length; ++i) CodeMirror.requireMode(missing[i], split)
} }
CodeMirror.requireMode = function (mode, cont) { CodeMirror.requireMode = function (mode, cont) {
if (typeof mode !== 'string') mode = mode.name if (typeof mode !== 'string') mode = mode.name
if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont) if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont)
if (loading.hasOwnProperty(mode)) return loading[mode].push(cont) if (loading.hasOwnProperty(mode)) return loading[mode].push(cont)
var file = CodeMirror.modeURL.replace(/%N/g, mode) var file = CodeMirror.modeURL.replace(/%N/g, mode)
var script = document.createElement('script') var script = document.createElement('script')
script.src = file script.src = file
var others = document.getElementsByTagName('script')[0] var others = document.getElementsByTagName('script')[0]
var list = loading[mode] = [cont] var list = loading[mode] = [cont]
CodeMirror.on(script, 'load', function () { CodeMirror.on(script, 'load', function () {
ensureDeps(mode, function () { ensureDeps(mode, function () {
for (var i = 0; i < list.length; ++i) list[i]() for (var i = 0; i < list.length; ++i) list[i]()
}) })
}) })
others.parentNode.insertBefore(script, others) others.parentNode.insertBefore(script, others)
} }
CodeMirror.autoLoadMode = function (instance, mode) { CodeMirror.autoLoadMode = function (instance, mode) {
if (CodeMirror.modes.hasOwnProperty(mode)) return if (CodeMirror.modes.hasOwnProperty(mode)) return
CodeMirror.requireMode(mode, function () { CodeMirror.requireMode(mode, function () {
instance.setOption('mode', mode) instance.setOption('mode', mode)
}) })
} }
export default CodeMirror export default CodeMirror

View File

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

View File

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

View File

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

View File

@@ -210,7 +210,7 @@ export default {
} }
}, },
scroll (event) { 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 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

@@ -1,9 +1,12 @@
<template> <template>
<div> <div>
<div id="progress">
<div v-bind:style="{ width: $store.state.progress + '%' }"></div>
</div>
<site-header></site-header> <site-header></site-header>
<sidebar></sidebar> <sidebar></sidebar>
<main> <main>
<router-view v-on:css-updated="updateCSS"></router-view> <router-view @css="$emit('update:css')"></router-view>
</main> </main>
<prompts></prompts> <prompts></prompts>
</div> </div>
@@ -31,23 +34,10 @@ export default {
} }
}, },
mounted () { mounted () {
this.updateCSS() this.$emit('update:css')
}, },
methods: { beforeDestroy () {
updateCSS () { this.$emit('clean:css')
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)
}
} }
} }
</script> </script>

View File

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

View File

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

View File

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

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