Make SAS pictures visible for their owner

This commit is contained in:
imperosol 2025-03-24 15:22:53 +01:00
parent bb3dfb7e8a
commit e1eb634c62
6 changed files with 58 additions and 49 deletions

View File

@ -880,11 +880,9 @@ class SithFile(models.Model):
def save(self, *args, **kwargs):
sas = SithFile.objects.filter(id=settings.SITH_SAS_ROOT_DIR_ID).first()
self.is_in_sas = sas in self.get_parent_list() or self == sas
copy_rights = False
if self.id is None:
copy_rights = True
adding = self._state.adding
super().save(*args, **kwargs)
if copy_rights:
if adding:
self.copy_rights()
if self.is_in_sas:
for user in User.objects.filter(

View File

@ -1,6 +1,6 @@
import { BasketItem } from "#counter:counter/basket";
import type { CounterConfig, ErrorMessage } from "#counter:counter/types";
import type { CounterProductSelect } from "./components/counter-product-select-index";
import type { CounterProductSelect } from "./components/counter-product-select-index.ts";
document.addEventListener("alpine:init", () => {
Alpine.data("counter", (config: CounterConfig) => ({

View File

@ -23,7 +23,7 @@ from typing import ClassVar, Self
from django.conf import settings
from django.core.cache import cache
from django.db import models
from django.db.models import Exists, OuterRef
from django.db.models import Exists, OuterRef, Q
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
@ -73,7 +73,7 @@ class PictureQuerySet(models.QuerySet):
if user.is_root or user.is_in_group(pk=settings.SITH_GROUP_SAS_ADMIN_ID):
return self.all()
if user.was_subscribed:
return self.filter(is_moderated=True)
return self.filter(Q(is_moderated=True) | Q(owner=user))
return self.filter(people__user_id=user.id, is_moderated=True)
@ -187,7 +187,7 @@ class AlbumQuerySet(models.QuerySet):
if user.is_root or user.is_in_group(pk=settings.SITH_GROUP_SAS_ADMIN_ID):
return self.all()
if user.was_subscribed:
return self.filter(is_moderated=True)
return self.filter(Q(is_moderated=True) | Q(owner=user))
# known bug : if all children of an album are also albums
# then this album is excluded, even if one of the sub-albums should be visible.
# The fs-like navigation is likely to be half-broken for non-subscribers,

View File

@ -4,9 +4,9 @@ import { History } from "#core:utils/history";
import type TomSelect from "tom-select";
import {
type IdentifiedUserSchema,
type ModerationRequestSchema,
type PictureSchema,
type PicturesFetchIdentificationsResponse,
type PicturesFetchModerationRequestsResponse,
type PicturesFetchPicturesData,
type UserProfileSchema,
picturesDeletePicture,
@ -30,7 +30,7 @@ class PictureWithIdentifications {
id: number;
// biome-ignore lint/style/useNamingConvention: api is in snake_case
compressed_url: string;
moderationRequests: PicturesFetchModerationRequestsResponse = null;
moderationRequests: ModerationRequestSchema[] = null;
constructor(picture: PictureSchema) {
Object.assign(this, picture);
@ -156,9 +156,6 @@ exportToHtml("loadViewer", (config: ViewerConfig) => {
* The select2 component used to identify users
**/
selector: undefined,
/**
* true if the page is in a loading state, else false
**/
/**
* Error message when a moderation operation fails
**/

View File

@ -17,6 +17,8 @@
{% from "sas/macros.jinja" import print_path %}
{% set user_is_sas_admin = user.is_root or user.is_in_group(pk = settings.SITH_GROUP_SAS_ADMIN_ID) %}
{% block content %}
<main x-data="picture_viewer">
<code>
@ -31,8 +33,13 @@
</div>
<br>
{# Non-moderated pictures (hence, this moderation alert too)
should be shown only to admins and to the picture owner.
Admins should see all infos and have access to all actions.
Non-admin picture owners should only see the message warning them that
the picture isn't moderated yet. #}
<template x-if="!currentPicture.is_moderated">
<div class="alert alert-red" @click="console.log(currentPicture)">
<div id="picture-moderation-alert" class="alert alert-red">
<div class="alert-main">
<template x-if="currentPicture.asked_for_removal">
<h3 class="alert-title">{% trans %}Asked for removal{% endtrans %}</h3>
@ -43,35 +50,37 @@
It will be hidden to other users until it has been moderated.
{% endtrans %}
</p>
<template x-if="currentPicture.asked_for_removal">
<div>
<h5 @click="console.log(currentPicture.moderationRequests)">
{% trans %}The following issues have been raised:{% endtrans %}
</h5>
<template x-for="req in (currentPicture.moderationRequests ?? [])" :key="req.id">
<div>
<h6
x-text="`${req.author.first_name} ${req.author.last_name}`"
></h6>
<i x-text="Intl.DateTimeFormat(
'{{ LANGUAGE_CODE }}',
{dateStyle: 'long', timeStyle: 'short'}
).format(new Date(req.created_at))"></i>
<blockquote x-text="`> ${req.reason}`"></blockquote>
</div>
</template>
</div>
</template>
</div>
<div class="alert-aside">
<button class="btn btn-blue" @click="moderatePicture()">
{% trans %}Moderate{% endtrans %}
</button>
<button class="btn btn-red" @click.prevent="deletePicture()">
{% trans %}Delete{% endtrans %}
</button>
<p x-show="!!moderationError" x-text="moderationError"></p>
{% if user_is_sas_admin %}
<template x-if="currentPicture.asked_for_removal">
<div>
<h5>{% trans %}The following issues have been raised:{% endtrans %}</h5>
<template x-for="req in (currentPicture.moderationRequests ?? [])" :key="req.id">
<div>
<h6
x-text="`${req.author.first_name} ${req.author.last_name}`"
></h6>
<i x-text="Intl.DateTimeFormat(
'{{ LANGUAGE_CODE }}',
{dateStyle: 'long', timeStyle: 'short'}
).format(new Date(req.created_at))"></i>
<blockquote x-text="`${req.reason}`"></blockquote>
</div>
</template>
</div>
</template>
{% endif %}
</div>
{% if user_is_sas_admin %}
<div class="alert-aside">
<button class="btn btn-blue" @click="moderatePicture()">
{% trans %}Moderate{% endtrans %}
</button>
<button class="btn btn-red" @click.prevent="deletePicture()">
{% trans %}Delete{% endtrans %}
</button>
<p x-show="!!moderationError" x-text="moderationError"></p>
</div>
{% endif %}
</div>
</template>
@ -203,7 +212,7 @@
albumUrl: "{{ album.get_absolute_url() }}",
firstPictureId: {{ picture.id }}, {# id of the first picture to show after page load #}
userId: {{ user.id }},
userIsSasAdmin: {{ (user.is_root or user.is_in_group(pk = settings.SITH_GROUP_SAS_ADMIN_ID))|tojson }}
userIsSasAdmin: {{ user_is_sas_admin|tojson }}
});
})
</script>

View File

@ -16,17 +16,22 @@ class TestPictureQuerySet(TestCase):
def test_root(self):
root = baker.make(User, is_superuser=True)
pictures = list(Picture.objects.viewable_by(root))
self.assertCountEqual(pictures, self.pictures)
pictures = list(Picture.objects.viewable_by(root).order_by("id"))
assert pictures == self.pictures
def test_subscriber(self):
"""Test that subscribed users see moderated pictures and pictures they own."""
subscriber = subscriber_user.make()
old_subcriber = old_subscriber_user.make()
qs = Picture.objects.filter(pk=self.pictures[1].id)
qs.update(is_moderated=False)
for user in (subscriber, old_subcriber):
pictures = list(Picture.objects.viewable_by(user))
self.assertCountEqual(pictures, self.pictures[1:])
qs.update(owner=user)
pictures = list(Picture.objects.viewable_by(user).order_by("id"))
assert pictures == self.pictures[1:]
def test_not_subscribed_identified(self):
"""Public users should only see moderated photos on which they are identified."""
user = baker.make(
# This is the guy who asked the feature of making pictures
# available for tagged users, even if not subscribed
@ -35,7 +40,7 @@ class TestPictureQuerySet(TestCase):
last_name="Dheilly",
nick_name="Sahmer",
)
user.pictures.create(picture=self.pictures[0])
user.pictures.create(picture=self.pictures[1])
user.pictures.create(picture=self.pictures[0]) # non-moderated
user.pictures.create(picture=self.pictures[1]) # moderated
pictures = list(Picture.objects.viewable_by(user))
assert pictures == [self.pictures[1]]