diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..71300769 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{js,rs,css,tera}] +charset = utf-8 + +[*.{rs,tera,css}] +indent_style = space +indent_size = 4 + +[*.js] +indent_style = space +indent_size = 2 diff --git a/.gitignore b/.gitignore index 925d54ce..5b06904d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ rls translations po/*.po~ .env +Rocket.toml +media diff --git a/Cargo.lock b/Cargo.lock index 9b58dc73..8820fa57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,6 +89,35 @@ dependencies = [ "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ascii" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "atom_syndication" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "derive_builder 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-xml 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "debug-builders 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "backtrace" version = "0.3.6" @@ -140,6 +169,11 @@ dependencies = [ "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bitflags" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bitflags" version = "0.9.1" @@ -168,6 +202,16 @@ dependencies = [ "opaque-debug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "buf_redux" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slice-deque 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "build_const" version = "0.2.1" @@ -202,6 +246,17 @@ name = "cfg-if" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "chomp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "debugtrace 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "either 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "chrono" version = "0.4.2" @@ -213,6 +268,11 @@ dependencies = [ "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "chunked_transfer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cloudabi" version = "0.0.3" @@ -229,6 +289,14 @@ dependencies = [ "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "conv" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cookie" version = "0.11.0-dev" @@ -316,11 +384,38 @@ dependencies = [ "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "custom_derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "data-encoding" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "dbghelp-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "debug-builders" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "debugtrace" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "derive-error-chain" version = "0.11.1" @@ -332,6 +427,25 @@ dependencies = [ "syntex_fmt_macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "derive_builder" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "derive_builder_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "derive_builder_core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "diesel" version = "1.2.2" @@ -370,6 +484,68 @@ name = "dtoa" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "either" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "encoding" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding-index-simpchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding-index-singlebyte 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding-index-tradchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "encoding-index-japanese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "encoding-index-korean" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "encoding-index-simpchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "encoding-index-singlebyte" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "encoding-index-tradchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "encoding_index_tests" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "encoding_rs" version = "0.7.2" @@ -490,6 +666,59 @@ name = "glob" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "groupable" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "guid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "chomp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "guid-macro-impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "guid-parser 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "guid-create" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "chomp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "guid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "guid-parser 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "guid-macro-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "chomp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "guid-parser 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "guid-parser" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "chomp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "heck" version = "0.3.0" @@ -611,6 +840,21 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "iron" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "mime_guess 1.8.6 (registry+https://github.com/rust-lang/crates.io-index)", + "modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "isatty" version = "0.1.7" @@ -708,6 +952,14 @@ name = "mac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "mach" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "maplit" version = "1.0.1" @@ -762,6 +1014,17 @@ dependencies = [ "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mime_guess" +version = "1.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "mime_guess" version = "2.0.0-alpha.4" @@ -802,6 +1065,41 @@ dependencies = [ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "modifier" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "multipart" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "buf_redux 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", + "iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mime_guess 1.8.6 (registry+https://github.com/rust-lang/crates.io-index)", + "nickel 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny_http 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "twoway 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mustache" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "native-tls" version = "0.1.5" @@ -834,6 +1132,25 @@ dependencies = [ "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "nickel" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "groupable 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "mustache 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nodrop" version = "0.1.12" @@ -980,23 +1297,34 @@ name = "pkg-config" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "plugin" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "plume" version = "0.1.0" dependencies = [ "activitypub 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "atom_syndication 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "diesel 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "dotenv 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "gettext-rs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "guid-create 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "multipart 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)", "plume-common 0.1.0", "plume-models 0.1.0", "rocket 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)", "rocket_codegen 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)", "rocket_contrib 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)", - "rocket_csrf 0.1.0 (git+https://github.com/fdb-hiroshima/rocket_csrf?branch=plume)", + "rocket_csrf 0.1.0 (git+https://github.com/fdb-hiroshima/rocket_csrf?rev=0fbacab1bc6a32419a18edb600586c5c02fdac4d)", "rocket_i18n 0.1.1 (git+https://github.com/BaptisteGelez/rocket_i18n?rev=5b4225d5bed5769482dc926a7e6d6b79f1217be6)", "rpassword 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1068,6 +1396,19 @@ name = "precomputed-hash" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "proc-macro-hack" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro-hack-impl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" version = "0.2.3" @@ -1100,6 +1441,21 @@ dependencies = [ "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quick-error" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "quick-xml" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quote" version = "0.3.15" @@ -1329,7 +1685,7 @@ dependencies = [ [[package]] name = "rocket_csrf" version = "0.1.0" -source = "git+https://github.com/fdb-hiroshima/rocket_csrf?branch=plume#5309b7634a9cd204d003312cb70ce9d36b660e6b" +source = "git+https://github.com/fdb-hiroshima/rocket_csrf?rev=0fbacab1bc6a32419a18edb600586c5c02fdac4d#0fbacab1bc6a32419a18edb600586c5c02fdac4d" dependencies = [ "csrf 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1401,6 +1757,11 @@ name = "safemem" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "safemem" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "schannel" version = "0.1.12" @@ -1509,6 +1870,16 @@ name = "slab" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "slice-deque" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "mach 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "slug" version = "0.1.3" @@ -1693,6 +2064,19 @@ dependencies = [ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tiny_http" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ascii 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "chunked_transfer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tokio" version = "0.1.5" @@ -1855,11 +2239,27 @@ name = "traitobject" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "twoway" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typeable" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "typemap" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typenum" version = "1.10.0" @@ -1927,6 +2327,14 @@ dependencies = [ "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "unsafe-any" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "untrusted" version = "0.6.2" @@ -2074,24 +2482,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" "checksum array_tool 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8f8cb5d814eb646a863c4f24978cff2880c4be96ad8cde2c0f0678732902e271" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" +"checksum ascii 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "97be891acc47ca214468e09425d02cef3af2c94d0d82081cd02061f996802f14" +"checksum atom_syndication 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a9a7ab83635ff7a3b04856f4ad95324dccc9b947ab1e790fc5c769ee6d6f60c" +"checksum backtrace 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "150ae7828afa7afb6d474f909d64072d21de1f3365b6e8ad8029bf7b1c6350a0" "checksum backtrace 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe525f66f42d207968308ee86bc2dd60aa5fab535b22e616323a173d097d8e" "checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" "checksum base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9263aa6a38da271eec5c91a83ce1e800f093c8535788d403d626d8d5c3f8f007" "checksum bcrypt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a1512813db09170b44a00870b58421876d797b77b085c5205a24db90905f758" +"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" "checksum block-cipher-trait 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6136d803280ae3532efa36114335255ea94f3d75d735ddedd66b0d7cd30bad3" "checksum blowfish 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "95ede07672d9f4144c578439aa352604ec5c67a80c940fe8d382ddbeeeb3c6d8" +"checksum buf_redux 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20c6687a26c9ce967594b78038c06139a0d3a5b3005d16572284d543924a01aa" "checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" "checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87" "checksum bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1d50c876fb7545f5f289cd8b2aee3f359d073ae819eed5d6373638e2c61e59" "checksum cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8b9d2900f78631a5876dc5d6c9033ede027253efcd33dd36b1309fc6cab97ee0" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" +"checksum chomp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f74ad218e66339b11fd23f693fb8f1d621e80ba6ac218297be26073365d163d" "checksum chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1cce36c92cb605414e9b824f866f5babe0a0368e39ea07393b9b63cf3844c0e6" +"checksum chunked_transfer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "498d20a7aaf62625b9bf26e637cf7736417cde1d0c99f1d04d1170229a85cf87" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0aa3473e85a3161b59845d6096b289bb577874cafeaf75ea1b1beaa6572c7fc" +"checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" "checksum cookie 0.11.0-dev (git+https://github.com/alexcrichton/cookie-rs?rev=0365a18)" = "" "checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67" "checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d" @@ -2101,12 +2517,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b" "checksum csrf 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "38f2ee2a7e76740d81de006e61eff53206c56448a30d8017b4ac97b5486682bd" +"checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" "checksum data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "67df0571a74bf0d97fb8b2ed22abdd9a48475c96bd327db968b7d9cace99655e" +"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" +"checksum debug-builders 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f5d8e3d14cabcb2a8a59d7147289173c6ada77a0bc526f6b85078f941c0cf12" +"checksum debugtrace 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "62e432bd83c5d70317f6ebd8a50ed4afb32907c64d6e2e1e65e339b06dc553f3" "checksum derive-error-chain 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cb4450afbe280461e78299b39182a085b70e3e71be049cf4a588ad72f1e44d33" +"checksum derive_builder 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c998e6ab02a828dd9735c18f154e14100e674ed08cb4e1938f0e4177543f439" +"checksum derive_builder_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "735e24ee9e5fa8e16b86da5007856e97d592e11867e45d76e0c0d0a164a0b757" "checksum diesel 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24815a0c2094f2c8dafe74ab3b9e975892f44acbb94b4d4b4898025a7615efa4" "checksum diesel_derives 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6471a2b637b414d3ee1504cf230409a550381c79204282f8fe06c527e4ae56be" "checksum dotenv 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a70de3c590ce18df70743cace1cf12565637a0b26fd8b04ef10c7d33fdc66cdc" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" +"checksum either 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a39bffec1e2015c5d8a6773cb0cf48d0d758c842398f624c34969071f5499ea7" +"checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" +"checksum encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" +"checksum encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" +"checksum encoding-index-simpchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" +"checksum encoding-index-singlebyte 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" +"checksum encoding-index-tradchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" +"checksum encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" "checksum encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98fd0f24d1fb71a4a6b9330c8ca04cbd4e7cc5d846b54ca74ff376bc7c9f798d" "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" "checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" @@ -2123,6 +2553,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum gettext-rs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b8c2412d5758f68a9eeba161f9ecb9a55f56bfdbf17857650b98f2b9b281a47" "checksum gettext-sys 0.19.8 (registry+https://github.com/rust-lang/crates.io-index)" = "62c644c0b8b73706fb8c7420533fd30abf6f41c2703994bc6f0826fceb7fb3d6" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" +"checksum groupable 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "32619942b8be646939eaf3db0602b39f5229b74575b67efc897811ded1db4e57" +"checksum guid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e691c64d9b226c7597e29aeb46be753beb8c9eeef96d8c78dfd4d306338a38da" +"checksum guid-create 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fcea207bf7a6092166ab590f98fe5dde5a7deed1f1920d98dcac31f80814c40d" +"checksum guid-macro-impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08d50f7c496073b5a5dec0f6f1c149113a50960ce25dd2a559987a5a71190816" +"checksum guid-parser 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "abc7adb441828023999e6cff9eb1ea63156f7ec37ab5bf690005e8fc6c1148ad" "checksum heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82" "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" "checksum html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b04478cf718862650a0bf66acaf8f2f8c906fbc703f35c916c1f4211b069a364" @@ -2135,6 +2570,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum if_chain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "61bb90bdd39e3af69b0172dfc6130f6cd6332bf040fbb9bdd4401d37adbd48b8" "checksum indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08173ba1e906efb6538785a8844dd496f5d34f0a2d88038e95195172fc667220" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" +"checksum iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8e17268922834707e1c29e8badbf9c712c9c43378e1b6a3388946baff10be2" "checksum isatty 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a118a53ba42790ef25c82bb481ecf36e2da892646cccd361e69a6bb881e19398" "checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" "checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682" @@ -2149,6 +2585,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" "checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +"checksum mach 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "977262a11cfd76b94da10b8898cea6e9ac391301ab74741e6da6bee13d7df46d" "checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43" "checksum markup5ever 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfedc97d5a503e96816d10fedcd5b42f760b2e525ce2f7ec71f6a41780548475" "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" @@ -2156,12 +2593,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" "checksum mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e00e17be181010a91dbfefb01660b17311059dc8c7f48b9017677721e732bd" +"checksum mime_guess 1.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2d4c0961143b8efdcfa29c3ae63281601b446a4a668165454b6c90f8024954c5" "checksum mime_guess 2.0.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130ea3c9c1b65dba905ab5a4d9ac59234a9585c24d135f264e187fe7336febbd" "checksum mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "6d771e3ef92d58a8da8df7d6976bfca9371ed1de6619d9d5a5ce5b1f29b85bfe" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41f5c9112cb662acd3b204077e0de5bc66305fa8df65c8019d5adb10e9ab6e58" +"checksum multipart 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b3bd50d71866514b14d2ca09823d81390d92daa40bc835f83a908c52ab0a802e" +"checksum mustache 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ddb004e419334fc9172d0a5ff91c0770bdd6239091b0b343eb5926101f0a7d13" "checksum native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f74dbadc8b43df7864539cedb7bc91345e532fdd913cfdc23ad94f4d2d40fbc0" "checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0" "checksum new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cdc457076c78ab54d5e0d6fa7c47981757f1e34dc39ff92787f217dede586c4" +"checksum nickel 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "22b40e35b9f46a076dcbd8193125cea0e4130b1c015f68655038010f3e826e04" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" "checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe" "checksum num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee092fcdf725aee04dd7da1d21debff559237d49ef1cb3e69bcb8ece44c7364" @@ -2180,12 +2622,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "05a079dd052e7b674d21cb31cbb6c05efd56a2cd2827db7692e2f1a507ebd998" "checksum phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c2261d544c2bb6aa3b10022b0be371b9c7c64f762ef28c6f5d4f1ef6d97b5930" "checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f" +"checksum plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1a6a0dc3910bc8db877ffed8e457763b317cf880df4ae19109b9f77d277cf6e0" "checksum pq-sys 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4dfb5e575ef93a1b7b2a381d47ba7c5d4e4f73bff37cee932195de769aad9a54" "checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +"checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0" +"checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892" "checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0" "checksum proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "49b6a521dc81b643e9a51e0d1cf05df46d5a2f3c0280ea72bcb68276ba64a118" "checksum proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "effdb53b25cdad54f8f48843d67398f7ef2e14f12c1b4cb4effc549a6462a4d6" "checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32" +"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" +"checksum quick-xml 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b14c27e04216596a49f2b82398a24f67ed9f131a5c0e0235496ea446bdacfb12" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408" "checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" @@ -2208,7 +2655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rocket_codegen 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)" = "" "checksum rocket_codegen_next 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)" = "" "checksum rocket_contrib 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)" = "" -"checksum rocket_csrf 0.1.0 (git+https://github.com/fdb-hiroshima/rocket_csrf?branch=plume)" = "" +"checksum rocket_csrf 0.1.0 (git+https://github.com/fdb-hiroshima/rocket_csrf?rev=0fbacab1bc6a32419a18edb600586c5c02fdac4d)" = "" "checksum rocket_http 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)" = "" "checksum rocket_i18n 0.1.1 (git+https://github.com/BaptisteGelez/rocket_i18n?rev=5b4225d5bed5769482dc926a7e6d6b79f1217be6)" = "" "checksum rpassword 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d127299b02abda51634f14025aec43ae87a7aa7a95202b6a868ec852607d1451" @@ -2216,6 +2663,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" +"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" "checksum schannel 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "85fd9df495640643ad2d00443b3d78aae69802ad488debab4f1dd52fc1806ade" "checksum scheduled-thread-pool 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a2ff3fc5223829be817806c6441279c676e454cc7da608faf03b0ccc09d3889" "checksum scoped-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8674d439c964889e2476f474a3bf198cc9e199e77499960893bac5de7e9218a4" @@ -2230,6 +2678,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" "checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d" +"checksum slice-deque 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "79e5bb98247a0eb0cfdedb7e792962ec71ac1003033f70558bd9961f8912e487" "checksum slug 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "797bcb4d24e91239a8615415814f4afb2d8ca400c472de3c73f803a5a7689e11" "checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" "checksum smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "26df3bb03ca5eac2e64192b723d51f56c1b1e0860e7c766281f4598f181acdc8" @@ -2250,6 +2699,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum tera 0.11.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e815b67d44c26feb06630011fb58b5b243f4e9585aac1ed0592c5795de64cd75" "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" "checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" +"checksum tiny_http 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a442681f9f72e440be192700eeb2861e4174b9983f16f4877c93a134cb5e5f63" "checksum tokio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "be15ef40f675c9fe66e354d74c73f3ed012ca1aa14d65846a33ee48f1ae8d922" "checksum tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" "checksum tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8cac2a7883ff3567e9d66bb09100d09b33d90311feca0206c7ca034bc0c55113" @@ -2264,7 +2714,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum tokio-udp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "137bda266504893ac4774e0ec4c2108f7ccdbcb7ac8dced6305fe9e4e0b5041a" "checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" +"checksum twoway 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" +"checksum typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" @@ -2276,6 +2728,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unidecode 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "402bb19d8e03f1d1a7450e2bd613980869438e0666331be3e073089124aa1adc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +"checksum unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" "checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f" "checksum url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7" "checksum utf-8 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1262dfab4c30d5cb7c07026be00ee343a6cf5027fdc0104a9160f354e5db75c" diff --git a/Cargo.toml b/Cargo.toml index f76918f2..2f13ef6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,11 +4,14 @@ name = "plume" version = "0.1.0" [dependencies] activitypub = "0.1.1" +atom_syndication = "0.6" colored = "1.6" dotenv = "*" failure = "0.1" gettext-rs = "0.4" +guid-create = "0.1" heck = "0.3.0" +multipart = "0.15" rpassword = "2.0" serde = "1.0" serde_derive = "1.0" @@ -42,8 +45,8 @@ git = "https://github.com/SergioBenitez/Rocket" rev = "df7111143e466c18d1f56377a8d9530a5a306aba" [dependencies.rocket_csrf] -branch = "plume" git = "https://github.com/fdb-hiroshima/rocket_csrf" +rev = "0fbacab1bc6a32419a18edb600586c5c02fdac4d" [dependencies.rocket_i18n] git = "https://github.com/BaptisteGelez/rocket_i18n" diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100644 index 778c6775..00000000 --- a/INSTALL.md +++ /dev/null @@ -1,72 +0,0 @@ -# How to install Plume on a Debian stretch: - -## Basic setup: - -```bash -apt update -apt install gettext postgresql postgresql-contrib libpq-dev -adduser plume -su - plume -cd /home/plume -git clone https://github.com/Plume-org/Plume.git -curl https://sh.rustup.rs -sSf | sh -cd Plume -rustup toolchain install nightly -rustup toolchain default nightly -rustup update -cargo install diesel_cli --no-default-features --features postgres --version '=1.2.0' # we dont need to compile anything else than pgsql -``` - -## Now, if you want to run postgresql on the same server: - -```bash -service postgresql start -cargo run # this will configure and launch Plume on the server. -``` - -## If you want to run Plume with a remote DB this time ( Postgresql is not installed on the same server/container): - -* On the DB server: - -```bash -service postgresql start -su - postgres -createuser -d -P plume -createdb -O plume plume -``` - -* On the Plume server: - -```bash -cd /home/plume/Plume -diesel migration run --database-url postgres://plume:PASSWORD@DBSERVERIP:DBPORT/plume -DB_URL=postgres://plume:PASSWORD@DBSERVERIP:DBPORT/plume cargo run # the first launch will ask questions to configure the instance. A second launch will not need the DB_URL. -``` - -## Plume is now accessible as seen on your console. You can have fun now, or configure an nginx proxy with the following excerpt: - - location / { - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $http_host; - - proxy_pass http://localhost:8000; - - client_max_body_size 16m; - } - -# Caveats: - -* Pgbouncer is not yet supported ( named transactions are used ). - -* Rust nightly is a moving target, dependancies can break and sometimes you need to check a few versions to find the one working. - -```bash -cd /home/plume/Plume -rustup override set nightly-2018-05-15 # this could be needed for compilation. If errors, try 2018-05-31. -# rustup override unset # remove the override for this directory. -``` - -* Rust nightly 2018-06-28 is known to be failing to compile diesel 1.3.2 - diff --git a/doc/PREREQUISITES.md b/doc/PREREQUISITES.md deleted file mode 100644 index a7007f86..00000000 --- a/doc/PREREQUISITES.md +++ /dev/null @@ -1,96 +0,0 @@ -# Installing Software Prerequisites - -These instructions have been adapted from the Aardwolf documentation, and may not be accurate. -As such, this notification should be updated once verified for Plume installs. - -> NOTE: These instructions may help in installing a production version, but are -intended for developers to be able to build and test their changes. If in doubt, -seek out documentation from your distribution package or from [the `doc` folder](doc). - -## Installing Requirements - -### Installing PostgreSQL - -In order to run the Plume backend, you will need to have access to a -[PostgreSQL](https://www.postgresql.org/) database. There are a few options for doing this, but for -this guide we’re going to assume you are running the database on your -development machine. - -#### Linux/OSX Instructions - -If you're on an Ubuntu-like machine, you should be able to install -PostgreSQL like this: - - $ sudo apt-get update - $ sudo apt-get install postgresql postgresql-contrib - -If you see an error like: - - = note: /usr/bin/ld: cannot find -lpq - collect2: error: ld returned 1 exit statusb - -Then you may need to install the libpq (PostgreSQL C-library) package as well : - - $ sudo apt-get install libpq-dev - -If you're on OSX and using `brew`, do - - $ brew update - $ brew install postgres - -For Gentoo (eselect-postgresql is optional), - - # emerge --sync - # emerge -av postgresql eselect-postgresql - -For Fedora/CentOS/RHEL, do - - # dnf install postgresql-server postgresql-contrib mariadb-devel libsq3-devel libpqxx libpqxx-devel - - -#### Windows Instructions - -For Windows, just download the installer [here](https://www.enterprisedb.com/downloads/postgres-postgresql-downloads#windows) and run it. After installing, make sure to add the /lib directory to your PATH system variable. - -### Installing rustup - -> Note: Rustup managed installations do appear to co-exist with system - installations on Gentoo, and should work on most other distributions. - If not, please file an issue with the Rust and Rustup teams or your distribution’s - managers. - -Next, you’ll need to have the [Rust](https://rust-lang.org/) toolchain -installed. The best way to do this is to install -[rustup](https://rustup.rs), which is a Rust toolchain manager. - -#### Linux/OSX Instructions - -Open your terminal and run the following command: - - $ curl https://sh.rustup.rs -sSf | sh - -For those who are (understandably) uncomfortable with piping a shell -script from the internet directly into `sh`, you can also -[use an alternate installation method](https://github.com/rust-lang-nursery/rustup.rs/#other-installation-methods). - -#### Windows Instructions - -If you don't already have them, download and install the [Visual C++ 2015 Build Tools](http://landinghub.visualstudio.com/visual-cpp-build-tools). - -Then, download the [rustup installer](https://www.rust-lang.org/en-US/install.html) and run it. That's it! - -### Installing Rust Toolchain - -Once you have `rustup` installed, make sure you have the `nightly` rust -toolchain installed: - - $ rustup toolchain install nightly - -### Installing GetText - -GetText is the tool we use to manage translations. It will be needed at runtime, since we only compile -translation files when starting Plume. - -#### Ubuntu-like Linux - - $ sudo apt-get install gettext diff --git a/DEVELOPMENT.md b/docs/DEVELOPMENT.md similarity index 56% rename from DEVELOPMENT.md rename to docs/DEVELOPMENT.md index 2555fe29..449f7ff5 100644 --- a/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -1,74 +1,10 @@ # Development Guide -## Running Plume locally +## Installing the development environment -### Mac OSX +Please refer to the [installation guide](INSTALL.md). -All commands are run in the Mac Terminal or terminal emulator of your choice, such as iTerm2. First, you will need [Git](https://git-scm.com/download/mac), [Homebrew](https://brew.sh/), [Rust](https://www.rust-lang.org/en-US/), and [Postgres](https://www.postgresql.org/). Follow the instructions to install Homebrew before continuing if you don't already have it. - -### Linux - -Similar to Mac OSX all commands should be run from a terminal (a.k.a command line). First, you will need [Git](https://git-scm.com/download/mac), [Rust](https://www.rust-lang.org/en-US/), and [Postgres](https://www.postgresql.org/). Step-by-step instructions are also available here: [Installing Prerequisites](/doc/PREREQUISITES.md) - -#### Download the Repository - -Navigate to the directory on your machine where you would like to install the repository, such as in `~/dev` by running `cd dev`. Now, clone the remote repository by running `git clone https://github.com/Plume-org/Plume.git`. This will install the codebase to the `Plume` subdirectory. Navigate into that directory by running `cd Plume`. - -#### Rust - -If you think you might already have rust on your machine, you can check by running - -``` -rustc --version -# Should output something like -# rustc 1.28.0-nightly (a805a2a5e 2018-06-10) -``` - -If you don't already have Rust, install it by running - -``` -curl https://sh.rustup.rs -sSf | sh -``` - -In the interactive installation, choose the option of the nightly toolchain. Restart your console so that the `rustc` CLI tool is available. - -#### Postgres - -Now we will use Homebrew to install Postgres. If you think you might already have it, try running `brew info postgres`. If it is not available, continue to install Postgres by running the following: - -``` -brew install postgres -``` - -Now, you can use the following command to start Postgres on a one-time basis. - -``` -pg_ctl -D /usr/local/var/postgres start -``` - -When you will launch Plume for the first time, it will setup the database by itself. - -#### Database Migration - -To run migrations and correctly setup the database, Plume use the `diesel` CLI tool under the hood. Therefore you should install it before running Plume. If this was your time installing Rust, you will probably need to run that using `cargo`. `cargo` is installed with `rustc` so if you followed the earlier instructions it will already be available. - -``` -cargo install diesel_cli --version '=1.2.0' -``` - -#### Running Plume - -To run Plume locally, make sure you are once again in the Plume directory, such as `~/dev/Plume`. Now you will be able to run the application using the command - -``` -cargo run -``` - -#### Configuration - -The first time you'll run Plume, it will help you setup your instance through an interactive tool. Once you'll have answered all its question, your instance will start. - -#### Testing the federation +## Testing the federation To test the federation, you'll need to setup another database (see "Setup the database"), also owned by the "plume" user, but with a different name. Then, you'll need to run the @@ -93,7 +29,7 @@ If you don't want to setup HTTPS locally, you can also disable it by running you USE_HTTPS=0 cargo run ``` -#### Making a Pull Request +## Making a Pull Request To create an upstream fork of the repository in GitHub, click "Fork" in the top right button on the main page of the [Plume repository](https://github.com/Plume-org/Plume). Now, in the command line, set another remote for the repository by running the following command, replacing `myname` with the name under which you forked the repo. You can use another name besides `upstream` if you prefer. Using [SSH](https://help.github.com/articles/connecting-to-github-with-ssh/) is recommended. ``` @@ -105,7 +41,7 @@ Now, make any changes to the code you want. After committing your changes, push The project maintainers may suggest further changes to improve the pull request even more. After implementing this locally, you can push to your upstream fork again and the changes will immediately show up in the pull request after pushing. Once all the suggested changes are made, the pull request may be accepted. Thanks for contributing. -#### When working with Tera templates +## When working with Tera templates When working with the interface, or any message that will be displayed to the final user, keep in mind that Plume is an internationalized software. To make sure that the parts of the interface you are changing are translatable, you should: @@ -139,3 +75,13 @@ msgstr[1] "" ``` And that's it! Once these new messages will have been translated, they will correctly be displayed in the requested locale! + +## Code Style + +For Rust, use the standard style. `rustfmt` can help you keeping your code clean. + +For CSS, the only rule is to use One True Brace Style. + +For JavaScript, we use [the JavaScript Standard Style](https://standardjs.com/). + +For HTML/Tera templates, we use HTML5 syntax. diff --git a/docs/INSTALL.md b/docs/INSTALL.md new file mode 100644 index 00000000..47391db9 --- /dev/null +++ b/docs/INSTALL.md @@ -0,0 +1,383 @@ +# Installing Plume (for development or production) + +## Prerequisites + +In order to be installed and to work correctly, Plume needs: + +- *Git* (to get the code) +- *Curl* (for RustUp, the Rust installer) +- *GCC* and *make* (to compile C dependencies) +- *PostgreSQL* (for the database) +- *GetText* (to manage translations) +- *Rust* and *Cargo* (to build the code) +- *OpenSSL* and *OpenSSL librairies* (for security) + +All the following instructions will need a terminal. + +Here are the commands to install PostgreSQL and GetText on various operating systems. +Some of them may need root permissions. + +On **Debian**: + +```bash +apt update +apt install gettext postgresql postgresql-contrib libpq-dev git curl gcc make openssl libssl-dev +``` + +On **Fedora**, **CentOS** or **RHEL**: + +```bash +dnf install postgresql-server postgresql-contrib mariadb-devel libsq3-devel libpqxx libpqxx-devel git curl gcc make openssl openssl-devel gettext +``` + +On **Gentoo**: + +```bash +emerge --sync +emerge -av postgresql eselect-postgresql gettext && emerge --ask dev-vcs/git +``` + +On **Mac OS X**, with [Homebrew](https://brew.sh/): + +```bash +brew update +brew install postgres gettext git +``` + +## Creating a new user (optional) + +This step is recommended if you are in a **production environment**, but it is not necessary. + +```bash +adduser plume +su - plume +cd ~ +``` + +Creating a new user will let you use systemd to manage Plume if you want (see the dedicated section below). + +## Installing Rust and Cargo + +We said that Plume needed Rust and Cargo to work, but we didn't installed them at the same time as PostgreSQL and GetText, because there is an universal installation method called RustUp. + +You can install it on **GNU/Linux** and **Mac OS X** with: + +```bash +curl https://sh.rustup.rs -sSf | sh +``` + +When asked, choose the *"1) Proceed with installation (default)"* option. + +Then run this command to be able to run cargo in the current session: + +```bash +export PATH="$PATH:/home/plume/.cargo/bin:/home/plume/.local/bin:/usr/local/sbin" +``` + +On **Windows**, you'll need, if you don't already have them, to download and install the [Visual C++ 2015 Build Tools](https://www.microsoft.com/en-us/download/details.aspx?id=48159). Then, download the [rustup installer](https://www.rust-lang.org/en-US/install.html) and run it. + +## Getting and compiling the Plume source code + +Plume needs to be compiled from source. + +```bash +git clone https://github.com/Plume-org/Plume.git +cd Plume + +# This may take some time as RustUp will download all +# the required Rust components, and Cargo will download +# and compile all dependencies. +cargo build +``` + +We may provide precompiled packages and Docker images in the future (if you have experience in these fields and want to help, you're welcome). + +## Configuring PostgreSQL + +You can either run PostgreSQL from the machine that runs Plume, or from another server. We recommend you to use the first setup for development environments, or in production for small instances. + +In the first case, just run this command after the PostgreSQL installation, to start it: + +``` +service postgresql start +``` + +If you want to have two separate machines, run these commands on the database server after you installed the dependencies mentionned above on both servers: + +```bash +service postgresql start +su - postgres +createuser -d -P plume +createdb -O plume plume +``` + + +## Running migrations + +Migrations are scripts to update the database. They are run by a tool called Diesel, which can be installed with: + +```bash +cargo install diesel_cli --no-default-features --features postgres --version '=1.2.0' +``` + +Plume should normally run migrations for you when needed, but if you want to run them manually, the command is: + +```bash +diesel migration run --database-url postgres://USER:PASSWORD@IP:PORT/plume +``` + +This command may be useful if you decided to use a separate database server. + +## Starting Plume + +When you launch Plume for the first time, it will ask you a few questions to setup your instance before it actually launches. To start it, run these commands. + +``` +# Optional, only do it if the database URL is not +# postgres://plume:plume@localhost/plume +export DB_URL=postgres://plume:PASSWORD@DBSERVERIP:DBPORT/plume + +cargo run +``` + +## Configuring Nginx + +Here is a sample Nginx configuration for a Plume instance (replace `blog.example.com` with your domain name): + +```nginx +server { + listen 80; + server_name blog.example.com; + + location /.well-known/acme-challenge {} + location / { + return 301 https://$host$request_uri; + } +} + +server { + server_name blog.example.org; + access_log /var/log/nginx/access.log; + + listen [::]:443 ssl; # managed by Certbot + SSLCertificateFile /etc/letsencrypt/live/blog.example.com/cert.pem + SSLCertificateKeyFile /etc/letsencrypt/live/blog.example.com/privkey.pem + SSLCertificateChainFile /etc/letsencrypt/live/blog.example.com/chain.pem + + # for ssl conf: https://cipherli.st/ + ssl_protocols TLSv1.2 TLSv1.3;# Requires nginx >= 1.13.0 else use TLSv1.2 + ssl_prefer_server_ciphers on; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;# openssl dhparam -out /etc/letsencrypt/ssl-dhparam.pem 4096 + ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384; + ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0 + ssl_session_timeout 10m; + ssl_session_cache shared:SSL:10m; + ssl_session_tickets off; # Requires nginx >= 1.5.9 + ssl_stapling on; # Requires nginx >= 1.3.7 + ssl_stapling_verify on; # Requires nginx => 1.3.7 + resolver 9.9.9.9 80.67.169.12 valid=300s; + resolver_timeout 5s; + add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; + add_header X-Frame-Options DENY; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header Content-Security-Policy "default-src 'self';"; + add_header Content-Security-Policy "frame-ancestors 'self'"; + + location ~* \.(jpg|jpeg|png|gif|ico|js|pdf)$ { + add_header Cache-Control "public"; + expires 7d; + } + + location / { + proxy_pass http://localhost:7878/; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + client_max_body_size 10m; + } +} +``` + +## Configuring Apache + +If you prefer Apache, you can use this configuration (here too replace `blog.example.com` with your domain): + +```apache + + ServerName blog.example.com + Redirect / https://blog.example.com/ + + + + ServerAdmin admin@example.com + ServerName blog.example.com + + Header always set Referrer-Policy "strict-origin-when-cross-origin" + Header always set Strict-Transport-Security "max-age=31536000" + SSLEngine on + + # for cipher conf: https://cipherli.st/ + SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH + SSLProtocol All -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 + SSLHonorCipherOrder On + Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" + Header always set X-Frame-Options DENY + Header always set X-Content-Type-Options nosniff + SSLCompression off + SSLUseStapling on + SSLStaplingCache "shmcb:logs/stapling-cache(150000)" + + # Requires Apache >= 2.4.11 + SSLSessionTickets Off + + SSLCertificateFile /etc/letsencrypt/live/blog.example.com/cert.pem + SSLCertificateKeyFile /etc/letsencrypt/live/blog.example.com/privkey.pem + SSLCertificateChainFile /etc/letsencrypt/live/blog.example.com/chain.pem + + ProxyPreserveHost On + RequestHeader set X-Forwarded-Proto "https" + + ProxyPass / http://127.0.0.1:7878/ + ProxyPassReverse / http://127.0.0.1:7878/ + +``` + +## Systemd integration + +If you want to manage your Plume instance with systemd, you can use the following unit file (to be saved in `/etc/systemd/system/plume.service`): + +```toml +[Unit] +Description=plume + +[Service] +Type=simple +User=plume +WorkingDirectory=/home/plume/Plume +ExecStart=/home/plume/.cargo/bin/cargo run +TimeoutSec=30 +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +## SysVinit integration + +This script can also be useful if you are using SysVinit. + +```bash +#!/bin/sh +### BEGIN INIT INFO +# Provides: +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start daemon at boot time +# Description: Federated blogging +# Based on https://raw.githubusercontent.com/fhd/init-script-template/master/template +### END INIT INFO + +dir="/home/plume/Plume" +cmd="/home/plume/.cargo/bin/cargo run" +user="plume" + +name=`basename $0` +pid_file="/var/run/$name.pid" +stdout_log="/home/plume/Plume/plume.log" +stderr_log="/home/plume/Plume/plume.err" + +get_pid() { + cat "$pid_file" +} + +is_running() { + [ -f "$pid_file" ] && ps -p `get_pid` > /dev/null 2>&1 +} + +case "$1" in + start) + if is_running; then + echo "Already started" + else + echo "Starting $name" + cd "$dir" + if [ -z "$user" ]; then + sudo $cmd >> "$stdout_log" 2>> "$stderr_log" & + else + sudo -u "$user" $cmd >> "$stdout_log" 2>> "$stderr_log" & + fi + echo $! > "$pid_file" + if ! is_running; then + echo "Unable to start, see $stdout_log and $stderr_log" + exit 1 + fi + fi + ;; + stop) + if is_running; then + echo -n "Stopping $name.." + kill `get_pid` + for i in 1 2 3 4 5 6 7 8 9 10 + # for i in `seq 10` + do + if ! is_running; then + break + fi + + echo -n "." + sleep 1 + done + echo + + if is_running; then + echo "Not stopped; may still be shutting down or shutdown may have failed" + exit 1 + else + echo "Stopped" + if [ -f "$pid_file" ]; then + rm "$pid_file" + fi + fi + else + echo "Not running" + fi + ;; + restart) + $0 stop + if is_running; then + echo "Unable to stop, will not attempt to start" + exit 1 + fi + $0 start + ;; + status) + if is_running; then + echo "Running" + else + echo "Stopped" + exit 1 + fi + ;; + *) + echo "Usage: $0 {start|stop|restart|status}" + exit 1 + ;; +esac + +exit 0 + +``` + +## Caveats: + +- Pgbouncer is not yet supported (named transactions are used). +- Rust nightly is a moving target, dependancies can break and sometimes you need to check a few versions to find the one working (run `rustup override set nightly-2018-05-15` or `rustup override set nightly-2018-05-31` in the Plume directory if you have issues during the compilation) +- Rust nightly 2018-06-28 is known to be failing to compile diesel 1.3.2 + +## Acknowledgements + +Most of this documentation have been written by *gled-rs*. The systemd unit file, Nginx and Apache configurations have been written by *nonbinaryanargeek*. Some parts (especially the instructions to install native dependencies) are from the [Aardwolf project](https://github.com/Aardwolf-Social/aardwolf). diff --git a/INTERNATIONALIZATION.md b/docs/INTERNATIONALIZATION.md similarity index 100% rename from INTERNATIONALIZATION.md rename to docs/INTERNATIONALIZATION.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..2133ad71 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,6 @@ +# Plume documentation + +- [Installing Plume (for development or production)](INSTALL.md) +- [Updating your instance](UPDATE.md) +- [Development Guide](DEVELOPMENT.md) +- [Making Plume available in your language](INTERNATIONALIZATION.md) diff --git a/docs/UPDATE.md b/docs/UPDATE.md new file mode 100644 index 00000000..4e851e60 --- /dev/null +++ b/docs/UPDATE.md @@ -0,0 +1,15 @@ +# Updating your instance + +To update your instance, run these commands with `plume` user if you created it, or with your default user, in the Plume directory. + +``` +git pull origin master + +# If you are not using systemd +cargo run + +# If you are using systemd +service plume restart +``` + +That's it! diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 00000000..c4192631 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/migrations/2018-09-02-111458_create_medias/down.sql b/migrations/2018-09-02-111458_create_medias/down.sql new file mode 100644 index 00000000..3ba01786 --- /dev/null +++ b/migrations/2018-09-02-111458_create_medias/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE medias; diff --git a/migrations/2018-09-02-111458_create_medias/up.sql b/migrations/2018-09-02-111458_create_medias/up.sql new file mode 100644 index 00000000..f89f448a --- /dev/null +++ b/migrations/2018-09-02-111458_create_medias/up.sql @@ -0,0 +1,10 @@ +-- Your SQL goes here +CREATE TABLE medias ( + id SERIAL PRIMARY KEY, + file_path TEXT NOT NULL DEFAULT '', + alt_text TEXT NOT NULL DEFAULT '', + is_remote BOOLEAN NOT NULL DEFAULT 'f', + remote_url TEXT, + sensitive BOOLEAN NOT NULL DEFAULT 'f', + content_warning TEXT +) diff --git a/migrations/2018-09-02-123623_medias_owner_id/down.sql b/migrations/2018-09-02-123623_medias_owner_id/down.sql new file mode 100644 index 00000000..c44809d6 --- /dev/null +++ b/migrations/2018-09-02-123623_medias_owner_id/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE medias DROP COLUMN owner_id; diff --git a/migrations/2018-09-02-123623_medias_owner_id/up.sql b/migrations/2018-09-02-123623_medias_owner_id/up.sql new file mode 100644 index 00000000..52dfa5c9 --- /dev/null +++ b/migrations/2018-09-02-123623_medias_owner_id/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE medias ADD COLUMN owner_id INTEGER REFERENCES users(id) ON DELETE CASCADE NOT NULL; diff --git a/plume-common/src/activity_pub/inbox.rs b/plume-common/src/activity_pub/inbox.rs index 6ce93c10..4a302a13 100644 --- a/plume-common/src/activity_pub/inbox.rs +++ b/plume-common/src/activity_pub/inbox.rs @@ -29,9 +29,10 @@ pub trait Notify { fn notify(&self, conn: &C); } -pub trait Deletable { - /// true if success - fn delete_activity(conn: &C, id: Id) -> bool; +pub trait Deletable { + fn delete(&self, conn: &C) -> A; + fn delete_id(id: String, conn: &C); + } pub trait WithInbox { diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index 39926d5c..b7511d8d 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -105,7 +105,7 @@ impl FromActivity for Comment { sensitive: false // "sensitive" is not a standard property, we need to think about how to support it with the activitypub crate }); - // save mentions + // save mentionsd if let Some(serde_json::Value::Array(tags)) = note.object_props.tag.clone() { for tag in tags.into_iter() { serde_json::from_value::(tag) diff --git a/plume-models/src/instance.rs b/plume-models/src/instance.rs index 5cb2a3fe..d0d553ed 100644 --- a/plume-models/src/instance.rs +++ b/plume-models/src/instance.rs @@ -72,6 +72,14 @@ impl Instance { .len() > 0 } + pub fn main_admin(&self, conn: &PgConnection) -> User { + users::table.filter(users::instance_id.eq(self.id)) + .filter(users::is_admin.eq(true)) + .limit(1) + .get_result::(conn) + .expect("Couldn't load admins") + } + pub fn compute_box(&self, prefix: &'static str, name: String, box_name: &'static str) -> String { ap_url(format!( "{instance}/{prefix}/{name}/{box_name}", @@ -96,4 +104,8 @@ impl Instance { )).get_result::(conn) .expect("Couldn't update instance") } + + pub fn count(conn: &PgConnection) -> i64 { + instances::table.count().get_result(conn).expect("Couldn't count instances") + } } diff --git a/plume-models/src/lib.rs b/plume-models/src/lib.rs index e0bd5b20..f2c80c62 100644 --- a/plume-models/src/lib.rs +++ b/plume-models/src/lib.rs @@ -78,7 +78,7 @@ fn get_next_id(conn: &PgConnection, seq: &str) -> i32 { // We cant' use currval because it may fail if nextval have never been called before let next = select(nextval(seq)).get_result::(conn).expect("Next ID fail"); if next > 1 { - select(setval(seq, next - 1)).get_result::(conn).expect("Reset ID fail"); + select(setval(seq, next - 1)).get_result::(conn).expect("Reset ID fail"); } next as i32 } @@ -87,7 +87,7 @@ fn get_next_id(conn: &PgConnection, seq: &str) -> i32 { lazy_static! { pub static ref BASE_URL: String = env::var("BASE_URL") .unwrap_or(format!("127.0.0.1:{}", env::var("ROCKET_PORT").unwrap_or(String::from("8000")))); - + pub static ref DB_URL: String = env::var("DB_URL") .unwrap_or(format!("postgres://plume:plume@localhost/{}", env::var("DB_NAME").unwrap_or(String::from("plume")))); @@ -111,6 +111,7 @@ pub mod db_conn; pub mod follows; pub mod instance; pub mod likes; +pub mod medias; pub mod mentions; pub mod notifications; pub mod post_authors; diff --git a/plume-models/src/likes.rs b/plume-models/src/likes.rs index 651b4d8d..f0a3973c 100644 --- a/plume-models/src/likes.rs +++ b/plume-models/src/likes.rs @@ -48,19 +48,6 @@ impl Like { } } - pub fn delete(&self, conn: &PgConnection) -> activity::Undo { - diesel::delete(self).execute(conn).unwrap(); - - let mut act = activity::Undo::default(); - act.undo_props.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).expect("Like::delete: actor error"); - act.undo_props.set_object_object(self.into_activity(conn)).expect("Like::delete: object error"); - act.object_props.set_id_string(format!("{}#delete", self.ap_url)).expect("Like::delete: id error"); - act.object_props.set_to_link(Id::new(PUBLIC_VISIBILTY.to_string())).expect("Like::delete: to error"); - act.object_props.set_cc_link_vec::(vec![]).expect("Like::delete: cc error"); - - act - } - pub fn into_activity(&self, conn: &PgConnection) -> activity::Like { let mut act = activity::Like::default(); act.like_props.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).expect("Like::into_activity: actor error"); @@ -100,13 +87,23 @@ impl Notify for Like { } } -impl Deletable for Like { - fn delete_activity(conn: &PgConnection, id: Id) -> bool { +impl Deletable for Like { + fn delete(&self, conn: &PgConnection) -> activity::Undo { + diesel::delete(self).execute(conn).unwrap(); + + let mut act = activity::Undo::default(); + act.undo_props.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).expect("Like::delete: actor error"); + act.undo_props.set_object_object(self.into_activity(conn)).expect("Like::delete: object error"); + act.object_props.set_id_string(format!("{}#delete", self.ap_url)).expect("Like::delete: id error"); + act.object_props.set_to_link(Id::new(PUBLIC_VISIBILTY.to_string())).expect("Like::delete: to error"); + act.object_props.set_cc_link_vec::(vec![]).expect("Like::delete: cc error"); + + act + } + + fn delete_id(id: String, conn: &PgConnection) { if let Some(like) = Like::find_by_ap_url(conn, id.into()) { like.delete(conn); - true - } else { - false } } } diff --git a/plume-models/src/medias.rs b/plume-models/src/medias.rs new file mode 100644 index 00000000..b656631c --- /dev/null +++ b/plume-models/src/medias.rs @@ -0,0 +1,68 @@ +use diesel::{self, PgConnection, QueryDsl, ExpressionMethods, RunQueryDsl}; +use serde_json; +use std::fs; + +use ap_url; +use instance::Instance; +use schema::medias; + +#[derive(Identifiable, Queryable, Serialize)] +pub struct Media { + pub id: i32, + pub file_path: String, + pub alt_text: String, + pub is_remote: bool, + pub remote_url: Option, + pub sensitive: bool, + pub content_warning: Option, + pub owner_id: i32 +} + +#[derive(Insertable)] +#[table_name = "medias"] +pub struct NewMedia { + pub file_path: String, + pub alt_text: String, + pub is_remote: bool, + pub remote_url: Option, + pub sensitive: bool, + pub content_warning: Option, + pub owner_id: i32 +} + +impl Media { + insert!(medias, NewMedia); + get!(medias); + list_by!(medias, for_user, owner_id as i32); + + pub fn to_json(&self, conn: &PgConnection) -> serde_json::Value { + let mut json = serde_json::to_value(self).unwrap(); + let (preview, html) = match self.file_path.rsplitn(2, '.').next().unwrap() { + "png" | "jpg" | "jpeg" | "gif" => ( + format!("\"{}\"", self.url(conn), self.alt_text, self.alt_text), + format!("\"{}\"", self.url(conn), self.alt_text, self.alt_text) + ), + "mp3" | "wav" | "flac" => ( + format!("", self.url(conn), self.alt_text), + format!("", self.url(conn), self.alt_text) + ), + "mp4" | "avi" | "webm" | "mov" => ( + format!("", self.url(conn), self.alt_text), + format!("", self.url(conn), self.alt_text) + ), + _ => (String::new(), String::new()) + }; + json["html_preview"] = json!(preview); + json["html"] = json!(html); + json + } + + pub fn url(&self, conn: &PgConnection) -> String { + ap_url(format!("{}/static/{}", Instance::get_local(conn).unwrap().public_domain, self.file_path)) + } + + pub fn delete(&self, conn: &PgConnection) { + fs::remove_file(self.file_path.as_str()).expect("Couldn't delete media from disk"); + diesel::delete(self).execute(conn).expect("Couldn't remove media from DB"); + } +} diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index 7ed756f1..5b67a66d 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -1,7 +1,7 @@ use activitypub::{ - activity::Create, + activity::{Create, Delete}, link, - object::Article + object::{Article, Tombstone} }; use chrono::{NaiveDateTime, TimeZone, Utc}; use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods, BelongingToDsl, dsl::any}; @@ -10,7 +10,7 @@ use serde_json; use plume_common::activity_pub::{ PUBLIC_VISIBILTY, Id, IntoId, - inbox::FromActivity + inbox::{Deletable, FromActivity} }; use {BASE_URL, ap_url}; use blogs::Blog; @@ -273,6 +273,27 @@ impl FromActivity for Post { } } +impl Deletable for Post { + fn delete(&self, conn: &PgConnection) -> Delete { + let mut act = Delete::default(); + act.delete_props.set_actor_link(self.get_authors(conn)[0].clone().into_id()).expect("Post::delete: actor error"); + + let mut tombstone = Tombstone::default(); + tombstone.object_props.set_id_string(self.ap_url.clone()).expect("Post::delete: object.id error"); + act.delete_props.set_object_object(tombstone).expect("Post::delete: object error"); + + act.object_props.set_id_string(format!("{}#delete", self.ap_url)).expect("Post::delete: id error"); + act.object_props.set_to_link_vec(vec![Id::new(PUBLIC_VISIBILTY)]).expect("Post::delete: to error"); + + diesel::delete(self).execute(conn).expect("Post::delete: DB error"); + act + } + + fn delete_id(id: String, conn: &PgConnection) { + Post::find_by_ap_url(conn, id).map(|p| p.delete(conn)); + } +} + impl IntoId for Post { fn into_id(self) -> Id { Id::new(self.ap_url.clone()) diff --git a/plume-models/src/reshares.rs b/plume-models/src/reshares.rs index 2acb0a44..734da12e 100644 --- a/plume-models/src/reshares.rs +++ b/plume-models/src/reshares.rs @@ -59,19 +59,6 @@ impl Reshare { User::get(conn, self.user_id) } - pub fn delete(&self, conn: &PgConnection) -> Undo { - diesel::delete(self).execute(conn).unwrap(); - - let mut act = Undo::default(); - act.undo_props.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).unwrap(); - act.undo_props.set_object_object(self.into_activity(conn)).unwrap(); - act.object_props.set_id_string(format!("{}#delete", self.ap_url)).expect("Reshare::delete: id error"); - act.object_props.set_to_link(Id::new(PUBLIC_VISIBILTY.to_string())).expect("Reshare::delete: to error"); - act.object_props.set_cc_link_vec::(vec![]).expect("Reshare::delete: cc error"); - - act - } - pub fn into_activity(&self, conn: &PgConnection) -> Announce { let mut act = Announce::default(); act.announce_props.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).unwrap(); @@ -86,8 +73,8 @@ impl Reshare { impl FromActivity for Reshare { fn from_activity(conn: &PgConnection, announce: Announce, _actor: Id) -> Reshare { - let user = User::from_url(conn, announce.announce_props.actor.as_str().unwrap().to_string()); - let post = Post::find_by_ap_url(conn, announce.announce_props.object.as_str().unwrap().to_string()); + let user = User::from_url(conn, announce.announce_props.actor_link::().expect("Reshare::from_activity: actor error").into()); + let post = Post::find_by_ap_url(conn, announce.announce_props.object_link::().expect("Reshare::from_activity: object error").into()); let reshare = Reshare::insert(conn, NewReshare { post_id: post.unwrap().id, user_id: user.unwrap().id, @@ -111,13 +98,23 @@ impl Notify for Reshare { } } -impl Deletable for Reshare { - fn delete_activity(conn: &PgConnection, id: Id) -> bool { - if let Some(reshare) = Reshare::find_by_ap_url(conn, id.into()) { +impl Deletable for Reshare { + fn delete(&self, conn: &PgConnection) -> Undo { + diesel::delete(self).execute(conn).unwrap(); + + let mut act = Undo::default(); + act.undo_props.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).unwrap(); + act.undo_props.set_object_object(self.into_activity(conn)).unwrap(); + act.object_props.set_id_string(format!("{}#delete", self.ap_url)).expect("Reshare::delete: id error"); + act.object_props.set_to_link(Id::new(PUBLIC_VISIBILTY.to_string())).expect("Reshare::delete: to error"); + act.object_props.set_cc_link_vec::(vec![]).expect("Reshare::delete: cc error"); + + act + } + + fn delete_id(id: String, conn: &PgConnection) { + if let Some(reshare) = Reshare::find_by_ap_url(conn, id) { reshare.delete(conn); - true - } else { - false } } } diff --git a/plume-models/src/schema.rs b/plume-models/src/schema.rs index 79807097..6fa2dc74 100644 --- a/plume-models/src/schema.rs +++ b/plume-models/src/schema.rs @@ -72,6 +72,19 @@ table! { } } +table! { + medias (id) { + id -> Int4, + file_path -> Text, + alt_text -> Text, + is_remote -> Bool, + remote_url -> Nullable, + sensitive -> Bool, + content_warning -> Nullable, + owner_id -> Int4, + } +} + table! { mentions (id) { id -> Int4, @@ -152,6 +165,7 @@ joinable!(comments -> posts (post_id)); joinable!(comments -> users (author_id)); joinable!(likes -> posts (post_id)); joinable!(likes -> users (user_id)); +joinable!(medias -> users (owner_id)); joinable!(mentions -> comments (comment_id)); joinable!(mentions -> posts (post_id)); joinable!(mentions -> users (mentioned_id)); @@ -170,6 +184,7 @@ allow_tables_to_appear_in_same_query!( follows, instances, likes, + medias, mentions, notifications, post_authors, diff --git a/po/de.po b/po/de.po index e6a2030b..860ad194 100644 --- a/po/de.po +++ b/po/de.po @@ -98,7 +98,7 @@ msgid "I don't want to boost this anymore" msgstr "Nicht mehr boosten" msgid "Boost" -msgstr "Boost" +msgstr "Boosten" msgid "Comments" msgstr "Kommentare" @@ -437,22 +437,20 @@ msgstr "Artikel geschrieben haben" msgid "Read the detailed rules" msgstr "Lies die detailierten Regeln" -#~ msgid "One reshare" -#~ msgid_plural "{{ count }} reshares" -#~ msgstr[0] "Ein Reshare" -#~ msgstr[1] "{{ count }} Reshares" +msgid "Delete this article" +msgstr "Artikel löschen" -#~ msgid "Reshare" -#~ msgstr "Resharen" +msgid "And connected to" +msgstr "Verbunden mit" -#~ msgid "You need to be logged in order to reshare a post" -#~ msgstr "Du musst eingeloggt sein, um einen Beitrag zu resharen" +msgid "other instances" +msgstr "anderen Instanzen" -#~ msgid "{{ data }} reshared your article" -#~ msgstr "{{ data }} hat deinen Artikel reshared" +msgid "Administred by" +msgstr "Administriert von" -#~ msgid "{{ user }} reshared your article." -#~ msgstr "{{ user }} hat deinen Artikel reshared." +msgid "Runs Plume {{ version }}" +msgstr "Verwendet Plume {{ version }}" #~ msgid "Your password should be at least 8 characters long" #~ msgstr "Das Passwort sollte mindestens 8 Zeichen lang sein" diff --git a/po/en.po b/po/en.po index e6bbfd88..9b1da45d 100644 --- a/po/en.po +++ b/po/en.po @@ -430,3 +430,18 @@ msgstr "" msgid "Read the detailed rules" msgstr "" + +msgid "Delete this article" +msgstr "" + +msgid "And connected to" +msgstr "" + +msgid "other instances" +msgstr "" + +msgid "Administred by" +msgstr "" + +msgid "Runs Plume {{ version }}" +msgstr "" diff --git a/po/fr.po b/po/fr.po index 687034d8..57c9f3f7 100644 --- a/po/fr.po +++ b/po/fr.po @@ -43,18 +43,18 @@ msgid "Submit comment" msgstr "Envoyer le commentaire" msgid "Something broke on our side." -msgstr "Nous avons cassé quelque chose" +msgstr "Nous avons cassé quelque chose." msgid "Sorry about that. If you think this is a bug, please report it." msgstr "" "Nous sommes désolé⋅e⋅s. Si vous pensez que c'est un bogue, merci de le " -"rapporter." +"signaler." msgid "Configuration" msgstr "Configuration" msgid "Configure your instance" -msgstr "Configurez votre instance" +msgstr "Configurer votre instance" msgid "Name" msgstr "Nom" @@ -92,9 +92,8 @@ msgid_plural "{{ count }} Boosts" msgstr[0] "" msgstr[1] "" -#, fuzzy msgid "I don't want to boost this anymore" -msgstr "Je ne veux plus repartager" +msgstr "Je ne veux plus repartager ceci" msgid "Boost" msgstr "" @@ -106,7 +105,7 @@ msgid "Respond" msgstr "Répondre" msgid "Comment" -msgstr "Commentez" +msgstr "Commenter" msgid "New post" msgstr "Nouvel article" @@ -141,7 +140,7 @@ msgstr "" "un." msgid "Start a new blog" -msgstr "Commencez un nouveau blog" +msgstr "Commencer un nouveau blog" msgid "Admin" msgstr "Administrateur" @@ -150,7 +149,7 @@ msgid "It is you" msgstr "C'est vous" msgid "Edit your profile" -msgstr "Éditez votre profil" +msgstr "Éditer votre profil" msgid "Open on {{ instance_url }}" msgstr "Ouvrir sur {{ instance_url }}" @@ -259,7 +258,8 @@ msgid "You need to be logged in order to edit your profile" msgstr "Vous devez vous connecter pour modifier votre profil" msgid "By {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name | escape }}{{ link_4 }}" -msgstr "De {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name | escape }}{{ link_4 }}" +msgstr "" +"Par {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name | escape }}{{ link_4 }}" #, fuzzy msgid "{{ data }} boosted your article" @@ -278,13 +278,13 @@ msgid "We couldn't find this page." msgstr "Page introuvable." msgid "The link that led you here may be broken." -msgstr "Le lien que vous avez suivi est cassé." +msgstr "Vous avez probablement suivi un lien cassé." msgid "You are not authorized." msgstr "Vous n'avez pas les droits." msgid "You are not author in this blog." -msgstr "Vous n'êtes pas auteur dans ce blog." +msgstr "Vous n'êtes pas auteur⋅ice dans ce blog." msgid "{{ data }} mentioned you." msgstr "{{ data }} vous a mentionné." @@ -302,7 +302,7 @@ msgid "A blog with the same name already exists." msgstr "Un blog avec le même nom existe déjà." msgid "Your comment can't be empty" -msgstr "Votre commentaire ne peux pas être vide" +msgstr "Votre commentaire ne peut pas être vide." msgid "A post with the same title already exists." msgstr "Un article avec le même titre existe déjà." @@ -314,7 +314,7 @@ msgstr "" #, fuzzy msgid "Your password can't be empty" -msgstr "Votre commentaire ne peux pas être vide" +msgstr "Votre mot de passe ne peut pas être vide." msgid "Passwords are not matching" msgstr "Les mots de passe ne correspondent pas." @@ -323,15 +323,15 @@ msgid "Username can't be empty" msgstr "Le nom d'utilisateur ne peut pas être vide." msgid "Invalid email" -msgstr "Adresse mail invalide" +msgstr "Adresse mail invalide." msgid "Password should be at least 8 characters long" msgstr "Le mot de passe doit faire au moins 8 caractères." msgid "One author in this blog: " msgid_plural "{{ count }} authors in this blog: " -msgstr[0] "{{ count }} aut⋅eur⋅rice dans ce blog : " -msgstr[1] "{{ count }} aut⋅eur⋅rice⋅s dans ce blog : " +msgstr[0] "{{ count }} auteur⋅ice dans ce blog : " +msgstr[1] "{{ count }} auteur⋅ice⋅s dans ce blog : " msgid "Login or use your Fediverse account to interact with this article" msgstr "" @@ -344,14 +344,14 @@ msgstr "Optionnel" #, fuzzy msgid "One article in this blog" msgid_plural "{{ count }} articles in this blog" -msgstr[0] "{{ count }} aut⋅eur⋅rice dans ce blog : " -msgstr[1] "{{ count }} aut⋅eur⋅rice⋅s dans ce blog : " +msgstr[0] "{{ count }} article dans ce blog" +msgstr[1] "{{ count }} articles dans ce blog" msgid "Previous page" -msgstr "" +msgstr "Page précédente" msgid "Next page" -msgstr "" +msgstr "Page suivante" msgid "{{ user }} mentioned you." msgstr "{{ user }} vous a mentionné." @@ -365,9 +365,8 @@ msgstr "{{ user }} vous suit" msgid "{{ user }} liked your article." msgstr "{{ user }} a aimé votre article" -#, fuzzy msgid "{{ user }} boosted your article." -msgstr "{{ user }} a commenté votre article" +msgstr "" msgid "Source code" msgstr "Code source" @@ -400,7 +399,7 @@ msgid "Save settings" msgstr "Enregistrer les paramètres" msgid "No comments yet. Be the first to react!" -msgstr "Pas encore de commentaires. Soyez læ premi⋅er⋅ère à réagir !" +msgstr "Pas encore de commentaires. Soyez læ premier⋅ère à réagir !" msgid "About this instance" msgstr "À propos de cette instance" @@ -412,14 +411,15 @@ msgid "Plume is a decentralized blogging engine." msgstr "Plume est un moteur de blog décentralisé." msgid "Authors can manage various blogs from an unique website." -msgstr "Les auteurs peuvent gérer différents blogs au sein d'un même site." +msgstr "" +"Les auteur⋅ice⋅s peuvent gérer différents blogs au sein d'un même site." msgid "" "Articles are also visible on other Plume websites, and you can interact with " "them directly from other platforms like Mastodon." msgstr "" -"Les articles sont également visibles sur d'autres sites Plume et vous pouvez " -"interagir avec eux directement depuis d'autres plateformes telles que " +"Les articles sont également visibles sur d'autres sites Plume, et vous " +"pouvez interagir avec directement depuis d'autres plateformes telles que " "Mastodon." msgid "Create your account" @@ -432,33 +432,34 @@ msgid "Home to" msgstr "Accueille" msgid "people" -msgstr "personnnes" +msgstr "personnes" msgid "Who wrote" -msgstr "Qui ont écrit" +msgstr "Ayant écrit" msgid "articles" msgstr "articles" msgid "Read the detailed rules" -msgstr "Lisez le détail des règles" +msgstr "Lire les règles détaillées" -#~ msgid "One reshare" -#~ msgid_plural "{{ count }} reshares" -#~ msgstr[0] "{{ count }} repartage" -#~ msgstr[1] "{{ count }} repartages" +#, fuzzy +msgid "Delete this article" +msgstr "Derniers articles" -#~ msgid "Reshare" -#~ msgstr "Repartagez" +msgid "And connected to" +msgstr "" -#~ msgid "You need to be logged in order to reshare a post" -#~ msgstr "Vous devez vous connecter pour repartager un article" +#, fuzzy +msgid "other instances" +msgstr "À propos de cette instance" -#~ msgid "{{ data }} reshared your article" -#~ msgstr "{{ data }} a repartagé votre article" +#, fuzzy +msgid "Administred by" +msgstr "Administration" -#~ msgid "{{ user }} reshared your article." -#~ msgstr "{{ user }} a repartagé votre article" +msgid "Runs Plume {{ version }}" +msgstr "" #~ msgid "Your password should be at least 8 characters long" #~ msgstr "Votre mot de passe doit faire au moins 8 caractères." diff --git a/po/gl.po b/po/gl.po index 831aab99..c0c09a0c 100644 --- a/po/gl.po +++ b/po/gl.po @@ -4,7 +4,7 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-06-15 16:33-0700\n" "PO-Revision-Date: 2018-06-15 16:33-0700\n" -"Last-Translator: Automatically generated\n" +"Last-Translator: Xosé M. \n" "Language-Team: none\n" "Language: gl\n" "MIME-Version: 1.0\n" @@ -13,76 +13,77 @@ msgstr "" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" msgid "Latest articles" -msgstr "" +msgstr "Últimos artigos" msgid "No posts to see here yet." -msgstr "" +msgstr "Aínda non hai entradas publicadas" msgid "New article" -msgstr "" +msgstr "Novo artigo" msgid "New blog" -msgstr "" +msgstr "Novo blog" msgid "Create a blog" -msgstr "" +msgstr "Crear un blog" msgid "Title" -msgstr "" +msgstr "Título" msgid "Create blog" -msgstr "" +msgstr "Crear blog" msgid "Comment \"{{ post }}\"" -msgstr "" +msgstr "Comentar \"{{ post }}\"" msgid "Content" -msgstr "" +msgstr "Contido" msgid "Submit comment" -msgstr "" +msgstr "Enviar comentario" msgid "Something broke on our side." -msgstr "" +msgstr "Algo fallou pola nosa parte" msgid "Sorry about that. If you think this is a bug, please report it." -msgstr "" +msgstr "Lamentálmolo. Si cree que é un bug, infórmenos por favor." msgid "Configuration" -msgstr "" +msgstr "Axustes" msgid "Configure your instance" -msgstr "" +msgstr "Configure a súa instancia" msgid "Name" -msgstr "" +msgstr "Nome" msgid "Let's go!" -msgstr "" +msgstr "Imos!" msgid "Welcome on {{ instance_name | escape }}" -msgstr "" +msgstr "Ben vida a {{ instance_name | escape }}" msgid "Notifications" -msgstr "" +msgstr "Notificacións" msgid "" "Written by {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}{{ link_3 }}" msgstr "" +"Escrito por {{ link_1 }}{{ url }}{{ link_2 }}{{ name | escape }}{{ link_3 }}" msgid "This article is under the {{ license }} license." -msgstr "" +msgstr "Este artigo ten licenza {{ license }}" msgid "One like" msgid_plural "{{ count }} likes" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Un gústame" +msgstr[1] "{{ count }} gústame" msgid "I don't like this anymore" -msgstr "" +msgstr "Xa non me gusta" msgid "Add yours" -msgstr "" +msgstr "Engada os seus" msgid "One Boost" msgid_plural "{{ count }} Boosts" @@ -96,330 +97,353 @@ msgid "Boost" msgstr "" msgid "Comments" -msgstr "" +msgstr "Comentarios" msgid "Respond" -msgstr "" +msgstr "Respostar" msgid "Comment" -msgstr "" +msgstr "Comentar" msgid "New post" -msgstr "" +msgstr "Nova entrada" msgid "Create a post" -msgstr "" +msgstr "Crear unha entrada" msgid "Publish" -msgstr "" +msgstr "Publicar" msgid "Login" -msgstr "" +msgstr "Conectar" msgid "Username or email" -msgstr "" +msgstr "Usuaria ou correo-e" msgid "Password" -msgstr "" +msgstr "Contrasinal" msgid "Dashboard" -msgstr "" +msgstr "Taboleiro" msgid "Your Dashboard" -msgstr "" +msgstr "O seu taboleiro" msgid "Your Blogs" -msgstr "" +msgstr "Os seus Blogs" msgid "You don't have any blog yet. Create your own, or ask to join one." -msgstr "" +msgstr "Aínda non ten blogs. Publique un, ou solicita unirse a un." msgid "Start a new blog" -msgstr "" +msgstr "Iniciar un blog" msgid "Admin" -msgstr "" +msgstr "Admin" msgid "It is you" -msgstr "" +msgstr "É vosted" msgid "Edit your profile" -msgstr "" +msgstr "Edite o seu perfil" msgid "Open on {{ instance_url }}" -msgstr "" +msgstr "Abrir en {{ instance_url }}" msgid "Follow" -msgstr "" +msgstr "Seguir" msgid "Unfollow" -msgstr "" +msgstr "Deixar de seguir" msgid "Recently boosted" msgstr "" msgid "One follower" msgid_plural "{{ count }} followers" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Unha seguidora" +msgstr[1] "{{ count }} seguidoras" msgid "Edit your account" -msgstr "" +msgstr "Edite a súa conta" msgid "Your Profile" -msgstr "" +msgstr "O seu perfil" msgid "Display Name" -msgstr "" +msgstr "Nome mostrado" msgid "Email" -msgstr "" +msgstr "Correo-e" msgid "Summary" -msgstr "" +msgstr "Resumen" msgid "Update account" -msgstr "" +msgstr "Actualizar conta" msgid "{{ name | escape }}'s followers" -msgstr "" +msgstr "Seguidoras de {{ name | escape }}" msgid "Followers" -msgstr "" +msgstr "Seguidoras" msgid "New Account" -msgstr "" +msgstr "Nova conta" msgid "Create an account" -msgstr "" +msgstr "Crear unha conta" msgid "Username" -msgstr "" +msgstr "Nome de usuaria" msgid "Password confirmation" -msgstr "" +msgstr "Confirmación do contrasinal" msgid "Create account" -msgstr "" +msgstr "Crear conta" msgid "Plume" -msgstr "" +msgstr "Plume" msgid "Menu" -msgstr "" +msgstr "Menú" msgid "My account" -msgstr "" +msgstr "A miña conta" msgid "Log Out" -msgstr "" +msgstr "Desconectar" msgid "Log In" -msgstr "" +msgstr "Conectar" msgid "Register" -msgstr "" +msgstr "Rexistrar" msgid "You need to be logged in order to create a new blog" -msgstr "" +msgstr "Debe estar conectada para crear un novo blog" msgid "You need to be logged in order to post a comment" -msgstr "" +msgstr "Debe estar conectada para publicar un comentario" msgid "You need to be logged in order to like a post" -msgstr "" +msgstr "Debe estar conectada para gustar unha entrada" msgid "You need to be logged in order to see your notifications" -msgstr "" +msgstr "Debe estar conectada para ver as súas notificacións" msgid "You need to be logged in order to write a new post" -msgstr "" +msgstr "Debe estar conectada para escribir unha nova entrada" msgid "You need to be logged in order to boost a post" msgstr "" msgid "Invalid username or password" -msgstr "" +msgstr "Usuaria ou Contrasinal incorrectos" msgid "You need to be logged in order to access your dashboard" -msgstr "" +msgstr "Debe estar conectada para acceder ao seu taboleiro" msgid "You need to be logged in order to follow someone" -msgstr "" +msgstr "Debe estar conectada para seguir a alguén" msgid "You need to be logged in order to edit your profile" -msgstr "" +msgstr "Debe estar conectada para editar o seu perfil" msgid "By {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name | escape }}{{ link_4 }}" msgstr "" +"Por {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name | escape }}{{ link_4 }}" msgid "{{ data }} boosted your article" msgstr "" msgid "{{ data }} started following you" -msgstr "" +msgstr "{{ data }} comezou a seguila" msgid "{{ data }} liked your article" -msgstr "" +msgstr "{{ data }} gustou do seu artigo" msgid "{{ data }} commented your article" -msgstr "" +msgstr "{{ data }} comentou o seu artigo" msgid "We couldn't find this page." -msgstr "" +msgstr "Non atopamos esta páxina" msgid "The link that led you here may be broken." -msgstr "" +msgstr "A ligazón que a trouxo aquí podería estar quebrado" msgid "You are not authorized." -msgstr "" +msgstr "Non ten permiso." msgid "You are not author in this blog." -msgstr "" +msgstr "Vostede non é autora en este blog." msgid "{{ data }} mentioned you." -msgstr "" +msgstr "{{ data }} mencionouna." msgid "Your comment" -msgstr "" +msgstr "O seu comentario" msgid "Unknown error" -msgstr "" +msgstr "Fallo descoñecido" msgid "Invalid name" -msgstr "" +msgstr "Nome non válido" msgid "A blog with the same name already exists." -msgstr "" +msgstr "Xa existe un blog co mismo nome." msgid "Your comment can't be empty" -msgstr "" +msgstr "O seu comentario non pode estar baldeiro" msgid "A post with the same title already exists." -msgstr "" +msgstr "Xa existe unha entrada co mismo nome." msgid "We need an email or a username to identify you" -msgstr "" +msgstr "Precisamos un correo-e ou un nome de usuaria para identificala" msgid "Your password can't be empty" -msgstr "" +msgstr "O contrasinal non pode estar baldeiro" msgid "Passwords are not matching" -msgstr "" +msgstr "Con coinciden os contrasinais" msgid "Username can't be empty" -msgstr "" +msgstr "O nome de usuaria non pode estar baldeiro" msgid "Invalid email" -msgstr "" +msgstr "Correo-e non válido" msgid "Password should be at least 8 characters long" -msgstr "" +msgstr "O contrasinal debe ter ao menos 8 caracteres" msgid "One author in this blog: " msgid_plural "{{ count }} authors in this blog: " -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Unha autora en este blog: " +msgstr[1] "{{ count }} autoras en este blog: " msgid "Login or use your Fediverse account to interact with this article" msgstr "" +"Conéctese ou utilice a súa conta no fediverso para interactuar con este " +"artigo" msgid "Optional" -msgstr "" +msgstr "Opcional" msgid "One article in this blog" msgid_plural "{{ count }} articles in this blog" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "Un artigo en este blog" +msgstr[1] "{{ count }} artigos en este blog" msgid "Previous page" -msgstr "" +msgstr "Páxina anterior" msgid "Next page" -msgstr "" +msgstr "Páxina seguinte" msgid "{{ user }} mentioned you." -msgstr "" +msgstr "{{ user }} mencionouna." msgid "{{ user }} commented your article." -msgstr "" +msgstr "{{ user }} comentou o artigo." msgid "{{ user }} is now following you." -msgstr "" +msgstr "{{ user }} está a seguila." msgid "{{ user }} liked your article." -msgstr "" +msgstr "{{ user }} gustou do seu artigo." msgid "{{ user }} boosted your article." msgstr "" msgid "Source code" -msgstr "" +msgstr "Código fonte" msgid "Matrix room" -msgstr "" +msgstr "Sala Matrix" msgid "Administration" -msgstr "" +msgstr "Administración" msgid "Instance settings" -msgstr "" +msgstr "Axustes da instancia" msgid "Allow anyone to register" -msgstr "" +msgstr "Permitir o rexistro aberto" msgid "Short description" -msgstr "" +msgstr "Descrición curta" msgid "Markdown is supported" -msgstr "" +msgstr "Escritura Markdown activada" msgid "Long description" -msgstr "" +msgstr "Descrición longa" msgid "Default license" -msgstr "" +msgstr "Licenza por omisión" msgid "Save settings" -msgstr "" +msgstr "Gardar axustes" msgid "No comments yet. Be the first to react!" -msgstr "" +msgstr "Sin comentarios. Sexa a primeira e comentar!" msgid "About this instance" -msgstr "" +msgstr "Sobre esta instancia" msgid "What is Plume?" -msgstr "" +msgstr "Qué é Plume?" msgid "Plume is a decentralized blogging engine." -msgstr "" +msgstr "Plume é un motor de publicación descentralizada." msgid "Authors can manage various blogs from an unique website." -msgstr "" +msgstr "As autoras poden xestionar varios blogs desde un único sitio web." msgid "" "Articles are also visible on other Plume websites, and you can interact with " "them directly from other platforms like Mastodon." msgstr "" +"Os artigos son visibles tamén en outros sitios Plume, e pode interactuar " +"coneles desde outras plataformas como Mastadon." msgid "Create your account" -msgstr "" +msgstr "Cree a súa conta" msgid "About {{ instance_name }}" -msgstr "" +msgstr "Acerca de {{ instance_name }}" msgid "Home to" -msgstr "" +msgstr "Fogar de" msgid "people" -msgstr "" +msgstr "persoas" msgid "Who wrote" -msgstr "" +msgstr "Que escribiron" msgid "articles" -msgstr "" +msgstr "artigos" msgid "Read the detailed rules" +msgstr "Lea o detalle das normas" + +#, fuzzy +msgid "Delete this article" +msgstr "Últimos artigos" + +msgid "And connected to" +msgstr "" + +#, fuzzy +msgid "other instances" +msgstr "Sobre esta instancia" + +#, fuzzy +msgid "Administred by" +msgstr "Administración" + +msgid "Runs Plume {{ version }}" msgstr "" diff --git a/po/nb.po b/po/nb.po index 0506fc02..5f141d6f 100644 --- a/po/nb.po +++ b/po/nb.po @@ -438,6 +438,24 @@ msgstr "artikler" msgid "Read the detailed rules" msgstr "Les reglene" +#, fuzzy +msgid "Delete this article" +msgstr "Siste artikler" + +msgid "And connected to" +msgstr "" + +#, fuzzy +msgid "other instances" +msgstr "Om denne instansen" + +#, fuzzy +msgid "Administred by" +msgstr "Administrasjon" + +msgid "Runs Plume {{ version }}" +msgstr "" + #~ msgid "One reshare" #~ msgid_plural "{{ count }} reshares" #~ msgstr[0] "Én deling" @@ -455,5 +473,6 @@ msgstr "Les reglene" #~ msgid "{{ user }} reshared your article." #~ msgstr "{{ user }} delte artikkelen din med sine følgere." + #~ msgid "Your password should be at least 8 characters long" #~ msgstr "Passordet ditt må bestå av minst åtte tegn" diff --git a/po/pl.po b/po/pl.po index 59298d75..af8f6d5b 100644 --- a/po/pl.po +++ b/po/pl.po @@ -449,6 +449,24 @@ msgstr "artykuły" msgid "Read the detailed rules" msgstr "Przeczytaj szczegółowe zasady" +#, fuzzy +msgid "Delete this article" +msgstr "Najnowsze artykuły" + +msgid "And connected to" +msgstr "" + +#, fuzzy +msgid "other instances" +msgstr "O tej instancji" + +#, fuzzy +msgid "Administred by" +msgstr "Administracja" + +msgid "Runs Plume {{ version }}" +msgstr "" + #~ msgid "One reshare" #~ msgid_plural "{{ count }} reshares" #~ msgstr[0] "Jedno udostępnienie" diff --git a/po/plume.pot b/po/plume.pot index 46971786..486bfefc 100644 --- a/po/plume.pot +++ b/po/plume.pot @@ -420,3 +420,18 @@ msgstr "" msgid "Read the detailed rules" msgstr "" + +msgid "Delete this article" +msgstr "" + +msgid "And connected to" +msgstr "" + +msgid "other instances" +msgstr "" + +msgid "Administred by" +msgstr "" + +msgid "Runs Plume {{ version }}" +msgstr "" diff --git a/src/inbox.rs b/src/inbox.rs index d5b2dcef..5a4d0bbc 100644 --- a/src/inbox.rs +++ b/src/inbox.rs @@ -1,4 +1,4 @@ -use activitypub::activity::{Announce, Create, Like, Undo}; +use activitypub::{activity::{Announce, Create, Delete, Like, Undo}, object::Tombstone}; use diesel::PgConnection; use failure::Error; use serde_json; @@ -32,6 +32,11 @@ pub trait Inbox { Err(InboxError::InvalidType)? } }, + "Delete" => { + let act: Delete = serde_json::from_value(act.clone())?; + Post::delete_id(act.delete_props.object_object::()?.object_props.id_string()?, conn); + Ok(()) + }, "Follow" => { Follow::from_activity(conn, serde_json::from_value(act.clone())?, actor_id); Ok(()) @@ -44,11 +49,11 @@ pub trait Inbox { let act: Undo = serde_json::from_value(act.clone())?; match act.undo_props.object["type"].as_str().unwrap() { "Like" => { - likes::Like::delete_activity(conn, Id::new(act.undo_props.object_object::()?.object_props.id_string()?)); + likes::Like::delete_id(act.undo_props.object_object::()?.object_props.id_string()?, conn); Ok(()) }, "Announce" => { - Reshare::delete_activity(conn, Id::new(act.undo_props.object_object::()?.object_props.id_string()?)); + Reshare::delete_id(act.undo_props.object_object::()?.object_props.id_string()?, conn); Ok(()) } _ => Err(InboxError::CantUndo)? diff --git a/src/main.rs b/src/main.rs index 2e0c76fc..014d811b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,16 @@ #![feature(custom_derive, decl_macro, plugin)] #![plugin(rocket_codegen)] - + extern crate activitypub; +extern crate atom_syndication; extern crate colored; extern crate diesel; extern crate dotenv; extern crate failure; extern crate gettextrs; +extern crate guid_create; extern crate heck; +extern crate multipart; extern crate plume_common; extern crate plume_models; extern crate rocket; @@ -45,6 +48,7 @@ fn main() { routes::blogs::new, routes::blogs::new_auth, routes::blogs::create, + routes::blogs::atom_feed, routes::comments::create, @@ -54,10 +58,18 @@ fn main() { routes::instance::update_settings, routes::instance::shared_inbox, routes::instance::nodeinfo, + routes::instance::about, routes::likes::create, routes::likes::create_auth, + routes::medias::list, + routes::medias::new, + routes::medias::upload, + routes::medias::details, + routes::medias::delete, + routes::medias::static_files, + routes::notifications::paginated_notifications, routes::notifications::notifications, routes::notifications::notifications_auth, @@ -68,6 +80,7 @@ fn main() { routes::posts::new, routes::posts::new_auth, routes::posts::create, + routes::posts::delete, routes::reshares::create, routes::reshares::create_auth, @@ -96,6 +109,7 @@ fn main() { routes::user::ap_followers, routes::user::new, routes::user::create, + routes::user::atom_feed, routes::well_known::host_meta, routes::well_known::nodeinfo, @@ -118,7 +132,7 @@ fn main() { .add_exceptions(vec![ ("/inbox".to_owned(), "/inbox".to_owned(), rocket::http::Method::Post), ("/@//inbox".to_owned(), "/@//inbox".to_owned(), rocket::http::Method::Post), - + ("/medias/new".to_owned(), "/medias/new".to_owned(), rocket::http::Method::Post), // not compatible with multipart/form-data ]) .finalize().unwrap()) .launch(); diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 528e0ae7..f4d783ae 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -1,7 +1,9 @@ use activitypub::collection::OrderedCollection; +use atom_syndication::{Entry, FeedBuilder}; use rocket::{ request::LenientForm, - response::{Redirect, Flash} + response::{Redirect, Flash, content::Content}, + http::ContentType }; use rocket_contrib::Template; use serde_json; @@ -129,3 +131,18 @@ fn outbox(name: String, conn: DbConn) -> ActivityStream { let blog = Blog::find_local(&*conn, name).unwrap(); blog.outbox(&*conn) } + +#[get("/~//atom.xml")] +fn atom_feed(name: String, conn: DbConn) -> Content { + let blog = Blog::find_by_fqn(&*conn, name.clone()).expect("Unable to find blog"); + let feed = FeedBuilder::default() + .title(blog.title.clone()) + .id(Instance::get_local(&*conn).unwrap().compute_box("~", name, "atom.xml")) + .entries(Post::get_recents_for_blog(&*conn, &blog, 15) + .into_iter() + .map(|p| super::post_to_atom(p, &*conn)) + .collect::>()) + .build() + .expect("Error building Atom feed"); + Content(ContentType::new("application", "atom+xml"), feed.to_string()) +} diff --git a/src/routes/instance.rs b/src/routes/instance.rs index cf8ec983..b4f395f6 100644 --- a/src/routes/instance.rs +++ b/src/routes/instance.rs @@ -123,3 +123,16 @@ fn nodeinfo(conn: DbConn) -> Json { "metadata": {} })) } + +#[get("/about")] +fn about(user: User, conn: DbConn) -> Template { + Template::render("instance/about", json!({ + "account": user, + "instance": Instance::get_local(&*conn), + "admin": Instance::get_local(&*conn).map(|i| i.main_admin(&*conn).to_json(&*conn)), + "version": "0.1.0", + "n_users": User::count_local(&*conn), + "n_articles": Post::count_local(&*conn), + "n_instances": Instance::count(&*conn) - 1 + })) +} diff --git a/src/routes/likes.rs b/src/routes/likes.rs index a3a2dd35..8e68ff4d 100644 --- a/src/routes/likes.rs +++ b/src/routes/likes.rs @@ -1,7 +1,7 @@ use rocket::{State, response::{Redirect, Flash}}; use workerpool::{Pool, thunk::*}; -use plume_common::activity_pub::{broadcast, inbox::Notify}; +use plume_common::activity_pub::{broadcast, inbox::{Notify, Deletable}}; use plume_common::utils; use plume_models::{ blogs::Blog, diff --git a/src/routes/medias.rs b/src/routes/medias.rs new file mode 100644 index 00000000..d5e39107 --- /dev/null +++ b/src/routes/medias.rs @@ -0,0 +1,111 @@ +use guid_create::GUID; +use multipart::server::{Multipart, save::{SavedData, SaveResult}}; +use rocket::{Data, http::ContentType, response::{NamedFile, Redirect}}; +use rocket_contrib::Template; +use serde_json; +use std::{fs, path::{Path, PathBuf}}; +use plume_models::{db_conn::DbConn, medias::*, users::User}; + +#[get("/medias")] +fn list(user: User, conn: DbConn) -> Template { + let medias = Media::for_user(&*conn, user.id); + Template::render("medias/index", json!({ + "account": user, + "medias": medias.into_iter().map(|m| m.to_json(&*conn)).collect::>() + })) +} + +#[get("/medias/new")] +fn new(user: User) -> Template { + Template::render("medias/new", json!({ + "account": user, + "form": {}, + "errors": {} + })) +} + +#[post("/medias/new", data = "")] +fn upload(user: User, data: Data, ct: &ContentType, conn: DbConn) -> Redirect { + if ct.is_form_data() { + let (_, boundary) = ct.params().find(|&(k, _)| k == "boundary").expect("No boundary"); + + match Multipart::with_body(data.open(), boundary).save().temp() { + SaveResult::Full(entries) => { + let fields = entries.fields; + + let filename = fields.get(&"file".to_string()).unwrap().into_iter().next().unwrap().headers + .filename.clone() + .unwrap_or("x.png".to_string()); // PNG by default + let ext = filename.rsplitn(2, ".") + .next() + .unwrap(); + let dest = format!("media/{}.{}", GUID::rand().to_string(), ext); + + if let SavedData::Bytes(ref bytes) = fields[&"file".to_string()][0].data { + fs::write(&dest, bytes).expect("Couldn't save upload"); + } else { + if let SavedData::File(ref path, _) = fields[&"file".to_string()][0].data { + fs::copy(path, &dest).expect("Couldn't copy temp upload"); + } else { + println!("not file"); + return Redirect::to(uri!(new)); + } + } + + let has_cw = read(&fields[&"cw".to_string()][0].data).len() > 0; + let media = Media::insert(&*conn, NewMedia { + file_path: dest, + alt_text: read(&fields[&"alt".to_string()][0].data), + is_remote: false, + remote_url: None, + sensitive: has_cw, + content_warning: if has_cw { + Some(read(&fields[&"cw".to_string()][0].data)) + } else { + None + }, + owner_id: user.id + }); + println!("ok"); + Redirect::to(uri!(details: id = media.id)) + }, + SaveResult::Partial(_, _) | SaveResult::Error(_) => { + println!("partial err"); + Redirect::to(uri!(new)) + } + } + } else { + println!("not form data"); + Redirect::to(uri!(new)) + } +} + +fn read(data: &SavedData) -> String { + if let SavedData::Text(s) = data { + s.clone() + } else { + panic!("Field is not a string") + } +} + +#[get("/medias/")] +fn details(id: i32, user: User, conn: DbConn) -> Template { + let media = Media::get(&*conn, id); + Template::render("medias/details", json!({ + "account": user, + "media": media.map(|m| m.to_json(&*conn)) + })) +} + +#[get("/medias//delete")] +fn delete(id: i32, _user: User, conn: DbConn) -> Redirect { + let media = Media::get(&*conn, id).expect("Media to delete not found"); + media.delete(&*conn); + Redirect::to(uri!(list)) +} + +#[get("/static/media/", rank = 1)] +fn static_files(file: PathBuf) -> Option { + NamedFile::open(Path::new("media/").join(file)).ok() +} + diff --git a/src/routes/mod.rs b/src/routes/mod.rs index c46fc2f2..d4442732 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,3 +1,5 @@ +use atom_syndication::{ContentBuilder, Entry, EntryBuilder, LinkBuilder, Person, PersonBuilder}; +use diesel::PgConnection; use rocket::{ http::uri::{FromUriParam, UriDisplay}, response::NamedFile @@ -7,6 +9,8 @@ use std::{ path::{Path, PathBuf} }; +use plume_models::posts::Post; + macro_rules! may_fail { ($account:expr, $expr:expr, $template:expr, $msg:expr, | $res:ident | $block:block) => { { @@ -75,11 +79,31 @@ impl Page { } } +pub fn post_to_atom(post: Post, conn: &PgConnection) -> Entry { + EntryBuilder::default() + .title(post.title.clone()) + .content(ContentBuilder::default() + .value(format!("", *post.content.get())) + .src(post.ap_url.clone()) + .content_type("html".to_string()) + .build().expect("Atom feed: content error")) + .authors(post.get_authors(&*conn) + .into_iter() + .map(|a| PersonBuilder::default() + .name(a.display_name) + .uri(a.ap_url) + .build().expect("Atom feed: author error")) + .collect::>()) + .links(vec![LinkBuilder::default().href(post.ap_url).build().expect("Atom feed: link error")]) + .build().expect("Atom feed: entry error") +} + pub mod blogs; pub mod comments; pub mod errors; pub mod instance; pub mod likes; +pub mod medias; pub mod notifications; pub mod posts; pub mod reshares; @@ -87,7 +111,7 @@ pub mod session; pub mod user; pub mod well_known; -#[get("/static/")] +#[get("/static/", rank = 2)] fn static_files(file: PathBuf) -> Option { NamedFile::open(Path::new("static/").join(file)).ok() } diff --git a/src/routes/posts.rs b/src/routes/posts.rs index 1fd4f4f2..f047ec30 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -8,7 +8,7 @@ use std::{collections::HashMap, borrow::Cow}; use validator::{Validate, ValidationError, ValidationErrors}; use workerpool::{Pool, thunk::*}; -use plume_common::activity_pub::{broadcast, ActivityStream, ApRequest}; +use plume_common::activity_pub::{broadcast, ActivityStream, ApRequest, inbox::Deletable}; use plume_common::utils; use plume_models::{ blogs::*, @@ -53,10 +53,11 @@ fn details_response(blog: String, slug: String, conn: DbConn, user: Option "has_liked": user.clone().map(|u| u.has_liked(&*conn, &post)).unwrap_or(false), "n_reshares": post.get_reshares(&*conn).len(), "has_reshared": user.clone().map(|u| u.has_reshared(&*conn, &post)).unwrap_or(false), - "account": user, + "account": &user, "date": &post.creation_date.timestamp(), "previous": query.and_then(|q| q.responding_to.map(|r| Comment::get(&*conn, r).expect("Error retrieving previous comment").to_json(&*conn, &vec![]))), - "user_fqn": user.map(|u| u.get_fqn(&*conn)).unwrap_or(String::new()) + "user_fqn": user.clone().map(|u| u.get_fqn(&*conn)).unwrap_or(String::new()), + "is_author": user.map(|u| post.get_authors(&*conn).into_iter().any(|a| u.id == a.id)).unwrap_or(false) })) }) }) @@ -176,3 +177,23 @@ fn create(blog_name: String, data: LenientForm, user: User, conn: D }))) } } + +#[get("/~///delete")] +fn delete(blog_name: String, slug: String, conn: DbConn, user: User, worker: State>>) -> Redirect { + let post = Blog::find_by_fqn(&*conn, blog_name.clone()) + .and_then(|blog| Post::find_by_slug(&*conn, slug.clone(), blog.id)); + + if let Some(post) = post { + if !post.get_authors(&*conn).into_iter().any(|a| a.id == user.id) { + Redirect::to(uri!(details: blog = blog_name.clone(), slug = slug.clone())) + } else { + let audience = user.get_followers(&*conn); + let delete_activity = post.delete(&*conn); + worker.execute(Thunk::of(move || broadcast(&user, delete_activity, audience))); + + Redirect::to(uri!(super::blogs::details: name = blog_name)) + } + } else { + Redirect::to(uri!(super::blogs::details: name = blog_name)) + } +} diff --git a/src/routes/reshares.rs b/src/routes/reshares.rs index 1ba673a5..f57efc48 100644 --- a/src/routes/reshares.rs +++ b/src/routes/reshares.rs @@ -1,7 +1,7 @@ use rocket::{State, response::{Redirect, Flash}}; use workerpool::{Pool, thunk::*}; -use plume_common::activity_pub::{broadcast, inbox::Notify}; +use plume_common::activity_pub::{broadcast, inbox::{Deletable, Notify}}; use plume_common::utils; use plume_models::{ blogs::Blog, diff --git a/src/routes/user.rs b/src/routes/user.rs index 2688b38b..2246c05f 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -3,10 +3,12 @@ use activitypub::{ collection::OrderedCollection, object::Article }; +use atom_syndication::{Entry, FeedBuilder}; use rocket::{ State, request::LenientForm, - response::{Redirect, Flash} + response::{Redirect, Flash, Content}, + http::ContentType }; use rocket_contrib::Template; use serde_json; @@ -276,3 +278,18 @@ fn ap_followers(name: String, conn: DbConn, _ap: ApRequest) -> ActivityStream/atom.xml")] +fn atom_feed(name: String, conn: DbConn) -> Content { + let author = User::find_by_fqn(&*conn, name.clone()).expect("Unable to find author"); + let feed = FeedBuilder::default() + .title(author.display_name.clone()) + .id(Instance::get_local(&*conn).unwrap().compute_box("~", name, "atom.xml")) + .entries(Post::get_recents_for_author(&*conn, &author, 15) + .into_iter() + .map(|p| super::post_to_atom(p, &*conn)) + .collect::>()) + .build() + .expect("Error building Atom feed"); + Content(ContentType::new("application", "atom+xml"), feed.to_string()) +} diff --git a/src/setup.rs b/src/setup.rs index 8efd038d..9239196f 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -215,7 +215,7 @@ fn create_admin(instance: Instance, conn: DbConn) { fn check_native_deps() { let mut not_found = Vec::new(); if !try_run("psql") { - not_found.push(("PostgreSQL", "sudo apt install postgres")); + not_found.push(("PostgreSQL", "sudo apt install postgresql")); } if !try_run("gettext") { not_found.push(("GetText", "sudo apt install gettext")) diff --git a/static/images/boxicon-menu.svg b/static/images/boxicon-menu.svg new file mode 100644 index 00000000..0dd7a2ba --- /dev/null +++ b/static/images/boxicon-menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/js/autoExpand.js b/static/js/autoExpand.js new file mode 100644 index 00000000..fbc25e82 --- /dev/null +++ b/static/js/autoExpand.js @@ -0,0 +1,9 @@ +function autosize () { + const el = this + el.style.height = 'auto' + el.style.height = `${el.scrollHeight}px` +} + +const articleContent = document.querySelector('#content') +autosize.bind(articleContent)() +articleContent.addEventListener('keyup', autosize) diff --git a/static/main.css b/static/main.css index 0ed247d8..5d22ce2a 100644 --- a/static/main.css +++ b/static/main.css @@ -24,8 +24,7 @@ a, a:visited { small { margin-left: 1em; - color: #242424; - opacity: 0.6; + color: rgba(36, 36, 36, 0.6); font-size: 0.75em; word-wrap: break-word; word-break: break-all; @@ -42,19 +41,43 @@ small { margin: 4rem 0; } +.banner { + background: #DADADA; + padding-top: 2em; + padding-bottom: 1em; + margin: 3em 0px; +} + /* * == Header == */ header { + background: #ECECEC; +} +header #content { display: flex; align-content: center; justify-content: space-between; - background: #ECECEC; } -header #menu { +header nav#menu { + position: relative; display: none; + transform: skewX(-15deg); + left: -1em; + padding: 1em 1em 1em 2em; + background: #7765E3; + align-self: flex-start; +} +header nav#menu a { + transform: skewX(15deg); + width: 1.75em; + height: 1.75em; + margin: 0; + padding: 0; + background: url("/static/images/boxicon-menu.svg") no-repeat center; + background-size: contain; } header nav { @@ -361,7 +384,7 @@ input:focus, textarea:focus { textarea { resize: vertical; - + font-family: "Lora", serif; font-size: 1.1em; line-height: 1.5em; @@ -422,6 +445,9 @@ form.new-post .title { } form.new-post textarea { min-height: 20em; + overflow-y: hidden; + resize: none; + box-sizing: content-box; } form.new-post input[type="submit"] { background: #ECECEC; @@ -442,6 +468,7 @@ form.new-post input[type="submit"]:hover { background: #DADADA; } flex-direction: row; align-items: center; margin-bottom: 0; + z-index: -20; } .badge { margin-left: 1em; @@ -545,45 +572,94 @@ form.new-post input[type="submit"]:hover { background: #DADADA; } * Small Screens * * ================= */ -@media screen and (max-width: 500px) { - header { - padding: 1em; +@media screen and (max-width: 900px) { + header { flex-direction: column; } - header #menu { - display: flex; + header nav#menu { + display: inline-flex; } - header > nav { + header #content { display: none; text-align: center; } - header nav a, header nav a.title { padding: 0.5em; } - header nav a.title { - width: 100vw; - margin: 0; - border: none; - font-size: 1.75em; + + @keyframes menuOpening { + from { + transform: scaleX(0); + transform-origin: left; + opacity: 0; + } + + to { + transform: scaleX(1); + transform-origin: left; + opacity: 1; + } } - header:focus-within > nav { + header:focus-within #content { + position: fixed; display: flex; flex-direction: column; + justify-content: flex-start; + + top: 0; + left: 0; + width: 100%; + height: 100%; + box-sizing: border-box; + + animation: 0.2s menuOpening; + } + header:focus-within #content::before { + content: ""; + position: absolute; + transform: skewX(-10deg); + top: 0; + left: -20%; + width: 100%; + height: 100%; + + z-index: -10; + + background: #7765E3; + } + header:focus-within #content > nav { + flex-direction: column; + align-items: flex-start; + } + header:focus-within #content > nav a { + margin: 0; + padding: 1rem 1.5rem; + color: #F4F4F4; + font-size: 1.4em; + font-weight: 300; + } + header:focus-within #content > nav a.title { + font-size: 1.8em; + } + header:focus-within #content > nav hr { + display: block; + margin: 0; + width: 100%; + border: solid #F4F4F4 0.1rem; } - body > main > * { + body > main > * { padding: 0 5%; } main .article-meta > * { margin: 0 5%; } - main .article-meta > p { + main .article-meta > p { margin: 2em 5%; font-size: 0.9em; } main .article-meta .comments > * { margin-left: 5%; margin-right: 5%; } - .card { + .card { min-width: 80%; min-height: 80%; } @@ -602,7 +678,8 @@ form.new-post input[type="submit"]:hover { background: #DADADA; } /*== Flex boxes ==*/ .flex { display: flex; - flex-direction: row; + flex-direction: row; + align-items: center; } .flex .grow { @@ -631,3 +708,30 @@ body > footer { body > footer * { margin: 5em 0; } + +/** Medias **/ + +figure { + text-align: center; + margin: 2em; + max-width: 100%; + width: auto; + height: auto; +} + +figure > * { + max-width: 100%; +} + +figcaption { + padding: 1em; +} + +.preview { + display: block; + max-width: 100px; + max-height: 100px; + width: auto; + height: auto; + margin-right: 20px; +} diff --git a/templates/base.html.tera b/templates/base.html.tera index 9a7fc977..d54b8856 100644 --- a/templates/base.html.tera +++ b/templates/base.html.tera @@ -9,24 +9,28 @@
- - -
{% block content %} diff --git a/templates/blogs/details.html.tera b/templates/blogs/details.html.tera index aa4be52f..a07cdb72 100644 --- a/templates/blogs/details.html.tera +++ b/templates/blogs/details.html.tera @@ -23,7 +23,10 @@

-

{{ "Latest articles" | _ }}

+

+ {{ "Latest articles" | _ }} + +

{% if posts | length < 1 %}

{{ "No posts to see here yet." | _ }}

{% endif %} diff --git a/templates/instance/about.html.tera b/templates/instance/about.html.tera new file mode 100644 index 00000000..69a70e30 --- /dev/null +++ b/templates/instance/about.html.tera @@ -0,0 +1,40 @@ +{% extends "base" %} +{% import "macros" as macros %} + +{% block title %} +About {{ instance.name }} +{% endblock title %} + +{% block content %} +

{{ "About {{ instance_name }}" | _(instance_name=instance.name) }}

+
+ {{ instance.short_description_html | safe }} +
+ +
+ {{ instance.long_description_html | safe }} +
+{% endblock content %} diff --git a/templates/medias/details.html.tera b/templates/medias/details.html.tera new file mode 100644 index 00000000..b9f8869f --- /dev/null +++ b/templates/medias/details.html.tera @@ -0,0 +1,24 @@ +{% extends "base" %} +{% import "macros" as macros %} + +{% block title %} +Your medias +{% endblock title %} + +{% block content %} +

{{ "Media details" }}

+
+ Go back to the gallery +
+ +
+
+ {{ media.html | safe }} +
{{ media.alt_text }}
+
+ +
+{% endblock content %} diff --git a/templates/medias/index.html.tera b/templates/medias/index.html.tera new file mode 100644 index 00000000..3e4280fd --- /dev/null +++ b/templates/medias/index.html.tera @@ -0,0 +1,31 @@ +{% extends "base" %} +{% import "macros" as macros %} + +{% block title %} +Your medias +{% endblock title %} + +{% block content %} +

{{ "Your medias" }}

+
+ Upload +
+ +
+ {% if medias | length < 1 %} +

{{ "You don't have any media yet." | _ }}

+ {% endif %} +
+ {% for media in medias %} +
+ {{ media.html_preview | safe }} +
+

{{ media.alt_text }}

+
+ Delete +
+ {% endfor %} +
+ {# macros::paginate(page=page, total=n_pages) #} +
+{% endblock content %} diff --git a/templates/medias/new.html.tera b/templates/medias/new.html.tera new file mode 100644 index 00000000..0c347a3f --- /dev/null +++ b/templates/medias/new.html.tera @@ -0,0 +1,17 @@ +{% extends "base" %} +{% import "macros" as macros %} + +{% block title %} +{{ "Media upload" | _ }} +{% endblock title %} + +{% block content %} +

{{ "Media upload" | _ }}

+
+ {{ macros::input(name="alt", label="Description", errors=errors, form=form, props='required minlength="1"', details='Useful for visually impaired people and licensing') }} + {{ macros::input(name="cw", label="Content warning", errors=errors, form=form, details='Let it empty if there is none') }} + {{ macros::input(name="file", type='file', label="File", errors=errors, form=form, props='required') }} + + +
+{% endblock content %} diff --git a/templates/posts/details.html.tera b/templates/posts/details.html.tera index 1c5cee5a..cfd947e8 100644 --- a/templates/posts/details.html.tera +++ b/templates/posts/details.html.tera @@ -22,6 +22,10 @@ }} — {{ date | date(format="%B %e, %Y") }} + {% if is_author %} + — + {{ "Delete this article" | _ }} + {% endif %}

{{ article.post.content | safe }} diff --git a/templates/posts/new.html.tera b/templates/posts/new.html.tera index 9a4b2972..1892f2f9 100644 --- a/templates/posts/new.html.tera +++ b/templates/posts/new.html.tera @@ -17,11 +17,12 @@ {% endif %} - + {% set license_infos = "Default license will be {{ instance.default_license }}" | _(instance=instance) %} {{ macros::input(name="license", label="License", errors=errors, form=form, optional=true, details=license_infos) }} + {% endblock content %} diff --git a/templates/users/details.html.tera b/templates/users/details.html.tera index 89e79cf2..3939846b 100644 --- a/templates/users/details.html.tera +++ b/templates/users/details.html.tera @@ -18,8 +18,11 @@ {{ user.summary | safe }} - {% if recents | length != 0 %} -

{{ "Latest articles" | _ }}

+ {% if recents | length != 0 %} +

+ {{ "Latest articles" | _ }} + +

{% for article in recents %} {{ macros::post_card(article=article) }}