Merge pull request #806 from ae-utbm/taiste

Bugfixes
This commit is contained in:
thomas girod 2024-09-02 00:03:40 +02:00 committed by GitHub
commit 878ee99fe4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 298 additions and 383 deletions

View File

@ -44,21 +44,3 @@ jobs:
poetry run ./manage.py compilemessages
sudo systemctl restart uwsgi
sentry:
runs-on: ubuntu-latest
environment: production
timeout-minutes: 30
needs: deployment
steps:
- uses: actions/checkout@v3
- name: Sentry Release
uses: getsentry/action-release@v1.2.0
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_URL: ${{ secrets.SENTRY_URL }}
with:
environment: production

View File

@ -43,21 +43,3 @@ jobs:
poetry run ./manage.py compilemessages
sudo systemctl restart uwsgi
sentry:
runs-on: ubuntu-latest
environment: taiste
timeout-minutes: 30
needs: deployment
steps:
- uses: actions/checkout@v3
- name: Sentry Release
uses: getsentry/action-release@v1.2.0
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_URL: ${{ secrets.SENTRY_URL }}
with:
environment: taiste

View File

@ -34,7 +34,6 @@ from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from club.models import Club
from core import utils
from core.models import Notification, Preferences, RealGroup, User
@ -44,7 +43,6 @@ class Sith(models.Model):
alert_msg = models.TextField(_("alert message"), default="", blank=True)
info_msg = models.TextField(_("info message"), default="", blank=True)
weekmail_destinations = models.TextField(_("weekmail destinations"), default="")
version = utils.get_git_revision_short_hash()
def __str__(self):
return "⛩ Sith ⛩"

View File

@ -234,10 +234,10 @@ Welcome to the wiki page!
skia_profile = SithFile(
parent=profiles_root,
name=name,
file=resize_image(Image.open(BytesIO(f.read())), 400, "JPEG"),
file=resize_image(Image.open(BytesIO(f.read())), 400, "WEBP"),
owner=skia,
is_folder=False,
mime_type="image/jpeg",
mime_type="image/webp",
size=skia_profile_path.stat().st_size,
)
skia_profile.file.name = name
@ -368,10 +368,10 @@ Welcome to the wiki page!
richard_profile = SithFile(
parent=profiles_root,
name=name,
file=resize_image(Image.open(BytesIO(f.read())), 400, "JPEG"),
file=resize_image(Image.open(BytesIO(f.read())), 400, "WEBP"),
owner=richard,
is_folder=False,
mime_type="image/jpeg",
mime_type="image/webp",
size=richard_profile_path.stat().st_size,
)
richard_profile.file.name = name
@ -853,10 +853,10 @@ Welcome to the wiki page!
sli_profile = SithFile(
parent=profiles_root,
name=name,
file=resize_image(Image.open(BytesIO(f.read())), 400, "JPEG"),
file=resize_image(Image.open(BytesIO(f.read())), 400, "WEBP"),
owner=sli,
is_folder=False,
mime_type="image/jpeg",
mime_type="image/webp",
size=sli_profile_path.stat().st_size,
)
sli_profile.file.name = name
@ -887,10 +887,10 @@ Welcome to the wiki page!
krophil_profile = SithFile(
parent=profiles_root,
name=name,
file=resize_image(Image.open(BytesIO(f.read())), 400, "JPEG"),
file=resize_image(Image.open(BytesIO(f.read())), 400, "WEBP"),
owner=krophil,
is_folder=False,
mime_type="image/jpeg",
mime_type="image/webp",
size=krophil_profile_path.stat().st_size,
)
krophil_profile.file.name = name
@ -1217,13 +1217,13 @@ Welcome to the wiki page!
parent=album,
name=p.name,
file=resize_image(
Image.open(BytesIO(p.read_bytes())), 1000, "JPEG"
Image.open(BytesIO(p.read_bytes())), 1000, "WEBP"
),
owner=root,
is_folder=False,
is_in_sas=True,
is_moderated=True,
mime_type="image/jpeg",
mime_type="image/webp",
size=p.stat().st_size,
)
pict.file.name = p.name
@ -1252,10 +1252,10 @@ Welcome to the wiki page!
skia_profile = SithFile(
parent=profiles_root,
name=name,
file=resize_image(Image.open(BytesIO(f.read())), 400, "JPEG"),
file=resize_image(Image.open(BytesIO(f.read())), 400, "WEBP"),
owner=skia,
is_folder=False,
mime_type="image/jpeg",
mime_type="image/webp",
size=skia_profile_path.stat().st_size,
)
skia_profile.file.name = name

View File

@ -290,11 +290,6 @@
</a>
{% endblock %}
<br>
<code class="version">
{% cache 1000 "sith_version" %}
{% trans %}Sith version:{% endtrans %}&nbsp;{{ get_sith().version }}
{% endcache %}
</code>
</footer>
{% endif %}

View File

@ -64,7 +64,7 @@
</div>
{% endif %}
</div>
{% if user.promo %}
{% if user.promo and user.promo_has_logo() %}
<div class="user_mini_profile_promo">
<img src="{{ static('core/img/promo_%02d.png' % user.promo) }}" title="Promo {{ user.promo }}" alt="Promo {{ user.promo }}" class="promo_pict" />
</div>

View File

@ -61,6 +61,7 @@
<p>
{{ form[field_name].label }}
</p>
{{ form[field_name].errors }}
{%- else -%}
<em>{% trans %}To edit your profile picture, ask a member of the AE{% endtrans %}</em>
{%- endif -%}
@ -163,7 +164,7 @@
/* Stop camera */
this.video.pause()
this.video.srcObject.getTracks().forEach((track) => {
if (track.readyState == 'live') {
if (track.readyState === 'live') {
track.stop();
}
});
@ -171,8 +172,8 @@
canvas.toBlob((blob) => {
let file = new File(
[blob],
"{% trans %}captured{% endtrans %}.png",
{ type: "image/jpeg" },
"{% trans %}captured{% endtrans %}.webp",
{ type: "image/webp" },
);
let list = new DataTransfer();

View File

@ -127,11 +127,6 @@ urlpatterns = [
UserUpdateProfileView.as_view(),
name="user_edit",
),
path(
"user/<int:user_id>/profile_upload/",
UserUploadProfilePictView.as_view(),
name="user_profile_upload",
),
path("user/<int:user_id>/clubs/", UserClubView.as_view(), name="user_clubs"),
path(
"user/<int:user_id>/prefs/",

View File

@ -13,7 +13,6 @@
#
#
import subprocess
from datetime import date
# Image utils
@ -29,17 +28,6 @@ from PIL import ExifTags
from PIL.Image import Resampling
def get_git_revision_short_hash() -> str:
"""Return the short hash of the current commit."""
try:
output = subprocess.check_output(["git", "rev-parse", "--short", "HEAD"])
if isinstance(output, bytes):
return output.decode("ascii").strip()
return output.strip()
except subprocess.CalledProcessError:
return ""
def get_start_of_semester(today: Optional[date] = None) -> date:
"""Return the date of the start of the semester of the given date.
If no date is given, return the start date of the current semester.
@ -102,13 +90,17 @@ def scale_dimension(width, height, long_edge):
def resize_image(im, edge, img_format):
(w, h) = im.size
(width, height) = scale_dimension(w, h, long_edge=edge)
img_format = img_format.upper()
content = BytesIO()
# use the lanczos filter for antialiasing and discard the alpha channel
im = im.resize((width, height), Resampling.LANCZOS).convert("RGB")
im = im.resize((width, height), Resampling.LANCZOS)
if img_format == "JPEG":
# converting an image with an alpha channel to jpeg would cause a crash
im = im.convert("RGB")
try:
im.save(
fp=content,
format=img_format.upper(),
format=img_format,
quality=90,
optimize=True,
progressive=True,
@ -117,7 +109,7 @@ def resize_image(im, edge, img_format):
PIL.ImageFile.MAXBLOCK = im.size[0] * im.size[1]
im.save(
fp=content,
format=img_format.upper(),
format=img_format,
quality=90,
optimize=True,
progressive=True,

View File

@ -12,6 +12,7 @@
# OR WITHIN THE LOCAL FILE "LICENSE"
#
#
import mimetypes
from urllib.parse import quote, urljoin
# This file contains all the views that concern the page model
@ -58,30 +59,28 @@ def send_file(
if not can_view(f, request.user) and not is_logged_in_counter(request):
raise PermissionDenied
name = getattr(f, file_attr).name
filepath = settings.MEDIA_ROOT / name
# check if file exists on disk
if not filepath.exists():
raise Http404
response = HttpResponse(
headers={"Content-Disposition": f'inline; filename="{quote(name)}"'}
)
if not settings.DEBUG:
# When receiving a response with the Accel-Redirect header,
# the reverse proxy will automatically handle the file sending.
# This is really hard to test (thus isn't tested)
# so please do not mess with this.
response = HttpResponse(status=200)
response["Content-Type"] = ""
response["Content-Type"] = "" # automatically set by nginx
response["X-Accel-Redirect"] = quote(urljoin(settings.MEDIA_URL, name))
return response
filepath = settings.MEDIA_ROOT / name
# check if file exists on disk
if not filepath.exists():
raise Http404
with open(filepath, "rb") as filename:
wrapper = FileWrapper(filename)
response = HttpResponse(wrapper, content_type=f.mime_type)
response.content = FileWrapper(filename)
response["Content-Type"] = mimetypes.guess_type(filepath)[0]
response["Last-Modified"] = http_date(f.date.timestamp())
response["Content-Length"] = filepath.stat().st_size
response["Content-Disposition"] = ('inline; filename="%s"' % f.name).encode(
"utf-8"
)
return response

View File

@ -201,10 +201,7 @@ class RegisteringForm(UserCreationForm):
class UserProfileForm(forms.ModelForm):
"""Form handling the user profile, managing the files
This form is actually pretty bad and was made in the rush before the migration. It should be refactored.
TODO: refactor this form.
"""
"""Form handling the user profile, managing the files"""
class Meta:
model = User
@ -237,24 +234,30 @@ class UserProfileForm(forms.ModelForm):
]
widgets = {
"date_of_birth": SelectDate,
"profile_pict": forms.ClearableFileInput,
"avatar_pict": forms.ClearableFileInput,
"scrub_pict": forms.ClearableFileInput,
"phone": RegionalPhoneNumberWidget,
"parent_phone": RegionalPhoneNumberWidget,
"quote": forms.Textarea,
}
labels = {
"profile_pict": _(
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Image fields are injected here to override the file field provided by the model
# This would be better if we could have a SithImage sort of model input instead of a generic SithFile
self.fields["profile_pict"] = forms.ImageField(
required=False,
label=_(
"Profile: you need to be visible on the picture, in order to be recognized (e.g. by the barmen)"
),
"avatar_pict": _("Avatar: used on the forum"),
"scrub_pict": _("Scrub: let other know how your scrub looks like!"),
}
def generate_name(self, field_name, f):
field_name = field_name[:-4]
return field_name + str(self.instance.id) + "." + f.content_type.split("/")[-1]
)
self.fields["avatar_pict"] = forms.ImageField(
required=False,
label=_("Avatar: used on the forum"),
)
self.fields["scrub_pict"] = forms.ImageField(
required=False,
label=_("Scrub: let other know how your scrub looks like!"),
)
def process(self, files):
avatar = self.instance.avatar_pict
@ -271,11 +274,11 @@ class UserProfileForm(forms.ModelForm):
im = Image.open(BytesIO(f.read()))
new_file = SithFile(
parent=parent,
name=self.generate_name(field, f),
file=resize_image(im, 400, f.content_type.split("/")[-1]),
name=f"{field.removesuffix('_pict')}_{self.instance.id}.webp",
file=resize_image(im, 400, "webp"),
owner=self.instance,
is_folder=False,
mime_type=f.content_type,
mime_type="image/wepb",
size=f.size,
moderator=self.instance,
is_moderated=True,
@ -305,7 +308,7 @@ class UserProfileForm(forms.ModelForm):
% {
"file_name": f,
"msg": _(
"Bad image format, only jpeg, png, and gif are accepted"
"Bad image format, only jpeg, png, webp and gif are accepted"
),
},
)

View File

@ -31,7 +31,7 @@ from django.conf import settings
from django.contrib.auth import login, views
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import PermissionDenied, ValidationError
from django.core.exceptions import PermissionDenied
from django.forms import CheckboxSelectMultiple
from django.forms.models import modelform_factory
from django.http import Http404, HttpResponse
@ -52,7 +52,7 @@ from django.views.generic.dates import MonthMixin, YearMixin
from django.views.generic.edit import FormView, UpdateView
from honeypot.decorators import check_honeypot
from core.models import Gift, Preferences, SithFile, User
from core.models import Gift, Preferences, User
from core.views import (
CanEditMixin,
CanEditPropMixin,
@ -561,43 +561,6 @@ class UserListView(ListView, CanEditPropMixin):
template_name = "core/user_list.jinja"
class UserUploadProfilePictView(CanEditMixin, DetailView):
"""Handle the upload of the profile picture taken with webcam in navigator."""
model = User
pk_url_kwarg = "user_id"
template_name = "core/user_edit.jinja"
def post(self, request, *args, **kwargs):
from io import BytesIO
from PIL import Image
from core.utils import resize_image
self.object = self.get_object()
if self.object.profile_pict:
raise ValidationError(_("User already has a profile picture"))
f = request.FILES["new_profile_pict"]
parent = SithFile.objects.filter(parent=None, name="profiles").first()
name = str(self.object.id) + "_profile.jpg" # Webcamejs uploads JPGs
im = Image.open(BytesIO(f.read()))
new_file = SithFile(
parent=parent,
name=name,
file=resize_image(im, 400, f.content_type.split("/")[-1]),
owner=self.object,
is_folder=False,
mime_type=f.content_type,
size=f.size,
)
new_file.file.name = name
new_file.save()
self.object.profile_pict = new_file
self.object.save()
return redirect("core:user_edit", user_id=self.object.id)
class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
"""Edit a user's profile."""

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
# Generated by Django 4.2.14 on 2024-08-29 09:53
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("core", "0038_alter_preferences_receive_weekmail"),
("sas", "0002_auto_20161119_1241"),
]
operations = [
migrations.CreateModel(
name="SasFile",
fields=[],
options={
"proxy": True,
"indexes": [],
"constraints": [],
},
bases=("core.sithfile",),
),
]

View File

@ -110,19 +110,25 @@ class Picture(SasFile):
im = exif_auto_rotate(im)
except:
pass
file = resize_image(im, max(im.size), self.mime_type.split("/")[-1])
thumb = resize_image(im, 200, self.mime_type.split("/")[-1])
compressed = resize_image(im, 1200, self.mime_type.split("/")[-1])
# convert the compressed image and the thumbnail into webp
# The original image keeps its original type, because it's not
# meant to be shown on the website, but rather to keep the real image
# for less frequent cases (like downloading the pictures of an user)
extension = self.mime_type.split("/")[-1]
file = resize_image(im, max(im.size), extension)
thumb = resize_image(im, 200, "webp")
compressed = resize_image(im, 1200, "webp")
if overwrite:
self.file.delete()
self.thumbnail.delete()
self.compressed.delete()
new_extension_name = self.name.removesuffix(extension) + "webp"
self.file = file
self.file.name = self.name
self.thumbnail = thumb
self.thumbnail.name = self.name
self.thumbnail.name = new_extension_name
self.compressed = compressed
self.compressed.name = self.name
self.compressed.name = new_extension_name
self.save()
def rotate(self, degree):
@ -224,9 +230,9 @@ class Album(SasFile):
.first()
)
if p and p.file:
im = Image.open(BytesIO(p.file.read()))
self.file = resize_image(im, 200, "jpeg")
self.file.name = self.name + "/thumb.jpg"
image = resize_image(Image.open(BytesIO(p.file.read())), 200, "webp")
self.file = image
self.file.name = f"{self.name}/thumb.webp"
self.save()

View File

@ -48,26 +48,11 @@ class SubscriptionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Add fields to allow basic user creation
self.fields["last_name"] = forms.CharField(
max_length=User._meta.get_field("last_name").max_length
self.fields |= forms.fields_for_model(
User,
fields=["first_name", "last_name", "email", "date_of_birth"],
widgets={"date_of_birth": SelectDate},
)
self.fields["first_name"] = forms.CharField(
max_length=User._meta.get_field("first_name").max_length
)
self.fields["email"] = forms.EmailField()
self.fields["date_of_birth"] = forms.DateField(widget=SelectDate)
self.field_order = [
"member",
"last_name",
"first_name",
"email",
"date_of_birth",
"subscription_type",
"payment_method",
"location",
]
def clean_member(self):
subscriber = self.cleaned_data.get("member")
@ -124,9 +109,8 @@ class NewSubscription(CreateView):
form_class = SubscriptionForm
def dispatch(self, request, *arg, **kwargs):
res = super().dispatch(request, *arg, **kwargs)
if request.user.can_create_subscription:
return res
return super().dispatch(request, *arg, **kwargs)
raise PermissionDenied
def get_initial(self):