forked from mirrors/bookwyrm
parent
16fec1b6d5
commit
1a33290267
11 changed files with 281 additions and 44 deletions
|
@ -75,5 +75,34 @@ class TagForm(ModelForm):
|
||||||
labels = {'name': 'Add a tag'}
|
labels = {'name': 'Add a tag'}
|
||||||
|
|
||||||
|
|
||||||
|
class CoverForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = models.Book
|
||||||
|
fields = ['cover']
|
||||||
|
help_texts = {f: None for f in fields}
|
||||||
|
|
||||||
|
|
||||||
|
class BookForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = models.Book
|
||||||
|
exclude = [
|
||||||
|
'created_date',
|
||||||
|
'updated_date',
|
||||||
|
'last_sync_date',
|
||||||
|
|
||||||
|
'authors',
|
||||||
|
'parent_work',
|
||||||
|
'shelves',
|
||||||
|
'misc_identifiers',
|
||||||
|
|
||||||
|
'subjects',
|
||||||
|
'subject_places',
|
||||||
|
|
||||||
|
'source_url',
|
||||||
|
'connector',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class ImportForm(forms.Form):
|
class ImportForm(forms.Form):
|
||||||
csv_file = forms.FileField()
|
csv_file = forms.FileField()
|
||||||
|
|
||||||
|
|
114
fedireads/migrations/0023_auto_20200328_2203.py
Normal file
114
fedireads/migrations/0023_auto_20200328_2203.py
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
# Generated by Django 3.0.3 on 2020-03-28 22:03
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('fedireads', '0022_auto_20200328_2001'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='book',
|
||||||
|
name='sync_cover',
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='author',
|
||||||
|
name='born',
|
||||||
|
field=models.DateTimeField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='author',
|
||||||
|
name='died',
|
||||||
|
field=models.DateTimeField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='author',
|
||||||
|
name='fedireads_key',
|
||||||
|
field=models.CharField(default=uuid.uuid4, max_length=255, unique=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='author',
|
||||||
|
name='first_name',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='author',
|
||||||
|
name='last_name',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='author',
|
||||||
|
name='openlibrary_key',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='book',
|
||||||
|
name='first_published_date',
|
||||||
|
field=models.DateTimeField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='book',
|
||||||
|
name='goodreads_key',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='book',
|
||||||
|
name='language',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='book',
|
||||||
|
name='librarything_key',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='book',
|
||||||
|
name='openlibrary_key',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='book',
|
||||||
|
name='published_date',
|
||||||
|
field=models.DateTimeField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='book',
|
||||||
|
name='sort_title',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='book',
|
||||||
|
name='subtitle',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='edition',
|
||||||
|
name='isbn',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='edition',
|
||||||
|
name='oclc_number',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='edition',
|
||||||
|
name='pages',
|
||||||
|
field=models.IntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='edition',
|
||||||
|
name='physical_format',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='work',
|
||||||
|
name='lccn',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -47,15 +47,16 @@ class Connector(FedireadsModel):
|
||||||
class Book(FedireadsModel):
|
class Book(FedireadsModel):
|
||||||
''' a generic book, which can mean either an edition or a work '''
|
''' a generic book, which can mean either an edition or a work '''
|
||||||
# these identifiers apply to both works and editions
|
# these identifiers apply to both works and editions
|
||||||
openlibrary_key = models.CharField(max_length=255, unique=True, null=True)
|
|
||||||
librarything_key = models.CharField(max_length=255, unique=True, null=True)
|
|
||||||
fedireads_key = models.CharField(max_length=255, unique=True, default=uuid4)
|
fedireads_key = models.CharField(max_length=255, unique=True, default=uuid4)
|
||||||
goodreads_key = models.CharField(max_length=255, unique=True, null=True)
|
openlibrary_key = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
librarything_key = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
goodreads_key = models.CharField(max_length=255, blank=True, null=True)
|
||||||
misc_identifiers = JSONField(null=True)
|
misc_identifiers = JSONField(null=True)
|
||||||
|
|
||||||
# info about where the data comes from and where/if to sync
|
# info about where the data comes from and where/if to sync
|
||||||
source_url = models.CharField(max_length=255, unique=True, null=True)
|
source_url = models.CharField(max_length=255, unique=True, null=True)
|
||||||
sync = models.BooleanField(default=True)
|
sync = models.BooleanField(default=True)
|
||||||
|
sync_cover = models.BooleanField(default=True)
|
||||||
last_sync_date = models.DateTimeField(default=datetime.now)
|
last_sync_date = models.DateTimeField(default=datetime.now)
|
||||||
connector = models.ForeignKey(
|
connector = models.ForeignKey(
|
||||||
'Connector', on_delete=models.PROTECT, null=True)
|
'Connector', on_delete=models.PROTECT, null=True)
|
||||||
|
@ -64,10 +65,10 @@ class Book(FedireadsModel):
|
||||||
|
|
||||||
# book/work metadata
|
# book/work metadata
|
||||||
title = models.CharField(max_length=255)
|
title = models.CharField(max_length=255)
|
||||||
sort_title = models.CharField(max_length=255, null=True)
|
sort_title = models.CharField(max_length=255, blank=True, null=True)
|
||||||
subtitle = models.TextField(blank=True, null=True)
|
subtitle = models.CharField(max_length=255, blank=True, null=True)
|
||||||
description = models.TextField(blank=True, null=True)
|
description = models.TextField(blank=True, null=True)
|
||||||
language = models.CharField(max_length=255, null=True)
|
language = models.CharField(max_length=255, blank=True, null=True)
|
||||||
series = models.CharField(max_length=255, blank=True, null=True)
|
series = models.CharField(max_length=255, blank=True, null=True)
|
||||||
series_number = models.CharField(max_length=255, blank=True, null=True)
|
series_number = models.CharField(max_length=255, blank=True, null=True)
|
||||||
subjects = ArrayField(
|
subjects = ArrayField(
|
||||||
|
@ -78,10 +79,9 @@ class Book(FedireadsModel):
|
||||||
)
|
)
|
||||||
# TODO: include an annotation about the type of authorship (ie, translator)
|
# TODO: include an annotation about the type of authorship (ie, translator)
|
||||||
authors = models.ManyToManyField('Author')
|
authors = models.ManyToManyField('Author')
|
||||||
# TODO: also store cover thumbnail
|
|
||||||
cover = models.ImageField(upload_to='covers/', blank=True, null=True)
|
cover = models.ImageField(upload_to='covers/', blank=True, null=True)
|
||||||
first_published_date = models.DateTimeField(null=True)
|
first_published_date = models.DateTimeField(blank=True, null=True)
|
||||||
published_date = models.DateTimeField(null=True)
|
published_date = models.DateTimeField(blank=True, null=True)
|
||||||
shelves = models.ManyToManyField(
|
shelves = models.ManyToManyField(
|
||||||
'Shelf',
|
'Shelf',
|
||||||
symmetrical=False,
|
symmetrical=False,
|
||||||
|
@ -109,16 +109,16 @@ class Book(FedireadsModel):
|
||||||
class Work(Book):
|
class Work(Book):
|
||||||
''' a work (an abstract concept of a book that manifests in an edition) '''
|
''' a work (an abstract concept of a book that manifests in an edition) '''
|
||||||
# library of congress catalog control number
|
# library of congress catalog control number
|
||||||
lccn = models.CharField(max_length=255, unique=True, null=True)
|
lccn = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
|
|
||||||
class Edition(Book):
|
class Edition(Book):
|
||||||
''' an edition of a book '''
|
''' an edition of a book '''
|
||||||
# these identifiers only apply to work
|
# these identifiers only apply to work
|
||||||
isbn = models.CharField(max_length=255, unique=True, null=True)
|
isbn = models.CharField(max_length=255, blank=True, null=True)
|
||||||
oclc_number = models.CharField(max_length=255, unique=True, null=True)
|
oclc_number = models.CharField(max_length=255, blank=True, null=True)
|
||||||
pages = models.IntegerField(null=True)
|
pages = models.IntegerField(blank=True, null=True)
|
||||||
physical_format = models.CharField(max_length=255, null=True)
|
physical_format = models.CharField(max_length=255, blank=True, null=True)
|
||||||
publishers = ArrayField(
|
publishers = ArrayField(
|
||||||
models.CharField(max_length=255), blank=True, default=list
|
models.CharField(max_length=255), blank=True, default=list
|
||||||
)
|
)
|
||||||
|
@ -126,15 +126,15 @@ class Edition(Book):
|
||||||
|
|
||||||
class Author(FedireadsModel):
|
class Author(FedireadsModel):
|
||||||
''' copy of an author from OL '''
|
''' copy of an author from OL '''
|
||||||
openlibrary_key = models.CharField(max_length=255, null=True, unique=True)
|
fedireads_key = models.CharField(max_length=255, unique=True, default=uuid4)
|
||||||
fedireads_key = models.CharField(max_length=255, null=True, unique=True)
|
openlibrary_key = models.CharField(max_length=255, blank=True, null=True)
|
||||||
wikipedia_link = models.CharField(max_length=255, blank=True, null=True)
|
wikipedia_link = models.CharField(max_length=255, blank=True, null=True)
|
||||||
# idk probably other keys would be useful here?
|
# idk probably other keys would be useful here?
|
||||||
born = models.DateTimeField(null=True)
|
born = models.DateTimeField(blank=True, null=True)
|
||||||
died = models.DateTimeField(null=True)
|
died = models.DateTimeField(blank=True, null=True)
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
last_name = models.CharField(max_length=255, null=True)
|
last_name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
first_name = models.CharField(max_length=255, null=True)
|
first_name = models.CharField(max_length=255, blank=True, null=True)
|
||||||
aliases = ArrayField(
|
aliases = ArrayField(
|
||||||
models.CharField(max_length=255), blank=True, default=list
|
models.CharField(max_length=255), blank=True, default=list
|
||||||
)
|
)
|
||||||
|
|
|
@ -281,12 +281,12 @@ button.warning {
|
||||||
height: 5em;
|
height: 5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-profile h2 a {
|
h2 .edit-link {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
.user-profile h2 .icon {
|
h2 .edit-link .icon {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
.user-profile .row > * {
|
.user-profile .row > * {
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="content-container">
|
<div class="content-container user-profile">
|
||||||
<h2><q>{{ book.title }}</q> by
|
<h2><q>{{ book.title }}</q> by
|
||||||
{% include 'snippets/authors.html' with book=book %}</h2>
|
{% include 'snippets/authors.html' with book=book %}
|
||||||
|
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
<a href="{{ book.fedireads_key }}/edit" class="edit-link">edit
|
||||||
|
<span class="icon icon-pencil">
|
||||||
|
<span class="hidden-text">Edit Book</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</h2>
|
||||||
<div>
|
<div>
|
||||||
{% if book.parent_work %}<p>Edition of <a href="/book/{{ book.parent_work.fedireads_key }}">{{ book.parent_work.title }}</a></p>{% endif %}
|
{% if book.parent_work %}<p>Edition of <a href="/book/{{ book.parent_work.fedireads_key }}">{{ book.parent_work.title }}</a></p>{% endif %}
|
||||||
<div class="book-preview">
|
<div class="book-preview">
|
||||||
|
@ -24,6 +33,15 @@
|
||||||
{% include 'snippets/shelve_button.html' %}
|
{% include 'snippets/shelve_button.html' %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
{% if request.user.is_authenticated and not book.cover %}
|
||||||
|
<form name="add-cover" method="POST" action="/upload_cover/{{book.id}}" enctype="multipart/form-data">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ cover_form.as_p }}
|
||||||
|
<button type="submit">Add cover</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
15
fedireads/templates/edit_book.html
Normal file
15
fedireads/templates/edit_book.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{% extends 'layout.html' %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="content-container">
|
||||||
|
<h2>Edit "{{ book.title }}"</h2>
|
||||||
|
|
||||||
|
<p class="book-cover">{% include 'snippets/book_cover.html' with book=book %}</p>
|
||||||
|
|
||||||
|
<form name="edit-book" action="/edit_book/{{ book.id }}" method="post" enctype="multipart/form-data">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.as_p }}
|
||||||
|
<button type="submit">Update book</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="content">
|
<div class="content-container">
|
||||||
<div class="user-profile">
|
<div class="user-profile">
|
||||||
<h1>{% if user.localname %}{{ user.localname }}{% else %}{{ user.username }}{% endif %}</h1>
|
<h2>Edit Profile</h2>
|
||||||
|
|
||||||
|
<p>{% include 'snippets/avatar.html' with user=user %} {% if user.localname %}{{ user.localname }}{% else %}{{ user.username }}{% endif %}</p>
|
||||||
|
|
||||||
<form name="avatar" action="/edit_profile/" method="post" enctype="multipart/form-data">
|
<form name="avatar" action="/edit_profile/" method="post" enctype="multipart/form-data">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
<div class="content-container user-profile">
|
<div class="content-container user-profile">
|
||||||
<h2>User Profile
|
<h2>User Profile
|
||||||
{% if is_self %}
|
{% if is_self %}
|
||||||
<a href="/user-edit/">edit
|
<a href="/user-edit/" class="edit-link">edit
|
||||||
<span class="icon icon-pencil">
|
<span class="icon icon-pencil">
|
||||||
<span class="hidden-text">Edit profile</span>
|
<span class="hidden-text">Edit profile</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -35,29 +35,27 @@ urlpatterns = [
|
||||||
re_path(r'^notifications/?', views.notifications_page),
|
re_path(r'^notifications/?', views.notifications_page),
|
||||||
re_path(r'books/?$', views.books_page),
|
re_path(r'books/?$', views.books_page),
|
||||||
re_path(r'import/?$', views.import_page),
|
re_path(r'import/?$', views.import_page),
|
||||||
|
re_path(r'user-edit/?$', views.edit_profile_page),
|
||||||
|
|
||||||
# should return a ui view or activitypub json blob as requested
|
# should return a ui view or activitypub json blob as requested
|
||||||
# users
|
# users
|
||||||
re_path(r'%s/?$' % user_path, views.user_page),
|
re_path(r'%s/?$' % user_path, views.user_page),
|
||||||
re_path(r'%s/?$' % local_user_path, views.user_page),
|
re_path(r'%s/?$' % local_user_path, views.user_page),
|
||||||
re_path(r'%s\.json$' % local_user_path, views.user_page),
|
re_path(r'%s\.json$' % local_user_path, views.user_page),
|
||||||
re_path(r'user-edit/?$', views.edit_profile_page),
|
|
||||||
re_path(r'%s/shelves/?$' % local_user_path, views.user_shelves_page),
|
re_path(r'%s/shelves/?$' % local_user_path, views.user_shelves_page),
|
||||||
re_path(r'%s/followers/?$' % local_user_path, views.followers_page),
|
re_path(r'%s/followers(.json)?/?$' % local_user_path, views.followers_page),
|
||||||
re_path(r'%s/followers.json$' % local_user_path, views.followers_page),
|
re_path(r'%s/following(.json)?/?$' % local_user_path, views.following_page),
|
||||||
re_path(r'%s/following/?$' % local_user_path, views.following_page),
|
|
||||||
re_path(r'%s/following.json$' % local_user_path, views.following_page),
|
|
||||||
|
|
||||||
# statuses
|
# statuses
|
||||||
re_path(r'%s/?$' % status_path, views.status_page),
|
re_path(r'%s(.json)?/?$' % status_path, views.status_page),
|
||||||
re_path(r'%s.json$' % status_path, views.status_page),
|
|
||||||
re_path(r'%s/activity/?$' % status_path, views.status_page),
|
re_path(r'%s/activity/?$' % status_path, views.status_page),
|
||||||
re_path(r'%s/replies/?$' % status_path, views.replies_page),
|
re_path(r'%s/replies(.json)?/?$' % status_path, views.replies_page),
|
||||||
re_path(r'%s/replies\.json$' % status_path, views.replies_page),
|
|
||||||
|
|
||||||
# books
|
# books
|
||||||
re_path(r'^book/(?P<book_identifier>[\w\-]+)(.json)?/?$', views.book_page),
|
re_path(r'^book/(?P<book_identifier>[\w\-]+)(.json)?/?$', views.book_page),
|
||||||
re_path(r'^book/(?P<book_identifier>[\w\-]+)/(?P<tab>friends|local|federated)?$', views.book_page),
|
re_path(r'^book/(?P<book_identifier>[\w\-]+)/(?P<tab>friends|local|federated)?$', views.book_page),
|
||||||
|
re_path(r'^book/(?P<book_identifier>[\w\-]+)/edit/?$', views.edit_book_page),
|
||||||
|
|
||||||
re_path(r'^author/(?P<author_identifier>\w+)/?$', views.author_page),
|
re_path(r'^author/(?P<author_identifier>\w+)/?$', views.author_page),
|
||||||
re_path(r'^tag/(?P<tag_id>.+)/?$', views.tag_page),
|
re_path(r'^tag/(?P<tag_id>.+)/?$', views.tag_page),
|
||||||
re_path(r'^shelf/%s/(?P<shelf_identifier>[\w-]+)(.json)?/?$' % username_regex, views.shelf_page),
|
re_path(r'^shelf/%s/(?P<shelf_identifier>[\w-]+)(.json)?/?$' % username_regex, views.shelf_page),
|
||||||
|
@ -67,23 +65,29 @@ urlpatterns = [
|
||||||
re_path(r'^logout/?$', actions.user_logout),
|
re_path(r'^logout/?$', actions.user_logout),
|
||||||
re_path(r'^user-login/?$', actions.user_login),
|
re_path(r'^user-login/?$', actions.user_login),
|
||||||
re_path(r'^register/?$', actions.register),
|
re_path(r'^register/?$', actions.register),
|
||||||
|
re_path(r'^edit_profile/?$', actions.edit_profile),
|
||||||
|
|
||||||
|
re_path(r'^search/?$', actions.search),
|
||||||
|
re_path(r'^import_data/?', actions.import_data),
|
||||||
|
re_path(r'^edit_book/(?P<book_id>\d+)/?', actions.edit_book),
|
||||||
|
re_path(r'^upload_cover/(?P<book_id>\d+)/?', actions.upload_cover),
|
||||||
|
|
||||||
re_path(r'^review/?$', actions.review),
|
re_path(r'^review/?$', actions.review),
|
||||||
re_path(r'^comment/?$', actions.comment),
|
re_path(r'^comment/?$', actions.comment),
|
||||||
re_path(r'^tag/?$', actions.tag),
|
re_path(r'^tag/?$', actions.tag),
|
||||||
re_path(r'^untag/?$', actions.untag),
|
re_path(r'^untag/?$', actions.untag),
|
||||||
re_path(r'^reply/?$', actions.reply),
|
re_path(r'^reply/?$', actions.reply),
|
||||||
|
|
||||||
re_path(r'^favorite/(?P<status_id>\d+)/?$', actions.favorite),
|
re_path(r'^favorite/(?P<status_id>\d+)/?$', actions.favorite),
|
||||||
re_path(r'^unfavorite/(?P<status_id>\d+)/?$', actions.unfavorite),
|
re_path(r'^unfavorite/(?P<status_id>\d+)/?$', actions.unfavorite),
|
||||||
|
|
||||||
re_path(r'^shelve/?$', actions.shelve),
|
re_path(r'^shelve/?$', actions.shelve),
|
||||||
|
|
||||||
re_path(r'^follow/?$', actions.follow),
|
re_path(r'^follow/?$', actions.follow),
|
||||||
re_path(r'^unfollow/?$', actions.unfollow),
|
re_path(r'^unfollow/?$', actions.unfollow),
|
||||||
re_path(r'^search/?$', actions.search),
|
|
||||||
re_path(r'^edit_profile/?$', actions.edit_profile),
|
|
||||||
re_path(r'^clear-notifications/?$', actions.clear_notifications),
|
|
||||||
|
|
||||||
re_path(r'^accept_follow_request/?$', actions.accept_follow_request),
|
re_path(r'^accept_follow_request/?$', actions.accept_follow_request),
|
||||||
re_path(r'^delete_follow_request/?$', actions.delete_follow_request),
|
re_path(r'^delete_follow_request/?$', actions.delete_follow_request),
|
||||||
|
|
||||||
re_path(r'import_data', actions.import_data),
|
re_path(r'^clear-notifications/?$', actions.clear_notifications),
|
||||||
|
|
||||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|
|
@ -3,7 +3,7 @@ from io import TextIOWrapper
|
||||||
|
|
||||||
from django.contrib.auth import authenticate, login, logout
|
from django.contrib.auth import authenticate, login, logout
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.http import HttpResponseBadRequest
|
from django.http import HttpResponseBadRequest, HttpResponseNotFound
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
import re
|
import re
|
||||||
|
@ -87,9 +87,51 @@ def edit_profile(request):
|
||||||
return redirect('/user/%s' % request.user.localname)
|
return redirect('/user/%s' % request.user.localname)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def edit_book(request, book_id):
|
||||||
|
''' edit a book cool '''
|
||||||
|
if not request.method == 'POST':
|
||||||
|
return redirect('/book/%s' % request.user.localname)
|
||||||
|
|
||||||
|
try:
|
||||||
|
book = models.Book.objects.get(id=book_id)
|
||||||
|
except models.Book.DoesNotExist:
|
||||||
|
return HttpResponseNotFound()
|
||||||
|
|
||||||
|
form = forms.BookForm(request.POST, request.FILES, instance=book)
|
||||||
|
if not form.is_valid():
|
||||||
|
return redirect(request.headers.get('Referer', '/'))
|
||||||
|
form.save()
|
||||||
|
|
||||||
|
return redirect('/book/%s' % book.fedireads_key)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def upload_cover(request, book_id):
|
||||||
|
''' upload a new cover '''
|
||||||
|
# TODO: alternate covers?
|
||||||
|
if not request.method == 'POST':
|
||||||
|
return redirect('/book/%s' % request.user.localname)
|
||||||
|
|
||||||
|
try:
|
||||||
|
book = models.Book.objects.get(id=book_id)
|
||||||
|
except models.Book.DoesNotExist:
|
||||||
|
return HttpResponseNotFound()
|
||||||
|
|
||||||
|
form = forms.CoverForm(request.POST, request.FILES, instance=book)
|
||||||
|
if not form.is_valid():
|
||||||
|
return redirect(request.headers.get('Referer', '/'))
|
||||||
|
|
||||||
|
book.cover = form.files['cover']
|
||||||
|
book.sync_cover = False
|
||||||
|
book.save()
|
||||||
|
|
||||||
|
return redirect('/book/%s' % book.fedireads_key)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def shelve(request):
|
def shelve(request):
|
||||||
''' put a book on a user's shelf '''
|
''' put a on a user's shelf '''
|
||||||
book = models.Book.objects.get(id=request.POST['book'])
|
book = models.Book.objects.get(id=request.POST['book'])
|
||||||
desired_shelf = models.Shelf.objects.filter(
|
desired_shelf = models.Shelf.objects.filter(
|
||||||
identifier=request.POST['shelf'],
|
identifier=request.POST['shelf'],
|
||||||
|
|
|
@ -390,10 +390,22 @@ def book_page(request, book_identifier, tab='friends'):
|
||||||
],
|
],
|
||||||
'active_tab': tab,
|
'active_tab': tab,
|
||||||
'path': '/book/%s' % book_identifier,
|
'path': '/book/%s' % book_identifier,
|
||||||
|
'cover_form': forms.CoverForm(instance=book),
|
||||||
}
|
}
|
||||||
return TemplateResponse(request, 'book.html', data)
|
return TemplateResponse(request, 'book.html', data)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def edit_book_page(request, book_identifier):
|
||||||
|
''' info about a book '''
|
||||||
|
book = books_manager.get_or_create_book(book_identifier)
|
||||||
|
data = {
|
||||||
|
'book': book,
|
||||||
|
'form': forms.BookForm(instance=book)
|
||||||
|
}
|
||||||
|
return TemplateResponse(request, 'edit_book.html', data)
|
||||||
|
|
||||||
|
|
||||||
def author_page(request, author_identifier):
|
def author_page(request, author_identifier):
|
||||||
''' landing page for an author '''
|
''' landing page for an author '''
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Reference in a new issue