Use Ructe (#327)

All the template are now compiled at compile-time with the `ructe` crate.

I preferred to use it instead of askama because it allows more complex Rust expressions, where askama only supports a small subset of expressions and doesn't allow them everywhere (for instance, `{{ macro!() | filter }}` would result in a parsing error).

The diff is quite huge, but there is normally no changes in functionality.

Fixes #161 and unblocks #110 and #273
This commit is contained in:
Baptiste Gelez
2018-12-06 18:54:16 +01:00
committed by GitHub
parent 5f059c3e98
commit 70af57c6e1
121 changed files with 3132 additions and 3260 deletions
-41
View File
@@ -1,41 +0,0 @@
{% extends "base" %}
{% import "macros" as macros %}
{% block title %}
{{ "Dashboard" | _ }}
{% endblock title %}
{% block content %}
<h1>{{ "Your Dashboard" | _ }}</h1>
<section>
<h2>{{ "Your Blogs" | _ }}</h2>
{% if blogs | length < 1 %}
<p>{{ "You don't have any blog yet. Create your own, or ask to join one." | _ }}</p>
{% endif %}
<div class="cards">
{% for blog in blogs %}
<div class="card">
<h3><a href="/~/{{ blog.actor_id }}/">{{ blog.title }}</a></h3>
</div>
{% endfor %}
</div>
<a class="button" href="/blogs/new">{{ "Start a new blog" | _ }}</a>
</section>
{% if drafts | length > 0 %}
<section>
<h2>{{ "Your Drafts" | _ }}</h2>
<div class="cards">
{% for draft in drafts %}
{{ macros::post_card(article=draft) }}
{% endfor %}
</div>
</section>
{% endif %}
<section>
<h2>{{ "Your media" | _ }}</h2>
<a class="button" href="/medias">{{ "Go to your gallery" | _ }}</a>
</section>
{% endblock content %}
+41
View File
@@ -0,0 +1,41 @@
@use templates::{base, partials::post_card};
@use template_utils::*;
@use plume_models::blogs::Blog;
@use plume_models::posts::Post;
@(ctx: BaseContext, blogs: Vec<Blog>, drafts: Vec<Post>)
@:base(ctx, "Your Dashboard", {}, {}, {
<h1>@i18n!(ctx.1, "Your Dashboard")</h1>
<section>
<h2>@i18n!(ctx.1, "Your Blogs")</h2>
@if blogs.is_empty() {
<p>@i18n!(ctx.1, "You don't have any blog yet. Create your own, or ask to join one.")</p>
}
<div class="cards">
@for blog in blogs {
<div class="card">
<h3><a href="/~/@blog.actor_id/">@blog.title</a></h3>
</div>
}
</div>
<a class="button" href="/blogs/new">@i18n!(ctx.1, "Start a new blog")</a>
</section>
@if !drafts.is_empty() {
<section>
<h2>@i18n!(ctx.1, "Your Drafts")</h2>
<div class="cards">
@for draft in drafts {
@:post_card(ctx, draft)
}
</div>
</section>
}
<section>
<h2>@i18n!(ctx.1, "Your media")</h2>
<a class="button" href="/medias">@i18n!(ctx.1, "Go to your gallery")</a>
</section>
})
-41
View File
@@ -1,41 +0,0 @@
{% extends "base" %}
{% import "macros" as macros %}
{% block title %}
{% if user.display_name %}
{% set name = user.display_name %}
{% else %}
{% set name = user.username %}
{% endif %}
{{ name }}
{% endblock title %}
{% block content %}
{% include "users/header" %}
{% set user_link = ['/@', user.fqn] %}
{% set followers_link = ['/@', user.fqn, 'followers'] %}
{{ macros::tabs(links=[ user_link | join(sep='/'), followers_link | join(sep='/')], titles=['Articles', 'Followers'], selected=1) }}
{% if recents | length != 0 %}
<h2>
{{ "Latest articles" | _ }}
<small><a href="/@/{{ user.fqn }}/atom.xml" title="Atom feed">{{ macros::feather(name="rss") }}</a></small>
</h2>
<div class="cards">
{% for article in recents %}
{{ macros::post_card(article=article) }}
{% endfor %}
</div>
{% endif %}
{% if reshares | length != 0 %}
<h2>{{ "Recently boosted" | _ }}</h2>
<div class="cards">
{% for article in reshares %}
{{ macros::post_card(article=article) }}
{% endfor %}
</div>
{% endif %}
{% endblock content %}
+36
View File
@@ -0,0 +1,36 @@
@use templates::{base, partials::post_card, users::header};
@use template_utils::*;
@use plume_models::posts::Post;
@use plume_models::users::User;
@(ctx: BaseContext, user: User, follows: bool, is_remote: bool, remote_url: String, recents: Vec<Post>, reshares: Vec<Post>)
@:base(ctx, &user.name(ctx.0), {}, {}, {
@:header(ctx, &user, follows, is_remote, remote_url)
@tabs(&[
(&format!("/@/{}", user.get_fqn(ctx.0)), i18n!(ctx.1, "Articles"), true),
(&format!("/@/{}/followers", user.get_fqn(ctx.0)), i18n!(ctx.1, "Followers"), false)
])
@if !recents.is_empty() {
<h2>
@i18n!(ctx.1, "Latest articles")
<small><a href="/@@/@user.get_fqn(ctx.0)/atom.xml" title="@i18n!(ctx.1, "Atom feed")">@icon!("rss")</a></small>
</h2>
<div class="cards">
@for article in recents {
@:post_card(ctx, article)
}
</div>
}
@if !reshares.is_empty() {
<h2>@i18n!(ctx.1, "Recently boosted")</h2>
<div class="cards">
@for article in reshares {
@:post_card(ctx, article)
}
</div>
}
})
-34
View File
@@ -1,34 +0,0 @@
{% extends "base" %}
{% block title %}
{{ "Edit your account" | _ }}
{% endblock title %}
{% block content %}
<h1>{{ "Your Profile" | _ }}</h1>
<form method="post">
<!-- Rocket hack to use various HTTP methods -->
<input type=hidden name="_method" value="put">
<label for="display_name">{{ "Display Name" | _ }}</label>
<input name="display_name" value="{{ account.display_name }}">
<label for="email">{{ "Email" | _ }}</label>
<input name="email" value="{{ account.email }}">
<label for="summary">{{ "Summary" | _ }}</label>
<input name="summary" value="{{ account.summary }}">
<input type="submit" value="{{ "Update account" | _ }}"/>
</form>
<h2>{{ "Danger zone" | _ }}</h2>
<p>{{ "Be very careful, any action taken here can't be cancelled." | _ }}
{% if not account.is_admin %}
<form method="post" action="/@/{{ account.fqn }}/delete">
<input type="submit" class="inline-block button destructive" value="{{ 'Delete your account' | _ }}">
</form>
{% else %}
<p>{{ "Sorry, but as an admin, you can't leave your instance." | _ }}</p>
{% endif %}
{% endblock content %}
+30
View File
@@ -0,0 +1,30 @@
@use templates::base;
@use template_utils::*;
@use routes::user::UpdateUserForm;
@use validator::ValidationErrors;
@(ctx: BaseContext, form: UpdateUserForm, errors: ValidationErrors)
@:base(ctx, "Edit your account", {}, {}, {
<h1>@i18n!(ctx.1, "Your Profile")</h1>
<form method="post">
<!-- Rocket hack to use various HTTP methods -->
<input type=hidden name="_method" value="put">
@input!(ctx.1, display_name (text), "Display name", form, errors.clone())
@input!(ctx.1, email (text), "Email", form, errors.clone())
@input!(ctx.1, summary (text), "Summary", form, errors)
<input type="submit" value="@i18n!(ctx.1, "Update account")"/>
</form>
<h2>@i18n!(ctx.1, "Danger zone")</h2>
<p>@i18n!(ctx.1, "Be very careful, any action taken here can't be cancelled.")
@if !ctx.2.clone().expect("Editing profile while not connected").is_admin {
<form method="post" action="/@@/@ctx.2.clone().expect("Editing profile while not connected").get_fqn(ctx.0)/delete">
<input type="submit" class="inline-block button destructive" value="@i18n!(ctx.1, "Delete your account")">
</form>
} else {
<p>@i18n!(ctx.1, "Sorry, but as an admin, you can't leave your instance.")</p>
}
})
-24
View File
@@ -1,24 +0,0 @@
{% extends "base" %}
{% import "macros" as macros %}
{% block title %}
{{ "{{ name | escape }}'s followers" | _(name=user.name) }}
{% endblock title %}
{% block content %}
{% include "users/header" %}
{% set user_link = ['/@', user.fqn] %}
{% set followers_link = ['/@', user.fqn, 'followers'] %}
{{ macros::tabs(links=[ user_link | join(sep='/'), followers_link | join(sep='/')], titles=['Articles', 'Followers'], selected=2) }}
<div class="cards">
{% for follower in followers %}
<div class="card">
<h3><a href="/@/{{ follower.fqn }}/">{{ follower.name }}</a> <small>@{{ follower.fqn }}</small></h3>
<main><p>{{ follower.summary | safe }}</p></main>
</div>
{% endfor %}
</div>
{{ macros::paginate(page=page, total=n_pages) }}
{% endblock content %}
+24
View File
@@ -0,0 +1,24 @@
@use templates::{base, users::header};
@use template_utils::*;
@use plume_models::users::User;
@(ctx: BaseContext, user: User, follows: bool, is_remote: bool, remote_url: String, followers: Vec<User>, page: i32, n_pages: i32)
@:base(ctx, &i18n!(ctx.1, "{0}'s followers"; user.name(ctx.0)), {}, {}, {
@:header(ctx, &user, follows, is_remote, remote_url)
@tabs(&[
(&format!("/@/{}", user.get_fqn(ctx.0)), i18n!(ctx.1, "Articles"), false),
(&format!("/@/{}/followers", user.get_fqn(ctx.0)), i18n!(ctx.1, "Followers"), true)
])
<div class="cards">
@for follower in followers {
<div class="card">
<h3><a href="/@@/@follower.get_fqn(ctx.0)/">@follower.name(ctx.0)</a> <small>@format!("@{}", follower.get_fqn(ctx.0))</small></h3>
<main><p>@Html(follower.summary)</p></main>
</div>
}
</div>
@paginate(ctx.1, page, n_pages)
})
-44
View File
@@ -1,44 +0,0 @@
<div class="user">
<div class="flex wrap">
{{ macros::avatar(user=user, size="medium") }}
<h1 class="grow flex vertical">
{{ user.name }}
<small>@{{ user.fqn }}</small>
</h1>
<p>
{% if user.is_admin %}
<span class="badge">{{ "Admin" | _ }}</span>
{% endif %}
{% if is_self %}
<span class="badge">{{ "It is you" | _ }}</span>
{% endif %}
{% if is_self %}
<a href="/@/{{ user.username }}/edit" class="button inline-block">{{ "Edit your profile" | _ }}</a>
{% endif %}
</p>
</div>
{% if is_remote %}
<a class="inline-block" href="{{ user.ap_url }}" target="_blank">{{ "Open on {{ instance_url }}" | _(instance_url=instance_url) }}</a>
{% endif %}
{% set not_self = not is_self %}
{% if not_self and (account is defined) %}
<form class="inline" method="post" action="/@/{{ user.fqn }}/follow/">
{% if follows %}
<input type="submit" value="{{ 'Unfollow' | _ }}">
{% else %}
<input type="submit" value="{{ 'Follow' | _ }}">
{% endif %}
</form>
{% endif %}
</div>
<div class="user-summary">
{{ user.summary | safe }}
</div>
+43
View File
@@ -0,0 +1,43 @@
@use template_utils::*;
@use plume_models::users::User;
@(ctx: BaseContext, user: &User, follows: bool, is_remote: bool, instance_url: String)
<div class="user">
<div class="flex wrap">
@avatar(ctx.0, &user, Size::Medium, false, ctx.1)
<h1 class="grow flex vertical">
@user.name(ctx.0)
<small>@user.get_fqn(ctx.0)</small>
</h1>
<p>
@if user.is_admin {
<span class="badge">@i18n!(ctx.1, "Admin")</span>
}
@if ctx.2.clone().map(|u| u.id == user.id).unwrap_or(false) {
<span class="badge">@i18n!(ctx.1, "It is you")</span>
<a href="/@@/@user.username/edit" class="button inline-block">@i18n!(ctx.1, "Edit your profile")</a>
}
</p>
</div>
@if is_remote {
<a class="inline-block" href="@user.ap_url" target="_blank">@i18n!(ctx.1, "Open on {0}"; instance_url)</a>
}
@if ctx.2.clone().map(|u| u.id != user.id).unwrap_or(false) {
<form class="inline" method="post" action="/@@/@user.get_fqn(ctx.0)/follow/">
@if follows {
<input type="submit" value="@i18n!(ctx.1, "Unfollow")">
} else {
<input type="submit" value="@i18n!(ctx.1, "Follow")">
}
</form>
}
</div>
<div class="user-summary">
@Html(user.summary.clone())
</div>
-22
View File
@@ -1,22 +0,0 @@
{% extends "base" %}
{% import "macros" as macros %}
{% block title %}
{{ "New Account" | _ }}
{% endblock title %}
{% block content %}
{% if enabled %}
<h1>{{ "Create an account" | _ }}</h1>
<form method="post">
{{ macros::input(name="username", label="Username", errors=errors, form=form, props='minlenght="1"') }}
{{ macros::input(name="email", label="Email", errors=errors, form=form, type="email") }}
{{ macros::input(name="password", label="Password", errors=errors, form=form, type="password", props='minlenght="8"') }}
{{ macros::input(name="password_confirmation", label="Password confirmation", errors=errors, form=form, type="password", props='minlenght="8"') }}
<input type="submit" value="{{ "Create account" | _ }}" />
</form>
{% else %}
<p class="center">{{ "Sorry, but registrations are closed on this instance. Try to find another one" | _ }}</p>
{% endif %}
{% endblock content %}
+22
View File
@@ -0,0 +1,22 @@
@use templates::base;
@use template_utils::*;
@use routes::user::NewUserForm;
@use validator::ValidationErrors;
@(ctx: BaseContext, enabled: bool, form: &NewUserForm, errors: ValidationErrors)
@:base(ctx, "Edit your account", {}, {}, {
@if enabled {
<h1>@i18n!(ctx.1, "Create an account")</h1>
<form method="post">
@input!(ctx.1, username (text), "Username", form, errors.clone(), "minlenght=\"1\"")
@input!(ctx.1, email (text), "Email", form, errors.clone())
@input!(ctx.1, password (password), "Password", form, errors.clone(), "minlenght=\"8\"")
@input!(ctx.1, password_confirmation (password), "Password confirmation", form, errors, "minlenght=\"8\"")
<input type="submit" value="@i18n!(ctx.1, "Create account")" />
</form>
} else {
<p class="center">@i18n!(ctx.1, "Sorry, but registrations are closed on this instance. Try to find another one")</p>
}
})