mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-10 00:03:24 +00:00
commit
878ee99fe4
18
.github/workflows/deploy.yml
vendored
18
.github/workflows/deploy.yml
vendored
@ -44,21 +44,3 @@ jobs:
|
|||||||
poetry run ./manage.py compilemessages
|
poetry run ./manage.py compilemessages
|
||||||
|
|
||||||
sudo systemctl restart uwsgi
|
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
|
|
||||||
|
18
.github/workflows/taiste.yml
vendored
18
.github/workflows/taiste.yml
vendored
@ -43,21 +43,3 @@ jobs:
|
|||||||
poetry run ./manage.py compilemessages
|
poetry run ./manage.py compilemessages
|
||||||
|
|
||||||
sudo systemctl restart uwsgi
|
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
|
|
||||||
|
@ -34,7 +34,6 @@ from django.utils import timezone
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from club.models import Club
|
from club.models import Club
|
||||||
from core import utils
|
|
||||||
from core.models import Notification, Preferences, RealGroup, User
|
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)
|
alert_msg = models.TextField(_("alert message"), default="", blank=True)
|
||||||
info_msg = models.TextField(_("info message"), default="", blank=True)
|
info_msg = models.TextField(_("info message"), default="", blank=True)
|
||||||
weekmail_destinations = models.TextField(_("weekmail destinations"), default="")
|
weekmail_destinations = models.TextField(_("weekmail destinations"), default="")
|
||||||
version = utils.get_git_revision_short_hash()
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "⛩ Sith ⛩"
|
return "⛩ Sith ⛩"
|
||||||
|
@ -234,10 +234,10 @@ Welcome to the wiki page!
|
|||||||
skia_profile = SithFile(
|
skia_profile = SithFile(
|
||||||
parent=profiles_root,
|
parent=profiles_root,
|
||||||
name=name,
|
name=name,
|
||||||
file=resize_image(Image.open(BytesIO(f.read())), 400, "JPEG"),
|
file=resize_image(Image.open(BytesIO(f.read())), 400, "WEBP"),
|
||||||
owner=skia,
|
owner=skia,
|
||||||
is_folder=False,
|
is_folder=False,
|
||||||
mime_type="image/jpeg",
|
mime_type="image/webp",
|
||||||
size=skia_profile_path.stat().st_size,
|
size=skia_profile_path.stat().st_size,
|
||||||
)
|
)
|
||||||
skia_profile.file.name = name
|
skia_profile.file.name = name
|
||||||
@ -368,10 +368,10 @@ Welcome to the wiki page!
|
|||||||
richard_profile = SithFile(
|
richard_profile = SithFile(
|
||||||
parent=profiles_root,
|
parent=profiles_root,
|
||||||
name=name,
|
name=name,
|
||||||
file=resize_image(Image.open(BytesIO(f.read())), 400, "JPEG"),
|
file=resize_image(Image.open(BytesIO(f.read())), 400, "WEBP"),
|
||||||
owner=richard,
|
owner=richard,
|
||||||
is_folder=False,
|
is_folder=False,
|
||||||
mime_type="image/jpeg",
|
mime_type="image/webp",
|
||||||
size=richard_profile_path.stat().st_size,
|
size=richard_profile_path.stat().st_size,
|
||||||
)
|
)
|
||||||
richard_profile.file.name = name
|
richard_profile.file.name = name
|
||||||
@ -853,10 +853,10 @@ Welcome to the wiki page!
|
|||||||
sli_profile = SithFile(
|
sli_profile = SithFile(
|
||||||
parent=profiles_root,
|
parent=profiles_root,
|
||||||
name=name,
|
name=name,
|
||||||
file=resize_image(Image.open(BytesIO(f.read())), 400, "JPEG"),
|
file=resize_image(Image.open(BytesIO(f.read())), 400, "WEBP"),
|
||||||
owner=sli,
|
owner=sli,
|
||||||
is_folder=False,
|
is_folder=False,
|
||||||
mime_type="image/jpeg",
|
mime_type="image/webp",
|
||||||
size=sli_profile_path.stat().st_size,
|
size=sli_profile_path.stat().st_size,
|
||||||
)
|
)
|
||||||
sli_profile.file.name = name
|
sli_profile.file.name = name
|
||||||
@ -887,10 +887,10 @@ Welcome to the wiki page!
|
|||||||
krophil_profile = SithFile(
|
krophil_profile = SithFile(
|
||||||
parent=profiles_root,
|
parent=profiles_root,
|
||||||
name=name,
|
name=name,
|
||||||
file=resize_image(Image.open(BytesIO(f.read())), 400, "JPEG"),
|
file=resize_image(Image.open(BytesIO(f.read())), 400, "WEBP"),
|
||||||
owner=krophil,
|
owner=krophil,
|
||||||
is_folder=False,
|
is_folder=False,
|
||||||
mime_type="image/jpeg",
|
mime_type="image/webp",
|
||||||
size=krophil_profile_path.stat().st_size,
|
size=krophil_profile_path.stat().st_size,
|
||||||
)
|
)
|
||||||
krophil_profile.file.name = name
|
krophil_profile.file.name = name
|
||||||
@ -1217,13 +1217,13 @@ Welcome to the wiki page!
|
|||||||
parent=album,
|
parent=album,
|
||||||
name=p.name,
|
name=p.name,
|
||||||
file=resize_image(
|
file=resize_image(
|
||||||
Image.open(BytesIO(p.read_bytes())), 1000, "JPEG"
|
Image.open(BytesIO(p.read_bytes())), 1000, "WEBP"
|
||||||
),
|
),
|
||||||
owner=root,
|
owner=root,
|
||||||
is_folder=False,
|
is_folder=False,
|
||||||
is_in_sas=True,
|
is_in_sas=True,
|
||||||
is_moderated=True,
|
is_moderated=True,
|
||||||
mime_type="image/jpeg",
|
mime_type="image/webp",
|
||||||
size=p.stat().st_size,
|
size=p.stat().st_size,
|
||||||
)
|
)
|
||||||
pict.file.name = p.name
|
pict.file.name = p.name
|
||||||
@ -1252,10 +1252,10 @@ Welcome to the wiki page!
|
|||||||
skia_profile = SithFile(
|
skia_profile = SithFile(
|
||||||
parent=profiles_root,
|
parent=profiles_root,
|
||||||
name=name,
|
name=name,
|
||||||
file=resize_image(Image.open(BytesIO(f.read())), 400, "JPEG"),
|
file=resize_image(Image.open(BytesIO(f.read())), 400, "WEBP"),
|
||||||
owner=skia,
|
owner=skia,
|
||||||
is_folder=False,
|
is_folder=False,
|
||||||
mime_type="image/jpeg",
|
mime_type="image/webp",
|
||||||
size=skia_profile_path.stat().st_size,
|
size=skia_profile_path.stat().st_size,
|
||||||
)
|
)
|
||||||
skia_profile.file.name = name
|
skia_profile.file.name = name
|
||||||
|
@ -290,11 +290,6 @@
|
|||||||
</a>
|
</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<br>
|
<br>
|
||||||
<code class="version">
|
|
||||||
{% cache 1000 "sith_version" %}
|
|
||||||
{% trans %}Sith version:{% endtrans %} {{ get_sith().version }}
|
|
||||||
{% endcache %}
|
|
||||||
</code>
|
|
||||||
</footer>
|
</footer>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if user.promo %}
|
{% if user.promo and user.promo_has_logo() %}
|
||||||
<div class="user_mini_profile_promo">
|
<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" />
|
<img src="{{ static('core/img/promo_%02d.png' % user.promo) }}" title="Promo {{ user.promo }}" alt="Promo {{ user.promo }}" class="promo_pict" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
<p>
|
<p>
|
||||||
{{ form[field_name].label }}
|
{{ form[field_name].label }}
|
||||||
</p>
|
</p>
|
||||||
|
{{ form[field_name].errors }}
|
||||||
{%- else -%}
|
{%- else -%}
|
||||||
<em>{% trans %}To edit your profile picture, ask a member of the AE{% endtrans %}</em>
|
<em>{% trans %}To edit your profile picture, ask a member of the AE{% endtrans %}</em>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
@ -163,7 +164,7 @@
|
|||||||
/* Stop camera */
|
/* Stop camera */
|
||||||
this.video.pause()
|
this.video.pause()
|
||||||
this.video.srcObject.getTracks().forEach((track) => {
|
this.video.srcObject.getTracks().forEach((track) => {
|
||||||
if (track.readyState == 'live') {
|
if (track.readyState === 'live') {
|
||||||
track.stop();
|
track.stop();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -171,8 +172,8 @@
|
|||||||
canvas.toBlob((blob) => {
|
canvas.toBlob((blob) => {
|
||||||
let file = new File(
|
let file = new File(
|
||||||
[blob],
|
[blob],
|
||||||
"{% trans %}captured{% endtrans %}.png",
|
"{% trans %}captured{% endtrans %}.webp",
|
||||||
{ type: "image/jpeg" },
|
{ type: "image/webp" },
|
||||||
);
|
);
|
||||||
|
|
||||||
let list = new DataTransfer();
|
let list = new DataTransfer();
|
||||||
|
@ -127,11 +127,6 @@ urlpatterns = [
|
|||||||
UserUpdateProfileView.as_view(),
|
UserUpdateProfileView.as_view(),
|
||||||
name="user_edit",
|
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>/clubs/", UserClubView.as_view(), name="user_clubs"),
|
||||||
path(
|
path(
|
||||||
"user/<int:user_id>/prefs/",
|
"user/<int:user_id>/prefs/",
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
import subprocess
|
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
# Image utils
|
# Image utils
|
||||||
@ -29,17 +28,6 @@ from PIL import ExifTags
|
|||||||
from PIL.Image import Resampling
|
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:
|
def get_start_of_semester(today: Optional[date] = None) -> date:
|
||||||
"""Return the date of the start of the semester of the given 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.
|
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):
|
def resize_image(im, edge, img_format):
|
||||||
(w, h) = im.size
|
(w, h) = im.size
|
||||||
(width, height) = scale_dimension(w, h, long_edge=edge)
|
(width, height) = scale_dimension(w, h, long_edge=edge)
|
||||||
|
img_format = img_format.upper()
|
||||||
content = BytesIO()
|
content = BytesIO()
|
||||||
# use the lanczos filter for antialiasing and discard the alpha channel
|
# 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:
|
try:
|
||||||
im.save(
|
im.save(
|
||||||
fp=content,
|
fp=content,
|
||||||
format=img_format.upper(),
|
format=img_format,
|
||||||
quality=90,
|
quality=90,
|
||||||
optimize=True,
|
optimize=True,
|
||||||
progressive=True,
|
progressive=True,
|
||||||
@ -117,7 +109,7 @@ def resize_image(im, edge, img_format):
|
|||||||
PIL.ImageFile.MAXBLOCK = im.size[0] * im.size[1]
|
PIL.ImageFile.MAXBLOCK = im.size[0] * im.size[1]
|
||||||
im.save(
|
im.save(
|
||||||
fp=content,
|
fp=content,
|
||||||
format=img_format.upper(),
|
format=img_format,
|
||||||
quality=90,
|
quality=90,
|
||||||
optimize=True,
|
optimize=True,
|
||||||
progressive=True,
|
progressive=True,
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# OR WITHIN THE LOCAL FILE "LICENSE"
|
# OR WITHIN THE LOCAL FILE "LICENSE"
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
import mimetypes
|
||||||
from urllib.parse import quote, urljoin
|
from urllib.parse import quote, urljoin
|
||||||
|
|
||||||
# This file contains all the views that concern the page model
|
# 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):
|
if not can_view(f, request.user) and not is_logged_in_counter(request):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
name = getattr(f, file_attr).name
|
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:
|
if not settings.DEBUG:
|
||||||
# When receiving a response with the Accel-Redirect header,
|
# When receiving a response with the Accel-Redirect header,
|
||||||
# the reverse proxy will automatically handle the file sending.
|
# the reverse proxy will automatically handle the file sending.
|
||||||
# This is really hard to test (thus isn't tested)
|
# This is really hard to test (thus isn't tested)
|
||||||
# so please do not mess with this.
|
# so please do not mess with this.
|
||||||
response = HttpResponse(status=200)
|
response["Content-Type"] = "" # automatically set by nginx
|
||||||
response["Content-Type"] = ""
|
|
||||||
response["X-Accel-Redirect"] = quote(urljoin(settings.MEDIA_URL, name))
|
response["X-Accel-Redirect"] = quote(urljoin(settings.MEDIA_URL, name))
|
||||||
return response
|
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:
|
with open(filepath, "rb") as filename:
|
||||||
wrapper = FileWrapper(filename)
|
response.content = FileWrapper(filename)
|
||||||
response = HttpResponse(wrapper, content_type=f.mime_type)
|
response["Content-Type"] = mimetypes.guess_type(filepath)[0]
|
||||||
response["Last-Modified"] = http_date(f.date.timestamp())
|
response["Last-Modified"] = http_date(f.date.timestamp())
|
||||||
response["Content-Length"] = filepath.stat().st_size
|
response["Content-Length"] = filepath.stat().st_size
|
||||||
response["Content-Disposition"] = ('inline; filename="%s"' % f.name).encode(
|
|
||||||
"utf-8"
|
|
||||||
)
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@ -201,10 +201,7 @@ class RegisteringForm(UserCreationForm):
|
|||||||
|
|
||||||
|
|
||||||
class UserProfileForm(forms.ModelForm):
|
class UserProfileForm(forms.ModelForm):
|
||||||
"""Form handling the user profile, managing the files
|
"""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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
@ -237,24 +234,30 @@ class UserProfileForm(forms.ModelForm):
|
|||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
"date_of_birth": SelectDate,
|
"date_of_birth": SelectDate,
|
||||||
"profile_pict": forms.ClearableFileInput,
|
|
||||||
"avatar_pict": forms.ClearableFileInput,
|
|
||||||
"scrub_pict": forms.ClearableFileInput,
|
|
||||||
"phone": RegionalPhoneNumberWidget,
|
"phone": RegionalPhoneNumberWidget,
|
||||||
"parent_phone": RegionalPhoneNumberWidget,
|
"parent_phone": RegionalPhoneNumberWidget,
|
||||||
"quote": forms.Textarea,
|
"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)"
|
"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!"),
|
self.fields["avatar_pict"] = forms.ImageField(
|
||||||
}
|
required=False,
|
||||||
|
label=_("Avatar: used on the forum"),
|
||||||
def generate_name(self, field_name, f):
|
)
|
||||||
field_name = field_name[:-4]
|
self.fields["scrub_pict"] = forms.ImageField(
|
||||||
return field_name + str(self.instance.id) + "." + f.content_type.split("/")[-1]
|
required=False,
|
||||||
|
label=_("Scrub: let other know how your scrub looks like!"),
|
||||||
|
)
|
||||||
|
|
||||||
def process(self, files):
|
def process(self, files):
|
||||||
avatar = self.instance.avatar_pict
|
avatar = self.instance.avatar_pict
|
||||||
@ -271,11 +274,11 @@ class UserProfileForm(forms.ModelForm):
|
|||||||
im = Image.open(BytesIO(f.read()))
|
im = Image.open(BytesIO(f.read()))
|
||||||
new_file = SithFile(
|
new_file = SithFile(
|
||||||
parent=parent,
|
parent=parent,
|
||||||
name=self.generate_name(field, f),
|
name=f"{field.removesuffix('_pict')}_{self.instance.id}.webp",
|
||||||
file=resize_image(im, 400, f.content_type.split("/")[-1]),
|
file=resize_image(im, 400, "webp"),
|
||||||
owner=self.instance,
|
owner=self.instance,
|
||||||
is_folder=False,
|
is_folder=False,
|
||||||
mime_type=f.content_type,
|
mime_type="image/wepb",
|
||||||
size=f.size,
|
size=f.size,
|
||||||
moderator=self.instance,
|
moderator=self.instance,
|
||||||
is_moderated=True,
|
is_moderated=True,
|
||||||
@ -305,7 +308,7 @@ class UserProfileForm(forms.ModelForm):
|
|||||||
% {
|
% {
|
||||||
"file_name": f,
|
"file_name": f,
|
||||||
"msg": _(
|
"msg": _(
|
||||||
"Bad image format, only jpeg, png, and gif are accepted"
|
"Bad image format, only jpeg, png, webp and gif are accepted"
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -31,7 +31,7 @@ from django.conf import settings
|
|||||||
from django.contrib.auth import login, views
|
from django.contrib.auth import login, views
|
||||||
from django.contrib.auth.forms import PasswordChangeForm
|
from django.contrib.auth.forms import PasswordChangeForm
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
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 import CheckboxSelectMultiple
|
||||||
from django.forms.models import modelform_factory
|
from django.forms.models import modelform_factory
|
||||||
from django.http import Http404, HttpResponse
|
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 django.views.generic.edit import FormView, UpdateView
|
||||||
from honeypot.decorators import check_honeypot
|
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 (
|
from core.views import (
|
||||||
CanEditMixin,
|
CanEditMixin,
|
||||||
CanEditPropMixin,
|
CanEditPropMixin,
|
||||||
@ -561,43 +561,6 @@ class UserListView(ListView, CanEditPropMixin):
|
|||||||
template_name = "core/user_list.jinja"
|
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):
|
class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
|
||||||
"""Edit a user's profile."""
|
"""Edit a user's profile."""
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
23
sas/migrations/0003_sasfile.py
Normal file
23
sas/migrations/0003_sasfile.py
Normal 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",),
|
||||||
|
),
|
||||||
|
]
|
@ -110,19 +110,25 @@ class Picture(SasFile):
|
|||||||
im = exif_auto_rotate(im)
|
im = exif_auto_rotate(im)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
file = resize_image(im, max(im.size), self.mime_type.split("/")[-1])
|
# convert the compressed image and the thumbnail into webp
|
||||||
thumb = resize_image(im, 200, self.mime_type.split("/")[-1])
|
# The original image keeps its original type, because it's not
|
||||||
compressed = resize_image(im, 1200, self.mime_type.split("/")[-1])
|
# 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:
|
if overwrite:
|
||||||
self.file.delete()
|
self.file.delete()
|
||||||
self.thumbnail.delete()
|
self.thumbnail.delete()
|
||||||
self.compressed.delete()
|
self.compressed.delete()
|
||||||
|
new_extension_name = self.name.removesuffix(extension) + "webp"
|
||||||
self.file = file
|
self.file = file
|
||||||
self.file.name = self.name
|
self.file.name = self.name
|
||||||
self.thumbnail = thumb
|
self.thumbnail = thumb
|
||||||
self.thumbnail.name = self.name
|
self.thumbnail.name = new_extension_name
|
||||||
self.compressed = compressed
|
self.compressed = compressed
|
||||||
self.compressed.name = self.name
|
self.compressed.name = new_extension_name
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def rotate(self, degree):
|
def rotate(self, degree):
|
||||||
@ -224,9 +230,9 @@ class Album(SasFile):
|
|||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
if p and p.file:
|
if p and p.file:
|
||||||
im = Image.open(BytesIO(p.file.read()))
|
image = resize_image(Image.open(BytesIO(p.file.read())), 200, "webp")
|
||||||
self.file = resize_image(im, 200, "jpeg")
|
self.file = image
|
||||||
self.file.name = self.name + "/thumb.jpg"
|
self.file.name = f"{self.name}/thumb.webp"
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,26 +48,11 @@ class SubscriptionForm(forms.ModelForm):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
# Add fields to allow basic user creation
|
self.fields |= forms.fields_for_model(
|
||||||
self.fields["last_name"] = forms.CharField(
|
User,
|
||||||
max_length=User._meta.get_field("last_name").max_length
|
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):
|
def clean_member(self):
|
||||||
subscriber = self.cleaned_data.get("member")
|
subscriber = self.cleaned_data.get("member")
|
||||||
@ -124,9 +109,8 @@ class NewSubscription(CreateView):
|
|||||||
form_class = SubscriptionForm
|
form_class = SubscriptionForm
|
||||||
|
|
||||||
def dispatch(self, request, *arg, **kwargs):
|
def dispatch(self, request, *arg, **kwargs):
|
||||||
res = super().dispatch(request, *arg, **kwargs)
|
|
||||||
if request.user.can_create_subscription:
|
if request.user.can_create_subscription:
|
||||||
return res
|
return super().dispatch(request, *arg, **kwargs)
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user