forked from mirrors/bookwyrm
commit
4fda5c8e22
68 changed files with 1140 additions and 1755 deletions
|
@ -57,7 +57,7 @@ def broadcast_task(sender_id, activity, recipients):
|
||||||
except requests.exceptions.HTTPError as e:
|
except requests.exceptions.HTTPError as e:
|
||||||
# TODO: maybe keep track of users who cause errors
|
# TODO: maybe keep track of users who cause errors
|
||||||
errors.append({
|
errors.append({
|
||||||
'error': e,
|
'error': str(e),
|
||||||
'recipient': recipient,
|
'recipient': recipient,
|
||||||
'activity': activity,
|
'activity': activity,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,13 +1,33 @@
|
||||||
''' using django model forms '''
|
''' using django model forms '''
|
||||||
import datetime
|
import datetime
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from django.forms import ModelForm, PasswordInput, widgets
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.forms import ModelForm, PasswordInput, widgets
|
||||||
|
from django.forms.widgets import Textarea
|
||||||
|
|
||||||
from bookwyrm import models
|
from bookwyrm import models
|
||||||
|
|
||||||
|
|
||||||
class LoginForm(ModelForm):
|
class CustomForm(ModelForm):
|
||||||
|
''' add css classes to the forms '''
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
css_classes = defaultdict(lambda: '')
|
||||||
|
css_classes['text'] = 'input'
|
||||||
|
css_classes['password'] = 'input'
|
||||||
|
css_classes['email'] = 'input'
|
||||||
|
css_classes['number'] = 'input'
|
||||||
|
css_classes['checkbox'] = 'checkbox'
|
||||||
|
css_classes['textarea'] = 'textarea'
|
||||||
|
super(CustomForm, self).__init__(*args, **kwargs)
|
||||||
|
for visible in self.visible_fields():
|
||||||
|
if hasattr(visible.field.widget, 'input_type'):
|
||||||
|
input_type = visible.field.widget.input_type
|
||||||
|
if isinstance(visible.field.widget, Textarea):
|
||||||
|
input_type = 'textarea'
|
||||||
|
visible.field.widget.attrs['class'] = css_classes[input_type]
|
||||||
|
|
||||||
|
class LoginForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.User
|
model = models.User
|
||||||
fields = ['username', 'password']
|
fields = ['username', 'password']
|
||||||
|
@ -17,7 +37,7 @@ class LoginForm(ModelForm):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RegisterForm(ModelForm):
|
class RegisterForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.User
|
model = models.User
|
||||||
fields = ['username', 'email', 'password']
|
fields = ['username', 'email', 'password']
|
||||||
|
@ -27,13 +47,13 @@ class RegisterForm(ModelForm):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RatingForm(ModelForm):
|
class RatingForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Review
|
model = models.Review
|
||||||
fields = ['rating']
|
fields = ['rating']
|
||||||
|
|
||||||
|
|
||||||
class ReviewForm(ModelForm):
|
class ReviewForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Review
|
model = models.Review
|
||||||
fields = ['name', 'content']
|
fields = ['name', 'content']
|
||||||
|
@ -44,7 +64,7 @@ class ReviewForm(ModelForm):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CommentForm(ModelForm):
|
class CommentForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Comment
|
model = models.Comment
|
||||||
fields = ['content']
|
fields = ['content']
|
||||||
|
@ -54,7 +74,7 @@ class CommentForm(ModelForm):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class QuotationForm(ModelForm):
|
class QuotationForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Quotation
|
model = models.Quotation
|
||||||
fields = ['quote', 'content']
|
fields = ['quote', 'content']
|
||||||
|
@ -65,7 +85,7 @@ class QuotationForm(ModelForm):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ReplyForm(ModelForm):
|
class ReplyForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Status
|
model = models.Status
|
||||||
fields = ['content']
|
fields = ['content']
|
||||||
|
@ -73,14 +93,14 @@ class ReplyForm(ModelForm):
|
||||||
labels = {'content': 'Comment'}
|
labels = {'content': 'Comment'}
|
||||||
|
|
||||||
|
|
||||||
class EditUserForm(ModelForm):
|
class EditUserForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.User
|
model = models.User
|
||||||
fields = ['avatar', 'name', 'summary', 'manually_approves_followers']
|
fields = ['avatar', 'name', 'summary', 'manually_approves_followers']
|
||||||
help_texts = {f: None for f in fields}
|
help_texts = {f: None for f in fields}
|
||||||
|
|
||||||
|
|
||||||
class TagForm(ModelForm):
|
class TagForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Tag
|
model = models.Tag
|
||||||
fields = ['name']
|
fields = ['name']
|
||||||
|
@ -88,17 +108,18 @@ class TagForm(ModelForm):
|
||||||
labels = {'name': 'Add a tag'}
|
labels = {'name': 'Add a tag'}
|
||||||
|
|
||||||
|
|
||||||
class CoverForm(ModelForm):
|
class CoverForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Book
|
model = models.Book
|
||||||
fields = ['cover']
|
fields = ['cover']
|
||||||
help_texts = {f: None for f in fields}
|
help_texts = {f: None for f in fields}
|
||||||
|
|
||||||
|
|
||||||
class EditionForm(ModelForm):
|
class EditionForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Edition
|
model = models.Edition
|
||||||
exclude = [
|
exclude = [
|
||||||
|
'remote_id',
|
||||||
'created_date',
|
'created_date',
|
||||||
'updated_date',
|
'updated_date',
|
||||||
'last_sync_date',
|
'last_sync_date',
|
||||||
|
@ -135,7 +156,7 @@ class ExpiryWidget(widgets.Select):
|
||||||
|
|
||||||
return datetime.datetime.now() + interval
|
return datetime.datetime.now() + interval
|
||||||
|
|
||||||
class CreateInviteForm(ModelForm):
|
class CreateInviteForm(CustomForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.SiteInvite
|
model = models.SiteInvite
|
||||||
exclude = ['code', 'user', 'times_used']
|
exclude = ['code', 'user', 'times_used']
|
||||||
|
|
|
@ -91,6 +91,11 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
||||||
to write this so it's just a property '''
|
to write this so it's just a property '''
|
||||||
return cls.objects.filter(reply_parent=status).select_subclasses()
|
return cls.objects.filter(reply_parent=status).select_subclasses()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def status_type(self):
|
||||||
|
''' expose the type of status for the ui using activity type '''
|
||||||
|
return self.activity_serializer.__name__
|
||||||
|
|
||||||
def to_replies(self, **kwargs):
|
def to_replies(self, **kwargs):
|
||||||
''' helper function for loading AP serialized replies to a status '''
|
''' helper function for loading AP serialized replies to a status '''
|
||||||
return self.to_ordered_collection(
|
return self.to_ordered_collection(
|
||||||
|
@ -211,7 +216,7 @@ class Boost(Status):
|
||||||
ActivityMapping('object', 'boosted_status'),
|
ActivityMapping('object', 'boosted_status'),
|
||||||
]
|
]
|
||||||
|
|
||||||
activity_serializer = activitypub.Like
|
activity_serializer = activitypub.Boost
|
||||||
|
|
||||||
# This constraint can't work as it would cross tables.
|
# This constraint can't work as it would cross tables.
|
||||||
# class Meta:
|
# class Meta:
|
||||||
|
|
|
@ -43,17 +43,20 @@ def handle_account_search(query):
|
||||||
except models.User.DoesNotExist:
|
except models.User.DoesNotExist:
|
||||||
url = 'https://%s/.well-known/webfinger?resource=acct:%s' % \
|
url = 'https://%s/.well-known/webfinger?resource=acct:%s' % \
|
||||||
(domain, query)
|
(domain, query)
|
||||||
|
try:
|
||||||
response = requests.get(url)
|
response = requests.get(url)
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
return None
|
||||||
if not response.ok:
|
if not response.ok:
|
||||||
response.raise_for_status()
|
return None
|
||||||
data = response.json()
|
data = response.json()
|
||||||
for link in data['links']:
|
for link in data['links']:
|
||||||
if link['rel'] == 'self':
|
if link['rel'] == 'self':
|
||||||
try:
|
try:
|
||||||
user = get_or_create_remote_user(link['href'])
|
user = get_or_create_remote_user(link['href'])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return HttpResponseNotFound()
|
return None
|
||||||
return user
|
return [user]
|
||||||
|
|
||||||
|
|
||||||
def handle_follow(user, to_follow):
|
def handle_follow(user, to_follow):
|
||||||
|
|
|
@ -44,8 +44,7 @@ def make_signature(sender, destination, date, digest):
|
||||||
|
|
||||||
def make_digest(data):
|
def make_digest(data):
|
||||||
''' creates a message digest for signing '''
|
''' creates a message digest for signing '''
|
||||||
return 'SHA-256=' + b64encode(hashlib.sha256(data.encode('utf-8'))\
|
return 'SHA-256=' + b64encode(hashlib.sha256(data).digest()).decode('utf-8')
|
||||||
.digest()).decode('utf-8')
|
|
||||||
|
|
||||||
|
|
||||||
def verify_digest(request):
|
def verify_digest(request):
|
||||||
|
|
1
bookwyrm/static/css/bulma.css.map
Normal file
1
bookwyrm/static/css/bulma.css.map
Normal file
File diff suppressed because one or more lines are too long
1
bookwyrm/static/css/bulma.min.css
vendored
Normal file
1
bookwyrm/static/css/bulma.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
115
bookwyrm/static/css/format.css
Normal file
115
bookwyrm/static/css/format.css
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
/* --- ICONS --- */
|
||||||
|
/* --- TOGGLES --- */
|
||||||
|
input.toggle-control {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.toggle-control:checked ~ .toggle-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- STARS --- */
|
||||||
|
.rate-stars button.icon {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
.rate-stars:hover .icon:before {
|
||||||
|
content: '\e9d9';
|
||||||
|
}
|
||||||
|
.rate-stars form:hover ~ form .icon:before{
|
||||||
|
content: '\e9d7';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* stars in a review form */
|
||||||
|
.form-rate-stars:hover .icon:before {
|
||||||
|
content: '\e9d9';
|
||||||
|
}
|
||||||
|
.form-rate-stars input + .icon:before {
|
||||||
|
content: '\e9d9';
|
||||||
|
}
|
||||||
|
.form-rate-stars input:checked + .icon:before {
|
||||||
|
content: '\e9d9';
|
||||||
|
}
|
||||||
|
.form-rate-stars input:checked + * ~ .icon:before {
|
||||||
|
content: '\e9d7';
|
||||||
|
}
|
||||||
|
.form-rate-stars:hover label.icon:before {
|
||||||
|
content: '\e9d9';
|
||||||
|
}
|
||||||
|
.form-rate-stars label.icon:hover:before {
|
||||||
|
content: '\e9d9';
|
||||||
|
}
|
||||||
|
.form-rate-stars label.icon:hover ~ label.icon:before{
|
||||||
|
content: '\e9d7';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* --- BOOK COVERS --- */
|
||||||
|
.cover-container {
|
||||||
|
height: 250px;
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
.cover-container.is-medium {
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
.cover-container.is-medium .no-cover div {
|
||||||
|
font-size: 0.9em;
|
||||||
|
padding: 0.3em;
|
||||||
|
}
|
||||||
|
.cover-container.is-small {
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
.cover-container.is-small .no-cover div {
|
||||||
|
font-size: 0.7em;
|
||||||
|
padding: 0.1em;
|
||||||
|
}
|
||||||
|
.book-cover {
|
||||||
|
height: 100%;
|
||||||
|
object-fit: scale-down;
|
||||||
|
}
|
||||||
|
.no-cover {
|
||||||
|
position: relative;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
.no-cover div {
|
||||||
|
position: absolute;
|
||||||
|
padding: 1em;
|
||||||
|
color: white;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* --- AVATAR --- */
|
||||||
|
.avatar {
|
||||||
|
vertical-align: middle;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* --- QUOTES --- */
|
||||||
|
.quote blockquote {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 2em;
|
||||||
|
}
|
||||||
|
.quote blockquote:before, .quote blockquote:after {
|
||||||
|
font-family: 'icomoon';
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
.quote blockquote:before {
|
||||||
|
content: "\e904";
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.quote blockquote:after {
|
||||||
|
content: "\e903";
|
||||||
|
right: 0;
|
||||||
|
}
|
|
@ -1,822 +0,0 @@
|
||||||
/* some colors that are okay: #247BA0 #70C1B2 #B2DBBF #F3FFBD #FF1654 */
|
|
||||||
|
|
||||||
/* general override */
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
line-height: 1.3em;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
html {
|
|
||||||
background-color: #FFF;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #247BA0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 1rem;
|
|
||||||
padding: 0.5rem 0.2rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
border-bottom: 3px solid #B2DBBF;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 .edit-link {
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 0.9em;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
h2 .edit-link .icon {
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
h3 small {
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* fixed display top bar */
|
|
||||||
body {
|
|
||||||
padding-top: 90px;
|
|
||||||
}
|
|
||||||
#top-bar {
|
|
||||||
overflow: visible;
|
|
||||||
padding: 0.5rem;
|
|
||||||
border-bottom: 3px solid #247BA0;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
width: 100%;
|
|
||||||
background-color: #FFF;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
height: 47px;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- header bar content */
|
|
||||||
#branding {
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
#menu {
|
|
||||||
list-style: none;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
flex-grow: 2;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
#menu li {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0 0.5em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
#menu a {
|
|
||||||
color: #555;
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#actions {
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
#actions > * {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
#actions > *:last-child {
|
|
||||||
margin-left: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#notifications .icon {
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
#notifications a {
|
|
||||||
color: black;
|
|
||||||
text-decoration: none;
|
|
||||||
position: relative;
|
|
||||||
top: 0.2rem;
|
|
||||||
}
|
|
||||||
#notifications .count {
|
|
||||||
background-color: #FF1654;
|
|
||||||
color: white;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
text-align: center;
|
|
||||||
top: -0.65rem;
|
|
||||||
right: -0.5rem;
|
|
||||||
height: 1rem;
|
|
||||||
width: 1rem;
|
|
||||||
}
|
|
||||||
.notification {
|
|
||||||
margin-bottom: 1em;
|
|
||||||
padding: 1em 0;
|
|
||||||
background-color: #EEE;
|
|
||||||
}
|
|
||||||
.notification.unread {
|
|
||||||
background-color: #DDD;
|
|
||||||
}
|
|
||||||
|
|
||||||
#search button {
|
|
||||||
border: none;
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#main, header {
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 55rem;
|
|
||||||
padding-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* pulldown */
|
|
||||||
.pulldown-container {
|
|
||||||
position: relative;
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pulldown {
|
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
list-style: none;
|
|
||||||
background: white;
|
|
||||||
padding: 1em;
|
|
||||||
right: 0;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
box-shadow: 0 5px 10px rgba(0,0,0,0.15);
|
|
||||||
width: max-content;
|
|
||||||
text-align: left;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pulldown-container:hover .pulldown {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pulldown li a {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
text-decoration: none;
|
|
||||||
padding: 0.3em 0.8em
|
|
||||||
}
|
|
||||||
|
|
||||||
div.pulldown-button {
|
|
||||||
background-color: #eee;
|
|
||||||
border-radius: 0.3em;
|
|
||||||
color: #247BA0;
|
|
||||||
width: max-content;
|
|
||||||
margin: 0 auto;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.post div.pulldown-button {
|
|
||||||
border: 2px solid #247BA0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pulldown-button form {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.pulldown-button button {
|
|
||||||
display: inline;
|
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
background-color: inherit;
|
|
||||||
color: #247BA0;
|
|
||||||
}
|
|
||||||
div.pulldown-button .pulldown-toggle {
|
|
||||||
padding-right: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
position: relative;
|
|
||||||
left: -0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.pulldown button {
|
|
||||||
display: block;
|
|
||||||
text-align: left;
|
|
||||||
width: 100%;
|
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
background-color: white;
|
|
||||||
color: #247BA0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pulldown button[disabled] {
|
|
||||||
color: #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pulldown button[disabled]:hover {
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pulldown button:hover, .pulldown li:hover {
|
|
||||||
background-color: #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* content area */
|
|
||||||
.content-container {
|
|
||||||
margin: 1rem;
|
|
||||||
}
|
|
||||||
.content-container > * {
|
|
||||||
padding-left: 1em;
|
|
||||||
padding-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#feed {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding-top: 70px;
|
|
||||||
position: relative;
|
|
||||||
z-index: 0;
|
|
||||||
margin-top: -2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* row component */
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
.row > * {
|
|
||||||
flex-grow: 1;
|
|
||||||
width: min-content;
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
|
||||||
.row > *:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
.row.shrink > * {
|
|
||||||
flex-grow: 0;
|
|
||||||
width: max-content;
|
|
||||||
}
|
|
||||||
.row.wrap {
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.column {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.column > * {
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* discover books page grid of covers */
|
|
||||||
.book-grid .book-cover {
|
|
||||||
height: 176px;
|
|
||||||
width: auto;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
.book-grid .no-cover {
|
|
||||||
width: 115px;
|
|
||||||
}
|
|
||||||
.book-grid > * {
|
|
||||||
margin-bottom: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* special case forms */
|
|
||||||
.review-form label {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.review-form textarea {
|
|
||||||
width: 30rem;
|
|
||||||
height: 10rem;
|
|
||||||
}
|
|
||||||
.review-form.quote-form textarea#id_content {
|
|
||||||
height: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.follow-requests .row {
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
.follow-requests .row > *:first-child {
|
|
||||||
width: 20em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.login form {
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
.login form p {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
padding: 0.5em 0;
|
|
||||||
}
|
|
||||||
.login form label {
|
|
||||||
width: 0;
|
|
||||||
flex-grow: 1;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.book-form textarea {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
.book-form label {
|
|
||||||
display: inline-block;
|
|
||||||
width: 8rem;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
.book-form .row label {
|
|
||||||
width: max-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* general form stuff */
|
|
||||||
input, button {
|
|
||||||
padding: 0.2em 0.5em;
|
|
||||||
}
|
|
||||||
button, input[type="submit"] {
|
|
||||||
cursor: pointer;
|
|
||||||
width: max-content;
|
|
||||||
}
|
|
||||||
.content-container button {
|
|
||||||
border: none;
|
|
||||||
background-color: #247BA0;
|
|
||||||
color: white;
|
|
||||||
padding: 0.3em 0.8em;
|
|
||||||
font-size: 0.9em;
|
|
||||||
border-radius: 0.3em;
|
|
||||||
}
|
|
||||||
button.secondary {
|
|
||||||
background-color: #EEE;
|
|
||||||
color: #247BA0;
|
|
||||||
}
|
|
||||||
.post button.secondary {
|
|
||||||
border: 2px solid #247BA0;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.warning {
|
|
||||||
background-color: #FF1654;
|
|
||||||
}
|
|
||||||
|
|
||||||
form input {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
form div {
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
textarea {
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* icons */
|
|
||||||
a .icon {
|
|
||||||
color: black;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
button .icon {
|
|
||||||
font-size: 1.1rem;
|
|
||||||
vertical-align: sub;
|
|
||||||
}
|
|
||||||
.hidden-text {
|
|
||||||
height: 0;
|
|
||||||
width: 0;
|
|
||||||
position: absolute;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* star ratings */
|
|
||||||
.stars {
|
|
||||||
letter-spacing: -0.15em;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
.rate-stars .icon {
|
|
||||||
cursor: pointer;
|
|
||||||
color: goldenrod;
|
|
||||||
}
|
|
||||||
.rate-stars label.icon {
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
.rate-stars form {
|
|
||||||
display: inline;
|
|
||||||
width: min-content;
|
|
||||||
}
|
|
||||||
.rate-stars button.icon {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
.cover-container .stars {
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.rate-stars:hover .icon:before {
|
|
||||||
content: '\e9d9';
|
|
||||||
}
|
|
||||||
.rate-stars form:hover ~ form .icon:before{
|
|
||||||
content: '\e9d7';
|
|
||||||
}
|
|
||||||
|
|
||||||
.review-form .rate-stars:hover .icon:before {
|
|
||||||
content: '\e9d9';
|
|
||||||
}
|
|
||||||
.review-form .rate-stars label {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
.review-form .rate-stars input + .icon:before {
|
|
||||||
content: '\e9d9';
|
|
||||||
}
|
|
||||||
.review-form .rate-stars input:checked + .icon:before {
|
|
||||||
content: '\e9d9';
|
|
||||||
}
|
|
||||||
.review-form .rate-stars input:checked + * ~ .icon:before {
|
|
||||||
content: '\e9d7';
|
|
||||||
}
|
|
||||||
.review-form .rate-stars:hover label.icon:before {
|
|
||||||
content: '\e9d9';
|
|
||||||
}
|
|
||||||
.review-form .rate-stars label.icon:hover:before {
|
|
||||||
content: '\e9d9';
|
|
||||||
}
|
|
||||||
.review-form .rate-stars label.icon:hover ~ label.icon:before{
|
|
||||||
content: '\e9d7';
|
|
||||||
}
|
|
||||||
.review-form .rate-stars input[type="radio"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* re-usable tab styles */
|
|
||||||
.tabs {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
border-bottom: 3px solid #FF1654;
|
|
||||||
padding-left: 1em;
|
|
||||||
}
|
|
||||||
.tabs.secondary {
|
|
||||||
border-bottom: 3px solid #247BA0;
|
|
||||||
}
|
|
||||||
.tab {
|
|
||||||
padding: 0.5em 1em;
|
|
||||||
border-radius: 0.25em 0.25em 0 0;
|
|
||||||
}
|
|
||||||
.secondary .tab {
|
|
||||||
padding: 0.25em 0.5em;
|
|
||||||
}
|
|
||||||
.tabs .tab.active {
|
|
||||||
background-color: #FF1654;
|
|
||||||
}
|
|
||||||
.tabs.secondary .tab.active {
|
|
||||||
background-color: #247BA0;
|
|
||||||
}
|
|
||||||
.tab.active a {
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.user-pic {
|
|
||||||
width: 2em;
|
|
||||||
height: 2em;
|
|
||||||
border-radius: 50%;
|
|
||||||
vertical-align: top;
|
|
||||||
position: relative;
|
|
||||||
bottom: 0.35em;
|
|
||||||
}
|
|
||||||
.user-pic.large {
|
|
||||||
width: 5em;
|
|
||||||
height: 5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.user-profile .row > * {
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
.user-profile .row > *:last-child {
|
|
||||||
flex-grow: 1;
|
|
||||||
margin-left: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* general book display */
|
|
||||||
.book-preview {
|
|
||||||
overflow: hidden;
|
|
||||||
z-index: 1;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.book-preview.grid {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cover-container {
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
.cover-container button {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
.book-cover {
|
|
||||||
width: 180px;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
.book-cover.small {
|
|
||||||
width: 50px;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-cover {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.no-cover div {
|
|
||||||
position: absolute;
|
|
||||||
padding: 1em;
|
|
||||||
color: white;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.no-cover .title {
|
|
||||||
text-transform: uppercase;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dl {
|
|
||||||
font-size: 0.9em;
|
|
||||||
margin-top: 0.5em;
|
|
||||||
}
|
|
||||||
dt {
|
|
||||||
float: left;
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
dd {
|
|
||||||
margin-bottom: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.all-shelves {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
margin-left: 0;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
.all-shelves h2 {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.all-shelves > div {
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
.all-shelves > div:last-child {
|
|
||||||
padding-right: 0;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
.all-shelves > div > * {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.all-shelves > div:first-child > * {
|
|
||||||
padding-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.covers-shelf {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
.covers-shelf .cover-container {
|
|
||||||
margin-right: 1em;
|
|
||||||
font-size: 0.9em;
|
|
||||||
overflow: unset;
|
|
||||||
width: min-content;
|
|
||||||
}
|
|
||||||
.covers-shelf .cover-container:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
.covers-shelf .book-cover:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
box-shadow: #F3FFBD 0em 0em 1em 1em;
|
|
||||||
}
|
|
||||||
.covers-shelf .book-cover {
|
|
||||||
height: 11rem;
|
|
||||||
width: auto;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close {
|
|
||||||
float: right;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.all-shelves input[type='radio'] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.compose-popout input[type="radio"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.compose-suggestion {
|
|
||||||
display: none;
|
|
||||||
box-shadow: 0 5px 10px rgba(0,0,0,0.15);
|
|
||||||
padding-bottom: 1em;
|
|
||||||
margin-top: 2em;
|
|
||||||
}
|
|
||||||
input:checked ~ .compose-suggestion {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.compose .book-preview {
|
|
||||||
background-color: #EEE;
|
|
||||||
padding: 1em;
|
|
||||||
}
|
|
||||||
.compose button {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.compose .stars {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0.2em;
|
|
||||||
border-radius: 0.2em;
|
|
||||||
background-color: #EEE;
|
|
||||||
}
|
|
||||||
.tag form {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
.tag a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
white-space: pre-line;
|
|
||||||
}
|
|
||||||
blockquote .icon-quote-open, blockquote .icon-quote-close, .quote blockquote:before, .quote blockquote:after {
|
|
||||||
font-size: 2rem;
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
blockquote .icon-quote-open {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote {
|
|
||||||
margin-bottom: 2em;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.quote blockquote {
|
|
||||||
background-color: white;
|
|
||||||
margin: 1em;
|
|
||||||
padding: 1em;
|
|
||||||
}
|
|
||||||
.quote blockquote:before, .quote blockquote:after {
|
|
||||||
font-family: 'icomoon';
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
.quote blockquote:before {
|
|
||||||
content: "\e904";
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
.quote blockquote:after {
|
|
||||||
content: "\e903";
|
|
||||||
bottom: 1em;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction {
|
|
||||||
background-color: #B2DBBF;
|
|
||||||
border-radius: 0 0 0.5em 0.5em;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
.interaction > * {
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
.interaction button:hover {
|
|
||||||
box-shadow: #247BA0 0em 0em 1em 0em;
|
|
||||||
color: #247BA0;
|
|
||||||
}
|
|
||||||
.interaction button {
|
|
||||||
background: white;
|
|
||||||
height: 2em;
|
|
||||||
min-width: 3em;
|
|
||||||
padding: 0;
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
.interaction .active button .icon {
|
|
||||||
color: #FF1654;
|
|
||||||
}
|
|
||||||
.interaction textarea {
|
|
||||||
height: 2em;
|
|
||||||
width: 23em;
|
|
||||||
float: left;
|
|
||||||
padding: 0.25em;
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
.interaction textarea:valid, .interaction textarea:focus {
|
|
||||||
height: 4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin: 1em;
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:nth-child(even) {
|
|
||||||
background-color: #EEE;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
th, td {
|
|
||||||
padding: 1em;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.errorlist {
|
|
||||||
list-style: none;
|
|
||||||
font-size: 0.8em;
|
|
||||||
color: #FF1654;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* status css */
|
|
||||||
.time-ago {
|
|
||||||
float: right;
|
|
||||||
display: block;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
.post {
|
|
||||||
background-color: #EFEFEF;
|
|
||||||
padding-top: 1em;
|
|
||||||
padding-bottom: 1em;
|
|
||||||
}
|
|
||||||
.post h2, .compose-suggestion h2 {
|
|
||||||
position: relative;
|
|
||||||
right: 2em;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
.post .time-ago {
|
|
||||||
position: relative;
|
|
||||||
left: 2em;
|
|
||||||
}
|
|
||||||
.post .user-pic, .compose-suggestion .user-pic {
|
|
||||||
right: 0.25em;
|
|
||||||
}
|
|
||||||
.post h2 .subhead {
|
|
||||||
display: block;
|
|
||||||
margin-left: 2em;
|
|
||||||
}
|
|
||||||
.post .subhead .time-ago {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* status page with replies */
|
|
||||||
.comment-thread .reply h2 {
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
.comment-thread .post {
|
|
||||||
margin-left: 4em;
|
|
||||||
border-left: 2px solid #247BA0;
|
|
||||||
}
|
|
||||||
.comment-thread .post.depth-1 {
|
|
||||||
margin-left: 0;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
.comment-thread .post.depth-2 {
|
|
||||||
margin-left: 1em;
|
|
||||||
}
|
|
||||||
.comment-thread .post.depth-3 {
|
|
||||||
margin-left: 2em;
|
|
||||||
}
|
|
||||||
.comment-thread .post.depth-4 {
|
|
||||||
margin-left: 3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* pagination */
|
|
||||||
.pagination a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.pagination .next {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* special one-off "delete all data" banner */
|
|
||||||
#warning {
|
|
||||||
background-color: #FF1654;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
|
@ -32,29 +32,19 @@ function rate_stars(e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function tabChange(e) {
|
function tabChange(e) {
|
||||||
e.preventDefault();
|
var target = e.target.closest('li')
|
||||||
var target = e.target.parentElement;
|
|
||||||
var identifier = target.getAttribute('data-id');
|
var identifier = target.getAttribute('data-id');
|
||||||
|
|
||||||
var options_class = target.getAttribute('data-category');
|
|
||||||
var options = document.getElementsByClassName(options_class);
|
|
||||||
for (var i = 0; i < options.length; i++) {
|
|
||||||
if (!options[i].className.includes('hidden')) {
|
|
||||||
options[i].className += ' hidden';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var tabs = target.parentElement.children;
|
var tabs = target.parentElement.children;
|
||||||
for (i = 0; i < tabs.length; i++) {
|
for (i = 0; i < tabs.length; i++) {
|
||||||
if (tabs[i].getAttribute('data-id') == identifier) {
|
if (tabs[i].getAttribute('data-id') == identifier) {
|
||||||
tabs[i].className += ' active';
|
tabs[i].className += ' is-active';
|
||||||
} else {
|
} else {
|
||||||
tabs[i].className = tabs[i].className.replace('active', '');
|
tabs[i].className = tabs[i].className.replace('is-active', '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var el = document.getElementById(identifier);
|
var el = document.getElementById(identifier);
|
||||||
el.className = el.className.replace('hidden', '');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function ajaxPost(form) {
|
function ajaxPost(form) {
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="content-container">
|
|
||||||
<h2>About {{ site_settings.name }}</h2>
|
<div class="columns">
|
||||||
|
<div class="column block">
|
||||||
|
<h2 class="title">About {{ site_settings.name }}</h2>
|
||||||
<p>
|
<p>
|
||||||
{{ site_settings.instance_description }}
|
{{ site_settings.instance_description }}
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p>
|
<div class="column block">
|
||||||
<small>
|
<h2 class="title">Code of Conduct</h2>
|
||||||
<a href="/login/">Login or Create an Account</a>
|
|
||||||
</small>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Code of Conduct</h2>
|
|
||||||
<p>
|
<p>
|
||||||
{{ site_settings.code_of_conduct }}
|
{{ site_settings.code_of_conduct }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,28 +1,19 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
<h2>{{ author.display_name }}</h2>
|
<h2 class="title">{{ author.display_name }}</h2>
|
||||||
|
|
||||||
{% if author.bio %}
|
{% if author.bio %}
|
||||||
<p>
|
<p>
|
||||||
{{ author.bio | author_bio }}
|
{{ author.bio }}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
<h2>Books by {{ author.display_name }}</h2>
|
<h3 class="title is-4">Books by {{ author.display_name }}</h3>
|
||||||
<div class="book-grid row shrink wrap">
|
{% include 'snippets/book_tiles.html' with books=books %}
|
||||||
{% for book in books %}
|
|
||||||
<div class="book-preview">
|
|
||||||
<a href="/book/{{ book.id }}">
|
|
||||||
{% include 'snippets/book_cover.html' with book=book %}
|
|
||||||
</a>
|
|
||||||
{% include 'snippets/shelve_button.html' with book=book %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,27 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
|
{% load humanize %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="content-container">
|
|
||||||
<h2>
|
|
||||||
{% include 'snippets/book_titleby.html' with book=book %}
|
|
||||||
|
|
||||||
{% if request.user.is_authenticated %}
|
<div class="block">
|
||||||
<a href="{{ book.id }}/edit" class="edit-link">edit
|
<div class="level">
|
||||||
<span class="icon icon-pencil">
|
<h2 class="title level-left">
|
||||||
<span class="hidden-text">Edit Book</span>
|
<span>{% include 'snippets/book_titleby.html' with book=book %}</span>
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div class="row">
|
{% if request.user.is_authenticated %}
|
||||||
|
<div class="level-right">
|
||||||
|
<a href="{{ book.id }}/edit">edit
|
||||||
|
<span class="icon icon-pencil">
|
||||||
|
<span class="is-sr-only">Edit Book</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="cover-container">
|
<div class="columns">
|
||||||
|
<div class="column is-narrow">
|
||||||
{% include 'snippets/book_cover.html' with book=book size=large %}
|
{% include 'snippets/book_cover.html' with book=book size=large %}
|
||||||
{% include 'snippets/rate_action.html' with user=request.user book=book %}
|
{% include 'snippets/rate_action.html' with user=request.user book=book %}
|
||||||
{% include 'snippets/shelve_button.html' %}
|
{% include 'snippets/shelve_button.html' %}
|
||||||
|
@ -25,11 +30,11 @@
|
||||||
<form name="add-cover" method="POST" action="/upload_cover/{{ book.id }}" enctype="multipart/form-data">
|
<form name="add-cover" method="POST" action="/upload_cover/{{ book.id }}" enctype="multipart/form-data">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ cover_form.as_p }}
|
{{ cover_form.as_p }}
|
||||||
<button type="submit">Add cover</button>
|
<button class="button" type="submit">Add cover</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<dl>
|
<dl class="content">
|
||||||
{% for field in info_fields %}
|
{% for field in info_fields %}
|
||||||
{% if field.value %}
|
{% if field.value %}
|
||||||
<dt>{{ field.name }}:</dt>
|
<dt>{{ field.name }}:</dt>
|
||||||
|
@ -40,56 +45,81 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<h3>{{ active_tab }} rating: {% include 'snippets/stars.html' with rating=rating %}</h3>
|
<div class="block">
|
||||||
|
<h3 class="field is-grouped">{% include 'snippets/stars.html' with rating=rating %} ({{ reviews|length }} review{{ reviews|length|pluralize }})</h3>
|
||||||
|
|
||||||
{% include 'snippets/book_description.html' %}
|
{% include 'snippets/book_description.html' %}
|
||||||
|
|
||||||
{% if book.parent_work.edition_set.count > 1 %}
|
{% if book.parent_work.edition_set.count > 1 %}
|
||||||
<p><a href="/editions/{{ book.parent_work.id }}">{{ book.parent_work.edition_set.count }} editions</a></p>
|
<p><a href="/editions/{{ book.parent_work.id }}">{{ book.parent_work.edition_set.count }} editions</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
<div class="compose">
|
<div class="block">
|
||||||
{% include 'snippets/create_status.html' with book=book hide_cover=True %}
|
{% include 'snippets/create_status.html' with book=book hide_cover=True %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="block">
|
||||||
<h3>Tags</h3>
|
<h3>Tags</h3>
|
||||||
<form name="tag" action="/tag/" method="post">
|
<form name="tag" action="/tag/" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="book" value="{{ book.id }}">
|
<input type="hidden" name="book" value="{{ book.id }}">
|
||||||
<input type="text" name="name">
|
<input class="input" type="text" name="name">
|
||||||
<button type="submit">Add tag</button>
|
<button class="button" type="submit">Add tag</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="tag-cloud">
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
|
<div class="field is-grouped is-grouped-multiline">
|
||||||
{% for tag in tags %}
|
{% for tag in tags %}
|
||||||
{% include 'snippets/tag.html' with book=book tag=tag user_tags=user_tags %}
|
{% include 'snippets/tag.html' with book=book tag=tag user_tags=user_tags %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% if request.user.is_authenticated %}
|
|
||||||
<div class="content-container tabs">
|
|
||||||
{% include 'snippets/tabs.html' with tabs=feed_tabs active_tab=active_tab path=path %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if not reviews %}
|
{% if not reviews %}
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
<p>No reviews yet!</p>
|
<p>No reviews yet!</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
{% for review in reviews %}
|
{% for review in reviews %}
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
{% include 'snippets/status.html' with status=review hide_book=True depth=1 %}
|
{% include 'snippets/status.html' with status=review hide_book=True depth=1 %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
<div class="block columns">
|
||||||
|
{% for rating in ratings %}
|
||||||
|
<div class="column">
|
||||||
|
<div class="media">
|
||||||
|
<div class="media-left">{% include 'snippets/avatar.html' %}</div>
|
||||||
|
<div class="media-content">
|
||||||
|
<div>
|
||||||
|
{% include 'snippets/username.html' %}
|
||||||
|
</div>
|
||||||
|
<div class="field is-grouped mb-0">
|
||||||
|
<div>rated it</div>
|
||||||
|
{% include 'snippets/stars.html' with rating=rating.rating %}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a href="{{ rating.remote_id }}">{{ rating.published_date | naturaltime }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
{% extends 'layout.html' %}
|
|
||||||
{% load fr_display %}
|
|
||||||
{% block content %}
|
|
||||||
<div class="content-container">
|
|
||||||
<h2>Recently Added Books</h2>
|
|
||||||
<div class="book-grid row wrap shrink">
|
|
||||||
{% for book in books %}
|
|
||||||
<div class="cover-container">
|
|
||||||
<a href="/book/{{ book.id }}">
|
|
||||||
{% include 'snippets/book_cover.html' with book=book %}
|
|
||||||
</a>
|
|
||||||
{% include 'snippets/shelve_button.html' with book=book %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
|
@ -1,71 +1,83 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
<h2>
|
<div class="level">
|
||||||
|
<h2 class="title level-left">
|
||||||
Edit "{{ book.title }}"
|
Edit "{{ book.title }}"
|
||||||
|
</h2>
|
||||||
|
<div class="level-right">
|
||||||
<a href="/book/{{ book.id }}">
|
<a href="/book/{{ book.id }}">
|
||||||
<span class="edit-link icon icon-close">
|
<span class="edit-link icon icon-close">
|
||||||
<span class="hidden-text">Close</span>
|
<span class="is-sr-only">Close</span>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</h2>
|
</div>
|
||||||
<div class="book-preview row">
|
</div>
|
||||||
<div class="cover-container">
|
<div class="columns">
|
||||||
|
<div class="column is-narrow">
|
||||||
{% include 'snippets/book_cover.html' with book=book size="small" %}
|
{% include 'snippets/book_cover.html' with book=book size="small" %}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="column is-narrow">
|
||||||
<p>Added: {{ book.created_date | naturaltime }}</p>
|
<p>Added: {{ book.created_date | naturaltime }}</p>
|
||||||
<p>Updated: {{ book.updated_date | naturaltime }}</p>
|
<p>Updated: {{ book.updated_date | naturaltime }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form class="book-form content-container" name="edit-book" action="/edit_book/{{ book.id }}" method="post" enctype="multipart/form-data">
|
<form class="block" name="edit-book" action="/edit_book/{{ book.id }}" method="post" enctype="multipart/form-data">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<h3>Data sync
|
<div class="block">
|
||||||
<small>If sync is enabled, any changes will be over-written</small>
|
<h3 class="title is-4">Data sync</h3>
|
||||||
|
<h4 class="subtitle is-5">If sync is enabled, any changes will be over-written</h4>
|
||||||
</h3>
|
</h3>
|
||||||
<div>
|
<div class="columns">
|
||||||
<div class="row">
|
<div class="column is-narrow">
|
||||||
<p><label for="id_sync">Sync:</label> <input type="checkbox" name="sync" id="id_sync"></p>
|
<label class="checkbox" for="id_sync"><input class="checkbox" type="checkbox" name="sync" id="id_sync"> Sync</label>
|
||||||
<p><label for="id_sync_cover">Sync cover:</label> <input type="checkbox" name="sync_cover" id="id_sync_cover"></p>
|
</div>
|
||||||
|
<div class="column is-narrow">
|
||||||
|
<label class="checkbox" for="id_sync_cover"><input class="checkbox" type="checkbox" name="sync_cover" id="id_sync_cover"> Sync cover</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3>Cover</h3>
|
<div class="columns">
|
||||||
<div class="image-form">
|
<div class="block column">
|
||||||
|
<h3 class="title is-4">Book Identifiers</h3>
|
||||||
|
<p class="fields is-grouped"><label class="label"for="id_isbn_13">ISBN 13:</label> {{ form.isbn_13 }} </p>
|
||||||
|
<p class="fields is-grouped"><label class="label"for="id_isbn_10">ISBN 10:</label> {{ form.isbn_10 }} </p>
|
||||||
|
<p class="fields is-grouped"><label class="label"for="id_openlibrary_key">Openlibrary key:</label> {{ form.openlibrary_key }} </p>
|
||||||
|
<p class="fields is-grouped"><label class="label"for="id_librarything_key">Librarything key:</label> {{ form.librarything_key }} </p>
|
||||||
|
<p class="fields is-grouped"><label class="label"for="id_goodreads_key">Goodreads key:</label> {{ form.goodreads_key }} </p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column">
|
||||||
|
<div class="block">
|
||||||
|
<h3 class="title is-4">Cover</h3>
|
||||||
<p>{{ form.cover }} </p>
|
<p>{{ form.cover }} </p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3>Book Identifiers</h2>
|
<div class="block">
|
||||||
<div>
|
<h3 class="title is-4">Physical Properties</h3>
|
||||||
<p><label for="id_isbn_13">ISBN 13:</label> {{ form.isbn_13 }} </p>
|
<p class="fields is-grouped"><label class="label"for="id_physical_format">Format:</label> {{ form.physical_format }} </p>
|
||||||
<p><label for="id_isbn_10">ISBN 10:</label> {{ form.isbn_10 }} </p>
|
<p class="fields is-grouped"><label class="label"for="id_pages">Pages:</label> {{ form.pages }} </p>
|
||||||
<p><label for="id_openlibrary_key">Openlibrary key:</label> {{ form.openlibrary_key }} </p>
|
</div>
|
||||||
<p><label for="id_librarything_key">Librarything key:</label> {{ form.librarything_key }} </p>
|
</div>
|
||||||
<p><label for="id_goodreads_key">Goodreads key:</label> {{ form.goodreads_key }} </p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3>Physical Properties</h3>
|
<div class="block">
|
||||||
<div>
|
<h3 class="title is-4">Metadata</h3>
|
||||||
<p><label for="id_physical_format">Format:</label> {{ form.physical_format }} </p>
|
<p class="fields is-grouped"><label class="label"for="id_title">Title:</label> {{ form.title }} </p>
|
||||||
<p><label for="id_pages">Pages:</label> {{ form.pages }} </p>
|
<p class="fields is-grouped"><label class="label"for="id_sort_title">Sort title:</label> {{ form.sort_title }} </p>
|
||||||
|
<p class="fields is-grouped"><label class="label"for="id_subtitle">Subtitle:</label> {{ form.subtitle }} </p>
|
||||||
|
<p class="fields is-grouped"><label class="label"for="id_description">Description:</label> {{ form.description }} </p>
|
||||||
|
<p class="fields is-grouped"><label class="label"for="id_series">Series:</label> {{ form.series }} </p>
|
||||||
|
<p class="fields is-grouped"><label class="label"for="id_series_number">Series number:</label> {{ form.series_number }} </p>
|
||||||
|
<p class="fields is-grouped"><label class="label"for="id_first_published_date">First published date:</label> {{ form.first_published_date }} </p>
|
||||||
|
<p class="fields is-grouped"><label class="label"for="id_published_date">Published date:</label> {{ form.published_date }} </p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="block">
|
||||||
<h3>Metadata</h3>
|
<button class="button is-primary" type="submit">Save</button>
|
||||||
<div>
|
|
||||||
<p><label for="id_title">Title:</label> {{ form.title }} </p>
|
|
||||||
<p><label for="id_sort_title">Sort title:</label> {{ form.sort_title }} </p>
|
|
||||||
<p><label for="id_subtitle">Subtitle:</label> {{ form.subtitle }} </p>
|
|
||||||
<p><label for="id_description">Description:</label> {{ form.description }} </p>
|
|
||||||
<p><label for="id_series">Series:</label> {{ form.series }} </p>
|
|
||||||
<p><label for="id_series_number">Series number:</label> {{ form.series_number }} </p>
|
|
||||||
<p><label for="id_first_published_date">First published date:</label> {{ form.first_published_date }} </p>
|
|
||||||
<p><label for="id_published_date">Published date:</label> {{ form.published_date }} </p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button type="submit">Save</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
<div class="user-profile">
|
<h2 class="title">Edit Profile</h2>
|
||||||
<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 }}
|
||||||
<button type="submit">Update profile</button>
|
<button class="button is-primary" type="submit">Update profile</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,18 +1,10 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
<h2>Editions of <a href="/book/{{ work.id }}">"{{ work.title }}"</a></h2>
|
<h2 class="title">Editions of <a href="/book/{{ work.id }}">"{{ work.title }}"</a></h2>
|
||||||
<ol class="book-grid row wrap">
|
|
||||||
{% for book in editions %}
|
{% include 'snippets/book_tiles.html' with books=editions %}
|
||||||
<li class="book-preview">
|
|
||||||
<a href="/book/{{ book.id }}">
|
|
||||||
{% include 'snippets/book_cover.html' with book=book %}
|
|
||||||
</a>
|
|
||||||
{% include 'snippets/shelve_button.html' with book=book %}
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ol>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
<h2>Server Error</h2>
|
<h2 class="title">Server Error</h2>
|
||||||
<p>Something went wrong! Sorry about that.</p>
|
<p>Something went wrong! Sorry about that.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -2,23 +2,42 @@
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% include 'snippets/covers_shelf.html' with shelves=shelves user=request.user %}
|
<div class="columns">
|
||||||
|
<div class="column is-one-third">
|
||||||
|
<h2 class="title is-4">Suggested books</h2>
|
||||||
<div id="feed">
|
<div class="tabs is-small is-toggle">
|
||||||
<div class="content-container tabs">
|
<ul>
|
||||||
{% include 'snippets/tabs.html' with tabs=feed_tabs active_tab=active_tab %}
|
{% for book in suggested_books %}
|
||||||
|
<li class="{% if forloop.first %}is-active{% endif %}" data-id="tab-book-{{ book.id }}">
|
||||||
|
<label for="book-{{ book.id }}" onclick="tabChange(event)"><a>{% include 'snippets/book_cover.html' with book=book size="medium" %}</a></label>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% for book in suggested_books %}
|
||||||
|
<div>
|
||||||
|
<input class="toggle-control" type="radio" name="recent-books" id="book-{{ book.id }}" {% if forloop.first %}checked{% endif %}>
|
||||||
|
<div class="toggle-content hidden">
|
||||||
|
<div class="block">
|
||||||
|
{% include 'snippets/book_titleby.html' with book=book %}
|
||||||
|
{% include 'snippets/shelve_button.html' with book=book %}
|
||||||
|
</div>
|
||||||
|
{% include 'snippets/create_status.html' with book=book %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="column is-two-thirds" id="feed">
|
||||||
{% for activity in activities %}
|
{% for activity in activities %}
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
{% include 'snippets/status.html' with status=activity %}
|
{% include 'snippets/status.html' with status=activity %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<div class="content-container pagination row">
|
<nav class="pagination" role="navigation" aria-label="pagination">
|
||||||
{% if prev %}
|
{% if prev %}
|
||||||
<p>
|
<p class="pagination-previous">
|
||||||
<a href="{{ prev }}">
|
<a href="{{ prev }}">
|
||||||
<span class="icon icon-arrow-left"></span>
|
<span class="icon icon-arrow-left"></span>
|
||||||
Previous
|
Previous
|
||||||
|
@ -27,14 +46,14 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if next %}
|
{% if next %}
|
||||||
<p class="next">
|
<p class="pagination-next">
|
||||||
<a href="{{ next }}">
|
<a href="{{ next }}">
|
||||||
Next
|
Next
|
||||||
<span class="icon icon-arrow-right"></span>
|
<span class="icon icon-arrow-right"></span>
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -3,20 +3,22 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% include 'user_header.html' with user=user %}
|
{% include 'user_header.html' with user=user %}
|
||||||
|
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
<h2>Followers</h2>
|
<h2 class="title">Followers</h2>
|
||||||
{% for followers in followers %}
|
{% for followers in followers %}
|
||||||
<div class="row shrink">
|
<div class="block">
|
||||||
<div>
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
{% include 'snippets/avatar.html' with user=followers %}
|
{% include 'snippets/avatar.html' with user=followers %}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="control">
|
||||||
{% include 'snippets/username.html' with user=followers show_full=True %}
|
{% include 'snippets/username.html' with user=followers show_full=True %}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="control">
|
||||||
{% include 'snippets/follow_button.html' with user=followers %}
|
{% include 'snippets/follow_button.html' with user=followers %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if not followers.count %}
|
{% if not followers.count %}
|
||||||
<div>{{ user|username }} has no followers</div>
|
<div>{{ user|username }} has no followers</div>
|
||||||
|
|
|
@ -3,20 +3,22 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% include 'user_header.html' %}
|
{% include 'user_header.html' %}
|
||||||
|
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
<h2>Following</h2>
|
<h2 class="title">Following</h2>
|
||||||
{% for follower in user.following.all %}
|
{% for follower in user.following.all %}
|
||||||
<div class="row shrink">
|
<div class="block">
|
||||||
<div>
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
{% include 'snippets/avatar.html' with user=follower %}
|
{% include 'snippets/avatar.html' with user=follower %}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="control">
|
||||||
{% include 'snippets/username.html' with user=follower show_full=True %}
|
{% include 'snippets/username.html' with user=follower show_full=True %}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="control">
|
||||||
{% include 'snippets/follow_button.html' with user=follower %}
|
{% include 'snippets/follow_button.html' with user=follower %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if not following.count %}
|
{% if not following.count %}
|
||||||
<div>No one is following {{ user|username }}</div>
|
<div>No one is following {{ user|username }}</div>
|
||||||
|
|
|
@ -1,17 +1,22 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
<h2>Import Books from GoodReads</h2>
|
<h2 class="title">Import Books from GoodReads</h2>
|
||||||
<form name="import" action="/import_data/" method="post" enctype="multipart/form-data">
|
<form name="import" action="/import_data/" method="post" enctype="multipart/form-data">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ import_form.as_p }}
|
{{ import_form.as_p }}
|
||||||
<button type="submit">Import</button>
|
<button class="button" type="submit">Import</button>
|
||||||
</form>
|
</form>
|
||||||
<p>
|
<p>
|
||||||
Imports are limited in size, and only the first {{ limit }} items will be imported.
|
Imports are limited in size, and only the first {{ limit }} items will be imported.
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>Recent Imports</h2>
|
<div class="content block">
|
||||||
|
<h2 class="title">Recent Imports</h2>
|
||||||
|
{% if not jobs %}
|
||||||
|
<p>No recent imports</p>
|
||||||
|
{% endif %}
|
||||||
<ul>
|
<ul>
|
||||||
{% for job in jobs %}
|
{% for job in jobs %}
|
||||||
<li><a href="/import_status/{{ job.id }}">{{ job.created_date | naturaltime }}</a></li>
|
<li><a href="/import_status/{{ job.id }}">{{ job.created_date | naturaltime }}</a></li>
|
||||||
|
|
|
@ -2,9 +2,8 @@
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="content">
|
<div class="block">
|
||||||
<div>
|
<h1 class="title">Import Status</h1>
|
||||||
<h1>Import Status</h1>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Import started: {{ job.created_date | naturaltime }}
|
Import started: {{ job.created_date | naturaltime }}
|
||||||
|
@ -16,7 +15,9 @@
|
||||||
<p>
|
<p>
|
||||||
{{ task.info }}
|
{{ task.info }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
{% if job.import_status %}
|
{% if job.import_status %}
|
||||||
{% include 'snippets/status.html' with status=job.import_status %}
|
{% include 'snippets/status.html' with status=job.import_status %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -25,8 +26,10 @@
|
||||||
<p>
|
<p>
|
||||||
(Hit reload to update!)
|
(Hit reload to update!)
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<table>
|
<div class="block">
|
||||||
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
Book
|
Book
|
||||||
|
|
|
@ -4,8 +4,9 @@
|
||||||
<head>
|
<head>
|
||||||
<title>BookWyrm</title>
|
<title>BookWyrm</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link type="text/css" rel="stylesheet" href="/static/format.css">
|
<link type="text/css" rel="stylesheet" href="/static/css/bulma.min.css">
|
||||||
<link type="text/css" rel="stylesheet" href="/static/icons.css">
|
<link type="text/css" rel="stylesheet" href="/static/css/format.css">
|
||||||
|
<link type="text/css" rel="stylesheet" href="/static/css/icons.css">
|
||||||
|
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="/static/images/favicon.ico">
|
<link rel="shortcut icon" type="image/x-icon" href="/static/images/favicon.ico">
|
||||||
|
|
||||||
|
@ -16,63 +17,103 @@
|
||||||
<meta name="og:description" content="Federated Social Reading">
|
<meta name="og:description" content="Federated Social Reading">
|
||||||
<meta name="twitter:creator" content="@tripofmice">
|
<meta name="twitter:creator" content="@tripofmice">
|
||||||
<meta name="twitter:site" content="@tripofmice">
|
<meta name="twitter:site" content="@tripofmice">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div id="top-bar">
|
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||||
<header class="row">
|
<div class="navbar-brand">
|
||||||
<div id="branding">
|
<a class="navbar-item" href="/">
|
||||||
<a href="/">
|
<img src="/static/images/logo-small.png" alt="BookWyrm" width="112" height="28">
|
||||||
<img id="logo" src="/static/images/logo-small.png" alt="BookWyrm"></img>
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
<form class="navbar-item" action="/search/">
|
||||||
|
<div class="field is-grouped">
|
||||||
<ul id="menu">
|
<input class="input" type="text" name="q" placeholder="Search for a book or user">
|
||||||
{% if request.user.is_authenticated %}
|
<button class="button" type="submit">
|
||||||
<li><a href="/user/{{request.user.localname}}/shelves">Your shelves</a></li>
|
|
||||||
{% endif %}
|
|
||||||
<li><a href="/#feed">Updates</a></li>
|
|
||||||
<li><a href="/books">Discover Books</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div id="actions">
|
|
||||||
<div id="search">
|
|
||||||
<form action="/search/">
|
|
||||||
<input type="text" name="q" placeholder="Search for a book or user">
|
|
||||||
<button type="submit">
|
|
||||||
<span class="icon icon-search">
|
<span class="icon icon-search">
|
||||||
<span class="hidden-text">search</span>
|
<span class="is-sr-only">search</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
|
||||||
{% if request.user.is_authenticated %}
|
<label for="main-nav" role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="mainNav">
|
||||||
<div id="notifications">
|
<span aria-hidden="true"></span>
|
||||||
<a href="/notifications">
|
<span aria-hidden="true"></span>
|
||||||
<span class="icon icon-bell">
|
<span aria-hidden="true"></span>
|
||||||
<span class="hidden-text">Notitications</span>
|
</label>
|
||||||
</span>
|
|
||||||
{% if request.user|notification_count %}<span class="count">{{ request.user | notification_count }}</span>{% endif %}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="pulldown-container">
|
|
||||||
{% include 'snippets/avatar.html' with user=request.user %}
|
|
||||||
<ul class="pulldown">
|
|
||||||
<li><a href="/user/{{ request.user }}">Your profile</a></li>
|
|
||||||
<li><a href="/user-edit/">Settings</a></li>
|
|
||||||
<li><a href="/import">Import Books</a></li>
|
|
||||||
<li><a href="/manage_invites/">Invites</a></li>
|
|
||||||
<li><a href="/logout/">Log out</a></li>
|
|
||||||
</ul>
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="main">
|
<input class="toggle-control" type="checkbox" id="main-nav">
|
||||||
|
<div id="mainNav" class="navbar-menu toggle-content">
|
||||||
|
<div class="navbar-start">
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
<a href="" class="navbar-item">
|
||||||
|
Lists
|
||||||
|
</a>
|
||||||
|
<a href="" class="navbar-item">
|
||||||
|
Groups
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="navbar-end">
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable">
|
||||||
|
<div class="navbar-link"><p>
|
||||||
|
{% include 'snippets/avatar.html' with user=user %}
|
||||||
|
{% include 'snippets/username.html' with user=request.user %}
|
||||||
|
</p></div>
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<a href="/user/{{request.user.localname}}" class="navbar-item">
|
||||||
|
Profile
|
||||||
|
</a>
|
||||||
|
<a href="/user-edit" class="navbar-item">
|
||||||
|
Settings
|
||||||
|
</a>
|
||||||
|
<a href="/invite" class="navbar-item">
|
||||||
|
Invites
|
||||||
|
</a>
|
||||||
|
<a href="/import" class="navbar-item">
|
||||||
|
Import books
|
||||||
|
</a>
|
||||||
|
<hr class="navbar-divider">
|
||||||
|
<a href="/logout" class="navbar-item">
|
||||||
|
Log out
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item">
|
||||||
|
<a href="/notifications">
|
||||||
|
<div class="tags has-addons">
|
||||||
|
<span class="tag is-medium">
|
||||||
|
<span class="icon icon-bell">
|
||||||
|
<span class="is-sr-only">Notitications</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
{% if request.user|notification_count %}
|
||||||
|
<span class="tag is-danger is-medium">{{ request.user | notification_count }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="navbar-item">
|
||||||
|
<div class="buttons">
|
||||||
|
<a href="/register" class="button is-primary">
|
||||||
|
<strong>Sign up</strong>
|
||||||
|
</a>
|
||||||
|
<a href="/login" class="button is-light">
|
||||||
|
Log in
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,52 +1,45 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="content-container">
|
<div class="columns">
|
||||||
<h2>About {{ site_settings.name }}</h2>
|
<div class="column">
|
||||||
<p>
|
<h2 class="title">About {{ site_settings.name }}</h2>
|
||||||
|
<p class="block">
|
||||||
{{ site_settings.instance_description }}
|
{{ site_settings.instance_description }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p class="block">
|
||||||
<small>
|
|
||||||
<a href="/about/">More about this site</a>
|
<a href="/about/">More about this site</a>
|
||||||
</small>
|
</p>
|
||||||
|
|
||||||
|
<p class="block">
|
||||||
|
<a href="/register" class="button is-link">Create an Account</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="column">
|
||||||
<div class="content-container login">
|
<h2 class="title">Log in</h2>
|
||||||
<h2>Create an Account</h2>
|
|
||||||
<p><small>
|
|
||||||
With a BookWyrm account, you can track and share your reading activity with
|
|
||||||
friends here and on any other federated server, like Mastodon and PixelFed.
|
|
||||||
</small></p>
|
|
||||||
|
|
||||||
{% if site_settings.allow_registration %}
|
<div class="block">
|
||||||
<div>
|
|
||||||
<form name="register" method="post" action="/register">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{ register_form.as_p }}
|
|
||||||
<button type="submit">Create account</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
|
|
||||||
<small>
|
|
||||||
This instance is not open for registration.
|
|
||||||
</small>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content-container login">
|
|
||||||
<h2>Log in</h2>
|
|
||||||
<div>
|
|
||||||
<form name="login" method="post" action="/user-login">
|
<form name="login" method="post" action="/user-login">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ login_form.as_p }}
|
<div class="field">
|
||||||
<button type="submit">Log in</button>
|
<label class="label" for="id_username">Username:</label>
|
||||||
|
<div class="control">{{ login_form.username }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="id_password">Password:</label>
|
||||||
|
<div class="control">{{ login_form.password }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-primary" type="submit">Log in</button>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<small><a href="/reset-password">Forgot your password?</a></small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<p><small><a href="/reset-password">Forgot your password?</a></small></p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
<div class="manage-invites">
|
<h2 class="title">Invites</h2>
|
||||||
<h2>Invites</h2>
|
<table class="table is-striped">
|
||||||
<table>
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>Link</th>
|
<th>Link</th>
|
||||||
<th>Expires</th>
|
<th>Expires</th>
|
||||||
<th>Max uses</th>
|
<th>Max uses</th>
|
||||||
<th>Times used</th>
|
<th>Times used</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% if not invites %}
|
||||||
|
<tr><td colspan="4">No active invites</td></tr>
|
||||||
|
{% endif %}
|
||||||
{% for invite in invites %}
|
{% for invite in invites %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{ invite.link }}">{{ invite.link }}</td>
|
<td><a href="{{ invite.link }}">{{ invite.link }}</td>
|
||||||
|
@ -20,13 +22,32 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
<h2>Generate New Invite</h2>
|
</div>
|
||||||
|
|
||||||
<form name="avatar" action="/create_invite/" method="post">
|
<div class="block">
|
||||||
|
<h2 class="title is-4">Generate New Invite</h2>
|
||||||
|
|
||||||
|
<form name="invite" action="/create_invite/" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
<div class="field">
|
||||||
<button type="submit">Create Invite</button>
|
<div class="control">
|
||||||
|
<label class="label" for="id_expiry">Expiry:</label>
|
||||||
|
</div>
|
||||||
|
<div class="select">
|
||||||
|
{{ form.expiry }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<label class="label" for="id_use_limit">Use limit:</label>
|
||||||
|
</div>
|
||||||
|
<div class="select">
|
||||||
|
{{ form.use_limit }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="button is-primary" type="submit">Create Invite</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
<h2>Not Found</h2>
|
<h2 class="title">Not Found</h2>
|
||||||
<p>The page your requested doesn't seem to exist!</p>
|
<p>The page your requested doesn't seem to exist!</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% load humanize %}l
|
{% load humanize %}l
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
<h2>Notifications</h2>
|
<h2 class="title">Notifications</h2>
|
||||||
|
|
||||||
<form name="clear" action="/clear-notifications" method="POST">
|
<form name="clear" action="/clear-notifications" method="POST">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button type="submit" class="secondary">Delete notifications</button>
|
<button class="button is-danger" type="submit" class="secondary">Delete notifications</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
{% for notification in notifications %}
|
{% for notification in notifications %}
|
||||||
<div class="notification{% if notification.id in unread %} unread{% endif %}">
|
<div class="notification level{% if notification.id in unread %} is-primary{% endif %}">
|
||||||
<small class="time-ago">{{ notification.created_date | naturaltime }}</small>
|
<div class="level-left">
|
||||||
|
<p>
|
||||||
{% if notification.related_user %}
|
{% if notification.related_user %}
|
||||||
|
{% include 'snippets/avatar.html' with user=notification.related_user %}
|
||||||
{% include 'snippets/username.html' with user=notification.related_user %}
|
{% include 'snippets/username.html' with user=notification.related_user %}
|
||||||
{% if notification.notification_type == 'FAVORITE' %}
|
{% if notification.notification_type == 'FAVORITE' %}
|
||||||
favorited your
|
favorited your
|
||||||
|
@ -41,6 +43,10 @@
|
||||||
your <a href="/import_status/{{ notification.related_import.id }}">import</a> completed.
|
your <a href="/import_status/{{ notification.related_import.id }}">import</a> completed.
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="level-right">{{ notification.created_date | naturaltime }}</p>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if not notifications %}
|
{% if not notifications %}
|
||||||
|
|
49
bookwyrm/templates/register.html
Normal file
49
bookwyrm/templates/register.html
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
{% extends 'layout.html' %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<h2 class="title">About {{ site_settings.name }}</h2>
|
||||||
|
<p class="block">
|
||||||
|
{{ site_settings.instance_description }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="block">
|
||||||
|
<a href="/about/">More about this site</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="block">
|
||||||
|
<a href="/login" class="button is-link">Log In</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column">
|
||||||
|
<h2 class="title">Create an Account</h2>
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
|
<form name="register" method="post" action="/user-register">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="id_username">Username:</label>
|
||||||
|
<div class="control">{{ register_form.username }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="id_email">Email address:</label>
|
||||||
|
<div class="control">{{ register_form.email }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="id_password">Password:</label>
|
||||||
|
<div class="control">{{ register_form.password }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-primary" type="submit">Sign Up</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div id="content">
|
<div class="block">
|
||||||
|
<nav class="breadcrumb has-succeeds-separator" aria-label="breadcrumbs">
|
||||||
|
<ul>
|
||||||
|
<li><a href="/user/{{ user.username }}">{% include 'snippets/username.html' with user=user %}</a></li>
|
||||||
|
<li><a href="/user/{{ user.username }}/shelves">Shelves</a></li>
|
||||||
|
<li class="is-active"><a href="#" aria-current="page">{{ shelf.name }}</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
<div>
|
<div>
|
||||||
<h2>{% include 'snippets/username.html' with user=user %} > {{ shelf.name }}</h2>
|
<h2 class="title">{{ shelf.name }}</h2>
|
||||||
{% include 'snippets/shelf.html' with shelf=shelf ratings=ratings %}
|
{% include 'snippets/shelf.html' with shelf=shelf ratings=ratings %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
<img class="user-pic{% if large %} large{% endif %}" src="{% if user.avatar %}/images/{{ user.avatar }}{% else %}/static/images/default_avi.jpg{% endif %}">
|
<img class="avatar image {% if large %}is-96x96{% else %}is-32x32{% endif %}" src="{% if user.avatar %}/images/{{ user.avatar }}{% else %}/static/images/default_avi.jpg{% endif %}">
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
|
<div class="cover-container is-{{ size }}">
|
||||||
{% if book.cover %}
|
{% if book.cover %}
|
||||||
<img class="book-cover {{ size }}" src="/images/{{ book.cover }}" alt="{% include 'snippets/cover_alt.html' with book=book %}">
|
<img class="book-cover" src="/images/{{ book.cover }}" alt="{% include 'snippets/cover_alt.html' with book=book %}">
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="no-cover book-cover {{ size }}">
|
<div class="no-cover book-cover">
|
||||||
<img class="book-cover {{ size }}" src="/static/images/no_cover.jpg" alt="No cover">
|
<img class="book-cover" src="/static/images/no_cover.jpg" alt="No cover">
|
||||||
<div>
|
<div>
|
||||||
<p class="title">{{ book.title }}</p>
|
<p>{{ book.title }}</p>
|
||||||
<p>({{ book|edition_info }})</p>
|
<p>({{ book|edition_info }})</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
{% load fr_display %}
|
|
||||||
{% if book.description %}
|
{% if book.description %}
|
||||||
<blockquote>{{ book.description | description }}</blockquote>
|
<blockquote class="content">{{ book.description }}</blockquote>
|
||||||
{% elif book.parent_work.description %}
|
{% elif book.parent_work.description %}
|
||||||
<blockquote>{{ book.parent_work.description | description }}</blockquote>
|
<blockquote>{{ book.parent_work.description }}</blockquote>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
18
bookwyrm/templates/snippets/book_tiles.html
Normal file
18
bookwyrm/templates/snippets/book_tiles.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<div class="columns">
|
||||||
|
{% for book in books %}
|
||||||
|
{% if forloop.counter0|divisibleby:"4" %}
|
||||||
|
</div>
|
||||||
|
<div class="columns">
|
||||||
|
{% endif %}
|
||||||
|
<div class="column is-narrow">
|
||||||
|
<div class="box">
|
||||||
|
<a href="/book/{{ book.id }}">
|
||||||
|
{% include 'snippets/book_cover.html' with book=book %}
|
||||||
|
</a>
|
||||||
|
{% include 'snippets/rate_action.html' with user=request.user book=book %}
|
||||||
|
{% include 'snippets/shelve_button.html' with book=book %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<span class="title">
|
<span>
|
||||||
<a href="/book/{{ book.id }}">{{ book.title }}</a>
|
<a href="/book/{{ book.id }}">{{ book.title }}</a>
|
||||||
</span>
|
</span>
|
||||||
{% if book.authors %}
|
{% if book.authors %}
|
||||||
<span class="author">
|
<span>
|
||||||
by {% include 'snippets/authors.html' with book=book %}
|
by {% include 'snippets/authors.html' with book=book %}
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
{% load fr_display %}
|
|
||||||
|
|
||||||
<div class="all-shelves content-container">
|
|
||||||
{% for shelf in shelves %}
|
|
||||||
{% if shelf.books %}
|
|
||||||
<div>
|
|
||||||
<h2>{{ shelf.name }}
|
|
||||||
{% if shelf.size > shelf.books|length %}
|
|
||||||
<small>(<a href="/shelf/{{ user | username }}/{{ shelf.identifier }}">See all {{ shelf.size }}</a>)</small>
|
|
||||||
{% endif %}
|
|
||||||
</h2>
|
|
||||||
<div class="covers-shelf {{ shelf.identifier }} ">
|
|
||||||
{% for book in shelf.books %}
|
|
||||||
<div class="cover-container">
|
|
||||||
<label for="book-{{ book.id }}-radio">
|
|
||||||
{% include 'snippets/book_cover.html' with book=book %}
|
|
||||||
</label>
|
|
||||||
{% include 'snippets/shelve_button.html' with book=book hide_pulldown=True %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% for shelf in shelves %}
|
|
||||||
{% for book in shelf.books %}
|
|
||||||
<div class="compose-popout">
|
|
||||||
<input name="book-popout" type="radio" id="book-{{ book.id }}-radio">
|
|
||||||
<div class="compose compose-suggestion" id="compose-book-{{ book.id }}">
|
|
||||||
<label class="close icon icon-close" for="book-{{ book.id }}-radio-close" onclick="hide_element(this)">
|
|
||||||
<span class="hidden-text">Close</span>
|
|
||||||
</label>
|
|
||||||
<input name="book-popout" type="radio" id="book-{{ book.id }}-radio-close">
|
|
||||||
<div class="content-container">
|
|
||||||
<h2>
|
|
||||||
{% include 'snippets/avatar.html' with user=user %}
|
|
||||||
Your thoughts on
|
|
||||||
a <a href="/book/{{ book.id }}">{{ book.title }}</a>
|
|
||||||
by {% include 'snippets/authors.html' with book=book %}
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
{% include 'snippets/create_status.html' with book=book user=request.user %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
|
@ -1,43 +1,79 @@
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
|
|
||||||
<div class="tabs secondary">
|
<div class="columns">
|
||||||
<div class="tab active" data-id="tab-review-{{ book.id }}" data-category="tab-option-{{ book.id }}">
|
<div class="column">
|
||||||
<a href="/book/{{ book.id }}/review" onclick="tabChange(event)">Review</a>
|
<div class="tabs is-boxed">
|
||||||
</div>
|
<ul>
|
||||||
<div class="tab" data-id="tab-comment-{{ book.id }}" data-category="tab-option-{{ book.id }}">
|
<li class="is-active" data-id="tab-review-{{ book.id }}" data-category="tab-option-{{ book.id }}">
|
||||||
<a href="/book/{{ book.id }}/comment" onclick="tabChange(event)">Comment</a>
|
<label for="review-{{ book.id }}" onclick="tabChange(event)"><a>Review</a></label>
|
||||||
</div>
|
</li>
|
||||||
<div class="tab" data-id="tab-quotation-{{ book.id }}" data-category="tab-option-{{ book.id }}">
|
<li data-id="tab-comment-{{ book.id }}" data-category="tab-option-{{ book.id }}">
|
||||||
<a href="/book/{{ book.id }}/quotation" onclick="tabChange(event)">Quote</a>
|
<label for="comment-{{ book.id}}" onclick="tabChange(event)"><a>Comment</a></label>
|
||||||
</div>
|
</li>
|
||||||
|
<li data-id="tab-quotation-{{ book.id }}" data-category="tab-option-{{ book.id }}">
|
||||||
|
<label for="quote-{{ book.id }}" onclick="tabChange(event)"><a>Quote</a></label>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="book-preview row">
|
<div>
|
||||||
{% if not hide_cover %}
|
<input class="toggle-control" type="radio" name="status-tabs-{{ book.id }}" id="review-{{ book.id }}" checked>
|
||||||
<div class="cover-container">
|
<form class="toggle-content hidden tab-option-{{ book.id }}" name="review" action="/review/" method="post" id="tab-review-{{ book.id }}">
|
||||||
{% include 'snippets/book_cover.html' with book=book %}
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="book" value="{{ book.id }}">
|
||||||
|
<div class="control">
|
||||||
|
<label class="label" for="id_name_{{ book.id }}_review">Title:</label>
|
||||||
|
<input type="text" name="name" maxlength="255" class="input" required="" id="id_name_{{ book.id }}_review" placeholder="My review of '{{ book.title }}'">
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
<div class="control">
|
||||||
<form class="tab-option-{{ book.id }} review-form" name="review" action="/review/" method="post" id="tab-review-{{ book.id }}">
|
<label class="label" for="id_content_{{ book.id }}_review">Review:</label>
|
||||||
{% csrf_token %}
|
|
||||||
<input type="hidden" name="book" value="{{ book.id }}">
|
|
||||||
{% include 'snippets/rate_form.html' with book=book %}
|
|
||||||
{{ review_form.as_p }}
|
|
||||||
<button type="submit">post review</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<form class="hidden tab-option-{{ book.id }} review-form" name="comment" action="/comment/" method="post" id="tab-comment-{{ book.id }}">
|
<span class="is-sr-only">Rating</span>
|
||||||
{% csrf_token %}
|
<div class="field is-grouped stars form-rate-stars">
|
||||||
<input type="hidden" name="book" value="{{ book.id }}">
|
<input class="hidden" type="radio" name="rating" value="" checked>
|
||||||
{{ comment_form.as_p }}
|
{% for i in '12345'|make_list %}
|
||||||
<button type="submit">post comment</button>
|
<input class="hidden" id="book{{book.id}}-star-{{ forloop.counter }}" type="radio" name="rating" value="{{ forloop.counter }}">
|
||||||
</form>
|
<label class="icon icon-star-empty" for="book{{book.id}}-star-{{ forloop.counter }}">
|
||||||
|
<span class="is-sr-only">{{ forloop.counter }} star{{ forloop.counter | pluralize }}</span>
|
||||||
|
</label>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<form class="hidden tab-option-{{ book.id }} review-form quote-form" name="quotation" action="/quotate/" method="post" id="tab-quotation-{{ book.id }}">
|
<textarea name="content" class="textarea" id="id_content_{{ book.id }}_review"></textarea>
|
||||||
{% csrf_token %}
|
</div>
|
||||||
<input type="hidden" name="book" value="{{ book.id }}">
|
<button class="button is-primary" type="submit">post review</button>
|
||||||
{{ quotation_form.as_p }}
|
|
||||||
<button typr="submit">post quote</button>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input class="toggle-control" type="radio" name="status-tabs-{{ book.id }}" id="comment-{{ book.id }}">
|
||||||
|
<form class="toggle-content hidden tab-option-{{ book.id }}" name="comment" action="/comment/" method="post" id="tab-comment-{{ book.id }}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="book" value="{{ book.id }}">
|
||||||
|
<div class="control">
|
||||||
|
<label class="label" for="id_content_{{ book.id }}_comment">Comment:</label>
|
||||||
|
<textarea name="content" class="textarea" id="id_content_{{ book.id }}_comment" placeholder="Some thoughts on '{{ book.title }}'"></textarea>
|
||||||
|
</div>
|
||||||
|
<button class="button is-primary" type="submit">post comment</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input class="toggle-control" type="radio" name="status-tabs-{{ book.id }}" id="quote-{{ book.id }}">
|
||||||
|
<form class="toggle-content hidden tab-option-{{ book.id }}" name="quotation" action="/quotate/" method="post" id="tab-quotation-{{ book.id }}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="book" value="{{ book.id }}">
|
||||||
|
<div class="control">
|
||||||
|
<label class="label" for="id_quote_{{ book.id }}_quote">Quote:</label>
|
||||||
|
<textarea name="quote" class="textarea" required="" id="id_quote_{{ book.id }}_quote" placeholder="An except from '{{ book.title }}'"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<label class="label" for="id_content_{{ book.id }}_quote">Comment:</label>
|
||||||
|
<textarea name="content" class="textarea is-small" id="id_content_{{ book.id }}_quote"></textarea>
|
||||||
|
</div>
|
||||||
|
<button class="button is-primary" type="submit">post quote</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
@ -11,14 +11,14 @@ Follow request already sent.
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="user" value="{{ user.username }}">
|
<input type="hidden" name="user" value="{{ user.username }}">
|
||||||
{% if user.manually_approves_followers %}
|
{% if user.manually_approves_followers %}
|
||||||
<button type="submit">Send follow request</button>
|
<button class="button is-small" type="submit">Send follow request</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button type="submit">Follow</button>
|
<button class="button is-small" type="submit">Follow</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
<form action="/unfollow/" method="POST" onsubmit="interact(event)" class="follow-{{ user.id }} {% if not request.user in user.followers.all %}hidden{%endif %}" data-id="follow-{{ user.id }}">
|
<form action="/unfollow/" method="POST" onsubmit="interact(event)" class="follow-{{ user.id }} {% if not request.user in user.followers.all %}hidden{%endif %}" data-id="follow-{{ user.id }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="user" value="{{ user.username }}">
|
<input type="hidden" name="user" value="{{ user.username }}">
|
||||||
<button type="submit">Unfollow</button>
|
<button class="button is-small" type="submit">Unfollow</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
<form action="/accept_follow_request/" method="POST">
|
<form action="/accept_follow_request/" method="POST">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="user" value="{{ user.username }}">
|
<input type="hidden" name="user" value="{{ user.username }}">
|
||||||
<button type="submit">Accept</button>
|
<button class="button is-small" type="submit">Accept</button>
|
||||||
</form>
|
</form>
|
||||||
<form action="/delete_follow_request/" method="POST">
|
<form action="/delete_follow_request/" method="POST">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="user" value="{{ user.username }}">
|
<input type="hidden" name="user" value="{{ user.username }}">
|
||||||
<button type="submit" class="warning">Delete</button>
|
<button class="button is-small" type="submit" class="warning">Delete</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -1,64 +1,65 @@
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
<div class="interaction">
|
<div class="card-footer-item">
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
<form name="reply" action="/reply" method="post" onsubmit="return reply(event)">
|
<form name="reply" action="/reply" method="post" onsubmit="return reply(event)">
|
||||||
|
<div class="field is-grouped">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="parent" value="{{ activity.id }}">
|
<input type="hidden" name="parent" value="{{ activity.id }}">
|
||||||
<textarea name="content" placeholder="Leave a comment..." id="id_content" required="true"></textarea>
|
<textarea name="content" placeholder="Leave a comment..." id="id_content_{{ activity.id }}" required="true"></textarea>
|
||||||
<button type="submit">
|
<button class="button" type="submit">
|
||||||
<span class="icon icon-comment">
|
<span class="icon icon-comment">
|
||||||
<span class="hidden-text">Comment</span>
|
<span class="is-sr-only">Comment</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form name="boost" action="/boost/{{ activity.id }}" method="post" onsubmit="return interact(event)" class="boost-{{ status.id }} {% if request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}">
|
<form name="boost" action="/boost/{{ activity.id }}" method="post" onsubmit="return interact(event)" class="boost-{{ status.id }} {% if request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button type="submit">
|
<button class="button" type="submit">
|
||||||
<span class="icon icon-boost">
|
<span class="icon icon-boost">
|
||||||
<span class="hidden-text">Boost status</span>
|
<span class="is-sr-only">Boost status</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<form name="unboost" action="/unboost/{{ activity.id }}" method="post" onsubmit="return interact(event)" class="boost-{{ status.id }} active {% if not request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}">
|
<form name="unboost" action="/unboost/{{ activity.id }}" method="post" onsubmit="return interact(event)" class="boost-{{ status.id }} active {% if not request.user|boosted:status %}hidden{% endif %}" data-id="boost-{{ status.id }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button type="submit">
|
<button class="button is-success" type="submit">
|
||||||
<span class="icon icon-boost">
|
<span class="icon icon-boost">
|
||||||
<span class="hidden-text">Un-boost status</span>
|
<span class="is-sr-only">Un-boost status</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form name="favorite" action="/favorite/{{ activity.id }}" method="POST" onsubmit="return interact(event)" class="fav-{{ status.id }} {% if request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}">
|
<form name="favorite" action="/favorite/{{ activity.id }}" method="POST" onsubmit="return interact(event)" class="fav-{{ status.id }} {% if request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button type="submit">
|
<button class="button" type="submit">
|
||||||
<span class="icon icon-heart">
|
<span class="icon icon-heart">
|
||||||
<span class="hidden-text">Like status</span>
|
<span class="is-sr-only">Like status</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<form name="unfavorite" action="/unfavorite/{{ activity.id }}" method="POST" onsubmit="return interact(event)" class="fav-{{ status.id }} active {% if not request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}">
|
<form name="unfavorite" action="/unfavorite/{{ activity.id }}" method="POST" onsubmit="return interact(event)" class="fav-{{ status.id }} active {% if not request.user|liked:status %}hidden{% endif %}" data-id="fav-{{ status.id }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button type="submit">
|
<button class="button is-success" type="submit">
|
||||||
<span class="icon icon-heart">
|
<span class="icon icon-heart">
|
||||||
<span class="hidden-text">Un-like status</span>
|
<span class="is-sr-only">Un-like status</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="/login">
|
<a href="/login">
|
||||||
<span class="icon icon-comment">
|
<span class="icon icon-comment">
|
||||||
<span class="hidden-text">Comment</span>
|
<span class="is-sr-only">Comment</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="icon icon-boost">
|
<span class="icon icon-boost">
|
||||||
<span class="hidden-text">Boost status</span>
|
<span class="is-sr-only">Boost status</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="icon icon-heart">
|
<span class="icon icon-heart">
|
||||||
<span class="hidden-text">Like status</span>
|
<span class="is-sr-only">Like status</span>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
<span class="hidden-text">Leave a rating</span>
|
<span class="is-sr-only">Leave a rating</span>
|
||||||
<div class="stars rate-stars">
|
<div class="field is-grouped stars rate-stars">
|
||||||
{% for i in '12345'|make_list %}
|
{% for i in '12345'|make_list %}
|
||||||
<form name="rate" action="/rate/" method="POST" onsubmit="return rate_stars(event)">
|
<form name="rate" action="/rate/" method="POST" onsubmit="return rate_stars(event)">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="book" value="{{ book.id }}">
|
<input type="hidden" name="book" value="{{ book.id }}">
|
||||||
<input type="hidden" name="rating" value="{{ forloop.counter }}">
|
<input type="hidden" name="rating" value="{{ forloop.counter }}">
|
||||||
<button type="submit" class="icon icon-star-{% if book|rating:user < forloop.counter %}empty{% else %}full{% endif %}">
|
<button type="submit" class="icon icon-star-{% if book|rating:user < forloop.counter %}empty{% else %}full{% endif %}">
|
||||||
<span class="hidden-text">{{ forloop.counter }} star{{ forloop.counter | pluralize }}</span>
|
<span class="is-sr-only">{{ forloop.counter }} star{{ forloop.counter | pluralize }}</span>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
{% load fr_display %}
|
|
||||||
<span class="hidden-text">Rating</span>
|
|
||||||
<div class="stars rate-stars">
|
|
||||||
<input type="radio" name="rating" value="" checked>
|
|
||||||
{% for i in '12345'|make_list %}
|
|
||||||
<input id="book{{book.id}}-star-{{ forloop.counter }}" type="radio" name="rating" value="{{ forloop.counter }}">
|
|
||||||
<label class="icon icon-star-empty" for="book{{book.id}}-star-{{ forloop.counter }}">
|
|
||||||
<span class="hidden-text">{{ forloop.counter }} star{{ forloop.counter | pluralize }}</span>
|
|
||||||
</label>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
{% if shelf.books %}
|
{% if shelf.books %}
|
||||||
<table>
|
<table class="table is-striped is-fullwidth">
|
||||||
|
|
||||||
<tr class="book-preview">
|
<tr class="book-preview">
|
||||||
<th>
|
<th>
|
||||||
Cover
|
Cover
|
||||||
|
|
|
@ -1,25 +1,27 @@
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
|
|
||||||
<div class="pulldown-button">
|
<div class="field is-grouped">
|
||||||
<form name="shelve" action="/shelve/" method="post">
|
<form name="shelve" action="/shelve/" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="book" value="{{ book.id }}">
|
<input type="hidden" name="book" value="{{ book.id }}">
|
||||||
<input type="hidden" name="shelf" value="{% shelve_button_identifier book %}">
|
<input type="hidden" name="shelf" value="{% shelve_button_identifier book %}">
|
||||||
<button type="submit" style="">{% shelve_button_text book %}</button>
|
<button class="button is-small" type="submit" style="">{% shelve_button_text book %}</button>
|
||||||
</form>
|
</form>
|
||||||
|
<div class="dropdown is-hoverable">
|
||||||
{% if not hide_pulldown %}
|
{% if not hide_pulldown %}
|
||||||
<div class="pulldown-container">
|
<div class="button dropdown-trigger is-small" >
|
||||||
<button class="pulldown-toggle">
|
<span class="icon icon-arrow-down"><span class="is-sr-only">More shelves</span></span>
|
||||||
<span class="icon icon-arrow-down"><span class="hidden-text">More shelves</span></span>
|
</div>
|
||||||
</button>
|
|
||||||
<ul class="pulldown">
|
<div class="dropdown-menu">
|
||||||
<form name="shelve" action="/shelve/" method="post">
|
<ul class="dropdown-content">
|
||||||
|
<form class="dropdown-item" name="shelve" action="/shelve/" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="book" value="{{ book.id }}">
|
<input type="hidden" name="book" value="{{ book.id }}">
|
||||||
{% for shelf in request.user.shelf_set.all %}
|
{% for shelf in request.user.shelf_set.all %}
|
||||||
<li>
|
<li>
|
||||||
<button name="shelf" type="submit" value="{{ shelf.identifier }}" {% if shelf in book.shelf_set.all %} disabled {% endif %}>{{ shelf.name }} {% if shelf in book.shelf_set.all %} ✓ {% endif %}</button>
|
<button class="is-small" name="shelf" type="submit" value="{{ shelf.identifier }}" {% if shelf in book.shelf_set.all %} disabled {% endif %}>{{ shelf.name }} {% if shelf in book.shelf_set.all %} ✓ {% endif %}</button>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</form>
|
</form>
|
||||||
|
@ -28,4 +30,5 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="stars">
|
<div class="stars">
|
||||||
<span class="hidden-text">{{ rating|floatformat }} star{{ rating|floatformat | pluralize }}</span>
|
<span class="is-sr-only">{{ rating|floatformat }} star{{ rating|floatformat | pluralize }}</span>
|
||||||
{% for i in '12345'|make_list %}
|
{% for i in '12345'|make_list %}
|
||||||
<span class="icon icon-star-{% if rating >= forloop.counter %}full{% elif rating|floatformat:0 >= forloop.counter|floatformat:0 %}half{% else %}empty{% endif %}">
|
<span class="icon icon-star-{% if rating >= forloop.counter %}full{% elif rating|floatformat:0 >= forloop.counter|floatformat:0 %}half{% else %}empty{% endif %}">
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,24 +1,31 @@
|
||||||
|
{% load humanize %}
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
|
|
||||||
<div class="post {{ status.status_type | lower }} depth-{{ depth }} {% if main %}main{% else %}reply{% endif %}">
|
<div class="card">
|
||||||
|
<header class="card-header">
|
||||||
<h2>
|
|
||||||
{% if status.boosted_status %}
|
|
||||||
{% include 'snippets/status_header.html' with status=status.boosted_status %}
|
|
||||||
<small class="subhead">{% include 'snippets/status_header.html' with status=status %}</small>
|
|
||||||
{% else %}
|
|
||||||
{% include 'snippets/status_header.html' with status=status %}
|
{% include 'snippets/status_header.html' with status=status %}
|
||||||
{% endif %}
|
</header>
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div class="status-content">
|
<div class="card-content">
|
||||||
{% include 'snippets/status_content.html' with status=status %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{% if status.status_type == 'Boost' %}
|
{% if status.status_type == 'Boost' %}
|
||||||
{% include 'snippets/interaction.html' with activity=status|boosted_status %}
|
{% include 'snippets/status_content.html' with status=status.boosted_status %}
|
||||||
|
{% else %}
|
||||||
|
{% include 'snippets/status_content.html' with status=status %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="card-footer">
|
||||||
|
{% if status.status_type == 'Boost' %}
|
||||||
|
{% include 'snippets/interaction.html' with activity=status.boosted_status %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% include 'snippets/interaction.html' with activity=status %}
|
{% include 'snippets/interaction.html' with activity=status %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="card-footer-item">
|
||||||
|
<span class="icon icon-public">
|
||||||
|
<span class="is-sr-only">Public post</span>
|
||||||
|
</span>
|
||||||
|
<a href="{{ status.remote_id }}">{{ status.published_date | naturaltime }}</a>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,38 +1,32 @@
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
|
|
||||||
|
<div class="media">
|
||||||
{% if not hide_book and status.mention_books.count %}
|
{% if not hide_book and status.mention_books.count %}
|
||||||
<div class="row">
|
<div class="media-left">
|
||||||
|
<div class="columns">
|
||||||
{% for book in status.mention_books.all|slice:"0:4" %}
|
{% for book in status.mention_books.all|slice:"0:4" %}
|
||||||
<div class="row">
|
<div class="column">
|
||||||
<div class="cover-container">
|
<a href="/book/{{ book.id }}">{% include 'snippets/book_cover.html' with book=book %}</a>
|
||||||
{% include 'snippets/book_cover.html' with book=book %}
|
|
||||||
{% if status.mention_books.count > 1 %}
|
{% if status.mention_books.count > 1 %}
|
||||||
<p>{% include 'snippets/book_titleby.html' with book=book %}</p>
|
<p>{% include 'snippets/book_titleby.html' with book=book %}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% include 'snippets/rate_action.html' with book=book user=request.user %}
|
|
||||||
{% include 'snippets/shelve_button.html' with book=book %}
|
{% include 'snippets/shelve_button.html' with book=book %}
|
||||||
</div>
|
</div>
|
||||||
{% if status.mention_books.count == 1 %}
|
|
||||||
<div>
|
|
||||||
<p>{% include 'snippets/book_titleby.html' with book=book %}</p>
|
|
||||||
{% include 'snippets/book_description.html' with book=book %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
{% if not hide_book and status.book %}
|
|
||||||
<div class="cover-container">
|
|
||||||
{% include 'snippets/book_cover.html' with book=status.book %}
|
|
||||||
{% include 'snippets/rate_action.html' with book=status.book user=request.user %}
|
|
||||||
{% include 'snippets/shelve_button.html' with book=status.book %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if not hide_book and status.book %}
|
||||||
|
<div class="media-left">
|
||||||
<div>
|
<div>
|
||||||
|
<a href="/book/{{ status.book.id }}">{% include 'snippets/book_cover.html' with book=status.book %}</a>
|
||||||
|
{% include 'snippets/shelve_button.html' with book=status.book %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="media-content">
|
||||||
{% if status.status_type == 'Review' %}
|
{% if status.status_type == 'Review' %}
|
||||||
<h3>
|
<h3>
|
||||||
{% if status.name %}{{ status.name }}<br>{% endif %}
|
{% if status.name %}{{ status.name }}<br>{% endif %}
|
||||||
|
@ -41,17 +35,21 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if status.quote %}
|
{% if status.quote %}
|
||||||
<div class="quote">
|
<div class="quote block">
|
||||||
<blockquote>{{ status.quote }}</blockquote>
|
<blockquote>{{ status.quote }}</blockquote>
|
||||||
|
|
||||||
<p> — {% include 'snippets/book_titleby.html' with book=status.book %}</p>
|
<p> — {% include 'snippets/book_titleby.html' with book=status.book %}</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if status.content and status.status_type != 'Update' and status.status_type != 'Boost' %}
|
{% if status.content and status.status_type != 'GeneratedNote' and status.status_type != 'Boost' %}
|
||||||
<blockquote>{{ status.content | safe }}</blockquote>
|
<blockquote>{{ status.content | safe }}</blockquote>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if status.mention_books.count == 1 and not status.book %}
|
||||||
|
{% include 'snippets/book_description.html' with book=status.mention_books.first %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if not status.content and status.book and not hide_book and status.status_type != 'Boost' %}
|
{% if not status.content and status.book and not hide_book and status.status_type != 'Boost' %}
|
||||||
{% include 'snippets/book_description.html' with book=status.book %}
|
{% include 'snippets/book_description.html' with book=status.book %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
{% load humanize %}
|
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
|
<div class="card-header-title">
|
||||||
|
<p>
|
||||||
{% include 'snippets/avatar.html' with user=status.user %}
|
{% include 'snippets/avatar.html' with user=status.user %}
|
||||||
{% include 'snippets/username.html' with user=status.user %}
|
|
||||||
|
|
||||||
{% if status.status_type == 'Update' %}
|
{% include 'snippets/username.html' with user=status.user %}
|
||||||
{{ status.content | safe }}
|
{% if status.status_type == 'GeneratedNote' %}
|
||||||
|
{{ status.content | safe }} {% include 'snippets/book_titleby.html' with book=status.mention_books.first %}
|
||||||
|
{% elif status.status_type == 'Boost' %}
|
||||||
|
boosted {% include 'snippets/avatar.html' with user=status.boosted_status.user %}{% include 'snippets/username.html' with user=status.boosted_status.user possessive=True %} status
|
||||||
{% elif status.status_type == 'Review' and not status.name and not status.content%}
|
{% elif status.status_type == 'Review' and not status.name and not status.content%}
|
||||||
rated <a href="/book/{{ status.book.id }}">{{ status.book.title }}</a>
|
rated <a href="/book/{{ status.book.id }}">{{ status.book.title }}</a>
|
||||||
{% elif status.status_type == 'Review' %}
|
{% elif status.status_type == 'Review' %}
|
||||||
|
@ -13,13 +16,10 @@
|
||||||
commented on <a href="/book/{{ status.book.id }}">{{ status.book.title }}</a>
|
commented on <a href="/book/{{ status.book.id }}">{{ status.book.title }}</a>
|
||||||
{% elif status.status_type == 'Quotation' %}
|
{% elif status.status_type == 'Quotation' %}
|
||||||
quoted <a href="/book/{{ status.book.id }}">{{ status.book.title }}</a>
|
quoted <a href="/book/{{ status.book.id }}">{{ status.book.title }}</a>
|
||||||
{% elif status.status_type == 'Boost' %}
|
|
||||||
boosted
|
|
||||||
{% elif status.reply_parent %}
|
{% elif status.reply_parent %}
|
||||||
{% with parent_status=status|parent %}
|
{% with parent_status=status|parent %}
|
||||||
replied to {% include 'snippets/username.html' with user=parent_status.user possessive=True %} <a href="{{parent_status.remote_id }}">{{ parent_status.status_type | lower }}</a>
|
replied to {% include 'snippets/username.html' with user=parent_status.user possessive=True %} <a href="{{parent_status.remote_id }}">{% if parent_status.status_type == 'GeneratedNote' %}update{% else %}{{ parent_status.status_type | lower }}{% endif %}</a>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span class="time-ago">
|
</p>
|
||||||
<a href="{{ status.remote_id }}">{{ status.published_date | naturaltime }}</a>
|
</div>
|
||||||
</span>
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
{% for tab in tabs %}
|
|
||||||
<div class="tab {% if tab.id == active_tab %}active{% endif %}">
|
|
||||||
<a href="{{ path }}/{{ tab.id }}">{{ tab.display }}</a>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
|
@ -1,20 +1,24 @@
|
||||||
|
<div class="control">
|
||||||
|
<div class="tags has-addons">
|
||||||
|
<a class="tag is-link" href="/tag/{{ tag.identifier|urlencode }}">
|
||||||
|
{{ tag.name }}
|
||||||
|
</a>
|
||||||
<div class="tag">
|
<div class="tag">
|
||||||
<a href="/tag/{{ tag.identifier|urlencode }}">{{ tag.name }}</a>
|
|
||||||
{% if tag.identifier in user_tags %}
|
{% if tag.identifier in user_tags %}
|
||||||
<form class="tag-form" name="tag" action="/untag/" method="post">
|
<form name="tag" action="/untag/" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="book" value="{{ book.id }}">
|
<input type="hidden" name="book" value="{{ book.id }}">
|
||||||
<input type="hidden" name="name" value="{{ tag.name }}">
|
<input type="hidden" name="name" value="{{ tag.name }}">
|
||||||
<button type="submit">x</button>
|
<button type="submit">x<span class="is-sr-only"> remove tag</span></button>
|
||||||
</form>
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
<form class="tag-form" name="tag" action="/tag/" method="post">
|
<form name="tag" action="/tag/" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="book" value="{{ book.id }}">
|
<input type="hidden" name="book" value="{{ book.id }}">
|
||||||
<input type="hidden" name="name" value="{{ tag.name }}">
|
<input type="hidden" name="name" value="{{ tag.name }}">
|
||||||
<button type="submit">+</button>
|
<button type="submit">+<span class="is-sr-only"> add tag</span></button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
|
<div class="block">
|
||||||
|
|
||||||
{% with depth=depth|add:1 %}
|
{% with depth=depth|add:1 %}
|
||||||
{% if depth <= max_depth and status.reply_parent and direction <= 0 %}
|
{% if depth <= max_depth and status.reply_parent and direction <= 0 %}
|
||||||
{% with direction=-1 %}
|
{% with direction=-1 %}
|
||||||
|
@ -16,3 +18,5 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
{% include 'snippets/avatar.html' with user=user %}
|
|
||||||
{% include 'snippets/username.html' with user=user %}
|
|
||||||
<small>{{ user.username }}</small>
|
|
||||||
</div>
|
|
||||||
{% if not is_self %}
|
|
||||||
{% include 'snippets/follow_button.html' with user=user %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div id="content">
|
<div class="block">
|
||||||
<div class="comment-thread">
|
|
||||||
{% include 'snippets/thread.html' with status=status depth=0 max_depth=6 is_root=True direction=0 %}
|
{% include 'snippets/thread.html' with status=status depth=0 max_depth=6 is_root=True direction=0 %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,12 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="content-container">
|
|
||||||
<h2>Books tagged "{{ tag.name }}"</h2>
|
<div class="block">
|
||||||
<div class="book-grid row wrap shrink">
|
<h2 class="title">Books tagged "{{ tag.name }}"</h2>
|
||||||
{% for book in books.all %}
|
{% include 'snippets/book_tiles.html' with books=books.all %}
|
||||||
<div class="cover-container">
|
|
||||||
<a href="/book/{{ book.id }}">
|
|
||||||
{% include 'snippets/book_cover.html' with book=book %}
|
|
||||||
</a>
|
|
||||||
{% include 'snippets/rate_action.html' with user=request.user book=book %}
|
|
||||||
{% include 'snippets/shelve_button.html' with book=book %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,43 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
{% include 'user_header.html' with user=user %}
|
{% include 'user_header.html' with user=user %}
|
||||||
{% include 'snippets/covers_shelf.html' with shelves=shelves user=user %}
|
</div>
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
|
<h2 class="title">Shelves</h2>
|
||||||
|
<div class="columns">
|
||||||
|
{% for shelf in shelves %}
|
||||||
|
<div class="column is-narrow">
|
||||||
|
<h3>{{ shelf.name }}
|
||||||
|
{% if shelf.size > 3 %}<small>(<a href="{{ shelf.remote_id }}">See all {{ shelf.size }}</a>)</small>{% endif %}</h3>
|
||||||
|
<div class="is-mobile field is-grouped">
|
||||||
|
{% for book in shelf.books %}
|
||||||
|
<div class="control">
|
||||||
|
<a href="/book/{{ book.id }}">
|
||||||
|
{% include 'snippets/book_cover.html' with book=book size="medium" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<small><a href="/user/{{ user.localname }}/shelves">See all {{ shelf_count }} shelves</a></small>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="content-container"><h2>User Activity</h2></div>
|
<div class="block">
|
||||||
|
<h2 class="title">User Activity</h2>
|
||||||
|
</div>
|
||||||
{% for activity in activities %}
|
{% for activity in activities %}
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
{% include 'snippets/status.html' with status=activity %}
|
{% include 'snippets/status.html' with status=activity %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if not activities %}
|
{% if not activities %}
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
<p>No activities yet!</a>
|
<p>No activities yet!</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
{% load fr_display %}
|
{% load fr_display %}
|
||||||
<div class="content-container user-profile">
|
<div class="block">
|
||||||
<h2>User Profile
|
<div class="level">
|
||||||
|
<h2 class="title">User Profile</h2>
|
||||||
{% if is_self %}
|
{% if is_self %}
|
||||||
|
<div class="level-right">
|
||||||
<a href="/user-edit/" class="edit-link">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="is-sr-only">Edit profile</span>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="pic-container">
|
|
||||||
{% include 'snippets/avatar.html' with user=user large=True %}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="columns">
|
||||||
|
<div class="column is-narrow">
|
||||||
|
<div class="media">
|
||||||
|
<div class="media-left">
|
||||||
|
{% include 'snippets/avatar.html' with user=user large=True %}
|
||||||
|
</div>
|
||||||
|
<div class="media-content">
|
||||||
<p>{% if user.name %}{{ user.name }}{% else %}{{ user.localname }}{% endif %}</p>
|
<p>{% if user.name %}{{ user.name }}{% else %}{{ user.localname }}{% endif %}</p>
|
||||||
<p>{{ user.username }}</p>
|
<p>{{ user.username }}</p>
|
||||||
<p>Joined {{ user.created_date | naturaltime }}</p>
|
<p>Joined {{ user.created_date | naturaltime }}</p>
|
||||||
|
@ -24,11 +28,15 @@
|
||||||
<a href="/user/{{ user|username }}/followers">{{ user.followers.count }} follower{{ user.followers.count | pluralize }}</a>,
|
<a href="/user/{{ user|username }}/followers">{{ user.followers.count }} follower{{ user.followers.count | pluralize }}</a>,
|
||||||
<a href="/user/{{ user|username }}/following">{{ user.following.count }} following</a></p>
|
<a href="/user/{{ user|username }}/following">{{ user.following.count }} following</a></p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column">
|
||||||
{% if user.summary %}
|
{% if user.summary %}
|
||||||
<blockquote><span class="icon icon-quote-open"></span>{{ user.summary | safe }}</blockquote>
|
<blockquote><span class="icon icon-quote-open"></span>{{ user.summary | safe }}</blockquote>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{% if not is_self %}
|
{% if not is_self %}
|
||||||
{% include 'snippets/follow_button.html' with user=user %}
|
{% include 'snippets/follow_button.html' with user=user %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
|
<h2 class="title">User search results</h2>
|
||||||
|
{% if not results %}
|
||||||
|
<p>No results found for "{{ query }}"</p>
|
||||||
|
{% endif %}
|
||||||
{% for result in results %}
|
{% for result in results %}
|
||||||
<div>
|
<div class="block">
|
||||||
<h2>{{ result.username }}</h2>
|
{% include 'snippets/avatar.html' with user=result %}</h2>
|
||||||
|
{% include 'snippets/username.html' with user=result show_full=True %}</h2>
|
||||||
{% include 'snippets/follow_button.html' with user=result %}
|
{% include 'snippets/follow_button.html' with user=result %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
{% include 'user_header.html' with user=user %}
|
{% include 'user_header.html' with user=user %}
|
||||||
|
|
||||||
{% for shelf in shelves %}
|
{% for shelf in shelves %}
|
||||||
<div class="content-container">
|
<div class="block">
|
||||||
<h2>{{ shelf.name }}</h2>
|
<h2 class="title">{{ shelf.name }}</h2>
|
||||||
{% include 'snippets/shelf.html' with shelf=shelf ratings=ratings %}
|
{% include 'snippets/shelf.html' with shelf=shelf ratings=ratings %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -25,27 +25,6 @@ def get_rating(book, user):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name='description')
|
|
||||||
def description_format(description):
|
|
||||||
''' handle the various OL description formats '''
|
|
||||||
if not description:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
if '----------' in description:
|
|
||||||
description = description.split('----------')[0]
|
|
||||||
|
|
||||||
return description.strip()
|
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name='author_bio')
|
|
||||||
def bio_format(bio):
|
|
||||||
''' clean up OL author bios '''
|
|
||||||
if isinstance(bio, dict) and 'value' in bio:
|
|
||||||
bio = bio['value']
|
|
||||||
bio = bio.split('\n')
|
|
||||||
return bio[0].strip()
|
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name='username')
|
@register.filter(name='username')
|
||||||
def get_user_identifier(user):
|
def get_user_identifier(user):
|
||||||
''' use localname for local users, username for remote '''
|
''' use localname for local users, username for remote '''
|
||||||
|
@ -67,12 +46,6 @@ def get_replies(status):
|
||||||
).select_subclasses().all()[:10]
|
).select_subclasses().all()[:10]
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name='reply_count')
|
|
||||||
def get_reply_count(status):
|
|
||||||
''' how many replies does a status have? '''
|
|
||||||
return models.Status.objects.filter(reply_parent=status).count()
|
|
||||||
|
|
||||||
|
|
||||||
@register.filter(name='parent')
|
@register.filter(name='parent')
|
||||||
def get_parent(status):
|
def get_parent(status):
|
||||||
''' get the reply parent for a status '''
|
''' get the reply parent for a status '''
|
||||||
|
@ -170,18 +143,6 @@ def shelve_button_text(context, book):
|
||||||
return 'Want to read'
|
return 'Want to read'
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
|
||||||
def current_shelf(context, book):
|
|
||||||
''' check what shelf a user has a book on, if any '''
|
|
||||||
try:
|
|
||||||
shelf = models.ShelfBook.objects.get(
|
|
||||||
shelf__user=context['user'],
|
|
||||||
book=book
|
|
||||||
)
|
|
||||||
except models.ShelfBook.DoesNotExist:
|
|
||||||
return None
|
|
||||||
return shelf.name
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=False)
|
@register.simple_tag(takes_context=False)
|
||||||
def latest_read_through(book, user):
|
def latest_read_through(book, user):
|
||||||
''' the most recent read activity '''
|
''' the most recent read activity '''
|
||||||
|
|
|
@ -38,14 +38,13 @@ urlpatterns = [
|
||||||
|
|
||||||
# ui views
|
# ui views
|
||||||
re_path(r'^login/?$', views.login_page),
|
re_path(r'^login/?$', views.login_page),
|
||||||
|
re_path(r'^register/?$', views.register_page),
|
||||||
re_path(r'^about/?$', views.about_page),
|
re_path(r'^about/?$', views.about_page),
|
||||||
|
re_path(r'^invite/?$', views.manage_invites),
|
||||||
re_path(r'^invite/(?P<code>[A-Za-z0-9]+)/?$', views.invite_page),
|
re_path(r'^invite/(?P<code>[A-Za-z0-9]+)/?$', views.invite_page),
|
||||||
re_path(r'^manage_invites/?$', views.manage_invites),
|
|
||||||
|
|
||||||
path('', views.home),
|
path('', views.home),
|
||||||
re_path(r'^(?P<tab>home|local|federated)/?$', views.home_tab),
|
|
||||||
re_path(r'^notifications/?', views.notifications_page),
|
re_path(r'^notifications/?', views.notifications_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'import_status/(\d+)/?$', views.import_status),
|
re_path(r'import_status/(\d+)/?$', views.import_status),
|
||||||
re_path(r'user-edit/?$', views.edit_profile_page),
|
re_path(r'user-edit/?$', views.edit_profile_page),
|
||||||
|
@ -66,8 +65,6 @@ urlpatterns = [
|
||||||
|
|
||||||
# books
|
# books
|
||||||
re_path(r'%s(.json)?/?$' % book_path, views.book_page),
|
re_path(r'%s(.json)?/?$' % book_path, views.book_page),
|
||||||
re_path(r'%s/(?P<tab>friends|local|federated)?$' % \
|
|
||||||
book_path, views.book_page),
|
|
||||||
re_path(r'%s/edit/?$' % book_path, views.edit_book_page),
|
re_path(r'%s/edit/?$' % book_path, views.edit_book_page),
|
||||||
re_path(r'^editions/(?P<work_id>\d+)/?$', views.editions_page),
|
re_path(r'^editions/(?P<work_id>\d+)/?$', views.editions_page),
|
||||||
|
|
||||||
|
@ -84,7 +81,7 @@ urlpatterns = [
|
||||||
# internal action endpoints
|
# internal action endpoints
|
||||||
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'^user-register/?$', actions.register),
|
||||||
re_path(r'^edit_profile/?$', actions.edit_profile),
|
re_path(r'^edit_profile/?$', actions.edit_profile),
|
||||||
|
|
||||||
re_path(r'^import_data/?', actions.import_data),
|
re_path(r'^import_data/?', actions.import_data),
|
||||||
|
|
|
@ -144,7 +144,7 @@ def resolve_book(request):
|
||||||
def edit_book(request, book_id):
|
def edit_book(request, book_id):
|
||||||
''' edit a book cool '''
|
''' edit a book cool '''
|
||||||
if not request.method == 'POST':
|
if not request.method == 'POST':
|
||||||
return redirect('/book/%s' % request.user.localname)
|
return redirect('/book/%s' % book_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
book = models.Edition.objects.get(id=book_id)
|
book = models.Edition.objects.get(id=book_id)
|
||||||
|
@ -444,4 +444,4 @@ def create_invite(request):
|
||||||
invite.user = request.user
|
invite.user = request.user
|
||||||
invite.save()
|
invite.save()
|
||||||
|
|
||||||
return redirect('/manage_invites')
|
return redirect('/invite')
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.db.models import Avg, Q
|
from django.db.models import Avg, Count, Q
|
||||||
from django.http import HttpResponseBadRequest, HttpResponseNotFound,\
|
from django.http import HttpResponseBadRequest, HttpResponseNotFound,\
|
||||||
JsonResponse
|
JsonResponse
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
|
@ -43,60 +43,54 @@ def not_found_page(request, _):
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def home(request):
|
def home(request):
|
||||||
''' this is the same as the feed on the home tab '''
|
|
||||||
return home_tab(request, 'home')
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def home_tab(request, tab):
|
|
||||||
''' user's homepage with activity feed '''
|
''' user's homepage with activity feed '''
|
||||||
|
# TODO: why on earth would this be where the pagination is set
|
||||||
page_size = 15
|
page_size = 15
|
||||||
try:
|
try:
|
||||||
page = int(request.GET.get('page', 1))
|
page = int(request.GET.get('page', 1))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
page = 1
|
page = 1
|
||||||
|
|
||||||
shelves = []
|
count = 5
|
||||||
shelves = get_user_shelf_preview(
|
querysets = [
|
||||||
request.user,
|
# recemt currently reading
|
||||||
[('reading', 3), ('read', 1), ('to-read', 3)]
|
models.Edition.objects.filter(
|
||||||
)
|
shelves__user=request.user,
|
||||||
size = sum(len(s['books']) for s in shelves)
|
shelves__identifier='reading'
|
||||||
# books new to the instance, for discovery
|
),
|
||||||
if size < 6:
|
# read
|
||||||
recent_books = models.Work.objects.order_by(
|
models.Edition.objects.filter(
|
||||||
'-created_date'
|
shelves__user=request.user,
|
||||||
)[:6 - size]
|
shelves__identifier='read'
|
||||||
recent_books = [b.default_edition for b in recent_books]
|
)[:2],
|
||||||
shelves.append({
|
# to-read
|
||||||
'name': 'Recently added',
|
models.Edition.objects.filter(
|
||||||
'identifier': None,
|
shelves__user=request.user,
|
||||||
'books': recent_books,
|
shelves__identifier='to-read'
|
||||||
'count': 6 - size,
|
),
|
||||||
})
|
# popular books
|
||||||
|
models.Edition.objects.annotate(
|
||||||
|
shelf_count=Count('shelves')
|
||||||
|
).order_by('-shelf_count')
|
||||||
|
]
|
||||||
|
suggested_books = []
|
||||||
|
for queryset in querysets:
|
||||||
|
length = count - len(suggested_books)
|
||||||
|
suggested_books += list(queryset[:length])
|
||||||
|
if len(suggested_books) >= count:
|
||||||
|
break
|
||||||
|
|
||||||
|
activities = get_activity_feed(request.user, 'home')
|
||||||
# allows us to check if a user has shelved a book
|
|
||||||
user_books = models.Edition.objects.filter(shelves__user=request.user).all()
|
|
||||||
|
|
||||||
activities = get_activity_feed(request.user, tab)
|
|
||||||
|
|
||||||
activity_count = activities.count()
|
activity_count = activities.count()
|
||||||
activities = activities[(page - 1) * page_size:page * page_size]
|
activities = activities[(page - 1) * page_size:page * page_size]
|
||||||
|
|
||||||
next_page = '/?page=%d' % (page + 1)
|
next_page = '/?page=%d#feed' % (page + 1)
|
||||||
prev_page = '/?page=%d' % (page - 1)
|
prev_page = '/?page=%d#feed' % (page - 1)
|
||||||
data = {
|
data = {
|
||||||
'user': request.user,
|
'user': request.user,
|
||||||
'shelves': shelves,
|
'suggested_books': suggested_books,
|
||||||
'user_books': user_books,
|
|
||||||
'activities': activities,
|
'activities': activities,
|
||||||
'feed_tabs': [
|
|
||||||
{'id': 'home', 'display': 'Home'},
|
|
||||||
{'id': 'local', 'display': 'Local'},
|
|
||||||
{'id': 'federated', 'display': 'Federated'}
|
|
||||||
],
|
|
||||||
'active_tab': tab,
|
|
||||||
'review_form': forms.ReviewForm(),
|
'review_form': forms.ReviewForm(),
|
||||||
'quotation_form': forms.QuotationForm(),
|
'quotation_form': forms.QuotationForm(),
|
||||||
'comment_form': forms.CommentForm(),
|
'comment_form': forms.CommentForm(),
|
||||||
|
@ -147,9 +141,9 @@ def search(request):
|
||||||
query = request.GET.get('q')
|
query = request.GET.get('q')
|
||||||
if re.match(r'\w+@\w+.\w+', query):
|
if re.match(r'\w+@\w+.\w+', query):
|
||||||
# if something looks like a username, search with webfinger
|
# if something looks like a username, search with webfinger
|
||||||
results = [outgoing.handle_account_search(query)]
|
results = outgoing.handle_account_search(query)
|
||||||
return TemplateResponse(
|
return TemplateResponse(
|
||||||
request, 'user_results.html', {'results': results}
|
request, 'user_results.html', {'results': results, 'query': query}
|
||||||
)
|
)
|
||||||
|
|
||||||
# or just send the question over to book search
|
# or just send the question over to book search
|
||||||
|
@ -163,23 +157,6 @@ def search(request):
|
||||||
return TemplateResponse(request, 'book_results.html', {'results': results})
|
return TemplateResponse(request, 'book_results.html', {'results': results})
|
||||||
|
|
||||||
|
|
||||||
def books_page(request):
|
|
||||||
''' discover books '''
|
|
||||||
recent_books = models.Work.objects
|
|
||||||
recent_books = recent_books.order_by('-created_date')[:50]
|
|
||||||
recent_books = [b.default_edition for b in recent_books]
|
|
||||||
if request.user.is_authenticated:
|
|
||||||
recent_books = models.Edition.objects.filter(
|
|
||||||
~Q(shelfbook__shelf__user=request.user),
|
|
||||||
id__in=[b.id for b in recent_books if b],
|
|
||||||
)
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'books': recent_books,
|
|
||||||
}
|
|
||||||
return TemplateResponse(request, 'books.html', data)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def import_page(request):
|
def import_page(request):
|
||||||
''' import history from goodreads '''
|
''' import history from goodreads '''
|
||||||
|
@ -216,6 +193,16 @@ def login_page(request):
|
||||||
return TemplateResponse(request, 'login.html', data)
|
return TemplateResponse(request, 'login.html', data)
|
||||||
|
|
||||||
|
|
||||||
|
def register_page(request):
|
||||||
|
''' authentication '''
|
||||||
|
# send user to the login page
|
||||||
|
data = {
|
||||||
|
'site_settings': models.SiteSettings.get(),
|
||||||
|
'register_form': forms.RegisterForm(),
|
||||||
|
}
|
||||||
|
return TemplateResponse(request, 'register.html', data)
|
||||||
|
|
||||||
|
|
||||||
def about_page(request):
|
def about_page(request):
|
||||||
''' more information about the instance '''
|
''' more information about the instance '''
|
||||||
data = {
|
data = {
|
||||||
|
@ -276,8 +263,6 @@ def user_page(request, username, subpage=None):
|
||||||
return JsonResponse(user.to_activity(), encoder=ActivityEncoder)
|
return JsonResponse(user.to_activity(), encoder=ActivityEncoder)
|
||||||
# otherwise we're at a UI view
|
# otherwise we're at a UI view
|
||||||
|
|
||||||
# TODO: change display with privacy and authentication considerations
|
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'user': user,
|
'user': user,
|
||||||
'is_self': request.user.id == user.id,
|
'is_self': request.user.id == user.id,
|
||||||
|
@ -292,10 +277,22 @@ def user_page(request, username, subpage=None):
|
||||||
data['shelves'] = user.shelf_set.all()
|
data['shelves'] = user.shelf_set.all()
|
||||||
return TemplateResponse(request, 'user_shelves.html', data)
|
return TemplateResponse(request, 'user_shelves.html', data)
|
||||||
|
|
||||||
shelves = get_user_shelf_preview(user)
|
data['shelf_count'] = user.shelf_set.count()
|
||||||
|
shelves = []
|
||||||
|
for shelf in user.shelf_set.all():
|
||||||
|
if not shelf.books.count():
|
||||||
|
continue
|
||||||
|
shelves.append({
|
||||||
|
'name': shelf.name,
|
||||||
|
'remote_id': shelf.remote_id,
|
||||||
|
'books': shelf.books.all()[:3],
|
||||||
|
'size': shelf.books.count(),
|
||||||
|
})
|
||||||
|
if len(shelves) > 2:
|
||||||
|
break
|
||||||
|
|
||||||
data['shelves'] = shelves
|
data['shelves'] = shelves
|
||||||
activities = get_activity_feed(user, 'self')[:15]
|
data['activities'] = get_activity_feed(user, 'self')[:15]
|
||||||
data['activities'] = activities
|
|
||||||
return TemplateResponse(request, 'user.html', data)
|
return TemplateResponse(request, 'user.html', data)
|
||||||
|
|
||||||
|
|
||||||
|
@ -398,7 +395,7 @@ def edit_profile_page(request):
|
||||||
return TemplateResponse(request, 'edit_user.html', data)
|
return TemplateResponse(request, 'edit_user.html', data)
|
||||||
|
|
||||||
|
|
||||||
def book_page(request, book_id, tab='friends'):
|
def book_page(request, book_id):
|
||||||
''' info about a book '''
|
''' info about a book '''
|
||||||
book = models.Book.objects.select_subclasses().get(id=book_id)
|
book = models.Book.objects.select_subclasses().get(id=book_id)
|
||||||
if is_api_request(request):
|
if is_api_request(request):
|
||||||
|
@ -413,33 +410,15 @@ def book_page(request, book_id, tab='friends'):
|
||||||
if not work:
|
if not work:
|
||||||
return HttpResponseNotFound()
|
return HttpResponseNotFound()
|
||||||
|
|
||||||
book_reviews = models.Review.objects.filter(book__in=work.edition_set.all())
|
reviews = models.Review.objects.filter(
|
||||||
|
book__in=work.edition_set.all(),
|
||||||
|
).order_by('-published_date')
|
||||||
|
|
||||||
|
user_tags = []
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
user_reviews = book_reviews.filter(
|
|
||||||
user=request.user,
|
|
||||||
).all()
|
|
||||||
|
|
||||||
reviews = get_activity_feed(request.user, tab, model=book_reviews)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# TODO: books can be on multiple shelves
|
|
||||||
shelf = models.Shelf.objects.filter(
|
|
||||||
user=request.user,
|
|
||||||
edition=book
|
|
||||||
).first()
|
|
||||||
except models.Shelf.DoesNotExist:
|
|
||||||
shelf = None
|
|
||||||
|
|
||||||
user_tags = models.Tag.objects.filter(
|
user_tags = models.Tag.objects.filter(
|
||||||
book=book, user=request.user
|
book=book, user=request.user
|
||||||
).values_list('identifier', flat=True)
|
).values_list('identifier', flat=True)
|
||||||
else:
|
|
||||||
tab = 'public'
|
|
||||||
reviews = book_reviews.filter(privacy='public')
|
|
||||||
shelf = None
|
|
||||||
user_reviews = []
|
|
||||||
user_tags = []
|
|
||||||
|
|
||||||
rating = reviews.aggregate(Avg('rating'))
|
rating = reviews.aggregate(Avg('rating'))
|
||||||
tags = models.Tag.objects.filter(
|
tags = models.Tag.objects.filter(
|
||||||
|
@ -450,20 +429,15 @@ def book_page(request, book_id, tab='friends'):
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'book': book,
|
'book': book,
|
||||||
'shelf': shelf,
|
'reviews': reviews.filter(content__isnull=False),
|
||||||
'user_reviews': user_reviews,
|
'ratings': reviews.filter(content__isnull=True),
|
||||||
'reviews': reviews.distinct(),
|
|
||||||
'rating': rating['rating__avg'],
|
'rating': rating['rating__avg'],
|
||||||
'tags': tags,
|
'tags': tags,
|
||||||
'user_tags': user_tags,
|
'user_tags': user_tags,
|
||||||
'review_form': forms.ReviewForm(),
|
'review_form': forms.ReviewForm(),
|
||||||
|
'quotation_form': forms.QuotationForm(),
|
||||||
|
'comment_form': forms.CommentForm(),
|
||||||
'tag_form': forms.TagForm(),
|
'tag_form': forms.TagForm(),
|
||||||
'feed_tabs': [
|
|
||||||
{'id': 'friends', 'display': 'Friends'},
|
|
||||||
{'id': 'local', 'display': 'Local'},
|
|
||||||
{'id': 'federated', 'display': 'Federated'}
|
|
||||||
],
|
|
||||||
'active_tab': tab,
|
|
||||||
'path': '/book/%s' % book_id,
|
'path': '/book/%s' % book_id,
|
||||||
'cover_form': forms.CoverForm(instance=book),
|
'cover_form': forms.CoverForm(instance=book),
|
||||||
'info_fields': [
|
'info_fields': [
|
||||||
|
@ -555,42 +529,3 @@ def shelf_page(request, username, shelf_identifier):
|
||||||
'user': user,
|
'user': user,
|
||||||
}
|
}
|
||||||
return TemplateResponse(request, 'shelf.html', data)
|
return TemplateResponse(request, 'shelf.html', data)
|
||||||
|
|
||||||
|
|
||||||
def get_user_shelf_preview(user, shelf_proportions=None):
|
|
||||||
''' data for the covers shelf (user page and feed page) '''
|
|
||||||
shelves = []
|
|
||||||
shelf_max = 6
|
|
||||||
if not shelf_proportions:
|
|
||||||
shelf_proportions = [('reading', 3), ('read', 2), ('to-read', -1)]
|
|
||||||
for (identifier, count) in shelf_proportions:
|
|
||||||
if shelf_max <= 0:
|
|
||||||
break
|
|
||||||
if count > shelf_max or count < 0:
|
|
||||||
count = shelf_max
|
|
||||||
|
|
||||||
try:
|
|
||||||
shelf = models.Shelf.objects.get(
|
|
||||||
user=user,
|
|
||||||
identifier=identifier,
|
|
||||||
)
|
|
||||||
except models.Shelf.DoesNotExist:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not shelf.books.count():
|
|
||||||
continue
|
|
||||||
books = models.ShelfBook.objects.filter(
|
|
||||||
shelf=shelf,
|
|
||||||
).order_by(
|
|
||||||
'-updated_date'
|
|
||||||
)[:count]
|
|
||||||
|
|
||||||
shelf_max -= len(books)
|
|
||||||
|
|
||||||
shelves.append({
|
|
||||||
'name': shelf.name,
|
|
||||||
'identifier': shelf.identifier,
|
|
||||||
'books': [b.book for b in books],
|
|
||||||
'size': shelf.books.count(),
|
|
||||||
})
|
|
||||||
return shelves
|
|
||||||
|
|
3
fr-dev
3
fr-dev
|
@ -44,6 +44,9 @@ case "$1" in
|
||||||
test_report)
|
test_report)
|
||||||
docker-compose exec web coverage report
|
docker-compose exec web coverage report
|
||||||
;;
|
;;
|
||||||
|
collectstatic)
|
||||||
|
docker-compose exec web python manage.py collectstatic
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unrecognised command. Try: up, initdb, resetdb, makemigrations, migrate, shell, dbshell, restart_celery, test, test_report"
|
echo "Unrecognised command. Try: up, initdb, resetdb, makemigrations, migrate, shell, dbshell, restart_celery, test, test_report"
|
||||||
;;
|
;;
|
||||||
|
|
Loading…
Reference in a new issue