Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6405ba57ee | |||
| d99b42582d | |||
| 92a386277b | |||
| 297d9fcf40 | |||
| ef70cb93e6 | |||
| efb76a3c17 | |||
| 197f0d7ecd | |||
| 3be842c653 | |||
| dabe904642 | |||
| 180e34b07c | |||
| 847d6f7fac | |||
| 6a3f210dfc | |||
| 73aa301d4a | |||
| b3d367b174 | |||
| edaccd1a31 | |||
| e1bd2eab75 | |||
| ac7a05b09a | |||
| 71e0a35e06 | |||
| c217e5e9b3 | |||
| 8ba0c17db5 | |||
| 4e43c676b4 | |||
| b834d1c282 | |||
| 506fe9955d | |||
| 02c528cae4 | |||
| 2a58835f92 | |||
| 5f8d6b8e0e | |||
| 3663bffe5c | |||
| 72464fb428 | |||
| 23049b638c | |||
| f3c05dae62 | |||
| e6bdeb7c4b | |||
| 458baf5f78 | |||
| 597778fd2f |
@@ -10,7 +10,7 @@ executors:
|
||||
type: boolean
|
||||
default: false
|
||||
docker:
|
||||
- image: plumeorg/plume-buildenv:v0.0.7
|
||||
- image: plumeorg/plume-buildenv:v0.0.9
|
||||
- image: <<#parameters.postgres>>circleci/postgres:9.6-alpine<</parameters.postgres>><<^parameters.postgres>>alpine:latest<</parameters.postgres>>
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
@@ -82,7 +82,7 @@ commands:
|
||||
type: string
|
||||
steps:
|
||||
- run: zip -0 ccov.zip `find . -name 'plume*.gc*' -o -name 'plm*.gc*'`
|
||||
- run: grcov ccov.zip -s . -t lcov --llvm --branch --ignore-not-existing --ignore-dir '/*' -o lcov.info
|
||||
- run: grcov ccov.zip -s . -t lcov --llvm --branch --ignore-not-existing --ignore '/*' -o lcov.info
|
||||
- run: bash <(curl -s https://codecov.io/bash) -f lcov.info -F <<parameters.type>>
|
||||
- run: find . -name 'plume*.gc*' -delete -o -name 'plm*.gc*' -delete
|
||||
- run: rm ccov.zip lcov.info
|
||||
@@ -226,6 +226,7 @@ jobs:
|
||||
steps:
|
||||
- restore_env:
|
||||
cache: none
|
||||
- run: cargo build
|
||||
- run: crowdin upload -b master
|
||||
|
||||
workflows:
|
||||
|
||||
@@ -4,11 +4,11 @@ ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
#install native/circleci/build dependancies
|
||||
RUN apt update &&\
|
||||
apt install -y --no-install-recommends git ssh tar gzip ca-certificates default-jre&&\
|
||||
apt install -y --no-install-recommends binutils-dev build-essential cmake curl gcc gettext git libcurl4-openssl-dev libdw-dev libelf-dev libiberty-dev libpq-dev libsqlite3-dev libssl-dev make openssl pkg-config postgresql postgresql-contrib python zlib1g-dev python3-pip zip unzip &&\
|
||||
apt install -y --no-install-recommends binutils-dev build-essential cmake curl gcc gettext git libcurl4-openssl-dev libdw-dev libelf-dev libiberty-dev libpq-dev libsqlite3-dev libssl-dev make openssl pkg-config postgresql postgresql-contrib python zlib1g-dev python3-pip zip unzip libclang-dev&&\
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
#install and configure rust
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-03-23 -y &&\
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2020-01-15 -y &&\
|
||||
rustup component add rustfmt clippy &&\
|
||||
rustup component add rust-std --target wasm32-unknown-unknown
|
||||
|
||||
|
||||
Generated
+2003
-1337
File diff suppressed because it is too large
Load Diff
+8
-6
@@ -1,8 +1,9 @@
|
||||
[package]
|
||||
authors = ["Plume contributors"]
|
||||
name = "plume"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
repository = "https://github.com/Plume-org/Plume"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
activitypub = "0.1.3"
|
||||
@@ -19,16 +20,16 @@ heck = "0.3.0"
|
||||
lettre = "0.9.2"
|
||||
lettre_email = "0.9.2"
|
||||
num_cpus = "1.10"
|
||||
rocket = "0.4.0"
|
||||
rocket_contrib = { version = "0.4.0", features = ["json"] }
|
||||
rocket = "0.4.2"
|
||||
rocket_contrib = { version = "0.4.2", features = ["json"] }
|
||||
rocket_i18n = { git = "https://github.com/Plume-org/rocket_i18n", rev = "e922afa7c366038b3433278c03b1456b346074f2" }
|
||||
rpassword = "4.0"
|
||||
runtime-fmt = "0.3.0"
|
||||
scheduled-thread-pool = "0.2.2"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_qs = "0.5"
|
||||
shrinkwraprs = "0.2.1"
|
||||
syntect = "3.3"
|
||||
validator = "0.8"
|
||||
validator_derive = "0.8"
|
||||
webfinger = "0.4.1"
|
||||
@@ -65,10 +66,10 @@ path = "plume-models"
|
||||
|
||||
[dependencies.rocket_csrf]
|
||||
git = "https://github.com/fdb-hiroshima/rocket_csrf"
|
||||
rev = "4a72ea2ec716cb0b26188fb00bccf2ef7d1e031c"
|
||||
rev = "29910f2829e7e590a540da3804336577b48c7b31"
|
||||
|
||||
[build-dependencies]
|
||||
ructe = "0.6.2"
|
||||
ructe = "0.9.0"
|
||||
rsass = "0.9"
|
||||
|
||||
[features]
|
||||
@@ -77,6 +78,7 @@ postgres = ["plume-models/postgres", "diesel/postgres"]
|
||||
sqlite = ["plume-models/sqlite", "diesel/sqlite"]
|
||||
debug-mailer = []
|
||||
test = []
|
||||
search-lindera = ["plume-models/search-lindera"]
|
||||
|
||||
[workspace]
|
||||
members = ["plume-api", "plume-cli", "plume-models", "plume-common", "plume-front", "plume-macro"]
|
||||
|
||||
+2
-1
@@ -10,7 +10,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
make \
|
||||
openssl \
|
||||
libssl-dev
|
||||
libssl-dev \
|
||||
clang
|
||||
|
||||
WORKDIR /scratch
|
||||
COPY script/wasm-deps.sh .
|
||||
|
||||
@@ -17,3 +17,12 @@ $text-color: $white;
|
||||
$primary: $purple;
|
||||
$primary-text-color: $white; // text color on primary background (buttons for instance)
|
||||
$success-color: $blue;
|
||||
|
||||
//Code Highlighting
|
||||
|
||||
$code-keyword-color: #f79ac1;
|
||||
$code-source-color: #a6f0ab;
|
||||
$code-constant-color: #dfec84;
|
||||
$code-operator-color: #eddf95;
|
||||
$code-string-color: #f2ae60;
|
||||
$code-comment-color: #a3b4f9;
|
||||
|
||||
@@ -190,7 +190,28 @@ p.error {
|
||||
|
||||
background: $gray;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
footer.authors {
|
||||
div {
|
||||
float: left;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
.likes { color: $red; }
|
||||
.reshares { color: $primary; }
|
||||
|
||||
span.likes, span.resahres {
|
||||
font-family: "Route159",serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
svg.feather {
|
||||
width: 0.85em;
|
||||
height: 0.85em;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
> * {
|
||||
margin: 20px;
|
||||
@@ -469,93 +490,6 @@ figure {
|
||||
|
||||
/// Small screens
|
||||
@media screen and (max-width: 600px) {
|
||||
@keyframes menuOpening {
|
||||
from {
|
||||
transform: scaleX(0);
|
||||
transform-origin: left;
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scaleX(1);
|
||||
transform-origin: left;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
body > header {
|
||||
flex-direction: column;
|
||||
|
||||
nav#menu {
|
||||
display: inline-flex;
|
||||
z-index: 21;
|
||||
}
|
||||
|
||||
#content {
|
||||
display: none;
|
||||
appearance: none;
|
||||
text-align: center;
|
||||
z-index: 20;
|
||||
}
|
||||
}
|
||||
|
||||
body > header:focus-within #content, #content.show {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
animation: 0.2s menuOpening;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
transform: skewX(-10deg);
|
||||
top: 0;
|
||||
left: -20%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
z-index: -10;
|
||||
|
||||
background: $primary;
|
||||
}
|
||||
|
||||
> nav {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
padding: 1rem 1.5rem;
|
||||
color: $background;
|
||||
font-size: 1.4em;
|
||||
font-weight: 300;
|
||||
|
||||
&.title { font-size: 1.8em; }
|
||||
|
||||
> *:first-child { width: 3rem; }
|
||||
> img:first-child { height: 3rem; }
|
||||
> *:last-child { margin-left: 1rem; }
|
||||
> nav hr {
|
||||
display: block;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
border: solid $background 0.1rem;
|
||||
}
|
||||
.mobile-label { display: initial; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main .article-meta {
|
||||
> *, .comments {
|
||||
margin: 0 5%;
|
||||
@@ -623,3 +557,28 @@ figure {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//highlighting
|
||||
code {
|
||||
.constant{
|
||||
color: $code-constant-color;
|
||||
}
|
||||
.string{
|
||||
color: $code-string-color;
|
||||
}
|
||||
.keyword.type,.keyword.control,.type{
|
||||
color: $code-keyword-color;
|
||||
}
|
||||
.keyword.operator{
|
||||
color: $code-operator-color;
|
||||
}
|
||||
.source{
|
||||
color: $code-source-color;
|
||||
}
|
||||
.comment{
|
||||
color: $code-comment-color;
|
||||
}
|
||||
.function{
|
||||
color:inherit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,96 @@ body > header {
|
||||
}
|
||||
}
|
||||
|
||||
/// Small screens
|
||||
@media screen and (max-width: 600px) {
|
||||
@keyframes menuOpening {
|
||||
from {
|
||||
transform: scaleX(0);
|
||||
transform-origin: left;
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: scaleX(1);
|
||||
transform-origin: left;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
body > header {
|
||||
flex-direction: column;
|
||||
|
||||
nav#menu {
|
||||
display: inline-flex;
|
||||
z-index: 21;
|
||||
}
|
||||
|
||||
#content {
|
||||
display: none;
|
||||
appearance: none;
|
||||
text-align: center;
|
||||
z-index: 20;
|
||||
}
|
||||
}
|
||||
|
||||
body > header:focus-within #content, #content.show {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
animation: 0.2s menuOpening;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
transform: skewX(-10deg);
|
||||
top: 0;
|
||||
left: -20%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
z-index: -10;
|
||||
|
||||
background: $primary;
|
||||
}
|
||||
|
||||
> nav {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
padding: 1rem 1.5rem;
|
||||
color: $background;
|
||||
font-size: 1.4em;
|
||||
font-weight: 300;
|
||||
|
||||
&.title { font-size: 1.8em; }
|
||||
|
||||
> *:first-child { width: 3rem; }
|
||||
> img:first-child { height: 3rem; }
|
||||
> *:last-child { margin-left: 1rem; }
|
||||
> nav hr {
|
||||
display: block;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
border: solid $background 0.1rem;
|
||||
}
|
||||
.mobile-label { display: initial; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Only enable label animations on large screens */
|
||||
@media screen and (min-width: 600px) {
|
||||
header nav a {
|
||||
|
||||
@@ -27,3 +27,11 @@ $margin: 0 $horizontal-margin;
|
||||
$route159: "Route159", serif;
|
||||
$playfair: "Playfair Display", serif;
|
||||
$lora: "Lora", serif;
|
||||
|
||||
//Code Highlighting
|
||||
$code-keyword-color: #45244a;
|
||||
$code-source-color: #4c588c;
|
||||
$code-constant-color: scale-color(magenta,$lightness:-5%);
|
||||
$code-operator-color: scale-color($code-source-color,$lightness:-5%);
|
||||
$code-string-color: #8a571c;
|
||||
$code-comment-color: #1c4c8a;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
extern crate rsass;
|
||||
extern crate ructe;
|
||||
use rsass;
|
||||
|
||||
use ructe::Ructe;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::{env, ffi::OsStr, fs::*, io::Write, path::*};
|
||||
use std::{ffi::OsStr, fs::*, io::Write, path::*};
|
||||
|
||||
fn compute_static_hash() -> String {
|
||||
//"find static/ -type f ! -path 'static/media/*' | sort | xargs stat -c'%n %Y' | openssl dgst -r"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
|
||||
drop table email_blocklist;
|
||||
@@ -0,0 +1,6 @@
|
||||
-- Your SQL goes here
|
||||
CREATE TABLE email_blocklist(id SERIAL PRIMARY KEY,
|
||||
email_address TEXT UNIQUE,
|
||||
note TEXT,
|
||||
notify_user BOOLEAN DEFAULT FALSE,
|
||||
notification_text TEXT);
|
||||
@@ -0,0 +1,3 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
|
||||
drop table email_blocklist;
|
||||
@@ -0,0 +1,6 @@
|
||||
-- Your SQL goes here
|
||||
CREATE TABLE email_blocklist(id INTEGER PRIMARY KEY,
|
||||
email_address TEXT UNIQUE,
|
||||
note TEXT,
|
||||
notify_user BOOLEAN DEFAULT FALSE,
|
||||
notification_text TEXT);
|
||||
@@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "plume-api"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
authors = ["Plume contributors"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
serde = "1.0"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "plume-cli"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
authors = ["Plume contributors"]
|
||||
edition = "2018"
|
||||
|
||||
[[bin]]
|
||||
name = "plm"
|
||||
@@ -22,3 +23,4 @@ path = "../plume-models"
|
||||
[features]
|
||||
postgres = ["plume-models/postgres", "diesel/postgres"]
|
||||
sqlite = ["plume-models/sqlite", "diesel/sqlite"]
|
||||
search-lindera = ["plume-models/search-lindera"]
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
extern crate clap;
|
||||
extern crate diesel;
|
||||
extern crate dotenv;
|
||||
extern crate plume_models;
|
||||
extern crate rpassword;
|
||||
use dotenv;
|
||||
|
||||
use clap::App;
|
||||
use diesel::Connection;
|
||||
|
||||
@@ -82,7 +82,7 @@ fn init<'a>(args: &ArgMatches<'a>, conn: &Connection) {
|
||||
}
|
||||
};
|
||||
if can_do || force {
|
||||
let searcher = Searcher::create(&path).unwrap();
|
||||
let searcher = Searcher::create(&path, &CONFIG.search_tokenizers).unwrap();
|
||||
refill(args, conn, Some(searcher));
|
||||
} else {
|
||||
eprintln!(
|
||||
@@ -98,7 +98,8 @@ fn refill<'a>(args: &ArgMatches<'a>, conn: &Connection, searcher: Option<Searche
|
||||
Some(path) => Path::new(path).join("search_index"),
|
||||
None => Path::new(&CONFIG.search_index).to_path_buf(),
|
||||
};
|
||||
let searcher = searcher.unwrap_or_else(|| Searcher::open(&path).unwrap());
|
||||
let searcher =
|
||||
searcher.unwrap_or_else(|| Searcher::open(&path, &CONFIG.search_tokenizers).unwrap());
|
||||
|
||||
searcher.fill(conn).expect("Couldn't import post");
|
||||
println!("Commiting result");
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "plume-common"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
authors = ["Plume contributors"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
activitypub = "0.1.1"
|
||||
@@ -19,7 +20,9 @@ serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
shrinkwraprs = "0.2.1"
|
||||
syntect = "3.3"
|
||||
tokio = "0.1.22"
|
||||
regex-syntax = { version = "0.6.17", default-features = false, features = ["unicode-perl"] }
|
||||
|
||||
[dependencies.chrono]
|
||||
features = ["serde"]
|
||||
|
||||
@@ -64,7 +64,7 @@ impl<T> ActivityStream<T> {
|
||||
}
|
||||
|
||||
impl<'r, O: Object> Responder<'r> for ActivityStream<O> {
|
||||
fn respond_to(self, request: &Request) -> Result<Response<'r>, Status> {
|
||||
fn respond_to(self, request: &Request<'_>) -> Result<Response<'r>, Status> {
|
||||
let mut json = serde_json::to_value(&self.0).map_err(|_| Status::InternalServerError)?;
|
||||
json["@context"] = context();
|
||||
serde_json::to_string(&json).respond_to(request).map(|r| {
|
||||
|
||||
@@ -5,8 +5,8 @@ use reqwest::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE, DATE, USER_A
|
||||
use std::ops::Deref;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use activity_pub::sign::Signer;
|
||||
use activity_pub::{ap_accept_header, AP_CONTENT_TYPE};
|
||||
use crate::activity_pub::sign::Signer;
|
||||
use crate::activity_pub::{ap_accept_header, AP_CONTENT_TYPE};
|
||||
|
||||
const PLUME_USER_AGENT: &str = concat!("Plume/", env!("CARGO_PKG_VERSION"));
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ impl SignatureValidity {
|
||||
|
||||
pub fn verify_http_headers<S: Signer + ::std::fmt::Debug>(
|
||||
sender: &S,
|
||||
all_headers: &HeaderMap,
|
||||
all_headers: &HeaderMap<'_>,
|
||||
data: &request::Digest,
|
||||
) -> SignatureValidity {
|
||||
let sig_header = all_headers.get_one("Signature");
|
||||
|
||||
+4
-14
@@ -1,26 +1,16 @@
|
||||
#![feature(custom_attribute, associated_type_defaults)]
|
||||
#![feature(associated_type_defaults)]
|
||||
|
||||
extern crate activitypub;
|
||||
#[macro_use]
|
||||
extern crate activitystreams_derive;
|
||||
extern crate activitystreams_traits;
|
||||
extern crate array_tool;
|
||||
extern crate base64;
|
||||
extern crate chrono;
|
||||
extern crate heck;
|
||||
extern crate hex;
|
||||
extern crate openssl;
|
||||
extern crate pulldown_cmark;
|
||||
extern crate reqwest;
|
||||
extern crate rocket;
|
||||
extern crate serde;
|
||||
use activitystreams_traits;
|
||||
|
||||
use serde;
|
||||
#[macro_use]
|
||||
extern crate shrinkwraprs;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate serde_json;
|
||||
extern crate tokio;
|
||||
|
||||
pub mod activity_pub;
|
||||
pub mod utils;
|
||||
|
||||
+98
-18
@@ -1,12 +1,15 @@
|
||||
use heck::CamelCase;
|
||||
use openssl::rand::rand_bytes;
|
||||
use pulldown_cmark::{html, Event, Options, Parser, Tag};
|
||||
use regex_syntax::is_word_character;
|
||||
use rocket::{
|
||||
http::uri::Uri,
|
||||
response::{Flash, Redirect},
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashSet;
|
||||
use syntect::html::ClassedHTMLGenerator;
|
||||
use syntect::parsing::SyntaxSet;
|
||||
|
||||
/// Generates an hexadecimal representation of 32 bytes of random data
|
||||
pub fn random_hex() -> String {
|
||||
@@ -46,7 +49,7 @@ enum State {
|
||||
Ready,
|
||||
}
|
||||
|
||||
fn to_inline(tag: Tag) -> Tag {
|
||||
fn to_inline(tag: Tag<'_>) -> Tag<'_> {
|
||||
match tag {
|
||||
Tag::Header(_) | Tag::Table(_) | Tag::TableHead | Tag::TableRow | Tag::TableCell => {
|
||||
Tag::Paragraph
|
||||
@@ -55,7 +58,54 @@ fn to_inline(tag: Tag) -> Tag {
|
||||
t => t,
|
||||
}
|
||||
}
|
||||
|
||||
struct HighlighterContext {
|
||||
content: Vec<String>,
|
||||
}
|
||||
fn highlight_code<'a>(
|
||||
context: &mut Option<HighlighterContext>,
|
||||
evt: Event<'a>,
|
||||
) -> Option<Vec<Event<'a>>> {
|
||||
match evt {
|
||||
Event::Start(Tag::CodeBlock(lang)) => {
|
||||
if lang.is_empty() {
|
||||
Some(vec![Event::Start(Tag::CodeBlock(lang))])
|
||||
} else {
|
||||
*context = Some(HighlighterContext { content: vec![] });
|
||||
Some(vec![Event::Start(Tag::CodeBlock(lang))])
|
||||
}
|
||||
}
|
||||
Event::End(Tag::CodeBlock(x)) => {
|
||||
let mut result = vec![];
|
||||
if let Some(ctx) = context.take() {
|
||||
let syntax_set = SyntaxSet::load_defaults_newlines();
|
||||
let syntax = syntax_set.find_syntax_by_token(&x).unwrap_or_else(|| {
|
||||
syntax_set
|
||||
.find_syntax_by_name(&x)
|
||||
.unwrap_or_else(|| syntax_set.find_syntax_plain_text())
|
||||
});
|
||||
let mut html = ClassedHTMLGenerator::new(&syntax, &syntax_set);
|
||||
for line in ctx.content {
|
||||
html.parse_html_for_line(&line);
|
||||
}
|
||||
let q = html.finalize();
|
||||
result.push(Event::Html(q.into()));
|
||||
}
|
||||
result.push(Event::End(Tag::CodeBlock(x)));
|
||||
*context = None;
|
||||
Some(result)
|
||||
}
|
||||
Event::Text(t) => {
|
||||
if let Some(mut c) = context.take() {
|
||||
c.content.push(t.to_string());
|
||||
*context = Some(c);
|
||||
Some(vec![])
|
||||
} else {
|
||||
Some(vec![Event::Text(t)])
|
||||
}
|
||||
}
|
||||
_ => Some(vec![evt]),
|
||||
}
|
||||
}
|
||||
fn flatten_text<'a>(state: &mut Option<String>, evt: Event<'a>) -> Option<Vec<Event<'a>>> {
|
||||
let (s, res) = match evt {
|
||||
Event::Text(txt) => match state.take() {
|
||||
@@ -97,7 +147,7 @@ fn inline_tags<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
pub type MediaProcessor<'a> = Box<'a + Fn(i32) -> Option<(String, Option<String>)>>;
|
||||
pub type MediaProcessor<'a> = Box<dyn 'a + Fn(i32) -> Option<(String, Option<String>)>>;
|
||||
|
||||
fn process_image<'a, 'b>(
|
||||
evt: Event<'a>,
|
||||
@@ -108,9 +158,7 @@ fn process_image<'a, 'b>(
|
||||
match evt {
|
||||
Event::Start(Tag::Image(id, title)) => {
|
||||
if let Some((url, cw)) = id.parse::<i32>().ok().and_then(processor.as_ref()) {
|
||||
if inline || cw.is_none() {
|
||||
Event::Start(Tag::Image(Cow::Owned(url), title))
|
||||
} else {
|
||||
if let (Some(cw), false) = (cw, inline) {
|
||||
// there is a cw, and where are not inline
|
||||
Event::Html(Cow::Owned(format!(
|
||||
r#"<label for="postcontent-cw-{id}">
|
||||
@@ -121,9 +169,11 @@ fn process_image<'a, 'b>(
|
||||
</span>
|
||||
<img src="{url}" alt=""#,
|
||||
id = random_hex(),
|
||||
cw = cw.unwrap(),
|
||||
cw = cw,
|
||||
url = url
|
||||
)))
|
||||
} else {
|
||||
Event::Start(Tag::Image(Cow::Owned(url), title))
|
||||
}
|
||||
} else {
|
||||
Event::Start(Tag::Image(id, title))
|
||||
@@ -151,6 +201,12 @@ fn process_image<'a, 'b>(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct DocumentContext {
|
||||
in_code: bool,
|
||||
in_link: bool,
|
||||
}
|
||||
|
||||
/// Returns (HTML, mentions, hashtags)
|
||||
pub fn md_to_html<'a>(
|
||||
md: &str,
|
||||
@@ -165,14 +221,32 @@ pub fn md_to_html<'a>(
|
||||
};
|
||||
let parser = Parser::new_ext(md, Options::all());
|
||||
|
||||
let (parser, mentions, hashtags): (Vec<Event>, Vec<String>, Vec<String>) = parser
|
||||
let (parser, mentions, hashtags): (Vec<Event<'_>>, Vec<String>, Vec<String>) = parser
|
||||
// Flatten text because pulldown_cmark break #hashtag in two individual text elements
|
||||
.scan(None, flatten_text)
|
||||
.flat_map(IntoIterator::into_iter)
|
||||
.flatten()
|
||||
.scan(None, highlight_code)
|
||||
.flatten()
|
||||
.map(|evt| process_image(evt, inline, &media_processor))
|
||||
// Ignore headings, images, and tables if inline = true
|
||||
.scan((vec![], inline), inline_tags)
|
||||
.map(|evt| match evt {
|
||||
.scan(&mut DocumentContext::default(), |ctx, evt| match evt {
|
||||
Event::Start(Tag::CodeBlock(_)) | Event::Start(Tag::Code) => {
|
||||
ctx.in_code = true;
|
||||
Some((vec![evt], vec![], vec![]))
|
||||
}
|
||||
Event::End(Tag::CodeBlock(_)) | Event::End(Tag::Code) => {
|
||||
ctx.in_code = false;
|
||||
Some((vec![evt], vec![], vec![]))
|
||||
}
|
||||
Event::Start(Tag::Link(_, _)) => {
|
||||
ctx.in_link = true;
|
||||
Some((vec![evt], vec![], vec![]))
|
||||
}
|
||||
Event::End(Tag::Link(_, _)) => {
|
||||
ctx.in_link = false;
|
||||
Some((vec![evt], vec![], vec![]))
|
||||
}
|
||||
Event::Text(txt) => {
|
||||
let (evts, _, _, _, new_mentions, new_hashtags) = txt.chars().fold(
|
||||
(vec![], State::Ready, String::new(), 0, vec![], vec![]),
|
||||
@@ -188,7 +262,7 @@ pub fn md_to_html<'a>(
|
||||
text_acc.push(c)
|
||||
}
|
||||
let mention = text_acc;
|
||||
let short_mention = mention.splitn(1, '@').nth(0).unwrap_or("");
|
||||
let short_mention = mention.splitn(1, '@').next().unwrap_or("");
|
||||
let link = Tag::Link(
|
||||
format!("{}@/{}/", base_url, &mention).into(),
|
||||
short_mention.to_owned().into(),
|
||||
@@ -210,7 +284,7 @@ pub fn md_to_html<'a>(
|
||||
}
|
||||
}
|
||||
State::Hashtag => {
|
||||
let char_matches = c.is_alphanumeric() || "-_".contains(c);
|
||||
let char_matches = c == '-' || is_word_character(c);
|
||||
if char_matches && (n < (txt.chars().count() - 1)) {
|
||||
text_acc.push(c);
|
||||
(events, State::Hashtag, text_acc, n + 1, mentions, hashtags)
|
||||
@@ -241,7 +315,7 @@ pub fn md_to_html<'a>(
|
||||
}
|
||||
}
|
||||
State::Ready => {
|
||||
if c == '@' {
|
||||
if !ctx.in_code && !ctx.in_link && c == '@' {
|
||||
events.push(Event::Text(text_acc.into()));
|
||||
(
|
||||
events,
|
||||
@@ -251,7 +325,7 @@ pub fn md_to_html<'a>(
|
||||
mentions,
|
||||
hashtags,
|
||||
)
|
||||
} else if c == '#' {
|
||||
} else if !ctx.in_code && !ctx.in_link && c == '#' {
|
||||
events.push(Event::Text(text_acc.into()));
|
||||
(
|
||||
events,
|
||||
@@ -296,9 +370,9 @@ pub fn md_to_html<'a>(
|
||||
}
|
||||
},
|
||||
);
|
||||
(evts, new_mentions, new_hashtags)
|
||||
Some((evts, new_mentions, new_hashtags))
|
||||
}
|
||||
_ => (vec![evt], vec![], vec![]),
|
||||
_ => Some((vec![evt], vec![], vec![])),
|
||||
})
|
||||
.fold(
|
||||
(vec![], vec![], vec![]),
|
||||
@@ -335,9 +409,12 @@ mod tests {
|
||||
("mention at @end", vec!["end"]),
|
||||
("between parenthesis (@test)", vec!["test"]),
|
||||
("with some punctuation @test!", vec!["test"]),
|
||||
(" @spaces ", vec!["spaces"]),
|
||||
(" @spaces ", vec!["spaces"]),
|
||||
("@is_a@mention", vec!["is_a@mention"]),
|
||||
("not_a@mention", vec![]),
|
||||
("`@helo`", vec![]),
|
||||
("```\n@hello\n```", vec![]),
|
||||
("[@atmark in link](https://example.org/)", vec![]),
|
||||
];
|
||||
|
||||
for (md, mentions) in tests {
|
||||
@@ -361,8 +438,11 @@ mod tests {
|
||||
("hashtag at #end", vec!["end"]),
|
||||
("between parenthesis (#test)", vec!["test"]),
|
||||
("with some punctuation #test!", vec!["test"]),
|
||||
(" #spaces ", vec!["spaces"]),
|
||||
(" #spaces ", vec!["spaces"]),
|
||||
("not_a#hashtag", vec![]),
|
||||
("#نرمافزار_آزاد", vec!["نرمافزار_آزاد"]),
|
||||
("[#hash in link](https://example.org/)", vec![]),
|
||||
("#zwsp\u{200b}inhash", vec!["zwsp"]),
|
||||
];
|
||||
|
||||
for (md, mentions) in tests {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "plume-front"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
authors = ["Plume contributors"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
stdweb = "=0.4.18"
|
||||
@@ -11,4 +12,4 @@ gettext-macros = { git = "https://github.com/Plume-org/gettext-macros/", rev = "
|
||||
gettext-utils = { git = "https://github.com/Plume-org/gettext-macros/", rev = "a7c605f7edd6bfbfbfe7778026bfefd88d82db10" }
|
||||
lazy_static = "1.3"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::CATALOG;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
use std::sync::Mutex;
|
||||
@@ -5,7 +6,6 @@ use stdweb::{
|
||||
unstable::{TryFrom, TryInto},
|
||||
web::{event::*, html_element::*, *},
|
||||
};
|
||||
use CATALOG;
|
||||
|
||||
macro_rules! mv {
|
||||
( $( $var:ident ),* => $exp:expr ) => {
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
#![recursion_limit = "128"]
|
||||
#![feature(decl_macro, proc_macro_hygiene, try_trait)]
|
||||
|
||||
extern crate gettext;
|
||||
#[macro_use]
|
||||
extern crate gettext_macros;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate stdweb;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
use stdweb::web::{event::*, *};
|
||||
|
||||
init_i18n!(
|
||||
@@ -22,6 +19,7 @@ init_i18n!(
|
||||
en,
|
||||
eo,
|
||||
es,
|
||||
fa,
|
||||
fr,
|
||||
gl,
|
||||
hi,
|
||||
@@ -48,6 +46,9 @@ lazy_static! {
|
||||
let lang = js! { return navigator.language }.into_string().unwrap();
|
||||
let lang = lang.splitn(2, '-').next().unwrap_or("en");
|
||||
|
||||
// Force a language (Add .env setting)
|
||||
//let lang = "fa"; ???
|
||||
|
||||
let english_position = catalogs
|
||||
.iter()
|
||||
.position(|(language_code, _)| *language_code == "en")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "plume-macro"
|
||||
version = "0.1.0"
|
||||
version = "0.4.0"
|
||||
authors = ["Trinity Pointard <trinity.pointard@insa-rennes.fr>"]
|
||||
edition = "2018"
|
||||
description = "Plume procedural macros"
|
||||
|
||||
+11
-14
@@ -1,8 +1,7 @@
|
||||
#![recursion_limit = "128"]
|
||||
extern crate proc_macro;
|
||||
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
extern crate syn;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
@@ -103,19 +102,17 @@ fn file_to_migration(file: &str) -> TokenStream2 {
|
||||
acc.push_str(line);
|
||||
acc.push('\n');
|
||||
}
|
||||
} else if line.starts_with("--#!") {
|
||||
acc.push_str(&line[4..]);
|
||||
acc.push('\n');
|
||||
} else if line.starts_with("--") {
|
||||
continue;
|
||||
} else {
|
||||
if line.starts_with("--#!") {
|
||||
acc.push_str(&line[4..]);
|
||||
acc.push('\n');
|
||||
} else if line.starts_with("--") {
|
||||
continue;
|
||||
} else {
|
||||
let func: TokenStream2 = trampoline(TokenStream::from_str(&acc).unwrap().into());
|
||||
actions.push(quote!(Action::Function(&#func)));
|
||||
sql = true;
|
||||
acc = line.to_string();
|
||||
acc.push('\n');
|
||||
}
|
||||
let func: TokenStream2 = trampoline(TokenStream::from_str(&acc).unwrap().into());
|
||||
actions.push(quote!(Action::Function(&#func)));
|
||||
sql = true;
|
||||
acc = line.to_string();
|
||||
acc.push('\n');
|
||||
}
|
||||
}
|
||||
if !acc.trim().is_empty() {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "plume-models"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
authors = ["Plume contributors"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
activitypub = "0.1.1"
|
||||
@@ -11,7 +12,7 @@ bcrypt = "0.5"
|
||||
guid-create = "0.1"
|
||||
heck = "0.3.0"
|
||||
itertools = "0.8.0"
|
||||
lazy_static = "*"
|
||||
lazy_static = "1.0"
|
||||
migrations_internals= "1.4.0"
|
||||
openssl = "0.10.22"
|
||||
rocket = "0.4.0"
|
||||
@@ -21,13 +22,15 @@ scheduled-thread-pool = "0.2.2"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
tantivy = "0.10.1"
|
||||
tantivy = "0.12.0"
|
||||
url = "2.1"
|
||||
walkdir = "2.2"
|
||||
webfinger = "0.4.1"
|
||||
whatlang = "0.7.1"
|
||||
shrinkwraprs = "0.2.1"
|
||||
diesel-derive-newtype = "0.1.2"
|
||||
glob = "0.3.0"
|
||||
lindera-tantivy = { version = "0.1.2", optional = true }
|
||||
|
||||
[dependencies.chrono]
|
||||
features = ["serde"]
|
||||
@@ -52,3 +55,4 @@ diesel_migrations = "1.3.0"
|
||||
[features]
|
||||
postgres = ["diesel/postgres", "plume-macro/postgres" ]
|
||||
sqlite = ["diesel/sqlite", "plume-macro/sqlite" ]
|
||||
search-lindera = ["lindera-tantivy"]
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use crate::users::User;
|
||||
use rocket::{
|
||||
http::Status,
|
||||
request::{self, FromRequest, Request},
|
||||
Outcome,
|
||||
};
|
||||
|
||||
use users::User;
|
||||
|
||||
/// Wrapper around User to use as a request guard on pages reserved to admins.
|
||||
pub struct Admin(pub User);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::{db_conn::DbConn, schema::api_tokens, Error, Result};
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
use rocket::{
|
||||
@@ -6,10 +7,6 @@ use rocket::{
|
||||
Outcome,
|
||||
};
|
||||
|
||||
use db_conn::DbConn;
|
||||
use schema::api_tokens;
|
||||
use {Error, Result};
|
||||
|
||||
#[derive(Clone, Queryable)]
|
||||
pub struct ApiToken {
|
||||
pub id: i32,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use crate::{schema::apps, Error, Result};
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
|
||||
use schema::apps;
|
||||
use {Error, Result};
|
||||
|
||||
#[derive(Clone, Queryable, Serialize)]
|
||||
pub struct App {
|
||||
pub id: i32,
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
use crate::{schema::email_blocklist, Connection, Error, Result};
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, TextExpressionMethods};
|
||||
use glob::Pattern;
|
||||
|
||||
#[derive(Clone, Queryable, Identifiable)]
|
||||
#[table_name = "email_blocklist"]
|
||||
pub struct BlocklistedEmail {
|
||||
pub id: i32,
|
||||
pub email_address: String,
|
||||
pub note: String,
|
||||
pub notify_user: bool,
|
||||
pub notification_text: String,
|
||||
}
|
||||
|
||||
#[derive(Insertable, FromForm)]
|
||||
#[table_name = "email_blocklist"]
|
||||
pub struct NewBlocklistedEmail {
|
||||
pub email_address: String,
|
||||
pub note: String,
|
||||
pub notify_user: bool,
|
||||
pub notification_text: String,
|
||||
}
|
||||
|
||||
impl BlocklistedEmail {
|
||||
insert!(email_blocklist, NewBlocklistedEmail);
|
||||
get!(email_blocklist);
|
||||
find_by!(email_blocklist, find_by_id, id as i32);
|
||||
pub fn delete_entries(conn: &Connection, ids: Vec<i32>) -> Result<bool> {
|
||||
use diesel::delete;
|
||||
for i in ids {
|
||||
let be: BlocklistedEmail = BlocklistedEmail::find_by_id(&conn, i)?;
|
||||
delete(&be).execute(conn)?;
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
pub fn find_for_domain(conn: &Connection, domain: &str) -> Result<Vec<BlocklistedEmail>> {
|
||||
let effective = format!("%@{}", domain);
|
||||
email_blocklist::table
|
||||
.filter(email_blocklist::email_address.like(effective))
|
||||
.load::<BlocklistedEmail>(conn)
|
||||
.map_err(Error::from)
|
||||
}
|
||||
pub fn matches_blocklist(conn: &Connection, email: &str) -> Result<Option<BlocklistedEmail>> {
|
||||
let mut result = email_blocklist::table.load::<BlocklistedEmail>(conn)?;
|
||||
for i in result.drain(..) {
|
||||
if let Ok(x) = Pattern::new(&i.email_address) {
|
||||
if x.matches(email) {
|
||||
return Ok(Some(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
pub fn page(conn: &Connection, (min, max): (i32, i32)) -> Result<Vec<BlocklistedEmail>> {
|
||||
email_blocklist::table
|
||||
.offset(min.into())
|
||||
.limit((max - min).into())
|
||||
.load::<BlocklistedEmail>(conn)
|
||||
.map_err(Error::from)
|
||||
}
|
||||
pub fn count(conn: &Connection) -> Result<i64> {
|
||||
email_blocklist::table
|
||||
.count()
|
||||
.get_result(conn)
|
||||
.map_err(Error::from)
|
||||
}
|
||||
pub fn pattern_errors(pat: &str) -> Option<glob::PatternError> {
|
||||
let c = Pattern::new(pat);
|
||||
c.err()
|
||||
}
|
||||
pub fn new(
|
||||
conn: &Connection,
|
||||
pattern: &str,
|
||||
note: &str,
|
||||
show_notification: bool,
|
||||
notification_text: &str,
|
||||
) -> Result<BlocklistedEmail> {
|
||||
let c = NewBlocklistedEmail {
|
||||
email_address: pattern.to_owned(),
|
||||
note: note.to_owned(),
|
||||
notify_user: show_notification,
|
||||
notification_text: notification_text.to_owned(),
|
||||
};
|
||||
BlocklistedEmail::insert(conn, c)
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::{instance::tests as instance_tests, tests::rockets, Connection as Conn};
|
||||
use diesel::Connection;
|
||||
|
||||
pub(crate) fn fill_database(conn: &Conn) -> Vec<BlocklistedEmail> {
|
||||
instance_tests::fill_database(conn);
|
||||
let domainblock =
|
||||
BlocklistedEmail::new(conn, "*@bad-actor.com", "Mean spammers", false, "").unwrap();
|
||||
let userblock = BlocklistedEmail::new(
|
||||
conn,
|
||||
"spammer@lax-administration.com",
|
||||
"Decent enough domain, but this user is a problem.",
|
||||
true,
|
||||
"Stop it please",
|
||||
)
|
||||
.unwrap();
|
||||
vec![domainblock, userblock]
|
||||
}
|
||||
#[test]
|
||||
fn test_match() {
|
||||
let r = rockets();
|
||||
let conn = &*r.conn;
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let various = fill_database(conn);
|
||||
let match1 = "user1@bad-actor.com";
|
||||
let match2 = "spammer@lax-administration.com";
|
||||
let no_match = "happy-user@lax-administration.com";
|
||||
assert_eq!(
|
||||
BlocklistedEmail::matches_blocklist(conn, match1)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.id,
|
||||
various[0].id
|
||||
);
|
||||
assert_eq!(
|
||||
BlocklistedEmail::matches_blocklist(conn, match2)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.id,
|
||||
various[1].id
|
||||
);
|
||||
assert_eq!(
|
||||
BlocklistedEmail::matches_blocklist(conn, no_match)
|
||||
.unwrap()
|
||||
.is_none(),
|
||||
true
|
||||
);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
use crate::{schema::blog_authors, Error, Result};
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
|
||||
use schema::blog_authors;
|
||||
use {Error, Result};
|
||||
|
||||
#[derive(Clone, Queryable, Identifiable)]
|
||||
pub struct BlogAuthor {
|
||||
pub id: i32,
|
||||
|
||||
+25
-25
@@ -1,3 +1,7 @@
|
||||
use crate::{
|
||||
ap_url, instance::*, medias::Media, posts::Post, safe_string::SafeString, schema::blogs,
|
||||
search::Searcher, users::User, Connection, Error, PlumeRocket, Result, ITEMS_PER_PAGE,
|
||||
};
|
||||
use activitypub::{
|
||||
actor::Group,
|
||||
collection::{OrderedCollection, OrderedCollectionPage},
|
||||
@@ -12,22 +16,13 @@ use openssl::{
|
||||
rsa::Rsa,
|
||||
sign::{Signer, Verifier},
|
||||
};
|
||||
use serde_json;
|
||||
use url::Url;
|
||||
use webfinger::*;
|
||||
|
||||
use instance::*;
|
||||
use medias::Media;
|
||||
use plume_common::activity_pub::{
|
||||
inbox::{AsActor, FromId},
|
||||
sign, ActivityStream, ApSignature, Id, IntoId, PublicKey, Source,
|
||||
};
|
||||
use posts::Post;
|
||||
use safe_string::SafeString;
|
||||
use schema::blogs;
|
||||
use search::Searcher;
|
||||
use users::User;
|
||||
use {ap_url, Connection, Error, PlumeRocket, Result, ITEMS_PER_PAGE};
|
||||
use serde_json;
|
||||
use url::Url;
|
||||
use webfinger::*;
|
||||
|
||||
pub type CustomGroup = CustomObject<ApSignature, Group>;
|
||||
|
||||
@@ -106,8 +101,8 @@ impl Blog {
|
||||
}
|
||||
|
||||
pub fn list_authors(&self, conn: &Connection) -> Result<Vec<User>> {
|
||||
use schema::blog_authors;
|
||||
use schema::users;
|
||||
use crate::schema::blog_authors;
|
||||
use crate::schema::users;
|
||||
let authors_ids = blog_authors::table
|
||||
.filter(blog_authors::blog_id.eq(self.id))
|
||||
.select(blog_authors::author_id);
|
||||
@@ -118,7 +113,7 @@ impl Blog {
|
||||
}
|
||||
|
||||
pub fn count_authors(&self, conn: &Connection) -> Result<i64> {
|
||||
use schema::blog_authors;
|
||||
use crate::schema::blog_authors;
|
||||
blog_authors::table
|
||||
.filter(blog_authors::blog_id.eq(self.id))
|
||||
.count()
|
||||
@@ -127,7 +122,7 @@ impl Blog {
|
||||
}
|
||||
|
||||
pub fn find_for_author(conn: &Connection, author: &User) -> Result<Vec<Blog>> {
|
||||
use schema::blog_authors;
|
||||
use crate::schema::blog_authors;
|
||||
let author_ids = blog_authors::table
|
||||
.filter(blog_authors::author_id.eq(author.id))
|
||||
.select(blog_authors::blog_id);
|
||||
@@ -501,14 +496,17 @@ impl NewBlog {
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use blog_authors::*;
|
||||
use crate::{
|
||||
blog_authors::*,
|
||||
config::CONFIG,
|
||||
instance::tests as instance_tests,
|
||||
medias::NewMedia,
|
||||
search::tests::get_searcher,
|
||||
tests::{db, rockets},
|
||||
users::tests as usersTests,
|
||||
Connection as Conn,
|
||||
};
|
||||
use diesel::Connection;
|
||||
use instance::tests as instance_tests;
|
||||
use medias::NewMedia;
|
||||
use search::tests::get_searcher;
|
||||
use tests::{db, rockets};
|
||||
use users::tests as usersTests;
|
||||
use Connection as Conn;
|
||||
|
||||
pub(crate) fn fill_database(conn: &Conn) -> (Vec<User>, Vec<Blog>) {
|
||||
instance_tests::fill_database(conn);
|
||||
@@ -770,7 +768,9 @@ pub(crate) mod tests {
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let (_, blogs) = fill_database(conn);
|
||||
|
||||
blogs[0].delete(conn, &get_searcher()).unwrap();
|
||||
blogs[0]
|
||||
.delete(conn, &get_searcher(&CONFIG.search_tokenizers))
|
||||
.unwrap();
|
||||
assert!(Blog::get(conn, blogs[0].id).is_err());
|
||||
Ok(())
|
||||
})
|
||||
@@ -780,7 +780,7 @@ pub(crate) mod tests {
|
||||
fn delete_via_user() {
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let searcher = get_searcher();
|
||||
let searcher = get_searcher(&CONFIG.search_tokenizers);
|
||||
let (user, _) = fill_database(conn);
|
||||
|
||||
let b1 = Blog::insert(
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
use crate::{comments::Comment, schema::comment_seers, users::User, Connection, Error, Result};
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
|
||||
use comments::Comment;
|
||||
use schema::comment_seers;
|
||||
use users::User;
|
||||
use {Connection, Error, Result};
|
||||
|
||||
#[derive(Queryable, Clone)]
|
||||
pub struct CommentSeers {
|
||||
pub id: i32,
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
use crate::{
|
||||
comment_seers::{CommentSeers, NewCommentSeers},
|
||||
instance::Instance,
|
||||
medias::Media,
|
||||
mentions::Mention,
|
||||
notifications::*,
|
||||
posts::Post,
|
||||
safe_string::SafeString,
|
||||
schema::comments,
|
||||
users::User,
|
||||
Connection, Error, PlumeRocket, Result,
|
||||
};
|
||||
use activitypub::{
|
||||
activity::{Create, Delete},
|
||||
link,
|
||||
@@ -5,25 +17,15 @@ use activitypub::{
|
||||
};
|
||||
use chrono::{self, NaiveDateTime};
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
||||
use serde_json;
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use comment_seers::{CommentSeers, NewCommentSeers};
|
||||
use instance::Instance;
|
||||
use medias::Media;
|
||||
use mentions::Mention;
|
||||
use notifications::*;
|
||||
use plume_common::activity_pub::{
|
||||
inbox::{AsActor, AsObject, FromId},
|
||||
Id, IntoId, PUBLIC_VISIBILITY,
|
||||
use plume_common::{
|
||||
activity_pub::{
|
||||
inbox::{AsActor, AsObject, FromId},
|
||||
Id, IntoId, PUBLIC_VISIBILITY,
|
||||
},
|
||||
utils,
|
||||
};
|
||||
use plume_common::utils;
|
||||
use posts::Post;
|
||||
use safe_string::SafeString;
|
||||
use schema::comments;
|
||||
use users::User;
|
||||
use {Connection, Error, PlumeRocket, Result};
|
||||
use serde_json;
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Queryable, Identifiable, Clone, AsChangeset)]
|
||||
pub struct Comment {
|
||||
@@ -77,7 +79,7 @@ impl Comment {
|
||||
}
|
||||
|
||||
pub fn count_local(conn: &Connection) -> Result<i64> {
|
||||
use schema::users;
|
||||
use crate::schema::users;
|
||||
let local_authors = users::table
|
||||
.filter(users::instance_id.eq(Instance::get_local()?.id))
|
||||
.select(users::id);
|
||||
@@ -127,9 +129,8 @@ impl Comment {
|
||||
)?))?;
|
||||
note.object_props
|
||||
.set_published_string(chrono::Utc::now().to_rfc3339())?;
|
||||
note.object_props
|
||||
.set_attributed_to_link(author.clone().into_id())?;
|
||||
note.object_props.set_to_link_vec(to.clone())?;
|
||||
note.object_props.set_attributed_to_link(author.into_id())?;
|
||||
note.object_props.set_to_link_vec(to)?;
|
||||
note.object_props.set_tag_link_vec(
|
||||
mentions
|
||||
.into_iter()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::search::TokenizerKind as SearchTokenizer;
|
||||
use rocket::config::Limits;
|
||||
use rocket::Config as RocketConfig;
|
||||
use std::env::{self, var};
|
||||
@@ -11,7 +12,10 @@ pub struct Config {
|
||||
pub base_url: String,
|
||||
pub database_url: String,
|
||||
pub db_name: &'static str,
|
||||
pub db_max_size: Option<u32>,
|
||||
pub db_min_idle: Option<u32>,
|
||||
pub search_index: String,
|
||||
pub search_tokenizers: SearchTokenizerConfig,
|
||||
pub rocket: Result<RocketConfig, RocketError>,
|
||||
pub logo: LogoConfig,
|
||||
pub default_theme: String,
|
||||
@@ -186,6 +190,56 @@ impl Default for LogoConfig {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SearchTokenizerConfig {
|
||||
pub tag_tokenizer: SearchTokenizer,
|
||||
pub content_tokenizer: SearchTokenizer,
|
||||
pub property_tokenizer: SearchTokenizer,
|
||||
}
|
||||
|
||||
impl SearchTokenizerConfig {
|
||||
pub fn init() -> Self {
|
||||
use SearchTokenizer::*;
|
||||
|
||||
match var("SEARCH_LANG").ok().as_deref() {
|
||||
Some("ja") => {
|
||||
#[cfg(not(feature = "search-lindera"))]
|
||||
panic!("You need build Plume with search-lindera feature, or execute it with SEARCH_TAG_TOKENIZER=ngram and SEARCH_CONTENT_TOKENIZER=ngram to enable Japanese search feature");
|
||||
#[cfg(feature = "search-lindera")]
|
||||
Self {
|
||||
tag_tokenizer: Self::determine_tokenizer("SEARCH_TAG_TOKENIZER", Lindera),
|
||||
content_tokenizer: Self::determine_tokenizer(
|
||||
"SEARCH_CONTENT_TOKENIZER",
|
||||
Lindera,
|
||||
),
|
||||
property_tokenizer: Ngram,
|
||||
}
|
||||
}
|
||||
_ => Self {
|
||||
tag_tokenizer: Self::determine_tokenizer("SEARCH_TAG_TOKENIZER", Whitespace),
|
||||
content_tokenizer: Self::determine_tokenizer("SEARCH_CONTENT_TOKENIZER", Simple),
|
||||
property_tokenizer: Ngram,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_tokenizer(var_name: &str, default: SearchTokenizer) -> SearchTokenizer {
|
||||
use SearchTokenizer::*;
|
||||
|
||||
match var(var_name).ok().as_deref() {
|
||||
Some("simple") => Simple,
|
||||
Some("ngram") => Ngram,
|
||||
Some("whitespace") => Whitespace,
|
||||
Some("lindera") => {
|
||||
#[cfg(not(feature = "search-lindera"))]
|
||||
panic!("You need build Plume with search-lindera feature to use Lindera tokenizer");
|
||||
#[cfg(feature = "search-lindera")]
|
||||
Lindera
|
||||
}
|
||||
_ => default,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref CONFIG: Config = Config {
|
||||
base_url: var("BASE_URL").unwrap_or_else(|_| format!(
|
||||
@@ -193,12 +247,21 @@ lazy_static! {
|
||||
var("ROCKET_PORT").unwrap_or_else(|_| "7878".to_owned())
|
||||
)),
|
||||
db_name: DB_NAME,
|
||||
db_max_size: var("DB_MAX_SIZE").map_or(None, |s| Some(
|
||||
s.parse::<u32>()
|
||||
.expect("Couldn't parse DB_MAX_SIZE into u32")
|
||||
)),
|
||||
db_min_idle: var("DB_MIN_IDLE").map_or(None, |s| Some(
|
||||
s.parse::<u32>()
|
||||
.expect("Couldn't parse DB_MIN_IDLE into u32")
|
||||
)),
|
||||
#[cfg(feature = "postgres")]
|
||||
database_url: var("DATABASE_URL")
|
||||
.unwrap_or_else(|_| format!("postgres://plume:plume@localhost/{}", DB_NAME)),
|
||||
#[cfg(feature = "sqlite")]
|
||||
database_url: var("DATABASE_URL").unwrap_or_else(|_| format!("{}.sqlite", DB_NAME)),
|
||||
search_index: var("SEARCH_INDEX").unwrap_or_else(|_| "search_index".to_owned()),
|
||||
search_tokenizers: SearchTokenizerConfig::init(),
|
||||
rocket: get_rocket_config(),
|
||||
logo: LogoConfig::default(),
|
||||
default_theme: var("DEFAULT_THEME").unwrap_or_else(|_| "default-light".to_owned()),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::Connection;
|
||||
use diesel::r2d2::{
|
||||
ConnectionManager, CustomizeConnection, Error as ConnError, Pool, PooledConnection,
|
||||
};
|
||||
@@ -10,8 +11,6 @@ use rocket::{
|
||||
};
|
||||
use std::ops::Deref;
|
||||
|
||||
use Connection;
|
||||
|
||||
pub type DbPool = Pool<ConnectionManager<Connection>>;
|
||||
|
||||
// From rocket documentation
|
||||
@@ -26,7 +25,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for DbConn {
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
|
||||
let pool = request.guard::<State<DbPool>>()?;
|
||||
let pool = request.guard::<State<'_, DbPool>>()?;
|
||||
match pool.get() {
|
||||
Ok(conn) => Outcome::Success(DbConn(conn)),
|
||||
Err(_) => Outcome::Failure((Status::ServiceUnavailable, ())),
|
||||
@@ -73,5 +72,4 @@ pub(crate) mod tests {
|
||||
Ok(conn.begin_test_transaction().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
use crate::{
|
||||
ap_url, notifications::*, schema::follows, users::User, Connection, Error, PlumeRocket, Result,
|
||||
CONFIG,
|
||||
};
|
||||
use activitypub::activity::{Accept, Follow as FollowAct, Undo};
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
||||
|
||||
use notifications::*;
|
||||
use plume_common::activity_pub::{
|
||||
broadcast,
|
||||
inbox::{AsActor, AsObject, FromId},
|
||||
sign::Signer,
|
||||
Id, IntoId, PUBLIC_VISIBILITY,
|
||||
};
|
||||
use schema::follows;
|
||||
use users::User;
|
||||
use {ap_url, Connection, Error, PlumeRocket, Result, CONFIG};
|
||||
|
||||
#[derive(Clone, Queryable, Identifiable, Associations, AsChangeset)]
|
||||
#[belongs_to(User, foreign_key = "following_id")]
|
||||
@@ -56,8 +55,7 @@ impl Follow {
|
||||
let target = User::get(conn, self.following_id)?;
|
||||
|
||||
let mut act = FollowAct::default();
|
||||
act.follow_props
|
||||
.set_actor_link::<Id>(user.clone().into_id())?;
|
||||
act.follow_props.set_actor_link::<Id>(user.into_id())?;
|
||||
act.follow_props
|
||||
.set_object_link::<Id>(target.clone().into_id())?;
|
||||
act.object_props.set_id_string(self.ap_url.clone())?;
|
||||
@@ -202,9 +200,8 @@ impl IntoId for Follow {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{tests::db, users::tests as user_tests};
|
||||
use diesel::Connection;
|
||||
use tests::db;
|
||||
use users::tests as user_tests;
|
||||
|
||||
#[test]
|
||||
fn test_id() {
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
use crate::{
|
||||
ap_url,
|
||||
medias::Media,
|
||||
safe_string::SafeString,
|
||||
schema::{instances, users},
|
||||
users::{Role, User},
|
||||
Connection, Error, Result,
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
use std::sync::RwLock;
|
||||
|
||||
use ap_url;
|
||||
use medias::Media;
|
||||
use plume_common::utils::md_to_html;
|
||||
use safe_string::SafeString;
|
||||
use schema::{instances, users};
|
||||
use users::{Role, User};
|
||||
use {Connection, Error, Result};
|
||||
use std::sync::RwLock;
|
||||
|
||||
#[derive(Clone, Identifiable, Queryable)]
|
||||
pub struct Instance {
|
||||
@@ -242,9 +243,8 @@ impl Instance {
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::{tests::db, Connection as Conn};
|
||||
use diesel::Connection;
|
||||
use tests::db;
|
||||
use Connection as Conn;
|
||||
|
||||
pub(crate) fn fill_database(conn: &Conn) -> Vec<(NewInstance, Instance)> {
|
||||
let res = vec![
|
||||
|
||||
+9
-29
@@ -1,41 +1,21 @@
|
||||
#![feature(try_trait)]
|
||||
#![feature(never_type)]
|
||||
#![feature(custom_attribute)]
|
||||
#![feature(proc_macro_hygiene)]
|
||||
|
||||
extern crate activitypub;
|
||||
extern crate ammonia;
|
||||
extern crate askama_escape;
|
||||
extern crate bcrypt;
|
||||
extern crate chrono;
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
extern crate guid_create;
|
||||
extern crate heck;
|
||||
extern crate itertools;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate migrations_internals;
|
||||
extern crate openssl;
|
||||
extern crate plume_api;
|
||||
extern crate plume_common;
|
||||
#[macro_use]
|
||||
extern crate plume_macro;
|
||||
extern crate reqwest;
|
||||
#[macro_use]
|
||||
extern crate rocket;
|
||||
extern crate rocket_i18n;
|
||||
extern crate scheduled_thread_pool;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate serde_json;
|
||||
#[macro_use]
|
||||
extern crate tantivy;
|
||||
extern crate url;
|
||||
extern crate walkdir;
|
||||
extern crate webfinger;
|
||||
extern crate whatlang;
|
||||
|
||||
use plume_common::activity_pub::inbox::InboxError;
|
||||
|
||||
@@ -53,6 +33,7 @@ pub type Connection = diesel::PgConnection;
|
||||
/// All the possible errors that can be encoutered in this crate
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Blocklisted(bool, String),
|
||||
Db(diesel::result::Error),
|
||||
Inbox(Box<InboxError<Error>>),
|
||||
InvalidValue,
|
||||
@@ -247,12 +228,13 @@ macro_rules! get {
|
||||
/// Model::insert(connection, NewModelType::new());
|
||||
/// ```
|
||||
macro_rules! insert {
|
||||
($table:ident, $from:ident) => {
|
||||
($table:ident, $from:ty) => {
|
||||
insert!($table, $from, |x, _conn| Ok(x));
|
||||
};
|
||||
($table:ident, $from:ident, |$val:ident, $conn:ident | $( $after:tt )+) => {
|
||||
($table:ident, $from:ty, |$val:ident, $conn:ident | $( $after:tt )+) => {
|
||||
last!($table);
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn insert(conn: &crate::Connection, new: $from) -> Result<Self> {
|
||||
diesel::insert_into($table::table)
|
||||
.values(new)
|
||||
@@ -279,6 +261,7 @@ macro_rules! insert {
|
||||
/// ```
|
||||
macro_rules! last {
|
||||
($table:ident) => {
|
||||
#[allow(dead_code)]
|
||||
pub fn last(conn: &crate::Connection) -> Result<Self> {
|
||||
$table::table
|
||||
.order_by($table::id.desc())
|
||||
@@ -298,16 +281,12 @@ pub fn ap_url(url: &str) -> String {
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
mod tests {
|
||||
use db_conn;
|
||||
use crate::{db_conn, migrations::IMPORTED_MIGRATIONS, search, Connection as Conn, CONFIG};
|
||||
use diesel::r2d2::ConnectionManager;
|
||||
use migrations::IMPORTED_MIGRATIONS;
|
||||
use plume_common::utils::random_hex;
|
||||
use scheduled_thread_pool::ScheduledThreadPool;
|
||||
use search;
|
||||
use std::env::temp_dir;
|
||||
use std::sync::Arc;
|
||||
use Connection as Conn;
|
||||
use CONFIG;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! part_eq {
|
||||
@@ -341,7 +320,7 @@ mod tests {
|
||||
pub fn rockets() -> super::PlumeRocket {
|
||||
super::PlumeRocket {
|
||||
conn: db_conn::DbConn((*DB_POOL).get().unwrap()),
|
||||
searcher: Arc::new(search::tests::get_searcher()),
|
||||
searcher: Arc::new(search::tests::get_searcher(&CONFIG.search_tokenizers)),
|
||||
worker: Arc::new(ScheduledThreadPool::new(2)),
|
||||
user: None,
|
||||
}
|
||||
@@ -351,6 +330,7 @@ mod tests {
|
||||
pub mod admin;
|
||||
pub mod api_tokens;
|
||||
pub mod apps;
|
||||
pub mod blocklisted_emails;
|
||||
pub mod blog_authors;
|
||||
pub mod blogs;
|
||||
pub mod comment_seers;
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
use crate::{
|
||||
notifications::*, posts::Post, schema::likes, timeline::*, users::User, Connection, Error,
|
||||
PlumeRocket, Result,
|
||||
};
|
||||
use activitypub::activity;
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
|
||||
use notifications::*;
|
||||
use plume_common::activity_pub::{
|
||||
inbox::{AsActor, AsObject, FromId},
|
||||
Id, IntoId, PUBLIC_VISIBILITY,
|
||||
};
|
||||
use posts::Post;
|
||||
use schema::likes;
|
||||
use timeline::*;
|
||||
use users::User;
|
||||
use {Connection, Error, PlumeRocket, Result};
|
||||
|
||||
#[derive(Clone, Queryable, Identifiable)]
|
||||
pub struct Like {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use crate::{
|
||||
blogs::Blog,
|
||||
schema::{blogs, list_elems, lists, users},
|
||||
users::User,
|
||||
Connection, Error, Result,
|
||||
};
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
|
||||
use blogs::Blog;
|
||||
use schema::{blogs, list_elems, lists, users};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use users::User;
|
||||
use {Connection, Error, Result};
|
||||
|
||||
/// Represent what a list is supposed to store. Represented in database as an integer
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
@@ -164,7 +165,7 @@ impl List {
|
||||
last!(lists);
|
||||
get!(lists);
|
||||
|
||||
fn insert(conn: &Connection, val: NewList) -> Result<Self> {
|
||||
fn insert(conn: &Connection, val: NewList<'_>) -> Result<Self> {
|
||||
diesel::insert_into(lists::table)
|
||||
.values(val)
|
||||
.execute(conn)?;
|
||||
@@ -309,7 +310,7 @@ mod private {
|
||||
};
|
||||
|
||||
impl ListElem {
|
||||
insert!(list_elems, NewListElem);
|
||||
insert!(list_elems, NewListElem<'_>);
|
||||
|
||||
pub fn user_in_list(conn: &Connection, list: &List, user: i32) -> Result<bool> {
|
||||
dsl::select(dsl::exists(
|
||||
@@ -359,9 +360,8 @@ mod private {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use blogs::tests as blog_tests;
|
||||
use crate::{blogs::tests as blog_tests, tests::db};
|
||||
use diesel::Connection;
|
||||
use tests::db;
|
||||
|
||||
#[test]
|
||||
fn list_type() {
|
||||
@@ -551,5 +551,4 @@ mod tests {
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+14
-15
@@ -1,20 +1,17 @@
|
||||
use crate::{
|
||||
ap_url, instance::Instance, safe_string::SafeString, schema::medias, users::User, Connection,
|
||||
Error, PlumeRocket, Result,
|
||||
};
|
||||
use activitypub::object::Image;
|
||||
use askama_escape::escape;
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
use guid_create::GUID;
|
||||
use reqwest;
|
||||
use std::{fs, path::Path};
|
||||
|
||||
use plume_common::{
|
||||
activity_pub::{inbox::FromId, Id},
|
||||
utils::MediaProcessor,
|
||||
};
|
||||
|
||||
use instance::Instance;
|
||||
use safe_string::SafeString;
|
||||
use schema::medias;
|
||||
use users::User;
|
||||
use {ap_url, Connection, Error, PlumeRocket, Result};
|
||||
use reqwest;
|
||||
use std::{fs, path::Path};
|
||||
|
||||
#[derive(Clone, Identifiable, Queryable)]
|
||||
pub struct Media {
|
||||
@@ -117,15 +114,19 @@ impl Media {
|
||||
Ok(match self.category() {
|
||||
MediaCategory::Image => SafeString::trusted(&format!(
|
||||
r#"<img src="{}" alt="{}" title="{}">"#,
|
||||
url, escape(&self.alt_text), escape(&self.alt_text)
|
||||
url,
|
||||
escape(&self.alt_text),
|
||||
escape(&self.alt_text)
|
||||
)),
|
||||
MediaCategory::Audio => SafeString::trusted(&format!(
|
||||
r#"<div class="media-preview audio"></div><audio src="{}" title="{}" controls></audio>"#,
|
||||
url, escape(&self.alt_text)
|
||||
url,
|
||||
escape(&self.alt_text)
|
||||
)),
|
||||
MediaCategory::Video => SafeString::trusted(&format!(
|
||||
r#"<video src="{}" title="{}" controls></video>"#,
|
||||
url, escape(&self.alt_text)
|
||||
url,
|
||||
escape(&self.alt_text)
|
||||
)),
|
||||
MediaCategory::Unknown => SafeString::trusted(&format!(
|
||||
r#"<a href="{}" class="media-preview unknown"></a>"#,
|
||||
@@ -259,13 +260,11 @@ impl Media {
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::{tests::db, users::tests as usersTests, Connection as Conn};
|
||||
use diesel::Connection;
|
||||
use std::env::{current_dir, set_current_dir};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use tests::db;
|
||||
use users::tests as usersTests;
|
||||
use Connection as Conn;
|
||||
|
||||
pub(crate) fn fill_database(conn: &Conn) -> (Vec<User>, Vec<Media>) {
|
||||
let mut wd = current_dir().unwrap().to_path_buf();
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
use crate::{
|
||||
comments::Comment, notifications::*, posts::Post, schema::mentions, users::User, Connection,
|
||||
Error, PlumeRocket, Result,
|
||||
};
|
||||
use activitypub::link;
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
|
||||
use comments::Comment;
|
||||
use notifications::*;
|
||||
use plume_common::activity_pub::inbox::AsActor;
|
||||
use posts::Post;
|
||||
use schema::mentions;
|
||||
use users::User;
|
||||
use PlumeRocket;
|
||||
use {Connection, Error, Result};
|
||||
|
||||
#[derive(Clone, Queryable, Identifiable)]
|
||||
pub struct Mention {
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
use Connection;
|
||||
use Error;
|
||||
use Result;
|
||||
|
||||
use crate::{Connection, Error, Result};
|
||||
use diesel::connection::{Connection as Conn, SimpleConnection};
|
||||
use migrations_internals::{setup_database, MigrationConnection};
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
#[allow(dead_code)] //variants might not be constructed if not required by current migrations
|
||||
enum Action {
|
||||
Sql(&'static str),
|
||||
Function(&'static Fn(&Connection, &Path) -> Result<()>),
|
||||
Function(&'static dyn Fn(&Connection, &Path) -> Result<()>),
|
||||
}
|
||||
|
||||
impl Action {
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
use crate::{
|
||||
comments::Comment,
|
||||
follows::Follow,
|
||||
likes::Like,
|
||||
mentions::Mention,
|
||||
posts::Post,
|
||||
reshares::Reshare,
|
||||
schema::{follows, notifications},
|
||||
users::User,
|
||||
Connection, Error, Result,
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::{self, ExpressionMethods, JoinOnDsl, QueryDsl, RunQueryDsl};
|
||||
|
||||
use comments::Comment;
|
||||
use follows::Follow;
|
||||
use likes::Like;
|
||||
use mentions::Mention;
|
||||
use posts::Post;
|
||||
use reshares::Reshare;
|
||||
use schema::{follows, notifications};
|
||||
use users::User;
|
||||
use {Connection, Error, Result};
|
||||
|
||||
pub mod notification_kind {
|
||||
pub const COMMENT: &str = "COMMENT";
|
||||
pub const FOLLOW: &str = "FOLLOW";
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::{schema::password_reset_requests, Connection, Error, Result};
|
||||
use chrono::{offset::Utc, Duration, NaiveDateTime};
|
||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
use schema::password_reset_requests;
|
||||
use {Connection, Error, Result};
|
||||
|
||||
#[derive(Clone, Identifiable, Queryable)]
|
||||
pub struct PasswordResetRequest {
|
||||
@@ -75,9 +74,8 @@ impl PasswordResetRequest {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{tests::db, users::tests as user_tests};
|
||||
use diesel::Connection;
|
||||
use tests::db;
|
||||
use users::tests as user_tests;
|
||||
|
||||
#[test]
|
||||
fn test_insert_and_find_password_reset_request() {
|
||||
|
||||
@@ -2,9 +2,7 @@ pub use self::module::PlumeRocket;
|
||||
|
||||
#[cfg(not(test))]
|
||||
mod module {
|
||||
use crate::db_conn::DbConn;
|
||||
use crate::search;
|
||||
use crate::users;
|
||||
use crate::{db_conn::DbConn, search, users};
|
||||
use rocket::{
|
||||
request::{self, FlashMessage, FromRequest, Request},
|
||||
Outcome, State,
|
||||
@@ -29,9 +27,9 @@ mod module {
|
||||
let conn = request.guard::<DbConn>()?;
|
||||
let intl = request.guard::<rocket_i18n::I18n>()?;
|
||||
let user = request.guard::<users::User>().succeeded();
|
||||
let worker = request.guard::<State<Arc<ScheduledThreadPool>>>()?;
|
||||
let searcher = request.guard::<State<Arc<search::Searcher>>>()?;
|
||||
let flash_msg = request.guard::<FlashMessage>().succeeded();
|
||||
let worker = request.guard::<'_, State<'_, Arc<ScheduledThreadPool>>>()?;
|
||||
let searcher = request.guard::<'_, State<'_, Arc<search::Searcher>>>()?;
|
||||
let flash_msg = request.guard::<FlashMessage<'_, '_>>().succeeded();
|
||||
Outcome::Success(PlumeRocket {
|
||||
conn,
|
||||
intl,
|
||||
@@ -46,9 +44,7 @@ mod module {
|
||||
|
||||
#[cfg(test)]
|
||||
mod module {
|
||||
use crate::db_conn::DbConn;
|
||||
use crate::search;
|
||||
use crate::users;
|
||||
use crate::{db_conn::DbConn, search, users};
|
||||
use rocket::{
|
||||
request::{self, FromRequest, Request},
|
||||
Outcome, State,
|
||||
@@ -70,8 +66,8 @@ mod module {
|
||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<PlumeRocket, ()> {
|
||||
let conn = request.guard::<DbConn>()?;
|
||||
let user = request.guard::<users::User>().succeeded();
|
||||
let worker = request.guard::<State<Arc<ScheduledThreadPool>>>()?;
|
||||
let searcher = request.guard::<State<Arc<search::Searcher>>>()?;
|
||||
let worker = request.guard::<'_, State<'_, Arc<ScheduledThreadPool>>>()?;
|
||||
let searcher = request.guard::<'_, State<'_, Arc<search::Searcher>>>()?;
|
||||
Outcome::Success(PlumeRocket {
|
||||
conn,
|
||||
user,
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
use crate::{posts::Post, schema::post_authors, users::User, Error, Result};
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
|
||||
use posts::Post;
|
||||
use schema::post_authors;
|
||||
use users::User;
|
||||
use {Error, Result};
|
||||
|
||||
#[derive(Clone, Queryable, Identifiable, Associations)]
|
||||
#[belongs_to(Post)]
|
||||
#[belongs_to(User, foreign_key = "author_id")]
|
||||
|
||||
+22
-30
@@ -1,3 +1,8 @@
|
||||
use crate::{
|
||||
ap_url, blogs::Blog, instance::Instance, medias::Media, mentions::Mention, post_authors::*,
|
||||
safe_string::SafeString, schema::posts, search::Searcher, tags::*, timeline::*, users::User,
|
||||
Connection, Error, PlumeRocket, Result, CONFIG,
|
||||
};
|
||||
use activitypub::{
|
||||
activity::{Create, Delete, Update},
|
||||
link,
|
||||
@@ -7,13 +12,6 @@ use activitypub::{
|
||||
use chrono::{NaiveDateTime, TimeZone, Utc};
|
||||
use diesel::{self, BelongingToDsl, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
|
||||
use heck::{CamelCase, KebabCase};
|
||||
use serde_json;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use blogs::Blog;
|
||||
use instance::Instance;
|
||||
use medias::Media;
|
||||
use mentions::Mention;
|
||||
use plume_common::{
|
||||
activity_pub::{
|
||||
inbox::{AsObject, FromId},
|
||||
@@ -21,14 +19,8 @@ use plume_common::{
|
||||
},
|
||||
utils::md_to_html,
|
||||
};
|
||||
use post_authors::*;
|
||||
use safe_string::SafeString;
|
||||
use schema::posts;
|
||||
use search::Searcher;
|
||||
use tags::*;
|
||||
use timeline::*;
|
||||
use users::User;
|
||||
use {ap_url, Connection, Error, PlumeRocket, Result, CONFIG};
|
||||
use serde_json;
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub type LicensedArticle = CustomObject<Licensed, Article>;
|
||||
|
||||
@@ -111,7 +103,7 @@ impl Post {
|
||||
tag: String,
|
||||
(min, max): (i32, i32),
|
||||
) -> Result<Vec<Post>> {
|
||||
use schema::tags;
|
||||
use crate::schema::tags;
|
||||
|
||||
let ids = tags::table.filter(tags::tag.eq(tag)).select(tags::post_id);
|
||||
posts::table
|
||||
@@ -125,7 +117,7 @@ impl Post {
|
||||
}
|
||||
|
||||
pub fn count_for_tag(conn: &Connection, tag: String) -> Result<i64> {
|
||||
use schema::tags;
|
||||
use crate::schema::tags;
|
||||
let ids = tags::table.filter(tags::tag.eq(tag)).select(tags::post_id);
|
||||
posts::table
|
||||
.filter(posts::id.eq_any(ids))
|
||||
@@ -139,8 +131,8 @@ impl Post {
|
||||
}
|
||||
|
||||
pub fn count_local(conn: &Connection) -> Result<i64> {
|
||||
use schema::post_authors;
|
||||
use schema::users;
|
||||
use crate::schema::post_authors;
|
||||
use crate::schema::users;
|
||||
let local_authors = users::table
|
||||
.filter(users::instance_id.eq(Instance::get_local()?.id))
|
||||
.select(users::id);
|
||||
@@ -188,7 +180,7 @@ impl Post {
|
||||
author: &User,
|
||||
limit: i64,
|
||||
) -> Result<Vec<Post>> {
|
||||
use schema::post_authors;
|
||||
use crate::schema::post_authors;
|
||||
|
||||
let posts = PostAuthor::belonging_to(author).select(post_authors::post_id);
|
||||
posts::table
|
||||
@@ -239,7 +231,7 @@ impl Post {
|
||||
}
|
||||
|
||||
pub fn drafts_by_author(conn: &Connection, author: &User) -> Result<Vec<Post>> {
|
||||
use schema::post_authors;
|
||||
use crate::schema::post_authors;
|
||||
|
||||
let posts = PostAuthor::belonging_to(author).select(post_authors::post_id);
|
||||
posts::table
|
||||
@@ -251,8 +243,8 @@ impl Post {
|
||||
}
|
||||
|
||||
pub fn get_authors(&self, conn: &Connection) -> Result<Vec<User>> {
|
||||
use schema::post_authors;
|
||||
use schema::users;
|
||||
use crate::schema::post_authors;
|
||||
use crate::schema::users;
|
||||
let author_list = PostAuthor::belonging_to(self).select(post_authors::author_id);
|
||||
users::table
|
||||
.filter(users::id.eq_any(author_list))
|
||||
@@ -261,7 +253,7 @@ impl Post {
|
||||
}
|
||||
|
||||
pub fn is_author(&self, conn: &Connection, author_id: i32) -> Result<bool> {
|
||||
use schema::post_authors;
|
||||
use crate::schema::post_authors;
|
||||
Ok(PostAuthor::belonging_to(self)
|
||||
.filter(post_authors::author_id.eq(author_id))
|
||||
.count()
|
||||
@@ -270,7 +262,7 @@ impl Post {
|
||||
}
|
||||
|
||||
pub fn get_blog(&self, conn: &Connection) -> Result<Blog> {
|
||||
use schema::blogs;
|
||||
use crate::schema::blogs;
|
||||
blogs::table
|
||||
.filter(blogs::id.eq(self.blog_id))
|
||||
.first(conn)
|
||||
@@ -278,7 +270,7 @@ impl Post {
|
||||
}
|
||||
|
||||
pub fn count_likes(&self, conn: &Connection) -> Result<i64> {
|
||||
use schema::likes;
|
||||
use crate::schema::likes;
|
||||
likes::table
|
||||
.filter(likes::post_id.eq(self.id))
|
||||
.count()
|
||||
@@ -287,7 +279,7 @@ impl Post {
|
||||
}
|
||||
|
||||
pub fn count_reshares(&self, conn: &Connection) -> Result<i64> {
|
||||
use schema::reshares;
|
||||
use crate::schema::reshares;
|
||||
reshares::table
|
||||
.filter(reshares::post_id.eq(self.id))
|
||||
.count()
|
||||
@@ -632,7 +624,7 @@ impl FromId<PlumeRocket> for Post {
|
||||
.into_iter()
|
||||
.map(|s| s.to_camel_case())
|
||||
.collect::<HashSet<_>>();
|
||||
if let Some(serde_json::Value::Array(tags)) = article.object_props.tag.clone() {
|
||||
if let Some(serde_json::Value::Array(tags)) = article.object_props.tag {
|
||||
for tag in tags {
|
||||
serde_json::from_value::<link::Mention>(tag.clone())
|
||||
.map(|m| Mention::from_activity(conn, &m, post.id, true, true))
|
||||
@@ -725,7 +717,7 @@ impl FromId<PlumeRocket> for PostUpdate {
|
||||
.ok()
|
||||
.map(|x| x.content),
|
||||
license: updated.custom_props.license_string().ok(),
|
||||
tags: updated.object.object_props.tag.clone(),
|
||||
tags: updated.object.object_props.tag,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -806,7 +798,7 @@ impl AsObject<User, Update, &PlumeRocket> for PostUpdate {
|
||||
|
||||
impl IntoId for Post {
|
||||
fn into_id(self) -> Id {
|
||||
Id::new(self.ap_url.clone())
|
||||
Id::new(self.ap_url)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
use crate::{
|
||||
notifications::*, posts::Post, schema::reshares, timeline::*, users::User, Connection, Error,
|
||||
PlumeRocket, Result,
|
||||
};
|
||||
use activitypub::activity::{Announce, Undo};
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
|
||||
use notifications::*;
|
||||
use plume_common::activity_pub::{
|
||||
inbox::{AsActor, AsObject, FromId},
|
||||
Id, IntoId, PUBLIC_VISIBILITY,
|
||||
};
|
||||
use posts::Post;
|
||||
use schema::reshares;
|
||||
use timeline::*;
|
||||
use users::User;
|
||||
use {Connection, Error, PlumeRocket, Result};
|
||||
|
||||
#[derive(Clone, Queryable, Identifiable)]
|
||||
pub struct Reshare {
|
||||
|
||||
@@ -31,10 +31,41 @@ lazy_static! {
|
||||
.add_tag_attributes("label", ["for"].iter())
|
||||
.add_tag_attributes("input", ["type", "checked"].iter())
|
||||
.add_allowed_classes("input", ["cw-checkbox"].iter())
|
||||
.add_allowed_classes(
|
||||
"span",
|
||||
[
|
||||
"cw-container",
|
||||
"cw-text",
|
||||
//Scope classes for the syntax highlighting.
|
||||
"attribute-name",
|
||||
"comment",
|
||||
"constant",
|
||||
"control",
|
||||
"declaration",
|
||||
"entity",
|
||||
"function",
|
||||
"invalid",
|
||||
"keyword",
|
||||
"language",
|
||||
"modifier",
|
||||
"name",
|
||||
"numeric",
|
||||
"operator",
|
||||
"parameter",
|
||||
"punctuation",
|
||||
"source",
|
||||
"storage",
|
||||
"string",
|
||||
"support",
|
||||
"tag",
|
||||
"type",
|
||||
"variable",
|
||||
]
|
||||
.iter(),
|
||||
)
|
||||
// Related to https://github.com/Plume-org/Plume/issues/637
|
||||
.add_allowed_classes("sup", ["footnote-reference", "footnote-definition-label"].iter())
|
||||
.add_allowed_classes("div", ["footnote-definition"].iter())
|
||||
.add_allowed_classes("span", ["cw-container", "cw-text"].iter())
|
||||
.attribute_filter(|elem, att, val| match (elem, att) {
|
||||
("input", "type") => Some("checkbox".into()),
|
||||
("input", "checked") => Some("checked".into()),
|
||||
@@ -51,7 +82,7 @@ lazy_static! {
|
||||
};
|
||||
}
|
||||
|
||||
fn url_add_prefix(url: &str) -> Option<Cow<str>> {
|
||||
fn url_add_prefix(url: &str) -> Option<Cow<'_, str>> {
|
||||
if url.starts_with('#') && !url.starts_with("#postcontent-") {
|
||||
//if start with an #
|
||||
let mut new_url = "#postcontent-".to_owned(); //change to valid id
|
||||
@@ -108,7 +139,7 @@ struct SafeStringVisitor;
|
||||
impl<'de> Visitor<'de> for SafeStringVisitor {
|
||||
type Value = SafeString;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("a string")
|
||||
}
|
||||
|
||||
@@ -150,7 +181,7 @@ where
|
||||
DB: diesel::backend::Backend,
|
||||
str: ToSql<diesel::sql_types::Text, DB>,
|
||||
{
|
||||
fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
|
||||
fn to_sql<W: Write>(&self, out: &mut Output<'_, W, DB>) -> serialize::Result {
|
||||
str::to_sql(&self.value, out)
|
||||
}
|
||||
}
|
||||
@@ -162,7 +193,7 @@ impl Borrow<str> for SafeString {
|
||||
}
|
||||
|
||||
impl Display for SafeString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,15 @@ table! {
|
||||
user_id -> Int4,
|
||||
}
|
||||
}
|
||||
table! {
|
||||
email_blocklist(id){
|
||||
id -> Int4,
|
||||
email_address -> VarChar,
|
||||
note -> Text,
|
||||
notify_user -> Bool,
|
||||
notification_text -> Text,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
follows (id) {
|
||||
|
||||
@@ -3,27 +3,32 @@ mod searcher;
|
||||
mod tokenizer;
|
||||
pub use self::query::PlumeQuery as Query;
|
||||
pub use self::searcher::*;
|
||||
pub use self::tokenizer::TokenizerKind;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::{Query, Searcher};
|
||||
use super::{Query, Searcher, TokenizerKind};
|
||||
use diesel::Connection;
|
||||
use plume_common::utils::random_hex;
|
||||
use std::env::temp_dir;
|
||||
use std::str::FromStr;
|
||||
|
||||
use blogs::tests::fill_database;
|
||||
use plume_common::utils::random_hex;
|
||||
use post_authors::*;
|
||||
use posts::{NewPost, Post};
|
||||
use safe_string::SafeString;
|
||||
use tests::db;
|
||||
use crate::{
|
||||
blogs::tests::fill_database,
|
||||
config::SearchTokenizerConfig,
|
||||
post_authors::*,
|
||||
posts::{NewPost, Post},
|
||||
safe_string::SafeString,
|
||||
tests::db,
|
||||
CONFIG,
|
||||
};
|
||||
|
||||
pub(crate) fn get_searcher() -> Searcher {
|
||||
pub(crate) fn get_searcher(tokenizers: &SearchTokenizerConfig) -> Searcher {
|
||||
let dir = temp_dir().join(&format!("plume-test-{}", random_hex()));
|
||||
if dir.exists() {
|
||||
Searcher::open(&dir)
|
||||
Searcher::open(&dir, tokenizers)
|
||||
} else {
|
||||
Searcher::create(&dir)
|
||||
Searcher::create(&dir, tokenizers)
|
||||
}
|
||||
.unwrap()
|
||||
}
|
||||
@@ -98,27 +103,27 @@ pub(crate) mod tests {
|
||||
fn open() {
|
||||
let dir = temp_dir().join(format!("plume-test-{}", random_hex()));
|
||||
{
|
||||
Searcher::create(&dir).unwrap();
|
||||
Searcher::create(&dir, &CONFIG.search_tokenizers).unwrap();
|
||||
}
|
||||
Searcher::open(&dir).unwrap();
|
||||
Searcher::open(&dir, &CONFIG.search_tokenizers).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create() {
|
||||
let dir = temp_dir().join(format!("plume-test-{}", random_hex()));
|
||||
|
||||
assert!(Searcher::open(&dir).is_err());
|
||||
assert!(Searcher::open(&dir, &CONFIG.search_tokenizers).is_err());
|
||||
{
|
||||
Searcher::create(&dir).unwrap();
|
||||
Searcher::create(&dir, &CONFIG.search_tokenizers).unwrap();
|
||||
}
|
||||
Searcher::open(&dir).unwrap(); //verify it's well created
|
||||
Searcher::open(&dir, &CONFIG.search_tokenizers).unwrap(); //verify it's well created
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn search() {
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let searcher = get_searcher();
|
||||
let searcher = get_searcher(&CONFIG.search_tokenizers);
|
||||
let blog = &fill_database(conn).1[0];
|
||||
let author = &blog.list_authors(conn).unwrap()[0];
|
||||
|
||||
@@ -178,10 +183,69 @@ pub(crate) mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "search-lindera")]
|
||||
#[test]
|
||||
fn search_japanese() {
|
||||
let conn = &db();
|
||||
conn.test_transaction::<_, (), _>(|| {
|
||||
let tokenizers = SearchTokenizerConfig {
|
||||
tag_tokenizer: TokenizerKind::Lindera,
|
||||
content_tokenizer: TokenizerKind::Lindera,
|
||||
property_tokenizer: TokenizerKind::Ngram,
|
||||
};
|
||||
let searcher = get_searcher(&tokenizers);
|
||||
let blog = &fill_database(conn).1[0];
|
||||
|
||||
let title = random_hex()[..8].to_owned();
|
||||
|
||||
let post = Post::insert(
|
||||
conn,
|
||||
NewPost {
|
||||
blog_id: blog.id,
|
||||
slug: title.clone(),
|
||||
title: title.clone(),
|
||||
content: SafeString::new("ブログエンジンPlumeです。"),
|
||||
published: true,
|
||||
license: "CC-BY-SA".to_owned(),
|
||||
ap_url: "".to_owned(),
|
||||
creation_date: None,
|
||||
subtitle: "".to_owned(),
|
||||
source: "".to_owned(),
|
||||
cover_id: None,
|
||||
},
|
||||
&searcher,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
searcher.commit();
|
||||
|
||||
assert_eq!(
|
||||
searcher.search_document(conn, Query::from_str("ブログエンジン").unwrap(), (0, 1))
|
||||
[0]
|
||||
.id,
|
||||
post.id
|
||||
);
|
||||
assert_eq!(
|
||||
searcher.search_document(conn, Query::from_str("Plume").unwrap(), (0, 1))[0].id,
|
||||
post.id
|
||||
);
|
||||
assert_eq!(
|
||||
searcher.search_document(conn, Query::from_str("です").unwrap(), (0, 1))[0].id,
|
||||
post.id
|
||||
);
|
||||
assert_eq!(
|
||||
searcher.search_document(conn, Query::from_str("。").unwrap(), (0, 1))[0].id,
|
||||
post.id
|
||||
);
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_writer() {
|
||||
let searcher = get_searcher();
|
||||
let searcher = get_searcher(&CONFIG.search_tokenizers);
|
||||
searcher.drop_writer();
|
||||
get_searcher();
|
||||
get_searcher(&CONFIG.search_tokenizers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::search::searcher::Searcher;
|
||||
use chrono::{naive::NaiveDate, offset::Utc, Datelike};
|
||||
use search::searcher::Searcher;
|
||||
use std::{cmp, ops::Bound};
|
||||
use tantivy::{query::*, schema::*, Term};
|
||||
|
||||
@@ -153,7 +153,7 @@ impl PlumeQuery {
|
||||
|
||||
/// Convert this Query to a Tantivy Query
|
||||
pub fn into_query(self) -> BooleanQuery {
|
||||
let mut result: Vec<(Occur, Box<Query>)> = Vec::new();
|
||||
let mut result: Vec<(Occur, Box<dyn Query>)> = Vec::new();
|
||||
gen_to_query!(self, result; normal: title, subtitle, content, tag;
|
||||
oneoff: instance, author, blog, lang, license);
|
||||
|
||||
@@ -279,7 +279,7 @@ impl PlumeQuery {
|
||||
}
|
||||
|
||||
// map a token and it's field to a query
|
||||
fn token_to_query(token: &str, field_name: &str) -> Box<Query> {
|
||||
fn token_to_query(token: &str, field_name: &str) -> Box<dyn Query> {
|
||||
let token = token.to_lowercase();
|
||||
let token = token.as_str();
|
||||
let field = Searcher::schema().get_field(field_name).unwrap();
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
use instance::Instance;
|
||||
use posts::Post;
|
||||
use schema::posts;
|
||||
use tags::Tag;
|
||||
use Connection;
|
||||
|
||||
use crate::{
|
||||
config::SearchTokenizerConfig, instance::Instance, posts::Post, schema::posts,
|
||||
search::query::PlumeQuery, tags::Tag, Connection, Result,
|
||||
};
|
||||
use chrono::Datelike;
|
||||
use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
use itertools::Itertools;
|
||||
use std::{cmp, fs::create_dir_all, path::Path, sync::Mutex};
|
||||
use tantivy::{
|
||||
collector::TopDocs, directory::MmapDirectory, schema::*, tokenizer::*, Index, IndexReader,
|
||||
IndexWriter, ReloadPolicy, Term,
|
||||
collector::TopDocs, directory::MmapDirectory, schema::*, Index, IndexReader, IndexWriter,
|
||||
ReloadPolicy, Term,
|
||||
};
|
||||
use whatlang::{detect as detect_lang, Lang};
|
||||
|
||||
use super::tokenizer;
|
||||
use search::query::PlumeQuery;
|
||||
use Result;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SearcherError {
|
||||
IndexCreationError,
|
||||
@@ -36,7 +30,7 @@ impl Searcher {
|
||||
pub fn schema() -> Schema {
|
||||
let tag_indexing = TextOptions::default().set_indexing_options(
|
||||
TextFieldIndexing::default()
|
||||
.set_tokenizer("whitespace_tokenizer")
|
||||
.set_tokenizer("tag_tokenizer")
|
||||
.set_index_option(IndexRecordOption::Basic),
|
||||
);
|
||||
|
||||
@@ -72,15 +66,7 @@ impl Searcher {
|
||||
schema_builder.build()
|
||||
}
|
||||
|
||||
pub fn create(path: &AsRef<Path>) -> Result<Self> {
|
||||
let whitespace_tokenizer = tokenizer::WhitespaceTokenizer.filter(LowerCaser);
|
||||
|
||||
let content_tokenizer = SimpleTokenizer
|
||||
.filter(RemoveLongFilter::limit(40))
|
||||
.filter(LowerCaser);
|
||||
|
||||
let property_tokenizer = NgramTokenizer::new(2, 8, false).filter(LowerCaser);
|
||||
|
||||
pub fn create(path: &dyn AsRef<Path>, tokenizers: &SearchTokenizerConfig) -> Result<Self> {
|
||||
let schema = Self::schema();
|
||||
|
||||
create_dir_all(path).map_err(|_| SearcherError::IndexCreationError)?;
|
||||
@@ -92,9 +78,9 @@ impl Searcher {
|
||||
|
||||
{
|
||||
let tokenizer_manager = index.tokenizers();
|
||||
tokenizer_manager.register("whitespace_tokenizer", whitespace_tokenizer);
|
||||
tokenizer_manager.register("content_tokenizer", content_tokenizer);
|
||||
tokenizer_manager.register("property_tokenizer", property_tokenizer);
|
||||
tokenizer_manager.register("tag_tokenizer", tokenizers.tag_tokenizer);
|
||||
tokenizer_manager.register("content_tokenizer", tokenizers.content_tokenizer);
|
||||
tokenizer_manager.register("property_tokenizer", tokenizers.property_tokenizer);
|
||||
} //to please the borrow checker
|
||||
Ok(Self {
|
||||
writer: Mutex::new(Some(
|
||||
@@ -111,31 +97,38 @@ impl Searcher {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn open(path: &AsRef<Path>) -> Result<Self> {
|
||||
let whitespace_tokenizer = tokenizer::WhitespaceTokenizer.filter(LowerCaser);
|
||||
|
||||
let content_tokenizer = SimpleTokenizer
|
||||
.filter(RemoveLongFilter::limit(40))
|
||||
.filter(LowerCaser);
|
||||
|
||||
let property_tokenizer = NgramTokenizer::new(2, 8, false).filter(LowerCaser);
|
||||
|
||||
let index =
|
||||
pub fn open(path: &dyn AsRef<Path>, tokenizers: &SearchTokenizerConfig) -> Result<Self> {
|
||||
let mut index =
|
||||
Index::open(MmapDirectory::open(path).map_err(|_| SearcherError::IndexOpeningError)?)
|
||||
.map_err(|_| SearcherError::IndexOpeningError)?;
|
||||
|
||||
{
|
||||
let tokenizer_manager = index.tokenizers();
|
||||
tokenizer_manager.register("whitespace_tokenizer", whitespace_tokenizer);
|
||||
tokenizer_manager.register("content_tokenizer", content_tokenizer);
|
||||
tokenizer_manager.register("property_tokenizer", property_tokenizer);
|
||||
tokenizer_manager.register("tag_tokenizer", tokenizers.tag_tokenizer);
|
||||
tokenizer_manager.register("content_tokenizer", tokenizers.content_tokenizer);
|
||||
tokenizer_manager.register("property_tokenizer", tokenizers.property_tokenizer);
|
||||
} //to please the borrow checker
|
||||
let mut writer = index
|
||||
let writer = index
|
||||
.writer(50_000_000)
|
||||
.map_err(|_| SearcherError::WriteLockAcquisitionError)?;
|
||||
writer
|
||||
.garbage_collect_files()
|
||||
|
||||
// Since Tantivy v0.12.0, IndexWriter::garbage_collect_files() returns Future.
|
||||
// To avoid conflict with Plume async project, we don't introduce async now.
|
||||
// After async is introduced to Plume, we can use garbage_collect_files() again.
|
||||
// Algorithm stolen from Tantivy's SegmentUpdater::list_files()
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
let mut files: HashSet<PathBuf> = index
|
||||
.list_all_segment_metas()
|
||||
.into_iter()
|
||||
.flat_map(|segment_meta| segment_meta.list_files())
|
||||
.collect();
|
||||
files.insert(Path::new("meta.json").to_path_buf());
|
||||
index
|
||||
.directory_mut()
|
||||
.garbage_collect(|| files)
|
||||
.map_err(|_| SearcherError::IndexEditionError)?;
|
||||
|
||||
Ok(Self {
|
||||
writer: Mutex::new(Some(writer)),
|
||||
reader: index
|
||||
@@ -175,7 +168,7 @@ impl Searcher {
|
||||
post_id => i64::from(post.id),
|
||||
author => post.get_authors(conn)?.into_iter().map(|u| u.fqn).join(" "),
|
||||
creation_date => i64::from(post.creation_date.num_days_from_ce()),
|
||||
instance => Instance::get(conn, post.get_blog(conn)?.instance_id)?.public_domain.clone(),
|
||||
instance => Instance::get(conn, post.get_blog(conn)?.instance_id)?.public_domain,
|
||||
tag => Tag::for_post(conn, post.id)?.into_iter().map(|t| t.tag).join(" "),
|
||||
blog_name => post.get_blog(conn)?.title,
|
||||
content => post.content.get().clone(),
|
||||
|
||||
@@ -1,5 +1,34 @@
|
||||
#[cfg(feature = "search-lindera")]
|
||||
use lindera_tantivy::tokenizer::LinderaTokenizer;
|
||||
use std::str::CharIndices;
|
||||
use tantivy::tokenizer::{Token, TokenStream, Tokenizer};
|
||||
use tantivy::tokenizer::*;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum TokenizerKind {
|
||||
Simple,
|
||||
Ngram,
|
||||
Whitespace,
|
||||
#[cfg(feature = "search-lindera")]
|
||||
Lindera,
|
||||
}
|
||||
|
||||
impl From<TokenizerKind> for TextAnalyzer {
|
||||
fn from(tokenizer: TokenizerKind) -> TextAnalyzer {
|
||||
use TokenizerKind::*;
|
||||
|
||||
match tokenizer {
|
||||
Simple => TextAnalyzer::from(SimpleTokenizer)
|
||||
.filter(RemoveLongFilter::limit(40))
|
||||
.filter(LowerCaser),
|
||||
Ngram => TextAnalyzer::from(NgramTokenizer::new(2, 8, false)).filter(LowerCaser),
|
||||
Whitespace => TextAnalyzer::from(WhitespaceTokenizer).filter(LowerCaser),
|
||||
#[cfg(feature = "search-lindera")]
|
||||
Lindera => {
|
||||
TextAnalyzer::from(LinderaTokenizer::new("decompose", "")).filter(LowerCaser)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Tokenize the text by splitting on whitespaces. Pretty much a copy of Tantivy's SimpleTokenizer,
|
||||
/// but not splitting on punctuation
|
||||
@@ -12,15 +41,13 @@ pub struct WhitespaceTokenStream<'a> {
|
||||
token: Token,
|
||||
}
|
||||
|
||||
impl<'a> Tokenizer<'a> for WhitespaceTokenizer {
|
||||
type TokenStreamImpl = WhitespaceTokenStream<'a>;
|
||||
|
||||
fn token_stream(&self, text: &'a str) -> Self::TokenStreamImpl {
|
||||
WhitespaceTokenStream {
|
||||
impl Tokenizer for WhitespaceTokenizer {
|
||||
fn token_stream<'a>(&self, text: &'a str) -> BoxTokenStream<'a> {
|
||||
BoxTokenStream::from(WhitespaceTokenStream {
|
||||
text,
|
||||
chars: text.char_indices(),
|
||||
token: Token::default(),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
impl<'a> WhitespaceTokenStream<'a> {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use crate::{ap_url, instance::Instance, schema::tags, Connection, Error, Result};
|
||||
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
|
||||
use instance::Instance;
|
||||
use plume_common::activity_pub::Hashtag;
|
||||
use schema::tags;
|
||||
use {ap_url, Connection, Error, Result};
|
||||
|
||||
#[derive(Clone, Identifiable, Queryable)]
|
||||
pub struct Tag {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
use crate::{
|
||||
lists::List,
|
||||
posts::Post,
|
||||
schema::{posts, timeline, timeline_definition},
|
||||
Connection, Error, PlumeRocket, Result,
|
||||
};
|
||||
use diesel::{self, BoolExpressionMethods, ExpressionMethods, QueryDsl, RunQueryDsl};
|
||||
|
||||
use lists::List;
|
||||
use posts::Post;
|
||||
use schema::{posts, timeline, timeline_definition};
|
||||
use std::ops::Deref;
|
||||
use {Connection, Error, PlumeRocket, Result};
|
||||
|
||||
pub(crate) mod query;
|
||||
|
||||
use self::query::{QueryError, TimelineQuery};
|
||||
|
||||
pub use self::query::Kind;
|
||||
use self::query::{QueryError, TimelineQuery};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Queryable, Identifiable, AsChangeset)]
|
||||
#[table_name = "timeline_definition"]
|
||||
@@ -119,7 +119,7 @@ impl Timeline {
|
||||
}
|
||||
})
|
||||
{
|
||||
Err(err)?;
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
Self::insert(
|
||||
@@ -157,7 +157,7 @@ impl Timeline {
|
||||
}
|
||||
})
|
||||
{
|
||||
Err(err)?;
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
Self::insert(
|
||||
@@ -208,7 +208,7 @@ impl Timeline {
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
pub fn add_to_all_timelines(rocket: &PlumeRocket, post: &Post, kind: Kind) -> Result<()> {
|
||||
pub fn add_to_all_timelines(rocket: &PlumeRocket, post: &Post, kind: Kind<'_>) -> Result<()> {
|
||||
let timelines = timeline_definition::table
|
||||
.load::<Self>(rocket.conn.deref())
|
||||
.map_err(Error::from)?;
|
||||
@@ -231,7 +231,7 @@ impl Timeline {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn matches(&self, rocket: &PlumeRocket, post: &Post, kind: Kind) -> Result<bool> {
|
||||
pub fn matches(&self, rocket: &PlumeRocket, post: &Post, kind: Kind<'_>) -> Result<bool> {
|
||||
let query = TimelineQuery::parse(&self.query)?;
|
||||
query.matches(rocket, self, post, kind)
|
||||
}
|
||||
@@ -240,16 +240,18 @@ impl Timeline {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use blogs::tests as blogTests;
|
||||
use crate::{
|
||||
blogs::tests as blogTests,
|
||||
follows::*,
|
||||
lists::ListType,
|
||||
post_authors::{NewPostAuthor, PostAuthor},
|
||||
posts::NewPost,
|
||||
safe_string::SafeString,
|
||||
tags::Tag,
|
||||
tests::{db, rockets},
|
||||
users::tests as userTests,
|
||||
};
|
||||
use diesel::Connection;
|
||||
use follows::*;
|
||||
use lists::ListType;
|
||||
use post_authors::{NewPostAuthor, PostAuthor};
|
||||
use posts::NewPost;
|
||||
use safe_string::SafeString;
|
||||
use tags::Tag;
|
||||
use tests::{db, rockets};
|
||||
use users::tests as userTests;
|
||||
|
||||
#[test]
|
||||
fn test_timeline() {
|
||||
@@ -495,8 +497,7 @@ mod tests {
|
||||
),
|
||||
published: true,
|
||||
license: "GPL".to_string(),
|
||||
source: "Actually, GNU+Linux, GNU×Linux, or GNU¿Linux are better."
|
||||
.to_string(),
|
||||
source: "Actually, GNU+Linux, GNU×Linux, or GNU¿Linux are better.".to_string(),
|
||||
ap_url: "".to_string(),
|
||||
creation_date: None,
|
||||
subtitle: "".to_string(),
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use blogs::Blog;
|
||||
use lists::{self, ListType};
|
||||
use crate::{
|
||||
blogs::Blog,
|
||||
lists::{self, ListType},
|
||||
posts::Post,
|
||||
tags::Tag,
|
||||
timeline::Timeline,
|
||||
users::User,
|
||||
PlumeRocket, Result,
|
||||
};
|
||||
use plume_common::activity_pub::inbox::AsActor;
|
||||
use posts::Post;
|
||||
use tags::Tag;
|
||||
use users::User;
|
||||
use whatlang::{self, Lang};
|
||||
|
||||
use {PlumeRocket, Result};
|
||||
|
||||
use super::Timeline;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum QueryError {
|
||||
SyntaxError(usize, usize, String),
|
||||
@@ -65,7 +65,7 @@ impl<'a> Token<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_error<T>(&self, token: Token) -> QueryResult<T> {
|
||||
fn get_error<T>(&self, token: Token<'_>) -> QueryResult<T> {
|
||||
let (b, e) = self.get_pos();
|
||||
let message = format!(
|
||||
"Syntax Error: Expected {}, got {}",
|
||||
@@ -79,7 +79,7 @@ impl<'a> Token<'a> {
|
||||
impl<'a> ToString for Token<'a> {
|
||||
fn to_string(&self) -> String {
|
||||
if let Token::Word(0, 0, v) = self {
|
||||
return v.to_string();
|
||||
return (*v).to_string();
|
||||
}
|
||||
format!(
|
||||
"'{}'",
|
||||
@@ -127,7 +127,7 @@ macro_rules! gen_tokenizer {
|
||||
}
|
||||
}
|
||||
|
||||
fn lex(stream: &str) -> Vec<Token> {
|
||||
fn lex(stream: &str) -> Vec<Token<'_>> {
|
||||
stream
|
||||
.chars()
|
||||
.chain(" ".chars()) // force a last whitespace to empty scan's state
|
||||
@@ -163,7 +163,7 @@ impl<'a> TQ<'a> {
|
||||
rocket: &PlumeRocket,
|
||||
timeline: &Timeline,
|
||||
post: &Post,
|
||||
kind: Kind,
|
||||
kind: Kind<'_>,
|
||||
) -> Result<bool> {
|
||||
match self {
|
||||
TQ::Or(inner) => inner.iter().try_fold(false, |s, e| {
|
||||
@@ -181,7 +181,7 @@ impl<'a> TQ<'a> {
|
||||
TQ::Or(inner) => inner.iter().flat_map(TQ::list_used_lists).collect(),
|
||||
TQ::And(inner) => inner.iter().flat_map(TQ::list_used_lists).collect(),
|
||||
TQ::Arg(Arg::In(typ, List::List(name)), _) => vec![(
|
||||
name.to_string(),
|
||||
(*name).to_string(),
|
||||
match typ {
|
||||
WithList::Blog => ListType::Blog,
|
||||
WithList::Author { .. } => ListType::User,
|
||||
@@ -208,7 +208,7 @@ impl<'a> Arg<'a> {
|
||||
rocket: &PlumeRocket,
|
||||
timeline: &Timeline,
|
||||
post: &Post,
|
||||
kind: Kind,
|
||||
kind: Kind<'_>,
|
||||
) -> Result<bool> {
|
||||
match self {
|
||||
Arg::In(t, l) => t.matches(rocket, timeline, post, l, kind),
|
||||
@@ -233,8 +233,8 @@ impl WithList {
|
||||
rocket: &PlumeRocket,
|
||||
timeline: &Timeline,
|
||||
post: &Post,
|
||||
list: &List,
|
||||
kind: Kind,
|
||||
list: &List<'_>,
|
||||
kind: Kind<'_>,
|
||||
) -> Result<bool> {
|
||||
match list {
|
||||
List::List(name) => {
|
||||
@@ -290,7 +290,8 @@ impl WithList {
|
||||
(_, _) => Err(QueryError::RuntimeError(format!(
|
||||
"The list '{}' is of the wrong type for this usage",
|
||||
name
|
||||
)))?,
|
||||
))
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
List::Array(list) => match self {
|
||||
@@ -373,7 +374,7 @@ impl Bool {
|
||||
rocket: &PlumeRocket,
|
||||
timeline: &Timeline,
|
||||
post: &Post,
|
||||
kind: Kind,
|
||||
kind: Kind<'_>,
|
||||
) -> Result<bool> {
|
||||
match self {
|
||||
Bool::Followed { boosts, likes } => {
|
||||
@@ -405,8 +406,8 @@ impl Bool {
|
||||
}
|
||||
}
|
||||
Bool::HasCover => Ok(post.cover_id.is_some()),
|
||||
Bool::Local => Ok(post.get_blog(&rocket.conn)?.is_local()),
|
||||
Bool::All => Ok(true),
|
||||
Bool::Local => Ok(post.get_blog(&rocket.conn)?.is_local() && kind == Kind::Original),
|
||||
Bool::All => Ok(kind == Kind::Original),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -644,7 +645,7 @@ impl<'a> TimelineQuery<'a> {
|
||||
rocket: &PlumeRocket,
|
||||
timeline: &Timeline,
|
||||
post: &Post,
|
||||
kind: Kind,
|
||||
kind: Kind<'_>,
|
||||
) -> Result<bool> {
|
||||
self.0.matches(rocket, timeline, post, kind)
|
||||
}
|
||||
@@ -811,9 +812,17 @@ mod tests {
|
||||
);
|
||||
|
||||
let expect_keyword = TimelineQuery::parse(r#"not_a_field contains something"#).unwrap_err();
|
||||
assert_eq!(expect_keyword, QueryError::SyntaxError(0, 11, "Syntax Error: Expected one of 'blog', \
|
||||
assert_eq!(
|
||||
expect_keyword,
|
||||
QueryError::SyntaxError(
|
||||
0,
|
||||
11,
|
||||
"Syntax Error: Expected one of 'blog', \
|
||||
'author', 'license', 'tags', 'lang', 'title', 'subtitle', 'content', 'followed', 'has_cover', \
|
||||
'local' or 'all', got 'not_a_field'".to_owned()));
|
||||
'local' or 'all', got 'not_a_field'"
|
||||
.to_owned()
|
||||
)
|
||||
);
|
||||
|
||||
let expect_bracket_or_comma = TimelineQuery::parse(r#"lang in [en ["#).unwrap_err();
|
||||
assert_eq!(
|
||||
|
||||
+47
-43
@@ -1,3 +1,9 @@
|
||||
use crate::{
|
||||
ap_url, blocklisted_emails::BlocklistedEmail, blogs::Blog, db_conn::DbConn, follows::Follow,
|
||||
instance::*, medias::Media, notifications::Notification, post_authors::PostAuthor, posts::Post,
|
||||
safe_string::SafeString, schema::users, search::Searcher, timeline::Timeline, Connection,
|
||||
Error, PlumeRocket, Result, ITEMS_PER_PAGE,
|
||||
};
|
||||
use activitypub::{
|
||||
activity::Delete,
|
||||
actor::Person,
|
||||
@@ -14,13 +20,15 @@ use openssl::{
|
||||
rsa::Rsa,
|
||||
sign,
|
||||
};
|
||||
use plume_common::activity_pub::{
|
||||
ap_accept_header,
|
||||
inbox::{AsActor, AsObject, FromId},
|
||||
sign::{gen_keypair, Signer},
|
||||
ActivityStream, ApSignature, Id, IntoId, PublicKey, PUBLIC_VISIBILITY,
|
||||
use plume_common::{
|
||||
activity_pub::{
|
||||
ap_accept_header,
|
||||
inbox::{AsActor, AsObject, FromId},
|
||||
sign::{gen_keypair, Signer},
|
||||
ActivityStream, ApSignature, Id, IntoId, PublicKey, PUBLIC_VISIBILITY,
|
||||
},
|
||||
utils,
|
||||
};
|
||||
use plume_common::utils;
|
||||
use reqwest::{
|
||||
header::{HeaderValue, ACCEPT},
|
||||
ClientBuilder,
|
||||
@@ -37,20 +45,6 @@ use std::{
|
||||
use url::Url;
|
||||
use webfinger::*;
|
||||
|
||||
use blogs::Blog;
|
||||
use db_conn::DbConn;
|
||||
use follows::Follow;
|
||||
use instance::*;
|
||||
use medias::Media;
|
||||
use notifications::Notification;
|
||||
use post_authors::PostAuthor;
|
||||
use posts::Post;
|
||||
use safe_string::SafeString;
|
||||
use schema::users;
|
||||
use search::Searcher;
|
||||
use timeline::Timeline;
|
||||
use {ap_url, Connection, Error, PlumeRocket, Result, ITEMS_PER_PAGE};
|
||||
|
||||
pub type CustomPerson = CustomObject<ApSignature, Person>;
|
||||
|
||||
pub enum Role {
|
||||
@@ -60,6 +54,7 @@ pub enum Role {
|
||||
}
|
||||
|
||||
#[derive(Queryable, Identifiable, Clone, Debug, AsChangeset)]
|
||||
#[changeset_options(treat_none_as_null = "true")]
|
||||
pub struct User {
|
||||
pub id: i32,
|
||||
pub username: String,
|
||||
@@ -136,7 +131,7 @@ impl User {
|
||||
}
|
||||
|
||||
pub fn delete(&self, conn: &Connection, searcher: &Searcher) -> Result<()> {
|
||||
use schema::post_authors;
|
||||
use crate::schema::post_authors;
|
||||
|
||||
for blog in Blog::find_for_author(conn, self)?
|
||||
.iter()
|
||||
@@ -461,8 +456,8 @@ impl User {
|
||||
.collect::<Vec<String>>())
|
||||
}
|
||||
fn get_activities_count(&self, conn: &Connection) -> i64 {
|
||||
use schema::post_authors;
|
||||
use schema::posts;
|
||||
use crate::schema::post_authors;
|
||||
use crate::schema::posts;
|
||||
let posts_by_self = PostAuthor::belonging_to(self).select(post_authors::post_id);
|
||||
posts::table
|
||||
.filter(posts::published.eq(true))
|
||||
@@ -476,8 +471,8 @@ impl User {
|
||||
conn: &Connection,
|
||||
(min, max): (i32, i32),
|
||||
) -> Result<Vec<serde_json::Value>> {
|
||||
use schema::post_authors;
|
||||
use schema::posts;
|
||||
use crate::schema::post_authors;
|
||||
use crate::schema::posts;
|
||||
let posts_by_self = PostAuthor::belonging_to(self).select(post_authors::post_id);
|
||||
let posts = posts::table
|
||||
.filter(posts::published.eq(true))
|
||||
@@ -497,7 +492,7 @@ impl User {
|
||||
}
|
||||
|
||||
pub fn get_followers(&self, conn: &Connection) -> Result<Vec<User>> {
|
||||
use schema::follows;
|
||||
use crate::schema::follows;
|
||||
let follows = Follow::belonging_to(self).select(follows::follower_id);
|
||||
users::table
|
||||
.filter(users::id.eq_any(follows))
|
||||
@@ -506,7 +501,7 @@ impl User {
|
||||
}
|
||||
|
||||
pub fn count_followers(&self, conn: &Connection) -> Result<i64> {
|
||||
use schema::follows;
|
||||
use crate::schema::follows;
|
||||
let follows = Follow::belonging_to(self).select(follows::follower_id);
|
||||
users::table
|
||||
.filter(users::id.eq_any(follows))
|
||||
@@ -520,7 +515,7 @@ impl User {
|
||||
conn: &Connection,
|
||||
(min, max): (i32, i32),
|
||||
) -> Result<Vec<User>> {
|
||||
use schema::follows;
|
||||
use crate::schema::follows;
|
||||
let follows = Follow::belonging_to(self).select(follows::follower_id);
|
||||
users::table
|
||||
.filter(users::id.eq_any(follows))
|
||||
@@ -531,7 +526,7 @@ impl User {
|
||||
}
|
||||
|
||||
pub fn get_followed(&self, conn: &Connection) -> Result<Vec<User>> {
|
||||
use schema::follows::dsl::*;
|
||||
use crate::schema::follows::dsl::*;
|
||||
let f = follows.filter(follower_id.eq(self.id)).select(following_id);
|
||||
users::table
|
||||
.filter(users::id.eq_any(f))
|
||||
@@ -540,7 +535,7 @@ impl User {
|
||||
}
|
||||
|
||||
pub fn count_followed(&self, conn: &Connection) -> Result<i64> {
|
||||
use schema::follows;
|
||||
use crate::schema::follows;
|
||||
follows::table
|
||||
.filter(follows::follower_id.eq(self.id))
|
||||
.count()
|
||||
@@ -553,7 +548,7 @@ impl User {
|
||||
conn: &Connection,
|
||||
(min, max): (i32, i32),
|
||||
) -> Result<Vec<User>> {
|
||||
use schema::follows;
|
||||
use crate::schema::follows;
|
||||
let follows = follows::table
|
||||
.filter(follows::follower_id.eq(self.id))
|
||||
.select(follows::following_id)
|
||||
@@ -566,7 +561,7 @@ impl User {
|
||||
}
|
||||
|
||||
pub fn is_followed_by(&self, conn: &Connection, other_id: i32) -> Result<bool> {
|
||||
use schema::follows;
|
||||
use crate::schema::follows;
|
||||
follows::table
|
||||
.filter(follows::follower_id.eq(other_id))
|
||||
.filter(follows::following_id.eq(self.id))
|
||||
@@ -577,7 +572,7 @@ impl User {
|
||||
}
|
||||
|
||||
pub fn is_following(&self, conn: &Connection, other_id: i32) -> Result<bool> {
|
||||
use schema::follows;
|
||||
use crate::schema::follows;
|
||||
follows::table
|
||||
.filter(follows::follower_id.eq(self.id))
|
||||
.filter(follows::following_id.eq(other_id))
|
||||
@@ -588,7 +583,7 @@ impl User {
|
||||
}
|
||||
|
||||
pub fn has_liked(&self, conn: &Connection, post: &Post) -> Result<bool> {
|
||||
use schema::likes;
|
||||
use crate::schema::likes;
|
||||
likes::table
|
||||
.filter(likes::post_id.eq(post.id))
|
||||
.filter(likes::user_id.eq(self.id))
|
||||
@@ -599,7 +594,7 @@ impl User {
|
||||
}
|
||||
|
||||
pub fn has_reshared(&self, conn: &Connection, post: &Post) -> Result<bool> {
|
||||
use schema::reshares;
|
||||
use crate::schema::reshares;
|
||||
reshares::table
|
||||
.filter(reshares::post_id.eq(post.id))
|
||||
.filter(reshares::user_id.eq(self.id))
|
||||
@@ -610,7 +605,7 @@ impl User {
|
||||
}
|
||||
|
||||
pub fn is_author_in(&self, conn: &Connection, blog: &Blog) -> Result<bool> {
|
||||
use schema::blog_authors;
|
||||
use crate::schema::blog_authors;
|
||||
blog_authors::table
|
||||
.filter(blog_authors::author_id.eq(self.id))
|
||||
.filter(blog_authors::blog_id.eq(blog.id))
|
||||
@@ -765,7 +760,7 @@ impl User {
|
||||
mime_type: None,
|
||||
href: None,
|
||||
template: Some(format!(
|
||||
"https://{}/remote_interact?{{uri}}",
|
||||
"https://{}/remote_interact?target={{uri}}",
|
||||
self.get_instance(conn)?.public_domain
|
||||
)),
|
||||
},
|
||||
@@ -810,7 +805,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for User {
|
||||
|
||||
impl IntoId for User {
|
||||
fn into_id(self) -> Id {
|
||||
Id::new(self.ap_url.clone())
|
||||
Id::new(self.ap_url)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -992,6 +987,10 @@ impl NewUser {
|
||||
) -> Result<User> {
|
||||
let (pub_key, priv_key) = gen_keypair();
|
||||
let instance = Instance::get_local()?;
|
||||
let blocklisted = BlocklistedEmail::matches_blocklist(conn, &email)?;
|
||||
if let Some(x) = blocklisted {
|
||||
return Err(Error::Blocklisted(x.notify_user, x.notification_text));
|
||||
}
|
||||
|
||||
let res = User::insert(
|
||||
conn,
|
||||
@@ -1026,11 +1025,14 @@ impl NewUser {
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
config::CONFIG,
|
||||
instance::{tests as instance_tests, Instance},
|
||||
search::tests::get_searcher,
|
||||
tests::{db, rockets},
|
||||
Connection as Conn,
|
||||
};
|
||||
use diesel::Connection;
|
||||
use instance::{tests as instance_tests, Instance};
|
||||
use search::tests::get_searcher;
|
||||
use tests::{db, rockets};
|
||||
use Connection as Conn;
|
||||
|
||||
pub(crate) fn fill_database(conn: &Conn) -> Vec<User> {
|
||||
instance_tests::fill_database(conn);
|
||||
@@ -1121,7 +1123,9 @@ pub(crate) mod tests {
|
||||
let inserted = fill_database(conn);
|
||||
|
||||
assert!(User::get(conn, inserted[0].id).is_ok());
|
||||
inserted[0].delete(conn, &get_searcher()).unwrap();
|
||||
inserted[0]
|
||||
.delete(conn, &get_searcher(&CONFIG.search_tokenizers))
|
||||
.unwrap();
|
||||
assert!(User::get(conn, inserted[0].id).is_err());
|
||||
Ok(())
|
||||
});
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
extern crate diesel;
|
||||
extern crate plume_common;
|
||||
extern crate plume_models;
|
||||
|
||||
use diesel::Connection;
|
||||
use plume_common::utils::random_hex;
|
||||
use plume_models::migrations::IMPORTED_MIGRATIONS;
|
||||
|
||||
+13
-14
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 19:54\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:06\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Afrikaans\n"
|
||||
"Language: af_ZA\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: af\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
msgid "Subtitle or summary"
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
|
||||
|
||||
+18
-20
@@ -3,57 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 19:54\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:06\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Arabic\n"
|
||||
"Language: ar_SA\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\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"
|
||||
"X-Generator: crowdin.com\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-Language: ar\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
msgstr "فتح محرر النصوص الغني"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "العنوان"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
#, fuzzy
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "العنوان الثانوي أو الملخص"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
msgstr "اكتب مقالك هنا. ماركداون مُدَعَّم."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
msgstr "يتبقا {} حرفا تقريبا"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "الوسوم"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "الرخصة"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr "الغلاف"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
msgstr "هذه مسودة"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "نشر كتابا"
|
||||
|
||||
|
||||
+22
-22
@@ -3,55 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:06\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Bulgarian\n"
|
||||
"Language: bg_BG\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: bg\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
msgstr "Отворете редактора с богат текст"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
msgstr "Заглавие"
|
||||
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
msgstr "Подзаглавие или резюме"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
msgstr "Напишете статията си тук. Поддържа се Markdown."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
msgstr "Остават {} знака вляво"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
msgstr "Етикети"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
msgstr "Лиценз"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
msgstr "Основно изображение"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
msgstr "Това е проект"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
msgstr "Публикувай"
|
||||
|
||||
|
||||
+22
-22
@@ -3,55 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 19:55\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:06\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Catalan\n"
|
||||
"Language: ca_ES\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: ca\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
msgstr "Obre l’editor de text enriquit"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
msgstr "Títol"
|
||||
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
msgstr "Subtítol o resum"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
msgstr "Escriviu el vostre article ací. Podeu fer servir el Markdown."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
msgstr "Queden uns {} caràcters"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
msgstr "Etiquetes"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
msgstr "Llicència"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
msgstr "Coberta"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
msgstr "Açò és un esborrany"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
msgstr "Publica"
|
||||
|
||||
|
||||
+14
-15
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:06\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Czech\n"
|
||||
"Language: cs_CZ\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3;\n"
|
||||
"X-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: cs\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr "Otevřít editor formátovaného textu"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "Nadpis"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
#, fuzzy
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "Podnadpis, nebo shrnutí"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr "Napište sem 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:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr "Zbývá kolem {} znaků"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "Tagy"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "Licence"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr "Titulka"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr "Tohle je koncept"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "Zveřejnit"
|
||||
|
||||
|
||||
+13
-14
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:06\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Danish\n"
|
||||
"Language: da_DK\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: da\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
msgid "Subtitle or summary"
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
|
||||
|
||||
+17
-18
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:14\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de_DE\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: de\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
msgstr " Rich Text Editor (RTE) öffnen"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "Titel"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
#, fuzzy
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "Untertitel oder Übersicht"
|
||||
msgstr "Untertitel oder Zusammenfassung"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr "Schreibe deinen Artikel hier. Markdown ist unterstützt."
|
||||
msgstr "Schreiben deinen Artikel hier. Markdown wird unterstützt."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr "Ungefähr {} Zeichen übrig"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "Tags"
|
||||
msgstr "Schlagwörter"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "Lizenz"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr "Einband"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr "Dies ist ein Entwurf"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "Veröffentlichen"
|
||||
|
||||
|
||||
+13
-14
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:14\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Greek\n"
|
||||
"Language: el_GR\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: el\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
msgid "Subtitle or summary"
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
|
||||
|
||||
+12
-12
@@ -3,24 +3,23 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: English\n"
|
||||
"Language: en_US\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: en\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
@@ -28,30 +27,31 @@ msgstr ""
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
|
||||
|
||||
+14
-15
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Esperanto\n"
|
||||
"Language: eo_UY\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: eo\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr "Malfermi la riĉan redaktilon"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "Titolo"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
#, fuzzy
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "Subtitolo aŭ resumo"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr "Verku vian artikolon ĉi tie. Markdown estas subtenita."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr "Proksimume {} signoj restantaj"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "Etikedoj"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "Permesilo"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr "Kovro"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr "Malfinias"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "Eldoni"
|
||||
|
||||
|
||||
+15
-16
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Spanish\n"
|
||||
"Language: es_ES\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: es-ES\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr "Abrir el editor de texto enriquecido"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "Título"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
#, fuzzy
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "Subtítulo o resumen"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr "Escriba su artículo aquí. Puede utilizar Markdown."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr "Quedan unos {} caracteres"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetas"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "Licencia"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr "Cobertura"
|
||||
msgstr "Cubierta"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr "Esto es un borrador"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "Publicar"
|
||||
|
||||
|
||||
+22
-23
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 19:55\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Persian\n"
|
||||
"Language: fa_IR\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: fa\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
msgstr "باز کردن ویرایشگر غنی"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
msgstr "عنوان"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
msgid "Subtitle or summary"
|
||||
msgstr ""
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "زیرعنوان، یا چکیده"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
msgstr "مقاله را اینجا بنویسید. از مارکداون پشتیبانی میشود."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
msgstr "نزدیک به {} نویسه باقی مانده است"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
msgstr "برچسبها"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
msgstr "پروانه"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
msgstr "این، یک پیشنویس است"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
msgstr "انتشار"
|
||||
|
||||
|
||||
+23
-24
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:14\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Finnish\n"
|
||||
"Language: fi_FI\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: fi\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
msgstr "Avaa edistynyt tekstieditori"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
msgstr "Otsikko"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
msgid "Subtitle or summary"
|
||||
msgstr ""
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "Alaotsikko tai tiivistelmä"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
msgstr "Kirjoita artikkelisi tähän. Markdown -kuvauskieli on tuettu."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
msgstr "%{count} merkkiä jäljellä"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
msgstr "Tagit"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
msgstr "Lisenssi"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
msgstr "Kansi"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
msgstr "Tämä on luonnos"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
msgstr "Julkaise"
|
||||
|
||||
|
||||
+14
-15
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:14\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: French\n"
|
||||
"Language: fr_FR\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-Generator: crowdin.com\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: fr\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr "Ouvrir l'éditeur de texte avancé"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "Titre"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
#, fuzzy
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "Sous-titre ou résumé"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr "Écrivez votre article ici. Vous pouvez utiliser du Markdown."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr "Environ {} caractères restant"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "Étiquettes"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "Licence"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr "Illustration"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr "Ceci est un brouillon"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "Publier"
|
||||
|
||||
|
||||
+14
-15
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:14\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Galician\n"
|
||||
"Language: gl_ES\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: gl\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr "Abra o editor de texto enriquecido"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "Título"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
#, fuzzy
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "Subtítulo ou resumo"
|
||||
msgstr "Subtítulo, ou resumo"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr "Escriba aquí o seu artigo: pode utilizar Markdown."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr "Dispón de {} caracteres restantes"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetas"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "Licenza"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr "Portada"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr "Este é un borrador"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "Publicar"
|
||||
|
||||
|
||||
+13
-14
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:14\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Hebrew\n"
|
||||
"Language: he_IL\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n"
|
||||
"X-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: he\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
msgid "Subtitle or summary"
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
|
||||
|
||||
+14
-15
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:14\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Hindi\n"
|
||||
"Language: hi_IN\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: hi\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "शीर्षक"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
#, fuzzy
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "उपशीर्षक या सारांश"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr "अपना आर्टिकल या लेख यहाँ लिखें. Markdown उपलब्ध है."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr "लगभग {} अक्षर बाकी हैं"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "टैग्स"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "लाइसेंस"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "पब्लिश करें"
|
||||
|
||||
|
||||
+15
-17
@@ -3,57 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:06\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Croatian\n"
|
||||
"Language: hr_HR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"X-Generator: crowdin.com\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: hr\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "Naslov"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
#, fuzzy
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "Podnaslov ili sažetak"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "Tagovi"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "Licenca"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "Objavi"
|
||||
|
||||
|
||||
+13
-14
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Hungarian\n"
|
||||
"Language: hu_HU\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: hu\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
msgid "Subtitle or summary"
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
|
||||
|
||||
+14
-15
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Italian\n"
|
||||
"Language: it_IT\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: it\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr "Apri il compositore di testo avanzato"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "Titolo"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
#, fuzzy
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "Sottotitolo o sommario"
|
||||
msgstr "Sottotitolo, o sommario"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr "Scrivi qui il tuo articolo. È supportato il Markdown."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr "Circa {} caratteri rimasti"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "Etichette"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "Licenza"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr "Copertina"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr "Questa è una bozza"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "Pubblica"
|
||||
|
||||
|
||||
+14
-15
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Japanese\n"
|
||||
"Language: ja_JP\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: ja\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr "リッチテキストエディターを開く"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "タイトル"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
#, fuzzy
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "サブタイトルまたは概要"
|
||||
msgstr "サブタイトル、または概要"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr "投稿をここに書きます。Markdown がサポートされています。"
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr "残り約 {} 文字"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "タグ"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "ライセンス"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr "カバー"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr "これは下書きです"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "公開"
|
||||
|
||||
|
||||
+13
-14
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Korean\n"
|
||||
"Language: ko_KR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: ko\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
msgid "Subtitle or summary"
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
|
||||
|
||||
+13
-14
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:06\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Dutch\n"
|
||||
"Language: nl_NL\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: nl\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
msgid "Subtitle or summary"
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
|
||||
|
||||
+13
-14
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 19:55\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Norwegian\n"
|
||||
"Language: no_NO\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: no\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
msgid "Subtitle or summary"
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
|
||||
|
||||
+15
-17
@@ -3,57 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 19:55\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Polish\n"
|
||||
"Language: pl_PL\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n"
|
||||
"%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n"
|
||||
"%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
|
||||
"X-Generator: crowdin.com\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: pl\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr "Otwórz edytor tekstu sformatowanego"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "Tytuł"
|
||||
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
msgstr "Podtytuł, lub podsumowanie"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr "Napisz swój artykuł tutaj. Markdown jest obsługiwany."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr "Pozostało w okolicy {} znaków"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "Tagi"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "Licencja"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr "Okładka"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr "To jest szkic"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "Opublikuj"
|
||||
msgstr "Publikuj"
|
||||
|
||||
|
||||
+19
-20
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-18 07:05\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"Language-Team: Portuguese\n"
|
||||
"Language: pt_PT\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Portuguese, Brazilian\n"
|
||||
"Language: pt_BR\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: pt-PT\n"
|
||||
"X-Crowdin-Language: pt-BR\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
msgstr "Abrir o editor de rich text"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "Título"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
#, fuzzy
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "Legenda ou resumo"
|
||||
msgstr "Subtítulo ou resumo"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr "Escreva seu artigo aqui. Markdown pode ser usado."
|
||||
msgstr "Escreva seu artigo aqui. Markdown é suportado."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr "Cerca de {} caracteres restantes"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "Tags"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "Licença"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr "Capa"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr "Isso é um rascunho"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "Publicar"
|
||||
|
||||
|
||||
+15
-17
@@ -3,57 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 19:55\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Romanian\n"
|
||||
"Language: ro_RO\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100>0 && n"
|
||||
"%100<20)) ? 1 : 2);\n"
|
||||
"X-Generator: crowdin.com\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100>0 && n%100<20)) ? 1 : 2);\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: ro\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr "Deschide editorul de text"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "Titlu"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
#, fuzzy
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "Subtitlu sau rezumat"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr "Scrie articolul tău aici. Markdown este acceptat."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr "În apropiere de {} caractere rămase"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "Etichete"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "Licenţă"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr "Coperta"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr "Aceasta este o ciornă"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "Publică"
|
||||
|
||||
|
||||
+13
-15
@@ -3,26 +3,23 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 19:55\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Russian\n"
|
||||
"Language: ru_RU\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 "
|
||||
"&& n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 "
|
||||
"&& n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
|
||||
"X-Generator: crowdin.com\n"
|
||||
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: ru\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
@@ -30,30 +27,31 @@ msgstr ""
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
|
||||
|
||||
+13
-14
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Slovak\n"
|
||||
"Language: sk_SK\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3;\n"
|
||||
"X-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: sk\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr "Otvor editor formátovaného textu"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "Nadpis"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
#, fuzzy
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "Zhrnutie, alebo podnadpis"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr "Tu napíš svoj článok. Markdown je podporovaný."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr "Zostáva asi {} znakov"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "Štítky"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "Licencia"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr "Obálka"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr "Toto je koncept"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "Zverejniť"
|
||||
|
||||
|
||||
+13
-14
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 19:55\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Slovenian\n"
|
||||
"Language: sl_SI\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0);\n"
|
||||
"X-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: sl\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "Naslov"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
msgid "Subtitle or summary"
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "Oznake"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "Licenca"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr "To je osnutek"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "Objavi"
|
||||
|
||||
|
||||
+22
-23
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 20:49\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Serbian (Latin)\n"
|
||||
"Language: sr_CS\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"X-Generator: crowdin.com\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: sr-CS\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
msgstr "Otvori uređivač sa stilizacijom"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "Naslov"
|
||||
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
msgstr "Podnaslov, ili sažetak"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
msgstr "Napišite vaš članak ovde. Na raspolaganju vam je Markdown."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
msgstr "Preostalo oko {} znakova"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
msgstr "Markeri"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
msgstr "Licenca"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
msgstr "Naslovna strana"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
msgstr "Ovo je nacrt"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
msgstr "Objavi"
|
||||
|
||||
|
||||
+14
-15
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Swedish\n"
|
||||
"Language: sv_SE\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: sv-SE\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr "Titel"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
#, fuzzy
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "Undertitel eller sammanfattning"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr "Skriv din artikel här. Markdown stöds."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr "Ungefär {} karaktärer kvar"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr "Taggar"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr "Licens"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr "Omslag"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr "Publicera"
|
||||
|
||||
|
||||
+23
-24
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Turkish\n"
|
||||
"Language: tr_TR\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-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: tr\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
msgstr "Zengin metin editörünü (RTE) aç"
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
msgstr "Başlık"
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
msgid "Subtitle or summary"
|
||||
msgstr ""
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr "Alt başlık, veya açıklama"
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
msgstr "Makaleni buraya yaz. Markdown kullanabilirsin."
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
msgstr "Yaklaşık {} karakter kaldı"
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
msgstr "Etiketler"
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
msgstr "Lisans"
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
msgstr "Kapak"
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
msgstr "Bu bir taslaktır"
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
msgstr "Yayınla"
|
||||
|
||||
|
||||
+13
-14
@@ -3,56 +3,55 @@ msgstr ""
|
||||
"Project-Id-Version: plume\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-06-15 16:33-0700\n"
|
||||
"PO-Revision-Date: 2019-04-17 18:15\n"
|
||||
"Last-Translator: Ana Gelez (AnaGelez)\n"
|
||||
"PO-Revision-Date: 2019-12-16 21:07\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Ukrainian\n"
|
||||
"Language: uk_UA\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
|
||||
"X-Generator: crowdin.com\n"
|
||||
"X-Crowdin-Project: plume\n"
|
||||
"X-Crowdin-Language: uk\n"
|
||||
"X-Crowdin-File: /master/po/plume-front/plume-front.pot\n"
|
||||
|
||||
# plume-front/src/editor.rs:114
|
||||
# plume-front/src/editor.rs:115
|
||||
msgid "Open the rich text editor"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:143
|
||||
# plume-front/src/editor.rs:145
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:147
|
||||
msgid "Subtitle or summary"
|
||||
# plume-front/src/editor.rs:149
|
||||
msgid "Subtitle, or summary"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:154
|
||||
# plume-front/src/editor.rs:156
|
||||
msgid "Write your article here. Markdown is supported."
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:165
|
||||
# plume-front/src/editor.rs:167
|
||||
msgid "Around {} characters left"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:228
|
||||
# plume-front/src/editor.rs:243
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:229
|
||||
# plume-front/src/editor.rs:244
|
||||
msgid "License"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:232
|
||||
# plume-front/src/editor.rs:247
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:252
|
||||
# plume-front/src/editor.rs:267
|
||||
msgid "This is a draft"
|
||||
msgstr ""
|
||||
|
||||
# plume-front/src/editor.rs:259
|
||||
# plume-front/src/editor.rs:274
|
||||
msgid "Publish"
|
||||
msgstr ""
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user