Add structured data to Book (#84).

This commit is contained in:
Fabien Basmaison 2021-04-09 23:57:49 +02:00
parent e339581ade
commit 4c4801e2da
8 changed files with 460 additions and 366 deletions

View file

@ -6,24 +6,36 @@
{% block title %}{{ book.title }}{% endblock %} {% block title %}{{ book.title }}{% endblock %}
{% block content %} {% block content %}
<div class="block"> {% with user_authenticated=request.user.is_authenticated can_edit_book=perms.bookwyrm.edit_book %}
<div class="block" itemscope itemtype="https://schema.org/Book">
<div class="columns is-mobile"> <div class="columns is-mobile">
<div class="column"> <div class="column">
<h1 class="title"> <h1 class="title">
{{ book.title }}{% if book.subtitle %}: <span itemprop="name">
<small>{{ book.subtitle }}</small>{% endif %} {{ book.title }}{% if book.subtitle %}:
<small>{{ book.subtitle }}</small>
{% endif %}
</span>
{% if book.series %} {% if book.series %}
<small class="has-text-grey-dark">({{ book.series }}{% if book.series_number %} #{{ book.series_number }}{% endif %})</small><br> <meta itemprop="isPartOf" content="{{ book.series }}">
<meta itemprop="volumeNumber" content="{{ book.series_number }}">
<small class="has-text-grey-dark">
({{ book.series }}
{% if book.series_number %} #{{ book.series_number }}{% endif %})
</small>
<br>
{% endif %} {% endif %}
</h1> </h1>
{% if book.authors %} {% if book.authors %}
<h2 class="subtitle"> <h2 class="subtitle">
{% trans "by" %} {% include 'snippets/authors.html' with book=book %} {% trans "by" %} {% include 'snippets/authors.html' with book=book %}
</h2> </h2>
{% endif %} {% endif %}
</div> </div>
{% if request.user.is_authenticated and perms.bookwyrm.edit_book %} {% if user_authenticated and can_edit_book %}
<div class="column is-narrow"> <div class="column is-narrow">
<a href="{{ book.id }}/edit"> <a href="{{ book.id }}/edit">
<span class="icon icon-pencil" title="{% trans "Edit Book" %}"> <span class="icon icon-pencil" title="{% trans "Edit Book" %}">
@ -44,7 +56,7 @@
{% include 'snippets/shelve_button/shelve_button.html' %} {% include 'snippets/shelve_button/shelve_button.html' %}
</div> </div>
{% if request.user.is_authenticated and not book.cover %} {% if user_authenticated and not book.cover %}
<div class="block"> <div class="block">
{% trans "Add cover" as button_text %} {% trans "Add cover" as button_text %}
{% include 'snippets/toggle/toggle_button.html' with text=button_text controls_text="add-cover" controls_uid=book.id focus="modal-title-add-cover" class="is-small" %} {% include 'snippets/toggle/toggle_button.html' with text=button_text controls_text="add-cover" controls_uid=book.id focus="modal-title-add-cover" class="is-small" %}
@ -60,7 +72,7 @@
{% if book.isbn_13 %} {% if book.isbn_13 %}
<div class="is-flex is-justify-content-space-between is-align-items-center"> <div class="is-flex is-justify-content-space-between is-align-items-center">
<dt>{% trans "ISBN:" %}</dt> <dt>{% trans "ISBN:" %}</dt>
<dd>{{ book.isbn_13 }}</dd> <dd itemprop="isbn">{{ book.isbn_13 }}</dd>
</div> </div>
{% endif %} {% endif %}
@ -89,14 +101,31 @@
<div class="column is-three-fifths"> <div class="column is-three-fifths">
<div class="block"> <div class="block">
<h3 class="field is-grouped"> <h3
class="field is-grouped"
itemprop="aggregateRating"
itemscope
itemtype="https://schema.org/AggregateRating"
>
<meta itemprop="ratingValue" content="{{ rating|floatformat }}">
{# @todo Is it possible to not hard-code the value? #}
<meta itemprop="bestRating" content="5">
<meta itemprop="reviewCount" content="{{ review_count }}">
{% include 'snippets/stars.html' with rating=rating %} {% include 'snippets/stars.html' with rating=rating %}
{% blocktrans count counter=review_count %}({{ review_count }} review){% plural %}({{ review_count }} reviews){% endblocktrans %}
{% blocktrans count counter=review_count trimmed %}
({{ review_count }} review)
{% plural %}
({{ review_count }} reviews)
{% endblocktrans %}
</h3> </h3>
{% include 'snippets/trimmed_text.html' with full=book|book_description %} {% with full=book|book_description itemprop='abstract' %}
{% include 'snippets/trimmed_text.html' %}
{% endwith %}
{% if request.user.is_authenticated and perms.bookwyrm.edit_book and not book|book_description %} {% if user_authenticated and can_edit_book and not book|book_description %}
{% trans 'Add Description' as button_text %} {% trans 'Add Description' as button_text %}
{% include 'snippets/toggle/open_button.html' with text=button_text controls_text="add-description" controls_uid=book.id focus="id_description" hide_active=True id="hide-description" %} {% include 'snippets/toggle/open_button.html' with text=button_text controls_text="add-description" controls_uid=book.id focus="id_description" hide_active=True id="hide-description" %}
@ -138,7 +167,7 @@
{% endfor %} {% endfor %}
</div> </div>
{% if request.user.is_authenticated %} {% if user_authenticated %}
<section class="block"> <section class="block">
<header class="columns"> <header class="columns">
<h2 class="column title is-5 mb-1">{% trans "Your reading activity" %}</h2> <h2 class="column title is-5 mb-1">{% trans "Your reading activity" %}</h2>
@ -176,14 +205,15 @@
</div> </div>
<div class="column is-one-fifth"> <div class="column is-one-fifth">
{% if book.subjects %} {% if book.subjects %}
<section class="content block"> <section class="content block">
<h2 class="title is-5">{% trans "Subjects" %}</h2> <h2 class="title is-5">{% trans "Subjects" %}</h2>
<ul>
{% for subject in book.subjects %} <ul>
<li>{{ subject }}</li> {% for subject in book.subjects %}
{% endfor %} <li itemprop="about">{{ subject }}</li>
</ul> {% endfor %}
</section> </ul>
</section>
{% endif %} {% endif %}
{% if book.subject_places %} {% if book.subject_places %}
@ -229,41 +259,54 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
</div>
<div class="block" id="reviews"> <div class="block" id="reviews">
{% for review in reviews %} {% for review in reviews %}
<div class="block"> <div
{% include 'snippets/status/status.html' with status=review hide_book=True depth=1 %} class="block"
</div> itemprop="review"
{% endfor %} itemscope
itemtype="https://schema.org/Review"
<div class="block is-flex is-flex-wrap-wrap"> >
{% for rating in ratings %} {% with status=review hide_book=True depth=1 %}
<div class="block mr-5"> {% include 'snippets/status/status.html' %}
<div class="media"> {% endwith %}
<div class="media-left">{% include 'snippets/avatar.html' with user=rating.user %}</div>
<div class="media-content">
<div>
<a href="{{ rating.user.local_path }}">{{ rating.user.display_name }}</a>
</div>
<div class="is-flex">
<p class="mr-1">{% trans "rated it" %}</p>
{% include 'snippets/stars.html' with rating=rating.rating %}
</div>
<div>
<a href="{{ rating.remote_id }}">{{ rating.published_date | naturaltime }}</a>
</div>
</div>
</div> </div>
</div>
{% endfor %} {% endfor %}
</div>
<div class="block"> <div class="block is-flex is-flex-wrap-wrap">
{% include 'snippets/pagination.html' with page=reviews path=book.local_path anchor="#reviews" %} {% for rating in ratings %}
{% with user=rating.user %}
<div class="block mr-5">
<div class="media">
<div class="media-left">
{% include 'snippets/avatar.html' %}
</div>
<div class="media-content">
<div>
<a href="{{ user.local_path }}">{{ user.display_name }}</a>
</div>
<div class="is-flex">
<p class="mr-1">{% trans "rated it" %}</p>
{% include 'snippets/stars.html' with rating=rating.rating %}
</div>
<div>
<a href="{{ rating.remote_id }}">{{ rating.published_date | naturaltime }}</a>
</div>
</div>
</div>
</div>
{% endwith %}
{% endfor %}
</div>
<div class="block">
{% include 'snippets/pagination.html' with page=reviews path=book.local_path anchor="#reviews" %}
</div>
</div> </div>
</div> </div>
{% endwith %}
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}

View file

@ -1,24 +1,71 @@
{% spaceless %}
{% load i18n %} {% load i18n %}
<p> <p>
{% if book.physical_format and not book.pages %} {% with format=book.physical_format pages=book.pages %}
{{ book.physical_format | title }} {% if format %}
{% elif book.physical_format and book.pages %} {% comment %}
{% blocktrans with format=book.physical_format|title pages=book.pages %}{{ format }}, {{ pages }} pages{% endblocktrans %} @todo The bookFormat property is limited to a list of values
{% elif book.pages %} whereas the book edition is free text.
{% blocktrans with pages=book.pages %}{{ pages }} pages{% endblocktrans %} @see https://schema.org/bookFormat
{% endif %} {% endcomment %}
<meta itemprop="bookFormat" content="{{ format }}">
{% endif %}
{% if pages %}
<meta itemprop="numberOfPages" content="{{ pages }}">
{% endif %}
{% if format and not pages %}
{% blocktrans %}{{ format }}{% endblocktrans %}
{% elif format and pages %}
{% blocktrans %}{{ format }}, {{ pages }} pages{% endblocktrans %}
{% elif pages %}
{% blocktrans %}{{ pages }} pages{% endblocktrans %}
{% endif %}
{% endwith %}
</p> </p>
{% if book.languages %} {% if book.languages %}
<p> {% for language in book.languages %}
{% blocktrans with languages=book.languages|join:", " %}{{ languages }} language{% endblocktrans %} <meta itemprop="inLanguage" content="{{ language }}">
</p> {% endfor %}
<p>
{% with languages=book.languages|join:", " %}
{% blocktrans %}{{ languages }} language{% endblocktrans %}
{% endwith %}
</p>
{% endif %} {% endif %}
<p> <p>
{% if book.published_date and book.publishers %} {% with date=book.published_date|date:'M jS Y' publisher=book.publishers|join:', ' %}
{% blocktrans with date=book.published_date|date:'M jS Y' publisher=book.publishers|join:', ' %}Published {{ date }} by {{ publisher }}.{% endblocktrans %} {% if date or book.first_published_date %}
{% elif book.published_date %} <meta
{% blocktrans with date=book.published_date|date:'M jS Y' %}Published {{ date }}{% endblocktrans %} itemprop="datePublished"
{% elif book.publishers %} content="{{ book.first_published_date|default:book.published_date|date:'Y-m-d' }}"
{% blocktrans with publisher=book.publishers|join:', ' %}Published by {{ publisher }}.{% endblocktrans %} >
{% endif %} {% endif %}
{% comment %}
@todo The publisher property needs to be an Organization or a Person.
Well be using Thing which is the more generic ancestor.
@see https://schema.org/Publisher
{% endcomment %}
{% if book.publishers %}
{% for publisher in book.publishers %}
<meta itemprop="publisher" content="{{ publisher }}">
{% endfor %}
{% endif %}
{% if date and publisher %}
{% blocktrans %}Published {{ date }} by {{ publisher }}.{% endblocktrans %}
{% elif date %}
{% blocktrans %}Published {{ date }}{% endblocktrans %}
{% elif publisher %}
{% blocktrans %}Published by {{ publisher }}.{% endblocktrans %}
{% endif %}
{% endwith %}
</p> </p>
{% endspaceless %}

View file

@ -1 +1,18 @@
{% for author in book.authors.all %}<a href="/author/{{ author.id }}" class="author">{{ author.name }}</a>{% if not forloop.last %}, {% endif %}{% endfor %} {% spaceless %}
{% comment %}
@todo The author property needs to be an Organization or a Person. Well be
using Thing which is the more generic ancestor.
@see https://schema.org/Author
{% endcomment %}
{% for author in book.authors.all %}
<a
href="/author/{{ author.id }}"
class="author"
itemprop="author"
itemscope
itemtype="https://schema.org/Thing"
><span
itemprop="name"
>{{ author.name }}<span></a>{% if not forloop.last %}, {% endif %}
{% endfor %}
{% endspaceless %}

View file

@ -1,13 +1,29 @@
{% spaceless %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %}
<div class="cover-container is-{{ size }}"> <div class="cover-container is-{{ size }}">
{% if book.cover %} {% if book.cover %}
<img class="book-cover" src="/images/{{ book.cover }}" alt="{{ book.alt_text }}" title="{{ book.alt_text }}"> <img
{% else %} class="book-cover"
<div class="no-cover book-cover"> src="/images/{{ book.cover }}"
<img class="book-cover" src="/static/images/no_cover.jpg" alt="No cover"> alt="{{ book.alt_text }}"
<div> title="{{ book.alt_text }}"
<p>{{ book.alt_text }}</p> itemprop="thumbnailUrl"
>
{% else %}
<div class="no-cover book-cover">
<img
class="book-cover"
src="/static/images/no_cover.jpg"
alt="{% trans "No cover" %}"
>
<div>
<p>{{ book.alt_text }}</p>
</div>
</div> </div>
</div> {% endif %}
{% endif %}
</div> </div>
{% endspaceless %}

View file

@ -1,68 +1,137 @@
{% spaceless %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %} {% load i18n %}
<div class="block">
{% if status.status_type == 'Review' or status.status_type == 'Rating' %} {% with status_type=status.status_type %}
<div> <div
{% if status.name %} class="block"
<h3 class="title is-5 has-subtitle" dir="auto">
{{ status.name|escape }} {% if status_type == 'Review' %}
</h3> {% firstof "reviewBody" as body_prop %}
{% endif %} {% firstof 'itemprop="reviewRating" itemscope itemtype="https://schema.org/Rating"' as rating_type %}
{% include 'snippets/stars.html' with rating=status.rating %} {% endif %}
</div>
{% if status_type == 'Rating' %}
itemprop="rating"
itemtype="https://schema.org/Rating"
{% endif %}
>
{% if status_type == 'Review' or status_type == 'Rating' %}
<div>
{% if status.name %}
<h3
class="title is-5 has-subtitle"
dir="auto"
itemprop="name"
>
{{ status.name|escape }}
</h3>
{% endif %}
<span
class="is-sr-only"
{{ rating_type }}
>
<meta itemprop="ratingValue" content="{{ status.rating|floatformat }}">
{% if status_type == 'Rating' %}
{# @todo Is it possible to not hard-code the value? #}
<meta itemprop="bestRating" content="5">
{% endif %}
</span>
{% include 'snippets/stars.html' with rating=status.rating %}
</div>
{% endif %} {% endif %}
{% if status.content_warning %} {% if status.content_warning %}
<div> <div>
<p>{{ status.content_warning }}</p> <p>{{ status.content_warning }}</p>
{% trans "Show more" as button_text %}
{% include 'snippets/toggle/open_button.html' with text=button_text class="is-small" controls_text="show-status-cw" controls_uid=status.id %} {% trans "Show more" as button_text %}
</div>
{% with text=button_text class="is-small" controls_text="show-status-cw" controls_uid=status.id %}
{% include 'snippets/toggle/open_button.html' %}
{% endwith %}
</div>
{% endif %} {% endif %}
<div{% if status.content_warning %} class="hidden" id="show-status-cw-{{ status.id }}"{% endif %}> <div
{% if status.content_warning %} {% if status.content_warning %}
{% trans "Show less" as button_text %} id="show-status-cw-{{ status.id }}"
{% include 'snippets/toggle/close_button.html' with text=button_text class="is-small" controls_text="show-status-cw" controls_uid=status.id %} class="hidden"
{% endif %}
>
{% if status.content_warning %}
{% trans "Show less" as button_text %}
{% with text=button_text class="is-small" controls_text="show-status-cw" controls_uid=status.id %}
{% include 'snippets/toggle/close_button.html' %}
{% endwith %}
{% endif %} {% endif %}
{% if status.quote %} {% if status.quote %}
<div class="quote block"> <div class="quote block">
<blockquote dir="auto" class="mb-2">{{ status.quote | safe }}</blockquote> <blockquote dir="auto" class="mb-2">{{ status.quote | safe }}</blockquote>
<p> &mdash; {% include 'snippets/book_titleby.html' with book=status.book %}</p> <p> &mdash; {% include 'snippets/book_titleby.html' with book=status.book %}</p>
</div> </div>
{% endif %} {% endif %}
{% if status.content and status.status_type != 'GeneratedNote' and status.status_type != 'Announce' %} {% if status.content and status_type != 'GeneratedNote' and status_type != 'Announce' %}
{% include 'snippets/trimmed_text.html' with full=status.content|safe no_trim=status.content_warning %} {% with full=status.content|safe no_trim=status.content_warning itemprop=body_prop %}
{% endif %} {% include 'snippets/trimmed_text.html' %}
{% if status.attachments.exists %} {% endwith %}
<div class="block"> {% endif %}
<div class="columns">
{% for attachment in status.attachments.all %} {% if status.attachments.exists %}
<div class="column is-narrow"> <div class="block">
<figure class="image is-128x128"> <div class="columns">
<a href="/images/{{ attachment.image }}" target="_blank" aria-label="{% trans 'Open image in new window' %}"> {% for attachment in status.attachments.all %}
<img src="/images/{{ attachment.image }}"{% if attachment.caption %} alt="{{ attachment.caption }}" title="{{ attachment.caption }}"{% endif %}> <div class="column is-narrow">
</a> <figure class="image is-128x128">
</figure> <a
</div> href="/images/{{ attachment.image }}"
{% endfor %} target="_blank"
aria-label="{% trans 'Open image in new window' %}"
>
<img
src="/images/{{ attachment.image }}"
{% if attachment.caption %}
alt="{{ attachment.caption }}"
title="{{ attachment.caption }}"
{% endif %}
>
</a>
</figure>
</div>
{% endfor %}
</div>
</div> </div>
</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% if not hide_book %} {% if not hide_book %}
{% if status.book or status.mention_books.count %} {% if status.book or status.mention_books.count %}
<div class="{% if status.status_type != 'GeneratedNote' %}box has-background-white-bis{% endif %}"> <div
{% if status.book %} {% if status_type != 'GeneratedNote' %}
{% include 'snippets/status/book_preview.html' with book=status.book %} class="box has-background-white-bis"
{% elif status.mention_books.count %} {% endif %}
{% include 'snippets/status/book_preview.html' with book=status.mention_books.first %} >
{% if status.book %}
{% with book=status.book %}
{% include 'snippets/status/book_preview.html' %}
{% endwith %}
{% elif status.mention_books.count %}
{% with book=status.mention_books.first %}
{% include 'snippets/status/book_preview.html' %}
{% endwith %}
{% endif %}
</div>
{% endif %} {% endif %}
</div>
{% endif %}
{% endif %} {% endif %}
{% endwith %}
{% endspaceless %}

View file

@ -1,9 +1,19 @@
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %} {% load i18n %}
<a href="{{ status.user.local_path }}"> <span
{% include 'snippets/avatar.html' with user=status.user ariaHide="true" %} itemprop="author"
{{ status.user.display_name }} itemscope
</a> itemtype="https://schema.org/Person"
>
<a
href="{{ status.user.local_path }}"
itemprop="url"
>
{% include 'snippets/avatar.html' with user=status.user ariaHide="true" %}
<span itemprop="name">{{ status.user.display_name }}</span>
</a>
</span>
{% if status.status_type == 'GeneratedNote' %} {% if status.status_type == 'GeneratedNote' %}
{{ status.content | safe }} {{ status.content | safe }}

View file

@ -1,40 +1,49 @@
{% spaceless %}
{% load bookwyrm_tags %} {% load bookwyrm_tags %}
{% load i18n %} {% load i18n %}
{% with 0|uuid as uuid %} {% with 0|uuid as uuid %}
{% if full %} {% if full %}
{% with full|to_markdown|safe as full %} {% with full|to_markdown|safe as full %}
{% with full|to_markdown|safe|truncatewords_html:60 as trimmed %}
{% if not no_trim and trimmed != full %}
<div id="hide-full-{{ uuid }}">
<div class="content" id="trimmed-{{ uuid }}">
<div dir="auto">{{ trimmed }}</div>
{% with full|to_markdown|safe|truncatewords_html:60 as trimmed %} <div>
{% if not no_trim and trimmed != full %} {% trans "Show more" as button_text %}
<div id="hide-full-{{ uuid }}"> {% include 'snippets/toggle/open_button.html' with text=button_text controls_text="full" controls_uid=uuid class="is-small" %}
<div class="content" id="trimmed-{{ uuid }}"> </div>
<div dir="auto">{{ trimmed }}</div> </div>
</div>
<div id="full-{{ uuid }}" class="hidden">
<div class="content">
<div
dir="auto"
{% if itemprop %}itemprop="{{ itemprop }}{% endif %}"
>
{{ full }}
</div>
<div> <div>
{% trans "Show more" as button_text %} {% trans "Show less" as button_text %}
{% include 'snippets/toggle/open_button.html' with text=button_text controls_text="full" controls_uid=uuid class="is-small" %} {% include 'snippets/toggle/close_button.html' with text=button_text controls_text="full" controls_uid=uuid class="is-small" %}
</div> </div>
</div> </div>
</div> </div>
<div id="full-{{ uuid }}" class="hidden"> {% else %}
<div class="content"> <div class="content">
<div dir="auto">{{ full }}</div> <div
dir="auto"
<div> {% if itemprop %}itemprop="{{ itemprop }}{% endif %}"
{% trans "Show less" as button_text %} >
{% include 'snippets/toggle/close_button.html' with text=button_text controls_text="full" controls_uid=uuid class="is-small" %} {{ full }}
</div> </div>
</div> </div>
</div> {% endif %}
{% else %} {% endwith %}
<div class="content"> {% endwith %}
<div dir="auto">{{ full }}</div> {% endif %}
</div>
{% endif %}
{% endwith %} {% endwith %}
{% endspaceless %}
{% endwith %}
{% endif %}
{% endwith %}

File diff suppressed because it is too large Load diff