takahe/activities/views/compose.py

200 lines
7.6 KiB
Python
Raw Normal View History

2022-12-01 17:46:49 -08:00
from django import forms
from django.conf import settings
from django.contrib import messages
from django.shortcuts import redirect
from django.utils import timezone
2022-12-01 17:46:49 -08:00
from django.views.generic import FormView
from activities.models import Post, PostAttachment, PostAttachmentStates, TimelineEvent
2022-12-01 17:46:49 -08:00
from core.files import blurhash_image, resize_image
from core.models import Config
from users.views.base import IdentityViewMixin
2022-12-01 17:46:49 -08:00
class Compose(IdentityViewMixin, FormView):
2022-12-01 17:46:49 -08:00
template_name = "activities/compose.html"
class form_class(forms.Form):
text = forms.CharField(
widget=forms.Textarea(
attrs={
2022-12-11 08:23:39 -08:00
"autofocus": "autofocus",
2022-12-01 17:46:49 -08:00
"placeholder": "What's on your mind?",
},
)
)
2022-12-01 17:46:49 -08:00
visibility = forms.ChoiceField(
choices=[
(Post.Visibilities.public, "Public"),
(Post.Visibilities.local_only, "Local Only"),
(Post.Visibilities.unlisted, "Unlisted"),
(Post.Visibilities.followers, "Followers & Mentioned Only"),
(Post.Visibilities.mentioned, "Mentioned Only"),
],
)
2022-12-01 17:46:49 -08:00
content_warning = forms.CharField(
required=False,
label=Config.lazy_system_value("content_warning_text"),
widget=forms.TextInput(
attrs={
"placeholder": Config.lazy_system_value("content_warning_text"),
},
),
help_text="Optional - Post will be hidden behind this text until clicked",
)
image = forms.ImageField(
required=False,
help_text="Optional - For multiple image uploads and cropping, please use an app",
widget=forms.FileInput(
attrs={
"_": f"""
on change
if me.files[0].size > {settings.SETUP.MEDIA_MAX_IMAGE_FILESIZE_MB * 1024 ** 2}
add [@disabled=] to #upload
remove <ul.errorlist/>
make <ul.errorlist/> called errorlist
make <li/> called error
set size_in_mb to (me.files[0].size / 1024 / 1024).toFixed(2)
put 'File must be {settings.SETUP.MEDIA_MAX_IMAGE_FILESIZE_MB}MB or less (actual: ' + size_in_mb + 'MB)' into error
put error into errorlist
put errorlist before me
else
remove @disabled from #upload
remove <ul.errorlist/>
end
end
"""
}
),
)
image_caption = forms.CharField(
required=False,
help_text="Provide an image caption for the visually impaired",
)
def __init__(self, identity, *args, **kwargs):
2022-12-11 08:34:44 -08:00
super().__init__(*args, **kwargs)
self.identity = identity
2022-12-11 08:34:44 -08:00
self.fields["text"].widget.attrs[
"_"
] = rf"""
init
-- Move cursor to the end of existing text
set my.selectionStart to my.value.length
end
2022-12-11 08:34:44 -08:00
on load or input
2022-12-31 19:17:48 -08:00
-- Unicode-aware counting to match Python
-- <LF> will be normalized as <CR><LF> in Django
set characters to Array.from(my.value.replaceAll('\n','\r\n').trim()).length
2022-12-11 08:34:44 -08:00
put {Config.system.post_length} - characters into #character-counter
if characters > {Config.system.post_length} then
set #character-counter's style.color to 'var(--color-text-error)'
add [@disabled=] to #post-button
else
set #character-counter's style.color to ''
remove @disabled from #post-button
end
"""
2022-12-01 17:46:49 -08:00
def clean_text(self):
text = self.cleaned_data.get("text")
# Check minimum interval
last_post = self.identity.posts.order_by("-created").first()
if (
last_post
and (timezone.now() - last_post.created).total_seconds()
< Config.system.post_minimum_interval
):
raise forms.ValidationError(
f"You must wait at least {Config.system.post_minimum_interval} seconds between posts"
)
2022-12-01 17:46:49 -08:00
if not text:
return text
# Check post length
2022-12-01 17:46:49 -08:00
length = len(text)
if length > Config.system.post_length:
raise forms.ValidationError(
f"Maximum post length is {Config.system.post_length} characters (you have {length})"
)
return text
def clean_image(self):
value = self.cleaned_data.get("image")
if value:
max_mb = settings.SETUP.MEDIA_MAX_IMAGE_FILESIZE_MB
max_bytes = max_mb * 1024 * 1024
if value.size > max_bytes:
# Erase the file from our data to stop trying to show it again
self.files = {}
raise forms.ValidationError(
f"File must be {max_mb}MB or less (actual: {value.size / 1024 ** 2:.2f})"
)
return value
def get_form(self, form_class=None):
return self.form_class(identity=self.identity, **self.get_form_kwargs())
2022-12-01 17:46:49 -08:00
def get_initial(self):
initial = super().get_initial()
initial["visibility"] = self.identity.config_identity.default_post_visibility
2022-12-01 17:46:49 -08:00
return initial
def form_valid(self, form):
# See if we need to make an image attachment
2022-12-01 17:46:49 -08:00
attachments = []
if form.cleaned_data.get("image"):
main_file = resize_image(
form.cleaned_data["image"],
size=(2000, 2000),
cover=False,
2022-12-01 17:46:49 -08:00
)
thumbnail_file = resize_image(
form.cleaned_data["image"],
size=(400, 225),
cover=True,
2022-12-01 17:46:49 -08:00
)
attachment = PostAttachment.objects.create(
blurhash=blurhash_image(thumbnail_file),
mimetype="image/webp",
width=main_file.image.width,
height=main_file.image.height,
name=form.cleaned_data.get("image_caption"),
state=PostAttachmentStates.fetched,
author=self.identity,
2022-12-01 17:46:49 -08:00
)
attachment.file.save(
main_file.name,
main_file,
)
attachment.thumbnail.save(
thumbnail_file.name,
thumbnail_file,
)
attachment.save()
attachments.append(attachment)
# Create the post
post = Post.create_local(
author=self.identity,
content=form.cleaned_data["text"],
summary=form.cleaned_data.get("content_warning"),
visibility=form.cleaned_data["visibility"],
attachments=attachments,
)
# Add their own timeline event for immediate visibility
TimelineEvent.add_post(self.identity, post)
messages.success(self.request, "Your post was created.")
return redirect(".")
2022-12-01 17:46:49 -08:00
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["identity"] = self.identity
context["section"] = "compose"
2022-12-01 17:46:49 -08:00
return context