feat: localization #94
12 changed files with 120 additions and 50 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -7,13 +7,13 @@ docker-compose.yml
|
||||||
/.vscode
|
/.vscode
|
||||||
|
|
||||||
# Data
|
# Data
|
||||||
data/index.md
|
data/index*.md
|
||||||
|
|
||||||
data/contacts/*
|
data/contacts/*
|
||||||
data/cours/*
|
data/cours/*
|
||||||
data/projects/*
|
data/projects/*
|
||||||
|
|
||||||
# Blog
|
# Blog
|
||||||
data/blog/*.md
|
data/blog/about*.md
|
||||||
data/blog/posts/*
|
data/blog/posts/*
|
||||||
!data/blog/posts/Makefile
|
!data/blog/posts/Makefile
|
||||||
|
|
|
@ -145,6 +145,8 @@ Markdown file
|
||||||
|
|
||||||
Markdown file is stored in `/app/data/index.md`
|
Markdown file is stored in `/app/data/index.md`
|
||||||
|
|
||||||
|
> For french clients, `/app/data/index-fr.md` will be read instead.
|
||||||
|
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
name: Option<String>
|
name: Option<String>
|
||||||
|
@ -188,6 +190,8 @@ Post content
|
||||||
|
|
||||||
The file is stored at `/app/data/blog/about.md`.
|
The file is stored at `/app/data/blog/about.md`.
|
||||||
|
|
||||||
|
> For french clients, `/app/data/blog/about-fr.md` will be read instead.
|
||||||
|
|
||||||
## Projects
|
## Projects
|
||||||
|
|
||||||
Markdown files are stored in `/app/data/projects/apps/`
|
Markdown files are stored in `/app/data/projects/apps/`
|
||||||
|
@ -214,6 +218,8 @@ files in `archive` subdirectory of `apps`.
|
||||||
|
|
||||||
The file is stored at `/app/data/projects/about.md`.
|
The file is stored at `/app/data/projects/about.md`.
|
||||||
|
|
||||||
|
> For french clients, `/app/data/projects/about-fr.md` will be read instead.
|
||||||
|
|
||||||
## Contacts
|
## Contacts
|
||||||
|
|
||||||
Markdown files are stored in `/app/data/contacts/`
|
Markdown files are stored in `/app/data/contacts/`
|
||||||
|
@ -254,6 +260,8 @@ For example, `socials` contact files are stored in `/app/data/contacts/socials/`
|
||||||
|
|
||||||
The file is stored at `/app/data/contacts/about.md`.
|
The file is stored at `/app/data/contacts/about.md`.
|
||||||
|
|
||||||
|
> For french clients, `/app/data/contacts/about-fr.md` will be read instead.
|
||||||
|
|
||||||
## Courses
|
## Courses
|
||||||
|
|
||||||
Markdown files are stored in `/app/data/cours/`
|
Markdown files are stored in `/app/data/cours/`
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::{fs::File, io::Write, path::Path};
|
||||||
use crate::template::Template;
|
use crate::template::Template;
|
||||||
|
|
||||||
/// Store the configuration of config/config.toml
|
/// Store the configuration of config/config.toml
|
||||||
#[derive(Clone, Debug, Default, Deserialize)]
|
#[derive(Clone, Debug, Default, Deserialize, Hash, PartialEq, Eq)]
|
||||||
pub struct FileConfiguration {
|
pub struct FileConfiguration {
|
||||||
/// http/https
|
/// http/https
|
||||||
pub scheme: Option<String>,
|
pub scheme: Option<String>,
|
||||||
|
@ -75,14 +75,14 @@ impl FileConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Paths where files are stored
|
// Paths where files are stored
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct Locations {
|
pub struct Locations {
|
||||||
pub static_dir: String,
|
pub static_dir: String,
|
||||||
pub data_dir: String,
|
pub data_dir: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configuration used internally in the app
|
/// Configuration used internally in the app
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// Information given in the config file
|
/// Information given in the config file
|
||||||
pub fc: FileConfiguration,
|
pub fc: FileConfiguration,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use actix_web::{get, http::header::ContentType, routes, web, HttpResponse, Responder};
|
use actix_web::{
|
||||||
use cached::proc_macro::once;
|
get, http::header::ContentType, routes, web, HttpRequest, HttpResponse, Responder,
|
||||||
|
};
|
||||||
|
use cached::proc_macro::cached;
|
||||||
use ramhorns::Content;
|
use ramhorns::Content;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -8,14 +10,17 @@ use crate::{
|
||||||
utils::{
|
utils::{
|
||||||
markdown::{File, FilePath},
|
markdown::{File, FilePath},
|
||||||
metadata::MType,
|
metadata::MType,
|
||||||
misc::{make_kw, read_file, Html},
|
misc::{lang, make_kw, read_file_fallback, Html, Lang},
|
||||||
routes::blog::{build_rss, get_post, get_posts, Post, BLOG_DIR, MIME_TYPE_RSS, POST_DIR},
|
routes::blog::{build_rss, get_post, get_posts, Post, BLOG_DIR, MIME_TYPE_RSS, POST_DIR},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[get("/blog")]
|
#[get("/blog")]
|
||||||
pub async fn index(config: web::Data<Config>) -> impl Responder {
|
pub async fn index(req: HttpRequest, config: web::Data<Config>) -> impl Responder {
|
||||||
Html(build_index(config.get_ref().to_owned()))
|
Html(build_index(
|
||||||
|
config.get_ref().to_owned(),
|
||||||
|
lang(req.headers()),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Content, Debug)]
|
#[derive(Content, Debug)]
|
||||||
|
@ -26,18 +31,19 @@ struct BlogIndexTemplate {
|
||||||
no_posts: bool,
|
no_posts: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[once(time = 60)]
|
#[cached(time = 60)]
|
||||||
fn build_index(config: Config) -> String {
|
fn build_index(config: Config, lang: Lang) -> String {
|
||||||
let blog_dir = format!("{}/{}", config.locations.data_dir, BLOG_DIR);
|
let blog_dir = format!("{}/{}", config.locations.data_dir, BLOG_DIR);
|
||||||
let mut posts = get_posts(&format!("{blog_dir}/{POST_DIR}"));
|
let mut posts = get_posts(&format!("{blog_dir}/{POST_DIR}"));
|
||||||
|
|
||||||
// Get about
|
// Get about
|
||||||
let about: Option<File> = read_file(
|
let about = read_file_fallback(
|
||||||
FilePath {
|
FilePath {
|
||||||
base: blog_dir,
|
base: blog_dir,
|
||||||
path: "about.md".to_owned(),
|
path: "about.md".to_owned(),
|
||||||
},
|
},
|
||||||
MType::Generic,
|
MType::Generic,
|
||||||
|
lang,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Sort from newest to oldest
|
// Sort from newest to oldest
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use actix_web::{get, routes, web, HttpRequest, Responder};
|
use actix_web::{get, routes, web, HttpRequest, Responder};
|
||||||
use cached::proc_macro::once;
|
use cached::proc_macro::cached;
|
||||||
use ramhorns::Content;
|
use ramhorns::Content;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
utils::{
|
utils::{
|
||||||
markdown::{File, FilePath},
|
markdown::{File, FilePath},
|
||||||
metadata::MType,
|
metadata::MType,
|
||||||
misc::{make_kw, read_file, Html},
|
misc::{lang, make_kw, read_file_fallback, Html, Lang},
|
||||||
routes::contact::{find_links, read, remove_paragraphs},
|
routes::contact::{find_links, read, remove_paragraphs},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -28,8 +28,8 @@ pub fn pages(cfg: &mut web::ServiceConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("")]
|
#[get("")]
|
||||||
async fn page(config: web::Data<Config>) -> impl Responder {
|
async fn page(req: HttpRequest, config: web::Data<Config>) -> impl Responder {
|
||||||
Html(build_page(config.get_ref().to_owned()))
|
Html(build_page(config.get_ref().to_owned(), lang(req.headers())))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[routes]
|
#[routes]
|
||||||
|
@ -78,18 +78,19 @@ struct NetworksTemplate {
|
||||||
others: Vec<File>,
|
others: Vec<File>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[once(time = 60)]
|
#[cached(time = 60)]
|
||||||
fn build_page(config: Config) -> String {
|
fn build_page(config: Config, lang: Lang) -> String {
|
||||||
let contacts_dir = format!("{}/{}", config.locations.data_dir, CONTACT_DIR);
|
let contacts_dir = format!("{}/{}", config.locations.data_dir, CONTACT_DIR);
|
||||||
let ext = ".md";
|
let ext = ".md";
|
||||||
|
|
||||||
// Get about
|
// Get about
|
||||||
let about = read_file(
|
let about = read_file_fallback(
|
||||||
FilePath {
|
FilePath {
|
||||||
base: contacts_dir.clone(),
|
base: contacts_dir.clone(),
|
||||||
path: "about.md".to_owned(),
|
path: "about.md".to_owned(),
|
||||||
},
|
},
|
||||||
MType::Generic,
|
MType::Generic,
|
||||||
|
lang,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut socials = read(&FilePath {
|
let mut socials = read(&FilePath {
|
||||||
|
|
|
@ -60,13 +60,14 @@ fn get_content(
|
||||||
path: filename.to_owned(),
|
path: filename.to_owned(),
|
||||||
},
|
},
|
||||||
MType::Cours,
|
MType::Cours,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_page(info: &web::Query<PathRequest>, config: Config) -> String {
|
fn build_page(info: &web::Query<PathRequest>, config: Config) -> String {
|
||||||
let cours_dir = "data/cours";
|
let cours_dir = "data/cours";
|
||||||
|
|
||||||
let (ep, el): (_, Vec<String>) = config
|
let (ep, el): (_, Vec<_>) = config
|
||||||
.fc
|
.fc
|
||||||
.exclude_courses
|
.exclude_courses
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use actix_web::{get, web, Responder};
|
use actix_web::{get, web, HttpRequest, Responder};
|
||||||
use cached::proc_macro::once;
|
use cached::proc_macro::cached;
|
||||||
use ramhorns::Content;
|
use ramhorns::Content;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -8,13 +8,13 @@ use crate::{
|
||||||
utils::{
|
utils::{
|
||||||
markdown::{File, FilePath},
|
markdown::{File, FilePath},
|
||||||
metadata::MType,
|
metadata::MType,
|
||||||
misc::{make_kw, read_file, Html},
|
misc::{lang, make_kw, read_file, read_file_fallback, Html, Lang},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
pub async fn page(config: web::Data<Config>) -> impl Responder {
|
pub async fn page(req: HttpRequest, config: web::Data<Config>) -> impl Responder {
|
||||||
Html(build_page(config.get_ref().to_owned()))
|
Html(build_page(config.get_ref().to_owned(), lang(req.headers())))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Content, Debug)]
|
#[derive(Content, Debug)]
|
||||||
|
@ -34,14 +34,15 @@ struct StyleAvatar {
|
||||||
square: bool,
|
square: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[once(time = 60)]
|
#[cached(time = 60)]
|
||||||
fn build_page(config: Config) -> String {
|
fn build_page(config: Config, lang: Lang) -> String {
|
||||||
let mut file = read_file(
|
let mut file = read_file_fallback(
|
||||||
FilePath {
|
FilePath {
|
||||||
base: config.locations.data_dir,
|
base: config.locations.data_dir.clone(),
|
||||||
path: "index.md".to_owned(),
|
path: "index.md".to_owned(),
|
||||||
},
|
},
|
||||||
MType::Index,
|
MType::Index,
|
||||||
|
lang,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Default values
|
// Default values
|
||||||
|
@ -77,6 +78,7 @@ fn build_page(config: Config) -> String {
|
||||||
path: "README.md".to_owned(),
|
path: "README.md".to_owned(),
|
||||||
},
|
},
|
||||||
MType::Generic,
|
MType::Generic,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use actix_web::{get, web, Responder};
|
use actix_web::{get, web, HttpRequest, Responder};
|
||||||
use cached::proc_macro::once;
|
use cached::proc_macro::cached;
|
||||||
use glob::glob;
|
use glob::glob;
|
||||||
use ramhorns::Content;
|
use ramhorns::Content;
|
||||||
|
|
||||||
|
@ -9,13 +9,13 @@ use crate::{
|
||||||
utils::{
|
utils::{
|
||||||
markdown::{File, FilePath},
|
markdown::{File, FilePath},
|
||||||
metadata::MType,
|
metadata::MType,
|
||||||
misc::{make_kw, read_file, Html},
|
misc::{lang, make_kw, read_file, read_file_fallback, Html, Lang},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[get("/portfolio")]
|
#[get("/portfolio")]
|
||||||
pub async fn page(config: web::Data<Config>) -> impl Responder {
|
pub async fn page(req: HttpRequest, config: web::Data<Config>) -> impl Responder {
|
||||||
Html(build_page(config.get_ref().to_owned()))
|
Html(build_page(config.get_ref().to_owned(), lang(req.headers())))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Content, Debug)]
|
#[derive(Content, Debug)]
|
||||||
|
@ -29,8 +29,8 @@ struct PortfolioTemplate<'a> {
|
||||||
err_msg: &'a str,
|
err_msg: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[once(time = 60)]
|
#[cached(time = 60)]
|
||||||
fn build_page(config: Config) -> String {
|
fn build_page(config: Config, lang: Lang) -> String {
|
||||||
let projects_dir = format!("{}/projects", config.locations.data_dir);
|
let projects_dir = format!("{}/projects", config.locations.data_dir);
|
||||||
let apps_dir = FilePath {
|
let apps_dir = FilePath {
|
||||||
base: format!("{projects_dir}/apps"),
|
base: format!("{projects_dir}/apps"),
|
||||||
|
@ -39,12 +39,13 @@ fn build_page(config: Config) -> String {
|
||||||
let ext = ".md";
|
let ext = ".md";
|
||||||
|
|
||||||
// Get about
|
// Get about
|
||||||
let about = read_file(
|
let about = read_file_fallback(
|
||||||
FilePath {
|
FilePath {
|
||||||
base: projects_dir,
|
base: projects_dir,
|
||||||
path: "about.md".to_owned(),
|
path: "about.md".to_owned(),
|
||||||
},
|
},
|
||||||
MType::Generic,
|
MType::Generic,
|
||||||
|
lang,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get apps
|
// Get apps
|
||||||
|
@ -54,6 +55,7 @@ fn build_page(config: Config) -> String {
|
||||||
read_file(
|
read_file(
|
||||||
apps_dir.from(&e.unwrap().to_string_lossy()),
|
apps_dir.from(&e.unwrap().to_string_lossy()),
|
||||||
MType::Portfolio,
|
MType::Portfolio,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
})
|
})
|
||||||
|
@ -72,6 +74,7 @@ fn build_page(config: Config) -> String {
|
||||||
read_file(
|
read_file(
|
||||||
apps_dir.from(&e.unwrap().to_string_lossy()),
|
apps_dir.from(&e.unwrap().to_string_lossy()),
|
||||||
MType::Portfolio,
|
MType::Portfolio,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use ramhorns::{Content, Ramhorns};
|
use ramhorns::{Content, Ramhorns};
|
||||||
|
|
||||||
/// Structure used in the config variable of the app
|
/// Structure used in the config variable of the app
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct Template {
|
pub struct Template {
|
||||||
/// Root directory where templates are stored
|
/// Root directory where templates are stored
|
||||||
pub directory: String,
|
pub directory: String,
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use std::{fs, os::unix::fs::MetadataExt, path::Path};
|
use std::{fs, os::unix::fs::MetadataExt, path::Path};
|
||||||
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
http::header::{self, ContentType, TryIntoHeaderValue},
|
http::{
|
||||||
http::StatusCode,
|
header::{self, ContentType, HeaderMap, TryIntoHeaderValue},
|
||||||
|
StatusCode,
|
||||||
|
},
|
||||||
HttpRequest, HttpResponse, Responder,
|
HttpRequest, HttpResponse, Responder,
|
||||||
};
|
};
|
||||||
use base64::{engine::general_purpose, Engine};
|
use base64::{engine::general_purpose, Engine};
|
||||||
|
@ -56,15 +58,33 @@ impl Responder for Html {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read a file localized, fallback to default file if localized file isn't found
|
||||||
|
pub fn read_file_fallback(filename: FilePath, expected_file: MType, lang: Lang) -> Option<File> {
|
||||||
|
read_file(filename.clone(), expected_file, Some(lang)).or(read_file(
|
||||||
|
filename,
|
||||||
|
expected_file,
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// Read a file
|
/// Read a file
|
||||||
pub fn read_file(filename: FilePath, expected_file: MType) -> Option<File> {
|
pub fn read_file(filename: FilePath, expected_file: MType, lang: Option<Lang>) -> Option<File> {
|
||||||
reader(filename, expected_file)
|
reader(filename, expected_file, lang.unwrap_or(Lang::English))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cached(time = 600)]
|
#[cached(time = 600)]
|
||||||
fn reader(filename: FilePath, expected_file: MType) -> Option<File> {
|
fn reader(filename: FilePath, expected_file: MType, lang: Lang) -> Option<File> {
|
||||||
let as_str = filename.to_string();
|
let as_str = match lang {
|
||||||
let path = Path::new(&as_str);
|
Lang::French => {
|
||||||
|
let str = filename.to_string();
|
||||||
|
let mut parts = str.split('.').collect::<Vec<_>>();
|
||||||
|
let extension = parts.pop().unwrap_or("");
|
||||||
|
let filename = parts.join(".");
|
||||||
|
&format!("{filename}-fr.{extension}")
|
||||||
|
}
|
||||||
|
Lang::English => &filename.to_string(),
|
||||||
|
};
|
||||||
|
let path = Path::new(as_str);
|
||||||
|
|
||||||
if let Ok(metadata) = path.metadata() {
|
if let Ok(metadata) = path.metadata() {
|
||||||
// Taille maximale : 30M
|
// Taille maximale : 30M
|
||||||
|
@ -76,12 +96,12 @@ fn reader(filename: FilePath, expected_file: MType) -> Option<File> {
|
||||||
path.extension().and_then(|ext| {
|
path.extension().and_then(|ext| {
|
||||||
match mime_guess::from_ext(ext.to_str().unwrap_or_default()).first_or_text_plain() {
|
match mime_guess::from_ext(ext.to_str().unwrap_or_default()).first_or_text_plain() {
|
||||||
mime if mime == mime::APPLICATION_PDF => {
|
mime if mime == mime::APPLICATION_PDF => {
|
||||||
fs::read(&as_str).map_or(None, |bytes| Some(read_pdf(bytes)))
|
fs::read(as_str).map_or(None, |bytes| Some(read_pdf(bytes)))
|
||||||
}
|
}
|
||||||
mime if mime.type_() == mime::IMAGE => {
|
mime if mime.type_() == mime::IMAGE => {
|
||||||
fs::read(&as_str).map_or(None, |bytes| Some(read_img(bytes, &mime)))
|
fs::read(as_str).map_or(None, |bytes| Some(read_img(bytes, &mime)))
|
||||||
}
|
}
|
||||||
_ => fs::read_to_string(&as_str).map_or(None, |text| {
|
_ => fs::read_to_string(as_str).map_or(None, |text| {
|
||||||
Some(read_md(&filename, &text, expected_file, None, true))
|
Some(read_md(&filename, &text, expected_file, None, true))
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
@ -116,3 +136,23 @@ fn read_img(data: Vec<u8>, mime: &mime::Mime) -> File {
|
||||||
pub fn remove_first_letter(s: &str) -> &str {
|
pub fn remove_first_letter(s: &str) -> &str {
|
||||||
s.chars().next().map(|c| &s[c.len_utf8()..]).unwrap()
|
s.chars().next().map(|c| &s[c.len_utf8()..]).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Hash, PartialEq, Eq, Clone)]
|
||||||
|
pub enum Lang {
|
||||||
|
French,
|
||||||
|
English,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the browser language
|
||||||
|
pub fn lang(headers: &HeaderMap) -> Lang {
|
||||||
|
headers
|
||||||
|
.get("Accept-Language")
|
||||||
|
.and_then(|lang| lang.to_str().ok())
|
||||||
|
.and_then(|lang| {
|
||||||
|
["fr", "fr-FR"]
|
||||||
|
.into_iter()
|
||||||
|
.any(|i| lang.contains(i))
|
||||||
|
.then_some(Lang::French)
|
||||||
|
})
|
||||||
|
.unwrap_or(Lang::English)
|
||||||
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ impl Post {
|
||||||
path: format!("{}{ext}", self.url),
|
path: format!("{}{ext}", self.url),
|
||||||
},
|
},
|
||||||
MType::Blog,
|
MType::Blog,
|
||||||
|
None,
|
||||||
) {
|
) {
|
||||||
self.content = Some(file.content);
|
self.content = Some(file.content);
|
||||||
}
|
}
|
||||||
|
@ -149,6 +150,7 @@ pub fn get_post(
|
||||||
path: format!("{filename}{ext}"),
|
path: format!("{filename}{ext}"),
|
||||||
},
|
},
|
||||||
MType::Blog,
|
MType::Blog,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let default = (
|
let default = (
|
||||||
|
|
|
@ -57,7 +57,14 @@ pub fn remove_paragraphs(list: &mut [File]) {
|
||||||
pub fn read(path: &FilePath) -> Vec<File> {
|
pub fn read(path: &FilePath) -> Vec<File> {
|
||||||
glob(&path.to_string())
|
glob(&path.to_string())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.map(|e| read_file(path.from(&e.unwrap().to_string_lossy()), MType::Contact).unwrap())
|
.map(|e| {
|
||||||
|
read_file(
|
||||||
|
path.from(&e.unwrap().to_string_lossy()),
|
||||||
|
MType::Contact,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
.filter(|f| {
|
.filter(|f| {
|
||||||
!f.metadata
|
!f.metadata
|
||||||
.info
|
.info
|
||||||
|
|
Loading…
Reference in a new issue