|
2 | 2 |
|
3 | 3 | from django.conf import settings |
4 | 4 | from django.core.exceptions import ValidationError |
| 5 | +from django.shortcuts import get_list_or_404 |
5 | 6 | from django.urls import reverse |
6 | | -from ninja import Body, File, Query |
| 7 | +from ninja import Body, Query, UploadedFile |
| 8 | +from ninja.errors import HttpError |
7 | 9 | from ninja.security import SessionAuth |
8 | 10 | from ninja_extra import ControllerBase, api_controller, paginate, route |
9 | 11 | from ninja_extra.exceptions import NotFound, PermissionDenied |
|
16 | 18 | CanAccessLookup, |
17 | 19 | CanEdit, |
18 | 20 | CanView, |
| 21 | + HasPerm, |
19 | 22 | IsInGroup, |
20 | 23 | IsRoot, |
21 | 24 | ) |
22 | 25 | from core.models import Notification, User |
23 | | -from core.schemas import UploadedImage |
| 26 | +from core.utils import get_list_exact_or_404 |
24 | 27 | from sas.models import Album, PeoplePictureRelation, Picture |
25 | 28 | from sas.schemas import ( |
26 | 29 | AlbumAutocompleteSchema, |
27 | 30 | AlbumFilterSchema, |
28 | 31 | AlbumSchema, |
29 | 32 | IdentifiedUserSchema, |
30 | 33 | ModerationRequestSchema, |
| 34 | + MoveAlbumSchema, |
31 | 35 | PictureFilterSchema, |
32 | 36 | PictureSchema, |
33 | 37 | ) |
@@ -69,6 +73,48 @@ def autocomplete_album(self, filters: Query[AlbumFilterSchema]): |
69 | 73 | Album.objects.viewable_by(self.context.request.user).order_by("-date") |
70 | 74 | ) |
71 | 75 |
|
| 76 | + @route.patch("/parent", permissions=[IsAuthenticated]) |
| 77 | + def change_album_parent(self, payload: list[MoveAlbumSchema]): |
| 78 | + """Change parents of albums |
| 79 | +
|
| 80 | + Note: |
| 81 | + For this operation to work, the user must be authorized |
| 82 | + to edit both the moved albums and their new parent. |
| 83 | + """ |
| 84 | + user: User = self.context.request.user |
| 85 | + albums: list[Album] = get_list_exact_or_404( |
| 86 | + Album, pk__in={a.id for a in payload} |
| 87 | + ) |
| 88 | + if not user.has_perm("sas.change_album"): |
| 89 | + unauthorized = [a.id for a in albums if not user.can_edit(a)] |
| 90 | + raise PermissionDenied( |
| 91 | + f"You can't move the following albums : {unauthorized}" |
| 92 | + ) |
| 93 | + parents: list[Album] = get_list_exact_or_404( |
| 94 | + Album, pk__in={a.new_parent_id for a in payload} |
| 95 | + ) |
| 96 | + if not user.has_perm("sas.change_album"): |
| 97 | + unauthorized = [a.id for a in parents if not user.can_edit(a)] |
| 98 | + raise PermissionDenied( |
| 99 | + f"You can't move to the following albums : {unauthorized}" |
| 100 | + ) |
| 101 | + id_to_new_parent = {i.id: i.new_parent_id for i in payload} |
| 102 | + for album in albums: |
| 103 | + album.parent_id = id_to_new_parent[album.id] |
| 104 | + # known caveat : moving an album won't move it's thumbnail. |
| 105 | + # E.g. if the album foo/bar is moved to foo/baz, |
| 106 | + # the thumbnail will still be foo/bar/thumb.webp |
| 107 | + # This has no impact for the end user |
| 108 | + # and doing otherwise would be hard for us to implement, |
| 109 | + # because we would then have to manage rollbacks on fail. |
| 110 | + Album.objects.bulk_update(albums, fields=["parent_id"]) |
| 111 | + |
| 112 | + @route.delete("", permissions=[HasPerm("sas.delete_album")]) |
| 113 | + def delete_album(self, album_ids: list[int]): |
| 114 | + # known caveat : deleting an album doesn't delete the pictures on the disk. |
| 115 | + # It's a db only operation. |
| 116 | + albums: list[Album] = get_list_or_404(Album, pk__in=album_ids) |
| 117 | + |
72 | 118 |
|
73 | 119 | @api_controller("/sas/picture") |
74 | 120 | class PicturesController(ControllerBase): |
@@ -110,27 +156,25 @@ def fetch_pictures(self, filters: Query[PictureFilterSchema]): |
110 | 156 | }, |
111 | 157 | url_name="upload_picture", |
112 | 158 | ) |
113 | | - def upload_picture(self, album_id: Body[int], picture: File[UploadedImage]): |
| 159 | + def upload_picture(self, album_id: Body[int], picture: UploadedFile): |
114 | 160 | album = self.get_object_or_exception(Album, pk=album_id) |
115 | 161 | user = self.context.request.user |
116 | 162 | self_moderate = user.has_perm("sas.moderate_sasfile") |
117 | 163 | new = Picture( |
118 | 164 | parent=album, |
119 | 165 | name=picture.name, |
120 | | - file=picture, |
| 166 | + original=picture, |
121 | 167 | owner=user, |
122 | 168 | is_moderated=self_moderate, |
123 | | - is_folder=False, |
124 | | - mime_type=picture.content_type, |
125 | 169 | ) |
126 | 170 | if self_moderate: |
127 | 171 | new.moderator = user |
| 172 | + new.generate_thumbnails() |
128 | 173 | try: |
129 | | - new.generate_thumbnails() |
130 | 174 | new.full_clean() |
131 | | - new.save() |
132 | 175 | except ValidationError as e: |
133 | | - return self.create_response({"detail": dict(e)}, status_code=409) |
| 176 | + raise HttpError(status_code=409, message=str(e)) from e |
| 177 | + new.save() |
134 | 178 |
|
135 | 179 | @route.get( |
136 | 180 | "/{picture_id}/identified", |
|
0 commit comments