mirror of
https://github.com/ae-utbm/sith.git
synced 2025-03-28 16:17:11 +00:00
Make SAS pictures visible for their owner
This commit is contained in:
parent
bb3dfb7e8a
commit
e1eb634c62
@ -880,11 +880,9 @@ class SithFile(models.Model):
|
|||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
sas = SithFile.objects.filter(id=settings.SITH_SAS_ROOT_DIR_ID).first()
|
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
|
self.is_in_sas = sas in self.get_parent_list() or self == sas
|
||||||
copy_rights = False
|
adding = self._state.adding
|
||||||
if self.id is None:
|
|
||||||
copy_rights = True
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
if copy_rights:
|
if adding:
|
||||||
self.copy_rights()
|
self.copy_rights()
|
||||||
if self.is_in_sas:
|
if self.is_in_sas:
|
||||||
for user in User.objects.filter(
|
for user in User.objects.filter(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { BasketItem } from "#counter:counter/basket";
|
import { BasketItem } from "#counter:counter/basket";
|
||||||
import type { CounterConfig, ErrorMessage } from "#counter:counter/types";
|
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", () => {
|
document.addEventListener("alpine:init", () => {
|
||||||
Alpine.data("counter", (config: CounterConfig) => ({
|
Alpine.data("counter", (config: CounterConfig) => ({
|
||||||
|
@ -23,7 +23,7 @@ from typing import ClassVar, Self
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.db import models
|
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.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext_lazy as _
|
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):
|
if user.is_root or user.is_in_group(pk=settings.SITH_GROUP_SAS_ADMIN_ID):
|
||||||
return self.all()
|
return self.all()
|
||||||
if user.was_subscribed:
|
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)
|
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):
|
if user.is_root or user.is_in_group(pk=settings.SITH_GROUP_SAS_ADMIN_ID):
|
||||||
return self.all()
|
return self.all()
|
||||||
if user.was_subscribed:
|
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
|
# 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.
|
# 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,
|
# The fs-like navigation is likely to be half-broken for non-subscribers,
|
||||||
|
@ -4,9 +4,9 @@ import { History } from "#core:utils/history";
|
|||||||
import type TomSelect from "tom-select";
|
import type TomSelect from "tom-select";
|
||||||
import {
|
import {
|
||||||
type IdentifiedUserSchema,
|
type IdentifiedUserSchema,
|
||||||
|
type ModerationRequestSchema,
|
||||||
type PictureSchema,
|
type PictureSchema,
|
||||||
type PicturesFetchIdentificationsResponse,
|
type PicturesFetchIdentificationsResponse,
|
||||||
type PicturesFetchModerationRequestsResponse,
|
|
||||||
type PicturesFetchPicturesData,
|
type PicturesFetchPicturesData,
|
||||||
type UserProfileSchema,
|
type UserProfileSchema,
|
||||||
picturesDeletePicture,
|
picturesDeletePicture,
|
||||||
@ -30,7 +30,7 @@ class PictureWithIdentifications {
|
|||||||
id: number;
|
id: number;
|
||||||
// biome-ignore lint/style/useNamingConvention: api is in snake_case
|
// biome-ignore lint/style/useNamingConvention: api is in snake_case
|
||||||
compressed_url: string;
|
compressed_url: string;
|
||||||
moderationRequests: PicturesFetchModerationRequestsResponse = null;
|
moderationRequests: ModerationRequestSchema[] = null;
|
||||||
|
|
||||||
constructor(picture: PictureSchema) {
|
constructor(picture: PictureSchema) {
|
||||||
Object.assign(this, picture);
|
Object.assign(this, picture);
|
||||||
@ -156,9 +156,6 @@ exportToHtml("loadViewer", (config: ViewerConfig) => {
|
|||||||
* The select2 component used to identify users
|
* The select2 component used to identify users
|
||||||
**/
|
**/
|
||||||
selector: undefined,
|
selector: undefined,
|
||||||
/**
|
|
||||||
* true if the page is in a loading state, else false
|
|
||||||
**/
|
|
||||||
/**
|
/**
|
||||||
* Error message when a moderation operation fails
|
* Error message when a moderation operation fails
|
||||||
**/
|
**/
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
{% from "sas/macros.jinja" import print_path %}
|
{% 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 %}
|
{% block content %}
|
||||||
<main x-data="picture_viewer">
|
<main x-data="picture_viewer">
|
||||||
<code>
|
<code>
|
||||||
@ -31,8 +33,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<br>
|
<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">
|
<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">
|
<div class="alert-main">
|
||||||
<template x-if="currentPicture.asked_for_removal">
|
<template x-if="currentPicture.asked_for_removal">
|
||||||
<h3 class="alert-title">{% trans %}Asked for removal{% endtrans %}</h3>
|
<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.
|
It will be hidden to other users until it has been moderated.
|
||||||
{% endtrans %}
|
{% endtrans %}
|
||||||
</p>
|
</p>
|
||||||
<template x-if="currentPicture.asked_for_removal">
|
{% if user_is_sas_admin %}
|
||||||
<div>
|
<template x-if="currentPicture.asked_for_removal">
|
||||||
<h5 @click="console.log(currentPicture.moderationRequests)">
|
<div>
|
||||||
{% trans %}The following issues have been raised:{% endtrans %}
|
<h5>{% trans %}The following issues have been raised:{% endtrans %}</h5>
|
||||||
</h5>
|
<template x-for="req in (currentPicture.moderationRequests ?? [])" :key="req.id">
|
||||||
<template x-for="req in (currentPicture.moderationRequests ?? [])" :key="req.id">
|
<div>
|
||||||
<div>
|
<h6
|
||||||
<h6
|
x-text="`${req.author.first_name} ${req.author.last_name}`"
|
||||||
x-text="`${req.author.first_name} ${req.author.last_name}`"
|
></h6>
|
||||||
></h6>
|
<i x-text="Intl.DateTimeFormat(
|
||||||
<i x-text="Intl.DateTimeFormat(
|
'{{ LANGUAGE_CODE }}',
|
||||||
'{{ LANGUAGE_CODE }}',
|
{dateStyle: 'long', timeStyle: 'short'}
|
||||||
{dateStyle: 'long', timeStyle: 'short'}
|
).format(new Date(req.created_at))"></i>
|
||||||
).format(new Date(req.created_at))"></i>
|
<blockquote x-text="`${req.reason}`"></blockquote>
|
||||||
<blockquote x-text="`> ${req.reason}`"></blockquote>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</template>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</template>
|
{% endif %}
|
||||||
</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>
|
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -203,7 +212,7 @@
|
|||||||
albumUrl: "{{ album.get_absolute_url() }}",
|
albumUrl: "{{ album.get_absolute_url() }}",
|
||||||
firstPictureId: {{ picture.id }}, {# id of the first picture to show after page load #}
|
firstPictureId: {{ picture.id }}, {# id of the first picture to show after page load #}
|
||||||
userId: {{ user.id }},
|
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>
|
</script>
|
||||||
|
@ -16,17 +16,22 @@ class TestPictureQuerySet(TestCase):
|
|||||||
|
|
||||||
def test_root(self):
|
def test_root(self):
|
||||||
root = baker.make(User, is_superuser=True)
|
root = baker.make(User, is_superuser=True)
|
||||||
pictures = list(Picture.objects.viewable_by(root))
|
pictures = list(Picture.objects.viewable_by(root).order_by("id"))
|
||||||
self.assertCountEqual(pictures, self.pictures)
|
assert pictures == self.pictures
|
||||||
|
|
||||||
def test_subscriber(self):
|
def test_subscriber(self):
|
||||||
|
"""Test that subscribed users see moderated pictures and pictures they own."""
|
||||||
subscriber = subscriber_user.make()
|
subscriber = subscriber_user.make()
|
||||||
old_subcriber = old_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):
|
for user in (subscriber, old_subcriber):
|
||||||
pictures = list(Picture.objects.viewable_by(user))
|
qs.update(owner=user)
|
||||||
self.assertCountEqual(pictures, self.pictures[1:])
|
pictures = list(Picture.objects.viewable_by(user).order_by("id"))
|
||||||
|
assert pictures == self.pictures[1:]
|
||||||
|
|
||||||
def test_not_subscribed_identified(self):
|
def test_not_subscribed_identified(self):
|
||||||
|
"""Public users should only see moderated photos on which they are identified."""
|
||||||
user = baker.make(
|
user = baker.make(
|
||||||
# This is the guy who asked the feature of making pictures
|
# This is the guy who asked the feature of making pictures
|
||||||
# available for tagged users, even if not subscribed
|
# available for tagged users, even if not subscribed
|
||||||
@ -35,7 +40,7 @@ class TestPictureQuerySet(TestCase):
|
|||||||
last_name="Dheilly",
|
last_name="Dheilly",
|
||||||
nick_name="Sahmer",
|
nick_name="Sahmer",
|
||||||
)
|
)
|
||||||
user.pictures.create(picture=self.pictures[0])
|
user.pictures.create(picture=self.pictures[0]) # non-moderated
|
||||||
user.pictures.create(picture=self.pictures[1])
|
user.pictures.create(picture=self.pictures[1]) # moderated
|
||||||
pictures = list(Picture.objects.viewable_by(user))
|
pictures = list(Picture.objects.viewable_by(user))
|
||||||
assert pictures == [self.pictures[1]]
|
assert pictures == [self.pictures[1]]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user