Add a page to list articles by tag

This commit is contained in:
Bat 2018-09-06 13:06:04 +01:00
parent a54d2e9d71
commit dd9c4a6a73
14 changed files with 105 additions and 7 deletions

View file

@ -59,6 +59,27 @@ impl Post {
find_by!(posts, find_by_slug, slug as String, blog_id as i32); find_by!(posts, find_by_slug, slug as String, blog_id as i32);
find_by!(posts, find_by_ap_url, ap_url as String); find_by!(posts, find_by_ap_url, ap_url as String);
pub fn list_by_tag(conn: &PgConnection, tag: String, (min, max): (i32, i32)) -> Vec<Post> {
use schema::tags;
let ids = tags::table.filter(tags::tag.eq(tag)).select(tags::post_id);
posts::table.filter(posts::id.eq(any(ids)))
.order(posts::creation_date.desc())
.offset(min.into())
.limit((max - min).into())
.get_results::<Post>(conn)
.expect("Error loading posts by tag")
}
pub fn count_for_tag(conn: &PgConnection, tag: String) -> i64 {
use schema::tags;
let ids = tags::table.filter(tags::tag.eq(tag)).select(tags::post_id);
posts::table.filter(posts::id.eq(any(ids)))
.count()
.get_result(conn)
.expect("Error counting posts by tag")
}
pub fn count_local(conn: &PgConnection) -> usize { pub fn count_local(conn: &PgConnection) -> usize {
use schema::post_authors; use schema::post_authors;
use schema::users; use schema::users;
@ -220,7 +241,7 @@ impl Post {
article.object_props.set_content_string(self.content.get().clone()).expect("Article::into_activity: content error"); article.object_props.set_content_string(self.content.get().clone()).expect("Article::into_activity: content error");
article.object_props.set_published_utctime(Utc.from_utc_datetime(&self.creation_date)).expect("Article::into_activity: published error"); article.object_props.set_published_utctime(Utc.from_utc_datetime(&self.creation_date)).expect("Article::into_activity: published error");
article.object_props.set_summary_string(self.subtitle.clone()).expect("Article::into_activity: summary error"); article.object_props.set_summary_string(self.subtitle.clone()).expect("Article::into_activity: summary error");
article.object_props.tag = json!(mentions_json.append(&mut tags_json)); article.object_props.tag = Some(json!(mentions_json.append(&mut tags_json)));
article.object_props.set_url_string(self.ap_url.clone()).expect("Article::into_activity: url error"); article.object_props.set_url_string(self.ap_url.clone()).expect("Article::into_activity: url error");
article.object_props.set_to_link_vec::<Id>(to.into_iter().map(Id::new).collect()).expect("Article::into_activity: to error"); article.object_props.set_to_link_vec::<Id>(to.into_iter().map(Id::new).collect()).expect("Article::into_activity: to error");
article.object_props.set_cc_link_vec::<Id>(vec![]).expect("Article::into_activity: cc error"); article.object_props.set_cc_link_vec::<Id>(vec![]).expect("Article::into_activity: cc error");
@ -300,11 +321,11 @@ impl FromActivity<Article, PgConnection> for Post {
// save mentions and tags // save mentions and tags
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.clone() {
for tag in tags.into_iter() { for tag in tags.into_iter() {
serde_json::from_value::<link::Mention>(tag) serde_json::from_value::<link::Mention>(tag.clone())
.map(|m| Mention::from_activity(conn, m, post.id, true)) .map(|m| Mention::from_activity(conn, m, post.id, true))
.ok(); .ok();
serde_json::from_value::<Hashtag>(tag) serde_json::from_value::<Hashtag>(tag.clone())
.map(|t| Tag::from_activity(conn, t, post.id)) .map(|t| Tag::from_activity(conn, t, post.id))
.ok(); .ok();
} }

View file

@ -5,7 +5,7 @@ use ap_url;
use instance::Instance; use instance::Instance;
use schema::tags; use schema::tags;
#[derive(Serialize, Queryable)] #[derive(Serialize, Queryable, Clone)]
pub struct Tag { pub struct Tag {
pub id: i32, pub id: i32,
pub tag: String, pub tag: String,
@ -24,12 +24,13 @@ pub struct NewTag {
impl Tag { impl Tag {
insert!(tags, NewTag); insert!(tags, NewTag);
get!(tags); get!(tags);
find_by!(tags, find_by_name, tag as String);
list_by!(tags, for_post, post_id as i32); list_by!(tags, for_post, post_id as i32);
pub fn into_activity(&self, conn: &PgConnection) -> Hashtag { pub fn into_activity(&self, conn: &PgConnection) -> Hashtag {
let ht = Hashtag::default(); let mut ht = Hashtag::default();
ht.set_href_string(ap_url(format!("{}/tag/{}", Instance::get_local(conn).unwrap().public_domain, self.tag))).expect("Tag::into_activity: href error"); ht.set_href_string(ap_url(format!("{}/tag/{}", Instance::get_local(conn).unwrap().public_domain, self.tag))).expect("Tag::into_activity: href error");
ht.set_name_string(self.tag).expect("Tag::into_activity: name error"); ht.set_name_string(self.tag.clone()).expect("Tag::into_activity: name error");
ht ht
} }

View file

@ -545,5 +545,9 @@ msgstr "Über {{ instance_name }}"
msgid "View all" msgid "View all"
msgstr "" msgstr ""
#, fuzzy
msgid "Articles tagged \"{{ tag }}\""
msgstr "Über {{ instance_name }}"
#~ msgid "Your password should be at least 8 characters long" #~ msgid "Your password should be at least 8 characters long"
#~ msgstr "Das Passwort sollte mindestens 8 Zeichen lang sein" #~ msgstr "Das Passwort sollte mindestens 8 Zeichen lang sein"

View file

@ -531,3 +531,7 @@ msgstr "Welcome on {{ instance_name }}"
msgid "View all" msgid "View all"
msgstr "" msgstr ""
#, fuzzy
msgid "Articles tagged \"{{ tag }}\""
msgstr "Welcome on {{ instance_name }}"

View file

@ -539,3 +539,7 @@ msgstr "Articles de {{ instance.name }}"
msgid "View all" msgid "View all"
msgstr "Tout afficher" msgstr "Tout afficher"
#, fuzzy
msgid "Articles tagged \"{{ tag }}\""
msgstr "Articles de {{ instance.name }}"

View file

@ -535,3 +535,7 @@ msgstr "Acerca de {{ instance_name }}"
msgid "View all" msgid "View all"
msgstr "" msgstr ""
#, fuzzy
msgid "Articles tagged \"{{ tag }}\""
msgstr "Acerca de {{ instance_name }}"

View file

@ -549,6 +549,10 @@ msgstr "Om {{ instance_name }}"
msgid "View all" msgid "View all"
msgstr "" msgstr ""
#, fuzzy
msgid "Articles tagged \"{{ tag }}\""
msgstr "Om {{ instance_name }}"
#~ msgid "One reshare" #~ msgid "One reshare"
#~ msgid_plural "{{ count }} reshares" #~ msgid_plural "{{ count }} reshares"
#~ msgstr[0] "Én deling" #~ msgstr[0] "Én deling"

View file

@ -548,6 +548,10 @@ msgstr "O {{ instance_name }}"
msgid "View all" msgid "View all"
msgstr "" msgstr ""
#, fuzzy
msgid "Articles tagged \"{{ tag }}\""
msgstr "O {{ instance_name }}"
#~ msgid "One reshare" #~ msgid "One reshare"
#~ msgid_plural "{{ count }} reshares" #~ msgid_plural "{{ count }} reshares"
#~ msgstr[0] "Jedno udostępnienie" #~ msgstr[0] "Jedno udostępnienie"

View file

@ -519,3 +519,6 @@ msgstr ""
msgid "View all" msgid "View all"
msgstr "" msgstr ""
msgid "Articles tagged \"{{ tag }}\""
msgstr ""

View file

@ -101,6 +101,9 @@ fn main() {
routes::static_files, routes::static_files,
routes::tags::tag,
routes::tags::paginated_tag,
routes::user::me, routes::user::me,
routes::user::details, routes::user::details,
routes::user::dashboard, routes::user::dashboard,

View file

@ -108,6 +108,7 @@ pub mod notifications;
pub mod posts; pub mod posts;
pub mod reshares; pub mod reshares;
pub mod session; pub mod session;
pub mod tags;
pub mod user; pub mod user;
pub mod well_known; pub mod well_known;

28
src/routes/tags.rs Normal file
View file

@ -0,0 +1,28 @@
use rocket_contrib::Template;
use serde_json;
use plume_models::{
db_conn::DbConn,
posts::Post,
tags::Tag,
users::User,
};
use routes::Page;
#[get("/tag/<name>")]
fn tag(user: Option<User>, conn: DbConn, name: String) -> Template {
paginated_tag(user, conn, name, Page::first())
}
#[get("/tag/<name>?<page>")]
fn paginated_tag(user: Option<User>, conn: DbConn, name: String, page: Page) -> Template {
let tag = Tag::find_by_name(&*conn, name).expect("Rendering tags::tag: tag not found");
let posts = Post::list_by_tag(&*conn, tag.tag.clone(), page.limits());
Template::render("tags/index", json!({
"tag": tag.clone(),
"account": user.map(|u| u.to_json(&*conn)),
"articles": posts.into_iter().map(|p| p.to_json(&*conn)).collect::<Vec<serde_json::Value>>(),
"page": page.page,
"n_pages": Page::total(Post::count_for_tag(&*conn, tag.tag) as i32)
}))
}

View file

@ -36,7 +36,7 @@
<p>{{ "This article is under the {{ license }} license." | _(license=article.post.license) }}</p> <p>{{ "This article is under the {{ license }} license." | _(license=article.post.license) }}</p>
<ul class="tags"> <ul class="tags">
{% for tag in article.tags %} {% for tag in article.tags %}
<li>{{ tag.tag }}</li> <li><a href="/tag/{{ tag.tag }}">{{ tag.tag }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
<div class="flex"> <div class="flex">

View file

@ -0,0 +1,17 @@
{% extends "base" %}
{% import "macros" as macros %}
{% block title %}
{{ 'Articles tagged "{{ tag }}"' | _(tag=tag.tag) }}
{% endblock title %}
{% block content %}
<h1>{{ 'Articles tagged "{{ tag }}"' | _(tag=tag.tag) }}</h1>
<div class="cards">
{% for article in articles %}
{{ macros::post_card(article=article) }}
{% endfor %}
</div>
{{ macros::paginate(page=page, total=n_pages) }}
{% endblock content %}