Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3cb6f86ea2 | |||
| e0f91f7481 |
@@ -63,7 +63,7 @@ commands:
|
|||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
steps:
|
steps:
|
||||||
- run: cargo clippy <<^parameters.no_feature>>--no-default-features --features="${FEATURES}"<</parameters.no_feature>> --release -p <<parameters.package>> -- -D warnings -A clippy::needless_borrow
|
- run: cargo clippy <<^parameters.no_feature>>--no-default-features --features="${FEATURES}"<</parameters.no_feature>> --release -p <<parameters.package>> -- -D warnings
|
||||||
|
|
||||||
run_with_coverage:
|
run_with_coverage:
|
||||||
description: run command with environment for coverage
|
description: run command with environment for coverage
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ RUN apt update &&\
|
|||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
#install and configure rust
|
#install and configure rust
|
||||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2021-11-27 -y &&\
|
RUN curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2021-01-15 -y &&\
|
||||||
rustup component add rustfmt clippy &&\
|
rustup component add rustfmt clippy &&\
|
||||||
rustup component add rust-std --target wasm32-unknown-unknown
|
rustup component add rust-std --target wasm32-unknown-unknown
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ DATABASE_URL=postgres://plume:plume@localhost/plume
|
|||||||
BASE_URL=plu.me
|
BASE_URL=plu.me
|
||||||
|
|
||||||
# Log level for each crate
|
# Log level for each crate
|
||||||
RUST_LOG=info
|
RUST_LOG=warn,html5ever=warn,hyper=warn,tantivy=warn
|
||||||
|
|
||||||
# The secret key for private cookies and CSRF protection
|
# The secret key for private cookies and CSRF protection
|
||||||
# You can generate one with `openssl rand -base64 32`
|
# You can generate one with `openssl rand -base64 32`
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
name: cd
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'main'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
docker:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
-
|
|
||||||
name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
-
|
|
||||||
name: Build and push
|
|
||||||
id: docker_build
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
push: true
|
|
||||||
tags: plumeorg/plume:latest
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
name: cd
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- '*.*.*'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
docker:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
-
|
|
||||||
name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v3
|
|
||||||
with:
|
|
||||||
images: plumeorg/plume
|
|
||||||
-
|
|
||||||
name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
-
|
|
||||||
name: Build and push
|
|
||||||
id: docker_build
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
+2
-34
@@ -4,26 +4,6 @@
|
|||||||
|
|
||||||
## [Unreleased] - ReleaseDate
|
## [Unreleased] - ReleaseDate
|
||||||
|
|
||||||
## [[0.7.1]] - 2022-01-12
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Introduce environment variable `MAIL_PORT` (#980)
|
|
||||||
- Introduce email sign-up feature (#636, #1002)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Some styling improvements (#976, #977, #978)
|
|
||||||
- Respond with error status code when error (#1002)
|
|
||||||
|
|
||||||
### Fiexed
|
|
||||||
|
|
||||||
- Fix comment link (#974)
|
|
||||||
- Fix a bug that prevents posting articles (#975)
|
|
||||||
- Fix a bug that notification page doesn't show (#981)
|
|
||||||
|
|
||||||
## [[0.7.0]] - 2022-01-02
|
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Allow `dir` attributes for LtoR text in RtoL document (#860)
|
- Allow `dir` attributes for LtoR text in RtoL document (#860)
|
||||||
@@ -31,18 +11,14 @@
|
|||||||
- Proxy support (#829)
|
- Proxy support (#829)
|
||||||
- Riker a actor system library (#870)
|
- Riker a actor system library (#870)
|
||||||
- (request-target) and Host header in HTTP Signature (#872)
|
- (request-target) and Host header in HTTP Signature (#872)
|
||||||
- Default log levels for RUST_LOG (#885, #886, #919)
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Upgrade some dependent crates (#858)
|
- Upgrade some dependent crates (#858)
|
||||||
- Use tracing crate (#868)
|
- Use tracing crate (#868)
|
||||||
- Update Rust version to nightly-2021-11-27 (#961)
|
- Update Rust version to nightly-2021-01-15 (#878)
|
||||||
- Upgrade Tantivy to 0.13.3 and lindera-tantivy to 0.7.1 (#878)
|
- Upgrade Tantivy to 0.13.3 and lindera-tantivy to 0.7.1 (#878)
|
||||||
- Run searcher on actor system (#870)
|
- Run searcher on actor system (#870)
|
||||||
- Extract a function to calculate posts' ap_url and share it with some places (#918)
|
|
||||||
- Use article title as its slug instead of capitalizing and inserting hyphens (#920)
|
|
||||||
- Sign GET requests to other instances (#957)
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
@@ -50,12 +26,6 @@
|
|||||||
- Menu animation not opening on iOS (#876, #897)
|
- Menu animation not opening on iOS (#876, #897)
|
||||||
- Make actors subscribe to channel once (#913)
|
- Make actors subscribe to channel once (#913)
|
||||||
- Upsert posts and media instead of trying to insert and fail (#912)
|
- Upsert posts and media instead of trying to insert and fail (#912)
|
||||||
- Update post's ActivityPub id when published by update (#915)
|
|
||||||
- Calculate media URI properly even when MEDIA_UPLOAD_DIRECTORY configured (#916)
|
|
||||||
- Prevent duplicated posts in 'all' timeline (#917)
|
|
||||||
- Draw side line for blockquote on start (#933)
|
|
||||||
- Fix URIs of posts on Mastodon (#947)
|
|
||||||
- Place edit link proper position (#956, #963, #964)
|
|
||||||
|
|
||||||
## [[0.6.0]] - 2020-12-29
|
## [[0.6.0]] - 2020-12-29
|
||||||
|
|
||||||
@@ -236,9 +206,7 @@
|
|||||||
- Ability to create multiple blogs
|
- Ability to create multiple blogs
|
||||||
|
|
||||||
<!-- next-url -->
|
<!-- next-url -->
|
||||||
[Unreleased]: https://github.com/Plume-org/Plume/compare/0.7.1...HEAD
|
[Unreleased]: https://github.com/Plume-org/Plume/compare/0.6.0...HEAD
|
||||||
[[0.7.1]]: https://github.com/Plume-org/Plume/compare/0.7.0...0.7.1
|
|
||||||
[[0.7.0]]: https://github.com/Plume-org/Plume/compare/0.6.0...0.7.0
|
|
||||||
[[0.6.0]]: https://github.com/Plume-org/Plume/compare/0.5.0...0.6.0
|
[[0.6.0]]: https://github.com/Plume-org/Plume/compare/0.5.0...0.6.0
|
||||||
[0.5.0]: https://github.com/Plume-org/Plume/compare/0.4.0-alpha-4...0.5.0
|
[0.5.0]: https://github.com/Plume-org/Plume/compare/0.4.0-alpha-4...0.5.0
|
||||||
[0.4.0]: https://github.com/Plume-org/Plume/compare/0.3.0-alpha-2...0.4.0-alpha-4
|
[0.4.0]: https://github.com/Plume-org/Plume/compare/0.3.0-alpha-2...0.4.0-alpha-4
|
||||||
|
|||||||
Generated
+1004
-1232
File diff suppressed because it is too large
Load Diff
+13
-8
@@ -1,32 +1,37 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["Plume contributors"]
|
authors = ["Plume contributors"]
|
||||||
name = "plume"
|
name = "plume"
|
||||||
version = "0.7.1"
|
version = "0.6.1-dev"
|
||||||
repository = "https://github.com/Plume-org/Plume"
|
repository = "https://github.com/Plume-org/Plume"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
activitypub = "0.1.3"
|
activitypub = "0.1.3"
|
||||||
|
askama_escape = "0.1"
|
||||||
atom_syndication = "0.6"
|
atom_syndication = "0.6"
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
gettext = { git = "https://github.com/Plume-org/gettext/", rev = "294c54d74c699fbc66502b480a37cc66c1daa7f3" }
|
gettext = { git = "https://github.com/Plume-org/gettext/", rev = "294c54d74c699fbc66502b480a37cc66c1daa7f3" }
|
||||||
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" }
|
||||||
guid-create = "0.2"
|
guid-create = "0.1"
|
||||||
|
heck = "0.3.0"
|
||||||
|
lettre = "0.9.2"
|
||||||
lettre_email = "0.9.2"
|
lettre_email = "0.9.2"
|
||||||
num_cpus = "1.10"
|
num_cpus = "1.10"
|
||||||
rocket = "0.4.6"
|
rocket = "0.4.6"
|
||||||
rocket_contrib = { version = "0.4.5", features = ["json"] }
|
rocket_contrib = { version = "0.4.5", features = ["json"] }
|
||||||
rocket_i18n = { git = "https://github.com/Plume-org/rocket_i18n", rev = "e922afa7c366038b3433278c03b1456b346074f2" }
|
rocket_i18n = { git = "https://github.com/Plume-org/rocket_i18n", rev = "e922afa7c366038b3433278c03b1456b346074f2" }
|
||||||
|
rpassword = "4.0"
|
||||||
scheduled-thread-pool = "0.2.2"
|
scheduled-thread-pool = "0.2.2"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0.70"
|
serde_json = "1.0"
|
||||||
shrinkwraprs = "0.3.0"
|
shrinkwraprs = "0.2.1"
|
||||||
validator = { version = "0.14", features = ["derive"] }
|
validator = "0.8"
|
||||||
|
validator_derive = "0.8"
|
||||||
webfinger = "0.4.1"
|
webfinger = "0.4.1"
|
||||||
tracing = "0.1.22"
|
tracing = "0.1.22"
|
||||||
tracing-subscriber = "0.3.5"
|
tracing-subscriber = "0.2.15"
|
||||||
riker = "0.4.2"
|
riker = "0.4.2"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
@@ -48,7 +53,7 @@ version = "1.4.5"
|
|||||||
[dependencies.multipart]
|
[dependencies.multipart]
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["server"]
|
features = ["server"]
|
||||||
version = "0.18"
|
version = "0.16"
|
||||||
|
|
||||||
[dependencies.plume-api]
|
[dependencies.plume-api]
|
||||||
path = "plume-api"
|
path = "plume-api"
|
||||||
@@ -65,7 +70,7 @@ rev = "29910f2829e7e590a540da3804336577b48c7b31"
|
|||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
ructe = "0.13.0"
|
ructe = "0.13.0"
|
||||||
rsass = "0.23"
|
rsass = "0.9"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["postgres"]
|
default = ["postgres"]
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ WORKDIR /app
|
|||||||
COPY --from=builder /app /app
|
COPY --from=builder /app /app
|
||||||
COPY --from=builder /usr/local/cargo/bin/plm /bin/
|
COPY --from=builder /usr/local/cargo/bin/plm /bin/
|
||||||
COPY --from=builder /usr/local/cargo/bin/plume /bin/
|
COPY --from=builder /usr/local/cargo/bin/plume /bin/
|
||||||
|
COPY --from=builder /usr/local/cargo/bin/remove-dup-images /bin/
|
||||||
|
|
||||||
CMD ["plume"]
|
CMD ["plume"]
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ A lot of features are still missing, but what is already here should be quite st
|
|||||||
- **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.
|
- **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
|
- **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.
|
rules and thematics, but they can all communicate with each other.
|
||||||
- **Collaborative writing**: invite other people to your blogs, and write articles together. (Not implemented yet, but will be in 1.0)
|
- **Collaborative writing**: invite other people to your blogs, and write articles together.
|
||||||
|
|
||||||
## Get involved
|
## Get involved
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ main article {
|
|||||||
}
|
}
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
border-inline-start: 5px solid $gray;
|
border-left: 5px solid $gray;
|
||||||
margin: 1em auto;
|
margin: 1em auto;
|
||||||
padding: 0em 2em;
|
padding: 0em 2em;
|
||||||
}
|
}
|
||||||
@@ -516,11 +516,4 @@ input:checked ~ .cw-container > .cw-text {
|
|||||||
main .article-meta > *, main .article-meta .comments, main .article-meta > .banner > * {
|
main .article-meta > *, main .article-meta .comments, main .article-meta > .banner > * {
|
||||||
margin: 0 5%;
|
margin: 0 5%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom-bar {
|
|
||||||
align-items: center;
|
|
||||||
& > div:nth-child(2) {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,23 +219,15 @@ p.error {
|
|||||||
margin: 20px;
|
margin: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cover-link {
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cover {
|
.cover {
|
||||||
min-height: 10em;
|
min-height: 10em;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
&:hover {
|
||||||
display: flex;
|
opacity: 0.9;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
@@ -244,14 +236,9 @@ p.error {
|
|||||||
font-family: $playfair;
|
font-family: $playfair;
|
||||||
font-size: 1.75em;
|
font-size: 1.75em;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
line-height: 1.10;
|
line-height: 1.75;
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
a {
|
a {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding-block-start: 0.5em;
|
|
||||||
transition: color 0.1s ease-in;
|
transition: color 0.1s ease-in;
|
||||||
color: $text-color;
|
color: $text-color;
|
||||||
|
|
||||||
@@ -260,8 +247,7 @@ p.error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.controls {
|
.controls {
|
||||||
flex-shrink: 0;
|
float: right;
|
||||||
text-align: end;
|
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
@@ -275,7 +261,7 @@ p.error {
|
|||||||
font-family: $lora;
|
font-family: $lora;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
line-height: 1.25;
|
line-height: 1.25;
|
||||||
text-align: initial;
|
text-align: left;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -479,10 +465,9 @@ figure {
|
|||||||
|
|
||||||
/// Avatars
|
/// Avatars
|
||||||
.avatar {
|
.avatar {
|
||||||
background-position: center !important;
|
background-position: center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
&.small {
|
&.small {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
@@ -576,6 +561,14 @@ figure {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bottom-bar {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
& > div {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
main .article-meta .comments .comment {
|
main .article-meta .comments .comment {
|
||||||
header {
|
header {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ fn main() {
|
|||||||
.expect("compile templates");
|
.expect("compile templates");
|
||||||
|
|
||||||
compile_themes().expect("Theme compilation error");
|
compile_themes().expect("Theme compilation error");
|
||||||
recursive_copy(&Path::new("assets").join("icons"), Path::new("static"))
|
recursive_copy(&Path::new("assets").join("icons"), &Path::new("static"))
|
||||||
.expect("Couldn't copy icons");
|
.expect("Couldn't copy icons");
|
||||||
recursive_copy(&Path::new("assets").join("images"), Path::new("static"))
|
recursive_copy(&Path::new("assets").join("images"), &Path::new("static"))
|
||||||
.expect("Couldn't copy images");
|
.expect("Couldn't copy images");
|
||||||
create_dir_all(&Path::new("static").join("media")).expect("Couldn't init media directory");
|
create_dir_all(&Path::new("static").join("media")).expect("Couldn't init media directory");
|
||||||
|
|
||||||
@@ -97,12 +97,12 @@ fn compile_theme(path: &Path, out_dir: &Path) -> std::io::Result<()> {
|
|||||||
.components()
|
.components()
|
||||||
.skip_while(|c| *c != Component::Normal(OsStr::new("themes")))
|
.skip_while(|c| *c != Component::Normal(OsStr::new("themes")))
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.map(|c| {
|
.filter_map(|c| {
|
||||||
c.as_os_str()
|
c.as_os_str()
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.split_once('.')
|
.splitn(2, '.')
|
||||||
.map_or(c.as_os_str().to_str().unwrap_or_default(), |x| x.0)
|
.next()
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("-");
|
.join("-");
|
||||||
@@ -120,14 +120,8 @@ fn compile_theme(path: &Path, out_dir: &Path) -> std::io::Result<()> {
|
|||||||
// compile the .scss/.sass file
|
// compile the .scss/.sass file
|
||||||
let mut out = File::create(out.join("theme.css"))?;
|
let mut out = File::create(out.join("theme.css"))?;
|
||||||
out.write_all(
|
out.write_all(
|
||||||
&rsass::compile_scss_path(
|
&rsass::compile_scss_file(path, rsass::OutputStyle::Compressed)
|
||||||
path,
|
.expect("SCSS compilation error"),
|
||||||
rsass::output::Format {
|
|
||||||
style: rsass::output::Style::Compressed,
|
|
||||||
..rsass::output::Format::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.expect("SCSS compilation error"),
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
DROP TABLE email_signups;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
CREATE TABLE email_signups (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
email VARCHAR NOT NULL,
|
|
||||||
token VARCHAR NOT NULL,
|
|
||||||
expiration_date TIMESTAMP NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX email_signups_token ON email_signups (token);
|
|
||||||
CREATE UNIQUE INDEX email_signups_token_requests_email ON email_signups (email);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
DROP TABLE email_signups;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
CREATE TABLE email_signups (
|
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
email VARCHAR NOT NULL,
|
|
||||||
token VARCHAR NOT NULL,
|
|
||||||
expiration_date TIMESTAMP NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX email_signups_token ON email_signups (token);
|
|
||||||
CREATE UNIQUE INDEX email_signups_token_requests_email ON email_signups (email);
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "plume-api"
|
name = "plume-api"
|
||||||
version = "0.7.1"
|
version = "0.6.1-dev"
|
||||||
authors = ["Plume contributors"]
|
authors = ["Plume contributors"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "plume-cli"
|
name = "plume-cli"
|
||||||
version = "0.7.1"
|
version = "0.6.1-dev"
|
||||||
authors = ["Plume contributors"]
|
authors = ["Plume contributors"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@@ -8,9 +8,13 @@ edition = "2018"
|
|||||||
name = "plm"
|
name = "plm"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "remove-dup-images"
|
||||||
|
path = "src/remove-dup-images.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
dotenv = "0.15"
|
dotenv = "0.14"
|
||||||
rpassword = "5.0.0"
|
rpassword = "5.0.0"
|
||||||
|
|
||||||
[dependencies.diesel]
|
[dependencies.diesel]
|
||||||
|
|||||||
@@ -68,6 +68,4 @@ fn new<'a>(args: &ArgMatches<'a>, conn: &Connection) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.expect("Couldn't save instance");
|
.expect("Couldn't save instance");
|
||||||
Instance::cache_local(conn);
|
|
||||||
Instance::create_local_instance_user(conn).expect("Couldn't save local instance user");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ fn main() {
|
|||||||
e => e.map(|_| ()).unwrap(),
|
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(Instance::cache_local);
|
let _ = conn.as_ref().map(|conn| Instance::cache_local(conn));
|
||||||
|
|
||||||
match matches.subcommand() {
|
match matches.subcommand() {
|
||||||
("instance", Some(args)) => {
|
("instance", Some(args)) => {
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
use diesel::{
|
||||||
|
BoolExpressionMethods, Connection, ExpressionMethods, JoinOnDsl, NullableExpressionMethods,
|
||||||
|
QueryDsl, RunQueryDsl,
|
||||||
|
};
|
||||||
|
use plume_models::{
|
||||||
|
blogs::Blog, instance::Instance, medias::Media, posts::Post, Connection as Conn, CONFIG,
|
||||||
|
};
|
||||||
|
use std::collections::hash_map::{DefaultHasher, HashMap};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::hash::Hasher;
|
||||||
|
use std::io::{BufReader, Read};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
match dotenv::dotenv() {
|
||||||
|
Ok(path) => eprintln!("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()).expect("extablish connection");
|
||||||
|
Instance::cache_local(&conn);
|
||||||
|
let covers = get_remote_post_covers(&conn);
|
||||||
|
let remote_media_hashes = calculate_remote_media_hashes(covers);
|
||||||
|
eprintln!("remote medias: {:?}", remote_media_hashes);
|
||||||
|
let orphan_medias = get_orphan_medias(&conn);
|
||||||
|
eprintln!("{:?} orphan media(s)", orphan_medias.len());
|
||||||
|
for media in orphan_medias {
|
||||||
|
match calculate_file_hash(&Path::new(&media.file_path)) {
|
||||||
|
Some(hash) => {
|
||||||
|
match remote_media_hashes.get(&hash) {
|
||||||
|
Some(file_path) => {
|
||||||
|
eprintln!(
|
||||||
|
"File already referred. Removes only medias record. {}",
|
||||||
|
&file_path
|
||||||
|
);
|
||||||
|
// Remove medias record
|
||||||
|
diesel::delete(&media)
|
||||||
|
.execute(&conn)
|
||||||
|
.expect("Delete medias record");
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
eprintln!("Removes {}", &media.file_path);
|
||||||
|
// Remove file and medias record
|
||||||
|
media.delete(&conn).expect("Delete media record and file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
eprintln!(
|
||||||
|
"File doesn't exist. Removes medias record. medias.id: {}, path: {}",
|
||||||
|
&media.id, &media.file_path
|
||||||
|
);
|
||||||
|
diesel::delete(&media)
|
||||||
|
.execute(&conn)
|
||||||
|
.expect("Delete medias record");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_remote_post_covers(conn: &Conn) -> Vec<Media> {
|
||||||
|
use plume_models::schema::blogs;
|
||||||
|
use plume_models::schema::posts;
|
||||||
|
|
||||||
|
let remote_instances = Instance::get_remotes(&conn).expect("get remote instances");
|
||||||
|
let remote_instance_ids = remote_instances.iter().map(|instance| instance.id);
|
||||||
|
let remote_blogs = blogs::table
|
||||||
|
.filter(blogs::instance_id.eq_any(remote_instance_ids))
|
||||||
|
.load::<Blog>(conn)
|
||||||
|
.expect("remote blogs");
|
||||||
|
let remote_blog_ids = remote_blogs.iter().map(|blog| blog.id);
|
||||||
|
let remote_posts = posts::table
|
||||||
|
.filter(posts::blog_id.eq_any(remote_blog_ids))
|
||||||
|
.load::<Post>(conn)
|
||||||
|
.expect("remote posts");
|
||||||
|
remote_posts
|
||||||
|
.iter()
|
||||||
|
.filter_map(|post| post.cover_id)
|
||||||
|
.map(|cover_id| Media::get(conn, cover_id).expect("Media"))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_remote_media_hashes(medias: Vec<Media>) -> HashMap<u64, String> {
|
||||||
|
let mut media_hashes = HashMap::new();
|
||||||
|
for media in medias.iter() {
|
||||||
|
if let Some(hash) = calculate_file_hash(Path::new(&media.file_path)) {
|
||||||
|
let _ = media_hashes.insert(hash, media.file_path.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
media_hashes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_file_hash(path: &Path) -> Option<u64> {
|
||||||
|
if !path.exists() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let file = File::open(path).expect("open file");
|
||||||
|
let mut reader = BufReader::new(file);
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
let mut buffer = [0; 2048];
|
||||||
|
|
||||||
|
while let Ok(n) = reader.read(&mut buffer) {
|
||||||
|
hasher.write(&buffer);
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(hasher.finish())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_orphan_medias(conn: &Conn) -> Vec<Media> {
|
||||||
|
use plume_models::schema::{self, medias};
|
||||||
|
use plume_models::schema::{blogs::dsl::blogs, posts::dsl::posts, users::dsl::users};
|
||||||
|
let query = medias::table
|
||||||
|
.select((
|
||||||
|
medias::id,
|
||||||
|
medias::file_path,
|
||||||
|
medias::alt_text,
|
||||||
|
medias::is_remote,
|
||||||
|
medias::remote_url,
|
||||||
|
medias::sensitive,
|
||||||
|
medias::content_warning,
|
||||||
|
medias::owner_id,
|
||||||
|
))
|
||||||
|
.left_outer_join(users.on(schema::users::avatar_id.eq(medias::id.nullable())))
|
||||||
|
.left_outer_join(
|
||||||
|
blogs.on(schema::blogs::icon_id
|
||||||
|
.eq(medias::id.nullable())
|
||||||
|
.or(schema::blogs::banner_id.eq(medias::id.nullable()))),
|
||||||
|
)
|
||||||
|
.left_outer_join(posts.on(schema::posts::cover_id.eq(medias::id.nullable())))
|
||||||
|
.filter(
|
||||||
|
schema::users::avatar_id.is_null().and(
|
||||||
|
schema::blogs::icon_id.is_null().and(
|
||||||
|
schema::blogs::banner_id.is_null().and(
|
||||||
|
schema::posts::cover_id
|
||||||
|
.is_null()
|
||||||
|
.and(medias::is_remote.eq(false)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
eprintln!(
|
||||||
|
"query for orphan medias: {}",
|
||||||
|
diesel::debug_query::<_, _>(&query)
|
||||||
|
);
|
||||||
|
query.load::<Media>(conn).expect("Load orphan medias")
|
||||||
|
}
|
||||||
+6
-11
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "plume-common"
|
name = "plume-common"
|
||||||
version = "0.7.1"
|
version = "0.6.1-dev"
|
||||||
authors = ["Plume contributors"]
|
authors = ["Plume contributors"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
@@ -9,21 +9,21 @@ activitypub = "0.1.1"
|
|||||||
activitystreams-derive = "0.1.1"
|
activitystreams-derive = "0.1.1"
|
||||||
activitystreams-traits = "0.1.0"
|
activitystreams-traits = "0.1.0"
|
||||||
array_tool = "1.0"
|
array_tool = "1.0"
|
||||||
base64 = "0.13"
|
base64 = "0.10"
|
||||||
heck = "0.4.0"
|
heck = "0.3.0"
|
||||||
hex = "0.4"
|
hex = "0.3"
|
||||||
|
hyper = "0.12.33"
|
||||||
openssl = "0.10.22"
|
openssl = "0.10.22"
|
||||||
rocket = "0.4.6"
|
rocket = "0.4.6"
|
||||||
reqwest = { version = "0.9", features = ["socks"] }
|
reqwest = { version = "0.9", features = ["socks"] }
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
serde_json = "1.0.70"
|
serde_json = "1.0"
|
||||||
shrinkwraprs = "0.3.0"
|
shrinkwraprs = "0.3.0"
|
||||||
syntect = "4.5.0"
|
syntect = "4.5.0"
|
||||||
tokio = "0.1.22"
|
tokio = "0.1.22"
|
||||||
regex-syntax = { version = "0.6.17", default-features = false, features = ["unicode-perl"] }
|
regex-syntax = { version = "0.6.17", default-features = false, features = ["unicode-perl"] }
|
||||||
tracing = "0.1.22"
|
tracing = "0.1.22"
|
||||||
askama_escape = "0.10.2"
|
|
||||||
|
|
||||||
[dependencies.chrono]
|
[dependencies.chrono]
|
||||||
features = ["serde"]
|
features = ["serde"]
|
||||||
@@ -33,8 +33,3 @@ version = "0.4"
|
|||||||
default-features = false
|
default-features = false
|
||||||
git = "https://git.joinplu.me/Plume/pulldown-cmark"
|
git = "https://git.joinplu.me/Plume/pulldown-cmark"
|
||||||
branch = "bidi-plume"
|
branch = "bidi-plume"
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
once_cell = "1.5.2"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
use reqwest;
|
use reqwest::header::{HeaderValue, ACCEPT};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use super::{request, sign::Signer};
|
|
||||||
|
|
||||||
/// Represents an ActivityPub inbox.
|
/// Represents an ActivityPub inbox.
|
||||||
///
|
///
|
||||||
/// It routes an incoming Activity through the registered handlers.
|
/// It routes an incoming Activity through the registered handlers.
|
||||||
@@ -12,50 +10,7 @@ use super::{request, sign::Signer};
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// # extern crate activitypub;
|
/// # extern crate activitypub;
|
||||||
/// # use activitypub::{actor::Person, activity::{Announce, Create}, object::Note};
|
/// # use activitypub::{actor::Person, activity::{Announce, Create}, object::Note};
|
||||||
/// # use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa};
|
|
||||||
/// # use once_cell::sync::Lazy;
|
|
||||||
/// # use plume_common::activity_pub::inbox::*;
|
/// # use plume_common::activity_pub::inbox::*;
|
||||||
/// # use plume_common::activity_pub::sign::{gen_keypair, Error as SignError, Result as SignResult, Signer};
|
|
||||||
/// #
|
|
||||||
/// # static MY_SIGNER: Lazy<MySigner> = Lazy::new(|| MySigner::new());
|
|
||||||
/// #
|
|
||||||
/// # struct MySigner {
|
|
||||||
/// # public_key: String,
|
|
||||||
/// # private_key: String,
|
|
||||||
/// # }
|
|
||||||
/// #
|
|
||||||
/// # impl MySigner {
|
|
||||||
/// # fn new() -> Self {
|
|
||||||
/// # let (pub_key, priv_key) = gen_keypair();
|
|
||||||
/// # Self {
|
|
||||||
/// # public_key: String::from_utf8(pub_key).unwrap(),
|
|
||||||
/// # private_key: String::from_utf8(priv_key).unwrap(),
|
|
||||||
/// # }
|
|
||||||
/// # }
|
|
||||||
/// # }
|
|
||||||
/// #
|
|
||||||
/// # impl Signer for MySigner {
|
|
||||||
/// # fn get_key_id(&self) -> String {
|
|
||||||
/// # "mysigner".into()
|
|
||||||
/// # }
|
|
||||||
/// #
|
|
||||||
/// # fn sign(&self, to_sign: &str) -> SignResult<Vec<u8>> {
|
|
||||||
/// # let key = PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.as_ref()).unwrap())
|
|
||||||
/// # .unwrap();
|
|
||||||
/// # let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &key).unwrap();
|
|
||||||
/// # signer.update(to_sign.as_bytes()).unwrap();
|
|
||||||
/// # signer.sign_to_vec().map_err(|_| SignError())
|
|
||||||
/// # }
|
|
||||||
/// #
|
|
||||||
/// # fn verify(&self, data: &str, signature: &[u8]) -> SignResult<bool> {
|
|
||||||
/// # let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref()).unwrap())
|
|
||||||
/// # .unwrap();
|
|
||||||
/// # let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &key).unwrap();
|
|
||||||
/// # verifier.update(data.as_bytes()).unwrap();
|
|
||||||
/// # verifier.verify(&signature).map_err(|_| SignError())
|
|
||||||
/// # }
|
|
||||||
/// # }
|
|
||||||
/// #
|
|
||||||
/// # struct User;
|
/// # struct User;
|
||||||
/// # impl FromId<()> for User {
|
/// # impl FromId<()> for User {
|
||||||
/// # type Error = ();
|
/// # type Error = ();
|
||||||
@@ -68,10 +23,6 @@ use super::{request, sign::Signer};
|
|||||||
/// # fn from_activity(_: &(), obj: Person) -> Result<Self, Self::Error> {
|
/// # fn from_activity(_: &(), obj: Person) -> Result<Self, Self::Error> {
|
||||||
/// # Ok(User)
|
/// # Ok(User)
|
||||||
/// # }
|
/// # }
|
||||||
/// #
|
|
||||||
/// # fn get_sender() -> &'static dyn Signer {
|
|
||||||
/// # &*MY_SIGNER
|
|
||||||
/// # }
|
|
||||||
/// # }
|
/// # }
|
||||||
/// # impl AsActor<&()> for User {
|
/// # impl AsActor<&()> for User {
|
||||||
/// # fn get_inbox_url(&self) -> String {
|
/// # fn get_inbox_url(&self) -> String {
|
||||||
@@ -91,10 +42,6 @@ use super::{request, sign::Signer};
|
|||||||
/// # fn from_activity(_: &(), obj: Note) -> Result<Self, Self::Error> {
|
/// # fn from_activity(_: &(), obj: Note) -> Result<Self, Self::Error> {
|
||||||
/// # Ok(Message)
|
/// # Ok(Message)
|
||||||
/// # }
|
/// # }
|
||||||
/// #
|
|
||||||
/// # fn get_sender() -> &'static dyn Signer {
|
|
||||||
/// # &*MY_SIGNER
|
|
||||||
/// # }
|
|
||||||
/// # }
|
/// # }
|
||||||
/// # impl AsObject<User, Create, &()> for Message {
|
/// # impl AsObject<User, Create, &()> for Message {
|
||||||
/// # type Error = ();
|
/// # type Error = ();
|
||||||
@@ -260,7 +207,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Handle the activity
|
// Handle the activity
|
||||||
match obj.activity(ctx, actor, act_id) {
|
match obj.activity(ctx, actor, &act_id) {
|
||||||
Ok(res) => Inbox::Handled(res.into()),
|
Ok(res) => Inbox::Handled(res.into()),
|
||||||
Err(e) => Inbox::Failed(e),
|
Err(e) => Inbox::Failed(e),
|
||||||
}
|
}
|
||||||
@@ -364,16 +311,35 @@ pub trait FromId<C>: Sized {
|
|||||||
id: &str,
|
id: &str,
|
||||||
proxy: Option<reqwest::Proxy>,
|
proxy: Option<reqwest::Proxy>,
|
||||||
) -> Result<Self::Object, (Option<serde_json::Value>, Self::Error)> {
|
) -> Result<Self::Object, (Option<serde_json::Value>, Self::Error)> {
|
||||||
request::get(id, Self::get_sender(), proxy)
|
if let Some(proxy) = proxy {
|
||||||
.map_err(|_| (None, InboxError::DerefError))
|
reqwest::ClientBuilder::new().proxy(proxy)
|
||||||
.and_then(|mut r| {
|
} else {
|
||||||
let json: serde_json::Value = r
|
reqwest::ClientBuilder::new()
|
||||||
.json()
|
}
|
||||||
.map_err(|_| (None, InboxError::InvalidObject(None)))?;
|
.connect_timeout(Some(std::time::Duration::from_secs(5)))
|
||||||
serde_json::from_value(json.clone())
|
.build()
|
||||||
.map_err(|_| (Some(json), InboxError::InvalidObject(None)))
|
.map_err(|_| (None, InboxError::DerefError.into()))?
|
||||||
})
|
.get(id)
|
||||||
.map_err(|(json, e)| (json, e.into()))
|
.header(
|
||||||
|
ACCEPT,
|
||||||
|
HeaderValue::from_str(
|
||||||
|
&super::ap_accept_header()
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", "),
|
||||||
|
)
|
||||||
|
.map_err(|_| (None, InboxError::DerefError.into()))?,
|
||||||
|
)
|
||||||
|
.send()
|
||||||
|
.map_err(|_| (None, InboxError::DerefError))
|
||||||
|
.and_then(|mut r| {
|
||||||
|
let json: serde_json::Value = r
|
||||||
|
.json()
|
||||||
|
.map_err(|_| (None, InboxError::InvalidObject(None)))?;
|
||||||
|
serde_json::from_value(json.clone())
|
||||||
|
.map_err(|_| (Some(json), InboxError::InvalidObject(None)))
|
||||||
|
})
|
||||||
|
.map_err(|(json, e)| (json, e.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a `Self` from its ActivityPub representation
|
/// Builds a `Self` from its ActivityPub representation
|
||||||
@@ -381,8 +347,6 @@ pub trait FromId<C>: Sized {
|
|||||||
|
|
||||||
/// Tries to find a `Self` with a given ID (`id`), using `ctx` (a database)
|
/// Tries to find a `Self` with a given ID (`id`), using `ctx` (a database)
|
||||||
fn from_db(ctx: &C, id: &str) -> Result<Self, Self::Error>;
|
fn from_db(ctx: &C, id: &str) -> Result<Self, Self::Error>;
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Should be implemented by anything representing an ActivityPub actor.
|
/// Should be implemented by anything representing an ActivityPub actor.
|
||||||
@@ -421,49 +385,6 @@ pub trait AsActor<C> {
|
|||||||
/// # extern crate activitypub;
|
/// # extern crate activitypub;
|
||||||
/// # use activitypub::{activity::Create, actor::Person, object::Note};
|
/// # use activitypub::{activity::Create, actor::Person, object::Note};
|
||||||
/// # use plume_common::activity_pub::inbox::{AsActor, AsObject, FromId};
|
/// # use plume_common::activity_pub::inbox::{AsActor, AsObject, FromId};
|
||||||
/// # use plume_common::activity_pub::sign::{gen_keypair, Error as SignError, Result as SignResult, Signer};
|
|
||||||
/// # use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa};
|
|
||||||
/// # use once_cell::sync::Lazy;
|
|
||||||
/// #
|
|
||||||
/// # static MY_SIGNER: Lazy<MySigner> = Lazy::new(|| MySigner::new());
|
|
||||||
/// #
|
|
||||||
/// # struct MySigner {
|
|
||||||
/// # public_key: String,
|
|
||||||
/// # private_key: String,
|
|
||||||
/// # }
|
|
||||||
/// #
|
|
||||||
/// # impl MySigner {
|
|
||||||
/// # fn new() -> Self {
|
|
||||||
/// # let (pub_key, priv_key) = gen_keypair();
|
|
||||||
/// # Self {
|
|
||||||
/// # public_key: String::from_utf8(pub_key).unwrap(),
|
|
||||||
/// # private_key: String::from_utf8(priv_key).unwrap(),
|
|
||||||
/// # }
|
|
||||||
/// # }
|
|
||||||
/// # }
|
|
||||||
/// #
|
|
||||||
/// # impl Signer for MySigner {
|
|
||||||
/// # fn get_key_id(&self) -> String {
|
|
||||||
/// # "mysigner".into()
|
|
||||||
/// # }
|
|
||||||
/// #
|
|
||||||
/// # fn sign(&self, to_sign: &str) -> SignResult<Vec<u8>> {
|
|
||||||
/// # let key = PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.as_ref()).unwrap())
|
|
||||||
/// # .unwrap();
|
|
||||||
/// # let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &key).unwrap();
|
|
||||||
/// # signer.update(to_sign.as_bytes()).unwrap();
|
|
||||||
/// # signer.sign_to_vec().map_err(|_| SignError())
|
|
||||||
/// # }
|
|
||||||
/// #
|
|
||||||
/// # fn verify(&self, data: &str, signature: &[u8]) -> SignResult<bool> {
|
|
||||||
/// # let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref()).unwrap())
|
|
||||||
/// # .unwrap();
|
|
||||||
/// # let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &key).unwrap();
|
|
||||||
/// # verifier.update(data.as_bytes()).unwrap();
|
|
||||||
/// # verifier.verify(&signature).map_err(|_| SignError())
|
|
||||||
/// # }
|
|
||||||
/// # }
|
|
||||||
/// #
|
|
||||||
/// # struct Account;
|
/// # struct Account;
|
||||||
/// # impl FromId<()> for Account {
|
/// # impl FromId<()> for Account {
|
||||||
/// # type Error = ();
|
/// # type Error = ();
|
||||||
@@ -476,10 +397,6 @@ pub trait AsActor<C> {
|
|||||||
/// # fn from_activity(_: &(), obj: Person) -> Result<Self, Self::Error> {
|
/// # fn from_activity(_: &(), obj: Person) -> Result<Self, Self::Error> {
|
||||||
/// # Ok(Account)
|
/// # Ok(Account)
|
||||||
/// # }
|
/// # }
|
||||||
/// #
|
|
||||||
/// # fn get_sender() -> &'static dyn Signer {
|
|
||||||
/// # &*MY_SIGNER
|
|
||||||
/// # }
|
|
||||||
/// # }
|
/// # }
|
||||||
/// # impl AsActor<()> for Account {
|
/// # impl AsActor<()> for Account {
|
||||||
/// # fn get_inbox_url(&self) -> String {
|
/// # fn get_inbox_url(&self) -> String {
|
||||||
@@ -503,10 +420,6 @@ pub trait AsActor<C> {
|
|||||||
/// fn from_activity(_: &(), obj: Note) -> Result<Self, Self::Error> {
|
/// fn from_activity(_: &(), obj: Note) -> Result<Self, Self::Error> {
|
||||||
/// Ok(Message { text: obj.object_props.content_string().map_err(|_| ())? })
|
/// Ok(Message { text: obj.object_props.content_string().map_err(|_| ())? })
|
||||||
/// }
|
/// }
|
||||||
///
|
|
||||||
/// fn get_sender() -> &'static dyn Signer {
|
|
||||||
/// &*MY_SIGNER
|
|
||||||
/// }
|
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl AsObject<Account, Create, ()> for Message {
|
/// impl AsObject<Account, Create, ()> for Message {
|
||||||
@@ -546,51 +459,7 @@ where
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::activity_pub::sign::{
|
|
||||||
gen_keypair, Error as SignError, Result as SignResult, Signer,
|
|
||||||
};
|
|
||||||
use activitypub::{activity::*, actor::Person, object::Note};
|
use activitypub::{activity::*, actor::Person, object::Note};
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa};
|
|
||||||
|
|
||||||
static MY_SIGNER: Lazy<MySigner> = Lazy::new(|| MySigner::new());
|
|
||||||
|
|
||||||
struct MySigner {
|
|
||||||
public_key: String,
|
|
||||||
private_key: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MySigner {
|
|
||||||
fn new() -> Self {
|
|
||||||
let (pub_key, priv_key) = gen_keypair();
|
|
||||||
Self {
|
|
||||||
public_key: String::from_utf8(pub_key).unwrap(),
|
|
||||||
private_key: String::from_utf8(priv_key).unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Signer for MySigner {
|
|
||||||
fn get_key_id(&self) -> String {
|
|
||||||
"mysigner".into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sign(&self, to_sign: &str) -> SignResult<Vec<u8>> {
|
|
||||||
let key = PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.as_ref()).unwrap())
|
|
||||||
.unwrap();
|
|
||||||
let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &key).unwrap();
|
|
||||||
signer.update(to_sign.as_bytes()).unwrap();
|
|
||||||
signer.sign_to_vec().map_err(|_| SignError())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify(&self, data: &str, signature: &[u8]) -> SignResult<bool> {
|
|
||||||
let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref()).unwrap())
|
|
||||||
.unwrap();
|
|
||||||
let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &key).unwrap();
|
|
||||||
verifier.update(data.as_bytes()).unwrap();
|
|
||||||
verifier.verify(&signature).map_err(|_| SignError())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MyActor;
|
struct MyActor;
|
||||||
impl FromId<()> for MyActor {
|
impl FromId<()> for MyActor {
|
||||||
@@ -604,10 +473,6 @@ mod tests {
|
|||||||
fn from_activity(_: &(), _obj: Person) -> Result<Self, Self::Error> {
|
fn from_activity(_: &(), _obj: Person) -> Result<Self, Self::Error> {
|
||||||
Ok(MyActor)
|
Ok(MyActor)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
&*MY_SIGNER
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsActor<&()> for MyActor {
|
impl AsActor<&()> for MyActor {
|
||||||
@@ -632,10 +497,6 @@ mod tests {
|
|||||||
fn from_activity(_: &(), _obj: Note) -> Result<Self, Self::Error> {
|
fn from_activity(_: &(), _obj: Note) -> Result<Self, Self::Error> {
|
||||||
Ok(MyObject)
|
Ok(MyObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
&*MY_SIGNER
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl AsObject<MyActor, Create, &()> for MyObject {
|
impl AsObject<MyActor, Create, &()> for MyObject {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
@@ -740,10 +601,6 @@ mod tests {
|
|||||||
fn from_activity(_: &(), _obj: Person) -> Result<Self, Self::Error> {
|
fn from_activity(_: &(), _obj: Person) -> Result<Self, Self::Error> {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
&*MY_SIGNER
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl AsActor<&()> for FailingActor {
|
impl AsActor<&()> for FailingActor {
|
||||||
fn get_inbox_url(&self) -> String {
|
fn get_inbox_url(&self) -> String {
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ where
|
|||||||
warn!("Inbox doesn't have host: {:?}", &inbox);
|
warn!("Inbox doesn't have host: {:?}", &inbox);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let host_header_value = HeaderValue::from_str(url.host_str().expect("Unreachable"));
|
let host_header_value = HeaderValue::from_str(&url.host_str().expect("Unreachable"));
|
||||||
if host_header_value.is_err() {
|
if host_header_value.is_err() {
|
||||||
warn!("Header value is invalid: {:?}", url.host_str());
|
warn!("Header value is invalid: {:?}", url.host_str());
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
use chrono::{offset::Utc, DateTime};
|
use chrono::{offset::Utc, DateTime};
|
||||||
use openssl::hash::{Hasher, MessageDigest};
|
use openssl::hash::{Hasher, MessageDigest};
|
||||||
use reqwest::{
|
use reqwest::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE, DATE, USER_AGENT};
|
||||||
header::{
|
|
||||||
HeaderMap, HeaderValue, InvalidHeaderValue, ACCEPT, CONTENT_TYPE, DATE, HOST, USER_AGENT,
|
|
||||||
},
|
|
||||||
ClientBuilder, Proxy, Response, Url, UrlError,
|
|
||||||
};
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
@@ -18,24 +13,6 @@ const PLUME_USER_AGENT: &str = concat!("Plume/", env!("CARGO_PKG_VERSION"));
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Error();
|
pub struct Error();
|
||||||
|
|
||||||
impl From<UrlError> for Error {
|
|
||||||
fn from(_err: UrlError) -> Self {
|
|
||||||
Error()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<InvalidHeaderValue> for Error {
|
|
||||||
fn from(_err: InvalidHeaderValue) -> Self {
|
|
||||||
Error()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<reqwest::Error> for Error {
|
|
||||||
fn from(_err: reqwest::Error) -> Self {
|
|
||||||
Error()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Digest(String);
|
pub struct Digest(String);
|
||||||
|
|
||||||
impl Digest {
|
impl Digest {
|
||||||
@@ -141,8 +118,8 @@ type Path<'a> = &'a str;
|
|||||||
type Query<'a> = &'a str;
|
type Query<'a> = &'a str;
|
||||||
type RequestTarget<'a> = (Method<'a>, Path<'a>, Option<Query<'a>>);
|
type RequestTarget<'a> = (Method<'a>, Path<'a>, Option<Query<'a>>);
|
||||||
|
|
||||||
pub fn signature(
|
pub fn signature<S: Signer>(
|
||||||
signer: &dyn Signer,
|
signer: &S,
|
||||||
headers: &HeaderMap,
|
headers: &HeaderMap,
|
||||||
request_target: RequestTarget,
|
request_target: RequestTarget,
|
||||||
) -> Result<HeaderValue, Error> {
|
) -> Result<HeaderValue, Error> {
|
||||||
@@ -187,35 +164,10 @@ pub fn signature(
|
|||||||
)).map_err(|_| Error())
|
)).map_err(|_| Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(url_str: &str, sender: &dyn Signer, proxy: Option<Proxy>) -> Result<Response, Error> {
|
|
||||||
let mut headers = headers();
|
|
||||||
let url = Url::parse(url_str)?;
|
|
||||||
if !url.has_host() {
|
|
||||||
return Err(Error());
|
|
||||||
}
|
|
||||||
let host_header_value = HeaderValue::from_str(url.host_str().expect("Unreachable"))?;
|
|
||||||
headers.insert(HOST, host_header_value);
|
|
||||||
if let Some(proxy) = proxy {
|
|
||||||
ClientBuilder::new().proxy(proxy)
|
|
||||||
} else {
|
|
||||||
ClientBuilder::new()
|
|
||||||
}
|
|
||||||
.connect_timeout(Some(std::time::Duration::from_secs(5)))
|
|
||||||
.build()?
|
|
||||||
.get(url_str)
|
|
||||||
.headers(headers.clone())
|
|
||||||
.header(
|
|
||||||
"Signature",
|
|
||||||
signature(sender, &headers, ("get", url.path(), url.query()))?,
|
|
||||||
)
|
|
||||||
.send()
|
|
||||||
.map_err(|_| Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::signature;
|
use super::{signature, Error};
|
||||||
use crate::activity_pub::sign::{gen_keypair, Error, Result, Signer};
|
use crate::activity_pub::sign::{gen_keypair, Signer};
|
||||||
use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa};
|
use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa};
|
||||||
use reqwest::header::HeaderMap;
|
use reqwest::header::HeaderMap;
|
||||||
|
|
||||||
@@ -235,11 +187,13 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Signer for MySigner {
|
impl Signer for MySigner {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
fn get_key_id(&self) -> String {
|
fn get_key_id(&self) -> String {
|
||||||
"mysigner".into()
|
"mysigner".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign(&self, to_sign: &str) -> Result<Vec<u8>> {
|
fn sign(&self, to_sign: &str) -> Result<Vec<u8>, Self::Error> {
|
||||||
let key = PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.as_ref()).unwrap())
|
let key = PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.as_ref()).unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &key).unwrap();
|
let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &key).unwrap();
|
||||||
@@ -247,7 +201,7 @@ mod tests {
|
|||||||
signer.sign_to_vec().map_err(|_| Error())
|
signer.sign_to_vec().map_err(|_| Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify(&self, data: &str, signature: &[u8]) -> Result<bool> {
|
fn verify(&self, data: &str, signature: &[u8]) -> Result<bool, Self::Error> {
|
||||||
let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref()).unwrap())
|
let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref()).unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &key).unwrap();
|
let mut verifier = openssl::sign::Verifier::new(MessageDigest::sha256(), &key).unwrap();
|
||||||
|
|||||||
@@ -19,25 +19,20 @@ pub fn gen_keypair() -> (Vec<u8>, Vec<u8>) {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Error();
|
pub struct Error();
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
impl From<openssl::error::ErrorStack> for Error {
|
|
||||||
fn from(_: openssl::error::ErrorStack) -> Self {
|
|
||||||
Self()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Signer {
|
pub trait Signer {
|
||||||
|
type Error;
|
||||||
|
|
||||||
fn get_key_id(&self) -> String;
|
fn get_key_id(&self) -> String;
|
||||||
|
|
||||||
/// Sign some data with the signer keypair
|
/// Sign some data with the signer keypair
|
||||||
fn sign(&self, to_sign: &str) -> Result<Vec<u8>>;
|
fn sign(&self, to_sign: &str) -> Result<Vec<u8>, Self::Error>;
|
||||||
/// Verify if the signature is valid
|
/// Verify if the signature is valid
|
||||||
fn verify(&self, data: &str, signature: &[u8]) -> Result<bool>;
|
fn verify(&self, data: &str, signature: &[u8]) -> Result<bool, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Signable {
|
pub trait Signable {
|
||||||
fn sign<T>(&mut self, creator: &T) -> Result<&mut Self>
|
fn sign<T>(&mut self, creator: &T) -> Result<&mut Self, Error>
|
||||||
where
|
where
|
||||||
T: Signer;
|
T: Signer;
|
||||||
fn verify<T>(self, creator: &T) -> bool
|
fn verify<T>(self, creator: &T) -> bool
|
||||||
@@ -51,7 +46,7 @@ pub trait Signable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Signable for serde_json::Value {
|
impl Signable for serde_json::Value {
|
||||||
fn sign<T: Signer>(&mut self, creator: &T) -> Result<&mut serde_json::Value> {
|
fn sign<T: Signer>(&mut self, creator: &T) -> Result<&mut serde_json::Value, Error> {
|
||||||
let creation_date = Utc::now().to_rfc3339();
|
let creation_date = Utc::now().to_rfc3339();
|
||||||
let mut options = json!({
|
let mut options = json!({
|
||||||
"type": "RsaSignature2017",
|
"type": "RsaSignature2017",
|
||||||
@@ -187,7 +182,7 @@ pub fn verify_http_headers<S: Signer + ::std::fmt::Debug>(
|
|||||||
}
|
}
|
||||||
let digest = all_headers.get_one("digest").unwrap_or("");
|
let digest = all_headers.get_one("digest").unwrap_or("");
|
||||||
let digest = request::Digest::from_header(digest);
|
let digest = request::Digest::from_header(digest);
|
||||||
if !digest.map(|d| d.verify_header(data)).unwrap_or(false) {
|
if !digest.map(|d| d.verify_header(&data)).unwrap_or(false) {
|
||||||
// signature was valid, but body content does not match its digest
|
// signature was valid, but body content does not match its digest
|
||||||
return SignatureValidity::Invalid;
|
return SignatureValidity::Invalid;
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-76
@@ -1,8 +1,11 @@
|
|||||||
use heck::ToUpperCamelCase;
|
use heck::CamelCase;
|
||||||
use openssl::rand::rand_bytes;
|
use openssl::rand::rand_bytes;
|
||||||
use pulldown_cmark::{html, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag};
|
use pulldown_cmark::{html, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag};
|
||||||
use regex_syntax::is_word_character;
|
use regex_syntax::is_word_character;
|
||||||
use rocket::http::uri::Uri;
|
use rocket::{
|
||||||
|
http::uri::Uri,
|
||||||
|
response::{Flash, Redirect},
|
||||||
|
};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use syntect::html::{ClassStyle, ClassedHTMLGenerator};
|
use syntect::html::{ClassStyle, ClassedHTMLGenerator};
|
||||||
use syntect::parsing::SyntaxSet;
|
use syntect::parsing::SyntaxSet;
|
||||||
@@ -18,63 +21,23 @@ pub fn random_hex() -> String {
|
|||||||
|
|
||||||
/// Remove non alphanumeric characters and CamelCase a string
|
/// Remove non alphanumeric characters and CamelCase a string
|
||||||
pub fn make_actor_id(name: &str) -> String {
|
pub fn make_actor_id(name: &str) -> String {
|
||||||
name.to_upper_camel_case()
|
name.to_camel_case()
|
||||||
.chars()
|
.chars()
|
||||||
.filter(|c| c.is_alphanumeric())
|
.filter(|c| c.is_alphanumeric())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Percent-encode characters which are not allowed in IRI path segments.
|
* Redirects to the login page with a given message.
|
||||||
*
|
*
|
||||||
* Intended to be used for generating Post ap_url.
|
* Note that the message should be translated before passed to this function.
|
||||||
*/
|
*/
|
||||||
pub fn iri_percent_encode_seg(segment: &str) -> String {
|
pub fn requires_login<T: Into<Uri<'static>>>(message: &str, url: T) -> Flash<Redirect> {
|
||||||
segment.chars().map(iri_percent_encode_seg_char).collect()
|
Flash::new(
|
||||||
}
|
Redirect::to(format!("/login?m={}", Uri::percent_encode(message))),
|
||||||
|
"callback",
|
||||||
pub fn iri_percent_encode_seg_char(c: char) -> String {
|
url.into().to_string(),
|
||||||
if c.is_alphanumeric() {
|
)
|
||||||
c.to_string()
|
|
||||||
} else {
|
|
||||||
match c {
|
|
||||||
'-'
|
|
||||||
| '.'
|
|
||||||
| '_'
|
|
||||||
| '~'
|
|
||||||
| '\u{A0}'..='\u{D7FF}'
|
|
||||||
| '\u{20000}'..='\u{2FFFD}'
|
|
||||||
| '\u{30000}'..='\u{3FFFD}'
|
|
||||||
| '\u{40000}'..='\u{4FFFD}'
|
|
||||||
| '\u{50000}'..='\u{5FFFD}'
|
|
||||||
| '\u{60000}'..='\u{6FFFD}'
|
|
||||||
| '\u{70000}'..='\u{7FFFD}'
|
|
||||||
| '\u{80000}'..='\u{8FFFD}'
|
|
||||||
| '\u{90000}'..='\u{9FFFD}'
|
|
||||||
| '\u{A0000}'..='\u{AFFFD}'
|
|
||||||
| '\u{B0000}'..='\u{BFFFD}'
|
|
||||||
| '\u{C0000}'..='\u{CFFFD}'
|
|
||||||
| '\u{D0000}'..='\u{DFFFD}'
|
|
||||||
| '\u{E0000}'..='\u{EFFFD}'
|
|
||||||
| '!'
|
|
||||||
| '$'
|
|
||||||
| '&'
|
|
||||||
| '\''
|
|
||||||
| '('
|
|
||||||
| ')'
|
|
||||||
| '*'
|
|
||||||
| '+'
|
|
||||||
| ','
|
|
||||||
| ';'
|
|
||||||
| '='
|
|
||||||
| ':'
|
|
||||||
| '@' => c.to_string(),
|
|
||||||
_ => {
|
|
||||||
let s = c.to_string();
|
|
||||||
Uri::percent_encode(&s).to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -125,13 +88,13 @@ fn highlight_code<'a>(
|
|||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
let syntax_set = SyntaxSet::load_defaults_newlines();
|
let syntax_set = SyntaxSet::load_defaults_newlines();
|
||||||
let syntax = syntax_set.find_syntax_by_token(lang).unwrap_or_else(|| {
|
let syntax = syntax_set.find_syntax_by_token(&lang).unwrap_or_else(|| {
|
||||||
syntax_set
|
syntax_set
|
||||||
.find_syntax_by_name(lang)
|
.find_syntax_by_name(&lang)
|
||||||
.unwrap_or_else(|| syntax_set.find_syntax_plain_text())
|
.unwrap_or_else(|| syntax_set.find_syntax_plain_text())
|
||||||
});
|
});
|
||||||
let mut html = ClassedHTMLGenerator::new_with_class_style(
|
let mut html = ClassedHTMLGenerator::new_with_class_style(
|
||||||
syntax,
|
&syntax,
|
||||||
&syntax_set,
|
&syntax_set,
|
||||||
ClassStyle::Spaced,
|
ClassStyle::Spaced,
|
||||||
);
|
);
|
||||||
@@ -318,15 +281,16 @@ pub fn md_to_html<'a>(
|
|||||||
text_acc.push(c)
|
text_acc.push(c)
|
||||||
}
|
}
|
||||||
let mention = text_acc;
|
let mention = text_acc;
|
||||||
|
let short_mention = mention.splitn(1, '@').next().unwrap_or("");
|
||||||
let link = Tag::Link(
|
let link = Tag::Link(
|
||||||
LinkType::Inline,
|
LinkType::Inline,
|
||||||
format!("{}@/{}/", base_url, &mention).into(),
|
format!("{}@/{}/", base_url, &mention).into(),
|
||||||
mention.clone().into(),
|
short_mention.to_owned().into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
mentions.push(mention.clone());
|
mentions.push(mention.clone());
|
||||||
events.push(Event::Start(link.clone()));
|
events.push(Event::Start(link.clone()));
|
||||||
events.push(Event::Text(format!("@{}", &mention).into()));
|
events.push(Event::Text(format!("@{}", &short_mention).into()));
|
||||||
events.push(Event::End(link));
|
events.push(Event::End(link));
|
||||||
|
|
||||||
(
|
(
|
||||||
@@ -450,10 +414,6 @@ pub fn md_to_html<'a>(
|
|||||||
(buf, mentions.collect(), hashtags.collect())
|
(buf, mentions.collect(), hashtags.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn escape(string: &str) -> askama_escape::Escaped<askama_escape::Html> {
|
|
||||||
askama_escape::escape(string, askama_escape::Html)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -516,20 +476,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_iri_percent_encode_seg() {
|
|
||||||
assert_eq!(
|
|
||||||
&iri_percent_encode_seg("including whitespace"),
|
|
||||||
"including%20whitespace"
|
|
||||||
);
|
|
||||||
assert_eq!(&iri_percent_encode_seg("%20"), "%2520");
|
|
||||||
assert_eq!(&iri_percent_encode_seg("é"), "é");
|
|
||||||
assert_eq!(
|
|
||||||
&iri_percent_encode_seg("空白入り 日本語"),
|
|
||||||
"空白入り%20日本語"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inline() {
|
fn test_inline() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "plume-front"
|
name = "plume-front"
|
||||||
version = "0.7.1"
|
version = "0.6.1-dev"
|
||||||
authors = ["Plume contributors"]
|
authors = ["Plume contributors"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
|||||||
+14
-20
@@ -54,6 +54,11 @@ pub enum EditorError {
|
|||||||
DOMError,
|
DOMError,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<std::option::NoneError> for EditorError {
|
||||||
|
fn from(_: std::option::NoneError) -> Self {
|
||||||
|
EditorError::NoneError
|
||||||
|
}
|
||||||
|
}
|
||||||
const AUTOSAVE_DEBOUNCE_TIME: i32 = 5000;
|
const AUTOSAVE_DEBOUNCE_TIME: i32 = 5000;
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct AutosaveInformation {
|
struct AutosaveInformation {
|
||||||
@@ -193,7 +198,7 @@ fn clear_autosave() {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.remove_item(&get_autosave_id())
|
.remove_item(&get_autosave_id())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
console::log_1(&format!("Saved to {}", &get_autosave_id()).into());
|
console::log_1(&&format!("Saved to {}", &get_autosave_id()).into());
|
||||||
}
|
}
|
||||||
type TimeoutHandle = i32;
|
type TimeoutHandle = i32;
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
@@ -361,9 +366,7 @@ fn init_editor() -> Result<(), EditorError> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let old_ed = old_ed.unwrap();
|
let old_ed = old_ed.unwrap();
|
||||||
let old_title = document()
|
let old_title = document().get_element_by_id("plume-editor-title")?;
|
||||||
.get_element_by_id("plume-editor-title")
|
|
||||||
.ok_or(EditorError::NoneError)?;
|
|
||||||
old_ed
|
old_ed
|
||||||
.dyn_ref::<HtmlElement>()
|
.dyn_ref::<HtmlElement>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -431,8 +434,7 @@ fn init_editor() -> Result<(), EditorError> {
|
|||||||
bg.class_list().add_1("show").unwrap();
|
bg.class_list().add_1("show").unwrap();
|
||||||
})) as Box<dyn FnMut(MouseEvent)>);
|
})) as Box<dyn FnMut(MouseEvent)>);
|
||||||
document()
|
document()
|
||||||
.get_element_by_id("publish")
|
.get_element_by_id("publish")?
|
||||||
.ok_or(EditorError::NoneError)?
|
|
||||||
.add_event_listener_with_callback("click", show_popup.as_ref().unchecked_ref())
|
.add_event_listener_with_callback("click", show_popup.as_ref().unchecked_ref())
|
||||||
.map_err(|_| EditorError::DOMError)?;
|
.map_err(|_| EditorError::DOMError)?;
|
||||||
show_popup.forget();
|
show_popup.forget();
|
||||||
@@ -526,14 +528,8 @@ fn init_popup(
|
|||||||
cover_label
|
cover_label
|
||||||
.set_attribute("for", "cover")
|
.set_attribute("for", "cover")
|
||||||
.map_err(|_| EditorError::DOMError)?;
|
.map_err(|_| EditorError::DOMError)?;
|
||||||
let cover = document
|
let cover = document.get_element_by_id("cover")?;
|
||||||
.get_element_by_id("cover")
|
cover.parent_element()?.remove_child(&cover).ok();
|
||||||
.ok_or(EditorError::NoneError)?;
|
|
||||||
cover
|
|
||||||
.parent_element()
|
|
||||||
.ok_or(EditorError::NoneError)?
|
|
||||||
.remove_child(&cover)
|
|
||||||
.ok();
|
|
||||||
popup
|
popup
|
||||||
.append_child(&cover_label)
|
.append_child(&cover_label)
|
||||||
.map_err(|_| EditorError::DOMError)?;
|
.map_err(|_| EditorError::DOMError)?;
|
||||||
@@ -558,7 +554,7 @@ fn init_popup(
|
|||||||
draft.set_checked(draft_checkbox.checked());
|
draft.set_checked(draft_checkbox.checked());
|
||||||
|
|
||||||
draft_label
|
draft_label
|
||||||
.append_child(draft)
|
.append_child(&draft)
|
||||||
.map_err(|_| EditorError::DOMError)?;
|
.map_err(|_| EditorError::DOMError)?;
|
||||||
draft_label
|
draft_label
|
||||||
.append_child(&document.create_text_node(&i18n!(CATALOG, "This is a draft")))
|
.append_child(&document.create_text_node(&i18n!(CATALOG, "This is a draft")))
|
||||||
@@ -624,12 +620,11 @@ fn init_popup(
|
|||||||
.map_err(|_| EditorError::DOMError)?;
|
.map_err(|_| EditorError::DOMError)?;
|
||||||
callback.forget();
|
callback.forget();
|
||||||
popup
|
popup
|
||||||
.append_child(button)
|
.append_child(&button)
|
||||||
.map_err(|_| EditorError::DOMError)?;
|
.map_err(|_| EditorError::DOMError)?;
|
||||||
|
|
||||||
document
|
document
|
||||||
.body()
|
.body()?
|
||||||
.ok_or(EditorError::NoneError)?
|
|
||||||
.append_child(&popup)
|
.append_child(&popup)
|
||||||
.map_err(|_| EditorError::DOMError)?;
|
.map_err(|_| EditorError::DOMError)?;
|
||||||
Ok(popup)
|
Ok(popup)
|
||||||
@@ -646,8 +641,7 @@ fn init_popup_bg() -> Result<Element, EditorError> {
|
|||||||
.map_err(|_| EditorError::DOMError)?;
|
.map_err(|_| EditorError::DOMError)?;
|
||||||
|
|
||||||
document()
|
document()
|
||||||
.body()
|
.body()?
|
||||||
.ok_or(EditorError::NoneError)?
|
|
||||||
.append_child(&bg)
|
.append_child(&bg)
|
||||||
.map_err(|_| EditorError::DOMError)?;
|
.map_err(|_| EditorError::DOMError)?;
|
||||||
let callback = Closure::wrap(Box::new(|_| close_popup()) as Box<dyn FnMut(MouseEvent)>);
|
let callback = Closure::wrap(Box::new(|_| close_popup()) as Box<dyn FnMut(MouseEvent)>);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![recursion_limit = "128"]
|
#![recursion_limit = "128"]
|
||||||
#![feature(decl_macro, proc_macro_hygiene)]
|
#![feature(decl_macro, proc_macro_hygiene, try_trait)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate gettext_macros;
|
extern crate gettext_macros;
|
||||||
@@ -61,7 +61,7 @@ lazy_static! {
|
|||||||
static ref CATALOG: gettext::Catalog = {
|
static ref CATALOG: gettext::Catalog = {
|
||||||
let catalogs = include_i18n!();
|
let catalogs = include_i18n!();
|
||||||
let lang = window().unwrap().navigator().language().unwrap();
|
let lang = window().unwrap().navigator().language().unwrap();
|
||||||
let lang = lang.split_once('-').map_or("en", |x| x.0);
|
let lang = lang.splitn(2, '-').next().unwrap_or("en");
|
||||||
|
|
||||||
let english_position = catalogs
|
let english_position = catalogs
|
||||||
.iter()
|
.iter()
|
||||||
@@ -85,7 +85,7 @@ pub fn main() -> Result<(), JsValue> {
|
|||||||
menu();
|
menu();
|
||||||
search();
|
search();
|
||||||
editor::init()
|
editor::init()
|
||||||
.map_err(|e| console::error_1(&format!("Editor error: {:?}", e).into()))
|
.map_err(|e| console::error_1(&&format!("Editor error: {:?}", e).into()))
|
||||||
.ok();
|
.ok();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "plume-macro"
|
name = "plume-macro"
|
||||||
version = "0.7.1"
|
version = "0.6.1-dev"
|
||||||
authors = ["Trinity Pointard <trinity.pointard@insa-rennes.fr>"]
|
authors = ["Trinity Pointard <trinity.pointard@insa-rennes.fr>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Plume procedural macros"
|
description = "Plume procedural macros"
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ pub fn import_migrations(input: TokenStream) -> TokenStream {
|
|||||||
(name, up_sql, down_sql)
|
(name, up_sql, down_sql)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let migrations_name = migrations.iter().map(|m| &m.0);
|
let migrations_name = migrations.iter().map(|m| &m.0).collect::<Vec<_>>();
|
||||||
let migrations_up = migrations
|
let migrations_up = migrations
|
||||||
.iter()
|
.iter()
|
||||||
.map(|m| m.1.as_str())
|
.map(|m| m.1.as_str())
|
||||||
@@ -103,7 +103,7 @@ fn file_to_migration(file: &str) -> TokenStream2 {
|
|||||||
acc.push('\n');
|
acc.push('\n');
|
||||||
}
|
}
|
||||||
} else if let Some(acc_str) = line.strip_prefix("--#!") {
|
} else if let Some(acc_str) = line.strip_prefix("--#!") {
|
||||||
acc.push_str(acc_str);
|
acc.push_str(&acc_str);
|
||||||
acc.push('\n');
|
acc.push('\n');
|
||||||
} else if line.starts_with("--") {
|
} else if line.starts_with("--") {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
+11
-11
@@ -1,17 +1,19 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "plume-models"
|
name = "plume-models"
|
||||||
version = "0.7.1"
|
version = "0.6.1-dev"
|
||||||
authors = ["Plume contributors"]
|
authors = ["Plume contributors"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
activitypub = "0.1.1"
|
activitypub = "0.1.1"
|
||||||
ammonia = "3.1.2"
|
ammonia = "2.1.1"
|
||||||
bcrypt = "0.10.1"
|
askama_escape = "0.1"
|
||||||
guid-create = "0.2"
|
bcrypt = "0.5"
|
||||||
itertools = "0.10.3"
|
guid-create = "0.1"
|
||||||
|
heck = "0.3.0"
|
||||||
|
itertools = "0.8.0"
|
||||||
lazy_static = "1.0"
|
lazy_static = "1.0"
|
||||||
ldap3 = "0.9.3"
|
ldap3 = "0.7.1"
|
||||||
migrations_internals= "1.4.0"
|
migrations_internals= "1.4.0"
|
||||||
openssl = "0.10.22"
|
openssl = "0.10.22"
|
||||||
rocket = "0.4.6"
|
rocket = "0.4.6"
|
||||||
@@ -20,21 +22,19 @@ reqwest = "0.9"
|
|||||||
scheduled-thread-pool = "0.2.2"
|
scheduled-thread-pool = "0.2.2"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
serde_json = "1.0.70"
|
serde_json = "1.0"
|
||||||
tantivy = "0.13.3"
|
tantivy = "0.13.3"
|
||||||
url = "2.1"
|
url = "2.1"
|
||||||
walkdir = "2.2"
|
walkdir = "2.2"
|
||||||
webfinger = "0.4.1"
|
webfinger = "0.4.1"
|
||||||
whatlang = "0.13.0"
|
whatlang = "0.11.1"
|
||||||
shrinkwraprs = "0.3.0"
|
shrinkwraprs = "0.2.1"
|
||||||
diesel-derive-newtype = "0.1.2"
|
diesel-derive-newtype = "0.1.2"
|
||||||
glob = "0.3.0"
|
glob = "0.3.0"
|
||||||
lindera-tantivy = { version = "0.7.1", optional = true }
|
lindera-tantivy = { version = "0.7.1", optional = true }
|
||||||
tracing = "0.1.22"
|
tracing = "0.1.22"
|
||||||
riker = "0.4.2"
|
riker = "0.4.2"
|
||||||
once_cell = "1.5.2"
|
once_cell = "1.5.2"
|
||||||
lettre = "0.9.6"
|
|
||||||
native-tls = "0.2.8"
|
|
||||||
|
|
||||||
[dependencies.chrono]
|
[dependencies.chrono]
|
||||||
features = ["serde"]
|
features = ["serde"]
|
||||||
|
|||||||
@@ -86,18 +86,14 @@ impl<'a, 'r> FromRequest<'a, 'r> for ApiToken {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut parsed_header = headers[0].split(' ');
|
let mut parsed_header = headers[0].split(' ');
|
||||||
let auth_type = parsed_header
|
let auth_type = parsed_header.next().map_or_else(
|
||||||
.next()
|
|| Outcome::Failure((Status::BadRequest, TokenError::NoType)),
|
||||||
.map_or_else::<rocket::Outcome<&str, _, ()>, _, _>(
|
Outcome::Success,
|
||||||
|| Outcome::Failure((Status::BadRequest, TokenError::NoType)),
|
)?;
|
||||||
Outcome::Success,
|
let val = parsed_header.next().map_or_else(
|
||||||
)?;
|
|| Outcome::Failure((Status::BadRequest, TokenError::NoValue)),
|
||||||
let val = parsed_header
|
Outcome::Success,
|
||||||
.next()
|
)?;
|
||||||
.map_or_else::<rocket::Outcome<&str, _, ()>, _, _>(
|
|
||||||
|| Outcome::Failure((Status::BadRequest, TokenError::NoValue)),
|
|
||||||
Outcome::Success,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if auth_type == "Bearer" {
|
if auth_type == "Bearer" {
|
||||||
let conn = request
|
let conn = request
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ impl BlocklistedEmail {
|
|||||||
pub fn delete_entries(conn: &Connection, ids: Vec<i32>) -> Result<bool> {
|
pub fn delete_entries(conn: &Connection, ids: Vec<i32>) -> Result<bool> {
|
||||||
use diesel::delete;
|
use diesel::delete;
|
||||||
for i in ids {
|
for i in ids {
|
||||||
let be: BlocklistedEmail = BlocklistedEmail::find_by_id(conn, i)?;
|
let be: BlocklistedEmail = BlocklistedEmail::find_by_id(&conn, i)?;
|
||||||
delete(&be).execute(conn)?;
|
delete(&be).execute(conn)?;
|
||||||
}
|
}
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
|||||||
+13
-26
@@ -149,15 +149,7 @@ impl Blog {
|
|||||||
.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")))
|
||||||
.ok_or(Error::Webfinger)
|
.ok_or(Error::Webfinger)
|
||||||
.and_then(|l| {
|
.and_then(|l| Blog::from_id(conn, &l.href?, None, CONFIG.proxy()).map_err(|(_, e)| e))
|
||||||
Blog::from_id(
|
|
||||||
conn,
|
|
||||||
&l.href.ok_or(Error::MissingApProperty)?,
|
|
||||||
None,
|
|
||||||
CONFIG.proxy(),
|
|
||||||
)
|
|
||||||
.map_err(|(_, e)| e)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_activity(&self, conn: &Connection) -> Result<CustomGroup> {
|
pub fn to_activity(&self, conn: &Connection) -> Result<CustomGroup> {
|
||||||
@@ -244,7 +236,7 @@ impl Blog {
|
|||||||
(min, max): (i32, i32),
|
(min, max): (i32, i32),
|
||||||
) -> Result<ActivityStream<OrderedCollectionPage>> {
|
) -> Result<ActivityStream<OrderedCollectionPage>> {
|
||||||
let mut coll = OrderedCollectionPage::default();
|
let mut coll = OrderedCollectionPage::default();
|
||||||
let acts = self.get_activity_page(conn, (min, max));
|
let acts = self.get_activity_page(&conn, (min, max));
|
||||||
//This still doesn't do anything because the outbox
|
//This still doesn't do anything because the outbox
|
||||||
//doesn't do anything yet
|
//doesn't do anything yet
|
||||||
coll.collection_page_props.set_next_link(Id::new(&format!(
|
coll.collection_page_props.set_next_link(Id::new(&format!(
|
||||||
@@ -273,10 +265,7 @@ impl Blog {
|
|||||||
|
|
||||||
pub fn get_keypair(&self) -> Result<PKey<Private>> {
|
pub fn get_keypair(&self) -> Result<PKey<Private>> {
|
||||||
PKey::from_rsa(Rsa::private_key_from_pem(
|
PKey::from_rsa(Rsa::private_key_from_pem(
|
||||||
self.private_key
|
self.private_key.clone()?.as_ref(),
|
||||||
.clone()
|
|
||||||
.ok_or(Error::MissingApProperty)?
|
|
||||||
.as_ref(),
|
|
||||||
)?)
|
)?)
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
}
|
}
|
||||||
@@ -329,7 +318,7 @@ impl Blog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(&self, conn: &Connection) -> Result<()> {
|
pub fn delete(&self, conn: &Connection) -> Result<()> {
|
||||||
for post in Post::get_for_blog(conn, self)? {
|
for post in Post::get_for_blog(conn, &self)? {
|
||||||
post.delete(conn)?;
|
post.delete(conn)?;
|
||||||
}
|
}
|
||||||
diesel::delete(self)
|
diesel::delete(self)
|
||||||
@@ -350,12 +339,12 @@ impl FromId<DbConn> for Blog {
|
|||||||
type Object = CustomGroup;
|
type Object = CustomGroup;
|
||||||
|
|
||||||
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
fn from_db(conn: &DbConn, id: &str) -> Result<Self> {
|
||||||
Self::find_by_ap_url(conn, id)
|
Self::find_by_ap_url(&conn, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_activity(conn: &DbConn, acct: CustomGroup) -> Result<Self> {
|
fn from_activity(conn: &DbConn, acct: CustomGroup) -> Result<Self> {
|
||||||
let url = Url::parse(&acct.object.object_props.id_string()?)?;
|
let url = Url::parse(&acct.object.object_props.id_string()?)?;
|
||||||
let inst = url.host_str().ok_or(Error::Url)?;
|
let inst = url.host_str()?;
|
||||||
let instance = Instance::find_by_domain(conn, inst).or_else(|_| {
|
let instance = Instance::find_by_domain(conn, inst).or_else(|_| {
|
||||||
Instance::insert(
|
Instance::insert(
|
||||||
conn,
|
conn,
|
||||||
@@ -443,10 +432,6 @@ impl FromId<DbConn> for Blog {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn sign::Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsActor<&PlumeRocket> for Blog {
|
impl AsActor<&PlumeRocket> for Blog {
|
||||||
@@ -466,22 +451,24 @@ impl AsActor<&PlumeRocket> for Blog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl sign::Signer for Blog {
|
impl sign::Signer for Blog {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
fn get_key_id(&self) -> String {
|
fn get_key_id(&self) -> String {
|
||||||
format!("{}#main-key", self.ap_url)
|
format!("{}#main-key", self.ap_url)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign(&self, to_sign: &str) -> sign::Result<Vec<u8>> {
|
fn sign(&self, to_sign: &str) -> Result<Vec<u8>> {
|
||||||
let key = self.get_keypair().map_err(|_| sign::Error())?;
|
let key = self.get_keypair()?;
|
||||||
let mut signer = Signer::new(MessageDigest::sha256(), &key)?;
|
let mut signer = Signer::new(MessageDigest::sha256(), &key)?;
|
||||||
signer.update(to_sign.as_bytes())?;
|
signer.update(to_sign.as_bytes())?;
|
||||||
signer.sign_to_vec().map_err(sign::Error::from)
|
signer.sign_to_vec().map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify(&self, data: &str, signature: &[u8]) -> sign::Result<bool> {
|
fn verify(&self, data: &str, signature: &[u8]) -> Result<bool> {
|
||||||
let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref())?)?;
|
let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref())?)?;
|
||||||
let mut verifier = Verifier::new(MessageDigest::sha256(), &key)?;
|
let mut verifier = Verifier::new(MessageDigest::sha256(), &key)?;
|
||||||
verifier.update(data.as_bytes())?;
|
verifier.update(data.as_bytes())?;
|
||||||
verifier.verify(signature).map_err(sign::Error::from)
|
verifier.verify(&signature).map_err(Error::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
|||||||
use plume_common::{
|
use plume_common::{
|
||||||
activity_pub::{
|
activity_pub::{
|
||||||
inbox::{AsActor, AsObject, FromId},
|
inbox::{AsActor, AsObject, FromId},
|
||||||
sign::Signer,
|
|
||||||
Id, IntoId, PUBLIC_VISIBILITY,
|
Id, IntoId, PUBLIC_VISIBILITY,
|
||||||
},
|
},
|
||||||
utils,
|
utils,
|
||||||
@@ -142,20 +141,18 @@ impl Comment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_activity(&self, conn: &DbConn) -> Result<Create> {
|
pub fn create_activity(&self, conn: &DbConn) -> Result<Create> {
|
||||||
let author = User::get(conn, self.author_id)?;
|
let author = User::get(&conn, self.author_id)?;
|
||||||
|
|
||||||
let note = self.to_activity(conn)?;
|
let note = self.to_activity(conn)?;
|
||||||
let mut act = Create::default();
|
let mut act = Create::default();
|
||||||
act.create_props.set_actor_link(author.into_id())?;
|
act.create_props.set_actor_link(author.into_id())?;
|
||||||
act.create_props.set_object_object(note.clone())?;
|
act.create_props.set_object_object(note.clone())?;
|
||||||
act.object_props.set_id_string(format!(
|
act.object_props
|
||||||
"{}/activity",
|
.set_id_string(format!("{}/activity", self.ap_url.clone()?,))?;
|
||||||
self.ap_url.clone().ok_or(Error::MissingApProperty)?,
|
|
||||||
))?;
|
|
||||||
act.object_props
|
act.object_props
|
||||||
.set_to_link_vec(note.object_props.to_link_vec::<Id>()?)?;
|
.set_to_link_vec(note.object_props.to_link_vec::<Id>()?)?;
|
||||||
act.object_props
|
act.object_props
|
||||||
.set_cc_link_vec(vec![Id::new(self.get_author(conn)?.followers_endpoint)])?;
|
.set_cc_link_vec(vec![Id::new(self.get_author(&conn)?.followers_endpoint)])?;
|
||||||
Ok(act)
|
Ok(act)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,9 +182,7 @@ impl Comment {
|
|||||||
.set_actor_link(self.get_author(conn)?.into_id())?;
|
.set_actor_link(self.get_author(conn)?.into_id())?;
|
||||||
|
|
||||||
let mut tombstone = Tombstone::default();
|
let mut tombstone = Tombstone::default();
|
||||||
tombstone
|
tombstone.object_props.set_id_string(self.ap_url.clone()?)?;
|
||||||
.object_props
|
|
||||||
.set_id_string(self.ap_url.clone().ok_or(Error::MissingApProperty)?)?;
|
|
||||||
act.delete_props.set_object_object(tombstone)?;
|
act.delete_props.set_object_object(tombstone)?;
|
||||||
|
|
||||||
act.object_props
|
act.object_props
|
||||||
@@ -209,13 +204,7 @@ impl FromId<DbConn> for Comment {
|
|||||||
|
|
||||||
fn from_activity(conn: &DbConn, note: Note) -> Result<Self> {
|
fn from_activity(conn: &DbConn, note: Note) -> Result<Self> {
|
||||||
let comm = {
|
let comm = {
|
||||||
let previous_url = note
|
let previous_url = note.object_props.in_reply_to.as_ref()?.as_str()?;
|
||||||
.object_props
|
|
||||||
.in_reply_to
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(Error::MissingApProperty)?
|
|
||||||
.as_str()
|
|
||||||
.ok_or(Error::MissingApProperty)?;
|
|
||||||
let previous_comment = Comment::find_by_ap_url(conn, previous_url);
|
let previous_comment = Comment::find_by_ap_url(conn, previous_url);
|
||||||
|
|
||||||
let is_public = |v: &Option<serde_json::Value>| match v
|
let is_public = |v: &Option<serde_json::Value>| match v
|
||||||
@@ -329,10 +318,6 @@ impl FromId<DbConn> for Comment {
|
|||||||
comm.notify(conn)?;
|
comm.notify(conn)?;
|
||||||
Ok(comm)
|
Ok(comm)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, Create, &DbConn> for Comment {
|
impl AsObject<User, Create, &DbConn> for Comment {
|
||||||
@@ -361,7 +346,7 @@ impl AsObject<User, Delete, &DbConn> for Comment {
|
|||||||
m.delete(conn)?;
|
m.delete(conn)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for n in Notification::find_for_comment(conn, &self)? {
|
for n in Notification::find_for_comment(&conn, &self)? {
|
||||||
n.delete(&**conn)?;
|
n.delete(&**conn)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-42
@@ -1,6 +1,4 @@
|
|||||||
use crate::search::TokenizerKind as SearchTokenizer;
|
use crate::search::TokenizerKind as SearchTokenizer;
|
||||||
use crate::signups::Strategy as SignupStrategy;
|
|
||||||
use crate::smtp::{SMTP_PORT, SUBMISSIONS_PORT, SUBMISSION_PORT};
|
|
||||||
use rocket::config::Limits;
|
use rocket::config::Limits;
|
||||||
use rocket::Config as RocketConfig;
|
use rocket::Config as RocketConfig;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
@@ -17,14 +15,12 @@ pub struct Config {
|
|||||||
pub db_name: &'static str,
|
pub db_name: &'static str,
|
||||||
pub db_max_size: Option<u32>,
|
pub db_max_size: Option<u32>,
|
||||||
pub db_min_idle: Option<u32>,
|
pub db_min_idle: Option<u32>,
|
||||||
pub signup: SignupStrategy,
|
|
||||||
pub search_index: String,
|
pub search_index: String,
|
||||||
pub search_tokenizers: SearchTokenizerConfig,
|
pub search_tokenizers: SearchTokenizerConfig,
|
||||||
pub rocket: Result<RocketConfig, InvalidRocketConfig>,
|
pub rocket: Result<RocketConfig, RocketError>,
|
||||||
pub logo: LogoConfig,
|
pub logo: LogoConfig,
|
||||||
pub default_theme: String,
|
pub default_theme: String,
|
||||||
pub media_directory: String,
|
pub media_directory: String,
|
||||||
pub mail: Option<MailConfig>,
|
|
||||||
pub ldap: Option<LdapConfig>,
|
pub ldap: Option<LdapConfig>,
|
||||||
pub proxy: Option<ProxyConfig>,
|
pub proxy: Option<ProxyConfig>,
|
||||||
}
|
}
|
||||||
@@ -35,21 +31,21 @@ impl Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum InvalidRocketConfig {
|
pub enum RocketError {
|
||||||
Env,
|
InvalidEnv,
|
||||||
Address,
|
InvalidAddress,
|
||||||
SecretKey,
|
InvalidSecretKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_rocket_config() -> Result<RocketConfig, InvalidRocketConfig> {
|
fn get_rocket_config() -> Result<RocketConfig, RocketError> {
|
||||||
let mut c = RocketConfig::active().map_err(|_| InvalidRocketConfig::Env)?;
|
let mut c = RocketConfig::active().map_err(|_| RocketError::InvalidEnv)?;
|
||||||
|
|
||||||
let address = var("ROCKET_ADDRESS").unwrap_or_else(|_| "localhost".to_owned());
|
let address = var("ROCKET_ADDRESS").unwrap_or_else(|_| "localhost".to_owned());
|
||||||
let port = var("ROCKET_PORT")
|
let port = var("ROCKET_PORT")
|
||||||
.ok()
|
.ok()
|
||||||
.map(|s| s.parse::<u16>().unwrap())
|
.map(|s| s.parse::<u16>().unwrap())
|
||||||
.unwrap_or(7878);
|
.unwrap_or(7878);
|
||||||
let secret_key = var("ROCKET_SECRET_KEY").map_err(|_| InvalidRocketConfig::SecretKey)?;
|
let secret_key = var("ROCKET_SECRET_KEY").map_err(|_| RocketError::InvalidSecretKey)?;
|
||||||
let form_size = var("FORM_SIZE")
|
let form_size = var("FORM_SIZE")
|
||||||
.unwrap_or_else(|_| "128".to_owned())
|
.unwrap_or_else(|_| "128".to_owned())
|
||||||
.parse::<u64>()
|
.parse::<u64>()
|
||||||
@@ -60,10 +56,10 @@ fn get_rocket_config() -> Result<RocketConfig, InvalidRocketConfig> {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
c.set_address(address)
|
c.set_address(address)
|
||||||
.map_err(|_| InvalidRocketConfig::Address)?;
|
.map_err(|_| RocketError::InvalidAddress)?;
|
||||||
c.set_port(port);
|
c.set_port(port);
|
||||||
c.set_secret_key(secret_key)
|
c.set_secret_key(secret_key)
|
||||||
.map_err(|_| InvalidRocketConfig::SecretKey)?;
|
.map_err(|_| RocketError::InvalidSecretKey)?;
|
||||||
|
|
||||||
c.set_limits(
|
c.set_limits(
|
||||||
Limits::new()
|
Limits::new()
|
||||||
@@ -159,7 +155,7 @@ impl Default for LogoConfig {
|
|||||||
.ok()
|
.ok()
|
||||||
.or_else(|| custom_main.clone());
|
.or_else(|| custom_main.clone());
|
||||||
let other = if let Some(main) = custom_main.clone() {
|
let other = if let Some(main) = custom_main.clone() {
|
||||||
let ext = |path: &str| match path.rsplit_once('.').map(|x| x.1) {
|
let ext = |path: &str| match path.rsplitn(2, '.').next() {
|
||||||
Some("png") => Some("image/png".to_owned()),
|
Some("png") => Some("image/png".to_owned()),
|
||||||
Some("jpg") | Some("jpeg") => Some("image/jpeg".to_owned()),
|
Some("jpg") | Some("jpeg") => Some("image/jpeg".to_owned()),
|
||||||
Some("svg") => Some("image/svg+xml".to_owned()),
|
Some("svg") => Some("image/svg+xml".to_owned()),
|
||||||
@@ -249,31 +245,6 @@ impl SearchTokenizerConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MailConfig {
|
|
||||||
pub server: String,
|
|
||||||
pub port: u16,
|
|
||||||
pub helo_name: String,
|
|
||||||
pub username: String,
|
|
||||||
pub password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_mail_config() -> Option<MailConfig> {
|
|
||||||
Some(MailConfig {
|
|
||||||
server: env::var("MAIL_SERVER").ok()?,
|
|
||||||
port: env::var("MAIL_PORT").map_or(SUBMISSIONS_PORT, |port| match port.as_str() {
|
|
||||||
"smtp" => SMTP_PORT,
|
|
||||||
"submissions" => SUBMISSIONS_PORT,
|
|
||||||
"submission" => SUBMISSION_PORT,
|
|
||||||
number => number
|
|
||||||
.parse()
|
|
||||||
.expect(r#"MAIL_PORT must be "smtp", "submissions", "submission" or an integer."#),
|
|
||||||
}),
|
|
||||||
helo_name: env::var("MAIL_HELO_NAME").unwrap_or_else(|_| "localhost".to_owned()),
|
|
||||||
username: env::var("MAIL_USER").ok()?,
|
|
||||||
password: env::var("MAIL_PASSWORD").ok()?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LdapConfig {
|
pub struct LdapConfig {
|
||||||
pub addr: String,
|
pub addr: String,
|
||||||
pub base_dn: String,
|
pub base_dn: String,
|
||||||
@@ -364,7 +335,6 @@ lazy_static! {
|
|||||||
s.parse::<u32>()
|
s.parse::<u32>()
|
||||||
.expect("Couldn't parse DB_MIN_IDLE into u32")
|
.expect("Couldn't parse DB_MIN_IDLE into u32")
|
||||||
)),
|
)),
|
||||||
signup: var("SIGNUP").map_or(SignupStrategy::default(), |s| s.parse().unwrap()),
|
|
||||||
#[cfg(feature = "postgres")]
|
#[cfg(feature = "postgres")]
|
||||||
database_url: var("DATABASE_URL")
|
database_url: var("DATABASE_URL")
|
||||||
.unwrap_or_else(|_| format!("postgres://plume:plume@localhost/{}", DB_NAME)),
|
.unwrap_or_else(|_| format!("postgres://plume:plume@localhost/{}", DB_NAME)),
|
||||||
@@ -377,7 +347,6 @@ lazy_static! {
|
|||||||
default_theme: var("DEFAULT_THEME").unwrap_or_else(|_| "default-light".to_owned()),
|
default_theme: var("DEFAULT_THEME").unwrap_or_else(|_| "default-light".to_owned()),
|
||||||
media_directory: var("MEDIA_UPLOAD_DIRECTORY")
|
media_directory: var("MEDIA_UPLOAD_DIRECTORY")
|
||||||
.unwrap_or_else(|_| "static/media".to_owned()),
|
.unwrap_or_else(|_| "static/media".to_owned()),
|
||||||
mail: get_mail_config(),
|
|
||||||
ldap: get_ldap_config(),
|
ldap: get_ldap_config(),
|
||||||
proxy: get_proxy_config(),
|
proxy: get_proxy_config(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,143 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
db_conn::DbConn,
|
|
||||||
schema::email_signups,
|
|
||||||
users::{NewUser, Role, User},
|
|
||||||
Error, Result,
|
|
||||||
};
|
|
||||||
use chrono::{offset::Utc, Duration, NaiveDateTime};
|
|
||||||
use diesel::{
|
|
||||||
Connection as _, ExpressionMethods, Identifiable, Insertable, QueryDsl, Queryable, RunQueryDsl,
|
|
||||||
};
|
|
||||||
use plume_common::utils::random_hex;
|
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
const TOKEN_VALIDITY_HOURS: i64 = 2;
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct Token(String);
|
|
||||||
|
|
||||||
impl From<String> for Token {
|
|
||||||
fn from(string: String) -> Self {
|
|
||||||
Token(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Token> for String {
|
|
||||||
fn from(token: Token) -> Self {
|
|
||||||
token.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Token {
|
|
||||||
type Target = String;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Token {
|
|
||||||
fn generate() -> Self {
|
|
||||||
Self(random_hex())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Identifiable, Queryable)]
|
|
||||||
pub struct EmailSignup {
|
|
||||||
pub id: i32,
|
|
||||||
pub email: String,
|
|
||||||
pub token: String,
|
|
||||||
pub expiration_date: NaiveDateTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Insertable)]
|
|
||||||
#[table_name = "email_signups"]
|
|
||||||
pub struct NewEmailSignup<'a> {
|
|
||||||
pub email: &'a str,
|
|
||||||
pub token: &'a str,
|
|
||||||
pub expiration_date: NaiveDateTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EmailSignup {
|
|
||||||
pub fn start(conn: &DbConn, email: &str) -> Result<Token> {
|
|
||||||
conn.transaction(|| {
|
|
||||||
Self::ensure_user_not_exist_by_email(conn, email)?;
|
|
||||||
let _rows = Self::delete_existings_by_email(conn, email)?;
|
|
||||||
let token = Token::generate();
|
|
||||||
let expiration_date = Utc::now()
|
|
||||||
.naive_utc()
|
|
||||||
.checked_add_signed(Duration::hours(TOKEN_VALIDITY_HOURS))
|
|
||||||
.expect("could not calculate expiration date");
|
|
||||||
let new_signup = NewEmailSignup {
|
|
||||||
email,
|
|
||||||
token: &token,
|
|
||||||
expiration_date,
|
|
||||||
};
|
|
||||||
let _rows = diesel::insert_into(email_signups::table)
|
|
||||||
.values(new_signup)
|
|
||||||
.execute(&**conn)?;
|
|
||||||
|
|
||||||
Ok(token)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_by_token(conn: &DbConn, token: Token) -> Result<Self> {
|
|
||||||
let signup = email_signups::table
|
|
||||||
.filter(email_signups::token.eq(token.as_str()))
|
|
||||||
.first::<Self>(&**conn)
|
|
||||||
.map_err(Error::from)?;
|
|
||||||
Ok(signup)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn confirm(&self, conn: &DbConn) -> Result<()> {
|
|
||||||
conn.transaction(|| {
|
|
||||||
Self::ensure_user_not_exist_by_email(conn, &self.email)?;
|
|
||||||
if self.expired() {
|
|
||||||
Self::delete_existings_by_email(conn, &self.email)?;
|
|
||||||
return Err(Error::Expired);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn complete(&self, conn: &DbConn, username: String, password: String) -> Result<User> {
|
|
||||||
conn.transaction(|| {
|
|
||||||
Self::ensure_user_not_exist_by_email(conn, &self.email)?;
|
|
||||||
let user = NewUser::new_local(
|
|
||||||
conn,
|
|
||||||
username,
|
|
||||||
"".to_string(),
|
|
||||||
Role::Normal,
|
|
||||||
"",
|
|
||||||
self.email.clone(),
|
|
||||||
Some(User::hash_pass(&password)?),
|
|
||||||
)?;
|
|
||||||
self.delete(conn)?;
|
|
||||||
Ok(user)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn delete(&self, conn: &DbConn) -> Result<()> {
|
|
||||||
let _rows = diesel::delete(self).execute(&**conn).map_err(Error::from)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ensure_user_not_exist_by_email(conn: &DbConn, email: &str) -> Result<()> {
|
|
||||||
if User::email_used(conn, email)? {
|
|
||||||
let _rows = Self::delete_existings_by_email(conn, email)?;
|
|
||||||
return Err(Error::UserAlreadyExists);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn delete_existings_by_email(conn: &DbConn, email: &str) -> Result<usize> {
|
|
||||||
let existing_signups = email_signups::table.filter(email_signups::email.eq(email));
|
|
||||||
diesel::delete(existing_signups)
|
|
||||||
.execute(&**conn)
|
|
||||||
.map_err(Error::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expired(&self) -> bool {
|
|
||||||
self.expiration_date < Utc::now().naive_utc()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
ap_url, db_conn::DbConn, instance::Instance, notifications::*, schema::follows, users::User,
|
ap_url, db_conn::DbConn, notifications::*, schema::follows, users::User, Connection, Error,
|
||||||
Connection, Error, Result, CONFIG,
|
Result, CONFIG,
|
||||||
};
|
};
|
||||||
use activitypub::activity::{Accept, Follow as FollowAct, Undo};
|
use activitypub::activity::{Accept, Follow as FollowAct, Undo};
|
||||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
||||||
@@ -183,10 +183,6 @@ impl FromId<DbConn> for Follow {
|
|||||||
.map_err(|(_, e)| e)?;
|
.map_err(|(_, e)| e)?;
|
||||||
Follow::accept_follow(conn, &actor, &target, follow, actor.id, target.id)
|
Follow::accept_follow(conn, &actor, &target, follow, actor.id, target.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, Undo, &DbConn> for Follow {
|
impl AsObject<User, Undo, &DbConn> for Follow {
|
||||||
@@ -199,7 +195,7 @@ impl AsObject<User, Undo, &DbConn> for Follow {
|
|||||||
diesel::delete(&self).execute(&**conn)?;
|
diesel::delete(&self).execute(&**conn)?;
|
||||||
|
|
||||||
// delete associated notification if any
|
// delete associated notification if any
|
||||||
if let Ok(notif) = Notification::find(conn, notification_kind::FOLLOW, self.id) {
|
if let Ok(notif) = Notification::find(&conn, notification_kind::FOLLOW, self.id) {
|
||||||
diesel::delete(¬if).execute(&**conn)?;
|
diesel::delete(¬if).execute(&**conn)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,11 @@ use crate::{
|
|||||||
medias::Media,
|
medias::Media,
|
||||||
safe_string::SafeString,
|
safe_string::SafeString,
|
||||||
schema::{instances, users},
|
schema::{instances, users},
|
||||||
users::{NewUser, Role, User},
|
users::{Role, User},
|
||||||
Connection, Error, Result,
|
Connection, Error, Result,
|
||||||
};
|
};
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use diesel::{self, result::Error::NotFound, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use plume_common::utils::md_to_html;
|
use plume_common::utils::md_to_html;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
@@ -46,9 +45,6 @@ lazy_static! {
|
|||||||
static ref LOCAL_INSTANCE: RwLock<Option<Instance>> = RwLock::new(None);
|
static ref LOCAL_INSTANCE: RwLock<Option<Instance>> = RwLock::new(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
const LOCAL_INSTANCE_USERNAME: &str = "__instance__";
|
|
||||||
static LOCAL_INSTANCE_USER: OnceCell<User> = OnceCell::new();
|
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
pub fn set_local(self) {
|
pub fn set_local(self) {
|
||||||
LOCAL_INSTANCE.write().unwrap().replace(self);
|
LOCAL_INSTANCE.write().unwrap().replace(self);
|
||||||
@@ -80,42 +76,6 @@ impl Instance {
|
|||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_local_instance_user(conn: &Connection) -> Result<User> {
|
|
||||||
let instance = Instance::get_local()?;
|
|
||||||
let email = format!("{}@{}", LOCAL_INSTANCE_USERNAME, &instance.public_domain);
|
|
||||||
NewUser::new_local(
|
|
||||||
conn,
|
|
||||||
LOCAL_INSTANCE_USERNAME.into(),
|
|
||||||
instance.public_domain,
|
|
||||||
Role::Instance,
|
|
||||||
"Local instance",
|
|
||||||
email,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_local_instance_user() -> Option<&'static User> {
|
|
||||||
LOCAL_INSTANCE_USER.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_local_instance_user_uncached(conn: &Connection) -> Result<User> {
|
|
||||||
users::table
|
|
||||||
.filter(users::role.eq(3))
|
|
||||||
.first(conn)
|
|
||||||
.or_else(|err| match err {
|
|
||||||
NotFound => Self::create_local_instance_user(conn),
|
|
||||||
_ => Err(Error::Db(err)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cache_local_instance_user(conn: &Connection) {
|
|
||||||
let _ = LOCAL_INSTANCE_USER.get_or_init(|| {
|
|
||||||
Self::get_local_instance_user_uncached(conn)
|
|
||||||
.or_else(|_| Self::create_local_instance_user(conn))
|
|
||||||
.expect("Failed to cache local instance user")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn page(conn: &Connection, (min, max): (i32, i32)) -> Result<Vec<Instance>> {
|
pub fn page(conn: &Connection, (min, max): (i32, i32)) -> Result<Vec<Instance>> {
|
||||||
instances::table
|
instances::table
|
||||||
.order(instances::public_domain.asc())
|
.order(instances::public_domain.asc())
|
||||||
@@ -344,7 +304,6 @@ pub(crate) mod tests {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Instance::cache_local(conn);
|
Instance::cache_local(conn);
|
||||||
Instance::cache_local_instance_user(conn);
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Regular → Executable
+8
-45
@@ -1,3 +1,4 @@
|
|||||||
|
#![feature(try_trait)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
#![feature(proc_macro_hygiene)]
|
#![feature(proc_macro_hygiene)]
|
||||||
#![feature(box_patterns)]
|
#![feature(box_patterns)]
|
||||||
@@ -16,10 +17,8 @@ extern crate serde_json;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate tantivy;
|
extern crate tantivy;
|
||||||
|
|
||||||
pub use lettre;
|
|
||||||
pub use lettre::smtp;
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use plume_common::activity_pub::{inbox::InboxError, request, sign};
|
use plume_common::activity_pub::inbox::InboxError;
|
||||||
use posts::PostEvent;
|
use posts::PostEvent;
|
||||||
use riker::actors::{channel, ActorSystem, ChannelRef, SystemBuilder};
|
use riker::actors::{channel, ActorSystem, ChannelRef, SystemBuilder};
|
||||||
use users::UserEvent;
|
use users::UserEvent;
|
||||||
@@ -67,7 +66,6 @@ pub enum Error {
|
|||||||
Url,
|
Url,
|
||||||
Webfinger,
|
Webfinger,
|
||||||
Expired,
|
Expired,
|
||||||
UserAlreadyExists,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<bcrypt::BcryptError> for Error {
|
impl From<bcrypt::BcryptError> for Error {
|
||||||
@@ -82,18 +80,18 @@ impl From<openssl::error::ErrorStack> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<sign::Error> for Error {
|
|
||||||
fn from(_: sign::Error) -> Self {
|
|
||||||
Error::Signature
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<diesel::result::Error> for Error {
|
impl From<diesel::result::Error> for Error {
|
||||||
fn from(err: diesel::result::Error) -> Self {
|
fn from(err: diesel::result::Error) -> Self {
|
||||||
Error::Db(err)
|
Error::Db(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<std::option::NoneError> for Error {
|
||||||
|
fn from(_: std::option::NoneError) -> Self {
|
||||||
|
Error::NotFound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<url::ParseError> for Error {
|
impl From<url::ParseError> for Error {
|
||||||
fn from(_: url::ParseError) -> Self {
|
fn from(_: url::ParseError) -> Self {
|
||||||
Error::Url
|
Error::Url
|
||||||
@@ -160,12 +158,6 @@ impl From<InboxError<Error>> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<request::Error> for Error {
|
|
||||||
fn from(_err: request::Error) -> Error {
|
|
||||||
Error::Request
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
/// Adds a function to a model, that returns the first
|
/// Adds a function to a model, that returns the first
|
||||||
@@ -303,33 +295,6 @@ pub fn ap_url(url: &str) -> String {
|
|||||||
format!("https://{}", url)
|
format!("https://{}", url)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SmtpNewWithAddr {
|
|
||||||
fn new_with_addr(
|
|
||||||
addr: (&str, u16),
|
|
||||||
) -> std::result::Result<smtp::SmtpClient, smtp::error::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SmtpNewWithAddr for smtp::SmtpClient {
|
|
||||||
// Stolen from lettre::smtp::SmtpClient::new_simple()
|
|
||||||
fn new_with_addr(addr: (&str, u16)) -> std::result::Result<Self, smtp::error::Error> {
|
|
||||||
use native_tls::TlsConnector;
|
|
||||||
use smtp::{
|
|
||||||
client::net::{ClientTlsParameters, DEFAULT_TLS_PROTOCOLS},
|
|
||||||
ClientSecurity, SmtpClient,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (domain, port) = addr;
|
|
||||||
|
|
||||||
let mut tls_builder = TlsConnector::builder();
|
|
||||||
tls_builder.min_protocol_version(Some(DEFAULT_TLS_PROTOCOLS[0]));
|
|
||||||
|
|
||||||
let tls_parameters =
|
|
||||||
ClientTlsParameters::new(domain.to_string(), tls_builder.build().unwrap());
|
|
||||||
|
|
||||||
SmtpClient::new((domain, port), ClientSecurity::Wrapper(tls_parameters))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod tests {
|
mod tests {
|
||||||
@@ -377,7 +342,6 @@ pub mod blogs;
|
|||||||
pub mod comment_seers;
|
pub mod comment_seers;
|
||||||
pub mod comments;
|
pub mod comments;
|
||||||
pub mod db_conn;
|
pub mod db_conn;
|
||||||
pub mod email_signups;
|
|
||||||
pub mod follows;
|
pub mod follows;
|
||||||
pub mod headers;
|
pub mod headers;
|
||||||
pub mod inbox;
|
pub mod inbox;
|
||||||
@@ -398,7 +362,6 @@ pub mod safe_string;
|
|||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
pub mod signups;
|
|
||||||
pub mod tags;
|
pub mod tags;
|
||||||
pub mod timeline;
|
pub mod timeline;
|
||||||
pub mod users;
|
pub mod users;
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
db_conn::DbConn, instance::Instance, notifications::*, posts::Post, schema::likes, timeline::*,
|
db_conn::DbConn, notifications::*, posts::Post, schema::likes, timeline::*, users::User,
|
||||||
users::User, Connection, Error, Result, CONFIG,
|
Connection, Error, Result, CONFIG,
|
||||||
};
|
};
|
||||||
use activitypub::activity;
|
use activitypub::activity;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||||
use plume_common::activity_pub::{
|
use plume_common::activity_pub::{
|
||||||
inbox::{AsActor, AsObject, FromId},
|
inbox::{AsActor, AsObject, FromId},
|
||||||
sign::Signer,
|
|
||||||
Id, IntoId, PUBLIC_VISIBILITY,
|
Id, IntoId, PUBLIC_VISIBILITY,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -138,10 +137,6 @@ impl FromId<DbConn> for Like {
|
|||||||
res.notify(conn)?;
|
res.notify(conn)?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, activity::Undo, &DbConn> for Like {
|
impl AsObject<User, activity::Undo, &DbConn> for Like {
|
||||||
@@ -153,7 +148,7 @@ impl AsObject<User, activity::Undo, &DbConn> for Like {
|
|||||||
diesel::delete(&self).execute(&**conn)?;
|
diesel::delete(&self).execute(&**conn)?;
|
||||||
|
|
||||||
// delete associated notification if any
|
// delete associated notification if any
|
||||||
if let Ok(notif) = Notification::find(conn, notification_kind::LIKE, self.id) {
|
if let Ok(notif) = Notification::find(&conn, notification_kind::LIKE, self.id) {
|
||||||
diesel::delete(¬if).execute(&**conn)?;
|
diesel::delete(¬if).execute(&**conn)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -143,7 +143,6 @@ macro_rules! func {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Clone, Queryable, Identifiable)]
|
#[derive(Clone, Queryable, Identifiable)]
|
||||||
struct ListElem {
|
struct ListElem {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
|
|||||||
+26
-35
@@ -3,11 +3,12 @@ use crate::{
|
|||||||
users::User, Connection, Error, Result, CONFIG,
|
users::User, Connection, Error, Result, CONFIG,
|
||||||
};
|
};
|
||||||
use activitypub::object::Image;
|
use activitypub::object::Image;
|
||||||
|
use askama_escape::escape;
|
||||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||||
use guid_create::GUID;
|
use guid_create::GUID;
|
||||||
use plume_common::{
|
use plume_common::{
|
||||||
activity_pub::{inbox::FromId, request, Id},
|
activity_pub::{inbox::FromId, Id},
|
||||||
utils::{escape, MediaProcessor},
|
utils::MediaProcessor,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
fs::{self, DirBuilder},
|
fs::{self, DirBuilder},
|
||||||
@@ -103,8 +104,8 @@ impl Media {
|
|||||||
pub fn category(&self) -> MediaCategory {
|
pub fn category(&self) -> MediaCategory {
|
||||||
match &*self
|
match &*self
|
||||||
.file_path
|
.file_path
|
||||||
.rsplit_once('.')
|
.rsplitn(2, '.')
|
||||||
.map(|x| x.1)
|
.next()
|
||||||
.expect("Media::category: extension error")
|
.expect("Media::category: extension error")
|
||||||
.to_lowercase()
|
.to_lowercase()
|
||||||
{
|
{
|
||||||
@@ -155,11 +156,7 @@ impl Media {
|
|||||||
if self.is_remote {
|
if self.is_remote {
|
||||||
Ok(self.remote_url.clone().unwrap_or_default())
|
Ok(self.remote_url.clone().unwrap_or_default())
|
||||||
} else {
|
} else {
|
||||||
let file_path = self.file_path.replace(path::MAIN_SEPARATOR, "/").replacen(
|
let file_path = self.file_path.replace(path::MAIN_SEPARATOR, "/");
|
||||||
&CONFIG.media_directory,
|
|
||||||
"static/media",
|
|
||||||
1,
|
|
||||||
); // "static/media" from plume::routs::plume_media_files()
|
|
||||||
Ok(ap_url(&format!(
|
Ok(ap_url(&format!(
|
||||||
"{}/{}",
|
"{}/{}",
|
||||||
Instance::get_local()?.public_domain,
|
Instance::get_local()?.public_domain,
|
||||||
@@ -207,33 +204,31 @@ impl Media {
|
|||||||
|
|
||||||
// TODO: merge with save_remote?
|
// TODO: merge with save_remote?
|
||||||
pub fn from_activity(conn: &DbConn, image: &Image) -> Result<Media> {
|
pub fn from_activity(conn: &DbConn, image: &Image) -> Result<Media> {
|
||||||
let remote_url = image
|
let remote_url = image.object_props.url_string().ok()?;
|
||||||
.object_props
|
|
||||||
.url_string()
|
|
||||||
.or(Err(Error::MissingApProperty))?;
|
|
||||||
let path = determine_mirror_file_path(&remote_url);
|
let path = determine_mirror_file_path(&remote_url);
|
||||||
let parent = path.parent().ok_or(Error::InvalidValue)?;
|
let parent = path.parent()?;
|
||||||
if !parent.is_dir() {
|
if !parent.is_dir() {
|
||||||
DirBuilder::new().recursive(true).create(parent)?;
|
DirBuilder::new().recursive(true).create(parent)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dest = fs::File::create(path.clone())?;
|
let mut dest = fs::File::create(path.clone()).ok()?;
|
||||||
// TODO: conditional GET
|
// TODO: conditional GET
|
||||||
request::get(
|
if let Some(proxy) = CONFIG.proxy() {
|
||||||
remote_url.as_str(),
|
reqwest::ClientBuilder::new().proxy(proxy.clone()).build()?
|
||||||
User::get_sender(),
|
} else {
|
||||||
CONFIG.proxy().cloned(),
|
reqwest::Client::new()
|
||||||
)?
|
}
|
||||||
.copy_to(&mut dest)?;
|
.get(remote_url.as_str())
|
||||||
|
.send()
|
||||||
|
.ok()?
|
||||||
|
.copy_to(&mut dest)
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
Media::find_by_file_path(conn, path.to_str().ok_or(Error::InvalidValue)?)
|
Media::find_by_file_path(conn, &path.to_str()?)
|
||||||
.and_then(|mut media| {
|
.and_then(|mut media| {
|
||||||
let mut updated = false;
|
let mut updated = false;
|
||||||
|
|
||||||
let alt_text = image
|
let alt_text = image.object_props.content_string().ok()?;
|
||||||
.object_props
|
|
||||||
.content_string()
|
|
||||||
.or(Err(Error::NotFound))?;
|
|
||||||
let sensitive = image.object_props.summary_string().is_ok();
|
let sensitive = image.object_props.summary_string().is_ok();
|
||||||
let content_warning = image.object_props.summary_string().ok();
|
let content_warning = image.object_props.summary_string().ok();
|
||||||
if media.alt_text != alt_text {
|
if media.alt_text != alt_text {
|
||||||
@@ -265,11 +260,8 @@ impl Media {
|
|||||||
Media::insert(
|
Media::insert(
|
||||||
conn,
|
conn,
|
||||||
NewMedia {
|
NewMedia {
|
||||||
file_path: path.to_str().ok_or(Error::InvalidValue)?.to_string(),
|
file_path: path.to_str()?.to_string(),
|
||||||
alt_text: image
|
alt_text: image.object_props.content_string().ok()?,
|
||||||
.object_props
|
|
||||||
.content_string()
|
|
||||||
.or(Err(Error::NotFound))?,
|
|
||||||
is_remote: false,
|
is_remote: false,
|
||||||
remote_url: None,
|
remote_url: None,
|
||||||
sensitive: image.object_props.summary_string().is_ok(),
|
sensitive: image.object_props.summary_string().is_ok(),
|
||||||
@@ -279,10 +271,9 @@ impl Media {
|
|||||||
image
|
image
|
||||||
.object_props
|
.object_props
|
||||||
.attributed_to_link_vec::<Id>()
|
.attributed_to_link_vec::<Id>()
|
||||||
.or(Err(Error::NotFound))?
|
.ok()?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.next()
|
.next()?
|
||||||
.ok_or(Error::NotFound)?
|
|
||||||
.as_ref(),
|
.as_ref(),
|
||||||
None,
|
None,
|
||||||
CONFIG.proxy(),
|
CONFIG.proxy(),
|
||||||
@@ -330,7 +321,7 @@ fn determine_mirror_file_path(url: &str) -> PathBuf {
|
|||||||
.next()
|
.next()
|
||||||
.map(ToOwned::to_owned)
|
.map(ToOwned::to_owned)
|
||||||
.unwrap_or_else(|| String::from("png"));
|
.unwrap_or_else(|| String::from("png"));
|
||||||
file_path.push(format!("{}.{}", GUID::rand(), ext));
|
file_path.push(format!("{}.{}", GUID::rand().to_string(), ext));
|
||||||
});
|
});
|
||||||
file_path
|
file_path
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,11 +47,7 @@ impl Mention {
|
|||||||
|
|
||||||
pub fn get_user(&self, conn: &Connection) -> Result<User> {
|
pub fn get_user(&self, conn: &Connection) -> Result<User> {
|
||||||
match self.get_post(conn) {
|
match self.get_post(conn) {
|
||||||
Ok(p) => Ok(p
|
Ok(p) => Ok(p.get_authors(conn)?.into_iter().next()?),
|
||||||
.get_authors(conn)?
|
|
||||||
.into_iter()
|
|
||||||
.next()
|
|
||||||
.ok_or(Error::NotFound)?),
|
|
||||||
Err(_) => self.get_comment(conn).and_then(|c| c.get_author(conn)),
|
Err(_) => self.get_comment(conn).and_then(|c| c.get_author(conn)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +77,7 @@ impl Mention {
|
|||||||
in_post: bool,
|
in_post: bool,
|
||||||
notify: bool,
|
notify: bool,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let ap_url = ment.link_props.href_string().or(Err(Error::NotFound))?;
|
let ap_url = ment.link_props.href_string().ok()?;
|
||||||
let mentioned = User::find_by_ap_url(conn, &ap_url)?;
|
let mentioned = User::find_by_ap_url(conn, &ap_url)?;
|
||||||
|
|
||||||
if in_post {
|
if in_post {
|
||||||
|
|||||||
@@ -105,8 +105,7 @@ impl ImportedMigrations {
|
|||||||
pub fn rerun_last_migration(&self, conn: &Connection, path: &Path) -> Result<()> {
|
pub fn rerun_last_migration(&self, conn: &Connection, path: &Path) -> Result<()> {
|
||||||
let latest_migration = conn.latest_run_migration_version()?;
|
let latest_migration = conn.latest_run_migration_version()?;
|
||||||
let id = latest_migration
|
let id = latest_migration
|
||||||
.and_then(|m| self.0.binary_search_by_key(&m.as_str(), |m| m.name).ok())
|
.and_then(|m| self.0.binary_search_by_key(&m.as_str(), |m| m.name).ok())?;
|
||||||
.ok_or(Error::NotFound)?;
|
|
||||||
let migration = &self.0[id];
|
let migration = &self.0[id];
|
||||||
conn.transaction(|| {
|
conn.transaction(|| {
|
||||||
migration.revert(conn, path)?;
|
migration.revert(conn, path)?;
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ impl PasswordResetRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_and_delete_by_token(conn: &Connection, token: &str) -> Result<Self> {
|
pub fn find_and_delete_by_token(conn: &Connection, token: &str) -> Result<Self> {
|
||||||
let request = Self::find_by_token(conn, token)?;
|
let request = Self::find_by_token(&conn, &token)?;
|
||||||
|
|
||||||
let filter =
|
let filter =
|
||||||
password_reset_requests::table.filter(password_reset_requests::id.eq(request.id));
|
password_reset_requests::table.filter(password_reset_requests::id.eq(request.id));
|
||||||
|
|||||||
+24
-41
@@ -10,15 +10,15 @@ use activitypub::{
|
|||||||
CustomObject,
|
CustomObject,
|
||||||
};
|
};
|
||||||
use chrono::{NaiveDateTime, TimeZone, Utc};
|
use chrono::{NaiveDateTime, TimeZone, Utc};
|
||||||
use diesel::{self, BelongingToDsl, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, BelongingToDsl, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
||||||
|
use heck::KebabCase;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use plume_common::{
|
use plume_common::{
|
||||||
activity_pub::{
|
activity_pub::{
|
||||||
inbox::{AsActor, AsObject, FromId},
|
inbox::{AsActor, AsObject, FromId},
|
||||||
sign::Signer,
|
|
||||||
Hashtag, Id, IntoId, Licensed, Source, PUBLIC_VISIBILITY,
|
Hashtag, Id, IntoId, Licensed, Source, PUBLIC_VISIBILITY,
|
||||||
},
|
},
|
||||||
utils::{iri_percent_encode_seg, md_to_html},
|
utils::md_to_html,
|
||||||
};
|
};
|
||||||
use riker::actors::{Publish, Tell};
|
use riker::actors::{Publish, Tell};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
@@ -67,15 +67,20 @@ impl Post {
|
|||||||
find_by!(posts, find_by_ap_url, ap_url as &str);
|
find_by!(posts, find_by_ap_url, ap_url as &str);
|
||||||
|
|
||||||
last!(posts);
|
last!(posts);
|
||||||
pub fn insert(conn: &Connection, mut new: NewPost) -> Result<Self> {
|
pub fn insert(conn: &Connection, new: NewPost) -> Result<Self> {
|
||||||
if new.ap_url.is_empty() {
|
|
||||||
let blog = Blog::get(conn, new.blog_id)?;
|
|
||||||
new.ap_url = Self::ap_url(blog, &new.slug);
|
|
||||||
}
|
|
||||||
diesel::insert_into(posts::table)
|
diesel::insert_into(posts::table)
|
||||||
.values(new)
|
.values(new)
|
||||||
.execute(conn)?;
|
.execute(conn)?;
|
||||||
let post = Self::last(conn)?;
|
let mut post = Self::last(conn)?;
|
||||||
|
if post.ap_url.is_empty() {
|
||||||
|
post.ap_url = ap_url(&format!(
|
||||||
|
"{}/~/{}/{}/",
|
||||||
|
CONFIG.base_url,
|
||||||
|
post.get_blog(conn)?.fqn,
|
||||||
|
post.slug
|
||||||
|
));
|
||||||
|
let _: Post = post.save_changes(conn)?;
|
||||||
|
}
|
||||||
|
|
||||||
if post.published {
|
if post.published {
|
||||||
post.publish_published();
|
post.publish_published();
|
||||||
@@ -98,7 +103,7 @@ impl Post {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete(&self, conn: &Connection) -> Result<()> {
|
pub fn delete(&self, conn: &Connection) -> Result<()> {
|
||||||
for m in Mention::list_for_post(conn, self.id)? {
|
for m in Mention::list_for_post(&conn, self.id)? {
|
||||||
m.delete(conn)?;
|
m.delete(conn)?;
|
||||||
}
|
}
|
||||||
diesel::delete(self).execute(conn)?;
|
diesel::delete(self).execute(conn)?;
|
||||||
@@ -249,20 +254,6 @@ impl Post {
|
|||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ap_url(blog: Blog, slug: &str) -> String {
|
|
||||||
ap_url(&format!(
|
|
||||||
"{}/~/{}/{}/",
|
|
||||||
CONFIG.base_url,
|
|
||||||
blog.fqn,
|
|
||||||
iri_percent_encode_seg(slug)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// It's better to calc slug in insert and update
|
|
||||||
pub fn slug(title: &str) -> &str {
|
|
||||||
title
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_authors(&self, conn: &Connection) -> Result<Vec<User>> {
|
pub fn get_authors(&self, conn: &Connection) -> Result<Vec<User>> {
|
||||||
use crate::schema::post_authors;
|
use crate::schema::post_authors;
|
||||||
use crate::schema::users;
|
use crate::schema::users;
|
||||||
@@ -458,14 +449,14 @@ impl Post {
|
|||||||
.filter_map(|(id, m)| id.map(|id| (m, id)))
|
.filter_map(|(id, m)| id.map(|id| (m, id)))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let old_mentions = Mention::list_for_post(conn, self.id)?;
|
let old_mentions = Mention::list_for_post(&conn, self.id)?;
|
||||||
let old_user_mentioned = old_mentions
|
let old_user_mentioned = old_mentions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|m| m.mentioned_id)
|
.map(|m| m.mentioned_id)
|
||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
for (m, id) in &mentions {
|
for (m, id) in &mentions {
|
||||||
if !old_user_mentioned.contains(id) {
|
if !old_user_mentioned.contains(&id) {
|
||||||
Mention::from_activity(&*conn, m, self.id, true, true)?;
|
Mention::from_activity(&*conn, &m, self.id, true, true)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,7 +468,7 @@ impl Post {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter(|m| !new_mentions.contains(&m.mentioned_id))
|
.filter(|m| !new_mentions.contains(&m.mentioned_id))
|
||||||
{
|
{
|
||||||
m.delete(conn)?;
|
m.delete(&conn)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -658,12 +649,12 @@ impl FromId<DbConn> for Post {
|
|||||||
.and_then(|mut post| {
|
.and_then(|mut post| {
|
||||||
let mut updated = false;
|
let mut updated = false;
|
||||||
|
|
||||||
let slug = Self::slug(&title);
|
let slug = title.to_kebab_case();
|
||||||
let content = SafeString::new(&article.object_props.content_string()?);
|
let content = SafeString::new(&article.object_props.content_string()?);
|
||||||
let subtitle = article.object_props.summary_string()?;
|
let subtitle = article.object_props.summary_string()?;
|
||||||
let source = article.ap_object_props.source_object::<Source>()?.content;
|
let source = article.ap_object_props.source_object::<Source>()?.content;
|
||||||
if post.slug != slug {
|
if post.slug != slug {
|
||||||
post.slug = slug.to_string();
|
post.slug = slug;
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
if post.title != title {
|
if post.title != title {
|
||||||
@@ -701,8 +692,8 @@ impl FromId<DbConn> for Post {
|
|||||||
Post::insert(
|
Post::insert(
|
||||||
conn,
|
conn,
|
||||||
NewPost {
|
NewPost {
|
||||||
blog_id: blog.ok_or(Error::NotFound)?.id,
|
blog_id: blog?.id,
|
||||||
slug: Self::slug(&title).to_string(),
|
slug: title.to_kebab_case(),
|
||||||
title,
|
title,
|
||||||
content: SafeString::new(&article.object_props.content_string()?),
|
content: SafeString::new(&article.object_props.content_string()?),
|
||||||
published: true,
|
published: true,
|
||||||
@@ -760,10 +751,6 @@ impl FromId<DbConn> for Post {
|
|||||||
|
|
||||||
Ok(post)
|
Ok(post)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, Create, &DbConn> for Post {
|
impl AsObject<User, Create, &DbConn> for Post {
|
||||||
@@ -835,10 +822,6 @@ impl FromId<DbConn> for PostUpdate {
|
|||||||
tags: updated.object.object_props.tag,
|
tags: updated.object.object_props.tag,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, Update, &DbConn> for PostUpdate {
|
impl AsObject<User, Update, &DbConn> for PostUpdate {
|
||||||
@@ -855,7 +838,7 @@ impl AsObject<User, Update, &DbConn> for PostUpdate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(title) = self.title {
|
if let Some(title) = self.title {
|
||||||
post.slug = Post::slug(&title).to_string();
|
post.slug = title.to_kebab_case();
|
||||||
post.title = title;
|
post.title = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
db_conn::DbConn, instance::Instance, notifications::*, posts::Post, schema::reshares,
|
db_conn::DbConn, notifications::*, posts::Post, schema::reshares, timeline::*, users::User,
|
||||||
timeline::*, users::User, Connection, Error, Result, CONFIG,
|
Connection, Error, Result, CONFIG,
|
||||||
};
|
};
|
||||||
use activitypub::activity::{Announce, Undo};
|
use activitypub::activity::{Announce, Undo};
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||||
use plume_common::activity_pub::{
|
use plume_common::activity_pub::{
|
||||||
inbox::{AsActor, AsObject, FromId},
|
inbox::{AsActor, AsObject, FromId},
|
||||||
sign::Signer,
|
|
||||||
Id, IntoId, PUBLIC_VISIBILITY,
|
Id, IntoId, PUBLIC_VISIBILITY,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -163,10 +162,6 @@ impl FromId<DbConn> for Reshare {
|
|||||||
res.notify(conn)?;
|
res.notify(conn)?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsObject<User, Undo, &DbConn> for Reshare {
|
impl AsObject<User, Undo, &DbConn> for Reshare {
|
||||||
@@ -178,7 +173,7 @@ impl AsObject<User, Undo, &DbConn> for Reshare {
|
|||||||
diesel::delete(&self).execute(&**conn)?;
|
diesel::delete(&self).execute(&**conn)?;
|
||||||
|
|
||||||
// delete associated notification if any
|
// delete associated notification if any
|
||||||
if let Ok(notif) = Notification::find(conn, notification_kind::RESHARE, self.id) {
|
if let Ok(notif) = Notification::find(&conn, notification_kind::RESHARE, self.id) {
|
||||||
diesel::delete(¬if).execute(&**conn)?;
|
diesel::delete(¬if).execute(&**conn)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ pub struct SafeString {
|
|||||||
impl SafeString {
|
impl SafeString {
|
||||||
pub fn new(value: &str) -> Self {
|
pub fn new(value: &str) -> Self {
|
||||||
SafeString {
|
SafeString {
|
||||||
value: CLEAN.clean(value).to_string(),
|
value: CLEAN.clean(&value).to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ table! {
|
|||||||
user_id -> Int4,
|
user_id -> Int4,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
email_blocklist(id){
|
email_blocklist(id){
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
@@ -84,15 +83,6 @@ table! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table! {
|
|
||||||
email_signups (id) {
|
|
||||||
id -> Int4,
|
|
||||||
email -> Varchar,
|
|
||||||
token -> Varchar,
|
|
||||||
expiration_date -> Timestamp,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
follows (id) {
|
follows (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
@@ -316,7 +306,6 @@ allow_tables_to_appear_in_same_query!(
|
|||||||
blogs,
|
blogs,
|
||||||
comments,
|
comments,
|
||||||
comment_seers,
|
comment_seers,
|
||||||
email_signups,
|
|
||||||
follows,
|
follows,
|
||||||
instances,
|
instances,
|
||||||
likes,
|
likes,
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ impl PlumeQuery {
|
|||||||
|
|
||||||
/// Parse a query string into this Query
|
/// Parse a query string into this Query
|
||||||
pub fn parse_query(&mut self, query: &str) -> &mut Self {
|
pub fn parse_query(&mut self, query: &str) -> &mut Self {
|
||||||
self.from_str_req(query.trim())
|
self.from_str_req(&query.trim())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert this Query to a Tantivy Query
|
/// Convert this Query to a Tantivy Query
|
||||||
@@ -360,7 +360,7 @@ impl std::str::FromStr for PlumeQuery {
|
|||||||
fn from_str(query: &str) -> Result<PlumeQuery, !> {
|
fn from_str(query: &str) -> Result<PlumeQuery, !> {
|
||||||
let mut res: PlumeQuery = Default::default();
|
let mut res: PlumeQuery = Default::default();
|
||||||
|
|
||||||
res.from_str_req(query.trim());
|
res.from_str_req(&query.trim());
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
use crate::CONFIG;
|
|
||||||
use rocket::request::{FromRequest, Outcome, Request};
|
|
||||||
use std::fmt;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
pub enum Strategy {
|
|
||||||
Password,
|
|
||||||
Email,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Strategy {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Password
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Strategy {
|
|
||||||
type Err = StrategyError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
use self::Strategy::*;
|
|
||||||
|
|
||||||
match s {
|
|
||||||
"password" => Ok(Password),
|
|
||||||
"email" => Ok(Email),
|
|
||||||
s => Err(StrategyError::Unsupported(s.to_string())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum StrategyError {
|
|
||||||
Unsupported(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for StrategyError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
use self::StrategyError::*;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
// FIXME: Calc option strings from enum
|
|
||||||
Unsupported(s) => write!(f, "Unsupported strategy: {}. Choose password or email", s),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for StrategyError {}
|
|
||||||
|
|
||||||
pub struct Password();
|
|
||||||
pub struct Email();
|
|
||||||
|
|
||||||
impl<'a, 'r> FromRequest<'a, 'r> for Password {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn from_request(_request: &'a Request<'r>) -> Outcome<Self, ()> {
|
|
||||||
match matches!(CONFIG.signup, Strategy::Password) {
|
|
||||||
true => Outcome::Success(Self()),
|
|
||||||
false => Outcome::Forward(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'r> FromRequest<'a, 'r> for Email {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn from_request(_request: &'a Request<'r>) -> Outcome<Self, ()> {
|
|
||||||
match matches!(CONFIG.signup, Strategy::Email) {
|
|
||||||
true => Outcome::Success(Self()),
|
|
||||||
false => Outcome::Forward(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -223,9 +223,6 @@ impl Timeline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_post(&self, conn: &Connection, post: &Post) -> Result<()> {
|
pub fn add_post(&self, conn: &Connection, post: &Post) -> Result<()> {
|
||||||
if self.includes_post(conn, post)? {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
diesel::insert_into(timeline::table)
|
diesel::insert_into(timeline::table)
|
||||||
.values(TimelineEntry {
|
.values(TimelineEntry {
|
||||||
post_id: post.id,
|
post_id: post.id,
|
||||||
@@ -239,16 +236,6 @@ impl Timeline {
|
|||||||
let query = TimelineQuery::parse(&self.query)?;
|
let query = TimelineQuery::parse(&self.query)?;
|
||||||
query.matches(conn, self, post, kind)
|
query.matches(conn, self, post, kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn includes_post(&self, conn: &Connection, post: &Post) -> Result<bool> {
|
|
||||||
diesel::dsl::select(diesel::dsl::exists(
|
|
||||||
timeline::table
|
|
||||||
.filter(timeline::timeline_id.eq(self.id))
|
|
||||||
.filter(timeline::post_id.eq(post.id)),
|
|
||||||
))
|
|
||||||
.get_result(conn)
|
|
||||||
.map_err(Error::from)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -18,6 +18,12 @@ pub enum QueryError {
|
|||||||
RuntimeError(String),
|
RuntimeError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<std::option::NoneError> for QueryError {
|
||||||
|
fn from(_: std::option::NoneError) -> Self {
|
||||||
|
QueryError::UnexpectedEndOfQuery
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type QueryResult<T> = std::result::Result<T, QueryError>;
|
pub type QueryResult<T> = std::result::Result<T, QueryError>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
@@ -233,7 +239,7 @@ impl WithList {
|
|||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
match list {
|
match list {
|
||||||
List::List(name) => {
|
List::List(name) => {
|
||||||
let list = lists::List::find_for_user_by_name(conn, timeline.user_id, name)?;
|
let list = lists::List::find_for_user_by_name(conn, timeline.user_id, &name)?;
|
||||||
match (self, list.kind()) {
|
match (self, list.kind()) {
|
||||||
(WithList::Blog, ListType::Blog) => list.contains_blog(conn, post.blog_id),
|
(WithList::Blog, ListType::Blog) => list.contains_blog(conn, post.blog_id),
|
||||||
(WithList::Author { boosts, likes }, ListType::User) => match kind {
|
(WithList::Author { boosts, likes }, ListType::User) => match kind {
|
||||||
@@ -408,7 +414,7 @@ enum List<'a> {
|
|||||||
|
|
||||||
fn parse_s<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], TQ<'a>)> {
|
fn parse_s<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], TQ<'a>)> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let (left, token) = parse_a(stream)?;
|
let (left, token) = parse_a(&stream)?;
|
||||||
res.push(token);
|
res.push(token);
|
||||||
stream = left;
|
stream = left;
|
||||||
while !stream.is_empty() {
|
while !stream.is_empty() {
|
||||||
@@ -430,7 +436,7 @@ fn parse_s<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>],
|
|||||||
|
|
||||||
fn parse_a<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], TQ<'a>)> {
|
fn parse_a<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], TQ<'a>)> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let (left, token) = parse_b(stream)?;
|
let (left, token) = parse_b(&stream)?;
|
||||||
res.push(token);
|
res.push(token);
|
||||||
stream = left;
|
stream = left;
|
||||||
while !stream.is_empty() {
|
while !stream.is_empty() {
|
||||||
@@ -457,7 +463,7 @@ fn parse_b<'a, 'b>(stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], TQ<
|
|||||||
match left.get(0) {
|
match left.get(0) {
|
||||||
Some(Token::RParent(_)) => Ok((&left[1..], token)),
|
Some(Token::RParent(_)) => Ok((&left[1..], token)),
|
||||||
Some(t) => t.get_error(Token::RParent(0)),
|
Some(t) => t.get_error(Token::RParent(0)),
|
||||||
None => Err(QueryError::UnexpectedEndOfQuery),
|
None => None?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => parse_c(stream),
|
_ => parse_c(stream),
|
||||||
@@ -478,13 +484,9 @@ fn parse_c<'a, 'b>(stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], TQ<
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_d<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], Arg<'a>)> {
|
fn parse_d<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], Arg<'a>)> {
|
||||||
match stream
|
match stream.get(0).map(Token::get_text)? {
|
||||||
.get(0)
|
|
||||||
.map(Token::get_text)
|
|
||||||
.ok_or(QueryError::UnexpectedEndOfQuery)?
|
|
||||||
{
|
|
||||||
s @ "blog" | s @ "author" | s @ "license" | s @ "tags" | s @ "lang" => {
|
s @ "blog" | s @ "author" | s @ "license" | s @ "tags" | s @ "lang" => {
|
||||||
match stream.get(1).ok_or(QueryError::UnexpectedEndOfQuery)? {
|
match stream.get(1)? {
|
||||||
Token::Word(_, _, r#in) if r#in == &"in" => {
|
Token::Word(_, _, r#in) if r#in == &"in" => {
|
||||||
let (mut left, list) = parse_l(&stream[2..])?;
|
let (mut left, list) = parse_l(&stream[2..])?;
|
||||||
let kind = match s {
|
let kind = match s {
|
||||||
@@ -496,12 +498,7 @@ fn parse_d<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>],
|
|||||||
if *clude != "include" && *clude != "exclude" {
|
if *clude != "include" && *clude != "exclude" {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
match (
|
match (*clude, left.get(1).map(Token::get_text)?) {
|
||||||
*clude,
|
|
||||||
left.get(1)
|
|
||||||
.map(Token::get_text)
|
|
||||||
.ok_or(QueryError::UnexpectedEndOfQuery)?,
|
|
||||||
) {
|
|
||||||
("include", "reshares") | ("include", "reshare") => {
|
("include", "reshares") | ("include", "reshare") => {
|
||||||
boosts = true
|
boosts = true
|
||||||
}
|
}
|
||||||
@@ -532,10 +529,7 @@ fn parse_d<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>],
|
|||||||
t => t.get_error(Token::Word(0, 0, "'in'")),
|
t => t.get_error(Token::Word(0, 0, "'in'")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s @ "title" | s @ "subtitle" | s @ "content" => match (
|
s @ "title" | s @ "subtitle" | s @ "content" => match (stream.get(1)?, stream.get(2)?) {
|
||||||
stream.get(1).ok_or(QueryError::UnexpectedEndOfQuery)?,
|
|
||||||
stream.get(2).ok_or(QueryError::UnexpectedEndOfQuery)?,
|
|
||||||
) {
|
|
||||||
(Token::Word(_, _, contains), Token::Word(_, _, w)) if contains == &"contains" => Ok((
|
(Token::Word(_, _, contains), Token::Word(_, _, w)) if contains == &"contains" => Ok((
|
||||||
&stream[3..],
|
&stream[3..],
|
||||||
Arg::Contains(
|
Arg::Contains(
|
||||||
@@ -561,13 +555,7 @@ fn parse_d<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>],
|
|||||||
if *clude != "include" && *clude != "exclude" {
|
if *clude != "include" && *clude != "exclude" {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
match (
|
match (*clude, stream.get(2).map(Token::get_text)?) {
|
||||||
*clude,
|
|
||||||
stream
|
|
||||||
.get(2)
|
|
||||||
.map(Token::get_text)
|
|
||||||
.ok_or(QueryError::UnexpectedEndOfQuery)?,
|
|
||||||
) {
|
|
||||||
("include", "reshares") | ("include", "reshare") => boosts = true,
|
("include", "reshares") | ("include", "reshare") => boosts = true,
|
||||||
("exclude", "reshares") | ("exclude", "reshare") => boosts = false,
|
("exclude", "reshares") | ("exclude", "reshare") => boosts = false,
|
||||||
("include", "likes") | ("include", "like") => likes = true,
|
("include", "likes") | ("include", "like") => likes = true,
|
||||||
@@ -589,23 +577,20 @@ fn parse_d<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>],
|
|||||||
"all" => Ok((&stream[1..], Arg::Boolean(Bool::All))),
|
"all" => Ok((&stream[1..], Arg::Boolean(Bool::All))),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
_ => stream
|
_ => stream.get(0)?.get_error(Token::Word(
|
||||||
.get(0)
|
0,
|
||||||
.ok_or(QueryError::UnexpectedEndOfQuery)?
|
0,
|
||||||
.get_error(Token::Word(
|
"one of 'blog', 'author', 'license', 'tags', 'lang', \
|
||||||
0,
|
|
||||||
0,
|
|
||||||
"one of 'blog', 'author', 'license', 'tags', 'lang', \
|
|
||||||
'title', 'subtitle', 'content', 'followed', 'has_cover', 'local' or 'all'",
|
'title', 'subtitle', 'content', 'followed', 'has_cover', 'local' or 'all'",
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_l<'a, 'b>(stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], List<'a>)> {
|
fn parse_l<'a, 'b>(stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], List<'a>)> {
|
||||||
match stream.get(0).ok_or(QueryError::UnexpectedEndOfQuery)? {
|
match stream.get(0)? {
|
||||||
Token::LBracket(_) => {
|
Token::LBracket(_) => {
|
||||||
let (left, list) = parse_m(&stream[1..])?;
|
let (left, list) = parse_m(&stream[1..])?;
|
||||||
match left.get(0).ok_or(QueryError::UnexpectedEndOfQuery)? {
|
match left.get(0)? {
|
||||||
Token::RBracket(_) => Ok((&left[1..], List::Array(list))),
|
Token::RBracket(_) => Ok((&left[1..], List::Array(list))),
|
||||||
t => t.get_error(Token::Word(0, 0, "one of ']' or ','")),
|
t => t.get_error(Token::Word(0, 0, "one of ']' or ','")),
|
||||||
}
|
}
|
||||||
@@ -616,20 +601,16 @@ fn parse_l<'a, 'b>(stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], Lis
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_m<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], Vec<&'a str>)> {
|
fn parse_m<'a, 'b>(mut stream: &'b [Token<'a>]) -> QueryResult<(&'b [Token<'a>], Vec<&'a str>)> {
|
||||||
let mut res: Vec<&str> = vec![
|
let mut res: Vec<&str> = vec![match stream.get(0)? {
|
||||||
match stream.get(0).ok_or(QueryError::UnexpectedEndOfQuery)? {
|
Token::Word(_, _, w) => w,
|
||||||
Token::Word(_, _, w) => w,
|
t => return t.get_error(Token::Word(0, 0, "any word")),
|
||||||
t => return t.get_error(Token::Word(0, 0, "any word")),
|
}];
|
||||||
},
|
|
||||||
];
|
|
||||||
stream = &stream[1..];
|
stream = &stream[1..];
|
||||||
while let Token::Comma(_) = stream[0] {
|
while let Token::Comma(_) = stream[0] {
|
||||||
res.push(
|
res.push(match stream.get(1)? {
|
||||||
match stream.get(1).ok_or(QueryError::UnexpectedEndOfQuery)? {
|
Token::Word(_, _, w) => w,
|
||||||
Token::Word(_, _, w) => w,
|
t => return t.get_error(Token::Word(0, 0, "any word")),
|
||||||
t => return t.get_error(Token::Word(0, 0, "any word")),
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
stream = &stream[2..];
|
stream = &stream[2..];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+79
-57
@@ -22,13 +22,17 @@ use openssl::{
|
|||||||
};
|
};
|
||||||
use plume_common::{
|
use plume_common::{
|
||||||
activity_pub::{
|
activity_pub::{
|
||||||
|
ap_accept_header,
|
||||||
inbox::{AsActor, AsObject, FromId},
|
inbox::{AsActor, AsObject, FromId},
|
||||||
request::get,
|
sign::{gen_keypair, Signer},
|
||||||
sign::{gen_keypair, Error as SignError, Result as SignResult, Signer},
|
|
||||||
ActivityStream, ApSignature, Id, IntoId, PublicKey, PUBLIC_VISIBILITY,
|
ActivityStream, ApSignature, Id, IntoId, PublicKey, PUBLIC_VISIBILITY,
|
||||||
},
|
},
|
||||||
utils,
|
utils,
|
||||||
};
|
};
|
||||||
|
use reqwest::{
|
||||||
|
header::{HeaderValue, ACCEPT},
|
||||||
|
ClientBuilder,
|
||||||
|
};
|
||||||
use riker::actors::{Publish, Tell};
|
use riker::actors::{Publish, Tell};
|
||||||
use rocket::{
|
use rocket::{
|
||||||
outcome::IntoOutcome,
|
outcome::IntoOutcome,
|
||||||
@@ -48,7 +52,6 @@ pub enum Role {
|
|||||||
Admin = 0,
|
Admin = 0,
|
||||||
Moderator = 1,
|
Moderator = 1,
|
||||||
Normal = 2,
|
Normal = 2,
|
||||||
Instance = 3,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Queryable, Identifiable, Clone, Debug, AsChangeset)]
|
#[derive(Queryable, Identifiable, Clone, Debug, AsChangeset)]
|
||||||
@@ -75,7 +78,6 @@ pub struct User {
|
|||||||
pub summary_html: SafeString,
|
pub summary_html: SafeString,
|
||||||
/// 0 = admin
|
/// 0 = admin
|
||||||
/// 1 = moderator
|
/// 1 = moderator
|
||||||
/// 3 = local instance
|
|
||||||
/// anything else = normal user
|
/// anything else = normal user
|
||||||
pub role: i32,
|
pub role: i32,
|
||||||
pub preferred_theme: Option<String>,
|
pub preferred_theme: Option<String>,
|
||||||
@@ -202,35 +204,13 @@ impl User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: Should create user record with normalized(lowercased) email
|
|
||||||
*/
|
|
||||||
pub fn email_used(conn: &DbConn, email: &str) -> Result<bool> {
|
|
||||||
use diesel::dsl::{exists, select};
|
|
||||||
|
|
||||||
select(exists(
|
|
||||||
users::table
|
|
||||||
.filter(users::instance_id.eq(Instance::get_local()?.id))
|
|
||||||
.filter(users::email.eq(email))
|
|
||||||
.or_filter(users::email.eq(email.to_ascii_lowercase())),
|
|
||||||
))
|
|
||||||
.get_result(&**conn)
|
|
||||||
.map_err(Error::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fetch_from_webfinger(conn: &DbConn, acct: &str) -> Result<User> {
|
fn fetch_from_webfinger(conn: &DbConn, acct: &str) -> Result<User> {
|
||||||
let link = resolve(acct.to_owned(), true)?
|
let link = resolve(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")))
|
||||||
.ok_or(Error::Webfinger)?;
|
.ok_or(Error::Webfinger)?;
|
||||||
User::from_id(
|
User::from_id(conn, link.href.as_ref()?, None, CONFIG.proxy()).map_err(|(_, e)| e)
|
||||||
conn,
|
|
||||||
link.href.as_ref().ok_or(Error::Webfinger)?,
|
|
||||||
None,
|
|
||||||
CONFIG.proxy(),
|
|
||||||
)
|
|
||||||
.map_err(|(_, e)| e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch_remote_interact_uri(acct: &str) -> Result<String> {
|
pub fn fetch_remote_interact_uri(acct: &str) -> Result<String> {
|
||||||
@@ -243,7 +223,20 @@ impl User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(url: &str) -> Result<CustomPerson> {
|
fn fetch(url: &str) -> Result<CustomPerson> {
|
||||||
let mut res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?;
|
let mut res = ClientBuilder::new()
|
||||||
|
.connect_timeout(Some(std::time::Duration::from_secs(5)))
|
||||||
|
.build()?
|
||||||
|
.get(url)
|
||||||
|
.header(
|
||||||
|
ACCEPT,
|
||||||
|
HeaderValue::from_str(
|
||||||
|
&ap_accept_header()
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", "),
|
||||||
|
)?,
|
||||||
|
)
|
||||||
|
.send()?;
|
||||||
let text = &res.text()?;
|
let text = &res.text()?;
|
||||||
// without this workaround, publicKey is not correctly deserialized
|
// without this workaround, publicKey is not correctly deserialized
|
||||||
let ap_sign = serde_json::from_str::<ApSignature>(text)?;
|
let ap_sign = serde_json::from_str::<ApSignature>(text)?;
|
||||||
@@ -262,10 +255,10 @@ impl User {
|
|||||||
conn,
|
conn,
|
||||||
json.object
|
json.object
|
||||||
.object_props
|
.object_props
|
||||||
.icon_image()? // FIXME: Fails when icon is not set
|
.icon_image()?
|
||||||
.object_props
|
.object_props
|
||||||
.url_string()?,
|
.url_string()?,
|
||||||
self,
|
&self,
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
@@ -434,12 +427,12 @@ impl User {
|
|||||||
let last = &format!(
|
let last = &format!(
|
||||||
"{}?page={}",
|
"{}?page={}",
|
||||||
&self.outbox_url,
|
&self.outbox_url,
|
||||||
self.get_activities_count(conn) / i64::from(ITEMS_PER_PAGE) + 1
|
self.get_activities_count(&conn) / i64::from(ITEMS_PER_PAGE) + 1
|
||||||
);
|
);
|
||||||
coll.collection_props.set_first_link(Id::new(first))?;
|
coll.collection_props.set_first_link(Id::new(first))?;
|
||||||
coll.collection_props.set_last_link(Id::new(last))?;
|
coll.collection_props.set_last_link(Id::new(last))?;
|
||||||
coll.collection_props
|
coll.collection_props
|
||||||
.set_total_items_u64(self.get_activities_count(conn) as u64)?;
|
.set_total_items_u64(self.get_activities_count(&conn) as u64)?;
|
||||||
Ok(ActivityStream::new(coll))
|
Ok(ActivityStream::new(coll))
|
||||||
}
|
}
|
||||||
pub fn outbox_page(
|
pub fn outbox_page(
|
||||||
@@ -448,7 +441,7 @@ impl User {
|
|||||||
(min, max): (i32, i32),
|
(min, max): (i32, i32),
|
||||||
) -> Result<ActivityStream<OrderedCollectionPage>> {
|
) -> Result<ActivityStream<OrderedCollectionPage>> {
|
||||||
let acts = self.get_activities_page(conn, (min, max))?;
|
let acts = self.get_activities_page(conn, (min, max))?;
|
||||||
let n_acts = self.get_activities_count(conn);
|
let n_acts = self.get_activities_count(&conn);
|
||||||
let mut coll = OrderedCollectionPage::default();
|
let mut coll = OrderedCollectionPage::default();
|
||||||
if n_acts - i64::from(min) >= i64::from(ITEMS_PER_PAGE) {
|
if n_acts - i64::from(min) >= i64::from(ITEMS_PER_PAGE) {
|
||||||
coll.collection_page_props.set_next_link(Id::new(&format!(
|
coll.collection_page_props.set_next_link(Id::new(&format!(
|
||||||
@@ -470,7 +463,20 @@ impl User {
|
|||||||
Ok(ActivityStream::new(coll))
|
Ok(ActivityStream::new(coll))
|
||||||
}
|
}
|
||||||
fn fetch_outbox_page<T: Activity>(&self, url: &str) -> Result<(Vec<T>, Option<String>)> {
|
fn fetch_outbox_page<T: Activity>(&self, url: &str) -> Result<(Vec<T>, Option<String>)> {
|
||||||
let mut res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?;
|
let mut res = ClientBuilder::new()
|
||||||
|
.connect_timeout(Some(std::time::Duration::from_secs(5)))
|
||||||
|
.build()?
|
||||||
|
.get(url)
|
||||||
|
.header(
|
||||||
|
ACCEPT,
|
||||||
|
HeaderValue::from_str(
|
||||||
|
&ap_accept_header()
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", "),
|
||||||
|
)?,
|
||||||
|
)
|
||||||
|
.send()?;
|
||||||
let text = &res.text()?;
|
let text = &res.text()?;
|
||||||
let json: serde_json::Value = serde_json::from_str(text)?;
|
let json: serde_json::Value = serde_json::from_str(text)?;
|
||||||
let items = json["items"]
|
let items = json["items"]
|
||||||
@@ -484,11 +490,20 @@ impl User {
|
|||||||
Ok((items, next))
|
Ok((items, next))
|
||||||
}
|
}
|
||||||
pub fn fetch_outbox<T: Activity>(&self) -> Result<Vec<T>> {
|
pub fn fetch_outbox<T: Activity>(&self) -> Result<Vec<T>> {
|
||||||
let mut res = get(
|
let mut res = ClientBuilder::new()
|
||||||
&self.outbox_url[..],
|
.connect_timeout(Some(std::time::Duration::from_secs(5)))
|
||||||
Self::get_sender(),
|
.build()?
|
||||||
CONFIG.proxy().cloned(),
|
.get(&self.outbox_url[..])
|
||||||
)?;
|
.header(
|
||||||
|
ACCEPT,
|
||||||
|
HeaderValue::from_str(
|
||||||
|
&ap_accept_header()
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", "),
|
||||||
|
)?,
|
||||||
|
)
|
||||||
|
.send()?;
|
||||||
let text = &res.text()?;
|
let text = &res.text()?;
|
||||||
let json: serde_json::Value = serde_json::from_str(text)?;
|
let json: serde_json::Value = serde_json::from_str(text)?;
|
||||||
if let Some(first) = json.get("first") {
|
if let Some(first) = json.get("first") {
|
||||||
@@ -498,7 +513,7 @@ impl User {
|
|||||||
if page.is_empty() {
|
if page.is_empty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
items.append(&mut page);
|
items.extend(page.drain(..));
|
||||||
if let Some(n) = nxt {
|
if let Some(n) = nxt {
|
||||||
if n == next {
|
if n == next {
|
||||||
break;
|
break;
|
||||||
@@ -520,11 +535,20 @@ impl User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch_followers_ids(&self) -> Result<Vec<String>> {
|
pub fn fetch_followers_ids(&self) -> Result<Vec<String>> {
|
||||||
let mut res = get(
|
let mut res = ClientBuilder::new()
|
||||||
&self.followers_endpoint[..],
|
.connect_timeout(Some(std::time::Duration::from_secs(5)))
|
||||||
Self::get_sender(),
|
.build()?
|
||||||
CONFIG.proxy().cloned(),
|
.get(&self.followers_endpoint[..])
|
||||||
)?;
|
.header(
|
||||||
|
ACCEPT,
|
||||||
|
HeaderValue::from_str(
|
||||||
|
&ap_accept_header()
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", "),
|
||||||
|
)?,
|
||||||
|
)
|
||||||
|
.send()?;
|
||||||
let text = &res.text()?;
|
let text = &res.text()?;
|
||||||
let json: serde_json::Value = serde_json::from_str(text)?;
|
let json: serde_json::Value = serde_json::from_str(text)?;
|
||||||
Ok(json["items"]
|
Ok(json["items"]
|
||||||
@@ -696,7 +720,7 @@ impl User {
|
|||||||
|
|
||||||
pub fn get_keypair(&self) -> Result<PKey<Private>> {
|
pub fn get_keypair(&self) -> Result<PKey<Private>> {
|
||||||
PKey::from_rsa(Rsa::private_key_from_pem(
|
PKey::from_rsa(Rsa::private_key_from_pem(
|
||||||
self.private_key.clone().ok_or(Error::Signature)?.as_ref(),
|
self.private_key.clone()?.as_ref(),
|
||||||
)?)
|
)?)
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
}
|
}
|
||||||
@@ -919,7 +943,7 @@ impl FromId<DbConn> for User {
|
|||||||
|
|
||||||
fn from_activity(conn: &DbConn, acct: CustomPerson) -> Result<Self> {
|
fn from_activity(conn: &DbConn, acct: CustomPerson) -> Result<Self> {
|
||||||
let url = Url::parse(&acct.object.object_props.id_string()?)?;
|
let url = Url::parse(&acct.object.object_props.id_string()?)?;
|
||||||
let inst = url.host_str().ok_or(Error::Url)?;
|
let inst = url.host_str()?;
|
||||||
let instance = Instance::find_by_domain(conn, inst).or_else(|_| {
|
let instance = Instance::find_by_domain(conn, inst).or_else(|_| {
|
||||||
Instance::insert(
|
Instance::insert(
|
||||||
conn,
|
conn,
|
||||||
@@ -1007,10 +1031,6 @@ impl FromId<DbConn> for User {
|
|||||||
|
|
||||||
Ok(user)
|
Ok(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sender() -> &'static dyn Signer {
|
|
||||||
Instance::get_local_instance_user().expect("Failed to local instance user")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsActor<&DbConn> for User {
|
impl AsActor<&DbConn> for User {
|
||||||
@@ -1043,22 +1063,24 @@ impl AsObject<User, Delete, &DbConn> for User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Signer for User {
|
impl Signer for User {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
fn get_key_id(&self) -> String {
|
fn get_key_id(&self) -> String {
|
||||||
format!("{}#main-key", self.ap_url)
|
format!("{}#main-key", self.ap_url)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign(&self, to_sign: &str) -> SignResult<Vec<u8>> {
|
fn sign(&self, to_sign: &str) -> Result<Vec<u8>> {
|
||||||
let key = self.get_keypair().map_err(|_| SignError())?;
|
let key = self.get_keypair()?;
|
||||||
let mut signer = sign::Signer::new(MessageDigest::sha256(), &key)?;
|
let mut signer = sign::Signer::new(MessageDigest::sha256(), &key)?;
|
||||||
signer.update(to_sign.as_bytes())?;
|
signer.update(to_sign.as_bytes())?;
|
||||||
signer.sign_to_vec().map_err(SignError::from)
|
signer.sign_to_vec().map_err(Error::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify(&self, data: &str, signature: &[u8]) -> SignResult<bool> {
|
fn verify(&self, data: &str, signature: &[u8]) -> Result<bool> {
|
||||||
let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref())?)?;
|
let key = PKey::from_rsa(Rsa::public_key_from_pem(self.public_key.as_ref())?)?;
|
||||||
let mut verifier = sign::Verifier::new(MessageDigest::sha256(), &key)?;
|
let mut verifier = sign::Verifier::new(MessageDigest::sha256(), &key)?;
|
||||||
verifier.update(data.as_bytes())?;
|
verifier.update(data.as_bytes())?;
|
||||||
verifier.verify(signature).map_err(SignError::from)
|
verifier.verify(&signature).map_err(Error::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1099,7 +1121,7 @@ impl NewUser {
|
|||||||
display_name,
|
display_name,
|
||||||
role: role as i32,
|
role: role as i32,
|
||||||
summary: summary.to_owned(),
|
summary: summary.to_owned(),
|
||||||
summary_html: SafeString::new(&utils::md_to_html(summary, None, false, None).0),
|
summary_html: SafeString::new(&utils::md_to_html(&summary, None, false, None).0),
|
||||||
email: Some(email),
|
email: Some(email),
|
||||||
hashed_password: password,
|
hashed_password: password,
|
||||||
instance_id: instance.id,
|
instance_id: instance.id,
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Afrikaans\n"
|
"Language-Team: Afrikaans\n"
|
||||||
"Language: af_ZA\n"
|
"Language: af_ZA\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Arabic\n"
|
"Language-Team: Arabic\n"
|
||||||
"Language: ar_SA\n"
|
"Language: ar_SA\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "فتح محرر النصوص الغني"
|
msgstr "فتح محرر النصوص الغني"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "العنوان"
|
msgstr "العنوان"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "العنوان الثانوي أو الملخص"
|
msgstr "العنوان الثانوي أو الملخص"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "اكتب مقالك هنا. ماركداون مُدَعَّم."
|
msgstr "اكتب مقالك هنا. ماركداون مُدَعَّم."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "يتبقا {} حرفا تقريبا"
|
msgstr "يتبقا {} حرفا تقريبا"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "الوسوم"
|
msgstr "الوسوم"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "الرخصة"
|
msgstr "الرخصة"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "الغلاف"
|
msgstr "الغلاف"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "هذه مسودة"
|
msgstr "هذه مسودة"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "نشر كتابا"
|
msgstr "نشر كتابا"
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Bulgarian\n"
|
"Language-Team: Bulgarian\n"
|
||||||
"Language: bg_BG\n"
|
"Language: bg_BG\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr "Искате ли да активирате локално автоматично запаметяване, последно редактирано в {}?"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Отворете редактора с богат текст"
|
msgstr "Отворете редактора с богат текст"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Заглавие"
|
msgstr "Заглавие"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Подзаглавие или резюме"
|
msgstr "Подзаглавие или резюме"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Напишете статията си тук. Поддържа се Markdown."
|
msgstr "Напишете статията си тук. Поддържа се Markdown."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Остават {} знака вляво"
|
msgstr "Остават {} знака вляво"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Етикети"
|
msgstr "Етикети"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Лиценз"
|
msgstr "Лиценз"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Основно изображение"
|
msgstr "Основно изображение"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Това е проект"
|
msgstr "Това е проект"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Публикувай"
|
msgstr "Публикувай"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Catalan\n"
|
"Language-Team: Catalan\n"
|
||||||
"Language: ca_ES\n"
|
"Language: ca_ES\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Obre l’editor de text enriquit"
|
msgstr "Obre l’editor de text enriquit"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Títol"
|
msgstr "Títol"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Subtítol o resum"
|
msgstr "Subtítol o resum"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Escriviu el vostre article ací. Podeu fer servir el Markdown."
|
msgstr "Escriviu el vostre article ací. Podeu fer servir el Markdown."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Queden uns {} caràcters"
|
msgstr "Queden uns {} caràcters"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Etiquetes"
|
msgstr "Etiquetes"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Llicència"
|
msgstr "Llicència"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Coberta"
|
msgstr "Coberta"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Açò és un esborrany"
|
msgstr "Açò és un esborrany"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Publica"
|
msgstr "Publica"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Czech\n"
|
"Language-Team: Czech\n"
|
||||||
"Language: cs_CZ\n"
|
"Language: cs_CZ\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Otevřít editor formátovaného textu"
|
msgstr "Otevřít editor formátovaného textu"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Nadpis"
|
msgstr "Nadpis"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Podnadpis, nebo shrnutí"
|
msgstr "Podnadpis, nebo shrnutí"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Sem napište svůj článek. Markdown je podporován."
|
msgstr "Sem napište svůj článek. Markdown je podporován."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Zbývá kolem {} znaků"
|
msgstr "Zbývá kolem {} znaků"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Tagy"
|
msgstr "Tagy"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Licence"
|
msgstr "Licence"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Titulka"
|
msgstr "Titulka"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Tohle je koncept"
|
msgstr "Tohle je koncept"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Zveřejnit"
|
msgstr "Zveřejnit"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Danish\n"
|
"Language-Team: Danish\n"
|
||||||
"Language: da_DK\n"
|
"Language: da_DK\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: German\n"
|
"Language-Team: German\n"
|
||||||
"Language: de_DE\n"
|
"Language: de_DE\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr "Möchten Sie die lokale automatische Speicherung laden, die zuletzt um {} bearbeitet wurde?"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr " Rich Text Editor (RTE) öffnen"
|
msgstr " Rich Text Editor (RTE) öffnen"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Titel"
|
msgstr "Titel"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Untertitel oder Zusammenfassung"
|
msgstr "Untertitel oder Zusammenfassung"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Schreiben deinen Artikel hier. Markdown wird unterstützt."
|
msgstr "Schreiben deinen Artikel hier. Markdown wird unterstützt."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Ungefähr {} Zeichen übrig"
|
msgstr "Ungefähr {} Zeichen übrig"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Schlagwörter"
|
msgstr "Schlagwörter"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Lizenz"
|
msgstr "Lizenz"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Einband"
|
msgstr "Einband"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Dies ist ein Entwurf"
|
msgstr "Dies ist ein Entwurf"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Veröffentlichen"
|
msgstr "Veröffentlichen"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Greek\n"
|
"Language-Team: Greek\n"
|
||||||
"Language: el_GR\n"
|
"Language: el_GR\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: English\n"
|
"Language-Team: English\n"
|
||||||
"Language: en_US\n"
|
"Language: en_US\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Esperanto\n"
|
"Language-Team: Esperanto\n"
|
||||||
"Language: eo_UY\n"
|
"Language: eo_UY\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Malfermi la riĉan redaktilon"
|
msgstr "Malfermi la riĉan redaktilon"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Titolo"
|
msgstr "Titolo"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Verku vian artikolon ĉi tie. Markdown estas subtenita."
|
msgstr "Verku vian artikolon ĉi tie. Markdown estas subtenita."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Proksimume {} signoj restantaj"
|
msgstr "Proksimume {} signoj restantaj"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Etikedoj"
|
msgstr "Etikedoj"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Permesilo"
|
msgstr "Permesilo"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Kovro"
|
msgstr "Kovro"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Malfinias"
|
msgstr "Malfinias"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Eldoni"
|
msgstr "Eldoni"
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Spanish\n"
|
"Language-Team: Spanish\n"
|
||||||
"Language: es_ES\n"
|
"Language: es_ES\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr "¿Quieres cargar el guardado automático local editado por última vez en {}?"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Abrir el editor de texto enriquecido"
|
msgstr "Abrir el editor de texto enriquecido"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Título"
|
msgstr "Título"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Subtítulo, o resumen"
|
msgstr "Subtítulo, o resumen"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Escriba su artículo aquí. Puede utilizar Markdown."
|
msgstr "Escriba su artículo aquí. Puede utilizar Markdown."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Quedan unos {} caracteres"
|
msgstr "Quedan unos {} caracteres"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Etiquetas"
|
msgstr "Etiquetas"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Licencia"
|
msgstr "Licencia"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Cubierta"
|
msgstr "Cubierta"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Esto es un borrador"
|
msgstr "Esto es un borrador"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Publicar"
|
msgstr "Publicar"
|
||||||
|
|
||||||
|
|||||||
+15
-15
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Persian\n"
|
"Language-Team: Persian\n"
|
||||||
"Language: fa_IR\n"
|
"Language: fa_IR\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr "آیا میخواهید نسخهٔ ذخیره شدهٔ خودکار محلّی از آخرین ویرایش در {} را بار کنید؟"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "باز کردن ویرایشگر غنی"
|
msgstr "باز کردن ویرایشگر غنی"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "عنوان"
|
msgstr "عنوان"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "زیرعنوان، یا چکیده"
|
msgstr "زیرعنوان، یا چکیده"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "مقالهتان را اینجا بنویسید. از مارکداون پشتیبانی میشود."
|
msgstr "مقاله را اینجا بنویسید. از مارکداون پشتیبانی میشود."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "نزدیک به {} حرف باقی مانده است"
|
msgstr "نزدیک به {} حرف باقی مانده است"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "برچسبها"
|
msgstr "برچسبها"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "پروانه"
|
msgstr "پروانه"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "جلد"
|
msgstr "تصویر شاخص"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "این، یک پیشنویس است"
|
msgstr "این، یک پیشنویس است"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "انتشار"
|
msgstr "انتشار"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Finnish\n"
|
"Language-Team: Finnish\n"
|
||||||
"Language: fi_FI\n"
|
"Language: fi_FI\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Avaa edistynyt tekstieditori"
|
msgstr "Avaa edistynyt tekstieditori"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Otsikko"
|
msgstr "Otsikko"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Alaotsikko tai tiivistelmä"
|
msgstr "Alaotsikko tai tiivistelmä"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Kirjoita artikkelisi tähän. Markdown -kuvauskieli on tuettu."
|
msgstr "Kirjoita artikkelisi tähän. Markdown -kuvauskieli on tuettu."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "%{count} merkkiä jäljellä"
|
msgstr "%{count} merkkiä jäljellä"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Tagit"
|
msgstr "Tagit"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Lisenssi"
|
msgstr "Lisenssi"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Kansi"
|
msgstr "Kansi"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Tämä on luonnos"
|
msgstr "Tämä on luonnos"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Julkaise"
|
msgstr "Julkaise"
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: French\n"
|
"Language-Team: French\n"
|
||||||
"Language: fr_FR\n"
|
"Language: fr_FR\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr "Voulez vous charger la sauvegarde automatique locale, éditée la dernière fois à {}?"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Ouvrir l'éditeur de texte avancé"
|
msgstr "Ouvrir l'éditeur de texte avancé"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Titre"
|
msgstr "Titre"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Sous-titre ou résumé"
|
msgstr "Sous-titre ou résumé"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Écrivez votre article ici. Vous pouvez utiliser du Markdown."
|
msgstr "Écrivez votre article ici. Vous pouvez utiliser du Markdown."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Environ {} caractères restant"
|
msgstr "Environ {} caractères restant"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Étiquettes"
|
msgstr "Étiquettes"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Licence"
|
msgstr "Licence"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Illustration"
|
msgstr "Illustration"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Ceci est un brouillon"
|
msgstr "Ceci est un brouillon"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Publier"
|
msgstr "Publier"
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Galician\n"
|
"Language-Team: Galician\n"
|
||||||
"Language: gl_ES\n"
|
"Language: gl_ES\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr "Queres cargar a última copia gardada editada o {}?"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Abre o editor de texto enriquecido"
|
msgstr "Abre o editor de texto enriquecido"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Título"
|
msgstr "Título"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Subtítulo, ou resumo"
|
msgstr "Subtítulo, ou resumo"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Escribe aquí o teu artigo: podes utilizar Markdown."
|
msgstr "Escribe aquí o teu artigo: podes utilizar Markdown."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Dispós de {} caracteres"
|
msgstr "Dispós de {} caracteres"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Etiquetas"
|
msgstr "Etiquetas"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Licenza"
|
msgstr "Licenza"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Portada"
|
msgstr "Portada"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Este é un borrador"
|
msgstr "Este é un borrador"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Publicar"
|
msgstr "Publicar"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Hebrew\n"
|
"Language-Team: Hebrew\n"
|
||||||
"Language: he_IL\n"
|
"Language: he_IL\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Hindi\n"
|
"Language-Team: Hindi\n"
|
||||||
"Language: hi_IN\n"
|
"Language: hi_IN\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "शीर्षक"
|
msgstr "शीर्षक"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "अपना आर्टिकल या लेख यहाँ लिखें. Markdown उपलब्ध है."
|
msgstr "अपना आर्टिकल या लेख यहाँ लिखें. Markdown उपलब्ध है."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "लगभग {} अक्षर बाकी हैं"
|
msgstr "लगभग {} अक्षर बाकी हैं"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "टैग्स"
|
msgstr "टैग्स"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "लाइसेंस"
|
msgstr "लाइसेंस"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "पब्लिश करें"
|
msgstr "पब्लिश करें"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Croatian\n"
|
"Language-Team: Croatian\n"
|
||||||
"Language: hr_HR\n"
|
"Language: hr_HR\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Naslov"
|
msgstr "Naslov"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Tagovi"
|
msgstr "Tagovi"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Licenca"
|
msgstr "Licenca"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Objavi"
|
msgstr "Objavi"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Hungarian\n"
|
"Language-Team: Hungarian\n"
|
||||||
"Language: hu_HU\n"
|
"Language: hu_HU\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Italian\n"
|
"Language-Team: Italian\n"
|
||||||
"Language: it_IT\n"
|
"Language: it_IT\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Apri il compositore di testo avanzato"
|
msgstr "Apri il compositore di testo avanzato"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Titolo"
|
msgstr "Titolo"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Sottotitolo, o sommario"
|
msgstr "Sottotitolo, o sommario"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Scrivi qui il tuo articolo. È supportato il Markdown."
|
msgstr "Scrivi qui il tuo articolo. È supportato il Markdown."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Circa {} caratteri rimasti"
|
msgstr "Circa {} caratteri rimasti"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Etichette"
|
msgstr "Etichette"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Licenza"
|
msgstr "Licenza"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Copertina"
|
msgstr "Copertina"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Questa è una bozza"
|
msgstr "Questa è una bozza"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Pubblica"
|
msgstr "Pubblica"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Japanese\n"
|
"Language-Team: Japanese\n"
|
||||||
"Language: ja_JP\n"
|
"Language: ja_JP\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "リッチテキストエディターを開く"
|
msgstr "リッチテキストエディターを開く"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "タイトル"
|
msgstr "タイトル"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "サブタイトル、または概要"
|
msgstr "サブタイトル、または概要"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "投稿をここに書きます。Markdown がサポートされています。"
|
msgstr "投稿をここに書きます。Markdown がサポートされています。"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "残り約 {} 文字"
|
msgstr "残り約 {} 文字"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "タグ"
|
msgstr "タグ"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "ライセンス"
|
msgstr "ライセンス"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "カバー"
|
msgstr "カバー"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "これは下書きです"
|
msgstr "これは下書きです"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "公開"
|
msgstr "公開"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Korean\n"
|
"Language-Team: Korean\n"
|
||||||
"Language: ko_KR\n"
|
"Language: ko_KR\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Dutch\n"
|
"Language-Team: Dutch\n"
|
||||||
"Language: nl_NL\n"
|
"Language: nl_NL\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr "Wilt u de lokale auto-opslaan laden? Laatst bewerkt om: {}"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Open de rich-text editor"
|
msgstr "Open de rich-text editor"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Titel"
|
msgstr "Titel"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Ondertitel of samenvatting"
|
msgstr "Ondertitel of samenvatting"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Schrijf hier je artikel. Markdown wordt ondersteund."
|
msgstr "Schrijf hier je artikel. Markdown wordt ondersteund."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Ongeveer {} tekens over"
|
msgstr "Ongeveer {} tekens over"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Tags"
|
msgstr "Tags"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Licentie"
|
msgstr "Licentie"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Hoofdafbeelding"
|
msgstr "Hoofdafbeelding"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Dit is een concept"
|
msgstr "Dit is een concept"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Publiceren"
|
msgstr "Publiceren"
|
||||||
|
|
||||||
|
|||||||
+14
-14
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Norwegian\n"
|
"Language-Team: Norwegian\n"
|
||||||
"Language: no_NO\n"
|
"Language: no_NO\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Lisens"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Publiser"
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Polish\n"
|
"Language-Team: Polish\n"
|
||||||
"Language: pl_PL\n"
|
"Language: pl_PL\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Otwórz edytor tekstu sformatowanego"
|
msgstr "Otwórz edytor tekstu sformatowanego"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Tytuł"
|
msgstr "Tytuł"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Podtytuł, lub podsumowanie"
|
msgstr "Podtytuł, lub podsumowanie"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Napisz swój artykuł tutaj. Markdown jest obsługiwany."
|
msgstr "Napisz swój artykuł tutaj. Markdown jest obsługiwany."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Pozostało w okolicy {} znaków"
|
msgstr "Pozostało w okolicy {} znaków"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Tagi"
|
msgstr "Tagi"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Licencja"
|
msgstr "Licencja"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Okładka"
|
msgstr "Okładka"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "To jest szkic"
|
msgstr "To jest szkic"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Publikuj"
|
msgstr "Publikuj"
|
||||||
|
|
||||||
|
|||||||
@@ -12,46 +12,46 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
|
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:188
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:281
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:314
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:318
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:325
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:336
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:413
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:414
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:417
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:437
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:444
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
+13
-13
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Portuguese, Brazilian\n"
|
"Language-Team: Portuguese, Brazilian\n"
|
||||||
"Language: pt_BR\n"
|
"Language: pt_BR\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr "Você quer carregar o último conteúdo salvo localmente editado em {}?"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Abrir o editor de rich text"
|
msgstr "Abrir o editor de rich text"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Título"
|
msgstr "Título"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Subtítulo ou resumo"
|
msgstr "Subtítulo ou resumo"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Escreva seu artigo aqui. Markdown é suportado."
|
msgstr "Escreva seu artigo aqui. Markdown é suportado."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Cerca de {} caracteres restantes"
|
msgstr "Cerca de {} caracteres restantes"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Tags"
|
msgstr "Tags"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Licença"
|
msgstr "Licença"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Capa"
|
msgstr "Capa"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Isso é um rascunho"
|
msgstr "Isso é um rascunho"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Publicar"
|
msgstr "Publicar"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Romanian\n"
|
"Language-Team: Romanian\n"
|
||||||
"Language: ro_RO\n"
|
"Language: ro_RO\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Deschide editorul de text"
|
msgstr "Deschide editorul de text"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Titlu"
|
msgstr "Titlu"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Scrie articolul tău aici. Markdown este acceptat."
|
msgstr "Scrie articolul tău aici. Markdown este acceptat."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "În apropiere de {} caractere rămase"
|
msgstr "În apropiere de {} caractere rămase"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Etichete"
|
msgstr "Etichete"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Licenţă"
|
msgstr "Licenţă"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Coperta"
|
msgstr "Coperta"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Aceasta este o ciornă"
|
msgstr "Aceasta este o ciornă"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Publică"
|
msgstr "Publică"
|
||||||
|
|
||||||
|
|||||||
+16
-16
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Russian\n"
|
"Language-Team: Russian\n"
|
||||||
"Language: ru_RU\n"
|
"Language: ru_RU\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr "Хотите ли вы загрузить локальное автосохранение, сделанное в {}?"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Открыть в визуальном редакторе"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Заголовок"
|
msgstr "Заголовок"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Подзаголовок или резюме"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Пишите свою статью здесь. Markdown поддерживается."
|
msgstr "Пишите свою статью здесь. Markdown поддерживается."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Осталось около {} символов"
|
msgstr "Осталось около {} символов"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Теги"
|
msgstr "Теги"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Лицензия"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Обложка"
|
msgstr "Обложка"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Это черновик"
|
msgstr "Это черновик"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Опубликовать"
|
msgstr "Опубликовать"
|
||||||
|
|
||||||
|
|||||||
+18
-18
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Santali\n"
|
"Language-Team: Santali\n"
|
||||||
"Language: sat_IN\n"
|
"Language: sat_IN\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr "ᱟᱢ ᱪᱮᱫ ᱢᱟᱲᱟᱝ {} ᱨᱮ ᱥᱟᱯᱲᱟᱣ ᱟᱠᱟᱱ ᱞᱚᱠᱟᱞ ᱚᱴᱚᱥᱮᱣ ᱞᱟᱫᱮ ᱥᱟᱱᱟᱢ ᱠᱟᱱᱟ ᱥᱮ?"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "ᱨᱤᱪ ᱚᱞ ᱥᱟᱯᱟᱣᱤᱡ ᱠᱷᱩᱞᱟᱹᱭ ᱢᱮ"
|
msgstr "ᱨᱤᱪ ᱚᱞ ᱥᱟᱯᱟᱣᱤᱡ ᱠᱷᱩᱞᱟᱹᱭ ᱢᱮᱸ"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "ᱴᱭᱴᱚᱞ"
|
msgstr "ᱴᱭᱴᱚᱞ"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "ᱥᱟᱹᱵᱴᱟᱭᱴᱟᱹᱞ, ᱟᱨ ᱵᱟᱝ ᱥᱟᱹᱢᱢᱟᱨᱭ"
|
msgstr "ᱥᱟᱹᱵᱴᱟᱭᱴᱟᱹᱞ, ᱟᱨ ᱵᱟᱝ ᱥᱟᱹᱢᱢᱟᱨᱭ"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "ᱟᱢᱟᱜ ᱚᱱᱚᱞ ᱱᱚᱰᱮ ᱚᱞ ᱛᱟᱢ ᱾ ᱪᱤᱱᱦᱟᱹ ᱥᱟᱯᱚᱴ ᱜᱮᱭᱟ ᱾"
|
msgstr "ᱟᱢᱟᱜ ᱚᱱᱚᱞ ᱱᱚᱰᱮ ᱚᱞ ᱛᱟᱢ ᱾ ᱪᱤᱱᱦᱟᱹ ᱥᱟᱯᱯᱚᱴ ᱜᱮᱭᱟ ᱾"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "ᱡᱷᱚᱛᱚ ᱨᱮ {} ᱡᱤᱱᱤᱥ ᱵᱟᱧᱪᱟᱣᱠᱟᱱᱟ"
|
msgstr "ᱡᱷᱚᱛᱚ ᱨᱮ {} ᱡᱤᱱᱤᱥ ᱵᱟᱧᱪᱟᱣᱠᱟᱱᱟ"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "ᱴᱮᱜᱽᱥ"
|
msgstr "ᱴᱮᱜᱥ"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "ᱞᱚᱭᱥᱮᱱᱥ"
|
msgstr "ᱞᱚᱭᱥᱮᱸᱱᱥ"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "ᱢᱚᱭᱞᱟᱹᱴ"
|
msgstr "ᱠᱚᱵᱷᱚᱨ"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "ᱱᱚᱶᱟ ᱫᱚ ᱰᱨᱟᱯᱷᱼᱴ ᱠᱟᱱᱟ"
|
msgstr "ᱱᱚᱶᱟ ᱫᱚ ᱰᱨᱟᱯᱷᱼᱴ ᱠᱟᱱᱟ"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "ᱯᱟᱨᱥᱟᱞ"
|
msgstr "ᱯᱟᱨᱥᱟᱞ"
|
||||||
|
|
||||||
|
|||||||
+14
-14
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Sinhala\n"
|
"Language-Team: Sinhala\n"
|
||||||
"Language: si_LK\n"
|
"Language: si_LK\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "මාතෘකාව"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "බලපත්රය"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Slovak\n"
|
"Language-Team: Slovak\n"
|
||||||
"Language: sk_SK\n"
|
"Language: sk_SK\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr "Chceš načítať automaticky uloženú zálohu, s poslednou úpravou {}?"
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Otvor editor formátovaného textu"
|
msgstr "Otvor editor formátovaného textu"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Nadpis"
|
msgstr "Nadpis"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Zhrnutie, alebo podnadpis"
|
msgstr "Zhrnutie, alebo podnadpis"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Tu napíš svoj článok. Markdown je podporovaný."
|
msgstr "Tu napíš svoj článok. Markdown je podporovaný."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Zostáva asi {} znakov"
|
msgstr "Zostáva asi {} znakov"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Štítky"
|
msgstr "Štítky"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Licencia"
|
msgstr "Licencia"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Obálka"
|
msgstr "Obálka"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Toto je koncept"
|
msgstr "Toto je koncept"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Zverejniť"
|
msgstr "Zverejniť"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Slovenian\n"
|
"Language-Team: Slovenian\n"
|
||||||
"Language: sl_SI\n"
|
"Language: sl_SI\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Naslov"
|
msgstr "Naslov"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Oznake"
|
msgstr "Oznake"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Licenca"
|
msgstr "Licenca"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "To je osnutek"
|
msgstr "To je osnutek"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Objavi"
|
msgstr "Objavi"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Serbian (Latin)\n"
|
"Language-Team: Serbian (Latin)\n"
|
||||||
"Language: sr_CS\n"
|
"Language: sr_CS\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Otvori uređivač sa stilizacijom"
|
msgstr "Otvori uređivač sa stilizacijom"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Naslov"
|
msgstr "Naslov"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Podnaslov, ili sažetak"
|
msgstr "Podnaslov, ili sažetak"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Napišite vaš članak ovde. Na raspolaganju vam je Markdown."
|
msgstr "Napišite vaš članak ovde. Na raspolaganju vam je Markdown."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Preostalo oko {} znakova"
|
msgstr "Preostalo oko {} znakova"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Markeri"
|
msgstr "Markeri"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Licenca"
|
msgstr "Licenca"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Naslovna strana"
|
msgstr "Naslovna strana"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Ovo je nacrt"
|
msgstr "Ovo je nacrt"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Objavi"
|
msgstr "Objavi"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Swedish\n"
|
"Language-Team: Swedish\n"
|
||||||
"Language: sv_SE\n"
|
"Language: sv_SE\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Titel"
|
msgstr "Titel"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Skriv din artikel här. Markdown stöds."
|
msgstr "Skriv din artikel här. Markdown stöds."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Ungefär {} karaktärer kvar"
|
msgstr "Ungefär {} karaktärer kvar"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Taggar"
|
msgstr "Taggar"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Licens"
|
msgstr "Licens"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Omslag"
|
msgstr "Omslag"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Publicera"
|
msgstr "Publicera"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Turkish\n"
|
"Language-Team: Turkish\n"
|
||||||
"Language: tr_TR\n"
|
"Language: tr_TR\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Zengin metin editörünü (RTE) aç"
|
msgstr "Zengin metin editörünü (RTE) aç"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Başlık"
|
msgstr "Başlık"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr "Alt başlık, veya açıklama"
|
msgstr "Alt başlık, veya açıklama"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "Makaleni buraya yaz. Markdown kullanabilirsin."
|
msgstr "Makaleni buraya yaz. Markdown kullanabilirsin."
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "Yaklaşık {} karakter kaldı"
|
msgstr "Yaklaşık {} karakter kaldı"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "Etiketler"
|
msgstr "Etiketler"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "Lisans"
|
msgstr "Lisans"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "Kapak"
|
msgstr "Kapak"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "Bu bir taslaktır"
|
msgstr "Bu bir taslaktır"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "Yayınla"
|
msgstr "Yayınla"
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Ukrainian\n"
|
"Language-Team: Ukrainian\n"
|
||||||
"Language: uk_UA\n"
|
"Language: uk_UA\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
"Project-Id-Version: plume\n"
|
|
||||||
"Report-Msgid-Bugs-To: \n"
|
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
|
||||||
"Last-Translator: \n"
|
|
||||||
"Language-Team: Urdu (Pakistan)\n"
|
|
||||||
"Language: ur_PK\n"
|
|
||||||
"MIME-Version: 1.0\n"
|
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
|
||||||
"X-Crowdin-Project: plume\n"
|
|
||||||
"X-Crowdin-Project-ID: 352097\n"
|
|
||||||
"X-Crowdin-Language: ur-PK\n"
|
|
||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
|
||||||
"X-Crowdin-File-ID: 12\n"
|
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
|
||||||
msgid "Open the rich text editor"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
|
||||||
msgid "Title"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
|
||||||
msgid "Subtitle, or summary"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
|
||||||
msgid "Write your article here. Markdown is supported."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
|
||||||
msgid "Around {} characters left"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
|
||||||
msgid "Tags"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
|
||||||
msgid "License"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
|
||||||
msgid "Cover"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
|
||||||
msgid "This is a draft"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
|
||||||
msgid "Publish"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
+12
-12
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:56\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Vietnamese\n"
|
"Language-Team: Vietnamese\n"
|
||||||
"Language: vi_VN\n"
|
"Language: vi_VN\n"
|
||||||
@@ -17,47 +17,47 @@ msgstr ""
|
|||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:282
|
||||||
msgid "Open the rich text editor"
|
msgid "Open the rich text editor"
|
||||||
msgstr "Văn bản của tôi"
|
msgstr "Văn bản của tôi"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
# plume-front/src/editor.rs:315
|
||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Tiêu Châu"
|
msgstr "Tiêu Châu"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
# plume-front/src/editor.rs:319
|
||||||
msgid "Subtitle, or summary"
|
msgid "Subtitle, or summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
+30
-30
@@ -3,61 +3,61 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:20\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Chinese Traditional\n"
|
"Language-Team: Chinese Simplified\n"
|
||||||
"Language: zh_TW\n"
|
"Language: zh_CN\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
"X-Crowdin-Project: plume\n"
|
"X-Crowdin-Project: plume\n"
|
||||||
"X-Crowdin-Project-ID: 352097\n"
|
"X-Crowdin-Project-ID: 352097\n"
|
||||||
"X-Crowdin-Language: zh-TW\n"
|
"X-Crowdin-Language: zh-CN\n"
|
||||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||||
"X-Crowdin-File-ID: 12\n"
|
"X-Crowdin-File-ID: 12\n"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:172
|
# plume-front/src/editor.rs:189
|
||||||
msgid "Do you want to load the local autosave last edited at {}?"
|
msgid "Do you want to load the local autosave last edited at {}?"
|
||||||
msgstr "你想要載入上次在 {} 编辑的本地自動保存嗎?"
|
msgstr ""
|
||||||
|
|
||||||
|
# plume-front/src/editor.rs:282
|
||||||
|
msgid "Open the rich text editor"
|
||||||
|
msgstr "打开富文本编辑器"
|
||||||
|
|
||||||
|
# plume-front/src/editor.rs:315
|
||||||
|
msgid "Title"
|
||||||
|
msgstr "标题"
|
||||||
|
|
||||||
|
# plume-front/src/editor.rs:319
|
||||||
|
msgid "Subtitle, or summary"
|
||||||
|
msgstr "副标题或摘要"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:326
|
# plume-front/src/editor.rs:326
|
||||||
msgid "Open the rich text editor"
|
|
||||||
msgstr "開啟 RTF 編輯器"
|
|
||||||
|
|
||||||
# plume-front/src/editor.rs:385
|
|
||||||
msgid "Title"
|
|
||||||
msgstr "標題"
|
|
||||||
|
|
||||||
# plume-front/src/editor.rs:389
|
|
||||||
msgid "Subtitle, or summary"
|
|
||||||
msgstr "副標題,或摘要"
|
|
||||||
|
|
||||||
# plume-front/src/editor.rs:396
|
|
||||||
msgid "Write your article here. Markdown is supported."
|
msgid "Write your article here. Markdown is supported."
|
||||||
msgstr "在這裡寫下您的文章。支援 Markdown 語法。"
|
msgstr "在这里写下您的文章。支持 Markdown 语法。"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:407
|
# plume-front/src/editor.rs:337
|
||||||
msgid "Around {} characters left"
|
msgid "Around {} characters left"
|
||||||
msgstr "大約還可輸入 {} 字符"
|
msgstr "大约剩余 {} 可输入字符"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:517
|
# plume-front/src/editor.rs:414
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr "標籤"
|
msgstr "标签"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:518
|
# plume-front/src/editor.rs:415
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "授權條款"
|
msgstr "许可协议"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:524
|
# plume-front/src/editor.rs:418
|
||||||
msgid "Cover"
|
msgid "Cover"
|
||||||
msgstr "封面"
|
msgstr "封面"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:564
|
# plume-front/src/editor.rs:438
|
||||||
msgid "This is a draft"
|
msgid "This is a draft"
|
||||||
msgstr "這是草稿"
|
msgstr "这是一个草稿"
|
||||||
|
|
||||||
# plume-front/src/editor.rs:575
|
# plume-front/src/editor.rs:445
|
||||||
msgid "Publish"
|
msgid "Publish"
|
||||||
msgstr "發布"
|
msgstr "发布"
|
||||||
|
|
||||||
|
|||||||
+146
-130
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:28\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Afrikaans\n"
|
"Language-Team: Afrikaans\n"
|
||||||
"Language: af_ZA\n"
|
"Language: af_ZA\n"
|
||||||
@@ -18,126 +18,106 @@ msgstr ""
|
|||||||
"X-Crowdin-File-ID: 8\n"
|
"X-Crowdin-File-ID: 8\n"
|
||||||
|
|
||||||
# src/template_utils.rs:105
|
# src/template_utils.rs:105
|
||||||
msgid "Someone"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/template_utils.rs:107
|
|
||||||
msgid "{0} commented on your article."
|
msgid "{0} commented on your article."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/template_utils.rs:108
|
# src/template_utils.rs:106
|
||||||
msgid "{0} is subscribed to you."
|
msgid "{0} is subscribed to you."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/template_utils.rs:109
|
# src/template_utils.rs:107
|
||||||
msgid "{0} liked your article."
|
msgid "{0} liked your article."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/template_utils.rs:110
|
# src/template_utils.rs:108
|
||||||
msgid "{0} mentioned you."
|
msgid "{0} mentioned you."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/template_utils.rs:111
|
# src/template_utils.rs:109
|
||||||
msgid "{0} boosted your article."
|
msgid "{0} boosted your article."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/template_utils.rs:118
|
# src/template_utils.rs:116
|
||||||
msgid "Your feed"
|
msgid "Your feed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/template_utils.rs:119
|
# src/template_utils.rs:117
|
||||||
msgid "Local feed"
|
msgid "Local feed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/template_utils.rs:120
|
# src/template_utils.rs:118
|
||||||
msgid "Federated feed"
|
msgid "Federated feed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/template_utils.rs:156
|
# src/template_utils.rs:154
|
||||||
msgid "{0}'s avatar"
|
msgid "{0}'s avatar"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/template_utils.rs:200
|
# src/template_utils.rs:198
|
||||||
msgid "Previous page"
|
msgid "Previous page"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/template_utils.rs:211
|
# src/template_utils.rs:209
|
||||||
msgid "Next page"
|
msgid "Next page"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/template_utils.rs:365
|
# src/template_utils.rs:363
|
||||||
msgid "Optional"
|
msgid "Optional"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/blogs.rs:68
|
# src/routes/blogs.rs:63
|
||||||
msgid "To create a new blog, you need to be logged in"
|
msgid "To create a new blog, you need to be logged in"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/blogs.rs:110
|
# src/routes/blogs.rs:102
|
||||||
msgid "A blog with the same name already exists."
|
msgid "A blog with the same name already exists."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/blogs.rs:148
|
# src/routes/blogs.rs:140
|
||||||
msgid "Your blog was successfully created!"
|
msgid "Your blog was successfully created!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/blogs.rs:166
|
# src/routes/blogs.rs:160
|
||||||
msgid "Your blog was deleted."
|
msgid "Your blog was deleted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/blogs.rs:174
|
# src/routes/blogs.rs:168
|
||||||
msgid "You are not allowed to delete this blog."
|
msgid "You are not allowed to delete this blog."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/blogs.rs:224
|
# src/routes/blogs.rs:219
|
||||||
msgid "You are not allowed to edit this blog."
|
msgid "You are not allowed to edit this blog."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/blogs.rs:280
|
# src/routes/blogs.rs:275
|
||||||
msgid "You can't use this media as a blog icon."
|
msgid "You can't use this media as a blog icon."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/blogs.rs:298
|
# src/routes/blogs.rs:293
|
||||||
msgid "You can't use this media as a blog banner."
|
msgid "You can't use this media as a blog banner."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/blogs.rs:332
|
# src/routes/blogs.rs:327
|
||||||
msgid "Your blog information have been updated."
|
msgid "Your blog information have been updated."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/comments.rs:100
|
# src/routes/comments.rs:97
|
||||||
msgid "Your comment has been posted."
|
msgid "Your comment has been posted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/comments.rs:177
|
# src/routes/comments.rs:172
|
||||||
msgid "Your comment has been deleted."
|
msgid "Your comment has been deleted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/email_signups.rs:82
|
# src/routes/instance.rs:120
|
||||||
msgid "Registrations are closed on this instance."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/routes/email_signups.rs:119
|
|
||||||
msgid "User registration"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/routes/email_signups.rs:120
|
|
||||||
msgid "Here is the link for registration: {0}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/routes/email_signups.rs:219
|
|
||||||
msgid "Your account has been created. Now you just need to log in, before you can use it."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/routes/instance.rs:117
|
|
||||||
msgid "Instance settings have been saved."
|
msgid "Instance settings have been saved."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/instance.rs:150
|
# src/routes/instance.rs:152
|
||||||
msgid "{} has been unblocked."
|
msgid "{} has been unblocked."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/instance.rs:152
|
# src/routes/instance.rs:154
|
||||||
msgid "{} has been blocked."
|
msgid "{} has been blocked."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -145,189 +125,170 @@ msgstr ""
|
|||||||
msgid "Blocks deleted"
|
msgid "Blocks deleted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/instance.rs:219
|
# src/routes/instance.rs:218
|
||||||
msgid "Email already blocked"
|
msgid "Email already blocked"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/instance.rs:224
|
# src/routes/instance.rs:223
|
||||||
msgid "Email Blocked"
|
msgid "Email Blocked"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/instance.rs:317
|
# src/routes/instance.rs:314
|
||||||
msgid "You can't change your own rights."
|
msgid "You can't change your own rights."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/instance.rs:328
|
# src/routes/instance.rs:325
|
||||||
msgid "You are not allowed to take this action."
|
msgid "You are not allowed to take this action."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/instance.rs:363
|
# src/routes/instance.rs:362
|
||||||
msgid "Done."
|
msgid "Done."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/likes.rs:58
|
# src/routes/likes.rs:53
|
||||||
msgid "To like a post, you need to be logged in"
|
msgid "To like a post, you need to be logged in"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/medias.rs:153
|
# src/routes/medias.rs:145
|
||||||
msgid "Your media have been deleted."
|
msgid "Your media have been deleted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/medias.rs:158
|
# src/routes/medias.rs:150
|
||||||
msgid "You are not allowed to delete this media."
|
msgid "You are not allowed to delete this media."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/medias.rs:175
|
# src/routes/medias.rs:167
|
||||||
msgid "Your avatar has been updated."
|
msgid "Your avatar has been updated."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/medias.rs:180
|
# src/routes/medias.rs:172
|
||||||
msgid "You are not allowed to use this media."
|
msgid "You are not allowed to use this media."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/notifications.rs:29
|
# src/routes/notifications.rs:28
|
||||||
msgid "To see your notifications, you need to be logged in"
|
msgid "To see your notifications, you need to be logged in"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:56
|
# src/routes/posts.rs:54
|
||||||
msgid "This post isn't published yet."
|
msgid "This post isn't published yet."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:126
|
# src/routes/posts.rs:125
|
||||||
msgid "To write a new post, you need to be logged in"
|
msgid "To write a new post, you need to be logged in"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:147
|
# src/routes/posts.rs:142
|
||||||
msgid "You are not an author of this blog."
|
msgid "You are not an author of this blog."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:154
|
# src/routes/posts.rs:149
|
||||||
msgid "New post"
|
msgid "New post"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:199
|
# src/routes/posts.rs:194
|
||||||
msgid "Edit {0}"
|
msgid "Edit {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:268
|
# src/routes/posts.rs:263
|
||||||
msgid "You are not allowed to publish on this blog."
|
msgid "You are not allowed to publish on this blog."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:368
|
# src/routes/posts.rs:355
|
||||||
msgid "Your article has been updated."
|
msgid "Your article has been updated."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:557
|
# src/routes/posts.rs:542
|
||||||
msgid "Your article has been saved."
|
msgid "Your article has been saved."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:564
|
# src/routes/posts.rs:549
|
||||||
msgid "New article"
|
msgid "New article"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:602
|
# src/routes/posts.rs:582
|
||||||
msgid "You are not allowed to delete this article."
|
msgid "You are not allowed to delete this article."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:626
|
# src/routes/posts.rs:607
|
||||||
msgid "Your article has been deleted."
|
msgid "Your article has been deleted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:631
|
# src/routes/posts.rs:612
|
||||||
msgid "It looks like the article you tried to delete doesn't exist. Maybe it is already gone?"
|
msgid ""
|
||||||
|
"It looks like the article you tried to delete doesn't exist. Maybe it is "
|
||||||
|
"already gone?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/posts.rs:673
|
# src/routes/posts.rs:652
|
||||||
msgid "Couldn't obtain enough information about your account. Please make sure your username is correct."
|
msgid ""
|
||||||
|
"Couldn't obtain enough information about your account. Please make sure your "
|
||||||
|
"username is correct."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/reshares.rs:58
|
# src/routes/reshares.rs:54
|
||||||
msgid "To reshare a post, you need to be logged in"
|
msgid "To reshare a post, you need to be logged in"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/session.rs:95
|
# src/routes/session.rs:87
|
||||||
msgid "You are now connected."
|
msgid "You are now connected."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/session.rs:116
|
# src/routes/session.rs:108
|
||||||
msgid "You are now logged off."
|
msgid "You are now logged off."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/session.rs:162
|
# src/routes/session.rs:153
|
||||||
msgid "Password reset"
|
msgid "Password reset"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/session.rs:163
|
# src/routes/session.rs:154
|
||||||
msgid "Here is the link to reset your password: {0}"
|
msgid "Here is the link to reset your password: {0}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/session.rs:235
|
# src/routes/session.rs:216
|
||||||
msgid "Your password was successfully reset."
|
msgid "Your password was successfully reset."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:87
|
# src/routes/user.rs:141
|
||||||
msgid "To access your dashboard, you need to be logged in"
|
msgid "To access your dashboard, you need to be logged in"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:109
|
# src/routes/user.rs:163
|
||||||
msgid "You are no longer following {}."
|
msgid "You are no longer following {}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:126
|
# src/routes/user.rs:180
|
||||||
msgid "You are now following {}."
|
msgid "You are now following {}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:203
|
# src/routes/user.rs:260
|
||||||
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:323
|
# src/routes/user.rs:364
|
||||||
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:369
|
# src/routes/user.rs:409
|
||||||
msgid "Your profile has been updated."
|
msgid "Your profile has been updated."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:397
|
# src/routes/user.rs:436
|
||||||
msgid "Your account has been deleted."
|
msgid "Your account has been deleted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/user.rs:403
|
# src/routes/user.rs:442
|
||||||
msgid "You can't delete someone else's account."
|
msgid "You can't delete someone else's account."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Create your account"
|
# src/routes/user.rs:526
|
||||||
|
msgid "Registrations are closed on this instance."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Create an account"
|
# src/routes/user.rs:549
|
||||||
msgstr ""
|
msgid ""
|
||||||
|
"Your account has been created. Now you just need to log in, before you can "
|
||||||
msgid "Email"
|
"use it."
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Email confirmation"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Apologies, but registrations are closed on this particular instance. You can, however, find a different one."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Registration"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Check your inbox!"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "We sent a mail to the address you gave us, with a link for registration."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Username"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Password"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Password confirmation"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Media upload"
|
msgid "Media upload"
|
||||||
@@ -468,6 +429,29 @@ msgstr ""
|
|||||||
msgid "Subscriptions"
|
msgid "Subscriptions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Create your account"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Create an account"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Username"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
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 subscriptions"
|
msgid "{0}'s subscriptions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -495,7 +479,8 @@ msgstr ""
|
|||||||
msgid "Your Profile"
|
msgid "Your Profile"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "To change your avatar, upload it to your gallery and then select from there."
|
msgid ""
|
||||||
|
"To change your avatar, upload it to your gallery and then select from there."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Upload an avatar"
|
msgid "Upload an avatar"
|
||||||
@@ -567,7 +552,10 @@ msgstr ""
|
|||||||
msgid "Invalid CSRF token"
|
msgid "Invalid CSRF token"
|
||||||
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 ""
|
||||||
|
"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 ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "You are not authorized."
|
msgid "You are not authorized."
|
||||||
@@ -651,13 +639,21 @@ msgstr ""
|
|||||||
msgid "Save these settings"
|
msgid "Save these settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "If you are browsing this site as a visitor, no data about you is collected."
|
msgid ""
|
||||||
|
"If you are browsing this site as a visitor, no data about you is collected."
|
||||||
msgstr ""
|
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."
|
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 ""
|
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."
|
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 ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Blocklisted Emails"
|
msgid "Blocklisted Emails"
|
||||||
@@ -666,7 +662,10 @@ msgstr ""
|
|||||||
msgid "Email address"
|
msgid "Email address"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "The email address you wish to block. In order to block domains, you can use globbing syntax, for example '*@example.com' blocks all addresses from example.com"
|
msgid ""
|
||||||
|
"The email address you wish to block. In order to block domains, you can use "
|
||||||
|
"globbing syntax, for example '*@example.com' blocks all addresses from "
|
||||||
|
"example.com"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Note"
|
msgid "Note"
|
||||||
@@ -675,13 +674,17 @@ msgstr ""
|
|||||||
msgid "Notify the user?"
|
msgid "Notify the user?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Optional, shows a message to the user when they attempt to create an account with that address"
|
msgid ""
|
||||||
|
"Optional, shows a message to the user when they attempt to create an account "
|
||||||
|
"with that address"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Blocklisting notification"
|
msgid "Blocklisting notification"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "The message to be shown when the user attempts to create an account with this email address"
|
msgid ""
|
||||||
|
"The message to be shown when the user attempts to create an account with "
|
||||||
|
"this email address"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Add blocklisted address"
|
msgid "Add blocklisted address"
|
||||||
@@ -753,7 +756,9 @@ msgstr ""
|
|||||||
msgid "Content"
|
msgid "Content"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "You can upload media to your gallery, and then copy their Markdown code into your articles to insert them."
|
msgid ""
|
||||||
|
"You can upload media to your gallery, and then copy their Markdown code into "
|
||||||
|
"your articles to insert them."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Upload media"
|
msgid "Upload media"
|
||||||
@@ -811,7 +816,9 @@ msgstr ""
|
|||||||
msgid "Boost"
|
msgid "Boost"
|
||||||
msgstr ""
|
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 "Comments"
|
msgid "Comments"
|
||||||
@@ -865,7 +872,12 @@ msgstr ""
|
|||||||
msgid "Update password"
|
msgid "Update password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "We sent a mail to the address you gave us, with a link to reset your password."
|
msgid "Check your inbox!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"We sent a mail to the address you gave us, with a link to reset your "
|
||||||
|
"password."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Send password reset link"
|
msgid "Send password reset link"
|
||||||
@@ -874,7 +886,9 @@ msgstr ""
|
|||||||
msgid "This token has expired"
|
msgid "This token has expired"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Please start the process again by clicking <a href=\"/password-reset\">here</a>."
|
msgid ""
|
||||||
|
"Please start the process again by clicking <a href=\"/password-reset\">here</"
|
||||||
|
"a>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "New Blog"
|
msgid "New Blog"
|
||||||
@@ -889,7 +903,8 @@ msgstr ""
|
|||||||
msgid "Edit \"{}\""
|
msgid "Edit \"{}\""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "You can upload images to your gallery, to use them as blog icons, or banners."
|
msgid ""
|
||||||
|
"You can upload images to your gallery, to use them as blog icons, or banners."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Upload images"
|
msgid "Upload images"
|
||||||
@@ -951,7 +966,9 @@ msgstr ""
|
|||||||
msgid "Authors can manage multiple blogs, each as its own website."
|
msgid "Authors can manage multiple blogs, each as its own website."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Articles are also visible on other Plume instances, and you can interact with them directly from other platforms like Mastodon."
|
msgid ""
|
||||||
|
"Articles are also visible on other Plume instances, and you can interact "
|
||||||
|
"with them directly from other platforms like Mastodon."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Read the detailed rules"
|
msgid "Read the detailed rules"
|
||||||
@@ -1031,4 +1048,3 @@ msgstr ""
|
|||||||
|
|
||||||
msgid "Article license"
|
msgid "Article license"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
+167
-139
@@ -3,14 +3,15 @@ msgstr ""
|
|||||||
"Project-Id-Version: plume\n"
|
"Project-Id-Version: plume\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||||
"PO-Revision-Date: 2022-01-12 01:28\n"
|
"PO-Revision-Date: 2020-12-19 09:55\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Arabic\n"
|
"Language-Team: Arabic\n"
|
||||||
"Language: ar_SA\n"
|
"Language: ar_SA\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n"
|
"Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
|
||||||
|
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n"
|
||||||
"X-Crowdin-Project: plume\n"
|
"X-Crowdin-Project: plume\n"
|
||||||
"X-Crowdin-Project-ID: 352097\n"
|
"X-Crowdin-Project-ID: 352097\n"
|
||||||
"X-Crowdin-Language: ar\n"
|
"X-Crowdin-Language: ar\n"
|
||||||
@@ -18,126 +19,106 @@ msgstr ""
|
|||||||
"X-Crowdin-File-ID: 8\n"
|
"X-Crowdin-File-ID: 8\n"
|
||||||
|
|
||||||
# src/template_utils.rs:105
|
# src/template_utils.rs:105
|
||||||
msgid "Someone"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/template_utils.rs:107
|
|
||||||
msgid "{0} commented on your article."
|
msgid "{0} commented on your article."
|
||||||
msgstr "علّق {0} على مقالك."
|
msgstr "علّق {0} على مقالك."
|
||||||
|
|
||||||
# src/template_utils.rs:108
|
# src/template_utils.rs:106
|
||||||
msgid "{0} is subscribed to you."
|
msgid "{0} is subscribed to you."
|
||||||
msgstr "{0} مشترك لك."
|
msgstr "{0} مشترك لك."
|
||||||
|
|
||||||
# src/template_utils.rs:109
|
# src/template_utils.rs:107
|
||||||
msgid "{0} liked your article."
|
msgid "{0} liked your article."
|
||||||
msgstr "{0} أعجبهم مقالك."
|
msgstr "{0} أعجبهم مقالك."
|
||||||
|
|
||||||
# src/template_utils.rs:110
|
# src/template_utils.rs:108
|
||||||
msgid "{0} mentioned you."
|
msgid "{0} mentioned you."
|
||||||
msgstr "أشار إليك {0}."
|
msgstr "أشار إليك {0}."
|
||||||
|
|
||||||
# src/template_utils.rs:111
|
# src/template_utils.rs:109
|
||||||
msgid "{0} boosted your article."
|
msgid "{0} boosted your article."
|
||||||
msgstr "{0} دعمو مقالك."
|
msgstr "{0} دعمو مقالك."
|
||||||
|
|
||||||
# src/template_utils.rs:118
|
# src/template_utils.rs:116
|
||||||
msgid "Your feed"
|
msgid "Your feed"
|
||||||
msgstr "خيطك"
|
msgstr "خيطك"
|
||||||
|
|
||||||
# src/template_utils.rs:119
|
# src/template_utils.rs:117
|
||||||
msgid "Local feed"
|
msgid "Local feed"
|
||||||
msgstr "الخيط المحلي"
|
msgstr "الخيط المحلي"
|
||||||
|
|
||||||
# src/template_utils.rs:120
|
# src/template_utils.rs:118
|
||||||
msgid "Federated feed"
|
msgid "Federated feed"
|
||||||
msgstr "الخيط الموحد"
|
msgstr "الخيط الموحد"
|
||||||
|
|
||||||
# src/template_utils.rs:156
|
# src/template_utils.rs:154
|
||||||
msgid "{0}'s avatar"
|
msgid "{0}'s avatar"
|
||||||
msgstr "الصورة الرمزية لـ {0}"
|
msgstr "الصورة الرمزية لـ {0}"
|
||||||
|
|
||||||
# src/template_utils.rs:200
|
# src/template_utils.rs:198
|
||||||
msgid "Previous page"
|
msgid "Previous page"
|
||||||
msgstr "الصفحة السابقة"
|
msgstr "الصفحة السابقة"
|
||||||
|
|
||||||
# src/template_utils.rs:211
|
# src/template_utils.rs:209
|
||||||
msgid "Next page"
|
msgid "Next page"
|
||||||
msgstr "الصفحة التالية"
|
msgstr "الصفحة التالية"
|
||||||
|
|
||||||
# src/template_utils.rs:365
|
# src/template_utils.rs:363
|
||||||
msgid "Optional"
|
msgid "Optional"
|
||||||
msgstr "اختياري"
|
msgstr "اختياري"
|
||||||
|
|
||||||
# src/routes/blogs.rs:68
|
# src/routes/blogs.rs:63
|
||||||
msgid "To create a new blog, you need to be logged in"
|
msgid "To create a new blog, you need to be logged in"
|
||||||
msgstr "لإنشاء مدونة جديدة، تحتاج إلى تسجيل الدخول"
|
msgstr "لإنشاء مدونة جديدة، تحتاج إلى تسجيل الدخول"
|
||||||
|
|
||||||
# src/routes/blogs.rs:110
|
# src/routes/blogs.rs:102
|
||||||
msgid "A blog with the same name already exists."
|
msgid "A blog with the same name already exists."
|
||||||
msgstr "توجد مدونة تحمل نفس العنوان."
|
msgstr "توجد مدونة تحمل نفس العنوان."
|
||||||
|
|
||||||
# src/routes/blogs.rs:148
|
# src/routes/blogs.rs:140
|
||||||
msgid "Your blog was successfully created!"
|
msgid "Your blog was successfully created!"
|
||||||
msgstr "تم إنشاء مدونتك بنجاح!"
|
msgstr "تم إنشاء مدونتك بنجاح!"
|
||||||
|
|
||||||
# src/routes/blogs.rs:166
|
# src/routes/blogs.rs:160
|
||||||
msgid "Your blog was deleted."
|
msgid "Your blog was deleted."
|
||||||
msgstr "تم حذف مدونتك."
|
msgstr "تم حذف مدونتك."
|
||||||
|
|
||||||
# src/routes/blogs.rs:174
|
# src/routes/blogs.rs:168
|
||||||
msgid "You are not allowed to delete this blog."
|
msgid "You are not allowed to delete this blog."
|
||||||
msgstr "لا يسمح لك بحذف هذه المدونة."
|
msgstr "لا يسمح لك بحذف هذه المدونة."
|
||||||
|
|
||||||
# src/routes/blogs.rs:224
|
# src/routes/blogs.rs:219
|
||||||
msgid "You are not allowed to edit this blog."
|
msgid "You are not allowed to edit this blog."
|
||||||
msgstr "لا يسمح لك بتعديل هذه المدونة."
|
msgstr "لا يسمح لك بتعديل هذه المدونة."
|
||||||
|
|
||||||
# src/routes/blogs.rs:280
|
# src/routes/blogs.rs:275
|
||||||
msgid "You can't use this media as a blog icon."
|
msgid "You can't use this media as a blog icon."
|
||||||
msgstr "لا يمكنك استخدام هذه الوسائط كأيقونة للمدونة."
|
msgstr "لا يمكنك استخدام هذه الوسائط كأيقونة للمدونة."
|
||||||
|
|
||||||
# src/routes/blogs.rs:298
|
# src/routes/blogs.rs:293
|
||||||
msgid "You can't use this media as a blog banner."
|
msgid "You can't use this media as a blog banner."
|
||||||
msgstr "لا يمكنك استخدام هذه الوسائط كشعار للمدونة."
|
msgstr "لا يمكنك استخدام هذه الوسائط كشعار للمدونة."
|
||||||
|
|
||||||
# src/routes/blogs.rs:332
|
# src/routes/blogs.rs:327
|
||||||
msgid "Your blog information have been updated."
|
msgid "Your blog information have been updated."
|
||||||
msgstr "تم تحديث معلومات مُدوّنتك."
|
msgstr "تم تحديث معلومات مُدوّنتك."
|
||||||
|
|
||||||
# src/routes/comments.rs:100
|
# src/routes/comments.rs:97
|
||||||
msgid "Your comment has been posted."
|
msgid "Your comment has been posted."
|
||||||
msgstr "تم نشر تعليقك."
|
msgstr "تم نشر تعليقك."
|
||||||
|
|
||||||
# src/routes/comments.rs:177
|
# src/routes/comments.rs:172
|
||||||
msgid "Your comment has been deleted."
|
msgid "Your comment has been deleted."
|
||||||
msgstr "تم حذف تعليقك."
|
msgstr "تم حذف تعليقك."
|
||||||
|
|
||||||
# src/routes/email_signups.rs:82
|
# src/routes/instance.rs:120
|
||||||
msgid "Registrations are closed on this instance."
|
|
||||||
msgstr "التسجيلات مُغلقة على مثيل الخادم هذ."
|
|
||||||
|
|
||||||
# src/routes/email_signups.rs:119
|
|
||||||
msgid "User registration"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/routes/email_signups.rs:120
|
|
||||||
msgid "Here is the link for registration: {0}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
# src/routes/email_signups.rs:219
|
|
||||||
msgid "Your account has been created. Now you just need to log in, before you can use it."
|
|
||||||
msgstr "لقد تم إنشاء حسابك. ما عليك إلّا الولوج الآن للتمكّن مِن استعماله."
|
|
||||||
|
|
||||||
# src/routes/instance.rs:117
|
|
||||||
msgid "Instance settings have been saved."
|
msgid "Instance settings have been saved."
|
||||||
msgstr "تم حفظ إعدادات المثيل."
|
msgstr "تم حفظ إعدادات المثيل."
|
||||||
|
|
||||||
# src/routes/instance.rs:150
|
# src/routes/instance.rs:152
|
||||||
msgid "{} has been unblocked."
|
msgid "{} has been unblocked."
|
||||||
msgstr "تم إلغاء حظر {}."
|
msgstr "تم إلغاء حظر {}."
|
||||||
|
|
||||||
# src/routes/instance.rs:152
|
# src/routes/instance.rs:154
|
||||||
msgid "{} has been blocked."
|
msgid "{} has been blocked."
|
||||||
msgstr "تم حظر {}."
|
msgstr "تم حظر {}."
|
||||||
|
|
||||||
@@ -145,190 +126,171 @@ msgstr "تم حظر {}."
|
|||||||
msgid "Blocks deleted"
|
msgid "Blocks deleted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/instance.rs:219
|
# src/routes/instance.rs:218
|
||||||
msgid "Email already blocked"
|
msgid "Email already blocked"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/instance.rs:224
|
# src/routes/instance.rs:223
|
||||||
msgid "Email Blocked"
|
msgid "Email Blocked"
|
||||||
msgstr "البريد الإلكتروني محظور"
|
msgstr "البريد الإلكتروني محظور"
|
||||||
|
|
||||||
# src/routes/instance.rs:317
|
# src/routes/instance.rs:314
|
||||||
msgid "You can't change your own rights."
|
msgid "You can't change your own rights."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# src/routes/instance.rs:328
|
# src/routes/instance.rs:325
|
||||||
msgid "You are not allowed to take this action."
|
msgid "You are not allowed to take this action."
|
||||||
msgstr "لا يسمح لك القيام بهذا الإجراء."
|
msgstr "لا يسمح لك القيام بهذا الإجراء."
|
||||||
|
|
||||||
# src/routes/instance.rs:363
|
# src/routes/instance.rs:362
|
||||||
msgid "Done."
|
msgid "Done."
|
||||||
msgstr "تم."
|
msgstr "تم."
|
||||||
|
|
||||||
# src/routes/likes.rs:58
|
# src/routes/likes.rs:53
|
||||||
msgid "To like a post, you need to be logged in"
|
msgid "To like a post, you need to be logged in"
|
||||||
msgstr "يجب عليك تسجيل الدخول أولا للإعجاب بهذا المقال"
|
msgstr "يجب عليك تسجيل الدخول أولا للإعجاب بهذا المقال"
|
||||||
|
|
||||||
# src/routes/medias.rs:153
|
# src/routes/medias.rs:145
|
||||||
msgid "Your media have been deleted."
|
msgid "Your media have been deleted."
|
||||||
msgstr "لقد تم حذف وسائطك."
|
msgstr "لقد تم حذف وسائطك."
|
||||||
|
|
||||||
# src/routes/medias.rs:158
|
# src/routes/medias.rs:150
|
||||||
msgid "You are not allowed to delete this media."
|
msgid "You are not allowed to delete this media."
|
||||||
msgstr "لا يسمح لك بحذف هذه الوسائط."
|
msgstr "لا يسمح لك بحذف هذه الوسائط."
|
||||||
|
|
||||||
# src/routes/medias.rs:175
|
# src/routes/medias.rs:167
|
||||||
msgid "Your avatar has been updated."
|
msgid "Your avatar has been updated."
|
||||||
msgstr "تم تحديث صورتك الشخصية."
|
msgstr "تم تحديث صورتك الشخصية."
|
||||||
|
|
||||||
# src/routes/medias.rs:180
|
# src/routes/medias.rs:172
|
||||||
msgid "You are not allowed to use this media."
|
msgid "You are not allowed to use this media."
|
||||||
msgstr "لا يسمح لك باستعمال هذه الوسائط."
|
msgstr "لا يسمح لك باستعمال هذه الوسائط."
|
||||||
|
|
||||||
# src/routes/notifications.rs:29
|
# src/routes/notifications.rs:28
|
||||||
msgid "To see your notifications, you need to be logged in"
|
msgid "To see your notifications, you need to be logged in"
|
||||||
msgstr "يجب عليك تسجيل الدخول أولا لعرض الإشعارات"
|
msgstr "يجب عليك تسجيل الدخول أولا لعرض الإشعارات"
|
||||||
|
|
||||||
# src/routes/posts.rs:56
|
# src/routes/posts.rs:54
|
||||||
msgid "This post isn't published yet."
|
msgid "This post isn't published yet."
|
||||||
msgstr "هذا المقال ليس منشورا بعد."
|
msgstr "هذا المقال ليس منشورا بعد."
|
||||||
|
|
||||||
# src/routes/posts.rs:126
|
# src/routes/posts.rs:125
|
||||||
msgid "To write a new post, you need to be logged in"
|
msgid "To write a new post, you need to be logged in"
|
||||||
msgstr "يجب عليك تسجيل الدخول أولا لكتابة مقال جديد"
|
msgstr "يجب عليك تسجيل الدخول أولا لكتابة مقال جديد"
|
||||||
|
|
||||||
# src/routes/posts.rs:147
|
# src/routes/posts.rs:142
|
||||||
msgid "You are not an author of this blog."
|
msgid "You are not an author of this blog."
|
||||||
msgstr "لست مِن محرري هذه المدونة."
|
msgstr "لست مِن محرري هذه المدونة."
|
||||||
|
|
||||||
# src/routes/posts.rs:154
|
# src/routes/posts.rs:149
|
||||||
msgid "New post"
|
msgid "New post"
|
||||||
msgstr "منشور جديد"
|
msgstr "منشور جديد"
|
||||||
|
|
||||||
# src/routes/posts.rs:199
|
# src/routes/posts.rs:194
|
||||||
msgid "Edit {0}"
|
msgid "Edit {0}"
|
||||||
msgstr "تعديل {0}"
|
msgstr "تعديل {0}"
|
||||||
|
|
||||||
# src/routes/posts.rs:268
|
# src/routes/posts.rs:263
|
||||||
msgid "You are not allowed to publish on this blog."
|
msgid "You are not allowed to publish on this blog."
|
||||||
msgstr "لا يسمح لك بالنشر على هذه المدونة."
|
msgstr "لا يسمح لك بالنشر على هذه المدونة."
|
||||||
|
|
||||||
# src/routes/posts.rs:368
|
# src/routes/posts.rs:355
|
||||||
msgid "Your article has been updated."
|
msgid "Your article has been updated."
|
||||||
msgstr "تم تحديث مقالك."
|
msgstr "تم تحديث مقالك."
|
||||||
|
|
||||||
# src/routes/posts.rs:557
|
# src/routes/posts.rs:542
|
||||||
msgid "Your article has been saved."
|
msgid "Your article has been saved."
|
||||||
msgstr "تم حفظ مقالك."
|
msgstr "تم حفظ مقالك."
|
||||||
|
|
||||||
# src/routes/posts.rs:564
|
# src/routes/posts.rs:549
|
||||||
msgid "New article"
|
msgid "New article"
|
||||||
msgstr "مقال جديد"
|
msgstr "مقال جديد"
|
||||||
|
|
||||||
# src/routes/posts.rs:602
|
# src/routes/posts.rs:582
|
||||||
msgid "You are not allowed to delete this article."
|
msgid "You are not allowed to delete this article."
|
||||||
msgstr "لا يسمح لك بحذف هذا المقال."
|
msgstr "لا يسمح لك بحذف هذا المقال."
|
||||||
|
|
||||||
# src/routes/posts.rs:626
|
# src/routes/posts.rs:607
|
||||||
msgid "Your article has been deleted."
|
msgid "Your article has been deleted."
|
||||||
msgstr "تم حذف مقالك."
|
msgstr "تم حذف مقالك."
|
||||||
|
|
||||||
# src/routes/posts.rs:631
|
# src/routes/posts.rs:612
|
||||||
msgid "It looks like the article you tried to delete doesn't exist. Maybe it is already gone?"
|
msgid ""
|
||||||
|
"It looks like the article you tried to delete doesn't exist. Maybe it is "
|
||||||
|
"already gone?"
|
||||||
msgstr "لم يتم العثور على المقال الذي تحاول حذفه. ربما سبق حذفه؟"
|
msgstr "لم يتم العثور على المقال الذي تحاول حذفه. ربما سبق حذفه؟"
|
||||||
|
|
||||||
# src/routes/posts.rs:673
|
# src/routes/posts.rs:652
|
||||||
msgid "Couldn't obtain enough information about your account. Please make sure your username is correct."
|
msgid ""
|
||||||
|
"Couldn't obtain enough information about your account. Please make sure your "
|
||||||
|
"username is correct."
|
||||||
msgstr "تعذر العثور عن معلومات حسابك. المرجو التحقق من صحة إسم المستخدم."
|
msgstr "تعذر العثور عن معلومات حسابك. المرجو التحقق من صحة إسم المستخدم."
|
||||||
|
|
||||||
# src/routes/reshares.rs:58
|
# src/routes/reshares.rs:54
|
||||||
msgid "To reshare a post, you need to be logged in"
|
msgid "To reshare a post, you need to be logged in"
|
||||||
msgstr "يجب عليك تسجيل الدخول أولا للإعادت نشر هذا المقال"
|
msgstr "يجب عليك تسجيل الدخول أولا للإعادت نشر هذا المقال"
|
||||||
|
|
||||||
# src/routes/session.rs:95
|
# src/routes/session.rs:87
|
||||||
msgid "You are now connected."
|
msgid "You are now connected."
|
||||||
msgstr "أنت الآن متصل."
|
msgstr "أنت الآن متصل."
|
||||||
|
|
||||||
# src/routes/session.rs:116
|
# src/routes/session.rs:108
|
||||||
msgid "You are now logged off."
|
msgid "You are now logged off."
|
||||||
msgstr "لقد قمتَ بالخروج للتوّ."
|
msgstr "لقد قمتَ بالخروج للتوّ."
|
||||||
|
|
||||||
# src/routes/session.rs:162
|
# src/routes/session.rs:153
|
||||||
msgid "Password reset"
|
msgid "Password reset"
|
||||||
msgstr "إعادة تعيين كلمة المرور"
|
msgstr "إعادة تعيين كلمة المرور"
|
||||||
|
|
||||||
# src/routes/session.rs:163
|
# src/routes/session.rs:154
|
||||||
msgid "Here is the link to reset your password: {0}"
|
msgid "Here is the link to reset your password: {0}"
|
||||||
msgstr "ها هو رابط إعادة تعيين كلمتك السرية: {0}"
|
msgstr "ها هو رابط إعادة تعيين كلمتك السرية: {0}"
|
||||||
|
|
||||||
# src/routes/session.rs:235
|
# src/routes/session.rs:216
|
||||||
msgid "Your password was successfully reset."
|
msgid "Your password was successfully reset."
|
||||||
msgstr "تمت إعادة تعيين كلمتك السرية بنجاح."
|
msgstr "تمت إعادة تعيين كلمتك السرية بنجاح."
|
||||||
|
|
||||||
# src/routes/user.rs:87
|
# src/routes/user.rs:141
|
||||||
msgid "To access your dashboard, you need to be logged in"
|
msgid "To access your dashboard, you need to be logged in"
|
||||||
msgstr "يجب عليك تسجيل الدخول أولاللنفاذ إلى لوح المراقبة"
|
msgstr "يجب عليك تسجيل الدخول أولاللنفاذ إلى لوح المراقبة"
|
||||||
|
|
||||||
# src/routes/user.rs:109
|
# src/routes/user.rs:163
|
||||||
msgid "You are no longer following {}."
|
msgid "You are no longer following {}."
|
||||||
msgstr "أنت لم تعد تتابع {}."
|
msgstr "أنت لم تعد تتابع {}."
|
||||||
|
|
||||||
# src/routes/user.rs:126
|
# src/routes/user.rs:180
|
||||||
msgid "You are now following {}."
|
msgid "You are now following {}."
|
||||||
msgstr "أنت الآن تتابع {}."
|
msgstr "أنت الآن تتابع {}."
|
||||||
|
|
||||||
# src/routes/user.rs:203
|
# src/routes/user.rs:260
|
||||||
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:323
|
# src/routes/user.rs:364
|
||||||
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:369
|
# src/routes/user.rs:409
|
||||||
msgid "Your profile has been updated."
|
msgid "Your profile has been updated."
|
||||||
msgstr "تم تحديث ملفك الشخصي."
|
msgstr "تم تحديث ملفك الشخصي."
|
||||||
|
|
||||||
# src/routes/user.rs:397
|
# src/routes/user.rs:436
|
||||||
msgid "Your account has been deleted."
|
msgid "Your account has been deleted."
|
||||||
msgstr "تم حذف حسابك."
|
msgstr "تم حذف حسابك."
|
||||||
|
|
||||||
# src/routes/user.rs:403
|
# src/routes/user.rs:442
|
||||||
msgid "You can't delete someone else's account."
|
msgid "You can't delete someone else's account."
|
||||||
msgstr "لا يمكنك حذف حساب شخص آخر."
|
msgstr "لا يمكنك حذف حساب شخص آخر."
|
||||||
|
|
||||||
msgid "Create your account"
|
# src/routes/user.rs:526
|
||||||
msgstr "انشئ حسابك"
|
msgid "Registrations are closed on this instance."
|
||||||
|
msgstr "التسجيلات مُغلقة على مثيل الخادم هذ."
|
||||||
|
|
||||||
msgid "Create an account"
|
# src/routes/user.rs:549
|
||||||
msgstr "انشئ حسابا"
|
msgid ""
|
||||||
|
"Your account has been created. Now you just need to log in, before you can "
|
||||||
msgid "Email"
|
"use it."
|
||||||
msgstr "البريد الالكتروني"
|
msgstr "لقد تم إنشاء حسابك. ما عليك إلّا الولوج الآن للتمكّن مِن استعماله."
|
||||||
|
|
||||||
msgid "Email confirmation"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Apologies, but registrations are closed on this particular instance. You can, however, find a different one."
|
|
||||||
msgstr "المعذرة، لاكن التسجيل مغلق في هذا المثيل بالدات. يمكنك إجاد مثيل آخر للتسجيل."
|
|
||||||
|
|
||||||
msgid "Registration"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Check your inbox!"
|
|
||||||
msgstr "تحقق من علبة الوارد الخاصة بك!"
|
|
||||||
|
|
||||||
msgid "We sent a mail to the address you gave us, with a link for registration."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Username"
|
|
||||||
msgstr "اسم المستخدم"
|
|
||||||
|
|
||||||
msgid "Password"
|
|
||||||
msgstr "كلمة السر"
|
|
||||||
|
|
||||||
msgid "Password confirmation"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Media upload"
|
msgid "Media upload"
|
||||||
msgstr "إرسال الوسائط"
|
msgstr "إرسال الوسائط"
|
||||||
@@ -468,6 +430,30 @@ msgstr "المشترِكون"
|
|||||||
msgid "Subscriptions"
|
msgid "Subscriptions"
|
||||||
msgstr "الاشتراكات"
|
msgstr "الاشتراكات"
|
||||||
|
|
||||||
|
msgid "Create your account"
|
||||||
|
msgstr "انشئ حسابك"
|
||||||
|
|
||||||
|
msgid "Create an account"
|
||||||
|
msgstr "انشئ حسابا"
|
||||||
|
|
||||||
|
msgid "Username"
|
||||||
|
msgstr "اسم المستخدم"
|
||||||
|
|
||||||
|
msgid "Email"
|
||||||
|
msgstr "البريد الالكتروني"
|
||||||
|
|
||||||
|
msgid "Password"
|
||||||
|
msgstr "كلمة السر"
|
||||||
|
|
||||||
|
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 subscriptions"
|
msgid "{0}'s subscriptions"
|
||||||
msgstr "{0} اشتراكات"
|
msgstr "{0} اشتراكات"
|
||||||
|
|
||||||
@@ -495,8 +481,10 @@ msgstr "تعديل حسابك"
|
|||||||
msgid "Your Profile"
|
msgid "Your Profile"
|
||||||
msgstr "ملفك الشخصي"
|
msgstr "ملفك الشخصي"
|
||||||
|
|
||||||
msgid "To change your avatar, upload it to your gallery and then select from there."
|
msgid ""
|
||||||
msgstr "لتغير الصورة التشخيصية قم أولا برفعها إلى الألبوم ثم قم بتعينها من هنالك."
|
"To change your avatar, upload it to your gallery and then select from there."
|
||||||
|
msgstr ""
|
||||||
|
"لتغير الصورة التشخيصية قم أولا برفعها إلى الألبوم ثم قم بتعينها من هنالك."
|
||||||
|
|
||||||
msgid "Upload an avatar"
|
msgid "Upload an avatar"
|
||||||
msgstr "تحميل صورة رمزية"
|
msgstr "تحميل صورة رمزية"
|
||||||
@@ -567,8 +555,13 @@ msgstr "نعتذر عن الإزعاج. إن كنت تضن أن هذه مشكل
|
|||||||
msgid "Invalid CSRF token"
|
msgid "Invalid CSRF token"
|
||||||
msgstr "الرمز المميز CSRF غير صالح"
|
msgstr "الرمز المميز CSRF غير صالح"
|
||||||
|
|
||||||
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 ""
|
||||||
msgstr "هناكخطأم ما في رمز CSRF. تحقق أن الكوكيز مفعل في متصفحك وأعد تحميل الصفحة. إذا واجهتهذا الخطأ منجديد يرجى التبليغ."
|
"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 ""
|
||||||
|
"هناكخطأم ما في رمز CSRF. تحقق أن الكوكيز مفعل في متصفحك وأعد تحميل الصفحة. "
|
||||||
|
"إذا واجهتهذا الخطأ منجديد يرجى التبليغ."
|
||||||
|
|
||||||
msgid "You are not authorized."
|
msgid "You are not authorized."
|
||||||
msgstr "ليست لديك التصريحات اللازمة للقيام بذلك."
|
msgstr "ليست لديك التصريحات اللازمة للقيام بذلك."
|
||||||
@@ -651,13 +644,21 @@ msgstr "الرخصة الافتراضية للمقال"
|
|||||||
msgid "Save these settings"
|
msgid "Save these settings"
|
||||||
msgstr "احفظ هذه الإعدادات"
|
msgstr "احفظ هذه الإعدادات"
|
||||||
|
|
||||||
msgid "If you are browsing this site as a visitor, no data about you is collected."
|
msgid ""
|
||||||
|
"If you are browsing this site as a visitor, no data about you is collected."
|
||||||
msgstr "إذا كنت تصفح هذا الموقع كزائر ، لا يتم تجميع أي بيانات عنك."
|
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."
|
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 ""
|
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."
|
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 ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Blocklisted Emails"
|
msgid "Blocklisted Emails"
|
||||||
@@ -666,7 +667,10 @@ msgstr ""
|
|||||||
msgid "Email address"
|
msgid "Email address"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "The email address you wish to block. In order to block domains, you can use globbing syntax, for example '*@example.com' blocks all addresses from example.com"
|
msgid ""
|
||||||
|
"The email address you wish to block. In order to block domains, you can use "
|
||||||
|
"globbing syntax, for example '*@example.com' blocks all addresses from "
|
||||||
|
"example.com"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Note"
|
msgid "Note"
|
||||||
@@ -675,13 +679,17 @@ msgstr ""
|
|||||||
msgid "Notify the user?"
|
msgid "Notify the user?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Optional, shows a message to the user when they attempt to create an account with that address"
|
msgid ""
|
||||||
|
"Optional, shows a message to the user when they attempt to create an account "
|
||||||
|
"with that address"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Blocklisting notification"
|
msgid "Blocklisting notification"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "The message to be shown when the user attempts to create an account with this email address"
|
msgid ""
|
||||||
|
"The message to be shown when the user attempts to create an account with "
|
||||||
|
"this email address"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Add blocklisted address"
|
msgid "Add blocklisted address"
|
||||||
@@ -753,8 +761,11 @@ msgstr "العنوان الثانوي"
|
|||||||
msgid "Content"
|
msgid "Content"
|
||||||
msgstr "المحتوى"
|
msgstr "المحتوى"
|
||||||
|
|
||||||
msgid "You can upload media to your gallery, and then copy their Markdown code into your articles to insert them."
|
msgid ""
|
||||||
msgstr "يكنك رفع الوسائط للألبوم ومن ثم نسخ شفرة الماركداون في مقالاتك لإدراجها."
|
"You can upload media to your gallery, and then copy their Markdown code into "
|
||||||
|
"your articles to insert them."
|
||||||
|
msgstr ""
|
||||||
|
"يكنك رفع الوسائط للألبوم ومن ثم نسخ شفرة الماركداون في مقالاتك لإدراجها."
|
||||||
|
|
||||||
msgid "Upload media"
|
msgid "Upload media"
|
||||||
msgstr "تحميل وسائط"
|
msgstr "تحميل وسائط"
|
||||||
@@ -819,8 +830,12 @@ msgstr "لم أعد أرغب في دعم هذا"
|
|||||||
msgid "Boost"
|
msgid "Boost"
|
||||||
msgstr "رقّي"
|
msgstr "رقّي"
|
||||||
|
|
||||||
msgid "{0}Log in{1}, or {2}use your Fediverse account{3} to interact with this article"
|
msgid ""
|
||||||
msgstr "{0}قم بتسجيل الدخول{1} أو {2}استخدم حسابك على الفديفرس{3} إن كنت ترغب في التفاعل مع هذا المقال"
|
"{0}Log in{1}, or {2}use your Fediverse account{3} to interact with this "
|
||||||
|
"article"
|
||||||
|
msgstr ""
|
||||||
|
"{0}قم بتسجيل الدخول{1} أو {2}استخدم حسابك على الفديفرس{3} إن كنت ترغب في "
|
||||||
|
"التفاعل مع هذا المقال"
|
||||||
|
|
||||||
msgid "Comments"
|
msgid "Comments"
|
||||||
msgstr "التعليقات"
|
msgstr "التعليقات"
|
||||||
@@ -873,8 +888,15 @@ msgstr "تأكيد"
|
|||||||
msgid "Update password"
|
msgid "Update password"
|
||||||
msgstr "تحديث الكلمة السرية"
|
msgstr "تحديث الكلمة السرية"
|
||||||
|
|
||||||
msgid "We sent a mail to the address you gave us, with a link to reset your password."
|
msgid "Check your inbox!"
|
||||||
msgstr "لقد أرسلنا رسالة للعنوان الذي توصلنا به من طرفك تضمنرابط لإعادت تحديد كلمة المرور."
|
msgstr "تحقق من علبة الوارد الخاصة بك!"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"We sent a mail to the address you gave us, with a link to reset your "
|
||||||
|
"password."
|
||||||
|
msgstr ""
|
||||||
|
"لقد أرسلنا رسالة للعنوان الذي توصلنا به من طرفك تضمنرابط لإعادت تحديد كلمة "
|
||||||
|
"المرور."
|
||||||
|
|
||||||
msgid "Send password reset link"
|
msgid "Send password reset link"
|
||||||
msgstr "أرسل رابط إعادة تعيين الكلمة السرية"
|
msgstr "أرسل رابط إعادة تعيين الكلمة السرية"
|
||||||
@@ -882,7 +904,9 @@ msgstr "أرسل رابط إعادة تعيين الكلمة السرية"
|
|||||||
msgid "This token has expired"
|
msgid "This token has expired"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Please start the process again by clicking <a href=\"/password-reset\">here</a>."
|
msgid ""
|
||||||
|
"Please start the process again by clicking <a href=\"/password-reset\">here</"
|
||||||
|
"a>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "New Blog"
|
msgid "New Blog"
|
||||||
@@ -897,7 +921,8 @@ msgstr "انشاء مدونة"
|
|||||||
msgid "Edit \"{}\""
|
msgid "Edit \"{}\""
|
||||||
msgstr "تعديل \"{}\""
|
msgstr "تعديل \"{}\""
|
||||||
|
|
||||||
msgid "You can upload images to your gallery, to use them as blog icons, or banners."
|
msgid ""
|
||||||
|
"You can upload images to your gallery, to use them as blog icons, or banners."
|
||||||
msgstr "يمكن رفع الصور إلى ألبومك من أجل إستعمالها كأيقونة المدونة أو الشعار."
|
msgstr "يمكن رفع الصور إلى ألبومك من أجل إستعمالها كأيقونة المدونة أو الشعار."
|
||||||
|
|
||||||
msgid "Upload images"
|
msgid "Upload images"
|
||||||
@@ -963,8 +988,12 @@ msgstr "بلوم محرك لامركزي للمدونات."
|
|||||||
msgid "Authors can manage multiple blogs, each as its own website."
|
msgid "Authors can manage multiple blogs, each as its own website."
|
||||||
msgstr "يمكن للمحررين أن يديرو العديد من المدونات كل واحدة كموقع منفرد."
|
msgstr "يمكن للمحررين أن يديرو العديد من المدونات كل واحدة كموقع منفرد."
|
||||||
|
|
||||||
msgid "Articles are also visible on other Plume instances, and you can interact with them directly from other platforms like Mastodon."
|
msgid ""
|
||||||
msgstr "ستكون المقالات معروضة على مواقع بلومالأخرى حيث يمكنكم التفاعل معها مباشرة عبر أية منصة أخرى مثل ماستدون."
|
"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"
|
msgid "Read the detailed rules"
|
||||||
msgstr "إقرأ القواعد بالتفصيل"
|
msgstr "إقرأ القواعد بالتفصيل"
|
||||||
@@ -1043,4 +1072,3 @@ msgstr "نشرتحت هذا الترخيص"
|
|||||||
|
|
||||||
msgid "Article license"
|
msgid "Article license"
|
||||||
msgstr "رخصة المقال"
|
msgstr "رخصة المقال"
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user