diff --git a/sas/api.py b/sas/api.py index ca4c10c6..6a25607a 100644 --- a/sas/api.py +++ b/sas/api.py @@ -1,3 +1,6 @@ +from typing import Annotated + +from annotated_types import MinLen from django.conf import settings from django.db.models import F from django.urls import reverse @@ -9,10 +12,11 @@ from ninja_extra.permissions import IsAuthenticated from ninja_extra.schemas import PaginatedResponseSchema from pydantic import NonNegativeInt -from core.api_permissions import CanView, IsInGroup, IsRoot +from core.api_permissions import CanAccessLookup, CanView, IsInGroup, IsRoot from core.models import Notification, User -from sas.models import PeoplePictureRelation, Picture +from sas.models import Album, PeoplePictureRelation, Picture from sas.schemas import ( + AlbumSchema, IdentifiedUserSchema, ModerationRequestSchema, PictureFilterSchema, @@ -22,6 +26,18 @@ from sas.schemas import ( IsSasAdmin = IsRoot | IsInGroup(settings.SITH_GROUP_SAS_ADMIN_ID) +@api_controller("/sas/album") +class AlbumController(ControllerBase): + @route.get( + "/search", + response=PaginatedResponseSchema[AlbumSchema], + permissions=[CanAccessLookup], + ) + @paginate(PageNumberPaginationExtra, page_size=50) + def search_album(self, search: Annotated[str, MinLen(1)]): + return Album.objects.filter(name__icontains=search) + + @api_controller("/sas/picture") class PicturesController(ControllerBase): @route.get( diff --git a/sas/forms.py b/sas/forms.py index 6569e92a..926fe6ca 100644 --- a/sas/forms.py +++ b/sas/forms.py @@ -1,14 +1,14 @@ from typing import Any -from ajax_select import make_ajax_field -from ajax_select.fields import AutoCompleteSelectMultipleField from django import forms from django.utils.translation import gettext_lazy as _ from core.models import User from core.views import MultipleImageField from core.views.forms import SelectDate -from sas.models import Album, PeoplePictureRelation, Picture, PictureModerationRequest +from core.views.widgets.select import AutoCompleteSelectMultipleGroup +from sas.models import Album, Picture, PictureModerationRequest +from sas.widgets.select import AutoCompleteSelectAlbum class SASForm(forms.Form): @@ -62,34 +62,24 @@ class SASForm(forms.Form): ) -class RelationForm(forms.ModelForm): - class Meta: - model = PeoplePictureRelation - fields = ["picture"] - widgets = {"picture": forms.HiddenInput} - - users = AutoCompleteSelectMultipleField( - "users", show_help_text=False, help_text="", label=_("Add user"), required=False - ) - - class PictureEditForm(forms.ModelForm): class Meta: model = Picture fields = ["name", "parent"] - - parent = make_ajax_field(Picture, "parent", "files", help_text="") + widgets = {"parent": AutoCompleteSelectAlbum} class AlbumEditForm(forms.ModelForm): class Meta: model = Album fields = ["name", "date", "file", "parent", "edit_groups"] + widgets = { + "parent": AutoCompleteSelectAlbum, + "edit_groups": AutoCompleteSelectMultipleGroup, + } name = forms.CharField(max_length=Album.NAME_MAX_LENGTH, label=_("file name")) date = forms.DateField(label=_("Date"), widget=SelectDate, required=True) - parent = make_ajax_field(Album, "parent", "files", help_text="") - edit_groups = make_ajax_field(Album, "edit_groups", "groups", help_text="") recursive = forms.BooleanField(label=_("Apply rights recursively"), required=False) diff --git a/sas/schemas.py b/sas/schemas.py index 6647f7d1..5e049858 100644 --- a/sas/schemas.py +++ b/sas/schemas.py @@ -1,11 +1,24 @@ from datetime import datetime +from pathlib import Path from django.urls import reverse from ninja import FilterSchema, ModelSchema, Schema from pydantic import Field, NonNegativeInt from core.schemas import SimpleUserSchema, UserProfileSchema -from sas.models import Picture, PictureModerationRequest +from sas.models import Album, Picture, PictureModerationRequest + + +class AlbumSchema(ModelSchema): + class Meta: + model = Album + fields = ["id", "name"] + + path: str + + @staticmethod + def resolve_path(obj: Album) -> str: + return str(Path(obj.get_parent_path()) / obj.name) class PictureFilterSchema(FilterSchema): diff --git a/sas/static/webpack/sas/components/ajax-select-index.ts b/sas/static/webpack/sas/components/ajax-select-index.ts new file mode 100644 index 00000000..5b811f52 --- /dev/null +++ b/sas/static/webpack/sas/components/ajax-select-index.ts @@ -0,0 +1,30 @@ +import { AjaxSelect } from "#core:core/components/ajax-select-base"; +import { registerComponent } from "#core:utils/web-components"; +import type { TomOption } from "tom-select/dist/types/types"; +import type { escape_html } from "tom-select/dist/types/utils"; +import { type AlbumSchema, albumSearchAlbum } from "#openapi"; + +@registerComponent("album-ajax-select") +export class AlbumAjaxSelect extends AjaxSelect { + protected valueField = "id"; + protected labelField = "path"; + protected searchField = ["path", "name"]; + + protected async search(query: string): Promise { + const resp = await albumSearchAlbum({ query: { search: query } }); + if (resp.data) { + return resp.data.results; + } + return []; + } + + protected renderOption(item: AlbumSchema, sanitize: typeof escape_html) { + return `
+ ${sanitize(item.path)} +
`; + } + + protected renderItem(item: AlbumSchema, sanitize: typeof escape_html) { + return `${sanitize(item.path)}`; + } +} diff --git a/sas/widgets/select.py b/sas/widgets/select.py new file mode 100644 index 00000000..2ebd4745 --- /dev/null +++ b/sas/widgets/select.py @@ -0,0 +1,25 @@ +from django.forms import Select, SelectMultiple + +from core.views.widgets.select import AutoCompleteSelectMixin +from sas.models import Album +from sas.schemas import AlbumSchema + + +class AutoCompleteSelectAlbum(AutoCompleteSelectMixin, Select): + component_name = "album-ajax-select" + model = Album + schema = AlbumSchema + + js = [ + "webpack/sas/components/ajax-select-index.ts", + ] + + +class AutoCompleteSelectMultipleAlbum(AutoCompleteSelectMixin, SelectMultiple): + component_name = "album-ajax-select" + model = Album + schema = AlbumSchema + + js = [ + "webpack/sas/components/ajax-select-index.ts", + ]