mirror of
https://github.com/ae-utbm/sith.git
synced 2025-07-10 03:49:24 +00:00
Move all user picture logic to sas
This commit is contained in:
@ -1,82 +0,0 @@
|
||||
import { paginated } from "#core:utils/api";
|
||||
import { HttpReader, ZipWriter } from "@zip.js/zip.js";
|
||||
import { showSaveFilePicker } from "native-file-system-adapter";
|
||||
import {
|
||||
type PictureSchema,
|
||||
type PicturesFetchPicturesData,
|
||||
picturesFetchPictures,
|
||||
} from "#openapi";
|
||||
|
||||
interface PagePictureConfig {
|
||||
userId?: number;
|
||||
albumId?: number;
|
||||
}
|
||||
|
||||
document.addEventListener("alpine:init", () => {
|
||||
Alpine.data("user_pictures", (config: PagePictureConfig) => ({
|
||||
isDownloading: false,
|
||||
loading: true,
|
||||
pictures: [] as PictureSchema[],
|
||||
albums: {} as Record<string, PictureSchema[]>,
|
||||
|
||||
async init() {
|
||||
const query: PicturesFetchPicturesData["query"] = {};
|
||||
|
||||
if (config.userId) {
|
||||
query.users_identified = [config.userId];
|
||||
} else {
|
||||
query.album_id = config.albumId;
|
||||
}
|
||||
this.pictures = await paginated(picturesFetchPictures, { query: query });
|
||||
|
||||
this.albums = this.pictures.reduce(
|
||||
(acc: Record<string, PictureSchema[]>, picture: PictureSchema) => {
|
||||
if (!acc[picture.album]) {
|
||||
acc[picture.album] = [];
|
||||
}
|
||||
acc[picture.album].push(picture);
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
this.loading = false;
|
||||
},
|
||||
|
||||
async downloadZip() {
|
||||
this.isDownloading = true;
|
||||
const bar = this.$refs.progress;
|
||||
bar.value = 0;
|
||||
bar.max = this.pictures.length;
|
||||
|
||||
const incrementProgressBar = (_total: number): undefined => {
|
||||
bar.value++;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const fileHandle = await showSaveFilePicker({
|
||||
_preferPolyfill: false,
|
||||
suggestedName: interpolate(
|
||||
gettext("pictures.%(extension)s"),
|
||||
{ extension: "zip" },
|
||||
true,
|
||||
),
|
||||
excludeAcceptAllOption: false,
|
||||
});
|
||||
const zipWriter = new ZipWriter(await fileHandle.createWritable());
|
||||
|
||||
await Promise.all(
|
||||
this.pictures.map((p: PictureSchema) => {
|
||||
const imgName = `${p.album}/IMG_${p.date.replace(/[:\-]/g, "_")}${p.name.slice(p.name.lastIndexOf("."))}`;
|
||||
return zipWriter.add(imgName, new HttpReader(p.full_size_url), {
|
||||
level: 9,
|
||||
lastModDate: new Date(p.date),
|
||||
onstart: incrementProgressBar,
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
await zipWriter.close();
|
||||
this.isDownloading = false;
|
||||
},
|
||||
}));
|
||||
});
|
@ -1,13 +0,0 @@
|
||||
{% macro download_button() %}
|
||||
<div x-show="pictures.length > 0" x-cloak>
|
||||
<button
|
||||
:disabled="isDownloading"
|
||||
class="btn btn-blue"
|
||||
@click="downloadZip()"
|
||||
>
|
||||
<i class="fa fa-download"></i>
|
||||
{% trans %}Download all pictures{% endtrans %}
|
||||
</button>
|
||||
<progress x-ref="progress" x-show="isDownloading"></progress>
|
||||
</div>
|
||||
{% endmacro %}
|
@ -1,53 +0,0 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
{% from "core/download_pictures.jinja" import download_button %}
|
||||
|
||||
{%- block additional_css -%}
|
||||
<link rel="stylesheet" href="{{ static('sas/css/album.scss') }}">
|
||||
{%- endblock -%}
|
||||
|
||||
{% block additional_js %}
|
||||
<script type="module" src="{{ static('bundled/user/pictures-index.ts') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
{% trans user_name=profile.get_display_name() %}{{ user_name }}'s pictures{% endtrans %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main x-data="user_pictures({ userId: {{ object.id }} })">
|
||||
{% if user.id == object.id %}
|
||||
{{ download_button() }}
|
||||
{% endif %}
|
||||
|
||||
<template x-for="[album, pictures] in Object.entries(albums)" x-cloak>
|
||||
<section>
|
||||
<br />
|
||||
<h4 x-text="album"></h4>
|
||||
<div class="photos">
|
||||
<template x-for="picture in pictures">
|
||||
<a :href="`/sas/picture/${picture.id}`">
|
||||
<div
|
||||
class="photo"
|
||||
:class="{not_moderated: !picture.is_moderated}"
|
||||
:style="`background-image: url(${picture.thumb_url})`"
|
||||
>
|
||||
<template x-if="!picture.is_moderated">
|
||||
<div class="overlay"> </div>
|
||||
<div class="text">{% trans %}To be moderated{% endtrans %}</div>
|
||||
</template>
|
||||
<template x-if="picture.is_moderated">
|
||||
<div class="text"> </div>
|
||||
</template>
|
||||
</div>
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<div class="photos" :aria-busy="loading"></div>
|
||||
</main>
|
||||
{% endblock content %}
|
||||
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
{% endblock script %}
|
@ -64,40 +64,6 @@ class TestImageAccess:
|
||||
assert not picture.is_owned_by(user)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestUserPicture:
|
||||
def test_anonymous_user_unauthorized(self, client):
|
||||
"""An anonymous user shouldn't have access to an user's photo page."""
|
||||
response = client.get(
|
||||
reverse(
|
||||
"core:user_pictures",
|
||||
kwargs={"user_id": User.objects.get(username="sli").pk},
|
||||
)
|
||||
)
|
||||
assert response.status_code == 403
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("username", "status"),
|
||||
[
|
||||
("guy", 403),
|
||||
("root", 200),
|
||||
("skia", 200),
|
||||
("sli", 200),
|
||||
],
|
||||
)
|
||||
def test_page_is_working(self, client, username, status):
|
||||
"""Only user that subscribed (or admins) should be able to see the page."""
|
||||
# Test for simple user
|
||||
client.force_login(User.objects.get(username=username))
|
||||
response = client.get(
|
||||
reverse(
|
||||
"core:user_pictures",
|
||||
kwargs={"user_id": User.objects.get(username="sli").pk},
|
||||
)
|
||||
)
|
||||
assert response.status_code == status
|
||||
|
||||
|
||||
# TODO: many tests on the pages:
|
||||
# - renaming a page
|
||||
# - changing a page's parent --> check that page's children's full_name
|
||||
|
@ -68,7 +68,6 @@ from core.views import (
|
||||
UserGodfathersView,
|
||||
UserListView,
|
||||
UserMiniView,
|
||||
UserPicturesView,
|
||||
UserPreferencesView,
|
||||
UserStatsView,
|
||||
UserToolsView,
|
||||
@ -143,9 +142,6 @@ urlpatterns = [
|
||||
path("user/", UserListView.as_view(), name="user_list"),
|
||||
path("user/<int:user_id>/mini/", UserMiniView.as_view(), name="user_profile_mini"),
|
||||
path("user/<int:user_id>/", UserView.as_view(), name="user_profile"),
|
||||
path(
|
||||
"user/<int:user_id>/pictures/", UserPicturesView.as_view(), name="user_pictures"
|
||||
),
|
||||
path(
|
||||
"user/<int:user_id>/godfathers/",
|
||||
UserGodfathersView.as_view(),
|
||||
|
@ -200,7 +200,7 @@ class UserTabsMixin(TabedViewMixin):
|
||||
"name": _("Family"),
|
||||
},
|
||||
{
|
||||
"url": reverse("core:user_pictures", kwargs={"user_id": user.id}),
|
||||
"url": reverse("sas:user_pictures", kwargs={"user_id": user.id}),
|
||||
"slug": "pictures",
|
||||
"name": _("Pictures"),
|
||||
},
|
||||
@ -297,16 +297,6 @@ class UserView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
return kwargs
|
||||
|
||||
|
||||
class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
"""Display a user's pictures."""
|
||||
|
||||
model = User
|
||||
pk_url_kwarg = "user_id"
|
||||
context_object_name = "profile"
|
||||
template_name = "core/user_pictures.jinja"
|
||||
current_tab = "pictures"
|
||||
|
||||
|
||||
def delete_user_godfather(request, user_id, godfather_id, is_father):
|
||||
user_is_admin = request.user.is_root or request.user.is_board_member
|
||||
if user_id != request.user.id and not user_is_admin:
|
||||
|
Reference in New Issue
Block a user