Merge branch 'download-all-my-pictures' into 'master'

Add feature to download all of your pictures as a user


See merge request ae-utbm/Sith!319
This commit is contained in:
Julien Constant | Tinople 2022-08-09 13:51:04 +00:00
commit effed9c760
8 changed files with 646 additions and 520 deletions

View File

@ -22,7 +22,7 @@
# #
# #
from django.urls import re_path, include from django.urls import re_path, path, include
from api.views import * from api.views import *
from rest_framework import routers from rest_framework import routers
@ -54,4 +54,5 @@ urlpatterns = [
re_path(r"^markdown$", RenderMarkdown, name="api_markdown"), re_path(r"^markdown$", RenderMarkdown, name="api_markdown"),
re_path(r"^mailings$", FetchMailingLists, name="mailings_fetch"), re_path(r"^mailings$", FetchMailingLists, name="mailings_fetch"),
re_path(r"^uv$", uv_endpoint, name="uv_endpoint"), re_path(r"^uv$", uv_endpoint, name="uv_endpoint"),
path("sas/<int:user>", all_pictures_of_user_endpoint, name="all_pictures_of_user"),
] ]

View File

@ -78,3 +78,4 @@ from .club import *
from .group import * from .group import *
from .launderette import * from .launderette import *
from .uv import * from .uv import *
from .sas import *

42
api/views/sas.py Normal file
View File

@ -0,0 +1,42 @@
from typing import List
from rest_framework.decorators import api_view, renderer_classes
from rest_framework.exceptions import PermissionDenied
from rest_framework.generics import get_object_or_404
from rest_framework.renderers import JSONRenderer
from rest_framework.request import Request
from rest_framework.response import Response
from core.views import can_edit_prop
from core.models import User
from sas.models import Picture
def all_pictures_of_user(user: User) -> List[Picture]:
return [
relation.picture
for relation in user.pictures.exclude(picture=None)
.order_by("-picture__parent__date", "id")
.select_related("picture__parent")
]
@api_view(["GET"])
@renderer_classes((JSONRenderer,))
def all_pictures_of_user_endpoint(request: Request, user: int):
requested_user: User = get_object_or_404(User, pk=user)
if not can_edit_prop(requested_user, request.user):
raise PermissionDenied
return Response(
[
{
"name": f"{picture.parent.name} - {picture.name}",
"date": picture.date,
"author": str(picture.owner),
"full_size_url": picture.get_download_url(),
"compressed_url": picture.get_download_compressed_url(),
"thumb_url": picture.get_download_thumb_url(),
}
for picture in all_pictures_of_user(requested_user)
]
)

View File

@ -6,6 +6,7 @@
{% block content %} {% block content %}
{% for a in albums %} {% for a in albums %}
<button id="download_all_pictures", onclick=download_pictures()>{% trans %}Download all my pictures{% endtrans %}</button>
<div style="padding: 10px"> <div style="padding: 10px">
<h4>{{ a.name }}</h4> <h4>{{ a.name }}</h4>
<hr> <hr>
@ -20,3 +21,67 @@
{% endfor %} {% endfor %}
{% endblock %} {% endblock %}
{% block script %}
{{ super() }}
<script type="text/javascript">
/**
* Download a list of files.
* @author speedplane
*/
function download_files(files) {
function download_next(i) {
if (i >= files.length) {
return;
}
var a = document.createElement('a');
a.href = files[i].download;
a.target = '_parent';
// Use a.download if available, it prevents plugins from opening.
if ('download' in a) {
a.download = files[i].filename;
}
// Add a to the doc for click to work.
(document.body || document.documentElement).appendChild(a);
if (a.click) {
a.click(); // The click method is supported by most browsers.
} else {
$(a).click(); // Backup using jquery
}
// Delete the temporary link.
a.parentNode.removeChild(a);
// Download the next file with a small timeout. The timeout is necessary
// for IE, which will otherwise only download the first file.
setTimeout(function() {
download_next(i + 1);
}, 500);
}
// Initiate the first download.
download_next(0);
}
function download_pictures() {
$("#download_all_pictures").prop("disabled", true);
var xhr = new XMLHttpRequest();
$.ajax({
type: "GET",
url: "{{ url('api:all_pictures_of_user', user=object.id) }}",
tryCount: 0,
xhr: function(){
return xhr;
},
success: function(data){
$("#download_all_pictures").prop("disabled", false);
to_download = [];
data.forEach(picture =>
to_download.push({ download: picture["full_size_url"], filename: picture["name"] })
);
download_files(to_download);
},
error: function(data){
console.log("Error retrieving data from url: " + data);
$("#download_all_pictures").text("{% trans %}Error downloading your pictures{% endtrans %}");
}
});
}
</script>
{% endblock %}

View File

@ -24,6 +24,7 @@
# This file contains all the views that concern the page model # This file contains all the views that concern the page model
from django.shortcuts import redirect, get_object_or_404 from django.shortcuts import redirect, get_object_or_404
from django.utils.http import http_date
from django.views.generic import ListView, DetailView, TemplateView from django.views.generic import ListView, DetailView, TemplateView
from django.views.generic.edit import UpdateView, FormMixin, DeleteView from django.views.generic.edit import UpdateView, FormMixin, DeleteView
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
@ -68,6 +69,7 @@ def send_file(request, file_id, file_class=SithFile, file_attr="file"):
with open(filepath.encode("utf-8"), "rb") as filename: with open(filepath.encode("utf-8"), "rb") as filename:
wrapper = FileWrapper(filename) wrapper = FileWrapper(filename)
response = HttpResponse(wrapper, content_type=f.mime_type) response = HttpResponse(wrapper, content_type=f.mime_type)
response["Last-Modified"] = http_date(f.date.timestamp())
response["Content-Length"] = os.path.getsize(filepath.encode("utf-8")) response["Content-Length"] = os.path.getsize(filepath.encode("utf-8"))
response["Content-Disposition"] = ('inline; filename="%s"' % f.name).encode( response["Content-Disposition"] = ('inline; filename="%s"' % f.name).encode(
"utf-8" "utf-8"

View File

@ -48,6 +48,7 @@ from django.views.generic.dates import YearMixin, MonthMixin
from datetime import timedelta, date from datetime import timedelta, date
import logging import logging
from api.views.sas import all_pictures_of_user
from core.views import ( from core.views import (
CanViewMixin, CanViewMixin,
@ -325,19 +326,15 @@ class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
kwargs = super(UserPicturesView, self).get_context_data(**kwargs) kwargs = super(UserPicturesView, self).get_context_data(**kwargs)
kwargs["albums"] = [] kwargs["albums"] = []
kwargs["pictures"] = {} kwargs["pictures"] = {}
picture_qs = ( picture_qs = all_pictures_of_user(self.object)
self.object.pictures.exclude(picture=None)
.order_by("-picture__parent__date", "id")
.select_related("picture__parent")
)
last_album = None last_album = None
for pict_relation in picture_qs: for picture in picture_qs:
album = pict_relation.picture.parent album = picture.parent
if album.id != last_album: if album.id != last_album:
kwargs["albums"].append(album) kwargs["albums"].append(album)
kwargs["pictures"][album.id] = [] kwargs["pictures"][album.id] = []
last_album = album.id last_album = album.id
kwargs["pictures"][album.id].append(pict_relation.picture) kwargs["pictures"][album.id].append(picture)
return kwargs return kwargs

View File

@ -39,7 +39,7 @@ La traduction se fait en trois étapes. Il faut d'abord générer un fichier de
.. sourcecode:: bash .. sourcecode:: bash
./manage.py makemessages --ignore "env/*" -e py,jinja ./manage.py makemessages --locale=fr --ignore "env/*" -e py,jinja
Éditer le fichier django.po Éditer le fichier django.po
--------------------------- ---------------------------

File diff suppressed because it is too large Load Diff