forked from mirrors/bookwyrm
Basic authentication views
This commit is contained in:
parent
4c4011ba75
commit
a312791259
13 changed files with 322 additions and 24 deletions
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 2.0.13 on 2020-01-25 21:32
|
# Generated by Django 2.0.13 on 2020-01-25 23:55
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import django.contrib.auth.models
|
import django.contrib.auth.models
|
||||||
|
@ -50,25 +50,36 @@ class Migration(migrations.Migration):
|
||||||
('objects', django.contrib.auth.models.UserManager()),
|
('objects', django.contrib.auth.models.UserManager()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Author',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('openlibary_key', models.CharField(max_length=255)),
|
||||||
|
('data', django.contrib.postgres.fields.jsonb.JSONField()),
|
||||||
|
('added_date', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_date', models.DateTimeField(auto_now=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Book',
|
name='Book',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('openlibary_key', models.CharField(max_length=255)),
|
('openlibary_key', models.CharField(max_length=255)),
|
||||||
('data', django.contrib.postgres.fields.jsonb.JSONField()),
|
('data', django.contrib.postgres.fields.jsonb.JSONField()),
|
||||||
('added_date', models.DateTimeField(auto_now_add=True)),
|
('added_date', models.DateTimeField(auto_now_add=True)),
|
||||||
('updated_date', models.DateTimeField(auto_now=True)),
|
('updated_date', models.DateTimeField(auto_now=True)),
|
||||||
('added_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
('added_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
||||||
|
('authors', models.ManyToManyField(to='fedireads.Author')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Review',
|
name='Review',
|
||||||
fields=[
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('name', models.CharField(max_length=255)),
|
('name', models.CharField(max_length=255)),
|
||||||
('content', django.contrib.postgres.fields.jsonb.JSONField(max_length=5000)),
|
('content', django.contrib.postgres.fields.jsonb.JSONField(max_length=5000)),
|
||||||
('created_date', models.DateTimeField(auto_now_add=True)),
|
('created_date', models.DateTimeField(auto_now_add=True)),
|
||||||
('updated_date', models.DateTimeField(auto_now=True)),
|
('updated_date', models.DateTimeField(auto_now=True)),
|
||||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
|
||||||
('star_rating', models.IntegerField(default=0)),
|
('star_rating', models.IntegerField(default=0)),
|
||||||
('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
||||||
('book', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='fedireads.Book')),
|
('book', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='fedireads.Book')),
|
||||||
|
@ -80,26 +91,44 @@ class Migration(migrations.Migration):
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Shelf',
|
name='Shelf',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('name', models.CharField(max_length=100)),
|
('name', models.CharField(max_length=100)),
|
||||||
('editable', models.BooleanField(default=True)),
|
('editable', models.BooleanField(default=True)),
|
||||||
|
('shelf_type', models.CharField(default='custom', max_length=100)),
|
||||||
('created_date', models.DateTimeField(auto_now_add=True)),
|
('created_date', models.DateTimeField(auto_now_add=True)),
|
||||||
('updated_date', models.DateTimeField(auto_now=True)),
|
('updated_date', models.DateTimeField(auto_now=True)),
|
||||||
('books', models.ManyToManyField(to='fedireads.Book')),
|
],
|
||||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ShelfBook',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('added_date', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('added_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
||||||
|
('book', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='fedireads.Book')),
|
||||||
|
('shelf', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='fedireads.Shelf')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Work',
|
name='Work',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('openlibary_key', models.CharField(max_length=255)),
|
('openlibary_key', models.CharField(max_length=255)),
|
||||||
('data', django.contrib.postgres.fields.jsonb.JSONField()),
|
('data', django.contrib.postgres.fields.jsonb.JSONField()),
|
||||||
('added_date', models.DateTimeField(auto_now_add=True)),
|
('added_date', models.DateTimeField(auto_now_add=True)),
|
||||||
('updated_date', models.DateTimeField(auto_now=True)),
|
('updated_date', models.DateTimeField(auto_now=True)),
|
||||||
('added_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='shelf',
|
||||||
|
name='books',
|
||||||
|
field=models.ManyToManyField(through='fedireads.ShelfBook', to='fedireads.Book'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='shelf',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='book',
|
model_name='book',
|
||||||
name='works',
|
name='works',
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
''' database schema for the whole dang thing '''
|
''' database schema for the whole dang thing '''
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.dispatch import receiver
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
from django.contrib.postgres.fields import JSONField
|
from django.contrib.postgres.fields import JSONField
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
|
@ -28,10 +29,27 @@ class User(AbstractUser):
|
||||||
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
@receiver(models.signals.post_save, sender=User)
|
||||||
|
def execute_after_save(sender, instance, created, *args, **kwargs):
|
||||||
|
if not created:
|
||||||
|
return
|
||||||
|
shelves = [{
|
||||||
|
'name': 'To Read',
|
||||||
|
'type': 'to-read',
|
||||||
|
}, {
|
||||||
|
'name': 'Currently Reading',
|
||||||
|
'type': 'reading',
|
||||||
|
}, {
|
||||||
|
'name': 'Read',
|
||||||
|
'type': 'read',
|
||||||
|
}]
|
||||||
|
|
||||||
|
for shelf in shelves:
|
||||||
|
Shelf(name=shelf['name'], shelf_type=shelf['type'], user=instance, editable=False).save()
|
||||||
|
|
||||||
|
|
||||||
class Message(models.Model):
|
class Message(models.Model):
|
||||||
''' any kind of user post, incl. reviews, replies, and status updates '''
|
''' any kind of user post, incl. reviews, replies, and status updates '''
|
||||||
id = models.AutoField(primary_key=True)
|
|
||||||
author = models.ForeignKey('User', on_delete=models.PROTECT)
|
author = models.ForeignKey('User', on_delete=models.PROTECT)
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
content = JSONField(max_length=5000)
|
content = JSONField(max_length=5000)
|
||||||
|
@ -43,35 +61,52 @@ class Message(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Review(Message):
|
class Review(Message):
|
||||||
id = models.AutoField(primary_key=True)
|
|
||||||
book = models.ForeignKey('Book', on_delete=models.PROTECT)
|
book = models.ForeignKey('Book', on_delete=models.PROTECT)
|
||||||
star_rating = models.IntegerField(default=0)
|
star_rating = models.IntegerField(default=0)
|
||||||
|
|
||||||
|
|
||||||
class Shelf(models.Model):
|
class Shelf(models.Model):
|
||||||
id = models.AutoField(primary_key=True)
|
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
user = models.ForeignKey('User', on_delete=models.PROTECT)
|
user = models.ForeignKey('User', on_delete=models.PROTECT)
|
||||||
editable = models.BooleanField(default=True)
|
editable = models.BooleanField(default=True)
|
||||||
books = models.ManyToManyField('Book', symmetrical=False)
|
shelf_type = models.CharField(default='custom', max_length=100)
|
||||||
|
books = models.ManyToManyField(
|
||||||
|
'Book',
|
||||||
|
symmetrical=False,
|
||||||
|
through='ShelfBook',
|
||||||
|
through_fields=('shelf', 'book')
|
||||||
|
)
|
||||||
created_date = models.DateTimeField(auto_now_add=True)
|
created_date = models.DateTimeField(auto_now_add=True)
|
||||||
updated_date = models.DateTimeField(auto_now=True)
|
updated_date = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
|
||||||
|
class ShelfBook(models.Model):
|
||||||
|
# many to many join table for books and shelves
|
||||||
|
book = models.ForeignKey('Book', on_delete=models.PROTECT)
|
||||||
|
shelf = models.ForeignKey('Shelf', on_delete=models.PROTECT)
|
||||||
|
added_by = models.ForeignKey('User', blank=True, null=True, on_delete=models.PROTECT)
|
||||||
|
added_date = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
|
||||||
class Book(models.Model):
|
class Book(models.Model):
|
||||||
''' a non-canonical copy from open library '''
|
''' a non-canonical copy from open library '''
|
||||||
id = models.AutoField(primary_key=True)
|
|
||||||
openlibary_key = models.CharField(max_length=255)
|
openlibary_key = models.CharField(max_length=255)
|
||||||
data = JSONField()
|
data = JSONField()
|
||||||
works = models.ManyToManyField('Work')
|
works = models.ManyToManyField('Work')
|
||||||
|
authors = models.ManyToManyField('Author')
|
||||||
added_by = models.ForeignKey('User', on_delete=models.PROTECT, blank=True, null=True)
|
added_by = models.ForeignKey('User', on_delete=models.PROTECT, blank=True, null=True)
|
||||||
added_date = models.DateTimeField(auto_now_add=True)
|
added_date = models.DateTimeField(auto_now_add=True)
|
||||||
updated_date = models.DateTimeField(auto_now=True)
|
updated_date = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
class Work(models.Model):
|
class Work(models.Model):
|
||||||
id = models.AutoField(primary_key=True)
|
|
||||||
openlibary_key = models.CharField(max_length=255)
|
openlibary_key = models.CharField(max_length=255)
|
||||||
data = JSONField()
|
data = JSONField()
|
||||||
added_by = models.ForeignKey('User', on_delete=models.PROTECT, blank=True, null=True)
|
|
||||||
added_date = models.DateTimeField(auto_now_add=True)
|
added_date = models.DateTimeField(auto_now_add=True)
|
||||||
updated_date = models.DateTimeField(auto_now=True)
|
updated_date = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
class Author(models.Model):
|
||||||
|
openlibary_key = models.CharField(max_length=255)
|
||||||
|
data = JSONField()
|
||||||
|
added_date = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_date = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
|
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.core import serializers
|
from django.core import serializers
|
||||||
from fedireads.models import Book, Work
|
from fedireads.models import Author, Book, Work
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
openlibrary_url = 'https://openlibrary.org'
|
openlibrary_url = 'https://openlibrary.org'
|
||||||
|
@ -22,15 +22,28 @@ def get_book(request, olkey):
|
||||||
for work_id in data['works']:
|
for work_id in data['works']:
|
||||||
work_id = work_id['key']
|
work_id = work_id['key']
|
||||||
book.works.add(get_or_create_work(work_id))
|
book.works.add(get_or_create_work(work_id))
|
||||||
|
for author_id in data['authors']:
|
||||||
|
author_id = author_id['key']
|
||||||
|
book.authors.add(get_or_create_author(author_id))
|
||||||
return HttpResponse(serializers.serialize('json', [book]))
|
return HttpResponse(serializers.serialize('json', [book]))
|
||||||
|
|
||||||
def get_or_create_work(olkey):
|
def get_or_create_work(olkey):
|
||||||
try:
|
try:
|
||||||
work = Work.objects.get(openlibary_key=olkey)
|
work = Work.objects.get(openlibary_key=olkey)
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
response = requests.get(openlibrary_url + '/work/' + olkey +'.json')
|
response = requests.get(openlibrary_url + olkey + '.json')
|
||||||
data = response.json()
|
data = response.json()
|
||||||
work = Work(openlibary_key=olkey, data=data)
|
work = Work(openlibary_key=olkey, data=data)
|
||||||
work.save()
|
work.save()
|
||||||
return work
|
return work
|
||||||
|
|
||||||
|
def get_or_create_author(olkey):
|
||||||
|
try:
|
||||||
|
author = Author.objects.get(openlibary_key=olkey)
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
response = requests.get(openlibrary_url + olkey + '.json')
|
||||||
|
data = response.json()
|
||||||
|
author = Author(openlibary_key=olkey, data=data)
|
||||||
|
author.save()
|
||||||
|
return author
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ ROOT_URLCONF = 'fedireads.urls'
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': [],
|
'DIRS': ['templates'],
|
||||||
'APP_DIRS': True,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'context_processors': [
|
'context_processors': [
|
||||||
|
@ -68,6 +68,7 @@ TEMPLATES = [
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
WSGI_APPLICATION = 'fedireads.wsgi.application'
|
WSGI_APPLICATION = 'fedireads.wsgi.application'
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,6 +86,7 @@ DATABASES = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOGIN_URL = 'login/'
|
||||||
AUTH_USER_MODEL = 'fedireads.User'
|
AUTH_USER_MODEL = 'fedireads.User'
|
||||||
|
|
||||||
# Password validation
|
# Password validation
|
||||||
|
|
69
fedireads/static/format.css
Normal file
69
fedireads/static/format.css
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1.3em;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > * > * {
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 1rem;
|
||||||
|
max-width: 75rem;
|
||||||
|
min-width: 30rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#top-bar {
|
||||||
|
height: 4rem;
|
||||||
|
border-bottom: 1px solid #aaa;
|
||||||
|
box-shadow: 0 0.5em 0.5em -0.6em #666;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#branding {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > div:first-child {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > div:last-child {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar {
|
||||||
|
width: 30%;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-pic {
|
||||||
|
width: 2em;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 50%;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-preview {
|
||||||
|
overflow: auto;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-preview img {
|
||||||
|
float: left;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update {
|
||||||
|
border: 1px solid #333;
|
||||||
|
border-radius: 0.2rem;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update > * {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interact {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
BIN
fedireads/static/images/med.jpg
Normal file
BIN
fedireads/static/images/med.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
fedireads/static/images/profile.jpg
Normal file
BIN
fedireads/static/images/profile.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
fedireads/static/images/small.jpg
Normal file
BIN
fedireads/static/images/small.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
51
fedireads/templates/feed.html
Normal file
51
fedireads/templates/feed.html
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
{% extends 'layout.html' %}
|
||||||
|
{% block content %}
|
||||||
|
<div id="sidebar">
|
||||||
|
{% for shelf in shelves %}
|
||||||
|
<h1>{{ shelf.name }}</h1>
|
||||||
|
{% for book in shelf.books.all %}
|
||||||
|
<div class="book-preview">
|
||||||
|
<img class="cover" src="static/images/small.jpg">
|
||||||
|
<p class="title">{{ book.data.title }}</p>
|
||||||
|
<p>by <a href="" class="author">{{ book.authors.first.data.name }}</a></p>
|
||||||
|
{% if shelf.type == 'reading' %}
|
||||||
|
<button>Done reading</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="main">
|
||||||
|
<div class="update">
|
||||||
|
<div class="user-preview">
|
||||||
|
<img class="user-pic" src="static/images/profile.jpg">
|
||||||
|
<span><a href="" class="user">Mouse</a> is currently reading</span>
|
||||||
|
</div>
|
||||||
|
<div class="book-preview">
|
||||||
|
<img class="cover" src="static/images/med.jpg">
|
||||||
|
<p class="title">Moby Dick</p>
|
||||||
|
<p>by <a href="" class="author">Herman Melville</a></p>
|
||||||
|
<p>"Command the murderous chalices! Drink ye harpooners! Drink and swear, ye men that man the deathful whaleboat's bow -- Death to Moby Dick!" So Captain Ahab binds his crew to fulfil his obsession -- the destruction of the great white whale. Under his lordly but maniacal command the Pequod's commercial mission is perverted to one of vengeance...</p>
|
||||||
|
</div>
|
||||||
|
<div class="interact">
|
||||||
|
<span>⭐️ Like</span>
|
||||||
|
<span>💬 <input type="text"></input></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="update">
|
||||||
|
<img class="user-pic" src="static/images/profile.jpg">
|
||||||
|
<span><a href="" class="user">Mouse</a> is currently reading</span>
|
||||||
|
<div class="book-preview">
|
||||||
|
<img class="cover" src="static/images/med.jpg">
|
||||||
|
<p class="title">Moby Dick</p>
|
||||||
|
<p>by <a href="" class="author">Herman Melville</a></p>
|
||||||
|
<p>"Command the murderous chalices! Drink ye harpooners! Drink and swear, ye men that man the deathful whaleboat's bow -- Death to Moby Dick!" So Captain Ahab binds his crew to fulfil his obsession -- the destruction of the great white whale. Under his lordly but maniacal command the Pequod's commercial mission is perverted to one of vengeance...</p>
|
||||||
|
</div>
|
||||||
|
<div class="interact">
|
||||||
|
<span>⭐️ Like</span>
|
||||||
|
<span>💬 <input type="text"></input></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
60
fedireads/templates/layout.html
Normal file
60
fedireads/templates/layout.html
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>FediReads</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link type="text/css" rel="stylesheet" href="/static/format.css">
|
||||||
|
|
||||||
|
<link rel="shortcut icon" type="image/x-icon" href="/static/images/favicon.ico">
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary">
|
||||||
|
<meta name="twitter:title" content="FediReads">
|
||||||
|
<meta name="og:title" content="FediReads">
|
||||||
|
<meta name="twitter:description" content="Federated Social Reading">
|
||||||
|
<meta name="og:description" content="Federated Social Reading">
|
||||||
|
<meta name="twitter:creator" content="@tripofmice">
|
||||||
|
<meta name="twitter:site" content="@tripofmice">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="top-bar">
|
||||||
|
<header>
|
||||||
|
<div id="branding">📚FediReads</div>
|
||||||
|
<div>
|
||||||
|
<div id="account">
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
<form name="logout" action="/logout/" method="post">
|
||||||
|
Welcome, {{ user.username }}
|
||||||
|
<input type="submit" value="Log out"></input>
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<form name="login" action="/login/" method="post">
|
||||||
|
<input type="text" name="username"></input>
|
||||||
|
<input type="password" name="password"></input>
|
||||||
|
<input type="submit" value="Log in"></input>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div id="search">
|
||||||
|
<form action="search">
|
||||||
|
<input type="text" name="q"></input>
|
||||||
|
<input type="submit" value="🔍"></input>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
<div>
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
8
fedireads/templates/login.html
Normal file
8
fedireads/templates/login.html
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{% extends 'layout.html' %}
|
||||||
|
{% block content %}
|
||||||
|
<form name="login" method="post">
|
||||||
|
<input type="text" name="username"></input>
|
||||||
|
<input type="password" name="password"></input>
|
||||||
|
<input type="submit"></input>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
|
@ -19,6 +19,9 @@ from fedireads import activitystream, openlibrary, views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
|
path('', views.home),
|
||||||
|
path('login/', views.user_login),
|
||||||
|
path('logout/', views.user_logout),
|
||||||
path('api/book/<str:olkey>', openlibrary.get_book),
|
path('api/book/<str:olkey>', openlibrary.get_book),
|
||||||
path('webfinger/', activitystream.webfinger),
|
path('webfinger/', activitystream.webfinger),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,11 +1,39 @@
|
||||||
|
''' application views/pages '''
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.contrib.auth import authenticate, login, logout
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from django.template.response import TemplateResponse
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
from fedireads.models import Shelf
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def account_page(request):
|
def home(request):
|
||||||
return 'hi'
|
''' user feed '''
|
||||||
|
shelves = Shelf.objects.filter(user=request.user.id)
|
||||||
|
data = {
|
||||||
|
'user': request.user,
|
||||||
|
'shelves': shelves,
|
||||||
|
}
|
||||||
|
return TemplateResponse(request, 'feed.html', data)
|
||||||
|
|
||||||
def webfinger(request):
|
@csrf_exempt
|
||||||
return 'hello'
|
def user_login(request):
|
||||||
|
''' authentication '''
|
||||||
|
# send user to the login page
|
||||||
|
if request.method == 'GET':
|
||||||
|
return TemplateResponse(request, 'login.html')
|
||||||
|
|
||||||
def api(request):
|
# authenticate user
|
||||||
return 'hey'
|
username = request.POST['username']
|
||||||
|
password = request.POST['password']
|
||||||
|
user = authenticate(request, username=username, password=password)
|
||||||
|
if user is not None:
|
||||||
|
login(request, user)
|
||||||
|
return redirect(request.GET.get('next', '/'))
|
||||||
|
return TemplateResponse(request, 'login.html')
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
@login_required
|
||||||
|
def user_logout(request):
|
||||||
|
logout(request)
|
||||||
|
return redirect('/')
|
||||||
|
|
Loading…
Reference in a new issue