Add a page to list articles by tag
This commit is contained in:
parent
a54d2e9d71
commit
dd9c4a6a73
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
po/de.po
4
po/de.po
@ -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"
|
||||||
|
4
po/en.po
4
po/en.po
@ -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 }}"
|
||||||
|
4
po/fr.po
4
po/fr.po
@ -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 }}"
|
||||||
|
4
po/gl.po
4
po/gl.po
@ -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 }}"
|
||||||
|
4
po/nb.po
4
po/nb.po
@ -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"
|
||||||
|
4
po/pl.po
4
po/pl.po
@ -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"
|
||||||
|
@ -519,3 +519,6 @@ msgstr ""
|
|||||||
|
|
||||||
msgid "View all"
|
msgid "View all"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Articles tagged \"{{ tag }}\""
|
||||||
|
msgstr ""
|
||||||
|
@ -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,
|
||||||
|
@ -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
28
src/routes/tags.rs
Normal 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)
|
||||||
|
}))
|
||||||
|
}
|
@ -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">
|
||||||
|
17
templates/tags/index.html.tera
Normal file
17
templates/tags/index.html.tera
Normal 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 %}
|
Loading…
Reference in New Issue
Block a user