Compare commits

..

7 Commits

Author SHA1 Message Date
Marek Ľach f2273b984c Update main.scss 2019-06-01 11:19:37 +02:00
Marek Ľach 7be397b76e Update _forms.scss 2019-05-31 13:08:36 +02:00
Marek Ľach d82a7674ce Update main.scss 2019-05-31 13:07:59 +02:00
Marek Ľach 38723a4d20 Update _forms.scss 2019-05-31 10:09:25 +02:00
Marek Ľach 1fc3b3dd4e Update _forms.scss 2019-05-31 10:06:28 +02:00
Marek Ľach d2a684650a Update _article.scss 2019-05-31 09:58:47 +02:00
Marek Ľach 82659391f9 Update _forms.scss 2019-05-31 09:49:33 +02:00
28 changed files with 549 additions and 782 deletions
+46 -2
View File
@@ -1,2 +1,46 @@
You can read our Code of Conduct [here](https://docs.joinplu.me/organization/code-of-conduct).
By contributing to this repository, you agree to be bound by this Code of Conduct.
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, sexual identity and orientation, etc.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at baptiste@gelez.xyz. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
Generated
+69 -128
View File
@@ -131,10 +131,10 @@ dependencies = [
[[package]]
name = "atomicwrites"
version = "0.2.3"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -144,7 +144,7 @@ name = "atty"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -164,7 +164,7 @@ dependencies = [
"dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"debug-builders 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -176,7 +176,7 @@ dependencies = [
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace-sys 0.1.28 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -186,7 +186,7 @@ version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.37 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -457,7 +457,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"core-foundation-sys 0.6.2 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -578,10 +578,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ctrlc"
version = "3.1.3"
version = "3.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -876,7 +876,7 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -887,7 +887,7 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crc32fast 1.2.0 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -914,7 +914,7 @@ name = "fs2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -932,7 +932,7 @@ name = "fsevent-sys"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1104,7 +1104,7 @@ name = "hostname"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1188,7 +1188,7 @@ dependencies = [
"net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1238,7 +1238,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1246,7 +1246,7 @@ name = "inotify-sys"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1254,7 +1254,7 @@ name = "iovec"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1352,7 +1352,7 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.58"
version = "0.2.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -1435,7 +1435,7 @@ name = "memmap"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1515,7 +1515,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
"crc 1.8.1 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1528,7 +1528,7 @@ dependencies = [
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1547,16 +1547,6 @@ dependencies = [
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mio-uds"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "miow"
version = "0.2.1"
@@ -1599,7 +1589,7 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.3.0 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl 0.10.22 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1616,7 +1606,7 @@ version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1627,13 +1617,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "nix"
version = "0.14.1"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (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)",
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nix"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (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)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1662,7 +1664,7 @@ dependencies = [
"fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)",
"mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1704,7 +1706,7 @@ name = "num_cpus"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1726,7 +1728,7 @@ dependencies = [
"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)",
"lazy_static 1.3.0 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-sys 0.9.46 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1742,7 +1744,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.37 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1787,7 +1789,7 @@ name = "parking_lot_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1799,7 +1801,7 @@ name = "parking_lot_core"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1880,7 +1882,7 @@ dependencies = [
"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)",
"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.2 (registry+https://github.com/rust-lang/crates.io-index)",
"diesel 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"dotenv 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gettext 0.3.0 (git+https://github.com/Plume-org/gettext/?rev=294c54d74c699fbc66502b480a37cc66c1daa7f3)",
@@ -1953,7 +1955,6 @@ dependencies = [
"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)",
"shrinkwraprs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -2142,7 +2143,7 @@ name = "rand"
version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2152,7 +2153,7 @@ version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-cprng 0.1.1 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2165,7 +2166,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-cprng 0.1.1 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2176,7 +2177,7 @@ version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.2 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2231,7 +2232,7 @@ name = "rand_jitter"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2243,7 +2244,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-cprng 0.1.1 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2362,7 +2363,7 @@ dependencies = [
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-threadpool 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2383,7 +2384,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2475,7 +2476,7 @@ version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2598,7 +2599,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation-sys 0.6.2 (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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2884,7 +2885,7 @@ name = "tantivy"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"atomicwrites 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"atomicwrites 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bitpacking 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2952,7 +2953,7 @@ version = "3.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"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.55 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
"remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2974,7 +2975,7 @@ name = "termion"
version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3001,33 +3002,28 @@ name = "time"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio"
version = "0.1.21"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-sync 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-threadpool 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-trace-core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -3040,16 +3036,6 @@ dependencies = [
"futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-codec"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-current-thread"
version = "0.1.6"
@@ -3068,16 +3054,6 @@ dependencies = [
"futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-fs"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-threadpool 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-io"
version = "0.1.12"
@@ -3157,43 +3133,12 @@ dependencies = [
[[package]]
name = "tokio-trace-core"
version = "0.2.0"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-udp"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-uds"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)",
"mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "toml"
version = "0.4.10"
@@ -3521,7 +3466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum ascii_utils 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a"
"checksum askama_escape 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "719b48039ffac1564f67d70162109ba9341125cee0096a540e478355b3c724a7"
"checksum atom_syndication 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a9a7ab83635ff7a3b04856f4ad95324dccc9b947ab1e790fc5c769ee6d6f60c"
"checksum atomicwrites 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c4e1aa99513c90202b4b04cfbe3c9d51dd914f2e26215a4caa76574b00bb6393"
"checksum atomicwrites 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a3420b33cdefd3feb223dddc23739fc05cc034eb0f2be792c763e3d89e1eb6e3"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
"checksum backtrace 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "150ae7828afa7afb6d474f909d64072d21de1f3365b6e8ad8029bf7b1c6350a0"
@@ -3573,7 +3518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b"
"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c"
"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda"
"checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f"
"checksum ctrlc 3.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5531b7f0698d9220b4729f8811931dbe0e91a05be2f7b3245fdc50dd856bae26"
"checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9"
"checksum data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97"
"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
@@ -3661,7 +3606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum lettre 0.9.0 (git+https://github.com/lettre/lettre?rev=c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49)" = "<none>"
"checksum lettre_email 0.9.0 (git+https://github.com/lettre/lettre?rev=c988b1760ad8179d9e7f3fb8594d2b86cf2a0a49)" = "<none>"
"checksum levenshtein_automata 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73a004f877f468548d8d0ac4977456a249d8fabbdb8416c36db163dfc8f2e8ca"
"checksum libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "6281b86796ba5e4366000be6e9e18bf35580adf9e63fbe2294aadb587613a319"
"checksum libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "42914d39aad277d9e176efbdad68acb1d5443ab65afe0e0e4f0d49352a950880"
"checksum libsqlite3-sys 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fd6457c70bbff456d9fe49deaba35ec47c3e598bf8d7950ff0575ceb7a8a6ad1"
"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
@@ -3684,14 +3629,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7fe927a42e3807ef71defb191dc87d4e24479b221e67015fe38ae2b7b447bab"
"checksum mio 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "049ba5ca2b63e837adeee724aa9e36b408ed593529dcc802aa96ca14bd329bdf"
"checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40"
"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
"checksum multipart 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "136eed74cadb9edd2651ffba732b19a450316b680e4f48d6c79e905799e19d01"
"checksum murmurhash32 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d736ff882f0e85fe9689fb23db229616c4c00aee2b3ac282f666d8f20eb25d4a"
"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e"
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
"checksum new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f40f005c60db6e03bae699e414c58bf9aa7ea02a2d0b9bfbcf19286cc4c82b30"
"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
"checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17"
"checksum nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46f0f3210768d796e8fa79ec70ee6af172dacbe7147f5e69be5240a47778302b"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
"checksum notify 4.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ceb1a496a81dd6125f68ce772b41b83efe89a54d21768ed6d0c33c95832604e6"
@@ -3824,21 +3769,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
"checksum tokio 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "ec2ffcf4bcfc641413fa0f1427bf8f91dfc78f56a6559cbf50e04837ae442a87"
"checksum tokio 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "94a1f9396aec29d31bb16c24d155cfa144d1af91c40740125db3131bdaf76da8"
"checksum tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46"
"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f"
"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443"
"checksum tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "83ea44c6c0773cc034771693711c35c677b4b5a4b21b9e7071704c54de7d555e"
"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af"
"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926"
"checksum tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6af16bfac7e112bea8b0442542161bfc41cbfa4466b580bdda7d18cb88b911ce"
"checksum tokio-sync 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5b2f843ffdf8d6e1f90bddd48da43f99ab071660cd92b7ec560ef3cdfd7a409a"
"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119"
"checksum tokio-threadpool 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72558af20be886ea124595ea0f806dd5703b8958e4705429dd58b3d8231f72f2"
"checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e"
"checksum tokio-trace-core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9c8a256d6956f7cb5e2bdfe8b1e8022f1a09206c6c2b1ba00f3b746b260c613"
"checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92"
"checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445"
"checksum tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "350c9edade9830dc185ae48ba45667a445ab59f6167ef6d0254ec9d2430d9dd3"
"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382"
+11
View File
@@ -0,0 +1,11 @@
<!--
If your issue is not purely technical, it should be debated on Loomio first
https://framavox.org/g/WK40YHMA/plume
-->
Plume version (and/or commit):
+9 -35
View File
@@ -3,53 +3,27 @@
Plume
</h1>
<p align="center">
<a href="https://github.com/Plume-org/Plume/"><img alt="CircleCI" src="https://img.shields.io/circleci/build/gh/Plume-org/Plume.svg"></a>
<a href="https://codecov.io/gh/Plume-org/Plume"><img src="https://codecov.io/gh/Plume-org/Plume/branch/master/graph/badge.svg" alt="Code coverage"></a>
<a title="Crowdin" target="_blank" href="https://crowdin.com/project/plume"><img src="https://d322cqt584bo4o.cloudfront.net/plume/localized.svg"></a>
<a href="https://hub.docker.com/r/plumeorg/plume"><img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/plumeorg/plume.svg"></a>
<a href="https://liberapay.com/Plume"><img alt="Liberapay patrons" src="https://img.shields.io/liberapay/patrons/Plume.svg"></a>
<a href="https://travis-ci.org/Plume-org/Plume"><img src="https://travis-ci.org/Plume-org/Plume.svg?branch=master" alt="Build status"></a>
<a href="hhttps://codecov.io/gh/Plume-org/Plume"><img src="https://codecov.io/gh/Plume-org/Plume/branch/master/graph/badge.svg" alt="Code coverage"></a>
</p>
<p align="center">
<a href="https://joinplu.me/">Website</a>
<a href="https://docs.joinplu.me/">Documentation</a>
<a href="https://docs.joinplu.me/contribute">Contribute</a>
<a href="https://contribute.joinplu.me/">Contribute</a>
<a href="https://joinplu.me/#instances">Instances list</a>
</p>
Plume is a **federated blogging engine**, based on *ActivityPub*. It is written in *Rust*, with the *Rocket* framework, and *Diesel* to interact with the database.
The front-end uses *Ructe* templates, *WASM* and *SCSS*.
Plume is a federated blogging engine, based on ActivityPub. It uses the Rocket framework, and Diesel to interact with the database.
## Features
It is not yet ready for production use, but we have all the basic features (account management, blogs, articles, comments, etc) and a basic federation.
A lot of features are still missing, but what is already here should be quite stable. Current and planned features include:
Feel free to join our Matrix room: `#plume:disroot.org` to discuss about the project!
- **A blog-centric approach**: you can create as much blogs as you want with your account, to keep your different publications separated.
- **Media management**: you can upload pictures to illustrate your articles, but also audio files if you host a podcast, and manage them all from Plume.
- **Federation**: Plume is part of a network of interconnected websites called the Fediverse. Each of these websites (often called *instances*) have their own
rules and thematics, but they can all communicate with each other.
- **Collaborative writing**: invite other people to your blogs, and write articles together.
We also have a [Loomio group](https://framavox.org/g/WK40YHMA/plume) to debate and vote and features. Feel free to join it to take part in Plume's decision process.
## Get involved
For more informations on the different ways to contribute, check out our [contribution page](https://contribute.joinplu.me). We are welcoming any kind of contribution, and you can probably find a way to help.
If you want to have regular news about the project, the best place is probably [our blog](https://fediverse.blog/~/PlumeDev), or our Matrix room: [`#plume:disroot.org`](https://riot.im/app/#/room/#plume:disroot.org).
If you want to contribute more, a good first step is to read [our contribution guides](https://docs.joinplu.me/contribute). We accept all kind of contribution:
- [Back-end or front-end development](https://docs.joinplu.me/contribute/development/)
- [Translations](https://docs.joinplu.me/contribute/translations/)
- [Documentation](https://docs.joinplu.me/contribute/documentation/)
- UI and/or UX design (we don't have a dedicated guide yet, but [we can talk](https://docs.joinplu.me/contribute/discussion/) to see how we can work together!)
- [Taking part in discussions](https://docs.joinplu.me/contribute/discussion/)
- [Financial support](https://docs.joinplu.me/contribute/donations/)
But this list is not exhaustive and if you want to contribute differently you are welcome too!
As we want the various spaces related to the project (GitHub, Matrix, Loomio, etc) to be as safe as possible for everyone, we adopted [a code of conduct](https://docs.joinplu.me/organization/code-of-conduct). Please read it and make sure you accept it before contributing.
## Starting your own instance
We provide various way to install Plume: from source, with pre-built binaries, with Docker or with YunoHost.
For detailed explanations, please refer to [the documentation](https://docs.joinplu.me/installation/).
As we want the various spaces related to the project (GitHub, Matrix, Loomio, etc) to be as safe as possible for everyone, we adopted [a code of conduct](https://github.com/Plume-org/Plume/blob/master/CODE_OF_CONDUCT.md). Please read it and make sure you accept it before contributing.
@@ -1 +0,0 @@
DROP TABLE password_reset_requests;
@@ -1,9 +0,0 @@
CREATE TABLE password_reset_requests (
id SERIAL PRIMARY KEY,
email VARCHAR NOT NULL,
token VARCHAR NOT NULL,
expiration_date TIMESTAMP NOT NULL
);
CREATE INDEX password_reset_requests_token ON password_reset_requests (token);
CREATE UNIQUE INDEX password_reset_requests_email ON password_reset_requests (email);
@@ -1 +0,0 @@
DROP TABLE password_reset_requests;
@@ -1,9 +0,0 @@
CREATE TABLE password_reset_requests (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
email VARCHAR NOT NULL,
token VARCHAR NOT NULL,
expiration_date DATETIME NOT NULL
);
CREATE INDEX password_reset_requests_token ON password_reset_requests (token);
CREATE UNIQUE INDEX password_reset_requests_email ON password_reset_requests (email);
+1 -1
View File
@@ -25,7 +25,7 @@ fn main() {
.subcommand(users::command());
let matches = app.clone().get_matches();
dotenv::dotenv().expect("error while reading .env");
dotenv::dotenv().ok();
let conn = Conn::establish(CONFIG.database_url.as_str());
let _ = conn.as_ref().map(|conn| Instance::cache_local(conn));
-1
View File
@@ -19,7 +19,6 @@ serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
shrinkwraprs = "0.2.1"
tokio = "0.1.21"
[dependencies.chrono]
features = ["serde"]
+28 -27
View File
@@ -1,6 +1,6 @@
use activitypub::{Activity, Link, Object};
use array_tool::vec::Uniq;
use reqwest::r#async::ClientBuilder;
use reqwest::ClientBuilder;
use rocket::{
http::Status,
request::{FromRequest, Request},
@@ -8,7 +8,6 @@ use rocket::{
Outcome,
};
use serde_json;
use tokio::prelude::*;
use self::sign::Signable;
@@ -130,36 +129,38 @@ where
.sign(sender)
.expect("activity_pub::broadcast: signature error");
let mut rt = tokio::runtime::current_thread::Runtime::new()
.expect("Error while initializing tokio runtime for federation");
let client = ClientBuilder::new()
.connect_timeout(std::time::Duration::from_secs(5))
.build()
.expect("Can't build client");
for inbox in boxes {
// TODO: run it in Sidekiq or something like that
let body = signed.to_string();
let mut headers = request::headers();
headers.insert("Digest", request::Digest::digest(&body));
rt.spawn(
client
.post(&inbox)
.headers(headers.clone())
.header(
"Signature",
request::signature(sender, &headers)
.expect("activity_pub::broadcast: request signature error"),
)
.body(body)
.send()
.and_then(|r| r.into_body().concat2())
.map(move |response| {
println!("Successfully sent activity to inbox ({})", inbox);
println!("Response: \"{:?}\"\n", response)
})
.map_err(|e| println!("Error while sending to inbox ({:?})", e)),
);
let res = ClientBuilder::new()
.connect_timeout(Some(std::time::Duration::from_secs(5)))
.build()
.and_then(|client| {
client
.post(&inbox)
.headers(headers.clone())
.header(
"Signature",
request::signature(sender, &headers)
.expect("activity_pub::broadcast: request signature error"),
)
.body(body)
.send()
});
match res {
Ok(mut r) => {
println!("Successfully sent activity to inbox ({})", inbox);
if let Ok(response) = r.text() {
println!("Response: \"{:?}\"\n\n", response)
} else {
println!("Error while reading response")
}
}
Err(e) => println!("Error while sending to inbox ({:?})", e),
}
}
rt.run().unwrap();
}
#[derive(Shrinkwrap, Clone, Serialize, Deserialize)]
-1
View File
@@ -20,7 +20,6 @@ extern crate shrinkwraprs;
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate tokio;
pub mod activity_pub;
pub mod utils;
-2
View File
@@ -65,7 +65,6 @@ pub enum Error {
Unauthorized,
Url,
Webfinger,
Expired,
}
impl From<bcrypt::BcryptError> for Error {
@@ -368,7 +367,6 @@ pub mod medias;
pub mod mentions;
pub mod migrations;
pub mod notifications;
pub mod password_reset_requests;
pub mod plume_rocket;
pub mod post_authors;
pub mod posts;
-164
View File
@@ -1,164 +0,0 @@
use chrono::{offset::Utc, Duration, NaiveDateTime};
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
use schema::password_reset_requests;
use {Connection, Error, Result};
#[derive(Clone, Identifiable, Queryable)]
pub struct PasswordResetRequest {
pub id: i32,
pub email: String,
pub token: String,
pub expiration_date: NaiveDateTime,
}
#[derive(Insertable)]
#[table_name = "password_reset_requests"]
pub struct NewPasswordResetRequest {
pub email: String,
pub token: String,
pub expiration_date: NaiveDateTime,
}
const TOKEN_VALIDITY_HOURS: i64 = 2;
impl PasswordResetRequest {
pub fn insert(conn: &Connection, email: &str) -> Result<String> {
// first, delete other password reset tokens associated with this email:
let existing_requests =
password_reset_requests::table.filter(password_reset_requests::email.eq(email));
diesel::delete(existing_requests).execute(conn)?;
// now, generate a random token, set the expiry date,
// and insert it into the DB:
let token = plume_common::utils::random_hex();
let expiration_date = Utc::now()
.naive_utc()
.checked_add_signed(Duration::hours(TOKEN_VALIDITY_HOURS))
.expect("could not calculate expiration date");
let new_request = NewPasswordResetRequest {
email: email.to_owned(),
token: token.clone(),
expiration_date,
};
diesel::insert_into(password_reset_requests::table)
.values(new_request)
.execute(conn)
.map_err(Error::from)?;
Ok(token)
}
pub fn find_by_token(conn: &Connection, token: &str) -> Result<Self> {
let token = password_reset_requests::table
.filter(password_reset_requests::token.eq(token))
.first::<Self>(conn)
.map_err(Error::from)?;
if token.expiration_date < Utc::now().naive_utc() {
return Err(Error::Expired);
}
Ok(token)
}
pub fn find_and_delete_by_token(conn: &Connection, token: &str) -> Result<Self> {
let request = Self::find_by_token(&conn, &token)?;
let filter =
password_reset_requests::table.filter(password_reset_requests::id.eq(request.id));
diesel::delete(filter).execute(conn)?;
Ok(request)
}
}
#[cfg(test)]
mod tests {
use super::*;
use diesel::Connection;
use tests::db;
use users::tests as user_tests;
#[test]
fn test_insert_and_find_password_reset_request() {
let conn = db();
conn.test_transaction::<_, (), _>(|| {
user_tests::fill_database(&conn);
let admin_email = "admin@example.com";
let token = PasswordResetRequest::insert(&conn, admin_email)
.expect("couldn't insert new request");
let request = PasswordResetRequest::find_by_token(&conn, &token)
.expect("couldn't retrieve request");
assert!(&token.len() > &32);
assert_eq!(&request.email, &admin_email);
Ok(())
});
}
#[test]
fn test_insert_delete_previous_password_reset_request() {
let conn = db();
conn.test_transaction::<_, (), _>(|| {
user_tests::fill_database(&conn);
let admin_email = "admin@example.com";
PasswordResetRequest::insert(&conn, &admin_email).expect("couldn't insert new request");
PasswordResetRequest::insert(&conn, &admin_email)
.expect("couldn't insert second request");
let count = password_reset_requests::table.count().get_result(&*conn);
assert_eq!(Ok(1), count);
Ok(())
});
}
#[test]
fn test_find_password_reset_request_by_token_time() {
let conn = db();
conn.test_transaction::<_, (), _>(|| {
user_tests::fill_database(&conn);
let admin_email = "admin@example.com";
let token = "abcdef";
let now = Utc::now().naive_utc();
diesel::insert_into(password_reset_requests::table)
.values((
password_reset_requests::email.eq(&admin_email),
password_reset_requests::token.eq(&token),
password_reset_requests::expiration_date.eq(now),
))
.execute(&*conn)
.expect("could not insert request");
match PasswordResetRequest::find_by_token(&conn, &token) {
Err(Error::Expired) => (),
_ => panic!("Received unexpected result finding expired token"),
}
Ok(())
});
}
#[test]
fn test_find_and_delete_password_reset_request() {
let conn = db();
conn.test_transaction::<_, (), _>(|| {
user_tests::fill_database(&conn);
let admin_email = "admin@example.com";
let token = PasswordResetRequest::insert(&conn, &admin_email)
.expect("couldn't insert new request");
PasswordResetRequest::find_and_delete_by_token(&conn, &token)
.expect("couldn't find and delete request");
let count = password_reset_requests::table.count().get_result(&*conn);
assert_eq!(Ok(0), count);
Ok(())
});
}
}
-10
View File
@@ -141,15 +141,6 @@ table! {
}
}
table! {
password_reset_requests (id) {
id -> Int4,
email -> Varchar,
token -> Varchar,
expiration_date -> Timestamp,
}
}
table! {
post_authors (id) {
id -> Int4,
@@ -256,7 +247,6 @@ allow_tables_to_appear_in_same_query!(
medias,
mentions,
notifications,
password_reset_requests,
post_authors,
posts,
reshares,
+6 -2
View File
@@ -24,9 +24,13 @@ if [ $ARCH == "aarch64" -o $ARCH == "armv71" ] ; then
apt-get install -y --no-install-recommends build-essential subversion ninja-build cmake
mkdir -p /scratch/src
cd /scratch/src
svn co http://llvm.org/svn/llvm-project/llvm/tags/RELEASE_800/final/ llvm
# Pin LLVM to post 7.0.1 tag and pin to a known-good revision for Plume builds
svn co -r350977 http://llvm.org/svn/llvm-project/llvm/trunk/ llvm
cd /scratch/src/llvm/tools
svn co http://llvm.org/svn/llvm-project/lld/tags/RELEASE_800/final/ lld
# Pin lld to post 7.0.1 tag and pin to a known-good revision for Plume builds
svn co -r350975 http://llvm.org/svn/llvm-project/lld/trunk lld
#svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
#svn co http://llvm.org/svn/llvm-project/clang-tools-extra/trunk extra
mkdir -p /scratch/build/arm
cd /scratch/build/arm
if [ "$ARCH" == "aarch64" ] ; then
+1 -1
View File
@@ -73,7 +73,7 @@ compile_i18n!();
/// Initializes a database pool.
fn init_pool() -> Option<DbPool> {
dotenv::dotenv().unwrap();
dotenv::dotenv().ok();
let manager = ConnectionManager::<Connection>::new(CONFIG.database_url.as_str());
let pool = DbPool::builder()
+122 -125
View File
@@ -16,7 +16,7 @@ use plume_models::{
blog_authors::*, blogs::*, instance::Instance, medias::*, posts::Post, safe_string::SafeString,
users::User, Connection, PlumeRocket,
};
use routes::{errors::ErrorPage, Page, RespondOrRedirect};
use routes::{errors::ErrorPage, Page};
use template_utils::{IntoContext, Ructe};
#[get("/~/<name>?<page>", rank = 2)]
@@ -84,7 +84,10 @@ fn valid_slug(title: &str) -> Result<(), ValidationError> {
}
#[post("/blogs/new", data = "<form>")]
pub fn create(form: LenientForm<NewBlogForm>, rockets: PlumeRocket) -> RespondOrRedirect {
pub fn create(
form: LenientForm<NewBlogForm>,
rockets: PlumeRocket,
) -> Result<Flash<Redirect>, Ructe> {
let slug = utils::make_actor_id(&form.title);
let conn = &*rockets.conn;
let intl = &rockets.intl.catalog;
@@ -108,43 +111,42 @@ pub fn create(form: LenientForm<NewBlogForm>, rockets: PlumeRocket) -> RespondOr
);
}
if !errors.is_empty() {
return render!(blogs::new(&rockets.to_context(), &*form, errors)).into();
}
let blog = Blog::insert(
&*conn,
NewBlog::new_local(
slug.clone(),
form.title.to_string(),
String::from(""),
Instance::get_local()
.expect("blog::create: instance error")
.id,
if errors.is_empty() {
let blog = Blog::insert(
&*conn,
NewBlog::new_local(
slug.clone(),
form.title.to_string(),
String::from(""),
Instance::get_local()
.expect("blog::create: instance error")
.id,
)
.expect("blog::create: new local error"),
)
.expect("blog::create: new local error"),
)
.expect("blog::create: error");
.expect("blog::create: error");
BlogAuthor::insert(
&*conn,
NewBlogAuthor {
blog_id: blog.id,
author_id: user.id,
is_owner: true,
},
)
.expect("blog::create: author error");
BlogAuthor::insert(
&*conn,
NewBlogAuthor {
blog_id: blog.id,
author_id: user.id,
is_owner: true,
},
)
.expect("blog::create: author error");
Flash::success(
Redirect::to(uri!(details: name = slug.clone(), page = _)),
&i18n!(intl, "Your blog was successfully created!"),
)
.into()
Ok(Flash::success(
Redirect::to(uri!(details: name = slug.clone(), page = _)),
&i18n!(intl, "Your blog was successfully created!"),
))
} else {
Err(render!(blogs::new(&rockets.to_context(), &*form, errors)))
}
}
#[post("/~/<name>/delete")]
pub fn delete(name: String, rockets: PlumeRocket) -> RespondOrRedirect {
pub fn delete(name: String, rockets: PlumeRocket) -> Result<Flash<Redirect>, Ructe> {
let conn = &*rockets.conn;
let blog = Blog::find_by_fqn(&rockets, &name).expect("blog::delete: blog not found");
@@ -156,21 +158,19 @@ pub fn delete(name: String, rockets: PlumeRocket) -> RespondOrRedirect {
{
blog.delete(&conn, &rockets.searcher)
.expect("blog::expect: deletion error");
Flash::success(
Ok(Flash::success(
Redirect::to(uri!(super::instance::index)),
i18n!(rockets.intl.catalog, "Your blog was deleted."),
)
.into()
))
} else {
// TODO actually return 403 error code
render!(errors::not_authorized(
Err(render!(errors::not_authorized(
&rockets.to_context(),
i18n!(
rockets.intl.catalog,
"You are not allowed to delete this blog."
)
))
.into()
)))
}
}
@@ -236,107 +236,104 @@ pub fn update(
name: String,
form: LenientForm<EditForm>,
rockets: PlumeRocket,
) -> RespondOrRedirect {
) -> Result<Flash<Redirect>, Ructe> {
let conn = &*rockets.conn;
let intl = &rockets.intl.catalog;
let mut blog = Blog::find_by_fqn(&rockets, &name).expect("blog::update: blog not found");
if !rockets
if rockets
.user
.clone()
.and_then(|u| u.is_author_in(&*conn, &blog).ok())
.unwrap_or(false)
{
let user = rockets
.user
.clone()
.expect("blogs::edit: User was None while it shouldn't");
form.validate()
.and_then(|_| {
if let Some(icon) = form.icon {
if !check_media(&*conn, icon, &user) {
let mut errors = ValidationErrors::new();
errors.add(
"",
ValidationError {
code: Cow::from("icon"),
message: Some(Cow::from(i18n!(
intl,
"You can't use this media as a blog icon."
))),
params: HashMap::new(),
},
);
return Err(errors);
}
}
if let Some(banner) = form.banner {
if !check_media(&*conn, banner, &user) {
let mut errors = ValidationErrors::new();
errors.add(
"",
ValidationError {
code: Cow::from("banner"),
message: Some(Cow::from(i18n!(
intl,
"You can't use this media as a blog banner."
))),
params: HashMap::new(),
},
);
return Err(errors);
}
}
blog.title = form.title.clone();
blog.summary = form.summary.clone();
blog.summary_html = SafeString::new(
&utils::md_to_html(
&form.summary,
None,
true,
Some(Media::get_media_processor(
&conn,
blog.list_authors(&conn)
.expect("Couldn't get list of authors")
.iter()
.collect(),
)),
)
.0,
);
blog.icon_id = form.icon;
blog.banner_id = form.banner;
blog.save_changes::<Blog>(&*conn)
.expect("Couldn't save blog changes");
Ok(Flash::success(
Redirect::to(uri!(details: name = name, page = _)),
i18n!(intl, "Your blog information have been updated."),
))
})
.map_err(|err| {
let medias = Media::for_user(&*conn, user.id).expect("Couldn't list media");
render!(blogs::edit(
&rockets.to_context(),
&blog,
medias,
&*form,
err
))
})
} else {
// TODO actually return 403 error code
return render!(errors::not_authorized(
Err(render!(errors::not_authorized(
&rockets.to_context(),
i18n!(
rockets.intl.catalog,
"You are not allowed to edit this blog."
)
))
.into();
)))
}
let user = rockets
.user
.clone()
.expect("blogs::edit: User was None while it shouldn't");
form.validate()
.and_then(|_| {
if let Some(icon) = form.icon {
if !check_media(&*conn, icon, &user) {
let mut errors = ValidationErrors::new();
errors.add(
"",
ValidationError {
code: Cow::from("icon"),
message: Some(Cow::from(i18n!(
intl,
"You can't use this media as a blog icon."
))),
params: HashMap::new(),
},
);
return Err(errors);
}
}
if let Some(banner) = form.banner {
if !check_media(&*conn, banner, &user) {
let mut errors = ValidationErrors::new();
errors.add(
"",
ValidationError {
code: Cow::from("banner"),
message: Some(Cow::from(i18n!(
intl,
"You can't use this media as a blog banner."
))),
params: HashMap::new(),
},
);
return Err(errors);
}
}
blog.title = form.title.clone();
blog.summary = form.summary.clone();
blog.summary_html = SafeString::new(
&utils::md_to_html(
&form.summary,
None,
true,
Some(Media::get_media_processor(
&conn,
blog.list_authors(&conn)
.expect("Couldn't get list of authors")
.iter()
.collect(),
)),
)
.0,
);
blog.icon_id = form.icon;
blog.banner_id = form.banner;
blog.save_changes::<Blog>(&*conn)
.expect("Couldn't save blog changes");
Ok(Flash::success(
Redirect::to(uri!(details: name = name, page = _)),
i18n!(intl, "Your blog information have been updated."),
))
})
.map_err(|err| {
let medias = Media::for_user(&*conn, user.id).expect("Couldn't list media");
render!(blogs::edit(
&rockets.to_context(),
&blog,
medias,
&*form,
err
))
})
.unwrap()
.into()
}
#[get("/~/<name>/outbox")]
+30 -30
View File
@@ -13,7 +13,7 @@ use plume_models::{
admin::Admin, comments::Comment, db_conn::DbConn, headers::Headers, instance::*, posts::Post,
safe_string::SafeString, users::User, Error, PlumeRocket, CONFIG,
};
use routes::{errors::ErrorPage, rocket_uri_macro_static_files, Page, RespondOrRedirect};
use routes::{errors::ErrorPage, rocket_uri_macro_static_files, Page};
use template_utils::{IntoContext, Ructe};
#[get("/")]
@@ -114,36 +114,36 @@ pub fn update_settings(
_admin: Admin,
form: LenientForm<InstanceSettingsForm>,
rockets: PlumeRocket,
) -> RespondOrRedirect {
) -> Result<Flash<Redirect>, Ructe> {
let conn = &*rockets.conn;
if let Err(e) = form.validate() {
let local_inst =
Instance::get_local().expect("instance::update_settings: local instance error");
render!(instance::admin(
&rockets.to_context(),
local_inst,
form.clone(),
e
))
.into()
} else {
let instance =
Instance::get_local().expect("instance::update_settings: local instance error");
instance
.update(
conn,
form.name.clone(),
form.open_registrations,
form.short_description.clone(),
form.long_description.clone(),
)
.expect("instance::update_settings: save error");
Flash::success(
Redirect::to(uri!(admin)),
i18n!(rockets.intl.catalog, "Instance settings have been saved."),
)
.into()
}
form.validate()
.and_then(|_| {
let instance =
Instance::get_local().expect("instance::update_settings: local instance error");
instance
.update(
conn,
form.name.clone(),
form.open_registrations,
form.short_description.clone(),
form.long_description.clone(),
)
.expect("instance::update_settings: save error");
Ok(Flash::success(
Redirect::to(uri!(admin)),
i18n!(rockets.intl.catalog, "Instance settings have been saved."),
))
})
.or_else(|e| {
let local_inst =
Instance::get_local().expect("instance::update_settings: local instance error");
Err(render!(instance::admin(
&rockets.to_context(),
local_inst,
form.clone(),
e
)))
})
}
#[get("/admin/instances?<page>")]
+64 -63
View File
@@ -38,75 +38,76 @@ pub fn upload(
ct: &ContentType,
conn: DbConn,
) -> Result<Redirect, status::BadRequest<&'static str>> {
if !ct.is_form_data() {
return Ok(Redirect::to(uri!(new)));
}
if ct.is_form_data() {
let (_, boundary) = ct
.params()
.find(|&(k, _)| k == "boundary")
.ok_or_else(|| status::BadRequest(Some("No boundary")))?;
let (_, boundary) = ct
.params()
.find(|&(k, _)| k == "boundary")
.ok_or_else(|| status::BadRequest(Some("No boundary")))?;
match Multipart::with_body(data.open(), boundary).save().temp() {
SaveResult::Full(entries) => {
let fields = entries.fields;
if let SaveResult::Full(entries) = Multipart::with_body(data.open(), boundary).save().temp() {
let fields = entries.fields;
let filename = fields
.get("file")
.and_then(|v| v.iter().next())
.ok_or_else(|| status::BadRequest(Some("No file uploaded")))?
.headers
.filename
.clone();
// Remove extension if it contains something else than just letters and numbers
let ext = filename
.and_then(|f| {
f.rsplit('.')
.next()
.and_then(|ext| {
if ext.chars().any(|c| !c.is_alphanumeric()) {
None
} else {
Some(ext.to_lowercase())
}
let filename = fields
.get("file")
.and_then(|v| v.iter().next())
.ok_or_else(|| status::BadRequest(Some("No file uploaded")))?
.headers
.filename
.clone();
// Remove extension if it contains something else than just letters and numbers
let ext = filename
.and_then(|f| {
f.rsplit('.')
.next()
.and_then(|ext| {
if ext.chars().any(|c| !c.is_alphanumeric()) {
None
} else {
Some(ext.to_lowercase())
}
})
.map(|ext| format!(".{}", ext))
})
.map(|ext| format!(".{}", ext))
})
.unwrap_or_default();
let dest = format!("static/media/{}{}", GUID::rand().to_string(), ext);
.unwrap_or_default();
let dest = format!("static/media/{}{}", GUID::rand().to_string(), ext);
match fields["file"][0].data {
SavedData::Bytes(ref bytes) => fs::write(&dest, bytes)
.map_err(|_| status::BadRequest(Some("Couldn't save upload")))?,
SavedData::File(ref path, _) => {
fs::copy(path, &dest)
.map_err(|_| status::BadRequest(Some("Couldn't copy upload")))?;
}
_ => {
return Ok(Redirect::to(uri!(new)));
match fields["file"][0].data {
SavedData::Bytes(ref bytes) => fs::write(&dest, bytes)
.map_err(|_| status::BadRequest(Some("Couldn't save upload")))?,
SavedData::File(ref path, _) => {
fs::copy(path, &dest)
.map_err(|_| status::BadRequest(Some("Couldn't copy upload")))?;
}
_ => {
return Ok(Redirect::to(uri!(new)));
}
}
let has_cw = !read(&fields["cw"][0].data)
.map(|cw| cw.is_empty())
.unwrap_or(false);
let media = Media::insert(
&*conn,
NewMedia {
file_path: dest,
alt_text: read(&fields["alt"][0].data)?,
is_remote: false,
remote_url: None,
sensitive: has_cw,
content_warning: if has_cw {
Some(read(&fields["cw"][0].data)?)
} else {
None
},
owner_id: user.id,
},
)
.map_err(|_| status::BadRequest(Some("Error while saving media")))?;
Ok(Redirect::to(uri!(details: id = media.id)))
}
SaveResult::Partial(_, _) | SaveResult::Error(_) => Ok(Redirect::to(uri!(new))),
}
let has_cw = !read(&fields["cw"][0].data)
.map(|cw| cw.is_empty())
.unwrap_or(false);
let media = Media::insert(
&*conn,
NewMedia {
file_path: dest,
alt_text: read(&fields["alt"][0].data)?,
is_remote: false,
remote_url: None,
sensitive: has_cw,
content_warning: if has_cw {
Some(read(&fields["cw"][0].data)?)
} else {
None
},
owner_id: user.id,
},
)
.map_err(|_| status::BadRequest(Some("Error while saving media")))?;
Ok(Redirect::to(uri!(details: id = media.id)))
} else {
Ok(Redirect::to(uri!(new)))
}
+1 -37
View File
@@ -7,51 +7,15 @@ use rocket::{
RawStr, Status,
},
request::{self, FromFormValue, FromRequest, Request},
response::{Flash, NamedFile, Redirect},
response::NamedFile,
Outcome,
};
use std::path::{Path, PathBuf};
use template_utils::Ructe;
use plume_models::{posts::Post, Connection};
const ITEMS_PER_PAGE: i32 = 12;
/// Special return type used for routes that "cannot fail", and instead
/// `Redirect`, or `Flash<Redirect>`, when we cannot deliver a `Ructe` Response
#[allow(clippy::large_enum_variant)]
#[derive(Responder)]
pub enum RespondOrRedirect {
Response(Ructe),
FlashResponse(Flash<Ructe>),
Redirect(Redirect),
FlashRedirect(Flash<Redirect>),
}
impl From<Ructe> for RespondOrRedirect {
fn from(response: Ructe) -> Self {
RespondOrRedirect::Response(response)
}
}
impl From<Flash<Ructe>> for RespondOrRedirect {
fn from(response: Flash<Ructe>) -> Self {
RespondOrRedirect::FlashResponse(response)
}
}
impl From<Redirect> for RespondOrRedirect {
fn from(redirect: Redirect) -> Self {
RespondOrRedirect::Redirect(redirect)
}
}
impl From<Flash<Redirect>> for RespondOrRedirect {
fn from(redirect: Flash<Redirect>) -> Self {
RespondOrRedirect::FlashRedirect(redirect)
}
}
#[derive(Shrinkwrap, Copy, Clone, UriDisplayQuery)]
pub struct Page(i32);
+28 -37
View File
@@ -26,9 +26,7 @@ use plume_models::{
users::User,
Error, PlumeRocket,
};
use routes::{
comments::NewCommentForm, errors::ErrorPage, ContentLen, RemoteForm, RespondOrRedirect,
};
use routes::{comments::NewCommentForm, errors::ErrorPage, ContentLen, RemoteForm};
use template_utils::{IntoContext, Ructe};
#[get("/~/<blog>/<slug>?<responding_to>", rank = 4)]
@@ -42,23 +40,17 @@ pub fn details(
let user = rockets.user.clone();
let blog = Blog::find_by_fqn(&rockets, &blog)?;
let post = Post::find_by_slug(&*conn, &slug, blog.id)?;
if !(post.published
if post.published
|| post
.get_authors(&*conn)?
.into_iter()
.any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0)))
.any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0))
{
return Ok(render!(errors::not_authorized(
&rockets.to_context(),
i18n!(rockets.intl.catalog, "This post isn't published yet.")
)));
}
let comments = CommentTree::from_post(&*conn, &post, user.as_ref())?;
let comments = CommentTree::from_post(&*conn, &post, user.as_ref())?;
let previous = responding_to.and_then(|r| Comment::get(&*conn, r).ok());
let previous = responding_to.and_then(|r| Comment::get(&*conn, r).ok());
Ok(render!(posts::details(
Ok(render!(posts::details(
&rockets.to_context(),
post.clone(),
blog,
@@ -95,6 +87,12 @@ pub fn details(
user.and_then(|u| u.is_following(&*conn, post.get_authors(&*conn).ok()?[0].id).ok()).unwrap_or(false),
post.get_authors(&*conn)?[0].clone()
)))
} else {
Ok(render!(errors::not_authorized(
&rockets.to_context(),
i18n!(rockets.intl.catalog, "This post isn't published yet.")
)))
}
}
#[get("/~/<blog>/<slug>", rank = 3)]
@@ -221,7 +219,7 @@ pub fn update(
cl: ContentLen,
form: LenientForm<NewPostForm>,
rockets: PlumeRocket,
) -> RespondOrRedirect {
) -> Result<Flash<Redirect>, Ructe> {
let conn = &*rockets.conn;
let b = Blog::find_by_fqn(&rockets, &blog).expect("post::update: blog error");
let mut post =
@@ -257,11 +255,10 @@ pub fn update(
.expect("posts::update: is author in error")
{
// actually it's not "Ok"…
Flash::error(
Ok(Flash::error(
Redirect::to(uri!(super::blogs::details: name = blog, page = _)),
i18n!(&intl, "You are not allowed to publish on this blog."),
)
.into()
))
} else {
let (content, mentions, hashtags) = utils::md_to_html(
form.content.to_string().as_ref(),
@@ -348,15 +345,14 @@ pub fn update(
}
}
Flash::success(
Ok(Flash::success(
Redirect::to(uri!(details: blog = blog, slug = new_slug, responding_to = _)),
i18n!(intl, "Your article has been updated."),
)
.into()
))
}
} else {
let medias = Media::for_user(&*conn, user.id).expect("posts:update: medias error");
render!(posts::new(
Err(render!(posts::new(
&rockets.to_context(),
i18n!(intl, "Edit {0}"; &form.title),
b,
@@ -367,8 +363,7 @@ pub fn update(
errors.clone(),
medias.clone(),
cl.0
))
.into()
)))
}
}
@@ -401,7 +396,7 @@ pub fn create(
form: LenientForm<NewPostForm>,
cl: ContentLen,
rockets: PlumeRocket,
) -> Result<RespondOrRedirect, ErrorPage> {
) -> Result<Flash<Redirect>, Result<Ructe, ErrorPage>> {
let conn = &*rockets.conn;
let blog = Blog::find_by_fqn(&rockets, &blog_name).expect("post::create: blog error");;
let slug = form.title.to_string().to_kebab_case();
@@ -434,8 +429,7 @@ pub fn create(
&rockets.intl.catalog,
"You are not allowed to publish on this blog."
),
)
.into());
));
}
let (content, mentions, hashtags) = utils::md_to_html(
@@ -536,11 +530,10 @@ pub fn create(
Ok(Flash::success(
Redirect::to(uri!(details: blog = blog_name, slug = slug, responding_to = _)),
i18n!(&rockets.intl.catalog, "Your article has been saved."),
)
.into())
))
} else {
let medias = Media::for_user(&*conn, user.id).expect("posts::create: medias error");
Ok(render!(posts::new(
Err(Ok(render!(posts::new(
&rockets.to_context(),
i18n!(rockets.intl.catalog, "New article"),
blog,
@@ -551,8 +544,7 @@ pub fn create(
errors.clone(),
medias,
cl.0
))
.into())
))))
}
}
@@ -635,14 +627,14 @@ pub fn remote_interact_post(
blog_name: String,
slug: String,
remote: LenientForm<RemoteForm>,
) -> Result<RespondOrRedirect, ErrorPage> {
) -> Result<Result<Ructe, Redirect>, ErrorPage> {
let target = Blog::find_by_fqn(&rockets, &blog_name)
.and_then(|blog| Post::find_by_slug(&rockets.conn, &slug, blog.id))?;
if let Some(uri) = User::fetch_remote_interact_uri(&remote.remote)
.ok()
.and_then(|uri| rt_format!(uri, uri = target.ap_url).ok())
{
Ok(Redirect::to(uri).into())
Ok(Err(Redirect::to(uri)))
} else {
let mut errs = ValidationErrors::new();
errs.add("remote", ValidationError {
@@ -651,14 +643,13 @@ pub fn remote_interact_post(
params: HashMap::new(),
});
//could not get your remote url?
Ok(render!(posts::remote_interact(
Ok(Ok(render!(posts::remote_interact(
&rockets.to_context(),
target,
super::session::LoginForm::default(),
ValidationErrors::default(),
remote.clone(),
errs
))
.into())
))))
}
}
+110 -64
View File
@@ -7,7 +7,6 @@ use rocket::{
State,
};
use rocket_i18n::I18n;
use routes::RespondOrRedirect;
use std::{
borrow::Cow,
sync::{Arc, Mutex},
@@ -17,10 +16,10 @@ use validator::{Validate, ValidationError, ValidationErrors};
use mail::{build_mail, Mailer};
use plume_models::{
password_reset_requests::*,
users::{User, AUTH_COOKIE},
Error, PlumeRocket, CONFIG,
};
use routes::errors::ErrorPage;
use template_utils::{IntoContext, Ructe};
#[get("/login?<m>")]
@@ -46,7 +45,7 @@ pub fn create(
form: LenientForm<LoginForm>,
mut cookies: Cookies,
rockets: PlumeRocket,
) -> RespondOrRedirect {
) -> Result<Flash<Redirect>, Ructe> {
let conn = &*rockets.conn;
let user = User::find_by_email(&*conn, &form.email_or_name)
.or_else(|_| User::find_by_fqn(&rockets, &form.email_or_name));
@@ -77,43 +76,48 @@ pub fn create(
String::new()
};
if !errors.is_empty() {
return render!(session::login(&rockets.to_context(), None, &*form, errors)).into();
}
if errors.is_empty() {
cookies.add_private(
Cookie::build(AUTH_COOKIE, user_id)
.same_site(SameSite::Lax)
.finish(),
);
let destination = rockets
.flash_msg
.clone()
.and_then(
|(name, msg)| {
if name == "callback" {
Some(msg)
} else {
None
}
},
)
.unwrap_or_else(|| "/".to_owned());
cookies.add_private(
Cookie::build(AUTH_COOKIE, user_id)
.same_site(SameSite::Lax)
.finish(),
);
let destination = rockets
.flash_msg
.clone()
.and_then(
|(name, msg)| {
if name == "callback" {
Some(msg)
} else {
None
}
},
)
.unwrap_or_else(|| "/".to_owned());
let uri = Uri::parse(&destination)
.map(IntoOwned::into_owned)
.map_err(|_| {
render!(session::login(
&(conn, &rockets.intl.catalog, None, None),
None,
&*form,
errors
))
})?;
if let Ok(uri) = Uri::parse(&destination).map(IntoOwned::into_owned) {
Flash::success(
Ok(Flash::success(
Redirect::to(uri),
i18n!(&rockets.intl.catalog, "You are now connected."),
)
.into()
))
} else {
render!(session::login(
&(conn, &rockets.intl.catalog, None, None),
Err(render!(session::login(
&rockets.to_context(),
None,
&*form,
errors
))
.into()
)))
}
}
@@ -160,17 +164,29 @@ pub struct ResetForm {
pub fn password_reset_request(
mail: State<Arc<Mutex<Mailer>>>,
form: Form<ResetForm>,
requests: State<Arc<Mutex<Vec<ResetRequest>>>>,
rockets: PlumeRocket,
) -> Ructe {
if User::find_by_email(&*rockets.conn, &form.email).is_ok() {
let token = PasswordResetRequest::insert(&*rockets.conn, &form.email)
.expect("password_reset_request::insert: error");
let mut requests = requests.lock().unwrap();
// Remove outdated requests (more than 1 day old) to avoid the list to grow too much
requests.retain(|r| r.creation_date.elapsed().as_secs() < 24 * 60 * 60);
let url = format!("https://{}/password-reset/{}", CONFIG.base_url, token);
if User::find_by_email(&*rockets.conn, &form.email).is_ok()
&& !requests.iter().any(|x| x.mail == form.email.clone())
{
let id = plume_common::utils::random_hex();
requests.push(ResetRequest {
mail: form.email.clone(),
id: id.clone(),
creation_date: Instant::now(),
});
let link = format!("https://{}/password-reset/{}", CONFIG.base_url, id);
if let Some(message) = build_mail(
form.email.clone(),
i18n!(rockets.intl.catalog, "Password reset"),
i18n!(rockets.intl.catalog, "Here is the link to reset your password: {0}"; url),
i18n!(rockets.intl.catalog, "Here is the link to reset your password: {0}"; link),
) {
if let Some(ref mut mail) = *mail.lock().unwrap() {
mail.send(message.into())
@@ -183,10 +199,17 @@ pub fn password_reset_request(
}
#[get("/password-reset/<token>")]
pub fn password_reset_form(token: String, rockets: PlumeRocket) -> Result<Ructe, Ructe> {
PasswordResetRequest::find_by_token(&*rockets.conn, &token)
.map_err(|err| password_reset_error_response(err, &rockets))?;
pub fn password_reset_form(
token: String,
requests: State<Arc<Mutex<Vec<ResetRequest>>>>,
rockets: PlumeRocket,
) -> Result<Ructe, ErrorPage> {
requests
.lock()
.unwrap()
.iter()
.find(|x| x.id == token.clone())
.ok_or(Error::NotFound)?;
Ok(render!(session::password_reset(
&rockets.to_context(),
&NewPasswordForm::default(),
@@ -216,33 +239,56 @@ fn passwords_match(form: &NewPasswordForm) -> Result<(), ValidationError> {
#[post("/password-reset/<token>", data = "<form>")]
pub fn password_reset(
token: String,
requests: State<Arc<Mutex<Vec<ResetRequest>>>>,
form: Form<NewPasswordForm>,
rockets: PlumeRocket,
) -> Result<Flash<Redirect>, Ructe> {
form.validate()
.map_err(|err| render!(session::password_reset(&rockets.to_context(), &form, err)))?;
PasswordResetRequest::find_and_delete_by_token(&*rockets.conn, &token)
.and_then(|request| User::find_by_email(&*rockets.conn, &request.email))
.and_then(|user| user.reset_password(&*rockets.conn, &form.password))
.map_err(|err| password_reset_error_response(err, &rockets))?;
Ok(Flash::success(
Redirect::to(uri!(
new: m = _
)),
i18n!(
rockets.intl.catalog,
"Your password was successfully reset."
),
))
.and_then(|_| {
let mut requests = requests.lock().unwrap();
let req = requests
.iter()
.find(|x| x.id == token.clone())
.ok_or_else(|| to_validation(0))?
.clone();
if req.creation_date.elapsed().as_secs() < 60 * 60 * 2 {
// Reset link is only valid for 2 hours
requests.retain(|r| *r != req);
let user = User::find_by_email(&*rockets.conn, &req.mail).map_err(to_validation)?;
user.reset_password(&*rockets.conn, &form.password).ok();
Ok(Flash::success(
Redirect::to(uri!(
new: m = _
)),
i18n!(
rockets.intl.catalog,
"Your password was successfully reset."
),
))
} else {
Ok(Flash::error(
Redirect::to(uri!(
new: m = _
)),
i18n!(
rockets.intl.catalog,
"Sorry, but the link expired. Try again"
),
))
}
})
.map_err(|err| render!(session::password_reset(&rockets.to_context(), &form, err)))
}
fn password_reset_error_response(err: Error, rockets: &PlumeRocket) -> Ructe {
match err {
Error::Expired => render!(session::password_reset_request_expired(
&rockets.to_context()
)),
_ => render!(errors::not_found(&rockets.to_context())),
}
fn to_validation<T>(_: T) -> ValidationErrors {
let mut errors = ValidationErrors::new();
errors.add(
"",
ValidationError {
code: Cow::from("server_error"),
message: Some(Cow::from("An unknown error occured")),
params: std::collections::HashMap::new(),
},
);
errors
}
+10 -12
View File
@@ -25,14 +25,14 @@ use plume_models::{
users::*,
Error, PlumeRocket,
};
use routes::{errors::ErrorPage, Page, RemoteForm, RespondOrRedirect};
use routes::{errors::ErrorPage, Page, RemoteForm};
use template_utils::{IntoContext, Ructe};
#[get("/me")]
pub fn me(user: Option<User>) -> RespondOrRedirect {
pub fn me(user: Option<User>) -> Result<Redirect, Flash<Redirect>> {
match user {
Some(user) => Redirect::to(uri!(details: name = user.username)).into(),
None => utils::requires_login("", uri!(me)).into(),
Some(user) => Ok(Redirect::to(uri!(details: name = user.username))),
None => Err(utils::requires_login("", uri!(me))),
}
}
@@ -190,7 +190,7 @@ pub fn follow_not_connected(
name: String,
remote_form: Option<LenientForm<RemoteForm>>,
i18n: I18n,
) -> Result<RespondOrRedirect, ErrorPage> {
) -> Result<Result<Flash<Ructe>, Redirect>, ErrorPage> {
let target = User::find_by_fqn(&rockets, &name)?;
if let Some(remote_form) = remote_form {
if let Some(uri) = User::fetch_remote_interact_uri(&remote_form)
@@ -207,7 +207,7 @@ pub fn follow_not_connected(
.ok()
})
{
Ok(Redirect::to(uri).into())
Ok(Err(Redirect::to(uri)))
} else {
let mut err = ValidationErrors::default();
err.add("remote",
@@ -217,7 +217,7 @@ pub fn follow_not_connected(
params: HashMap::new(),
},
);
Ok(Flash::new(
Ok(Ok(Flash::new(
render!(users::follow_remote(
&rockets.to_context(),
target,
@@ -228,11 +228,10 @@ pub fn follow_not_connected(
)),
"callback",
uri!(follow: name = name).to_string(),
)
.into())
)))
}
} else {
Ok(Flash::new(
Ok(Ok(Flash::new(
render!(users::follow_remote(
&rockets.to_context(),
target,
@@ -244,8 +243,7 @@ pub fn follow_not_connected(
)),
"callback",
uri!(follow: name = name).to_string(),
)
.into())
)))
}
}
+5 -4
View File
@@ -64,7 +64,7 @@ main header.article {
}
main .article-info {
margin: 0 auto 3em;
margin: 0 auto 2em;
font-size: 0.95em;
font-weight: 400;
@@ -76,7 +76,7 @@ main .article-info {
// The article itself
main article {
max-width: $article-width;
margin: 2.5em auto;
margin: 1.5em auto;
font-family: $lora;
font-size: 1.2em;
line-height: 1.7;
@@ -87,7 +87,7 @@ main article {
img {
display: block;
margin: 3em auto;
margin: 2em auto;
max-width: 100%;
}
@@ -118,7 +118,7 @@ main .article-meta {
}
> .banner {
margin: 3em 0;
margin: 2em 0;
& > * {
margin: $margin;
}
@@ -370,6 +370,7 @@ main .article-meta {
background: transparent;
align-items: center;
justify-content: space-between;
resize: auto;
button {
flex: 0 0 10em;
font-size: 1.25em;
+3 -3
View File
@@ -24,13 +24,13 @@ input, textarea, select {
}
}
form input[type="submit"] {
margin: 2em auto;
margin: 1.5em auto;
-webkit-appearance: none;
}
textarea {
resize: vertical;
overflow-y: scroll;
overflow-y: scroll;
font-family: $lora;
font-size: 1.1em;
line-height: 1.5;
@@ -128,7 +128,7 @@ form.new-post {
textarea {
min-height: 20em;
overflow-y: scroll;
resize: none;
resize: vertical;
-webkit-appearance: textarea;
}
}
+5 -4
View File
@@ -44,6 +44,7 @@ html {
display: none;
appearance: none;
text-align: center;
overflow-y: scroll;
z-index: 20;
}
}
@@ -108,13 +109,13 @@ html {
main .article-meta {
> *, .comments {
margin: 0 5%;
margin: 0 3%;
}
> p {
margin: 2em 5%;
margin: 1em 3%;
font-size: 0.9em;
}
.comments > * { margin: auto 5%; }
.comments > * { margin: auto 3%; }
.comments .comment { padding: 2em 0px; }
}
main .article-info, main article, main h1.article, main h2.article {
@@ -127,7 +128,7 @@ html {
}
.tabs {
margin: auto 0px 2em;
margin: auto 0px 1em;
}
.stats { flex-direction: column; }
@@ -1,9 +0,0 @@
@use template_utils::*;
@use templates::base;
@(ctx: BaseContext)
@:base(ctx, i18n!(ctx.1, "Password reset"), {}, {}, {
<h1>@i18n!(ctx.1, "This token has expired")</h1>
<p>@i18n!(ctx.1, "Please start the process again by clicking") <a href="/password-reset">@i18n!(ctx.1, "here")</a>.</p>
})