Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 39edca5edc | |||
| bce806ac63 | |||
| 3669a0097d | |||
| cc998e7c61 | |||
| 4142e73018 | |||
| 5d03331f0c | |||
| 3198f30515 | |||
| 4f7c20fc26 | |||
| 54c6d21fc5 | |||
| c031804464 | |||
| 5289fe872a |
Generated
+36
-22
@@ -243,7 +243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.0.4"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -370,7 +370,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -382,7 +382,7 @@ name = "cloudabi"
|
|||||||
version = "0.0.3"
|
version = "0.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -659,7 +659,7 @@ name = "devise_core"
|
|||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -670,7 +670,7 @@ name = "diesel"
|
|||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"diesel_derives 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"diesel_derives 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -923,7 +923,7 @@ name = "fsevent"
|
|||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -953,7 +953,7 @@ name = "fuchsia-zircon"
|
|||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1236,7 +1236,7 @@ name = "inotify"
|
|||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@@ -1630,7 +1630,7 @@ name = "nix"
|
|||||||
version = "0.14.1"
|
version = "0.14.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -1656,7 +1656,7 @@ name = "notify"
|
|||||||
version = "4.0.11"
|
version = "4.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"filetime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"filetime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"fsevent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fsevent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -1722,7 +1722,7 @@ name = "openssl"
|
|||||||
version = "0.10.22"
|
version = "0.10.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -1879,6 +1879,7 @@ dependencies = [
|
|||||||
"askama_escape 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"askama_escape 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"atom_syndication 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"atom_syndication 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"diesel 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"diesel 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -1897,7 +1898,7 @@ dependencies = [
|
|||||||
"plume-models 0.3.0",
|
"plume-models 0.3.0",
|
||||||
"rocket 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rocket 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rocket_contrib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rocket_contrib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rocket_csrf 0.1.0 (git+https://github.com/fdb-hiroshima/rocket_csrf?rev=4a72ea2ec716cb0b26188fb00bccf2ef7d1e031c)",
|
"rocket_csrf 0.1.0 (git+https://github.com/Plume-org/rocket_csrf?rev=89ecb380266234f858c651354216bf5bf3cc09b2)",
|
||||||
"rocket_i18n 0.4.0 (git+https://github.com/Plume-org/rocket_i18n?rev=e922afa7c366038b3433278c03b1456b346074f2)",
|
"rocket_i18n 0.4.0 (git+https://github.com/Plume-org/rocket_i18n?rev=e922afa7c366038b3433278c03b1456b346074f2)",
|
||||||
"rpassword 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rpassword 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rsass 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rsass 0.9.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -1910,7 +1911,7 @@ dependencies = [
|
|||||||
"shrinkwraprs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"shrinkwraprs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"validator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"validator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"validator_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"validator_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"webfinger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"webfinger 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1964,6 +1965,9 @@ dependencies = [
|
|||||||
"gettext-macros 0.4.0 (git+https://github.com/Plume-org/gettext-macros/?rev=a7c605f7edd6bfbfbfe7778026bfefd88d82db10)",
|
"gettext-macros 0.4.0 (git+https://github.com/Plume-org/gettext-macros/?rev=a7c605f7edd6bfbfbfe7778026bfefd88d82db10)",
|
||||||
"gettext-utils 0.1.0 (git+https://github.com/Plume-org/gettext-macros/?rev=a7c605f7edd6bfbfbfe7778026bfefd88d82db10)",
|
"gettext-utils 0.1.0 (git+https://github.com/Plume-org/gettext-macros/?rev=a7c605f7edd6bfbfbfe7778026bfefd88d82db10)",
|
||||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"plume-api 0.3.0",
|
||||||
|
"pulldown-cmark 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"stdweb 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"stdweb 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"stdweb-internal-runtime 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"stdweb-internal-runtime 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@@ -2008,7 +2012,7 @@ dependencies = [
|
|||||||
"shrinkwraprs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"shrinkwraprs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"tantivy 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tantivy 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"webfinger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"webfinger 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"whatlang 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"whatlang 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2079,7 +2083,17 @@ name = "pulldown-cmark"
|
|||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pulldown-cmark"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2435,7 +2449,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "rocket_csrf"
|
name = "rocket_csrf"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/fdb-hiroshima/rocket_csrf?rev=4a72ea2ec716cb0b26188fb00bccf2ef7d1e031c#4a72ea2ec716cb0b26188fb00bccf2ef7d1e031c"
|
source = "git+https://github.com/Plume-org/rocket_csrf?rev=89ecb380266234f858c651354216bf5bf3cc09b2#89ecb380266234f858c651354216bf5bf3cc09b2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -2683,7 +2697,7 @@ name = "shrinkwraprs"
|
|||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quote 0.4.2 (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)",
|
"syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -3412,13 +3426,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webfinger"
|
name = "webfinger"
|
||||||
version = "0.3.1"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"reqwest 0.9.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
"reqwest 0.9.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3534,7 +3547,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80"
|
"checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80"
|
||||||
"checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb"
|
"checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb"
|
||||||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||||
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
|
||||||
"checksum bitpacking 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "667f3f518358b2cf64891b46a6dd2eb794e9f80d39f7eb5974f4784bcda9a61b"
|
"checksum bitpacking 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "667f3f518358b2cf64891b46a6dd2eb794e9f80d39f7eb5974f4784bcda9a61b"
|
||||||
"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
|
"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
|
||||||
"checksum blowfish 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aeb80d00f2688459b8542068abd974cfb101e7a82182414a99b5026c0d85cc3"
|
"checksum blowfish 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aeb80d00f2688459b8542068abd974cfb101e7a82182414a99b5026c0d85cc3"
|
||||||
@@ -3728,6 +3741,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||||
"checksum publicsuffix 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5afecba86dcf1e4fd610246f89899d1924fe12e1e89f555eb7c7f710f3c5ad1d"
|
"checksum publicsuffix 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5afecba86dcf1e4fd610246f89899d1924fe12e1e89f555eb7c7f710f3c5ad1d"
|
||||||
"checksum pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eef52fac62d0ea7b9b4dc7da092aa64ea7ec3d90af6679422d3d7e0e14b6ee15"
|
"checksum pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eef52fac62d0ea7b9b4dc7da092aa64ea7ec3d90af6679422d3d7e0e14b6ee15"
|
||||||
|
"checksum pulldown-cmark 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "77043da1282374688ee212dc44b3f37ff929431de9c9adc3053bd3cee5630357"
|
||||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||||
"checksum quick-xml 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8065cbb01701c11cc195cde85cbf39d1c6a80705b67a157ebb3042e0e5777f"
|
"checksum quick-xml 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8065cbb01701c11cc195cde85cbf39d1c6a80705b67a157ebb3042e0e5777f"
|
||||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||||
@@ -3763,7 +3777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum rocket 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "55b83fcf219c8b4980220231d5dd9eae167bdc63449fdab0a04b6c8b8cd361a8"
|
"checksum rocket 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "55b83fcf219c8b4980220231d5dd9eae167bdc63449fdab0a04b6c8b8cd361a8"
|
||||||
"checksum rocket_codegen 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5549dc59a729fbd0e6f5d5de33ba136340228871633485e4946664d36289ffd7"
|
"checksum rocket_codegen 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5549dc59a729fbd0e6f5d5de33ba136340228871633485e4946664d36289ffd7"
|
||||||
"checksum rocket_contrib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5af691b5f5c06c3a30213217696681d3d3bdc2f10428fa3ce6bbaeab156b6409"
|
"checksum rocket_contrib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5af691b5f5c06c3a30213217696681d3d3bdc2f10428fa3ce6bbaeab156b6409"
|
||||||
"checksum rocket_csrf 0.1.0 (git+https://github.com/fdb-hiroshima/rocket_csrf?rev=4a72ea2ec716cb0b26188fb00bccf2ef7d1e031c)" = "<none>"
|
"checksum rocket_csrf 0.1.0 (git+https://github.com/Plume-org/rocket_csrf?rev=89ecb380266234f858c651354216bf5bf3cc09b2)" = "<none>"
|
||||||
"checksum rocket_http 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "abec045da00893bd4eef6084307a4bec0742278a7635a6a8b943da023202a5f7"
|
"checksum rocket_http 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "abec045da00893bd4eef6084307a4bec0742278a7635a6a8b943da023202a5f7"
|
||||||
"checksum rocket_i18n 0.4.0 (git+https://github.com/Plume-org/rocket_i18n?rev=e922afa7c366038b3433278c03b1456b346074f2)" = "<none>"
|
"checksum rocket_i18n 0.4.0 (git+https://github.com/Plume-org/rocket_i18n?rev=e922afa7c366038b3433278c03b1456b346074f2)" = "<none>"
|
||||||
"checksum rpassword 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c34fa7bcae7fca3c8471e8417088bbc3ad9af8066b0ecf4f3c0d98a0d772716e"
|
"checksum rpassword 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c34fa7bcae7fca3c8471e8417088bbc3ad9af8066b0ecf4f3c0d98a0d772716e"
|
||||||
@@ -3870,7 +3884,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
"checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1"
|
"checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1"
|
||||||
"checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3"
|
"checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3"
|
||||||
"checksum webfinger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edc8f298f29f04bf5b6a85d7d448de4f16b7d45807d0a3ec422efcfbf1960519"
|
"checksum webfinger 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ec24b1b0700d4b466d280228ed0f62274eedeaa80206820f071fdc8ed787b664"
|
||||||
"checksum whatlang 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bf61042986b0f0612917b71609b861d695e76c55f9fc81b8a8f8cd363646816c"
|
"checksum whatlang 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bf61042986b0f0612917b71609b861d695e76c55f9fc81b8a8f8cd363646816c"
|
||||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||||
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
|
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
|
||||||
|
|||||||
+4
-3
@@ -8,6 +8,7 @@ repository = "https://github.com/Plume-org/Plume"
|
|||||||
activitypub = "0.1.3"
|
activitypub = "0.1.3"
|
||||||
askama_escape = "0.1"
|
askama_escape = "0.1"
|
||||||
atom_syndication = "0.6"
|
atom_syndication = "0.6"
|
||||||
|
clap = "2.33"
|
||||||
colored = "1.8"
|
colored = "1.8"
|
||||||
dotenv = "0.14"
|
dotenv = "0.14"
|
||||||
gettext = { git = "https://github.com/Plume-org/gettext/", rev = "294c54d74c699fbc66502b480a37cc66c1daa7f3" }
|
gettext = { git = "https://github.com/Plume-org/gettext/", rev = "294c54d74c699fbc66502b480a37cc66c1daa7f3" }
|
||||||
@@ -30,7 +31,7 @@ serde_qs = "0.4"
|
|||||||
shrinkwraprs = "0.2.1"
|
shrinkwraprs = "0.2.1"
|
||||||
validator = "0.8"
|
validator = "0.8"
|
||||||
validator_derive = "0.8"
|
validator_derive = "0.8"
|
||||||
webfinger = "0.3.1"
|
webfinger = "0.4.1"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "plume"
|
name = "plume"
|
||||||
@@ -63,8 +64,8 @@ path = "plume-common"
|
|||||||
path = "plume-models"
|
path = "plume-models"
|
||||||
|
|
||||||
[dependencies.rocket_csrf]
|
[dependencies.rocket_csrf]
|
||||||
git = "https://github.com/fdb-hiroshima/rocket_csrf"
|
git = "https://github.com/Plume-org/rocket_csrf"
|
||||||
rev = "4a72ea2ec716cb0b26188fb00bccf2ef7d1e031c"
|
rev = "89ecb380266234f858c651354216bf5bf3cc09b2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
ructe = "0.6.2"
|
ructe = "0.6.2"
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
DELETE FROM apps WHERE name = 'Plume web interface';
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
-- Your SQL goes here
|
||||||
|
--#!|conn: &Connection, path: &Path| {
|
||||||
|
--#! use plume_common::utils::random_hex;
|
||||||
|
--#!
|
||||||
|
--#! let client_id = random_hex();
|
||||||
|
--#! let client_secret = random_hex();
|
||||||
|
--#! let app = crate::apps::App::insert(
|
||||||
|
--#! &*conn,
|
||||||
|
--#! crate::apps::NewApp {
|
||||||
|
--#! name: "Plume web interface".into(),
|
||||||
|
--#! client_id,
|
||||||
|
--#! client_secret,
|
||||||
|
--#! redirect_uri: None,
|
||||||
|
--#! website: Some("https://joinplu.me".into()),
|
||||||
|
--#! },
|
||||||
|
--#! ).unwrap();
|
||||||
|
--#!
|
||||||
|
--#! for i in 0..=(crate::users::User::count_local(conn).unwrap() as i32 / 20) {
|
||||||
|
--#! if let Ok(page) = crate::users::User::get_local_page(conn, (i * 20, (i + 1) * 20)) {
|
||||||
|
--#! for user in page {
|
||||||
|
--#! crate::api_tokens::ApiToken::insert(
|
||||||
|
--#! conn,
|
||||||
|
--#! crate::api_tokens::NewApiToken {
|
||||||
|
--#! app_id: app.id,
|
||||||
|
--#! user_id: user.id,
|
||||||
|
--#! value: random_hex(),
|
||||||
|
--#! scopes: "read+write".into(),
|
||||||
|
--#! },
|
||||||
|
--#! ).unwrap();
|
||||||
|
--#! }
|
||||||
|
--#! }
|
||||||
|
--#! }
|
||||||
|
--#!
|
||||||
|
--#! Ok(())
|
||||||
|
--#!}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
DELETE FROM apps WHERE name = 'Plume web interface';
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
-- Your SQL goes here
|
||||||
|
--#!|conn: &Connection, path: &Path| {
|
||||||
|
--#! use plume_common::utils::random_hex;
|
||||||
|
--#!
|
||||||
|
--#! let client_id = random_hex();
|
||||||
|
--#! let client_secret = random_hex();
|
||||||
|
--#! let app = crate::apps::App::insert(
|
||||||
|
--#! &*conn,
|
||||||
|
--#! crate::apps::NewApp {
|
||||||
|
--#! name: "Plume web interface".into(),
|
||||||
|
--#! client_id,
|
||||||
|
--#! client_secret,
|
||||||
|
--#! redirect_uri: None,
|
||||||
|
--#! website: Some("https://joinplu.me".into()),
|
||||||
|
--#! },
|
||||||
|
--#! ).unwrap();
|
||||||
|
--#!
|
||||||
|
--#! for i in 0..=(crate::users::User::count_local(conn).unwrap() as i32 / 20) {
|
||||||
|
--#! if let Ok(page) = crate::users::User::get_local_page(conn, (i * 20, (i + 1) * 20)) {
|
||||||
|
--#! for user in page {
|
||||||
|
--#! crate::api_tokens::ApiToken::insert(
|
||||||
|
--#! conn,
|
||||||
|
--#! crate::api_tokens::NewApiToken {
|
||||||
|
--#! app_id: app.id,
|
||||||
|
--#! user_id: user.id,
|
||||||
|
--#! value: random_hex(),
|
||||||
|
--#! scopes: "read+write".into(),
|
||||||
|
--#! },
|
||||||
|
--#! ).unwrap();
|
||||||
|
--#! }
|
||||||
|
--#! }
|
||||||
|
--#! }
|
||||||
|
--#!
|
||||||
|
--#! Ok(())
|
||||||
|
--#!}
|
||||||
@@ -28,4 +28,5 @@ pub struct PostData {
|
|||||||
pub license: String,
|
pub license: String,
|
||||||
pub tags: Vec<String>,
|
pub tags: Vec<String>,
|
||||||
pub cover_id: Option<i32>,
|
pub cover_id: Option<i32>,
|
||||||
|
pub url: String,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,11 @@ fn main() {
|
|||||||
.subcommand(users::command());
|
.subcommand(users::command());
|
||||||
let matches = app.clone().get_matches();
|
let matches = app.clone().get_matches();
|
||||||
|
|
||||||
dotenv::dotenv().ok();
|
match dotenv::dotenv() {
|
||||||
|
Ok(path) => println!("Configuration read from {}", path.display()),
|
||||||
|
Err(ref e) if e.not_found() => eprintln!("no .env was found"),
|
||||||
|
e => e.map(|_| ()).unwrap(),
|
||||||
|
}
|
||||||
let conn = Conn::establish(CONFIG.database_url.as_str());
|
let conn = Conn::establish(CONFIG.database_url.as_str());
|
||||||
let _ = conn.as_ref().map(|conn| Instance::cache_local(conn));
|
let _ = conn.as_ref().map(|conn| Instance::cache_local(conn));
|
||||||
|
|
||||||
|
|||||||
@@ -10,3 +10,9 @@ gettext = { git = "https://github.com/Plume-org/gettext/", rev = "294c54d74c699f
|
|||||||
gettext-macros = { git = "https://github.com/Plume-org/gettext-macros/", rev = "a7c605f7edd6bfbfbfe7778026bfefd88d82db10" }
|
gettext-macros = { git = "https://github.com/Plume-org/gettext-macros/", rev = "a7c605f7edd6bfbfbfe7778026bfefd88d82db10" }
|
||||||
gettext-utils = { git = "https://github.com/Plume-org/gettext-macros/", rev = "a7c605f7edd6bfbfbfe7778026bfefd88d82db10" }
|
gettext-utils = { git = "https://github.com/Plume-org/gettext-macros/", rev = "a7c605f7edd6bfbfbfe7778026bfefd88d82db10" }
|
||||||
lazy_static = "1.3"
|
lazy_static = "1.3"
|
||||||
|
plume-api = { path = "../plume-api" }
|
||||||
|
serde_json = "1.0"
|
||||||
|
|
||||||
|
[dependencies.pulldown-cmark]
|
||||||
|
default-features = false
|
||||||
|
version = "0.5"
|
||||||
|
|||||||
+417
-285
@@ -1,32 +1,212 @@
|
|||||||
|
use pulldown_cmark::{Event, Options, Parser, Tag};
|
||||||
use stdweb::{
|
use stdweb::{
|
||||||
unstable::{TryFrom, TryInto},
|
unstable::{TryFrom, TryInto},
|
||||||
web::{event::*, html_element::*, *},
|
web::{event::*, html_element::*, *},
|
||||||
};
|
};
|
||||||
use CATALOG;
|
use CATALOG;
|
||||||
|
|
||||||
macro_rules! mv {
|
fn from_md(md: &str) {
|
||||||
( $( $var:ident ),* => $exp:expr ) => {
|
let md_parser = Parser::new_ext(md, Options::all());
|
||||||
{
|
md_parser.fold(
|
||||||
$( let $var = $var.clone(); )*
|
document().get_element_by_id("editor-main").unwrap(),
|
||||||
$exp
|
|last_elt, event| {
|
||||||
|
match event {
|
||||||
|
Event::Start(tag) => {
|
||||||
|
let new = match tag {
|
||||||
|
Tag::Paragraph => document().create_element("p").unwrap(),
|
||||||
|
Tag::Rule => document().create_element("hr").unwrap(),
|
||||||
|
Tag::Header(level) => {
|
||||||
|
document().create_element(&format!("h{}", level)).unwrap()
|
||||||
}
|
}
|
||||||
|
Tag::BlockQuote => document().create_element("blockquote").unwrap(),
|
||||||
|
Tag::CodeBlock(code) => {
|
||||||
|
let pre = document().create_element("pre").unwrap();
|
||||||
|
let code_elt = document().create_element("code").unwrap();
|
||||||
|
code_elt.append_child(&document().create_text_node(&code));
|
||||||
|
pre.append_child(&code_elt);
|
||||||
|
pre
|
||||||
|
}
|
||||||
|
Tag::List(None) => document().create_element("ul").unwrap(),
|
||||||
|
Tag::List(Some(_start_index)) => document().create_element("ol").unwrap(), // TODO: handle start_index
|
||||||
|
Tag::Item => document().create_element("li").unwrap(),
|
||||||
|
Tag::FootnoteDefinition(def) => {
|
||||||
|
let note = document().create_element("div").unwrap();
|
||||||
|
note.class_list().add("footnote");
|
||||||
|
note.append_child(&document().create_text_node(&def));
|
||||||
|
note
|
||||||
|
}
|
||||||
|
Tag::HtmlBlock => document().create_element("div").unwrap(),
|
||||||
|
Tag::Table(_alignements) => document().create_element("table").unwrap(), // TODO: handle alignements
|
||||||
|
Tag::TableHead => document().create_element("th").unwrap(),
|
||||||
|
Tag::TableRow => document().create_element("tr").unwrap(),
|
||||||
|
Tag::TableCell => document().create_element("td").unwrap(),
|
||||||
|
Tag::Emphasis => document().create_element("em").unwrap(),
|
||||||
|
Tag::Strong => document().create_element("strong").unwrap(),
|
||||||
|
Tag::Strikethrough => document().create_element("s").unwrap(),
|
||||||
|
Tag::Link(_link_type, url, text) => {
|
||||||
|
let url: &str = &url;
|
||||||
|
let text: &str = &text;
|
||||||
|
let link = document().create_element("a").unwrap();
|
||||||
|
js! {
|
||||||
|
@{&link}.href = @{url};
|
||||||
|
@{&link}.title = @{text};
|
||||||
|
};
|
||||||
|
link
|
||||||
|
}
|
||||||
|
Tag::Image(_link_type, url, text) => {
|
||||||
|
let url: &str = &url;
|
||||||
|
let text: &str = &text;
|
||||||
|
let img = document().create_element("img").unwrap();
|
||||||
|
js! {
|
||||||
|
@{&img}.src = @{url};
|
||||||
|
@{&img}.title = @{text};
|
||||||
|
@{&img}.alt = @{text};
|
||||||
|
};
|
||||||
|
img
|
||||||
|
}
|
||||||
|
};
|
||||||
|
last_elt.append_child(&new);
|
||||||
|
new
|
||||||
|
}
|
||||||
|
Event::End(_) => last_elt.parent_element().unwrap(),
|
||||||
|
Event::Text(text) => {
|
||||||
|
let node = document().create_text_node(&text);
|
||||||
|
last_elt.append_child(&node);
|
||||||
|
last_elt
|
||||||
|
}
|
||||||
|
Event::Code(code) => {
|
||||||
|
let elt = document().create_element("code").unwrap();
|
||||||
|
let content = document().create_text_node(&code);
|
||||||
|
elt.append_child(&content);
|
||||||
|
last_elt.append_child(&elt);
|
||||||
|
last_elt
|
||||||
|
}
|
||||||
|
Event::Html(html) => {
|
||||||
|
// TODO: sanitize it?
|
||||||
|
last_elt.set_attribute("innerHtml", &html);
|
||||||
|
last_elt
|
||||||
|
}
|
||||||
|
Event::InlineHtml(html) => {
|
||||||
|
let elt = document().create_element("span").unwrap();
|
||||||
|
elt.set_attribute("innerHtml", &html);
|
||||||
|
last_elt.append_child(&elt);
|
||||||
|
last_elt
|
||||||
|
}
|
||||||
|
Event::FootnoteReference(reference) => {
|
||||||
|
last_elt // TODO
|
||||||
|
}
|
||||||
|
Event::SoftBreak => {
|
||||||
|
last_elt.append_child(&document().create_element("br").unwrap());
|
||||||
|
last_elt
|
||||||
|
}
|
||||||
|
Event::HardBreak => {
|
||||||
|
last_elt // TODO
|
||||||
|
}
|
||||||
|
Event::TaskListMarker(done) => {
|
||||||
|
last_elt // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
MutationObserver::new(|muts, _obs| {
|
||||||
|
for m in muts {
|
||||||
|
console!(log, "mut!!");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.observe(
|
||||||
|
&document().get_element_by_id("editor-main").unwrap(),
|
||||||
|
MutationObserverInit {
|
||||||
|
child_list: true,
|
||||||
|
attributes: true,
|
||||||
|
character_data: false,
|
||||||
|
subtree: true,
|
||||||
|
attribute_old_value: true,
|
||||||
|
character_data_old_value: false,
|
||||||
|
attribute_filter: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_md() -> String {
|
||||||
|
let root = document().get_element_by_id("editor-main").unwrap();
|
||||||
|
fold_children(&root).join("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold_children(elt: &Element) -> Vec<String> {
|
||||||
|
elt.child_nodes().iter().fold(vec![], |mut blocks, node| {
|
||||||
|
blocks.push(html_to_md(&node));
|
||||||
|
blocks
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn html_to_md(node: &Node) -> String {
|
||||||
|
console!(log, node);
|
||||||
|
if let Ok(elt) = Element::try_from(node.clone()) {
|
||||||
|
console!(log, elt.node_name().to_lowercase());
|
||||||
|
match elt.node_name().to_lowercase().as_ref() {
|
||||||
|
"hr" => "---".into(),
|
||||||
|
"h1" => format!("# {}\n\n", fold_children(&elt).join("")),
|
||||||
|
"h2" => format!("## {}\n\n", fold_children(&elt).join("")),
|
||||||
|
"h3" => format!("### {}\n\n", fold_children(&elt).join("")),
|
||||||
|
"h4" => format!("#### {}\n\n", fold_children(&elt).join("")),
|
||||||
|
"h5" => format!("##### {}\n\n", fold_children(&elt).join("")),
|
||||||
|
"h6" => format!("###### {}\n\n", fold_children(&elt).join("")),
|
||||||
|
"blockquote" => format!("> {}\n\n", fold_children(&elt).join("> ")),
|
||||||
|
"pre" => format!("```\n{}\n```\n\n", node.text_content().unwrap_or_default()),
|
||||||
|
"li" => match elt
|
||||||
|
.parent_element()
|
||||||
|
.unwrap()
|
||||||
|
.node_name()
|
||||||
|
.to_lowercase()
|
||||||
|
.as_ref()
|
||||||
|
{
|
||||||
|
"ol" => format!(
|
||||||
|
"{}. {}\n",
|
||||||
|
elt.parent_element()
|
||||||
|
.unwrap()
|
||||||
|
.child_nodes()
|
||||||
|
.iter()
|
||||||
|
.position(|n| Element::try_from(n).unwrap() == elt)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
fold_children(&elt).join(""),
|
||||||
|
),
|
||||||
|
_ => format!("- {}\n", fold_children(&elt).join("")),
|
||||||
|
},
|
||||||
|
"em" => format!("_{}_", fold_children(&elt).join("")),
|
||||||
|
"strong" => format!("**{}**", fold_children(&elt).join("")),
|
||||||
|
"s" => format!("~~{}~~", fold_children(&elt).join("")),
|
||||||
|
"a" => format!(
|
||||||
|
"[{}]({})",
|
||||||
|
fold_children(&elt).join(""),
|
||||||
|
String::try_from(js! { return @{&elt}.href }).unwrap()
|
||||||
|
),
|
||||||
|
"img" => format!(
|
||||||
|
"",
|
||||||
|
String::try_from(js! { return @{&elt}.alt }).unwrap(),
|
||||||
|
String::try_from(js! { return @{&elt}.src }).unwrap()
|
||||||
|
),
|
||||||
|
other => {
|
||||||
|
console!(log, "Warning: unhandled element:", other);
|
||||||
|
String::new()
|
||||||
|
} // TODO: refs, tables, raw html
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node.text_content().unwrap_or_default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_elt_value(id: &'static str) -> String {
|
fn get_elt_value(id: &'static str) -> String {
|
||||||
let elt = document().get_element_by_id(id).unwrap();
|
let elt = document().get_element_by_id(id).unwrap();
|
||||||
let inp: Result<InputElement, _> = elt.clone().try_into();
|
let inp: Result<InputElement, _> = elt.clone().try_into();
|
||||||
|
let select: Result<SelectElement, _> = elt.clone().try_into();
|
||||||
let textarea: Result<TextAreaElement, _> = elt.try_into();
|
let textarea: Result<TextAreaElement, _> = elt.try_into();
|
||||||
inp.map(|i| i.raw_value())
|
let res = inp.map(|i| i.raw_value()).unwrap_or_else(|_| {
|
||||||
.unwrap_or_else(|_| textarea.unwrap().value())
|
textarea
|
||||||
}
|
.map(|t| t.value())
|
||||||
|
.unwrap_or_else(|_| select.unwrap().value().unwrap_or_default())
|
||||||
fn set_value<S: AsRef<str>>(id: &'static str, val: S) {
|
});
|
||||||
let elt = document().get_element_by_id(id).unwrap();
|
res
|
||||||
let inp: Result<InputElement, _> = elt.clone().try_into();
|
|
||||||
let textarea: Result<TextAreaElement, _> = elt.try_into();
|
|
||||||
inp.map(|i| i.set_raw_value(val.as_ref()))
|
|
||||||
.unwrap_or_else(|_| textarea.unwrap().set_value(val.as_ref()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn no_return(evt: KeyDownEvent) {
|
fn no_return(evt: KeyDownEvent) {
|
||||||
@@ -63,33 +243,7 @@ impl From<stdweb::private::ConversionError> for EditorError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_widget(
|
fn filter_paste(elt: &Element) {
|
||||||
parent: &Element,
|
|
||||||
tag: &'static str,
|
|
||||||
placeholder_text: String,
|
|
||||||
content: String,
|
|
||||||
disable_return: bool,
|
|
||||||
) -> Result<HtmlElement, EditorError> {
|
|
||||||
let widget = placeholder(make_editable(tag).try_into()?, &placeholder_text);
|
|
||||||
if !content.is_empty() {
|
|
||||||
widget.dataset().insert("edited", "true")?;
|
|
||||||
}
|
|
||||||
widget.append_child(&document().create_text_node(&content));
|
|
||||||
if disable_return {
|
|
||||||
widget.add_event_listener(no_return);
|
|
||||||
}
|
|
||||||
|
|
||||||
parent.append_child(&widget);
|
|
||||||
// We need to do that to make sure the placeholder is correctly rendered
|
|
||||||
widget.focus();
|
|
||||||
widget.blur();
|
|
||||||
|
|
||||||
filter_paste(&widget);
|
|
||||||
|
|
||||||
Ok(widget)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn filter_paste(elt: &HtmlElement) {
|
|
||||||
// Only insert text when pasting something
|
// Only insert text when pasting something
|
||||||
js! {
|
js! {
|
||||||
@{&elt}.addEventListener("paste", function (evt) {
|
@{&elt}.addEventListener("paste", function (evt) {
|
||||||
@@ -127,64 +281,69 @@ pub fn init() -> Result<(), EditorError> {
|
|||||||
|
|
||||||
fn init_editor() -> Result<(), EditorError> {
|
fn init_editor() -> Result<(), EditorError> {
|
||||||
if let Some(ed) = document().get_element_by_id("plume-editor") {
|
if let Some(ed) = document().get_element_by_id("plume-editor") {
|
||||||
|
document().body()?.set_attribute("id", "editor")?;
|
||||||
|
|
||||||
|
let aside = document().get_element_by_id("plume-editor-aside")?;
|
||||||
|
|
||||||
// Show the editor
|
// Show the editor
|
||||||
js! { @{&ed}.style.display = "block"; };
|
js! {
|
||||||
|
@{&ed}.style.display = "grid";
|
||||||
|
@{&aside}.style.display = "block";
|
||||||
|
};
|
||||||
// And hide the HTML-only fallback
|
// And hide the HTML-only fallback
|
||||||
let old_ed = document().get_element_by_id("plume-fallback-editor")?;
|
let old_ed = document().get_element_by_id("plume-fallback-editor")?;
|
||||||
let old_title = document().get_element_by_id("plume-editor-title")?;
|
|
||||||
js! {
|
js! {
|
||||||
@{&old_ed}.style.display = "none";
|
@{&old_ed}.style.display = "none";
|
||||||
@{&old_title}.style.display = "none";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get content from the old editor (when editing an article for instance)
|
|
||||||
let title_val = get_elt_value("title");
|
|
||||||
let subtitle_val = get_elt_value("subtitle");
|
|
||||||
let content_val = get_elt_value("editor-content");
|
|
||||||
// And pre-fill the new editor with this values
|
// And pre-fill the new editor with this values
|
||||||
let title = init_widget(&ed, "h1", i18n!(CATALOG, "Title"), title_val, true)?;
|
let title = document().get_element_by_id("editor-title")?;
|
||||||
let subtitle = init_widget(
|
let subtitle = document().get_element_by_id("editor-subtitle")?;
|
||||||
&ed,
|
let source = get_elt_value("editor-content");
|
||||||
"h2",
|
|
||||||
i18n!(CATALOG, "Subtitle, or summary"),
|
|
||||||
subtitle_val,
|
|
||||||
true,
|
|
||||||
)?;
|
|
||||||
let content = init_widget(
|
|
||||||
&ed,
|
|
||||||
"article",
|
|
||||||
i18n!(CATALOG, "Write your article here. Markdown is supported."),
|
|
||||||
content_val.clone(),
|
|
||||||
false,
|
|
||||||
)?;
|
|
||||||
js! { @{&content}.innerHTML = @{content_val}; };
|
|
||||||
|
|
||||||
// character counter
|
setup_toolbar();
|
||||||
content.add_event_listener(mv!(content => move |_: KeyDownEvent| {
|
from_md(&source);
|
||||||
window().set_timeout(mv!(content => move || {
|
|
||||||
if let Some(e) = document().get_element_by_id("char-count") {
|
title.add_event_listener(no_return);
|
||||||
let count = chars_left("#plume-fallback-editor", &content).unwrap_or_default();
|
subtitle.add_event_listener(no_return);
|
||||||
let text = i18n!(CATALOG, "Around {} characters left"; count);
|
|
||||||
HtmlElement::try_from(e).map(|e| {
|
filter_paste(&title);
|
||||||
js!{@{e}.innerText = @{text}};
|
filter_paste(&subtitle);
|
||||||
}).ok();
|
// TODO: filter_paste(&content);
|
||||||
|
|
||||||
|
document()
|
||||||
|
.get_element_by_id("publish")?
|
||||||
|
.add_event_listener(|_: ClickEvent| {
|
||||||
|
let publish_page = document().get_element_by_id("publish-page").unwrap();
|
||||||
|
let options_page = document().get_element_by_id("options-page").unwrap();
|
||||||
|
js! {
|
||||||
|
@{&options_page}.style.display = "none";
|
||||||
|
@{&publish_page}.style.display = "flex";
|
||||||
};
|
};
|
||||||
}), 0);
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
document().get_element_by_id("publish")?.add_event_listener(
|
document()
|
||||||
mv!(title, subtitle, content, old_ed => move |_: ClickEvent| {
|
.get_element_by_id("cancel-publish")?
|
||||||
let popup = document().get_element_by_id("publish-popup").or_else(||
|
.add_event_listener(|_: ClickEvent| {
|
||||||
init_popup(&title, &subtitle, &content, &old_ed).ok()
|
let publish_page = document().get_element_by_id("publish-page").unwrap();
|
||||||
).unwrap();
|
let options_page = document().get_element_by_id("options-page").unwrap();
|
||||||
let bg = document().get_element_by_id("popup-bg").or_else(||
|
js! {
|
||||||
init_popup_bg().ok()
|
@{&publish_page}.style.display = "none";
|
||||||
).unwrap();
|
@{&options_page}.style.display = "flex";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
popup.class_list().add("show").unwrap();
|
document()
|
||||||
bg.class_list().add("show").unwrap();
|
.get_element_by_id("confirm-publish")?
|
||||||
}),
|
.add_event_listener(|_: ClickEvent| {
|
||||||
);
|
save(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
document()
|
||||||
|
.get_element_by_id("save-draft")?
|
||||||
|
.add_event_listener(|_: ClickEvent| {
|
||||||
|
save(true);
|
||||||
|
});
|
||||||
|
|
||||||
show_errors();
|
show_errors();
|
||||||
setup_close_button();
|
setup_close_button();
|
||||||
@@ -192,6 +351,176 @@ fn init_editor() -> Result<(), EditorError> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn select_style(style: &str) {
|
||||||
|
if let Some(select) = document()
|
||||||
|
.get_element_by_id("toolbar-style")
|
||||||
|
.and_then(|e| SelectElement::try_from(e).ok())
|
||||||
|
{
|
||||||
|
select.set_value(Some(style));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_toolbar() {
|
||||||
|
let toolbar = document().get_element_by_id("editor-toolbar").unwrap();
|
||||||
|
|
||||||
|
// List of styles (headings, quote, code, etc)
|
||||||
|
let style_select =
|
||||||
|
SelectElement::try_from(document().create_element("select").unwrap()).unwrap();
|
||||||
|
let options = vec![
|
||||||
|
("p", i18n!(CATALOG, "Paragraph")),
|
||||||
|
("ul", i18n!(CATALOG, "List")),
|
||||||
|
("ol", i18n!(CATALOG, "Ordered list")),
|
||||||
|
("h1", i18n!(CATALOG, "Heading 1")),
|
||||||
|
("h2", i18n!(CATALOG, "Heading 2")),
|
||||||
|
("h3", i18n!(CATALOG, "Heading 3")),
|
||||||
|
("h4", i18n!(CATALOG, "Heading 4")),
|
||||||
|
("h5", i18n!(CATALOG, "Heading 5")),
|
||||||
|
("h6", i18n!(CATALOG, "Heading 6")),
|
||||||
|
("blockquote", i18n!(CATALOG, "Quote")),
|
||||||
|
("pre", i18n!(CATALOG, "Code")),
|
||||||
|
];
|
||||||
|
for (tag, name) in options.clone() {
|
||||||
|
let opt = document().create_element("option").unwrap();
|
||||||
|
opt.set_attribute("value", tag);
|
||||||
|
opt.append_child(&document().create_text_node(&name));
|
||||||
|
style_select.append_child(&opt)
|
||||||
|
}
|
||||||
|
style_select.set_attribute("id", "toolbar-style");
|
||||||
|
|
||||||
|
let options_clone = options.clone();
|
||||||
|
document().add_event_listener(move |_: SelectionChangeEvent| {
|
||||||
|
let block = std::iter::successors(
|
||||||
|
window().get_selection().and_then(|s| s.anchor_node()),
|
||||||
|
|node| {
|
||||||
|
let t = node.node_name().to_lowercase();
|
||||||
|
if options_clone.iter().any(|(tag, _)| *tag == &t) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
node.parent_node()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.last();
|
||||||
|
|
||||||
|
if let Some(b) = block {
|
||||||
|
select_style(&b.node_name().to_lowercase());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
style_select.add_event_listener(move |_: ChangeEvent| {
|
||||||
|
let block = std::iter::successors(
|
||||||
|
window().get_selection().and_then(|s| s.anchor_node()),
|
||||||
|
|node| {
|
||||||
|
let t = node.node_name().to_lowercase();
|
||||||
|
if options.iter().any(|(tag, _)| *tag == &t) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
node.parent_node()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.last();
|
||||||
|
|
||||||
|
if let Some(block) = block {
|
||||||
|
if let Some(select) = document()
|
||||||
|
.get_element_by_id("toolbar-style")
|
||||||
|
.and_then(|e| SelectElement::try_from(e).ok())
|
||||||
|
{
|
||||||
|
let tag = select.value();
|
||||||
|
|
||||||
|
let new = document().create_element(&tag.unwrap_or_default()).unwrap();
|
||||||
|
for ch in block.child_nodes() {
|
||||||
|
block.remove_child(&ch);
|
||||||
|
new.append_child(&ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
block.parent_node().unwrap().replace_child(&new, &block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bold
|
||||||
|
|
||||||
|
// Italics
|
||||||
|
|
||||||
|
// Insert an image
|
||||||
|
|
||||||
|
toolbar.append_child(&style_select);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save(is_draft: bool) {
|
||||||
|
let req = XmlHttpRequest::new();
|
||||||
|
if bool::try_from(js! { return window.editing }).unwrap_or(false) {
|
||||||
|
req.open(
|
||||||
|
"PUT",
|
||||||
|
&format!(
|
||||||
|
"/api/v1/posts/{}",
|
||||||
|
i32::try_from(js! { return window.post_id }).unwrap()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
} else {
|
||||||
|
req.open("POST", "/api/v1/posts").unwrap();
|
||||||
|
}
|
||||||
|
req.set_request_header("Accept", "application/json")
|
||||||
|
.unwrap();
|
||||||
|
req.set_request_header("Content-Type", "application/json")
|
||||||
|
.unwrap();
|
||||||
|
req.set_request_header(
|
||||||
|
"Authorization",
|
||||||
|
&format!(
|
||||||
|
"Bearer {}",
|
||||||
|
String::try_from(js! { return window.api_token }).unwrap()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let req_clone = req.clone();
|
||||||
|
req.add_event_listener(move |_: ProgressLoadEvent| {
|
||||||
|
if let Ok(Some(res)) = req_clone.response_text() {
|
||||||
|
serde_json::from_str(&res)
|
||||||
|
.map(|res: plume_api::posts::PostData| {
|
||||||
|
let url = res.url;
|
||||||
|
js! {
|
||||||
|
window.location.href = @{url};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.map_err(|_| {
|
||||||
|
let json: serde_json::Value = serde_json::from_str(&res).unwrap();
|
||||||
|
window().alert(&format!(
|
||||||
|
"Error: {}",
|
||||||
|
json["error"].as_str().unwrap_or_default()
|
||||||
|
));
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console!(log, to_md());
|
||||||
|
let data = plume_api::posts::NewPostData {
|
||||||
|
title: HtmlElement::try_from(document().get_element_by_id("editor-title").unwrap())
|
||||||
|
.unwrap()
|
||||||
|
.inner_text(),
|
||||||
|
subtitle: document()
|
||||||
|
.get_element_by_id("editor-subtitle")
|
||||||
|
.map(|s| HtmlElement::try_from(s).unwrap().inner_text()),
|
||||||
|
source: to_md(),
|
||||||
|
author: String::new(), // it is ignored anyway (TODO: remove it ??)
|
||||||
|
blog_id: i32::try_from(js! { return window.blog_id }).ok(),
|
||||||
|
published: Some(!is_draft),
|
||||||
|
creation_date: None,
|
||||||
|
license: Some(get_elt_value("license")),
|
||||||
|
tags: Some(
|
||||||
|
get_elt_value("tags")
|
||||||
|
.split(',')
|
||||||
|
.map(|t| t.trim().to_string())
|
||||||
|
.filter(|t| !t.is_empty())
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
cover_id: get_elt_value("cover").parse().ok(),
|
||||||
|
};
|
||||||
|
let json = serde_json::to_string(&data).unwrap();
|
||||||
|
req.send_with_string(&json).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
fn setup_close_button() {
|
fn setup_close_button() {
|
||||||
if let Some(button) = document().get_element_by_id("close-editor") {
|
if let Some(button) = document().get_element_by_id("close-editor") {
|
||||||
button.add_event_listener(|_: ClickEvent| {
|
button.add_event_listener(|_: ClickEvent| {
|
||||||
@@ -223,200 +552,3 @@ fn show_errors() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_popup(
|
|
||||||
title: &HtmlElement,
|
|
||||||
subtitle: &HtmlElement,
|
|
||||||
content: &HtmlElement,
|
|
||||||
old_ed: &Element,
|
|
||||||
) -> Result<Element, EditorError> {
|
|
||||||
let popup = document().create_element("div")?;
|
|
||||||
popup.class_list().add("popup")?;
|
|
||||||
popup.set_attribute("id", "publish-popup")?;
|
|
||||||
|
|
||||||
let tags = get_elt_value("tags")
|
|
||||||
.split(',')
|
|
||||||
.map(str::trim)
|
|
||||||
.map(str::to_string)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let license = get_elt_value("license");
|
|
||||||
make_input(&i18n!(CATALOG, "Tags"), "popup-tags", &popup).set_raw_value(&tags.join(", "));
|
|
||||||
make_input(&i18n!(CATALOG, "License"), "popup-license", &popup).set_raw_value(&license);
|
|
||||||
|
|
||||||
let cover_label = document().create_element("label")?;
|
|
||||||
cover_label.append_child(&document().create_text_node(&i18n!(CATALOG, "Cover")));
|
|
||||||
cover_label.set_attribute("for", "cover")?;
|
|
||||||
let cover = document().get_element_by_id("cover")?;
|
|
||||||
cover.parent_element()?.remove_child(&cover).ok();
|
|
||||||
popup.append_child(&cover_label);
|
|
||||||
popup.append_child(&cover);
|
|
||||||
|
|
||||||
if let Some(draft_checkbox) = document().get_element_by_id("draft") {
|
|
||||||
let draft_label = document().create_element("label")?;
|
|
||||||
draft_label.set_attribute("for", "popup-draft")?;
|
|
||||||
|
|
||||||
let draft = document().create_element("input").unwrap();
|
|
||||||
js! {
|
|
||||||
@{&draft}.id = "popup-draft";
|
|
||||||
@{&draft}.name = "popup-draft";
|
|
||||||
@{&draft}.type = "checkbox";
|
|
||||||
@{&draft}.checked = @{&draft_checkbox}.checked;
|
|
||||||
};
|
|
||||||
|
|
||||||
draft_label.append_child(&draft);
|
|
||||||
draft_label.append_child(&document().create_text_node(&i18n!(CATALOG, "This is a draft")));
|
|
||||||
popup.append_child(&draft_label);
|
|
||||||
}
|
|
||||||
|
|
||||||
let button = document().create_element("input")?;
|
|
||||||
js! {
|
|
||||||
@{&button}.type = "submit";
|
|
||||||
@{&button}.value = @{i18n!(CATALOG, "Publish")};
|
|
||||||
};
|
|
||||||
button.append_child(&document().create_text_node(&i18n!(CATALOG, "Publish")));
|
|
||||||
button.add_event_listener(
|
|
||||||
mv!(title, subtitle, content, old_ed => move |_: ClickEvent| {
|
|
||||||
title.focus(); // Remove the placeholder before publishing
|
|
||||||
set_value("title", title.inner_text());
|
|
||||||
subtitle.focus();
|
|
||||||
set_value("subtitle", subtitle.inner_text());
|
|
||||||
content.focus();
|
|
||||||
set_value("editor-content", content.child_nodes().iter().fold(String::new(), |md, ch| {
|
|
||||||
let to_append = match ch.node_type() {
|
|
||||||
NodeType::Element => {
|
|
||||||
if js!{ return @{&ch}.tagName; } == "DIV" {
|
|
||||||
(js!{ return @{&ch}.innerHTML; }).try_into().unwrap_or_default()
|
|
||||||
} else {
|
|
||||||
(js!{ return @{&ch}.outerHTML; }).try_into().unwrap_or_default()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
NodeType::Text => ch.node_value().unwrap_or_default(),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
format!("{}\n\n{}", md, to_append)
|
|
||||||
}));
|
|
||||||
set_value("tags", get_elt_value("popup-tags"));
|
|
||||||
if let Some(draft) = document().get_element_by_id("popup-draft") {
|
|
||||||
js!{
|
|
||||||
document.getElementById("draft").checked = @{draft}.checked;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let cover = document().get_element_by_id("cover").unwrap();
|
|
||||||
cover.parent_element().unwrap().remove_child(&cover).ok();
|
|
||||||
old_ed.append_child(&cover);
|
|
||||||
set_value("license", get_elt_value("popup-license"));
|
|
||||||
js! {
|
|
||||||
@{&old_ed}.submit();
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
popup.append_child(&button);
|
|
||||||
|
|
||||||
document().body()?.append_child(&popup);
|
|
||||||
Ok(popup)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_popup_bg() -> Result<Element, EditorError> {
|
|
||||||
let bg = document().create_element("div")?;
|
|
||||||
bg.class_list().add("popup-bg")?;
|
|
||||||
bg.set_attribute("id", "popup-bg")?;
|
|
||||||
|
|
||||||
document().body()?.append_child(&bg);
|
|
||||||
bg.add_event_listener(|_: ClickEvent| close_popup());
|
|
||||||
Ok(bg)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn chars_left(selector: &str, content: &HtmlElement) -> Option<i32> {
|
|
||||||
match document().query_selector(selector) {
|
|
||||||
Ok(Some(form)) => HtmlElement::try_from(form).ok().and_then(|form| {
|
|
||||||
if let Some(len) = form
|
|
||||||
.get_attribute("content-size")
|
|
||||||
.and_then(|s| s.parse::<i32>().ok())
|
|
||||||
{
|
|
||||||
(js! {
|
|
||||||
let x = encodeURIComponent(@{content}.innerHTML)
|
|
||||||
.replace(/%20/g, "+")
|
|
||||||
.replace(/%0A/g, "%0D%0A")
|
|
||||||
.replace(new RegExp("[!'*()]", "g"), "XXX") // replace exceptions of encodeURIComponent with placeholder
|
|
||||||
.length + 2;
|
|
||||||
console.log(x);
|
|
||||||
return x;
|
|
||||||
})
|
|
||||||
.try_into()
|
|
||||||
.map(|c: i32| len - c)
|
|
||||||
.ok()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn close_popup() {
|
|
||||||
let hide = |x: Element| x.class_list().remove("show");
|
|
||||||
document().get_element_by_id("publish-popup").map(hide);
|
|
||||||
document().get_element_by_id("popup-bg").map(hide);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_input(label_text: &str, name: &'static str, form: &Element) -> InputElement {
|
|
||||||
let label = document().create_element("label").unwrap();
|
|
||||||
label.append_child(&document().create_text_node(label_text));
|
|
||||||
label.set_attribute("for", name).unwrap();
|
|
||||||
|
|
||||||
let inp: InputElement = document()
|
|
||||||
.create_element("input")
|
|
||||||
.unwrap()
|
|
||||||
.try_into()
|
|
||||||
.unwrap();
|
|
||||||
inp.set_attribute("name", name).unwrap();
|
|
||||||
inp.set_attribute("id", name).unwrap();
|
|
||||||
|
|
||||||
form.append_child(&label);
|
|
||||||
form.append_child(&inp);
|
|
||||||
inp
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_editable(tag: &'static str) -> Element {
|
|
||||||
let elt = document()
|
|
||||||
.create_element(tag)
|
|
||||||
.expect("Couldn't create editable element");
|
|
||||||
elt.set_attribute("contenteditable", "true")
|
|
||||||
.expect("Couldn't make the element editable");
|
|
||||||
elt
|
|
||||||
}
|
|
||||||
|
|
||||||
fn placeholder(elt: HtmlElement, text: &str) -> HtmlElement {
|
|
||||||
elt.dataset().insert("placeholder", text).unwrap();
|
|
||||||
elt.dataset().insert("edited", "false").unwrap();
|
|
||||||
|
|
||||||
elt.add_event_listener(mv!(elt => move |_: FocusEvent| {
|
|
||||||
if elt.dataset().get("edited").unwrap().as_str() != "true" {
|
|
||||||
clear_children(&elt);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
elt.add_event_listener(mv!(elt => move |_: BlurEvent| {
|
|
||||||
if elt.dataset().get("edited").unwrap().as_str() != "true" {
|
|
||||||
clear_children(&elt);
|
|
||||||
|
|
||||||
let ph = document().create_element("span").expect("Couldn't create placeholder");
|
|
||||||
ph.class_list().add("placeholder").expect("Couldn't add class");
|
|
||||||
ph.append_child(&document().create_text_node(&elt.dataset().get("placeholder").unwrap_or_default()));
|
|
||||||
elt.append_child(&ph);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
elt.add_event_listener(mv!(elt => move |_: KeyUpEvent| {
|
|
||||||
elt.dataset().insert("edited", if elt.inner_text().trim_matches('\n').is_empty() {
|
|
||||||
"false"
|
|
||||||
} else {
|
|
||||||
"true"
|
|
||||||
}).expect("Couldn't update edition state");
|
|
||||||
}));
|
|
||||||
elt
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear_children(elt: &HtmlElement) {
|
|
||||||
for child in elt.child_nodes() {
|
|
||||||
elt.remove_child(&child).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ extern crate gettext;
|
|||||||
extern crate gettext_macros;
|
extern crate gettext_macros;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
extern crate pulldown_cmark;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate stdweb;
|
extern crate stdweb;
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
use stdweb::web::{event::*, *};
|
use stdweb::web::{event::*, *};
|
||||||
|
|
||||||
@@ -56,6 +58,18 @@ lazy_static! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
std::panic::set_hook(Box::new(|info: &std::panic::PanicInfo| {
|
||||||
|
let mut msg = info.to_string();
|
||||||
|
msg.push_str("\n\nStack:\n\n");
|
||||||
|
let e = error::Error::new("Panicked");
|
||||||
|
let stack = js! { return @{&e}.stack; }
|
||||||
|
.into_string()
|
||||||
|
.unwrap_or_default();
|
||||||
|
msg.push_str(&stack);
|
||||||
|
msg.push_str("\n\n");
|
||||||
|
console!(error, msg);
|
||||||
|
}));
|
||||||
|
|
||||||
menu();
|
menu();
|
||||||
search();
|
search();
|
||||||
editor::init()
|
editor::init()
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ serde_derive = "1.0"
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
tantivy = "0.9.1"
|
tantivy = "0.9.1"
|
||||||
url = "1.7"
|
url = "1.7"
|
||||||
webfinger = "0.3.1"
|
webfinger = "0.4.1"
|
||||||
whatlang = "0.7.1"
|
whatlang = "0.7.1"
|
||||||
shrinkwraprs = "0.2.1"
|
shrinkwraprs = "0.2.1"
|
||||||
diesel-derive-newtype = "0.1.2"
|
diesel-derive-newtype = "0.1.2"
|
||||||
|
|||||||
@@ -44,6 +44,18 @@ impl ApiToken {
|
|||||||
get!(api_tokens);
|
get!(api_tokens);
|
||||||
insert!(api_tokens, NewApiToken);
|
insert!(api_tokens, NewApiToken);
|
||||||
find_by!(api_tokens, find_by_value, value as &str);
|
find_by!(api_tokens, find_by_value, value as &str);
|
||||||
|
find_by!(
|
||||||
|
api_tokens,
|
||||||
|
find_by_app_and_user,
|
||||||
|
app_id as i32,
|
||||||
|
user_id as i32
|
||||||
|
);
|
||||||
|
|
||||||
|
/// The token for Plume's front-end
|
||||||
|
pub fn web_token(conn: &crate::Connection, user_id: i32) -> Result<ApiToken> {
|
||||||
|
let app = crate::apps::App::find_by_name(conn, "Plume web interface")?;
|
||||||
|
Self::find_by_app_and_user(conn, app.id, user_id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn can(&self, what: &'static str, scope: &'static str) -> bool {
|
pub fn can(&self, what: &'static str, scope: &'static str) -> bool {
|
||||||
let full_scope = what.to_owned() + ":" + scope;
|
let full_scope = what.to_owned() + ":" + scope;
|
||||||
|
|||||||
@@ -29,4 +29,5 @@ impl App {
|
|||||||
get!(apps);
|
get!(apps);
|
||||||
insert!(apps, NewApp);
|
insert!(apps, NewApp);
|
||||||
find_by!(apps, find_by_client_id, client_id as &str);
|
find_by!(apps, find_by_client_id, client_id as &str);
|
||||||
|
find_by!(apps, find_by_name, name as &str);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ impl Blog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_from_webfinger(c: &PlumeRocket, acct: &str) -> Result<Blog> {
|
fn fetch_from_webfinger(c: &PlumeRocket, acct: &str) -> Result<Blog> {
|
||||||
resolve(acct.to_owned(), true)?
|
resolve_with_prefix(Prefix::Group, acct.to_owned(), true)?
|
||||||
.links
|
.links
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.find(|l| l.mime_type == Some(String::from("application/activity+json")))
|
.find(|l| l.mime_type == Some(String::from("application/activity+json")))
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ pub enum Error {
|
|||||||
Signature,
|
Signature,
|
||||||
Unauthorized,
|
Unauthorized,
|
||||||
Url,
|
Url,
|
||||||
|
Validation(String),
|
||||||
Webfinger,
|
Webfinger,
|
||||||
Expired,
|
Expired,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -707,7 +707,7 @@ impl User {
|
|||||||
mime_type: None,
|
mime_type: None,
|
||||||
href: None,
|
href: None,
|
||||||
template: Some(format!(
|
template: Some(format!(
|
||||||
"{}/remote_interact?{{uri}}",
|
"https://{}/remote_interact?{{uri}}",
|
||||||
self.get_instance(conn)?.public_domain
|
self.get_instance(conn)?.public_domain
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
|
|||||||
+468
-498
File diff suppressed because it is too large
Load Diff
+434
-461
File diff suppressed because it is too large
Load Diff
+446
-476
File diff suppressed because it is too large
Load Diff
+468
-498
File diff suppressed because it is too large
Load Diff
+472
-502
File diff suppressed because it is too large
Load Diff
+411
-438
File diff suppressed because it is too large
Load Diff
+416
-443
File diff suppressed because it is too large
Load Diff
+466
-496
File diff suppressed because it is too large
Load Diff
+486
-516
File diff suppressed because it is too large
Load Diff
+471
-501
File diff suppressed because it is too large
Load Diff
+447
-474
File diff suppressed because it is too large
Load Diff
+436
-463
File diff suppressed because it is too large
Load Diff
+465
-495
File diff suppressed because it is too large
Load Diff
+475
-505
File diff suppressed because it is too large
Load Diff
+488
-518
File diff suppressed because it is too large
Load Diff
+467
-497
File diff suppressed because it is too large
Load Diff
+397
-417
@@ -73,11 +73,11 @@ msgid "Your blog information have been updated."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/comments.rs:97
|
# src/routes/comments.rs:97
|
||||||
msgid "Your comment has been posted."
|
msgid "Your comment have been posted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/comments.rs:172
|
# src/routes/comments.rs:172
|
||||||
msgid "Your comment has been deleted."
|
msgid "Your comment have been deleted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/instance.rs:134
|
# src/routes/instance.rs:134
|
||||||
@@ -109,7 +109,7 @@ msgid "You are not allowed to delete this media."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/medias.rs:163
|
# src/routes/medias.rs:163
|
||||||
msgid "Your avatar has been updated."
|
msgid "Your avatar have been updated."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/medias.rs:168
|
# src/routes/medias.rs:168
|
||||||
@@ -145,15 +145,11 @@ msgid "You are not allowed to publish on this blog."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:350
|
# src/routes/posts.rs:350
|
||||||
msgid "Your article has been updated."
|
msgid "Your article have been updated."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:532
|
# src/routes/posts.rs:532
|
||||||
msgid "Your article has been saved."
|
msgid "Your post have been saved."
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/routes/posts.rs:538
|
|
||||||
msgid "New article"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:572
|
# src/routes/posts.rs:572
|
||||||
@@ -161,7 +157,7 @@ msgid "You are not allowed to delete this article."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:597
|
# src/routes/posts.rs:597
|
||||||
msgid "Your article has been deleted."
|
msgid "Your article have been deleted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:602
|
# src/routes/posts.rs:602
|
||||||
@@ -212,152 +208,328 @@ msgstr ""
|
|||||||
msgid "You are now following {}."
|
msgid "You are now following {}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:254
|
# src/routes/user.rs:255
|
||||||
msgid "To subscribe to someone, you need to be logged in"
|
msgid "To subscribe to someone, you need to be logged in"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:356
|
# src/routes/user.rs:357
|
||||||
msgid "To edit your profile, you need to be logged in"
|
msgid "To edit your profile, you need to be logged in"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:398
|
# src/routes/user.rs:399
|
||||||
msgid "Your profile has been updated."
|
msgid "Your profile have been updated."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:425
|
# src/routes/user.rs:426
|
||||||
msgid "Your account has been deleted."
|
msgid "Your account have been deleted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:431
|
# src/routes/user.rs:432
|
||||||
msgid "You can't delete someone else's account."
|
msgid "You can't delete someone else's account."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:503
|
# src/routes/user.rs:504
|
||||||
msgid "Registrations are closed on this instance."
|
msgid "Registrations are closed on this instance."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:527
|
# src/routes/user.rs:528
|
||||||
msgid "Your account has been created. Now you just need to log in, before you can use it."
|
msgid "Your account have been created. You just need to login before you can use it."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Internal server error"
|
msgid "Plume"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Something broke on our side."
|
msgid "Menu"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Sorry about that. If you think this is a bug, please report it."
|
msgid "Search"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "You are not authorized."
|
msgid "Dashboard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Page not found"
|
msgid "Notifications"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "We couldn't find this page."
|
msgid "Log Out"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "The link that led you here may be broken."
|
msgid "My account"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "The content you sent can't be processed."
|
msgid "Log In"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Maybe it was too long."
|
msgid "Register"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Invalid CSRF token"
|
msgid "About this instance"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Something is wrong with your CSRF token. Make sure cookies are enabled in you browser, and try reloading this page. If you continue to see this error message, please report it."
|
msgid "Source code"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Articles tagged \"{0}\""
|
msgid "Matrix room"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "There are currently no articles with such a tag"
|
msgid "Administration"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "New Blog"
|
msgid "Welcome to {}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Create a blog"
|
msgid "Latest articles"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Your feed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Federated feed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Local feed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Administration of {0}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Instances"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Configuration"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Users"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Unblock"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Block"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Ban"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "All the articles of the Fediverse"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Articles from {}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Nothing to see here yet. Try subscribing to more people."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/template_utils.rs:251
|
# src/template_utils.rs:251
|
||||||
msgid "Title"
|
msgid "Name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/template_utils.rs:254
|
# src/template_utils.rs:254
|
||||||
msgid "Optional"
|
msgid "Optional"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Create blog"
|
msgid "Allow anyone to register here"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Edit \"{}\""
|
msgid "Short description"
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Description"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Markdown syntax is supported"
|
msgid "Markdown syntax is supported"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "You can upload images to your gallery, to use them as blog icons, or banners."
|
msgid "Long description"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Upload images"
|
# src/template_utils.rs:251
|
||||||
|
msgid "Default article license"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Blog icon"
|
msgid "Save these settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Blog banner"
|
msgid "About {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Update blog"
|
msgid "Home to <em>{0}</em> people"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Who wrote <em>{0}</em> articles"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "And are connected to <em>{0}</em> other instances"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Administred by"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Runs Plume {0}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Follow {}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Log in to follow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Enter your full username handle to follow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Edit your account"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Your Profile"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "To change your avatar, upload it to your gallery and then select from there."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Upload an avatar"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# src/template_utils.rs:251
|
||||||
|
msgid "Display name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# src/template_utils.rs:251
|
||||||
|
msgid "Email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Summary"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Update account"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Danger zone"
|
msgid "Danger zone"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Be very careful, any action taken here can't be reversed."
|
msgid "Be very careful, any action taken here can't be cancelled."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Permanently delete this blog"
|
msgid "Delete your account"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "{}'s icon"
|
msgid "Sorry, but as an admin, you can't leave your own instance."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Edit"
|
msgid "Your Dashboard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "There's one author on this blog: "
|
msgid "Your Blogs"
|
||||||
msgid_plural "There are {0} authors on this blog: "
|
|
||||||
msgstr[0] ""
|
|
||||||
|
|
||||||
msgid "Latest articles"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "No posts to see here yet."
|
msgid "You don't have any blog yet. Create your own, or ask to join one."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Search result(s) for \"{0}\""
|
msgid "Start a new blog"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Search result(s)"
|
msgid "Your Drafts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "No results for your query"
|
msgid "Your media"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "No more results for your query"
|
msgid "Go to your gallery"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Search"
|
msgid "Create your account"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Create an account"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# src/template_utils.rs:251
|
||||||
|
msgid "Username"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# src/template_utils.rs:251
|
||||||
|
msgid "Password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# src/template_utils.rs:251
|
||||||
|
msgid "Password confirmation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Apologies, but registrations are closed on this particular instance. You can, however, find a different one."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Articles"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Subscribers"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Subscriptions"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Atom feed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Recently boosted"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Admin"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "It is you"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Edit your profile"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Open on {0}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Unsubscribe"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Subscribe"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "{0}'s subscriptions"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "{0}'s subscribers"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Respond"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Are you sure?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Delete this comment"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "What is Plume?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Plume is a decentralized blogging engine."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Authors can manage multiple blogs, each as its own website."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Articles are also visible on other Plume instances, and you can interact with them directly from other platforms like Mastodon."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Read the detailed rules"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "None"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No description"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "View all"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "By {0}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Your query"
|
msgid "Your query"
|
||||||
@@ -370,6 +542,9 @@ msgstr ""
|
|||||||
msgid "Article title matching these words"
|
msgid "Article title matching these words"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Title"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
# src/template_utils.rs:339
|
# src/template_utils.rs:339
|
||||||
msgid "Subtitle matching these words"
|
msgid "Subtitle matching these words"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -434,6 +609,52 @@ msgstr ""
|
|||||||
msgid "Article license"
|
msgid "Article license"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Search result(s) for \"{0}\""
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Search result(s)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No results for your query"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No more results for your query"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Reset your password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# src/template_utils.rs:251
|
||||||
|
msgid "New password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# src/template_utils.rs:251
|
||||||
|
msgid "Confirmation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Update password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Check your inbox!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "We sent a mail to the address you gave us, with a link to reset your password."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# src/template_utils.rs:251
|
||||||
|
msgid "E-mail"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Send password reset link"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Log in"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# src/template_utils.rs:251
|
||||||
|
msgid "Username, or email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Interact with {}"
|
msgid "Interact with {}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -492,6 +713,12 @@ msgstr ""
|
|||||||
msgid "Written by {0}"
|
msgid "Written by {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Edit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Delete this article"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "All rights reserved."
|
msgid "All rights reserved."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -521,12 +748,6 @@ msgstr ""
|
|||||||
msgid "{0}Log in{1}, or {2}use your Fediverse account{3} to interact with this article"
|
msgid "{0}Log in{1}, or {2}use your Fediverse account{3} to interact with this article"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Unsubscribe"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Subscribe"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Comments"
|
msgid "Comments"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -543,16 +764,120 @@ msgstr ""
|
|||||||
msgid "No comments yet. Be the first to react!"
|
msgid "No comments yet. Be the first to react!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Are you sure?"
|
msgid "Invalid CSRF token"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Something is wrong with your CSRF token. Make sure cookies are enabled in you browser, and try reloading this page. If you continue to see this error message, please report it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Page not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "We couldn't find this page."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "The link that led you here may be broken."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "The content you sent can't be processed."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Maybe it was too long."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "You are not authorized."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Internal server error"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Something broke on our side."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Sorry about that. If you think this is a bug, please report it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Edit \"{}\""
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Description"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "You can upload images to your gallery, to use them as blog icons, or banners."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Upload images"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Blog icon"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Blog banner"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Update blog"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Be very careful, any action taken here can't be reversed."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Permanently delete this blog"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "New Blog"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Create a blog"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Create blog"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "{}'s icon"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "New article"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "There's one author on this blog: "
|
||||||
|
msgid_plural "There are {0} authors on this blog: "
|
||||||
|
msgstr[0] ""
|
||||||
|
|
||||||
|
msgid "No posts to see here yet."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Articles tagged \"{0}\""
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "There are currently no articles with such a tag"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "I'm from this instance"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "I'm from another instance"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
# src/template_utils.rs:259
|
||||||
|
msgid "Example: user@plu.me"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Continue to your instance"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Upload"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "You don't have any media yet."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Content warning: {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Delete"
|
msgid "Delete"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "This article is still a draft. Only you and other authors can see it."
|
msgid "Details"
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Only you and other authors can edit this article."
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Media upload"
|
msgid "Media upload"
|
||||||
@@ -570,21 +895,6 @@ msgstr ""
|
|||||||
msgid "Send"
|
msgid "Send"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Your media"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Upload"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "You don't have any media yet."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Content warning: {0}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Details"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Media details"
|
msgid "Media details"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -599,333 +909,3 @@ msgstr ""
|
|||||||
|
|
||||||
msgid "Use as an avatar"
|
msgid "Use as an avatar"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Notifications"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Plume"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Menu"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Dashboard"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Log Out"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "My account"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Log In"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Register"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "About this instance"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Privacy policy"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Administration"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Documentation"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Source code"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Matrix room"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Your feed"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Federated feed"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Local feed"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Nothing to see here yet. Try subscribing to more people."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Articles from {}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "All the articles of the Fediverse"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Users"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Configuration"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Instances"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Ban"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Administration of {0}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/template_utils.rs:251
|
|
||||||
msgid "Name"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Allow anyone to register here"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Short description"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Long description"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/template_utils.rs:251
|
|
||||||
msgid "Default article license"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Save these settings"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "About {0}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Runs Plume {0}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Home to <em>{0}</em> people"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Who wrote <em>{0}</em> articles"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "And are connected to <em>{0}</em> other instances"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Administred by"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "If you are browsing this site as a visitor, no data about you is collected."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "As a registered user, you have to provide your username (which does not have to be your real name), your functional email address and a password, in order to be able to log in, write articles and comment. The content you submit is stored until you delete it."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "When you log in, we store two cookies, one to keep your session open, the second to prevent other people to act on your behalf. We don't store any other cookies."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Welcome to {}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Unblock"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Block"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Reset your password"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/template_utils.rs:251
|
|
||||||
msgid "New password"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/template_utils.rs:251
|
|
||||||
msgid "Confirmation"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Update password"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Log in"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/template_utils.rs:251
|
|
||||||
msgid "Username, or email"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/template_utils.rs:251
|
|
||||||
msgid "Password"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/template_utils.rs:251
|
|
||||||
msgid "E-mail"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Send password reset link"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Check your inbox!"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "We sent a mail to the address you gave us, with a link to reset your password."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Admin"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "It is you"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Edit your profile"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Open on {0}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Follow {}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Log in to follow"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Enter your full username handle to follow"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "{0}'s subscriptions"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Articles"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Subscribers"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Subscriptions"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Create your account"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Create an account"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/template_utils.rs:251
|
|
||||||
msgid "Username"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/template_utils.rs:251
|
|
||||||
msgid "Email"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/template_utils.rs:251
|
|
||||||
msgid "Password confirmation"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Apologies, but registrations are closed on this particular instance. You can, however, find a different one."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "{0}'s subscribers"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Edit your account"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Your Profile"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "To change your avatar, upload it to your gallery and then select from there."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Upload an avatar"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/template_utils.rs:251
|
|
||||||
msgid "Display name"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Summary"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Update account"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Be very careful, any action taken here can't be cancelled."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Delete your account"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Sorry, but as an admin, you can't leave your own instance."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Your Dashboard"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Your Blogs"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "You don't have any blog yet. Create your own, or ask to join one."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Start a new blog"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Your Drafts"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Go to your gallery"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Atom feed"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Recently boosted"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "What is Plume?"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Plume is a decentralized blogging engine."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Authors can manage multiple blogs, each as its own website."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Articles are also visible on other Plume instances, and you can interact with them directly from other platforms like Mastodon."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Read the detailed rules"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "View all"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "None"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "No description"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "By {0}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Draft"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Respond"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Delete this comment"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "I'm from this instance"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "I'm from another instance"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/template_utils.rs:259
|
|
||||||
msgid "Example: user@plu.me"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Continue to your instance"
|
|
||||||
msgstr ""
|
|
||||||
|
|||||||
+462
-492
File diff suppressed because it is too large
Load Diff
+436
-463
File diff suppressed because it is too large
Load Diff
+430
-460
File diff suppressed because it is too large
Load Diff
+469
-499
File diff suppressed because it is too large
Load Diff
+414
-441
File diff suppressed because it is too large
Load Diff
+411
-438
File diff suppressed because it is too large
Load Diff
@@ -37,6 +37,7 @@ impl<'r> Responder<'r> for ApiError {
|
|||||||
"error": "You are not authorized to access this resource"
|
"error": "You are not authorized to access this resource"
|
||||||
}))
|
}))
|
||||||
.respond_to(req),
|
.respond_to(req),
|
||||||
|
Error::Validation(msg) => Json(json!({ "error": msg })).respond_to(req),
|
||||||
_ => Json(json!({
|
_ => Json(json!({
|
||||||
"error": "Server error"
|
"error": "Server error"
|
||||||
}))
|
}))
|
||||||
|
|||||||
+154
-3
@@ -1,6 +1,7 @@
|
|||||||
use chrono::NaiveDateTime;
|
use chrono::{NaiveDateTime, Utc};
|
||||||
use heck::{CamelCase, KebabCase};
|
use heck::{CamelCase, KebabCase};
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use crate::api::{authorization::*, Api};
|
use crate::api::{authorization::*, Api};
|
||||||
use plume_api::posts::*;
|
use plume_api::posts::*;
|
||||||
@@ -44,6 +45,7 @@ pub fn get(id: i32, auth: Option<Authorization<Read, Post>>, conn: DbConn) -> Ap
|
|||||||
published: post.published,
|
published: post.published,
|
||||||
license: post.license,
|
license: post.license,
|
||||||
cover_id: post.cover_id,
|
cover_id: post.cover_id,
|
||||||
|
url: post.ap_url,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +93,7 @@ pub fn list(
|
|||||||
published: p.published,
|
published: p.published,
|
||||||
license: p.license,
|
license: p.license,
|
||||||
cover_id: p.cover_id,
|
cover_id: p.cover_id,
|
||||||
|
url: p.ap_url,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
@@ -114,6 +117,20 @@ pub fn create(
|
|||||||
NaiveDateTime::parse_from_str(format!("{} 00:00:00", d).as_ref(), "%Y-%m-%d %H:%M:%S").ok()
|
NaiveDateTime::parse_from_str(format!("{} 00:00:00", d).as_ref(), "%Y-%m-%d %H:%M:%S").ok()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if slug.as_str() == "new" {
|
||||||
|
return Err(
|
||||||
|
Error::Validation("Sorry, but your article can't have this title.".into()).into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload.title.is_empty() {
|
||||||
|
return Err(Error::Validation("You have to give your article a title.".into()).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload.source.is_empty() {
|
||||||
|
return Err(Error::Validation("Your article can't be empty.".into()).into());
|
||||||
|
}
|
||||||
|
|
||||||
let domain = &Instance::get_local()?.public_domain;
|
let domain = &Instance::get_local()?.public_domain;
|
||||||
let (content, mentions, hashtags) = md_to_html(
|
let (content, mentions, hashtags) = md_to_html(
|
||||||
&payload.source,
|
&payload.source,
|
||||||
@@ -131,6 +148,10 @@ pub fn create(
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
if !author.is_author_in(conn, &Blog::get(conn, blog)?)? {
|
||||||
|
return Err(Error::Unauthorized.into());
|
||||||
|
}
|
||||||
|
|
||||||
if Post::find_by_slug(conn, slug, blog).is_ok() {
|
if Post::find_by_slug(conn, slug, blog).is_ok() {
|
||||||
return Err(Error::InvalidValue.into());
|
return Err(Error::InvalidValue.into());
|
||||||
}
|
}
|
||||||
@@ -166,11 +187,19 @@ pub fn create(
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some(ref tags) = payload.tags {
|
if let Some(ref tags) = payload.tags {
|
||||||
|
let tags = tags
|
||||||
|
.iter()
|
||||||
|
.map(|t| t.to_camel_case())
|
||||||
|
.filter(|t| !t.is_empty())
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|t| Tag::build_activity(t).ok());
|
||||||
|
|
||||||
for tag in tags {
|
for tag in tags {
|
||||||
Tag::insert(
|
Tag::insert(
|
||||||
conn,
|
conn,
|
||||||
NewTag {
|
NewTag {
|
||||||
tag: tag.to_string(),
|
tag: tag.name_string().unwrap(),
|
||||||
is_hashtag: false,
|
is_hashtag: false,
|
||||||
post_id: post.id,
|
post_id: post.id,
|
||||||
},
|
},
|
||||||
@@ -211,7 +240,6 @@ pub fn create(
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|t| t.tag)
|
.map(|t| t.tag)
|
||||||
.collect(),
|
.collect(),
|
||||||
|
|
||||||
id: post.id,
|
id: post.id,
|
||||||
title: post.title,
|
title: post.title,
|
||||||
subtitle: post.subtitle,
|
subtitle: post.subtitle,
|
||||||
@@ -221,9 +249,132 @@ pub fn create(
|
|||||||
published: post.published,
|
published: post.published,
|
||||||
license: post.license,
|
license: post.license,
|
||||||
cover_id: post.cover_id,
|
cover_id: post.cover_id,
|
||||||
|
url: post.ap_url,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[put("/posts/<id>", data = "<payload>")]
|
||||||
|
pub fn update(
|
||||||
|
id: i32,
|
||||||
|
auth: Authorization<Write, Post>,
|
||||||
|
payload: Json<NewPostData>,
|
||||||
|
rockets: PlumeRocket,
|
||||||
|
) -> Api<PostData> {
|
||||||
|
let conn = &*rockets.conn;
|
||||||
|
let mut post = Post::get(&*conn, id)?;
|
||||||
|
let author = User::get(conn, auth.0.user_id)?;
|
||||||
|
let b = post.get_blog(&*conn)?;
|
||||||
|
|
||||||
|
let new_slug = if !post.published {
|
||||||
|
payload.title.to_string().to_kebab_case()
|
||||||
|
} else {
|
||||||
|
post.slug.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
if new_slug != post.slug && Post::find_by_slug(&*conn, &new_slug, b.id).is_ok() {
|
||||||
|
return Err(Error::Validation("A post with the same title already exists.".into()).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !author.is_author_in(&*conn, &b)? {
|
||||||
|
Err(Error::Unauthorized.into())
|
||||||
|
} else {
|
||||||
|
let (content, mentions, hashtags) = md_to_html(
|
||||||
|
&payload.source,
|
||||||
|
Some(&Instance::get_local()?.public_domain),
|
||||||
|
false,
|
||||||
|
Some(Media::get_media_processor(
|
||||||
|
&conn,
|
||||||
|
b.list_authors(&conn)?.iter().collect(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// update publication date if when this article is no longer a draft
|
||||||
|
let newly_published = if !post.published && payload.published.unwrap_or(post.published) {
|
||||||
|
post.published = true;
|
||||||
|
post.creation_date = Utc::now().naive_utc();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
post.slug = new_slug.clone();
|
||||||
|
post.title = payload.title.clone();
|
||||||
|
post.subtitle = payload.subtitle.clone().unwrap_or_default();
|
||||||
|
post.content = SafeString::new(&content);
|
||||||
|
post.source = payload.source.clone();
|
||||||
|
post.license = payload.license.clone().unwrap_or_default();
|
||||||
|
post.cover_id = payload.cover_id;
|
||||||
|
post.update(&*conn, &rockets.searcher)?;
|
||||||
|
|
||||||
|
if post.published {
|
||||||
|
post.update_mentions(
|
||||||
|
&conn,
|
||||||
|
mentions
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|m| Mention::build_activity(&rockets, &m).ok())
|
||||||
|
.collect(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tags = payload
|
||||||
|
.tags
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.iter()
|
||||||
|
.map(|t| t.trim().to_camel_case())
|
||||||
|
.filter(|t| !t.is_empty())
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|t| Tag::build_activity(t).ok())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
post.update_tags(&conn, tags)?;
|
||||||
|
|
||||||
|
let hashtags = hashtags
|
||||||
|
.into_iter()
|
||||||
|
.map(|h| h.to_camel_case())
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|t| Tag::build_activity(t).ok())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
post.update_hashtags(&conn, hashtags)?;
|
||||||
|
|
||||||
|
if post.published {
|
||||||
|
if newly_published {
|
||||||
|
let act = post.create_activity(&conn)?;
|
||||||
|
let dest = User::one_by_instance(&*conn)?;
|
||||||
|
rockets
|
||||||
|
.worker
|
||||||
|
.execute(move || broadcast(&author, act, dest));
|
||||||
|
} else {
|
||||||
|
let act = post.update_activity(&*conn)?;
|
||||||
|
let dest = User::one_by_instance(&*conn)?;
|
||||||
|
rockets
|
||||||
|
.worker
|
||||||
|
.execute(move || broadcast(&author, act, dest));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Json(PostData {
|
||||||
|
authors: post.get_authors(conn)?.into_iter().map(|a| a.fqn).collect(),
|
||||||
|
creation_date: post.creation_date.format("%Y-%m-%d").to_string(),
|
||||||
|
tags: Tag::for_post(conn, post.id)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|t| t.tag)
|
||||||
|
.collect(),
|
||||||
|
id: post.id,
|
||||||
|
title: post.title,
|
||||||
|
subtitle: post.subtitle,
|
||||||
|
content: post.content.to_string(),
|
||||||
|
source: Some(post.source),
|
||||||
|
blog_id: post.blog_id,
|
||||||
|
published: post.published,
|
||||||
|
license: post.license,
|
||||||
|
cover_id: post.cover_id,
|
||||||
|
url: post.ap_url,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[delete("/posts/<id>")]
|
#[delete("/posts/<id>")]
|
||||||
pub fn delete(auth: Authorization<Write, Post>, rockets: PlumeRocket, id: i32) -> Api<()> {
|
pub fn delete(auth: Authorization<Write, Post>, rockets: PlumeRocket, id: i32) -> Api<()> {
|
||||||
let author = User::get(&*rockets.conn, auth.0.user_id)?;
|
let author = User::get(&*rockets.conn, auth.0.user_id)?;
|
||||||
|
|||||||
+24
-8
@@ -5,6 +5,7 @@ extern crate activitypub;
|
|||||||
extern crate askama_escape;
|
extern crate askama_escape;
|
||||||
extern crate atom_syndication;
|
extern crate atom_syndication;
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
|
extern crate clap;
|
||||||
extern crate colored;
|
extern crate colored;
|
||||||
extern crate ctrlc;
|
extern crate ctrlc;
|
||||||
extern crate diesel;
|
extern crate diesel;
|
||||||
@@ -38,6 +39,7 @@ extern crate validator;
|
|||||||
extern crate validator_derive;
|
extern crate validator_derive;
|
||||||
extern crate webfinger;
|
extern crate webfinger;
|
||||||
|
|
||||||
|
use clap::App;
|
||||||
use diesel::r2d2::ConnectionManager;
|
use diesel::r2d2::ConnectionManager;
|
||||||
use plume_models::{
|
use plume_models::{
|
||||||
db_conn::{DbPool, PragmaForeignKey},
|
db_conn::{DbPool, PragmaForeignKey},
|
||||||
@@ -73,7 +75,11 @@ compile_i18n!();
|
|||||||
|
|
||||||
/// Initializes a database pool.
|
/// Initializes a database pool.
|
||||||
fn init_pool() -> Option<DbPool> {
|
fn init_pool() -> Option<DbPool> {
|
||||||
dotenv::dotenv().ok();
|
match dotenv::dotenv() {
|
||||||
|
Ok(path) => println!("Configuration read from {}", path.display()),
|
||||||
|
Err(ref e) if e.not_found() => eprintln!("no .env was found"),
|
||||||
|
e => e.map(|_| ()).unwrap(),
|
||||||
|
}
|
||||||
|
|
||||||
let manager = ConnectionManager::<Connection>::new(CONFIG.database_url.as_str());
|
let manager = ConnectionManager::<Connection>::new(CONFIG.database_url.as_str());
|
||||||
let pool = DbPool::builder()
|
let pool = DbPool::builder()
|
||||||
@@ -85,6 +91,19 @@ fn init_pool() -> Option<DbPool> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
App::new("Plume")
|
||||||
|
.bin_name("plume")
|
||||||
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
|
.about("Plume backend server")
|
||||||
|
.after_help(
|
||||||
|
r#"
|
||||||
|
The plume command should be run inside the directory
|
||||||
|
containing the `.env` configuration file and `static` directory.
|
||||||
|
See https://docs.joinplu.me/installation/config
|
||||||
|
and https://docs.joinplu.me/installation/init for more info.
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
let dbpool = init_pool().expect("main: database pool initialization error");
|
let dbpool = init_pool().expect("main: database pool initialization error");
|
||||||
if IMPORTED_MIGRATIONS
|
if IMPORTED_MIGRATIONS
|
||||||
.is_pending(&dbpool.get().unwrap())
|
.is_pending(&dbpool.get().unwrap())
|
||||||
@@ -256,6 +275,7 @@ Then try to restart Plume
|
|||||||
api::posts::get,
|
api::posts::get,
|
||||||
api::posts::list,
|
api::posts::list,
|
||||||
api::posts::create,
|
api::posts::create,
|
||||||
|
api::posts::update,
|
||||||
api::posts::delete,
|
api::posts::delete,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -280,18 +300,14 @@ Then try to restart Plume
|
|||||||
(
|
(
|
||||||
"/inbox".to_owned(),
|
"/inbox".to_owned(),
|
||||||
"/inbox".to_owned(),
|
"/inbox".to_owned(),
|
||||||
rocket::http::Method::Post,
|
Some(rocket::http::Method::Post),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"/@/<name>/inbox".to_owned(),
|
"/@/<name>/inbox".to_owned(),
|
||||||
"/@/<name>/inbox".to_owned(),
|
"/@/<name>/inbox".to_owned(),
|
||||||
rocket::http::Method::Post,
|
Some(rocket::http::Method::Post),
|
||||||
),
|
|
||||||
(
|
|
||||||
"/api/<path..>".to_owned(),
|
|
||||||
"/api/<path..>".to_owned(),
|
|
||||||
rocket::http::Method::Post,
|
|
||||||
),
|
),
|
||||||
|
("/api/<path..>".to_owned(), "/api/<path..>".to_owned(), None),
|
||||||
])
|
])
|
||||||
.finalize()
|
.finalize()
|
||||||
.expect("main: csrf fairing creation error"),
|
.expect("main: csrf fairing creation error"),
|
||||||
|
|||||||
+11
-4
@@ -13,6 +13,7 @@ use validator::{Validate, ValidationError, ValidationErrors};
|
|||||||
use plume_common::activity_pub::{broadcast, ActivityStream, ApRequest};
|
use plume_common::activity_pub::{broadcast, ActivityStream, ApRequest};
|
||||||
use plume_common::utils;
|
use plume_common::utils;
|
||||||
use plume_models::{
|
use plume_models::{
|
||||||
|
api_tokens::ApiToken,
|
||||||
blogs::*,
|
blogs::*,
|
||||||
comments::{Comment, CommentTree},
|
comments::{Comment, CommentTree},
|
||||||
inbox::inbox,
|
inbox::inbox,
|
||||||
@@ -156,7 +157,8 @@ pub fn new(blog: String, cl: ContentLen, rockets: PlumeRocket) -> Result<Ructe,
|
|||||||
None,
|
None,
|
||||||
ValidationErrors::default(),
|
ValidationErrors::default(),
|
||||||
medias,
|
medias,
|
||||||
cl.0
|
cl.0,
|
||||||
|
ApiToken::web_token(&*conn, user.id)?.value
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +212,8 @@ pub fn edit(
|
|||||||
Some(post),
|
Some(post),
|
||||||
ValidationErrors::default(),
|
ValidationErrors::default(),
|
||||||
medias,
|
medias,
|
||||||
cl.0
|
cl.0,
|
||||||
|
ApiToken::web_token(&*conn, user.id)?.value
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,7 +369,10 @@ pub fn update(
|
|||||||
Some(post),
|
Some(post),
|
||||||
errors.clone(),
|
errors.clone(),
|
||||||
medias.clone(),
|
medias.clone(),
|
||||||
cl.0
|
cl.0,
|
||||||
|
ApiToken::web_token(&*conn, user.id)
|
||||||
|
.expect("The default API token cannot be retrieved")
|
||||||
|
.value
|
||||||
))
|
))
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
@@ -550,7 +556,8 @@ pub fn create(
|
|||||||
None,
|
None,
|
||||||
errors.clone(),
|
errors.clone(),
|
||||||
medias,
|
medias,
|
||||||
cl.0
|
cl.0,
|
||||||
|
ApiToken::web_token(&*conn, user.id)?.value
|
||||||
))
|
))
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,14 +48,16 @@ impl Resolver<PlumeRocket> for WebfingerResolver {
|
|||||||
CONFIG.base_url.as_str()
|
CONFIG.base_url.as_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find(acct: String, ctx: PlumeRocket) -> Result<Webfinger, ResolverError> {
|
fn find(prefix: Prefix, acct: String, ctx: PlumeRocket) -> Result<Webfinger, ResolverError> {
|
||||||
User::find_by_fqn(&ctx, &acct)
|
match prefix {
|
||||||
|
Prefix::Acct => User::find_by_fqn(&ctx, &acct)
|
||||||
.and_then(|usr| usr.webfinger(&*ctx.conn))
|
.and_then(|usr| usr.webfinger(&*ctx.conn))
|
||||||
.or_else(|_| {
|
.or(Err(ResolverError::NotFound)),
|
||||||
Blog::find_by_fqn(&ctx, &acct)
|
Prefix::Group => Blog::find_by_fqn(&ctx, &acct)
|
||||||
.and_then(|blog| blog.webfinger(&*ctx.conn))
|
.and_then(|blog| blog.webfinger(&*ctx.conn))
|
||||||
.or(Err(ResolverError::NotFound))
|
.or(Err(ResolverError::NotFound)),
|
||||||
})
|
Prefix::Custom(_) => Err(ResolverError::NotFound),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,9 +74,7 @@ pub fn webfinger(resource: String, rockets: PlumeRocket) -> Content<String> {
|
|||||||
"Invalid resource. Make sure to request an acct: URI"
|
"Invalid resource. Make sure to request an acct: URI"
|
||||||
}
|
}
|
||||||
ResolverError::NotFound => "Requested resource was not found",
|
ResolverError::NotFound => "Requested resource was not found",
|
||||||
ResolverError::WrongInstance => {
|
ResolverError::WrongDomain => "This is not the instance of the requested resource",
|
||||||
"This is not the instance of the requested resource"
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ pub fn avatar(
|
|||||||
pub fn tabs(links: &[(&str, String, bool)]) -> Html<String> {
|
pub fn tabs(links: &[(&str, String, bool)]) -> Html<String> {
|
||||||
let mut res = String::from(r#"<div class="tabs">"#);
|
let mut res = String::from(r#"<div class="tabs">"#);
|
||||||
for (url, title, selected) in links {
|
for (url, title, selected) in links {
|
||||||
res.push_str(r#"<a href=""#);
|
res.push_str(r#"<a dir="auto" href=""#);
|
||||||
res.push_str(url);
|
res.push_str(url);
|
||||||
if *selected {
|
if *selected {
|
||||||
res.push_str(r#"" class="selected">"#);
|
res.push_str(r#"" class="selected">"#);
|
||||||
@@ -176,7 +176,7 @@ pub fn paginate_param(
|
|||||||
p
|
p
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
res.push_str(r#"<div class="pagination">"#);
|
res.push_str(r#"<div class="pagination" dir="auto">"#);
|
||||||
if page != 1 {
|
if page != 1 {
|
||||||
res.push_str(
|
res.push_str(
|
||||||
format!(
|
format!(
|
||||||
@@ -239,13 +239,13 @@ macro_rules! input {
|
|||||||
|
|
||||||
Html(format!(
|
Html(format!(
|
||||||
r#"
|
r#"
|
||||||
<label for="{name}">
|
<label for="{name}" dir="auto">
|
||||||
{label}
|
{label}
|
||||||
{optional}
|
{optional}
|
||||||
{details}
|
{details}
|
||||||
</label>
|
</label>
|
||||||
{error}
|
{error}
|
||||||
<input type="{kind}" id="{name}" name="{name}" value="{val}" {props}/>
|
<input type="{kind}" id="{name}" name="{name}" value="{val}" {props} dir="auto"/>
|
||||||
"#,
|
"#,
|
||||||
name = stringify!($name),
|
name = stringify!($name),
|
||||||
label = i18n!(cat, $label),
|
label = i18n!(cat, $label),
|
||||||
@@ -264,7 +264,7 @@ macro_rules! input {
|
|||||||
$err.errors().get(stringify!($name))
|
$err.errors().get(stringify!($name))
|
||||||
{
|
{
|
||||||
format!(
|
format!(
|
||||||
r#"<p class="error">{}</p>"#,
|
r#"<p class="error" dir="auto">{}</p>"#,
|
||||||
errs[0]
|
errs[0]
|
||||||
.message
|
.message
|
||||||
.clone()
|
.clone()
|
||||||
@@ -332,8 +332,8 @@ macro_rules! input {
|
|||||||
let cat = $catalog;
|
let cat = $catalog;
|
||||||
Html(format!(
|
Html(format!(
|
||||||
r#"
|
r#"
|
||||||
<label for="{name}">{label}</label>
|
<label for="{name}" dir="auto">{label}</label>
|
||||||
<input type="{kind}" id="{name}" name="{name}" {props}/>
|
<input type="{kind}" id="{name}" name="{name}" {props} dir="auto"/>
|
||||||
"#,
|
"#,
|
||||||
name = stringify!($name),
|
name = stringify!($name),
|
||||||
label = i18n!(cat, $label),
|
label = i18n!(cat, $label),
|
||||||
|
|||||||
+71
-14
@@ -364,32 +364,89 @@ main .article-meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#plume-editor {
|
#plume-editor {
|
||||||
header {
|
margin: 0;
|
||||||
|
grid: 50px 1fr / 1fr auto 20%;
|
||||||
|
min-height: 80vh;
|
||||||
|
|
||||||
|
& > header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row-reverse;
|
|
||||||
background: transparent;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
button {
|
padding: 0px 20px;
|
||||||
flex: 0 0 10em;
|
border-bottom: 1px solid $purple;
|
||||||
font-size: 1.25em;
|
max-height: 90px;
|
||||||
margin: .5em 0em .5em 1em;
|
background: $background;
|
||||||
}
|
|
||||||
|
grid-column: 1 / 3;
|
||||||
|
grid-row: 1 / 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > * {
|
#edition-area {
|
||||||
|
margin: 0;
|
||||||
|
max-width: none;
|
||||||
|
max-height: 90vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#edition-area > * {
|
||||||
min-height: 1em;
|
min-height: 1em;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
margin-left: 20%;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
padding-right: 5%;
|
||||||
|
|
||||||
.placeholder {
|
&:empty::before {
|
||||||
|
content: attr(data-placeholder);
|
||||||
|
display: none;
|
||||||
color: transparentize($black, 0.6);
|
color: transparentize($black, 0.6);
|
||||||
|
cursor: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
article {
|
&:empty:not(:focus)::before {
|
||||||
max-width: none;
|
display: inline;
|
||||||
min-height: 80vh;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#edition-area > h1 {
|
||||||
|
margin-top: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#editor-title, #editor-subtitle, #editor-main > * {
|
||||||
|
padding-left: 18px;
|
||||||
|
border-left: 2px solid transparent;
|
||||||
|
transition: border-left-color 0.1s ease-in;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-left-color: transparentize($black, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aside {
|
||||||
|
background: $gray;
|
||||||
|
margin: 0;
|
||||||
|
flex: 0 0 15%;
|
||||||
|
padding: 0 1em;
|
||||||
|
grid-row: 1 / 4;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin: 2em 0 .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-size: 1.25em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body#editor {
|
||||||
|
footer {
|
||||||
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
<link href='@Instance::get_local().unwrap().compute_box("~", &blog.fqn, "atom.xml")' rel='alternate' type='application/atom+xml'>
|
<link href='@Instance::get_local().unwrap().compute_box("~", &blog.fqn, "atom.xml")' rel='alternate' type='application/atom+xml'>
|
||||||
<link href='@blog.ap_url' rel='alternate' type='application/activity+json'>
|
<link href='@blog.ap_url' rel='alternate' type='application/activity+json'>
|
||||||
}, {
|
}, {
|
||||||
<a href="@uri!(blogs::details: name = &blog.fqn, page = _)">@blog.title</a>
|
<a href="@uri!(blogs::details: name = &blog.fqn, page = _)" dir="auto">@blog.title</a>
|
||||||
}, {
|
}, {
|
||||||
<div class="hidden">
|
<div class="hidden">
|
||||||
@for author in authors {
|
@for author in authors {
|
||||||
@@ -41,38 +41,38 @@
|
|||||||
}
|
}
|
||||||
<div class="h-card">
|
<div class="h-card">
|
||||||
<div class="user">
|
<div class="user">
|
||||||
<div class="flex wrap">
|
<div class="flex wrap" dir="auto">
|
||||||
<div class="avatar medium" style="background-image: url('@blog.icon_url(ctx.0)');" aria-label="@i18n!(ctx.1, "{}'s icon"; &blog.title)"></div>
|
<div class="avatar medium" style="background-image: url('@blog.icon_url(ctx.0)');" aria-label="@i18n!(ctx.1, "{}'s icon"; &blog.title)"></div>
|
||||||
<img class="hidden u-photo" src="@blog.icon_url(ctx.0)"/>
|
<img class="hidden u-photo" src="@blog.icon_url(ctx.0)"/>
|
||||||
|
|
||||||
<h1 class="grow flex vertical">
|
<h1 class="grow flex vertical">
|
||||||
<span class="p-name">@blog.title</span>
|
<span class="p-name">@blog.title</span>
|
||||||
<small>~@blog.fqn</small>
|
<small dir="auto">~@blog.fqn</small>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
@if ctx.2.clone().and_then(|u| u.is_author_in(ctx.0, &blog).ok()).unwrap_or(false) {
|
@if ctx.2.clone().and_then(|u| u.is_author_in(ctx.0, &blog).ok()).unwrap_or(false) {
|
||||||
<a href="@uri!(posts::new: blog = &blog.fqn)" class="button">@i18n!(ctx.1, "New article")</a>
|
<a href="@uri!(posts::new: blog = &blog.fqn)" class="button" dir="auto">@i18n!(ctx.1, "New article")</a>
|
||||||
<a href="@uri!(blogs::edit: name = &blog.fqn)" class="button">@i18n!(ctx.1, "Edit")</a>
|
<a href="@uri!(blogs::edit: name = &blog.fqn)" class="button" dir="auto">@i18n!(ctx.1, "Edit")</a>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<main class="user-summary">
|
<main class="user-summary" dir="auto">
|
||||||
<p>
|
<p>
|
||||||
@i18n!(ctx.1, "There's one author on this blog: ", "There are {0} authors on this blog: "; authors.len())
|
@i18n!(ctx.1, "There's one author on this blog: ", "There are {0} authors on this blog: "; authors.len())
|
||||||
@for (i, author) in authors.iter().enumerate() {@if i >= 1 {, }
|
@for (i, author) in authors.iter().enumerate() {@if i >= 1 {, }
|
||||||
<a class="author p-author" href="@uri!(user::details: name = &author.fqn)">@author.name()</a>}
|
<a class="author p-author" href="@uri!(user::details: name = &author.fqn)" dir="auto">@author.name()</a>}
|
||||||
</p>
|
</p>
|
||||||
@Html(blog.summary_html.clone())
|
@Html(blog.summary_html.clone())
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>
|
<h2 dir="auto">
|
||||||
@i18n!(ctx.1, "Latest articles")
|
@i18n!(ctx.1, "Latest articles")
|
||||||
<small><a href="@uri!(blogs::atom_feed: name = &blog.fqn)" title="Atom feed">@icon!("rss")</a></small>
|
<small><a href="@uri!(blogs::atom_feed: name = &blog.fqn)" title="Atom feed">@icon!("rss")</a></small>
|
||||||
</h2>
|
</h2>
|
||||||
@if posts.is_empty() {
|
@if posts.is_empty() {
|
||||||
<p>@i18n!(ctx.1, "No posts to see here yet.")</p>
|
<p dir="auto">@i18n!(ctx.1, "No posts to see here yet.")</p>
|
||||||
}
|
}
|
||||||
<div class="cards">
|
<div class="cards">
|
||||||
@for article in posts {
|
@for article in posts {
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
@(ctx: BaseContext, form: &NewBlogForm, errors: ValidationErrors)
|
@(ctx: BaseContext, form: &NewBlogForm, errors: ValidationErrors)
|
||||||
|
|
||||||
@:base(ctx, i18n!(ctx.1, "New Blog"), {}, {}, {
|
@:base(ctx, i18n!(ctx.1, "New Blog"), {}, {}, {
|
||||||
<h1>@i18n!(ctx.1, "Create a blog")</h1>
|
<h1 dir="auto">@i18n!(ctx.1, "Create a blog")</h1>
|
||||||
<form method="post" action="@uri!(blogs::create)">
|
<form method="post" action="@uri!(blogs::create)">
|
||||||
@input!(ctx.1, title (text), "Title", form, errors, "required minlength=\"1\"")
|
@input!(ctx.1, title (text), "Title", form, errors, "required minlength=\"1\"")
|
||||||
<input type="submit" value="@i18n!(ctx.1, "Create blog")"/>
|
<input type="submit" value="@i18n!(ctx.1, "Create blog")" dir="auto"/>
|
||||||
</form>
|
</form>
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<div class="comment u-comment h-cite" id="comment-@comm.id">
|
<div class="comment u-comment h-cite" id="comment-@comm.id">
|
||||||
<main class="content">
|
<main class="content">
|
||||||
<header>
|
<header>
|
||||||
<a class="author u-author h-card" href="@uri!(user::details: name = &author.fqn)">
|
<a class="author u-author h-card" href="@uri!(user::details: name = &author.fqn)" dir="auto">
|
||||||
@avatar(ctx.0, &author, Size::Small, true, ctx.1)
|
@avatar(ctx.0, &author, Size::Small, true, ctx.1)
|
||||||
<span class="display-name p-name">@author.name()</span>
|
<span class="display-name p-name">@author.name()</span>
|
||||||
<small>@author.fqn</small>
|
<small>@author.fqn</small>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<div class="text p-content">
|
<div class="text p-content">
|
||||||
@if comm.sensitive {
|
@if comm.sensitive {
|
||||||
<details>
|
<details>
|
||||||
<summary>@comm.spoiler_text</summary>
|
<summary dir="auto">@comm.spoiler_text</summary>
|
||||||
}
|
}
|
||||||
@Html(&comm.content)
|
@Html(&comm.content)
|
||||||
@if comm.sensitive {
|
@if comm.sensitive {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
@if !articles.is_empty() {
|
@if !articles.is_empty() {
|
||||||
<div class="h-feed">
|
<div class="h-feed">
|
||||||
<h2><span class="p-name">@title</span> — <a href="@link">@i18n!(ctx.1, "View all")</a></h2>
|
<h2 dir="auto"><span class="p-name">@title</span> — <a href="@link">@i18n!(ctx.1, "View all")</a></h2>
|
||||||
<div class="cards">
|
<div class="cards">
|
||||||
@for article in articles {
|
@for article in articles {
|
||||||
@:post_card(ctx, article)
|
@:post_card(ctx, article)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
@(ctx: BaseContext, id: &str, title: String, optional: bool, medias: Vec<Media>, selected: Option<i32>)
|
@(ctx: BaseContext, id: &str, title: String, optional: bool, medias: Vec<Media>, selected: Option<i32>)
|
||||||
|
|
||||||
<label for="@id">
|
<label for="@id" dir="auto">
|
||||||
@title
|
@title
|
||||||
@if optional {
|
@if optional {
|
||||||
<small>@i18n!(ctx.1, "Optional")</small>
|
<small>@i18n!(ctx.1, "Optional")</small>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<option value="none" @if selected.is_none() { selected }>@i18n!(ctx.1, "None")</option>
|
<option value="none" @if selected.is_none() { selected }>@i18n!(ctx.1, "None")</option>
|
||||||
@for media in medias {
|
@for media in medias {
|
||||||
@if media.category() == MediaCategory::Image {
|
@if media.category() == MediaCategory::Image {
|
||||||
<option value="@media.id" @if selected.map(|c| c == media.id).unwrap_or(false) { selected }>
|
<option value="@media.id" @if selected.map(|c| c == media.id).unwrap_or(false) { selected } dir="auto">
|
||||||
@if !media.alt_text.is_empty() {
|
@if !media.alt_text.is_empty() {
|
||||||
@media.alt_text
|
@media.alt_text
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
@(ctx: BaseContext, instance: Instance, n_users: i64, n_articles: i64)
|
@(ctx: BaseContext, instance: Instance, n_users: i64, n_articles: i64)
|
||||||
|
|
||||||
<section class="split">
|
<section class="split" dir="auto">
|
||||||
<div class="presentation card">
|
<div class="presentation card">
|
||||||
<h2>@i18n!(ctx.1, "What is Plume?")</h2>
|
<h2>@i18n!(ctx.1, "What is Plume?")</h2>
|
||||||
<main>
|
<main>
|
||||||
|
|||||||
@@ -8,13 +8,13 @@
|
|||||||
@if article.cover_id.is_some() {
|
@if article.cover_id.is_some() {
|
||||||
<div class="cover" style="background-image: url('@Html(article.cover_url(ctx.0).unwrap_or_default())')"></div>
|
<div class="cover" style="background-image: url('@Html(article.cover_url(ctx.0).unwrap_or_default())')"></div>
|
||||||
}
|
}
|
||||||
<h3 class="p-name">
|
<h3 class="p-name" dir="auto">
|
||||||
<a class="u-url" href="@uri!(posts::details: blog = article.get_blog(ctx.0).unwrap().fqn, slug = &article.slug, responding_to = _)">
|
<a class="u-url" href="@uri!(posts::details: blog = article.get_blog(ctx.0).unwrap().fqn, slug = &article.slug, responding_to = _)">
|
||||||
@article.title
|
@article.title
|
||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
<main>
|
<main>
|
||||||
<p class="p-summary">@article.subtitle</p>
|
<p class="p-summary" dir="auto">@article.subtitle</p>
|
||||||
</main>
|
</main>
|
||||||
<footer class="authors">
|
<footer class="authors">
|
||||||
@Html(i18n!(ctx.1, "By {0}"; format!(
|
@Html(i18n!(ctx.1, "By {0}"; format!(
|
||||||
|
|||||||
@@ -28,8 +28,8 @@
|
|||||||
@if article.cover_id.is_some() { style="background-image: url('@article.cover_url(ctx.0).unwrap_or_default()'" }
|
@if article.cover_id.is_some() { style="background-image: url('@article.cover_url(ctx.0).unwrap_or_default()'" }
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="article p-name">@article.title</h1>
|
<h1 class="article p-name" dir="auto">@article.title</h1>
|
||||||
<div class="article-info">
|
<div class="article-info" dir="auto">
|
||||||
<span class="author">
|
<span class="author">
|
||||||
@Html(i18n!(ctx.1, "Written by {0}"; format!("<a href=\"{}\">{}</a>",
|
@Html(i18n!(ctx.1, "Written by {0}"; format!("<a href=\"{}\">{}</a>",
|
||||||
uri!(user::details: name = &author.fqn),
|
uri!(user::details: name = &author.fqn),
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
—
|
—
|
||||||
<span class="date dt-published" datetime="@article.creation_date.format("%F %T")">@article.creation_date.format("%B %e, %Y")</span><a class="u-url" href="@article.ap_url"></a>
|
<span class="date dt-published" datetime="@article.creation_date.format("%F %T")">@article.creation_date.format("%B %e, %Y")</span><a class="u-url" href="@article.ap_url"></a>
|
||||||
</div>
|
</div>
|
||||||
<h2 class="article p-summary">@article.subtitle</h2>
|
<h2 class="article p-summary" dir="auto">@article.subtitle</h2>
|
||||||
</div>
|
</div>
|
||||||
@if article.cover_id.is_some() {
|
@if article.cover_id.is_some() {
|
||||||
<div class="shadow"></div>
|
<div class="shadow"></div>
|
||||||
@@ -46,12 +46,12 @@
|
|||||||
}
|
}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<article class="e-content">
|
<article class="e-content" dir="auto">
|
||||||
@Html(&article.content)
|
@Html(&article.content)
|
||||||
</article>
|
</article>
|
||||||
<div class="article-meta">
|
<div class="article-meta">
|
||||||
<section class="split">
|
<section class="split">
|
||||||
<ul class="tags">
|
<ul class="tags" dir="auto">
|
||||||
@for tag in tags {
|
@for tag in tags {
|
||||||
@if !tag.is_hashtag {
|
@if !tag.is_hashtag {
|
||||||
<li><a class="p-category" href="@uri!(tags::tag: name = &tag.tag, page = _)">@tag.tag</a></li>
|
<li><a class="p-category" href="@uri!(tags::tag: name = &tag.tag, page = _)">@tag.tag</a></li>
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
<p class="right">
|
<p class="right" dir="auto">
|
||||||
@if article.license.is_empty() {
|
@if article.license.is_empty() {
|
||||||
@i18n!(ctx.1, "All rights reserved."; &article.license)
|
@i18n!(ctx.1, "All rights reserved."; &article.license)
|
||||||
} else {
|
} else {
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
<section class="banner">
|
<section class="banner">
|
||||||
<div class="flex p-author h-card user">
|
<div class="flex p-author h-card user" dir="auto">
|
||||||
@avatar(ctx.0, &author, Size::Medium, true, ctx.1)
|
@avatar(ctx.0, &author, Size::Medium, true, ctx.1)
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
<h2 class="p-name">
|
<h2 class="p-name">
|
||||||
@@ -132,7 +132,7 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="comments">
|
<section class="comments" dir="auto">
|
||||||
<h2>@i18n!(ctx.1, "Comments")</h2>
|
<h2>@i18n!(ctx.1, "Comments")</h2>
|
||||||
|
|
||||||
@if ctx.2.is_some() {
|
@if ctx.2.is_some() {
|
||||||
@@ -143,7 +143,7 @@
|
|||||||
@if let Some(ref prev) = previous_comment {
|
@if let Some(ref prev) = previous_comment {
|
||||||
<input type="hidden" name="responding_to" value="@prev.id"/>
|
<input type="hidden" name="responding_to" value="@prev.id"/>
|
||||||
}
|
}
|
||||||
<textarea id="plume-editor" name="content">@comment_form.content</textarea>
|
<textarea id="plume-editor" name="content" dir="auto">@comment_form.content</textarea>
|
||||||
<input type="submit" value="@i18n!(ctx.1, "Submit comment")" />
|
<input type="submit" value="@i18n!(ctx.1, "Submit comment")" />
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
@@ -153,7 +153,7 @@
|
|||||||
@:comment(ctx, &comm, Some(&article.ap_url), &blog.fqn, &article.slug)
|
@:comment(ctx, &comm, Some(&article.ap_url), &blog.fqn, &article.slug)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
<p class="center">@i18n!(ctx.1, "No comments yet. Be the first to react!")</p>
|
<p class="center" dir="auto">@i18n!(ctx.1, "No comments yet. Be the first to react!")</p>
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+41
-11
@@ -9,22 +9,52 @@
|
|||||||
@use routes::posts::NewPostForm;
|
@use routes::posts::NewPostForm;
|
||||||
@use routes::*;
|
@use routes::*;
|
||||||
|
|
||||||
@(ctx: BaseContext, title: String, blog: Blog, editing: bool, form: &NewPostForm, is_draft: bool, article: Option<Post>, errors: ValidationErrors, medias: Vec<Media>, content_len: u64)
|
@(ctx: BaseContext, title: String, blog: Blog, editing: bool, form: &NewPostForm, is_draft: bool, article: Option<Post>, errors: ValidationErrors, medias: Vec<Media>, content_len: u64, api_token: String)
|
||||||
|
|
||||||
@:base(ctx, title.clone(), {}, {}, {
|
@:base(ctx, title.clone(), {}, {}, {
|
||||||
<h1 id="plume-editor-title">@title</h1>
|
<script>
|
||||||
<div id="plume-editor" style="display: none;">
|
window.blog_id = @blog.id
|
||||||
<header>
|
window.api_token = '@api_token'
|
||||||
<button id="publish" class="button">@i18n!(ctx.1, "Publish")</button>
|
@if editing {
|
||||||
<p id="char-count">@content_len</p>
|
window.editing = true
|
||||||
|
window.post_id = @article.clone().unwrap().id
|
||||||
|
} else {
|
||||||
|
window.editing = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<div id="plume-editor" style="display: none;" dir="auto">
|
||||||
|
<header id="editor-toolbar">
|
||||||
<a href="#" id="close-editor">@i18n!(ctx.1, "Classic editor (any changes will be lost)")</a>
|
<a href="#" id="close-editor">@i18n!(ctx.1, "Classic editor (any changes will be lost)")</a>
|
||||||
</header>
|
</header>
|
||||||
|
<article id="edition-area">
|
||||||
|
<h1 contenteditable id="editor-title" data-placeholder="@i18n!(ctx.1, "Type your title")">@form.title</h1>
|
||||||
|
<h2 contenteditable id="editor-subtitle" data-placeholder="@i18n!(ctx.1, "Type a subtitle or a summary")">@form.subtitle</h2>
|
||||||
|
<article contenteditable id="editor-main" data-placeholder="@i18n!(ctx.1, "Write your article here. You can use markdown.")"></article>
|
||||||
|
</article>
|
||||||
|
<aside id="plume-editor-aside" style="display: none;">
|
||||||
|
<div id="options-page">
|
||||||
|
<button id="publish" class="button">@i18n!(ctx.1, "Publish")</button>
|
||||||
|
|
||||||
|
@input!(ctx.1, tags (optional text), "Tags, separated by commas", form, errors.clone(), "")
|
||||||
|
@input!(ctx.1, license (optional text), "License", "Leave it empty to reserve all rights", form, errors.clone(), "")
|
||||||
|
@:image_select(ctx, "cover", i18n!(ctx.1, "Illustration"), true, medias.clone(), form.cover)
|
||||||
</div>
|
</div>
|
||||||
|
<div id="publish-page" style="display: none">
|
||||||
|
<a href="#" id="cancel-publish">@i18n!(ctx.1, "Cancel")</a>
|
||||||
|
<p>@i18n!(ctx.1, "You are about to publish this article in {}"; &blog.title)</p>
|
||||||
|
<button id="confirm-publish" class="button">@i18n!(ctx.1, "Confirm publication")</button>
|
||||||
|
<button id="save-draft" class="button secondary">@i18n!(ctx.1, "Save as draft")</button>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
|
||||||
@if let Some(article) = article {
|
@if let Some(article) = article {
|
||||||
<form id="plume-fallback-editor" class="new-post" method="post" action="@uri!(posts::update: blog = blog.actor_id, slug = &article.slug)" content-size="@content_len">
|
<form id="plume-fallback-editor" class="new-post" method="post" action="@uri!(posts::update: blog = blog.actor_id, slug = &article.slug)" content-size="@content_len">
|
||||||
} else {
|
} else {
|
||||||
<form id="plume-fallback-editor" class="new-post" method="post" action="@uri!(posts::new: blog = blog.actor_id)" content-size="@content_len">
|
<form id="plume-fallback-editor" class="new-post" method="post" action="@uri!(posts::new: blog = blog.actor_id)" content-size="@content_len">
|
||||||
}
|
}
|
||||||
|
<h1 id="plume-editor-title" dir="auto">@title</h1>
|
||||||
|
|
||||||
@input!(ctx.1, title (text), "Title", form, errors.clone(), "required")
|
@input!(ctx.1, title (text), "Title", form, errors.clone(), "required")
|
||||||
@input!(ctx.1, subtitle (optional text), "Subtitle", form, errors.clone(), "")
|
@input!(ctx.1, subtitle (optional text), "Subtitle", form, errors.clone(), "")
|
||||||
|
|
||||||
@@ -32,10 +62,10 @@
|
|||||||
@format!(r#"<p class="error">{}</p>"#, errs[0].message.clone().unwrap_or_else(|| Cow::from("Unknown error")))
|
@format!(r#"<p class="error">{}</p>"#, errs[0].message.clone().unwrap_or_else(|| Cow::from("Unknown error")))
|
||||||
}
|
}
|
||||||
|
|
||||||
<label for="editor-content">@i18n!(ctx.1, "Content")<small>@i18n!(ctx.1, "Markdown syntax is supported")</small></label>
|
<label for="editor-content" dir="auto">@i18n!(ctx.1, "Content")<small>@i18n!(ctx.1, "Markdown syntax is supported")</small></label>
|
||||||
<textarea id="editor-content" name="content" rows="20">@Html(&form.content)</textarea>
|
<textarea id="editor-content" name="content" rows="20" dir="auto">@Html(&form.content)</textarea>
|
||||||
<small id="editor-left">@content_len</small>
|
<small id="editor-left" dir="auto">@content_len</small>
|
||||||
<p>
|
<p dir="auto">
|
||||||
@i18n!(ctx.1, "You can upload media to your gallery, and then copy their Markdown code into your articles to insert them.")
|
@i18n!(ctx.1, "You can upload media to your gallery, and then copy their Markdown code into your articles to insert them.")
|
||||||
<a href="@uri!(medias::new)">@i18n!(ctx.1, "Upload media")</a>
|
<a href="@uri!(medias::new)">@i18n!(ctx.1, "Upload media")</a>
|
||||||
</p>
|
</p>
|
||||||
@@ -47,7 +77,7 @@
|
|||||||
@:image_select(ctx, "cover", i18n!(ctx.1, "Illustration"), true, medias, form.cover)
|
@:image_select(ctx, "cover", i18n!(ctx.1, "Illustration"), true, medias, form.cover)
|
||||||
|
|
||||||
@if is_draft {
|
@if is_draft {
|
||||||
<label for="draft">
|
<label for="draft" dir="auto">
|
||||||
<input type="checkbox" name="draft" id="draft" checked>
|
<input type="checkbox" name="draft" id="draft" checked>
|
||||||
@i18n!(ctx.1, "This is a draft, don't publish it yet.")
|
@i18n!(ctx.1, "This is a draft, don't publish it yet.")
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -9,6 +9,6 @@
|
|||||||
@(ctx: BaseContext, post: Post, login_form: LoginForm, login_errs: ValidationErrors, remote_form: RemoteForm, remote_errs: ValidationErrors)
|
@(ctx: BaseContext, post: Post, login_form: LoginForm, login_errs: ValidationErrors, remote_form: RemoteForm, remote_errs: ValidationErrors)
|
||||||
|
|
||||||
@:remote_interact_base(ctx, i18n!(ctx.1, "Interact with {}"; post.title.clone()), i18n!(ctx.1, "Log in to interact"), i18n!(ctx.1, "Enter your full username to interact"), {
|
@:remote_interact_base(ctx, i18n!(ctx.1, "Interact with {}"; post.title.clone()), i18n!(ctx.1, "Log in to interact"), i18n!(ctx.1, "Enter your full username to interact"), {
|
||||||
<h1>@i18n!(ctx.1, "Interact with {}"; post.title.clone())</h1>
|
<h1 dir="auto">@i18n!(ctx.1, "Interact with {}"; post.title.clone())</h1>
|
||||||
@:post_card(ctx, post)
|
@:post_card(ctx, post)
|
||||||
}, login_form, login_errs, remote_form, remote_errs)
|
}, login_form, login_errs, remote_form, remote_errs)
|
||||||
|
|||||||
Reference in New Issue
Block a user