Logged out experience, config, and profiles

This commit is contained in:
Andrew Godwin 2022-11-17 19:21:00 -07:00
parent 0851fbd1ec
commit 291d7e404e
19 changed files with 295 additions and 114 deletions

View File

@ -1,5 +1,8 @@
from functools import partial
from django.db import models from django.db import models
from core.uploads import upload_namer
from stator.models import State, StateField, StateGraph, StatorModel from stator.models import State, StateField, StateGraph, StatorModel
@ -31,7 +34,9 @@ class PostAttachment(StatorModel):
mimetype = models.CharField(max_length=200) mimetype = models.CharField(max_length=200)
# File may not be populated if it's remote and not cached on our side yet # File may not be populated if it's remote and not cached on our side yet
file = models.FileField(upload_to="attachments/%Y/%m/%d/", null=True, blank=True) file = models.FileField(
upload_to=partial(upload_namer, "attachments"), null=True, blank=True
)
remote_url = models.CharField(max_length=500, null=True, blank=True) remote_url = models.CharField(max_length=500, null=True, blank=True)

View File

@ -57,7 +57,6 @@ class Home(FormView):
return redirect(".") return redirect(".")
@method_decorator(identity_required, name="dispatch")
class Local(TemplateView): class Local(TemplateView):
template_name = "activities/local.html" template_name = "activities/local.html"

View File

@ -1,9 +1,21 @@
from functools import partial
from typing import ClassVar from typing import ClassVar
import pydantic import pydantic
from django.core.files import File
from django.db import models from django.db import models
from django.templatetags.static import static
from django.utils.functional import classproperty from django.utils.functional import classproperty
from core.uploads import upload_namer
from takahe import __version__
class UploadedImage(str):
"""
Type used to indicate a setting is an image
"""
class Config(models.Model): class Config(models.Model):
""" """
@ -31,7 +43,11 @@ class Config(models.Model):
) )
json = models.JSONField(blank=True, null=True) json = models.JSONField(blank=True, null=True)
image = models.ImageField(blank=True, null=True, upload_to="config/%Y/%m/%d/") image = models.ImageField(
blank=True,
null=True,
upload_to=partial(upload_namer, "config"),
)
class Meta: class Meta:
unique_together = [ unique_together = [
@ -46,60 +62,110 @@ class Config(models.Model):
system: ClassVar["Config.ConfigOptions"] # type: ignore system: ClassVar["Config.ConfigOptions"] # type: ignore
@classmethod @classmethod
def load_system(cls): def load_values(cls, options_class, filters):
""" """
Load all of the system config options and return an object with them Loads config options and returns an object with them
""" """
values = {} values = {}
for config in cls.objects.filter(user__isnull=True, identity__isnull=True): for config in cls.objects.filter(**filters):
values[config.key] = config.image or config.json values[config.key] = config.image.url if config.image else config.json
return cls.SystemOptions(**values) if values[config.key] is None:
del values[config.key]
values["version"] = __version__
return options_class(**values)
@classmethod
def load_system(cls):
"""
Loads the system config options object
"""
return cls.load_values(
cls.SystemOptions,
{"identity__isnull": True, "user__isnull": True},
)
@classmethod @classmethod
def load_user(cls, user): def load_user(cls, user):
""" """
Load all of the user config options and return an object with them Loads a user config options object
""" """
values = {} return cls.load_values(
for config in cls.objects.filter(user=user, identity__isnull=True): cls.SystemOptions,
values[config.key] = config.image or config.json {"identity__isnull": True, "user": user},
return cls.UserOptions(**values) )
@classmethod @classmethod
def load_identity(cls, identity): def load_identity(cls, identity):
""" """
Load all of the identity config options and return an object with them Loads a user config options object
""" """
values = {} return cls.load_values(
for config in cls.objects.filter(user__isnull=True, identity=identity): cls.IdentityOptions,
values[config.key] = config.image or config.json {"identity": identity, "user__isnull": True},
return cls.IdentityOptions(**values) )
@classmethod
def set_value(cls, key, value, options_class, filters):
config_field = options_class.__fields__[key]
if isinstance(value, File):
if config_field.type_ is not UploadedImage:
raise ValueError(f"Cannot save file to {key} of type: {type(value)}")
cls.objects.update_or_create(
key=key,
defaults={"json": None, "image": value},
**filters,
)
elif value is None:
cls.objects.filter(key=key, **filters).delete()
else:
if not isinstance(value, config_field.type_):
raise ValueError(f"Invalid type for {key}: {type(value)}")
if value == config_field.default:
cls.objects.filter(key=key, **filters).delete()
else:
cls.objects.update_or_create(
key=key,
defaults={"json": value},
**filters,
)
@classmethod @classmethod
def set_system(cls, key, value): def set_system(cls, key, value):
config_field = cls.SystemOptions.__fields__[key] cls.set_value(
if not isinstance(value, config_field.type_): key,
raise ValueError(f"Invalid type for {key}: {type(value)}") value,
cls.objects.update_or_create( cls.SystemOptions,
key=key, {"identity__isnull": True, "user__isnull": True},
defaults={"json": value}, )
@classmethod
def set_user(cls, user, key, value):
cls.set_value(
key,
value,
cls.UserOptions,
{"identity__isnull": True, "user": user},
) )
@classmethod @classmethod
def set_identity(cls, identity, key, value): def set_identity(cls, identity, key, value):
config_field = cls.IdentityOptions.__fields__[key] cls.set_value(
if not isinstance(value, config_field.type_): key,
raise ValueError(f"Invalid type for {key}: {type(value)}") value,
cls.objects.update_or_create( cls.IdentityOptions,
identity=identity, {"identity": identity, "user__isnull": True},
key=key,
defaults={"json": value},
) )
class SystemOptions(pydantic.BaseModel): class SystemOptions(pydantic.BaseModel):
version: str = __version__
site_name: str = "takahē" site_name: str = "takahē"
highlight_color: str = "#449c8c" highlight_color: str = "#449c8c"
site_about: str = "<h2>Welcome!</h2>\n\nThis is a community running Takahē."
site_icon: UploadedImage = static("img/icon-128.png")
site_banner: UploadedImage = static("img/fjords-banner-600.jpg")
post_length: int = 500 post_length: int = 500
identity_max_age: int = 24 * 60 * 60 identity_max_age: int = 24 * 60 * 60

View File

@ -19,7 +19,7 @@ class LoggedOutHomepage(TemplateView):
def get_context_data(self): def get_context_data(self):
return { return {
"identities": Identity.objects.filter(local=True), "identities": Identity.objects.filter(local=True).order_by("-created")[:20],
} }

View File

@ -104,6 +104,7 @@ body {
color: var(--color-text-main); color: var(--color-text-main);
font-family: "Raleway", sans-serif; font-family: "Raleway", sans-serif;
font-size: 16px; font-size: 16px;
min-height: 100%;
} }
main { main {
@ -113,6 +114,19 @@ main {
border-radius: 5px; border-radius: 5px;
} }
footer {
width: 900px;
margin: 0 auto;
padding: 0 0 10px 0;
color: var(--color-text-duller);
text-align: center;
font-size: 90%;
}
footer a {
border-bottom: 1px solid var(--color-text-duller);
}
header { header {
display: flex; display: flex;
} }
@ -127,6 +141,7 @@ header .logo {
font-size: 130%; font-size: 130%;
color: var(--color-text-main); color: var(--color-text-main);
border-bottom: 3px solid rgba(0, 0, 0, 0); border-bottom: 3px solid rgba(0, 0, 0, 0);
z-index: 10;
} }
header .logo:hover { header .logo:hover {
@ -144,6 +159,7 @@ header menu {
display: flex; display: flex;
list-style-type: none; list-style-type: none;
justify-content: flex-start; justify-content: flex-start;
z-index: 10;
} }
header menu a { header menu a {
@ -151,7 +167,11 @@ header menu a {
color: #eee; color: #eee;
line-height: 30px; line-height: 30px;
border-bottom: 3px solid rgba(0, 0, 0, 0); border-bottom: 3px solid rgba(0, 0, 0, 0);
border-right: 1px solid var(--color-bg-menu); }
body.has-banner header menu a {
background: rgba(0, 0, 0, 0.5);
border-right: 0;
} }
header menu a:hover, header menu a:hover,
@ -159,6 +179,12 @@ header menu a.selected {
border-bottom: 3px solid var(--color-highlight); border-bottom: 3px solid var(--color-highlight);
} }
header menu a i {
font-size: 24px;
display: inline-block;
vertical-align: middle;
}
header menu .gap { header menu .gap {
flex-grow: 1; flex-grow: 1;
} }
@ -167,17 +193,11 @@ header menu a.identity {
border-right: 0; border-right: 0;
text-align: right; text-align: right;
padding-right: 10px; padding-right: 10px;
background: var(--color-bg-menu); background: var(--color-bg-menu) !important;
border-radius: 0 5px 0 0; border-radius: 0 5px 0 0;
width: 250px; width: 250px;
} }
header menu a i {
display: inline-block;
vertical-align: middle;
margin-right: 10px;
}
header menu a img { header menu a img {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
@ -267,8 +287,6 @@ nav a i {
/* Icon menus */ /* Icon menus */
.icon-menu {}
.icon-menu>a { .icon-menu>a {
display: block; display: block;
margin: 0px 0 20px 0; margin: 0px 0 20px 0;
@ -431,6 +449,17 @@ form textarea {
color: var(--color-text-main); color: var(--color-text-main);
} }
form .clear {
color: var(--color-text-main);
font-size: 90%;
margin: 5px 0 5px 0;
}
form .clear input {
display: inline;
width: 32px;
}
.right-column form.compose input, .right-column form.compose input,
.right-column form.compose textarea { .right-column form.compose textarea {
margin: 0 0 10px 0; margin: 0 0 10px 0;
@ -531,6 +560,16 @@ form .button:hover {
padding: 2px 6px; padding: 2px 6px;
} }
/* Logged out homepage */
.about img.banner {
width: calc(100% + 30px);
height: auto;
object-fit: cover;
margin: -65px -15px 0 -15px;
display: block;
}
/* Identities */ /* Identities */
h1.identity { h1.identity {
@ -542,7 +581,8 @@ h1.identity .banner {
height: 200px; height: 200px;
object-fit: cover; object-fit: cover;
display: block; display: block;
margin: 0 0 20px 0; width: calc(100% + 30px);
margin: -65px -15px 20px -15px;
} }
h1.identity .icon { h1.identity .icon {
@ -723,6 +763,12 @@ h1.identity small {
border-radius: 0; border-radius: 0;
} }
footer {
width: 100%;
background-color: var(--color-bg-box);
padding: 10px 0;
}
header .logo { header .logo {
border-radius: 0; border-radius: 0;
} }
@ -730,22 +776,6 @@ h1.identity small {
} }
@media (max-width: 800px) {
header menu a {
font-size: 0;
padding: 10px 20px 4px 20px;
}
header menu a i {
display: inline-block;
vertical-align: middle;
margin: 0;
font-size: 20px;
}
}
@media (max-width: 700px) { @media (max-width: 700px) {
header menu a.identity { header menu a.identity {
width: 50px; width: 50px;

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

View File

@ -0,0 +1 @@
__version__ = "0.3.0"

View File

@ -2,15 +2,35 @@
<a href="/" {% if current_page == "home" %}class="selected"{% endif %}> <a href="/" {% if current_page == "home" %}class="selected"{% endif %}>
<i class="fa-solid fa-home"></i> Home <i class="fa-solid fa-home"></i> Home
</a> </a>
<a href="/notifications/" {% if current_page == "notifications" %}class="selected"{% endif %}> {% if request.user.is_authenticated %}
<i class="fa-solid fa-at"></i> Notifications <a href="{% url "notifications" %}" {% if current_page == "notifications" %}class="selected"{% endif %}>
</a> <i class="fa-solid fa-at"></i> Notifications
<a href="/local/" {% if current_page == "local" %}class="selected"{% endif %}> </a>
<i class="fa-solid fa-city"></i> Local <a href="{% url "local" %}" {% if current_page == "local" %}class="selected"{% endif %}>
</a> <i class="fa-solid fa-city"></i> Local
<a href="/federated/" {% if current_page == "federated" %}class="selected"{% endif %}> </a>
<i class="fa-solid fa-globe"></i> Federated <a href="{% url "federated" %}" {% if current_page == "federated" %}class="selected"{% endif %}>
</a> <i class="fa-solid fa-globe"></i> Federated
</a>
<h3></h3>
<a href="{% url "compose" %}" {% if top_section == "compose" %}class="selected"{% endif %}>
<i class="fa-solid fa-feather"></i> Compose
</a>
<a href="{% url "search" %}" {% if top_section == "search" %}class="selected"{% endif %}>
<i class="fa-solid fa-search"></i> Search
</a>
<a href="{% url "settings" %}" {% if top_section == "settings" %}class="selected"{% endif %}>
<i class="fa-solid fa-gear"></i> Settings
</a>
{% else %}
<a href="/local/" {% if current_page == "local" %}class="selected"{% endif %}>
<i class="fa-solid fa-city"></i> Local Posts
</a>
<h3></h3>
<a href="/auth/signup/" {% if current_page == "signup" %}class="selected"{% endif %}>
<i class="fa-solid fa-user-plus"></i> Create Account
</a>
{% endif %}
</nav> </nav>
{% if current_page == "home" %} {% if current_page == "home" %}

View File

@ -3,14 +3,14 @@
{% block title %}Login{% endblock %} {% block title %}Login{% endblock %}
{% block content %} {% block content %}
<nav>
<a href="." class="selected">Login</a>
</nav>
<form action="." method="POST"> <form action="." method="POST">
{% csrf_token %} {% csrf_token %}
{% for field in form %} <fieldset>
{% include "forms/_field.html" %} <legend>Login</legend>
{% endfor %} {% for field in form %}
{% include "forms/_field.html" %}
{% endfor %}
</fieldset>
<div class="buttons"> <div class="buttons">
<button>Login</button> <button>Login</button>
</div> </div>

View File

@ -23,56 +23,57 @@
<main> <main>
<header> <header>
<a class="logo" href="/"> <a class="logo" href="/">
<img src="{% static "img/icon-128.png" %}" width="32"> <img src="{{ config.site_icon }}" width="32">
{{ config.site_name }} {{ config.site_name }}
</a> </a>
<menu> <menu>
{% if user.is_authenticated %} {% if user.is_authenticated %}
<a href="{% url "compose" %}" title="Compose" {% if top_section == "compose" %}class="selected"{% endif %}> <a href="{% url "compose" %}" title="Compose" {% if top_section == "compose" %}class="selected"{% endif %}>
<i class="fa-solid fa-feather"></i> Compose <i class="fa-solid fa-feather"></i>
</a> </a>
<a href="{% url "search" %}" title="Search" {% if top_section == "search" %}class="selected"{% endif %}> <a href="{% url "search" %}" title="Search" {% if top_section == "search" %}class="selected"{% endif %}>
<i class="fa-solid fa-search"></i> Search <i class="fa-solid fa-search"></i>
</a> </a>
<a href="{% url "settings" %}" title="Settings" {% if top_section == "settings" %}class="selected"{% endif %}> <a href="{% url "settings" %}" title="Settings" {% if top_section == "settings" %}class="selected"{% endif %}>
<i class="fa-solid fa-gear"></i> Settings <i class="fa-solid fa-gear"></i>
</a> </a>
<div class="gap"></div> <div class="gap"></div>
<a href="/identity/select/" class="identity"> <a href="/identity/select/" class="identity">
{% if not request.identity %} {% if not request.identity %}
No Identity No Identity
<img src="{% static "img/unknown-icon-128.png" %}" title="No identity selected"> <img src="{% static "img/unknown-icon-128.png" %}" title="No identity selected">
{% elif request.identity.icon %}
{{ request.identity.username }}
<img src="{{ request.identity.icon.url }}" title="{{ request.identity.handle }}">
{% elif request.identity.icon_uri %}
{{ request.identity.username }}
<img src="{{ request.identity.icon_uri }}" title="{{ request.identity.handle }}">
{% else %} {% else %}
{{ request.identity.username }} {{ request.identity.username }}
<img src="{% static "img/unknown-icon-128.png" %}" title="{{ request.identity.handle }}"> <img src="{{ request.identity.local_icon_url }}" title="{{ request.identity.handle }}">
{% endif %} {% endif %}
</a> </a>
{% else %} {% else %}
<a href="/auth/login/"><i class="fa-solid fa-right-to-bracket"></i> Login</a> <div class="gap"></div>
<a href="/auth/login/" class="identity"><i class="fa-solid fa-right-to-bracket"></i> Login</a>
{% endif %} {% endif %}
</menu> </menu>
</header> </header>
{% block full_content %} {% block full_content %}
<div class="columns"> {% block pre_content %}
<div class="left-column"> {% endblock %}
{% block content %} <div class="columns">
{% endblock %} <div class="left-column">
{% block content %}
{% endblock %}
</div>
<div class="right-column">
{% block right_content %}
{% include "activities/_menu.html" %}
{% endblock %}
</div>
</div> </div>
<div class="right-column">
{% block right_content %}
{% include "activities/_menu.html" %}
{% endblock %}
</div>
</div>
{% endblock %} {% endblock %}
</main> </main>
<footer>
<span>Powered by <a href="https://jointakahe.com">Takahē {{ config.version }}</a></span>
</footer>
</body> </body>
</html> </html>

View File

@ -10,9 +10,14 @@
</p> </p>
{% endif %} {% endif %}
{{ field.errors }} {{ field.errors }}
{% if field.field.widget.input_type == "file" and field.value %}
<div class="clear">
<input type="checkbox" class="clear" name="{{ field.name }}__clear"> Clear current value</input>
</div>
{% endif %}
{{ field }} {{ field }}
</div> </div>
{% if preview %} {% if field.field.widget.input_type == "file" %}
<img class="preview" src="{{ preview }}"> <img class="preview" src="{{ field.value }}">
{% endif %} {% endif %}
</div> </div>

View File

@ -1,5 +1,11 @@
<nav> <nav>
<a href="/identity/select/" {% if identities %}class="selected"{% endif %}>Select Identity</a> <a href="/identity/select/" {% if identities %}class="selected"{% endif %}>
<a href="/identity/create/" {% if form %}class="selected"{% endif %}>Create Identity</a> <i class="fa-solid fa-user"></i> Select Identity
<a href="/auth/logout/">Logout</a> </a>
<a href="/identity/create/" {% if form %}class="selected"{% endif %}>
<i class="fa-solid fa-plus"></i> Create Identity
</a>
<a href="/auth/logout/">
<i class="fa-solid fa-right-from-bracket"></i> Logout
</a>
</nav> </nav>

View File

@ -2,6 +2,8 @@
{% block title %}{{ identity }}{% endblock %} {% block title %}{{ identity }}{% endblock %}
{% block body_class %}has-banner{% endblock %}
{% block content %} {% block content %}
<h1 class="identity"> <h1 class="identity">
{% if identity.local_image_url %} {% if identity.local_image_url %}

View File

@ -2,12 +2,14 @@
{% block title %}Welcome{% endblock %} {% block title %}Welcome{% endblock %}
{% block content %}
<nav>
<a href="/" class="selected">Home</a>
</nav>
{% block content %}
<div class="about">
<img class="banner" src="{{ config.site_banner }}">
{{ config.site_about|safe|linebreaks }}
</div>
<h2>People</h2>
{% for identity in identities %} {% for identity in identities %}
<a href="{{ identity.urls.view }}">{{ identity }}</a> {% include "activities/_identity.html" %}
{% endfor %} {% endfor %}
{% endblock %} {% endblock %}

View File

@ -11,6 +11,9 @@
<a href="#" {% if section == "login" %}class="selected"{% endif %}> <a href="#" {% if section == "login" %}class="selected"{% endif %}>
<i class="fa-solid fa-key"></i> Login &amp; Security <i class="fa-solid fa-key"></i> Login &amp; Security
</a> </a>
<a href="/auth/logout/">
<i class="fa-solid fa-right-from-bracket"></i> Logout
</a>
<h3>Administration</h3> <h3>Administration</h3>
<a href="{% url "admin_basic" %}" {% if section == "basic" %}class="selected"{% endif %}> <a href="{% url "admin_basic" %}" {% if section == "basic" %}class="selected"{% endif %}>
<i class="fa-solid fa-book"></i> Basic <i class="fa-solid fa-book"></i> Basic
@ -24,5 +27,8 @@
<a href="{% url "admin_identities" %}" {% if section == "identities" %}class="selected"{% endif %}> <a href="{% url "admin_identities" %}" {% if section == "identities" %}class="selected"{% endif %}>
<i class="fa-solid fa-id-card"></i> Identities <i class="fa-solid fa-id-card"></i> Identities
</a> </a>
<a href="/djadmin">
<i class="fa-solid fa-gear"></i> Django Admin
</a>
{% endif %} {% endif %}
</nav> </nav>

View File

@ -3,7 +3,7 @@
{% block subtitle %}{{ section.title }}{% endblock %} {% block subtitle %}{{ section.title }}{% endblock %}
{% block content %} {% block content %}
<form action="." method="POST"> <form action="." method="POST" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
{% for title, fields in fieldsets.items %} {% for title, fields in fieldsets.items %}
<fieldset> <fieldset>

View File

@ -40,7 +40,6 @@ class BasicPage(AdminSettingsPage):
options = { options = {
"site_name": { "site_name": {
"title": "Site Name", "title": "Site Name",
"help_text": "Shown in the top-left of the page, and titles",
}, },
"highlight_color": { "highlight_color": {
"title": "Highlight Color", "title": "Highlight Color",
@ -50,10 +49,29 @@ class BasicPage(AdminSettingsPage):
"title": "Maximum Post Length", "title": "Maximum Post Length",
"help_text": "The maximum number of characters allowed per post", "help_text": "The maximum number of characters allowed per post",
}, },
"site_about": {
"title": "About This Site",
"help_text": "Displayed on the homepage and the about page",
"display": "textarea",
},
"site_icon": {
"title": "Site Icon",
"help_text": "Minimum size 64x64px. Should be square.",
},
"site_banner": {
"title": "Site Banner",
"help_text": "Must be at least 650px wide. 3:1 ratio of width:height recommended.",
},
} }
layout = { layout = {
"Branding": ["site_name", "highlight_color"], "Branding": [
"site_name",
"site_about",
"site_icon",
"site_banner",
"highlight_color",
],
"Posts": ["post_length"], "Posts": ["post_length"],
} }

View File

@ -2,18 +2,19 @@ from functools import partial
from typing import ClassVar, Dict, List from typing import ClassVar, Dict, List
from django import forms from django import forms
from django.core.files import File
from django.shortcuts import redirect from django.shortcuts import redirect
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views.generic import FormView, RedirectView from django.views.generic import FormView, RedirectView
from PIL import Image, ImageOps from PIL import Image, ImageOps
from core.models import Config from core.models.config import Config, UploadedImage
from users.decorators import identity_required from users.decorators import identity_required
@method_decorator(identity_required, name="dispatch") @method_decorator(identity_required, name="dispatch")
class SettingsRoot(RedirectView): class SettingsRoot(RedirectView):
url = "/settings/interface/" pattern_name = "settings_profile"
@method_decorator(identity_required, name="dispatch") @method_decorator(identity_required, name="dispatch")
@ -41,8 +42,16 @@ class SettingsPage(FormView):
choices=[(True, "Enabled"), (False, "Disabled")] choices=[(True, "Enabled"), (False, "Disabled")]
), ),
) )
elif config_field.type_ is UploadedImage:
form_field = forms.ImageField
elif config_field.type_ is str: elif config_field.type_ is str:
form_field = forms.CharField if details.get("display") == "textarea":
form_field = partial(
forms.CharField,
widget=forms.Textarea,
)
else:
form_field = forms.CharField
elif config_field.type_ is int: elif config_field.type_ is int:
form_field = forms.IntegerField form_field = forms.IntegerField
else: else:
@ -80,6 +89,15 @@ class SettingsPage(FormView):
def form_valid(self, form): def form_valid(self, form):
# Save each key # Save each key
for field in form: for field in form:
if field.field.__class__.__name__ == "ImageField":
# These can be cleared with an extra checkbox
if self.request.POST.get(f"{field.name}__clear"):
self.save_config(field.name, None)
continue
# We shove the preview values in initial_data, so only save file
# fields if they have a File object.
if not isinstance(form.cleaned_data[field.name], File):
continue
self.save_config( self.save_config(
field.name, field.name,
form.cleaned_data[field.name], form.cleaned_data[field.name],
@ -128,6 +146,8 @@ class ProfilePage(FormView):
return { return {
"name": self.request.identity.name, "name": self.request.identity.name,
"summary": self.request.identity.summary, "summary": self.request.identity.summary,
"icon": self.request.identity.icon.url,
"image": self.request.identity.image.url,
} }
def get_context_data(self): def get_context_data(self):
@ -142,12 +162,12 @@ class ProfilePage(FormView):
# Resize images # Resize images
icon = form.cleaned_data.get("icon") icon = form.cleaned_data.get("icon")
image = form.cleaned_data.get("image") image = form.cleaned_data.get("image")
if icon: if isinstance(icon, File):
resized_image = ImageOps.fit(Image.open(icon), (400, 400)) resized_image = ImageOps.fit(Image.open(icon), (400, 400))
icon.open() icon.open()
resized_image.save(icon) resized_image.save(icon)
self.request.identity.icon = icon self.request.identity.icon = icon
if image: if isinstance(image, File):
resized_image = ImageOps.fit(Image.open(image), (1500, 500)) resized_image = ImageOps.fit(Image.open(image), (1500, 500))
image.open() image.open()
resized_image.save(image) resized_image.save(image)