mirror of
https://github.com/ae-utbm/sith.git
synced 2026-04-16 00:08:25 +00:00
Compare commits
3 Commits
album_fix
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0888e5d0a2 | ||
| 30df859039 | |||
|
9c3f846f98
|
@@ -16,7 +16,7 @@
|
|||||||
# details.
|
# details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License along with
|
# You should have received a copy of the GNU General Public License along with
|
||||||
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
|
||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
@@ -110,7 +110,7 @@ class Command(BaseCommand):
|
|||||||
p.save(force_lock=True)
|
p.save(force_lock=True)
|
||||||
|
|
||||||
club_root = SithFile.objects.create(name="clubs", owner=root)
|
club_root = SithFile.objects.create(name="clubs", owner=root)
|
||||||
sas = SithFile.objects.create(name="SAS", owner=root, is_in_sas=True)
|
sas = SithFile.objects.create(name="SAS", owner=root)
|
||||||
main_club = Club.objects.create(
|
main_club = Club.objects.create(
|
||||||
id=1, name="AE", address="6 Boulevard Anatole France, 90000 Belfort"
|
id=1, name="AE", address="6 Boulevard Anatole France, 90000 Belfort"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -56,6 +56,12 @@ Commencez par installer les dépendances système :
|
|||||||
sudo pacman -S postgresql nginx
|
sudo pacman -S postgresql nginx
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "Fedora/RHEL/AlmaLinux/Rocky"
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dnf install postgresql libpq-devel nginx
|
||||||
|
```
|
||||||
|
|
||||||
=== "macOS"
|
=== "macOS"
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -100,9 +106,11 @@ PROCFILE_SERVICE=
|
|||||||
vous devez ouvrir une autre fenêtre de votre terminal
|
vous devez ouvrir une autre fenêtre de votre terminal
|
||||||
et lancer la commande `npm run serve`
|
et lancer la commande `npm run serve`
|
||||||
|
|
||||||
## Configurer Redis en service externe
|
## Configurer Redis/Valkey en service externe
|
||||||
|
|
||||||
Redis est installé comme dépendance mais pas lancé par défaut.
|
Redis est installé comme dépendance mais n'es pas lancé par défaut.
|
||||||
|
|
||||||
|
Si vous avez installé Valkey parce que Redis n'es pas disponible, remplacez juste `redis` par `valkey`.
|
||||||
|
|
||||||
En mode développement, le sith se charge de le démarrer mais
|
En mode développement, le sith se charge de le démarrer mais
|
||||||
pas en production !
|
pas en production !
|
||||||
|
|||||||
@@ -79,6 +79,29 @@ cd /mnt/<la_lettre_du_disque>/vos/fichiers/comme/dhab
|
|||||||
sudo pacman -S uv gcc git gettext pkgconf npm valkey
|
sudo pacman -S uv gcc git gettext pkgconf npm valkey
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "Fedora"
|
||||||
|
```bash
|
||||||
|
sudo dnf update
|
||||||
|
sudo dnf install epel-release
|
||||||
|
sudo dnf install python-devel uv git gettext pkgconf npm redis @c-development @development-tools
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "RHEL/AlmaLinux/Rocky"
|
||||||
|
```bash
|
||||||
|
dnf update
|
||||||
|
dnf install epel-release
|
||||||
|
dnf install python-devel uv git gettext pkgconf npm valkey
|
||||||
|
dnf group install "Development Tools"
|
||||||
|
```
|
||||||
|
|
||||||
|
La couche de compatibilitée valkey/redis est un package Fedora.
|
||||||
|
Il est nécessaire de faire un alias nous même:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ln -s /usr/bin/valkey-server /usr/bin/redis-server
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
=== "macOS"
|
=== "macOS"
|
||||||
|
|
||||||
Pour installer les dépendances, il est fortement recommandé d'installer le gestionnaire de paquets `homebrew <https://brew.sh/index_fr>`_.
|
Pour installer les dépendances, il est fortement recommandé d'installer le gestionnaire de paquets `homebrew <https://brew.sh/index_fr>`_.
|
||||||
@@ -98,7 +121,7 @@ cd /mnt/<la_lettre_du_disque>/vos/fichiers/comme/dhab
|
|||||||
!!!note
|
!!!note
|
||||||
|
|
||||||
Python ne fait pas parti des dépendances puisqu'il est automatiquement
|
Python ne fait pas parti des dépendances puisqu'il est automatiquement
|
||||||
installé par uv.
|
installé par uv. Il est cependant parfois nécessaire d'installer les headers Python nécessaire à la compilation de certains paquets.
|
||||||
|
|
||||||
## Finaliser l'installation
|
## Finaliser l'installation
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ dev = [
|
|||||||
"pre-commit>=4.5.1,<5.0.0",
|
"pre-commit>=4.5.1,<5.0.0",
|
||||||
"ruff>=0.15.5,<1.0.0",
|
"ruff>=0.15.5,<1.0.0",
|
||||||
"djhtml>=3.0.10,<4.0.0",
|
"djhtml>=3.0.10,<4.0.0",
|
||||||
"faker>=40.8.0,<41.0.0",
|
"faker>=40.13.0,<41.0.0",
|
||||||
"rjsmin>=1.2.5,<2.0.0",
|
"rjsmin>=1.2.5,<2.0.0",
|
||||||
]
|
]
|
||||||
tests = [
|
tests = [
|
||||||
|
|||||||
@@ -50,15 +50,13 @@ class AlbumEditForm(forms.ModelForm):
|
|||||||
model = Album
|
model = Album
|
||||||
fields = ["name", "date", "file", "parent", "edit_groups"]
|
fields = ["name", "date", "file", "parent", "edit_groups"]
|
||||||
widgets = {
|
widgets = {
|
||||||
|
"parent": AutoCompleteSelectAlbum,
|
||||||
"edit_groups": AutoCompleteSelectMultipleGroup,
|
"edit_groups": AutoCompleteSelectMultipleGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
name = forms.CharField(max_length=Album.NAME_MAX_LENGTH, label=_("file name"))
|
name = forms.CharField(max_length=Album.NAME_MAX_LENGTH, label=_("file name"))
|
||||||
date = forms.DateField(label=_("Date"), widget=SelectDate, required=True)
|
date = forms.DateField(label=_("Date"), widget=SelectDate, required=True)
|
||||||
recursive = forms.BooleanField(label=_("Apply rights recursively"), required=False)
|
recursive = forms.BooleanField(label=_("Apply rights recursively"), required=False)
|
||||||
parent = forms.ModelChoiceField(
|
|
||||||
Album.objects.all(), required=True, widget=AutoCompleteSelectAlbum
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class PictureModerationRequestForm(forms.ModelForm):
|
class PictureModerationRequestForm(forms.ModelForm):
|
||||||
|
|||||||
@@ -205,13 +205,7 @@ class AlbumQuerySet(models.QuerySet):
|
|||||||
|
|
||||||
class SASAlbumManager(models.Manager):
|
class SASAlbumManager(models.Manager):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return (
|
return super().get_queryset().filter(is_in_sas=True, is_folder=True)
|
||||||
super()
|
|
||||||
.get_queryset()
|
|
||||||
.filter(
|
|
||||||
Q(id=settings.SITH_SAS_ROOT_DIR_ID) | Q(is_in_sas=True, is_folder=True)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Album(SasFile):
|
class Album(SasFile):
|
||||||
|
|||||||
@@ -20,14 +20,12 @@ from django.conf import settings
|
|||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.test import Client, TestCase
|
from django.test import Client, TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
|
||||||
from model_bakery import baker
|
from model_bakery import baker
|
||||||
from pytest_django.asserts import assertHTMLEqual, assertInHTML, assertRedirects
|
from pytest_django.asserts import assertHTMLEqual, assertInHTML, assertRedirects
|
||||||
|
|
||||||
from core.baker_recipes import old_subscriber_user, subscriber_user
|
from core.baker_recipes import old_subscriber_user, subscriber_user
|
||||||
from core.models import Group, User
|
from core.models import Group, User
|
||||||
from sas.baker_recipes import picture_recipe
|
from sas.baker_recipes import picture_recipe
|
||||||
from sas.forms import AlbumEditForm
|
|
||||||
from sas.models import Album, Picture
|
from sas.models import Album, Picture
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
@@ -135,171 +133,6 @@ class TestAlbumUpload:
|
|||||||
assert not album.children.exists()
|
assert not album.children.exists()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
|
||||||
class TestAlbumEdit:
|
|
||||||
@pytest.fixture
|
|
||||||
def sas_root(self) -> Album:
|
|
||||||
return Album.objects.get(id=settings.SITH_SAS_ROOT_DIR_ID)
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def album(self) -> Album:
|
|
||||||
return baker.make(
|
|
||||||
Album, parent_id=settings.SITH_SAS_ROOT_DIR_ID, is_moderated=True
|
|
||||||
)
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"user",
|
|
||||||
[
|
|
||||||
None,
|
|
||||||
subscriber_user.make,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_permission_denied(
|
|
||||||
self,
|
|
||||||
client: Client,
|
|
||||||
album: Album,
|
|
||||||
request: pytest.FixtureRequest,
|
|
||||||
user: Callable[[], User] | None,
|
|
||||||
):
|
|
||||||
if user:
|
|
||||||
client.force_login(user())
|
|
||||||
|
|
||||||
response = client.get(reverse("sas:album_edit", kwargs={"album_id": album.pk}))
|
|
||||||
assert response.status_code == 403
|
|
||||||
response = client.post(reverse("sas:album_edit", kwargs={"album_id": album.pk}))
|
|
||||||
assert response.status_code == 403
|
|
||||||
|
|
||||||
def test_sas_root_read_only(self, client: Client, sas_root: Album):
|
|
||||||
moderator = baker.make(
|
|
||||||
User, groups=[Group.objects.get(pk=settings.SITH_GROUP_SAS_ADMIN_ID)]
|
|
||||||
)
|
|
||||||
client.force_login(moderator)
|
|
||||||
response = client.get(
|
|
||||||
reverse("sas:album_edit", kwargs={"album_id": sas_root.pk})
|
|
||||||
)
|
|
||||||
assert response.status_code == 404
|
|
||||||
response = client.post(
|
|
||||||
reverse("sas:album_edit", kwargs={"album_id": sas_root.pk})
|
|
||||||
)
|
|
||||||
assert response.status_code == 404
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
("excluded", "is_valid"),
|
|
||||||
[
|
|
||||||
("name", False),
|
|
||||||
("date", False),
|
|
||||||
("file", True),
|
|
||||||
("parent", False),
|
|
||||||
("edit_groups", True),
|
|
||||||
("recursive", True),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_form_required(self, album: Album, excluded: str, is_valid: bool): # noqa: FBT001
|
|
||||||
data = {
|
|
||||||
"name": album.name[: Album.NAME_MAX_LENGTH],
|
|
||||||
"parent": baker.make(Album, parent=album.parent, is_moderated=True).pk,
|
|
||||||
"date": timezone.now().strftime("%Y-%m-%d"),
|
|
||||||
"file": "/random/path",
|
|
||||||
"edit_groups": [settings.SITH_GROUP_SAS_ADMIN_ID],
|
|
||||||
"recursive": False,
|
|
||||||
}
|
|
||||||
del data[excluded]
|
|
||||||
assert AlbumEditForm(data=data).is_valid() == is_valid
|
|
||||||
|
|
||||||
def test_form_album_name(self, album: Album):
|
|
||||||
data = {
|
|
||||||
"name": album.name[: Album.NAME_MAX_LENGTH],
|
|
||||||
"parent": album.pk,
|
|
||||||
"date": timezone.now().strftime("%Y-%m-%d"),
|
|
||||||
}
|
|
||||||
assert AlbumEditForm(data=data).is_valid()
|
|
||||||
|
|
||||||
data["name"] = album.name[: Album.NAME_MAX_LENGTH + 1]
|
|
||||||
assert not AlbumEditForm(data=data).is_valid()
|
|
||||||
|
|
||||||
def test_update_recursive_parent(self, client: Client, album: Album):
|
|
||||||
client.force_login(User.objects.get(username="root"))
|
|
||||||
|
|
||||||
payload = {
|
|
||||||
"name": album.name[: Album.NAME_MAX_LENGTH],
|
|
||||||
"parent": album.pk,
|
|
||||||
"date": timezone.now().strftime("%Y-%m-%d"),
|
|
||||||
}
|
|
||||||
response = client.post(
|
|
||||||
reverse(
|
|
||||||
"sas:album_edit",
|
|
||||||
kwargs={"album_id": album.pk},
|
|
||||||
),
|
|
||||||
payload,
|
|
||||||
)
|
|
||||||
assertInHTML(
|
|
||||||
"<li>Boucle dans l'arborescence des dossiers</li>",
|
|
||||||
response.text,
|
|
||||||
)
|
|
||||||
assert response.status_code == 200
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"user",
|
|
||||||
[
|
|
||||||
lambda: User.objects.get(username="root"),
|
|
||||||
lambda: baker.make(
|
|
||||||
User, groups=[Group.objects.get(pk=settings.SITH_GROUP_SAS_ADMIN_ID)]
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_update(
|
|
||||||
self,
|
|
||||||
client: Client,
|
|
||||||
album: Album,
|
|
||||||
sas_root: Album,
|
|
||||||
user: Callable[[], User],
|
|
||||||
):
|
|
||||||
client.force_login(user())
|
|
||||||
|
|
||||||
# Prepare a good payload
|
|
||||||
expected_redirect = reverse("sas:album", kwargs={"album_id": album.pk})
|
|
||||||
expected_date = timezone.now()
|
|
||||||
payload = {
|
|
||||||
"name": album.name[: Album.NAME_MAX_LENGTH],
|
|
||||||
"parent": baker.make(Album, parent=sas_root, is_moderated=True).pk,
|
|
||||||
"date": expected_date.strftime("%Y-%m-%d"),
|
|
||||||
"recursive": False,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Test successful update
|
|
||||||
response = client.post(
|
|
||||||
reverse(
|
|
||||||
"sas:album_edit",
|
|
||||||
kwargs={"album_id": album.pk},
|
|
||||||
),
|
|
||||||
payload,
|
|
||||||
)
|
|
||||||
assertRedirects(response, expected_redirect)
|
|
||||||
updated_album = Album.objects.get(id=album.pk)
|
|
||||||
assert updated_album.name == payload["name"]
|
|
||||||
assert updated_album.parent.id == payload["parent"]
|
|
||||||
assert timezone.localdate(updated_album.date) == timezone.localdate(
|
|
||||||
expected_date
|
|
||||||
)
|
|
||||||
|
|
||||||
# Test root album can be used as parent
|
|
||||||
payload["parent"] = sas_root.pk
|
|
||||||
response = client.post(
|
|
||||||
reverse(
|
|
||||||
"sas:album_edit",
|
|
||||||
kwargs={"album_id": album.pk},
|
|
||||||
),
|
|
||||||
payload,
|
|
||||||
)
|
|
||||||
assertRedirects(response, expected_redirect)
|
|
||||||
updated_album = Album.objects.get(id=album.pk)
|
|
||||||
assert updated_album.name == payload["name"]
|
|
||||||
assert updated_album.parent.id == payload["parent"]
|
|
||||||
assert timezone.localdate(updated_album.date) == timezone.localdate(
|
|
||||||
expected_date
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TestSasModeration(TestCase):
|
class TestSasModeration(TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ from sas.forms import (
|
|||||||
PictureModerationRequestForm,
|
PictureModerationRequestForm,
|
||||||
PictureUploadForm,
|
PictureUploadForm,
|
||||||
)
|
)
|
||||||
from sas.models import Album, AlbumQuerySet, PeoplePictureRelation, Picture
|
from sas.models import Album, PeoplePictureRelation, Picture
|
||||||
|
|
||||||
|
|
||||||
class AlbumCreateFragment(FragmentMixin, CreateView):
|
class AlbumCreateFragment(FragmentMixin, CreateView):
|
||||||
@@ -266,9 +266,6 @@ class AlbumEditView(CanEditMixin, UpdateView):
|
|||||||
template_name = "core/edit.jinja"
|
template_name = "core/edit.jinja"
|
||||||
pk_url_kwarg = "album_id"
|
pk_url_kwarg = "album_id"
|
||||||
|
|
||||||
def get_queryset(self) -> AlbumQuerySet:
|
|
||||||
return super().get_queryset().exclude(id=settings.SITH_SAS_ROOT_DIR_ID)
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
ret = super().form_valid(form)
|
ret = super().form_valid(form)
|
||||||
if form.cleaned_data["recursive"]:
|
if form.cleaned_data["recursive"]:
|
||||||
|
|||||||
Reference in New Issue
Block a user