Merge branch 'master' of https://github.com/Plume-org/Plume into feature/reshare_to_boost

This commit is contained in:
Dominik Pataky 2018-09-03 10:41:37 +02:00
commit 1fc89e8aeb
55 changed files with 1865 additions and 526 deletions

17
.editorconfig Normal file
View file

@ -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

2
.gitignore vendored
View file

@ -6,3 +6,5 @@ rls
translations
po/*.po~
.env
Rocket.toml
media

459
Cargo.lock generated
View file

@ -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)" = "<none>"
"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)" = "<none>"
"checksum rocket_codegen_next 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)" = "<none>"
"checksum rocket_contrib 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)" = "<none>"
"checksum rocket_csrf 0.1.0 (git+https://github.com/fdb-hiroshima/rocket_csrf?branch=plume)" = "<none>"
"checksum rocket_csrf 0.1.0 (git+https://github.com/fdb-hiroshima/rocket_csrf?rev=0fbacab1bc6a32419a18edb600586c5c02fdac4d)" = "<none>"
"checksum rocket_http 0.4.0-dev (git+https://github.com/SergioBenitez/Rocket?rev=df7111143e466c18d1f56377a8d9530a5a306aba)" = "<none>"
"checksum rocket_i18n 0.1.1 (git+https://github.com/BaptisteGelez/rocket_i18n?rev=5b4225d5bed5769482dc926a7e6d6b79f1217be6)" = "<none>"
"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"

View file

@ -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"

View file

@ -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

View file

@ -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 were 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 <POSTGRES INSTALL PATH>/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 distributions
managers.
Next, youll 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

View file

@ -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.

383
docs/INSTALL.md Normal file
View file

@ -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
<VirtualHost *:80>
ServerName blog.example.com
Redirect / https://blog.example.com/
</VirtualHost>
<VirtualHost *:443>
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/
</VirtualHost>
```
## 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).

6
docs/README.md Normal file
View file

@ -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)

15
docs/UPDATE.md Normal file
View file

@ -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!

1
docs/_config.yml Normal file
View file

@ -0,0 +1 @@
theme: jekyll-theme-cayman

View file

@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
DROP TABLE medias;

View file

@ -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
)

View file

@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
ALTER TABLE medias DROP COLUMN owner_id;

View file

@ -0,0 +1,2 @@
-- Your SQL goes here
ALTER TABLE medias ADD COLUMN owner_id INTEGER REFERENCES users(id) ON DELETE CASCADE NOT NULL;

View file

@ -29,9 +29,10 @@ pub trait Notify<C> {
fn notify(&self, conn: &C);
}
pub trait Deletable<C> {
/// true if success
fn delete_activity(conn: &C, id: Id) -> bool;
pub trait Deletable<C, A> {
fn delete(&self, conn: &C) -> A;
fn delete_id(id: String, conn: &C);
}
pub trait WithInbox {

View file

@ -105,7 +105,7 @@ impl FromActivity<Note, PgConnection> 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::<link::Mention>(tag)

View file

@ -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::<User>(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::<Instance>(conn)
.expect("Couldn't update instance")
}
pub fn count(conn: &PgConnection) -> i64 {
instances::table.count().get_result(conn).expect("Couldn't count instances")
}
}

View file

@ -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;

View file

@ -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::<Id>(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<PgConnection> for Like {
}
}
impl Deletable<PgConnection> for Like {
fn delete_activity(conn: &PgConnection, id: Id) -> bool {
impl Deletable<PgConnection, activity::Undo> 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::<Id>(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
}
}
}

View file

@ -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<String>,
pub sensitive: bool,
pub content_warning: Option<String>,
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<String>,
pub sensitive: bool,
pub content_warning: Option<String>,
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!("<img src=\"{}\" alt=\"{}\" title=\"{}\" class=\"preview\">", self.url(conn), self.alt_text, self.alt_text),
format!("<img src=\"{}\" alt=\"{}\" title=\"{}\">", self.url(conn), self.alt_text, self.alt_text)
),
"mp3" | "wav" | "flac" => (
format!("<audio src=\"{}\" title=\"{}\" class=\"preview\"></audio>", self.url(conn), self.alt_text),
format!("<audio src=\"{}\" title=\"{}\"></audio>", self.url(conn), self.alt_text)
),
"mp4" | "avi" | "webm" | "mov" => (
format!("<video src=\"{}\" title=\"{}\" class=\"preview\"></video>", self.url(conn), self.alt_text),
format!("<video src=\"{}\" title=\"{}\"></video>", 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");
}
}

View file

@ -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<Article, PgConnection> for Post {
}
}
impl Deletable<PgConnection, Delete> 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())

View file

@ -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::<Id>(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<Announce, PgConnection> 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::<Id>().expect("Reshare::from_activity: actor error").into());
let post = Post::find_by_ap_url(conn, announce.announce_props.object_link::<Id>().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<PgConnection> for Reshare {
}
}
impl Deletable<PgConnection> for Reshare {
fn delete_activity(conn: &PgConnection, id: Id) -> bool {
if let Some(reshare) = Reshare::find_by_ap_url(conn, id.into()) {
impl Deletable<PgConnection, Undo> 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::<Id>(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
}
}
}

View file

@ -72,6 +72,19 @@ table! {
}
}
table! {
medias (id) {
id -> Int4,
file_path -> Text,
alt_text -> Text,
is_remote -> Bool,
remote_url -> Nullable<Text>,
sensitive -> Bool,
content_warning -> Nullable<Text>,
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,

View file

@ -98,7 +98,7 @@ msgid "I don&#x27;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"

View file

@ -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 ""

View file

@ -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&#x27;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&#x27;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 }} auteur⋅rice dans ce blog : "
msgstr[1] "{{ count }} auteur⋅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æ premier⋅è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."

276
po/gl.po
View file

@ -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. <correo@xmgz.eu>\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&#x27;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&#x27;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&#x27;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&#x27;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 ""

View file

@ -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"

View file

@ -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"

View file

@ -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 ""

View file

@ -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::<Tombstone>()?.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::<Like>()?.object_props.id_string()?));
likes::Like::delete_id(act.undo_props.object_object::<Like>()?.object_props.id_string()?, conn);
Ok(())
},
"Announce" => {
Reshare::delete_activity(conn, Id::new(act.undo_props.object_object::<Announce>()?.object_props.id_string()?));
Reshare::delete_id(act.undo_props.object_object::<Announce>()?.object_props.id_string()?, conn);
Ok(())
}
_ => Err(InboxError::CantUndo)?

View file

@ -2,12 +2,15 @@
#![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),
("/@/<name>/inbox".to_owned(), "/@/<name>/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();

View file

@ -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<OrderedCollection> {
let blog = Blog::find_local(&*conn, name).unwrap();
blog.outbox(&*conn)
}
#[get("/~/<name>/atom.xml")]
fn atom_feed(name: String, conn: DbConn) -> Content<String> {
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::<Vec<Entry>>())
.build()
.expect("Error building Atom feed");
Content(ContentType::new("application", "atom+xml"), feed.to_string())
}

View file

@ -123,3 +123,16 @@ fn nodeinfo(conn: DbConn) -> Json<serde_json::Value> {
"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
}))
}

View file

@ -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,

111
src/routes/medias.rs Normal file
View file

@ -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::<Vec<serde_json::Value>>()
}))
}
#[get("/medias/new")]
fn new(user: User) -> Template {
Template::render("medias/new", json!({
"account": user,
"form": {},
"errors": {}
}))
}
#[post("/medias/new", data = "<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/<id>")]
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/<id>/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/<file..>", rank = 1)]
fn static_files(file: PathBuf) -> Option<NamedFile> {
NamedFile::open(Path::new("media/").join(file)).ok()
}

View file

@ -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!("<![CDATA[{}]]>", *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::<Vec<Person>>())
.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/<file..>")]
#[get("/static/<file..>", rank = 2)]
fn static_files(file: PathBuf) -> Option<NamedFile> {
NamedFile::open(Path::new("static/").join(file)).ok()
}

View file

@ -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<User>
"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<NewPostForm>, user: User, conn: D
})))
}
}
#[get("/~/<blog_name>/<slug>/delete")]
fn delete(blog_name: String, slug: String, conn: DbConn, user: User, worker: State<Pool<ThunkWorker<()>>>) -> 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))
}
}

View file

@ -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,

View file

@ -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<Or
coll.collection_props.set_items_link_vec(followers).expect("Follower collection: items error");
ActivityStream::new(coll)
}
#[get("/@/<name>/atom.xml")]
fn atom_feed(name: String, conn: DbConn) -> Content<String> {
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::<Vec<Entry>>())
.build()
.expect("Error building Atom feed");
Content(ContentType::new("application", "atom+xml"), feed.to_string())
}

View file

@ -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"))

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M3 6h18v2H3zM3 11h18v2H3zM3 16h18v2H3z" fill="#F4F4F4"/></svg>

After

Width:  |  Height:  |  Size: 154 B

9
static/js/autoExpand.js Normal file
View file

@ -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)

View file

@ -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 {
@ -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,31 +572,80 @@ form.new-post input[type="submit"]:hover { background: #DADADA; }
* Small Screens *
* ================= */
@media screen and (max-width: 500px) {
@media screen and (max-width: 900px) {
header {
padding: 1em;
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;
}
header:focus-within > nav {
to {
transform: scaleX(1);
transform-origin: left;
opacity: 1;
}
}
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 > * {
@ -603,6 +679,7 @@ form.new-post input[type="submit"]:hover { background: #DADADA; }
.flex {
display: flex;
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;
}

View file

@ -9,7 +9,10 @@
</head>
<body>
<header>
<nav id="menu"><a href="#">{{ "Menu" | _ }}</a></nav>
<nav id="menu">
<a href="#" aria-label="{{ "Menu" | _ }}" title="{{ "Menu" | _ }}"></a>
</nav>
<div id="content">
<nav>
<a href="/" class="title">{{ "Plume" | _ }}</a>
<hr/>
@ -27,6 +30,7 @@
<a href="/users/new">{{ "Register" | _ }}</a>
{% endif %}
</nav>
</div>
</header>
<main>
{% block content %}

View file

@ -23,7 +23,10 @@
</p>
<section>
<h2>{{ "Latest articles" | _ }}</h2>
<h2>
{{ "Latest articles" | _ }}
<small><a href="/@/{{ blog.fqn }}/atom.xml"><i title="Atom feed" class="fa fa-rss"></i></a></small>
</h2>
{% if posts | length < 1 %}
<p>{{ "No posts to see here yet." | _ }}</p>
{% endif %}

View file

@ -0,0 +1,40 @@
{% extends "base" %}
{% import "macros" as macros %}
{% block title %}
About {{ instance.name }}
{% endblock title %}
{% block content %}
<h1>{{ "About {{ instance_name }}" | _(instance_name=instance.name) }}</h1>
<section>
{{ instance.short_description_html | safe }}
</section>
<div class="banner">
<section class="stats">
<div>
<p>{{ "Home to" | _ }}</p>
<em>{{ n_users }}</em>
<p>{{ "people" | _ }}</p>
</div>
<div>
<p>{{ "Who wrote" | _ }}</p>
<em>{{ n_articles }}</em>
<p>{{ "articles" | _ }}</p>
</div>
<div>
<p>{{ "And connected to" | _ }}</p>
<em>{{ n_instances }}</em>
<p>{{ "other instances" | _ }}</p>
</div>
<div>
<p>{{ "Administred by" | _ }}</p>
<p><a href="/@/{{ admin.fqn }}">{{ admin.name }}</a><small>(@{{ admin.fqn }})</small></p>
</div>
</section>
<p>{{ "Runs Plume {{ version }}" | _(version=version) }}
</div>
<section>
{{ instance.long_description_html | safe }}
</section>
{% endblock content %}

View file

@ -0,0 +1,24 @@
{% extends "base" %}
{% import "macros" as macros %}
{% block title %}
Your medias
{% endblock title %}
{% block content %}
<h1>{{ "Media details" }}</h1>
<section>
<a href="/medias">Go back to the gallery</a>
</section>
<section>
<figure class="media">
{{ media.html | safe }}
<figcaption>{{ media.alt_text }}</figcaption>
</figure>
<div>
<a href="/medias/{{ media.id }}/avatar" class="button inline-block">Use as avatar</a>
<a href="/medias/{{ media.id }}/delete" class="button inline-block">Delete</a>
</div>
</section>
{% endblock content %}

View file

@ -0,0 +1,31 @@
{% extends "base" %}
{% import "macros" as macros %}
{% block title %}
Your medias
{% endblock title %}
{% block content %}
<h1>{{ "Your medias" }}</h1>
<div>
<a href="/medias/new" class="inline-block button">Upload</a>
</div>
<section>
{% if medias | length < 1 %}
<p>{{ "You don't have any media yet." | _ }}</p>
{% endif %}
<div class="list">
{% for media in medias %}
<div class="card flex">
{{ media.html_preview | safe }}
<main class="grow">
<p><a href="/medias/{{ media.id }}">{{ media.alt_text }}</a></p>
</main>
<a href="/medias/{{ media.id }}/delete">Delete</a>
</div>
{% endfor %}
</div>
{# macros::paginate(page=page, total=n_pages) #}
</section>
{% endblock content %}

View file

@ -0,0 +1,17 @@
{% extends "base" %}
{% import "macros" as macros %}
{% block title %}
{{ "Media upload" | _ }}
{% endblock title %}
{% block content %}
<h1>{{ "Media upload" | _ }}</h1>
<form method="post" enctype="multipart/form-data">
{{ 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') }}
<input type="submit" value="{{ "Send" | _ }}"/>
</form>
{% endblock content %}

View file

@ -22,6 +22,10 @@
}}</a></span>
&mdash;
<span class="date">{{ date | date(format="%B %e, %Y") }}</span>
{% if is_author %}
&mdash;
<a href="{{ article.url}}delete">{{ "Delete this article" | _ }}</a>
{% endif %}
</p>
<article>
{{ article.post.content | safe }}

View file

@ -17,11 +17,12 @@
{% endif %}
<label for="content">{{ "Content" | _ }}<small>{{ "Markdown is supported" | _ }}</small></label>
<textarea id="content" name="content" value="{{ form.content | default(value="") }}"></textarea>
<textarea id="content" name="content" value="{{ form.content | default(value="") }}" rows="20"></textarea>
{% 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) }}
<input type="submit" value="{{ "Publish" | _ }}" />
</form>
<script src="/static/js/autoExpand.js"></script>
{% endblock content %}

View file

@ -19,7 +19,10 @@
</div>
{% if recents | length != 0 %}
<h2>{{ "Latest articles" | _ }}</h2>
<h2>
{{ "Latest articles" | _ }}
<small><a href="/@/{{ user.fqn }}/atom.xml"><i title="Atom feed" class="fa fa-rss"></i></a></small>
</h2>
<div class="cards">
{% for article in recents %}
{{ macros::post_card(article=article) }}