Compare commits
18 commits
Author | SHA1 | Date | |
---|---|---|---|
Danielle McLean | 5348dc9f82 | ||
Danielle McLean | e36ad27d49 | ||
Danielle McLean | d21d4bda83 | ||
Danielle McLean | 8d8aa4749b | ||
Danielle McLean | 3baf75e59e | ||
Danielle McLean | 880b899e81 | ||
Danielle McLean | 6061d6f600 | ||
Danielle McLean | a680a6501c | ||
Danielle McLean | 625b5d963a | ||
Danielle McLean | 9d11cc7576 | ||
Danielle McLean | c49e17db90 | ||
Danielle McLean | 7696ff45db | ||
Danielle McLean | 731f177d18 | ||
Danielle McLean | 0061111ad8 | ||
Danielle McLean | 6b53c00d7c | ||
Danielle McLean | 1490a95735 | ||
Danielle McLean | c398b0d3f4 | ||
Danielle McLean | 95cca433bc |
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -1,4 +1,3 @@
|
|||
|
||||
# Created by https://www.gitignore.io/api/django
|
||||
|
||||
### Django ###
|
||||
|
@ -15,9 +14,10 @@ media
|
|||
# <django-project-name>/staticfiles/
|
||||
|
||||
# End of https://www.gitignore.io/api/django
|
||||
|
||||
/.pdm-python
|
||||
/.env
|
||||
/.mypy_cache
|
||||
/.pytest_cache
|
||||
/*.egg-info/
|
||||
/static
|
||||
node_modules
|
||||
/node_modules
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
image: python:3.6
|
||||
services:
|
||||
- postgres:latest
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: normal
|
||||
PIP_CACHE_DIR: $CI_PROJECT_DIR/.cache/pip
|
||||
PIPENV_CACHE_DIR: $CI_PROJECT_DIR/.cache/pipenv
|
||||
POSTGRES_HOST: postgres
|
||||
POSTGRES_DB: nice_marmot
|
||||
POSTGRES_USER: runner
|
||||
POSTGRES_PASSWORD: ''
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- .cache
|
||||
|
||||
test:
|
||||
script:
|
||||
- pip install pipenv
|
||||
- pipenv sync --dev
|
||||
- pipenv run pytest
|
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -1,3 +1,3 @@
|
|||
[submodule "lemoncurry/static/base16-materialtheme-scheme"]
|
||||
path = lemoncurry/static/base16-materialtheme-scheme
|
||||
url = git://github.com/ntpeters/base16-materialtheme-scheme.git
|
||||
url = https://github.com/ntpeters/base16-materialtheme-scheme
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: check-ast
|
||||
- id: check-builtin-literals
|
||||
- id: check-case-conflict
|
||||
- id: check-docstring-first
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-json
|
||||
- id: check-merge-conflict
|
||||
- id: check-symlinks
|
||||
- id: check-toml
|
||||
- id: check-vcs-permalinks
|
||||
- id: check-yaml
|
||||
- id: destroyed-symlinks
|
||||
- id: end-of-file-fixer
|
||||
- id: fix-byte-order-marker
|
||||
- id: mixed-line-ending
|
||||
args:
|
||||
- --fix=lf
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.7.0
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3.11
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: pytest
|
||||
name: Check pytest unit tests pass
|
||||
entry: poetry run pytest
|
||||
pass_filenames: false
|
||||
language: system
|
||||
types: [python]
|
||||
- id: mypy
|
||||
name: Check mypy static types match
|
||||
entry: poetry run mypy . --ignore-missing-imports
|
||||
pass_filenames: false
|
||||
language: system
|
||||
types: [python]
|
16
.travis.yml
16
.travis.yml
|
@ -1,16 +0,0 @@
|
|||
language: python
|
||||
cache:
|
||||
directories:
|
||||
- $PIP_CACHE_DIR
|
||||
- $PIPENV_CACHE_DIR
|
||||
env:
|
||||
global:
|
||||
- PIP_CACHE_DIR=$HOME/.cache/pip
|
||||
- PIPENV_CACHE_DIR=$HOME/.cache/pipenv
|
||||
python:
|
||||
- '3.6'
|
||||
install:
|
||||
- pip install pipenv
|
||||
- pipenv install --dev
|
||||
script:
|
||||
- pipenv run pytest
|
|
@ -1,4 +0,0 @@
|
|||
# vim: set ft=yaml :
|
||||
host: 00dani.dev
|
||||
port: 443
|
||||
cname: dev.00dani.me
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 - 2018 Danielle McLean
|
||||
Copyright (c) 2017 - 2024 Danielle McLean
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
{% extends 'lemoncurry/layout.html' %}
|
||||
{% block head %}
|
||||
<link rel="shortlink" href="{{ entry.short_url }}" />
|
||||
<link rel="alternate" type="application/json+oembed" href="https://wirres.net/oembed/oembed/php?url={{ entry.absolute_url | urlencode }}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<article class="h-entry media">
|
||||
{{i}}<aside class="info">
|
||||
{{i}}<a class="p-author h-card" href="{{ entry.author.url }}">
|
||||
{{i}}<img class="u-photo img-fluid" src="{{ entry.author.avatar.url }}" alt="{{ entry.author.name }}" />
|
||||
{{i}}{% if entry.author.avatar %}<img class="u-photo img-fluid" src="{{ entry.author.avatar.url }}" alt="{{ entry.author.name }}" />{% endif %}
|
||||
{{i}}<span class="p-name sr-only">{{ entry.author.name }}</span>
|
||||
{{i}}</a>
|
||||
{{i}}<a class="u-uid u-url" href="{{ entry.url }}">
|
||||
|
|
|
@ -1,35 +1,32 @@
|
|||
from django.core.paginator import Paginator
|
||||
from typing import Callable
|
||||
|
||||
from django.core.paginator import Page, Paginator
|
||||
from django.shortcuts import redirect
|
||||
|
||||
from lemoncurry.middleware import ResponseException
|
||||
|
||||
|
||||
def paginate(queryset, reverse, page):
|
||||
class Page:
|
||||
def __init__(self, i):
|
||||
self.i = i
|
||||
def paginate(queryset, reverse: Callable[[int], str], page: int | None) -> Page:
|
||||
def redirect_to_page(i: int):
|
||||
raise ResponseException(redirect(reverse(i)))
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return reverse(self.i)
|
||||
|
||||
@property
|
||||
def current(self):
|
||||
return self.i == entries.number
|
||||
|
||||
# If the first page was requested, redirect to the clean version of the URL
|
||||
# with no page suffix.
|
||||
if page == 1:
|
||||
raise ResponseException(redirect(Page(1).url))
|
||||
def reversible(p: Page) -> Page:
|
||||
p.reverse = reverse
|
||||
return p
|
||||
|
||||
paginator = Paginator(queryset, 10)
|
||||
entries = paginator.page(page or 1)
|
||||
|
||||
entries.pages = tuple(Page(i) for i in paginator.page_range)
|
||||
# If no page number was specified, return page one.
|
||||
if page is None:
|
||||
return reversible(paginator.page(1))
|
||||
|
||||
if entries.has_previous():
|
||||
entries.prev = Page(entries.previous_page_number())
|
||||
if entries.has_next():
|
||||
entries.next = Page(entries.next_page_number())
|
||||
# If the first page was explicitly requested, or the page number was negative, redirect to page one with no URL suffix.
|
||||
if page <= 1:
|
||||
redirect_to_page(1)
|
||||
|
||||
return entries
|
||||
# If the page requested is larger than the last page, then redirect to the last page.
|
||||
if page > paginator.num_pages:
|
||||
redirect_to_page(paginator.num_pages)
|
||||
|
||||
# Just return the current page! Hooray!
|
||||
return reversible(paginator.page(page))
|
||||
|
|
|
@ -62,17 +62,19 @@
|
|||
</div>
|
||||
|
||||
<div id="verified-success" hidden>
|
||||
this client has been <strong>verified</strong> using <code>{{ '<link rel="redirect_uri">' | escape }}</code> - they are who they claim to be!
|
||||
this client has been <strong>verified</strong> using <code>{{ '<link rel="redirect_uri">' | escape }}</code> <br/>- they are who they claim to be!
|
||||
</div>
|
||||
<div id="verified-warning" hidden>
|
||||
this client could <strong>not</strong> be verified using <code>{{ '<link rel="redirect_uri">' | escape }}</code> - check the redirect uri carefully yourself!
|
||||
this client could <strong>not</strong> be verified using <code>{{ '<link rel="redirect_uri">' | escape }}</code> <br/>- check the redirect uri carefully yourself!
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block foot %}
|
||||
<script type="text/javascript">
|
||||
tippy('[data-tippy-theme]', {
|
||||
tippy('[data-tippy-html]', {
|
||||
arrow: true,
|
||||
allowHTML: true,
|
||||
maxWidth: 500,
|
||||
content: function(element) {
|
||||
return document.querySelector(element.getAttribute('data-tippy-html')).innerHTML;
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
img
|
||||
height 2em
|
||||
margin-right .5em
|
||||
.tippy-tooltip
|
||||
&.success-theme
|
||||
.tippy-box
|
||||
&[data-theme~='success']
|
||||
color $base0B
|
||||
&.warning-theme
|
||||
&[data-theme~='warning']
|
||||
color $base0A
|
||||
.verified-success
|
||||
color $base0B
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from requests import HTTPError
|
||||
from requests.exceptions import RequestException
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.views.generic import TemplateView
|
||||
from typing import Dict, Optional, Set
|
||||
|
@ -24,7 +24,7 @@ class Client:
|
|||
try:
|
||||
apps = mf2(self.id).to_dict(filter_by_type="h-x-app")
|
||||
self.app = apps[0]["properties"]
|
||||
except (HTTPError, IndexError):
|
||||
except (RequestException, IndexError):
|
||||
self.app = None
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!doctype html>
|
||||
<html{% block html_attr %} dir="ltr" lang="en"{% endblock %}>
|
||||
<html{% block html_attr %} dir="ltr" lang="en" data-bs-theme="dark"{% endblock %}>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
@ -27,10 +27,10 @@
|
|||
<link rel="{{ i.rel }}" type="{{ i.mime }}" sizes="{{ i.sizes }}" href="{{ i.url }}" />
|
||||
{% endfor %}
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
||||
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/monokai.min.css"
|
||||
integrity="sha384-bHqbpRh/XW+phptvH9nQvMKHwPH1ZbOxpIeAB2D2OIEL4Ni7aZzZgMFpsRra+v1g" crossorigin="anonymous">
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
|
||||
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/@highlightjs/cdn-assets@11.9.0/styles/monokai.min.css"
|
||||
integrity="sha384-88Jvj9Q2LiBDwL7w3yciRTcH5q2zzvMFYIm4xX9/evqxJsxA33Xk9XYKcvUlPITo" crossorigin="anonymous">
|
||||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/openwebicons@1.6.0/css/openwebicons.min.css"
|
||||
integrity="sha384-NkRWM9o4Kfak7GwS+un+sProBBpj02vc/e1EoXvdCUSdRk0muOfkKJ5NtpueAuka" crossorigin="anonymous">
|
||||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/tippy.js@3.4.1/dist/tippy.css"
|
||||
|
@ -40,12 +40,11 @@
|
|||
{% block styles %}{% endblock %}
|
||||
{% endcompress %}
|
||||
|
||||
<script type="text/javascript" defer src="https://use.fontawesome.com/releases/v5.8.1/js/all.js"
|
||||
integrity="sha384-g5uSoOSBd7KkhAMlnQILrecXvzst9TdC09/VM+pjDTCM+1il8RHz5fKANTFFb+gQ" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" defer src="https://kit.fontawesome.com/a3aade9b41.js" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
<body{% block body_attr %}{% endblock %}>
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-md navbar-dark">
|
||||
<nav class="navbar navbar-expand-md"><div class="container-fluid">
|
||||
<a class="navbar-brand" rel="home" href="{{ url('home:index') }}">{{ request.site.name }}</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar"
|
||||
aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
|
@ -97,7 +96,7 @@
|
|||
</div>
|
||||
{% endactiveurl %}
|
||||
|
||||
</nav>
|
||||
</div></nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
|
@ -111,29 +110,29 @@
|
|||
|
||||
<nav>
|
||||
<ul class="pagination">
|
||||
{% if entries.prev %}
|
||||
{% if entries.has_previous() %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" rel="prev" href="{{ entries.prev.url }}">
|
||||
<a class="page-link" rel="prev" href="{{ entries.reverse(entries.previous_page_number()) }}">
|
||||
<i class="fas fa-step-backward" aria-hidden="true"></i> <span class="sr-only">previous page</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% endif %}
|
||||
{% for page in entries.pages %}
|
||||
{% if page.current %}
|
||||
{% for i in entries.paginator.page_range %}
|
||||
{% if i == entries.number %}
|
||||
<li class="page-item active">
|
||||
<span class="page-link">{{ page.i }} <span class="sr-only">(current page)</span></span>
|
||||
<span class="page-link">{{ i }} <span class="sr-only">(current page)</span></span>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ page.url }}">{{ page.i }}</a>
|
||||
<a class="page-link" href="{{ entries.reverse(i) }}">{{ i }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if entries.next %}
|
||||
{% if entries.has_next() %}
|
||||
|
||||
<li class="page-item">
|
||||
<a class="page-link" rel="next" href="{{ entries.next.url }}">
|
||||
<a class="page-link" rel="next" href="{{ entries.reverse(entries.next_page_number()) }}">
|
||||
<i class="fas fa-step-forward" aria-hidden="true"></i> <span class="sr-only">next page</span>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -147,14 +146,14 @@
|
|||
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"
|
||||
integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" crossorigin="anonymous"
|
||||
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" crossorigin="anonymous"
|
||||
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/highlight.min.js" crossorigin="anonymous"
|
||||
integrity="sha384-BlPof9RtjBqeJFskKv3sK3dh4Wk70iKlpIe92FeVN+6qxaGUOUu+mZNpALZ+K7ya"></script>
|
||||
<script src="https://unpkg.com/tippy.js@3.4.1/dist/tippy.standalone.min.js" crossorigin="anonymous"
|
||||
integrity="sha384-x7dGoSfOWUdyPccAel9dkWte6n8GxDWbByavEixRzW0O9xvPGzg3y0qzZBwGNUw9"></script>
|
||||
<script src="https://unpkg.com/@highlightjs/cdn-assets@11.9.0/highlight.min.js" crossorigin="anonymous"
|
||||
integrity="sha384-F/bZzf7p3Joyp5psL90p/p89AZJsndkSoGwRpXcZhleCWhd8SnRuoYo4d0yirjJp"></script>
|
||||
<script src="https://unpkg.com/@popperjs/core@2.11.8/dist/umd/popper.min.js" crossorigin="anonymous"
|
||||
integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js" crossorigin="anonymous"
|
||||
integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy"></script>
|
||||
<script src="https://unpkg.com/tippy.js@6.3.7/dist/tippy-bundle.umd.js" crossorigin="anonymous"
|
||||
integrity="sha384-dtMr4wkcxQWUqsJFgElu4AttgIhOsjr2vYIzP2mv0MZbD/uJ6OHxFdbgE3MOKabN"></script>
|
||||
{% compress js %}
|
||||
<script type="text/javascript">
|
||||
hljs.initHighlightingOnLoad();
|
||||
|
|
|
@ -75,7 +75,6 @@ INSTALLED_APPS = [
|
|||
"django.contrib.sitemaps",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"analytical",
|
||||
"annoying",
|
||||
"compressor",
|
||||
"computed_property",
|
||||
|
@ -154,7 +153,6 @@ CACHES = {
|
|||
"LOCATION": "redis://127.0.0.1:6380/0",
|
||||
"KEY_PREFIX": "lemoncurry",
|
||||
"OPTIONS": {
|
||||
"PARSER_CLASS": "redis.connection.HiredisParser",
|
||||
"SERIALIZER": "lemoncurry.msgpack.MSGPackModernSerializer",
|
||||
},
|
||||
"VERSION": 2,
|
||||
|
|
|
@ -5,6 +5,7 @@ html
|
|||
|
||||
a
|
||||
color $base0D
|
||||
text-decoration none
|
||||
&:hover
|
||||
color $base0C
|
||||
|
||||
|
@ -31,13 +32,13 @@ code, pre, .code, .pre
|
|||
text-decoration none
|
||||
line-height 1
|
||||
|
||||
for placement in top bottom left right
|
||||
.tippy-popper[x-placement^={placement}] .tippy-tooltip.dark-theme .tippy-arrow
|
||||
border-{placement}-color $base03
|
||||
|
||||
.tippy-tooltip.dark-theme
|
||||
.tippy-box[data-theme~='dark']
|
||||
background-color $base03
|
||||
color $base04
|
||||
text-align center
|
||||
for placement in top bottom left right
|
||||
&[data-placement^={placement}] > .tippy-arrow::before
|
||||
border-{placement}-color $base03
|
||||
|
||||
body
|
||||
display flex
|
||||
|
@ -103,6 +104,12 @@ ul.pagination
|
|||
background-color $base02
|
||||
border 1px solid rgba(0,0,0,.125)
|
||||
|
||||
.media
|
||||
display flex
|
||||
> .media-body
|
||||
flex-grow 1
|
||||
margin-left 3px
|
||||
|
||||
.card
|
||||
background-color $base02
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "lemoncurry",
|
||||
"version": "1.12.3",
|
||||
"version": "1.12.4",
|
||||
"repository": "https://git.00dani.me/00dani/lemoncurry",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
|
|
2380
poetry.lock
generated
2380
poetry.lock
generated
File diff suppressed because it is too large
Load diff
133
pyproject.toml
133
pyproject.toml
|
@ -1,67 +1,78 @@
|
|||
[tool.poetry]
|
||||
[project]
|
||||
name = "lemoncurry"
|
||||
version = "1.12.3"
|
||||
version = "1.12.5"
|
||||
description = "Indieweb-compatible personal website"
|
||||
authors = ["Danielle McLean <dani@00dani.me>"]
|
||||
license = "MIT"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.9"
|
||||
accept-types = "*"
|
||||
ago = "*"
|
||||
argon2-cffi = "*"
|
||||
bleach = "*"
|
||||
cachecontrol = "*"
|
||||
django = "<4,>=3"
|
||||
django-activeurl = "*"
|
||||
django-analytical = "*"
|
||||
django-annoying = "*"
|
||||
django-compressor = "*"
|
||||
django-computed-property = "*"
|
||||
django-cors-headers = "*"
|
||||
django-debug-toolbar = "*"
|
||||
django-extensions = "*"
|
||||
django-meta = "*"
|
||||
django-model-utils = "*"
|
||||
django-otp = "*"
|
||||
django-otp-agents = "*"
|
||||
django-push = "*"
|
||||
django-randomslugfield = "*"
|
||||
django-redis = "*"
|
||||
django-rq = "*"
|
||||
docutils = "*"
|
||||
gevent = "*"
|
||||
gunicorn = {extras = ["gevent"], version = "*"}
|
||||
hiredis = "*"
|
||||
jinja2 = "*"
|
||||
markdown = "*"
|
||||
mf2py = "*"
|
||||
mf2util = "*"
|
||||
msgpack = "*"
|
||||
pillow = "*"
|
||||
psycopg2-binary = "*"
|
||||
python-baseconv = "*"
|
||||
python-magic = "*"
|
||||
python-slugify = "*"
|
||||
pyup-django = "*"
|
||||
pyyaml = "*"
|
||||
qrcode = "*"
|
||||
ronkyuu = "*"
|
||||
xrd = "*"
|
||||
greenlet = "^2.0.2"
|
||||
authors = [
|
||||
{name = "Danielle McLean", email = "dani@00dani.me"},
|
||||
]
|
||||
license = {text = "MIT"}
|
||||
requires-python = "<4.0,>=3.12"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
mypy = "*"
|
||||
ptpython = "*"
|
||||
pytest-django = "*"
|
||||
types-bleach = "*"
|
||||
types-markdown = "*"
|
||||
types-python-slugify = "*"
|
||||
types-pyyaml = "*"
|
||||
types-requests = "*"
|
||||
watchdog = "*"
|
||||
werkzeug = "*"
|
||||
dependencies = [
|
||||
"accept-types",
|
||||
"ago",
|
||||
"argon2-cffi",
|
||||
"bleach",
|
||||
"cachecontrol",
|
||||
"django<4,>=3",
|
||||
"django-activeurl",
|
||||
"django-annoying",
|
||||
"django-compressor",
|
||||
"django-computed-property",
|
||||
"django-cors-headers",
|
||||
"django-debug-toolbar",
|
||||
"django-extensions",
|
||||
"django-meta",
|
||||
"django-model-utils",
|
||||
"django-otp",
|
||||
"django-otp-agents",
|
||||
"django-push",
|
||||
"django-randomslugfield",
|
||||
"django-redis",
|
||||
"django-rq",
|
||||
"docutils",
|
||||
"gevent",
|
||||
"gunicorn[gevent]",
|
||||
"hiredis",
|
||||
"jinja2",
|
||||
"markdown",
|
||||
"mf2py",
|
||||
"mf2util",
|
||||
"msgpack",
|
||||
"pillow",
|
||||
"psycopg2-binary",
|
||||
"python-baseconv",
|
||||
"python-magic",
|
||||
"python-slugify",
|
||||
"pyup-django",
|
||||
"pyyaml",
|
||||
"qrcode",
|
||||
"ronkyuu",
|
||||
"xrd",
|
||||
"greenlet",
|
||||
]
|
||||
|
||||
[tool.pdm.dev-dependencies]
|
||||
dev = [
|
||||
"mypy",
|
||||
"ptpython",
|
||||
"pytest-django",
|
||||
"types-bleach",
|
||||
"types-markdown",
|
||||
"types-python-slugify",
|
||||
"types-pyyaml",
|
||||
"types-requests",
|
||||
"watchdog",
|
||||
"werkzeug",
|
||||
]
|
||||
|
||||
[tool.pdm.build]
|
||||
includes = []
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E4", "E7", "E9", "F", "I"]
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry>=0.12"]
|
||||
build-backend = "poetry.masonry.api"
|
||||
requires = ["pdm-backend"]
|
||||
build-backend = "pdm.backend"
|
||||
|
|
|
@ -128,7 +128,7 @@ class User(ModelMeta, AbstractUser):
|
|||
|
||||
@property
|
||||
def avatar_url(self):
|
||||
return self.avatar.url
|
||||
return self.avatar.url if self.avatar else None
|
||||
|
||||
@cached_property
|
||||
def facebook_id(self):
|
||||
|
@ -154,7 +154,7 @@ class User(ModelMeta, AbstractUser):
|
|||
"url": self.full_url,
|
||||
"name": self.name,
|
||||
"email": self.email,
|
||||
"image": urljoin(base, self.avatar.url),
|
||||
"image": urljoin(base, self.avatar.url) if self.avatar else None,
|
||||
"givenName": self.first_name,
|
||||
"familyName": self.last_name,
|
||||
"sameAs": [profile.url for profile in self.profiles.all()],
|
||||
|
|
Loading…
Reference in a new issue