Skip to content

[FEATURE] Deletion of existing photos, of event by any leader #787

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 28, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions collectives/forms/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from flask import current_app
from flask_login import current_user
import sqlalchemy
from wtforms import SubmitField, SelectField, IntegerField, HiddenField
from wtforms import SubmitField, SelectField, IntegerField, HiddenField, Field
from wtforms import FieldList, BooleanField, FormField, RadioField, SelectMultipleField
from wtforms.validators import DataRequired
from wtforms_alchemy import ModelForm
Expand Down Expand Up @@ -144,6 +144,8 @@ class Meta:
exclude = ["photo"]

photo_file = FileField(validators=[FileAllowed(photos, "Image only!")])
remove_photo = BooleanField("Supprimer la photo existante")

duplicate_event = HiddenField()
event_type_id = SelectField("Type d'événement", choices=[], coerce=int)
single_activity_type = SelectField("Activité", choices=[], coerce=int)
Expand All @@ -166,16 +168,16 @@ class Meta:

user_group = FormField(UserGroupForm, default=UserGroup)

source_event = None
current_leaders = []
main_leader_fields = []

def __init__(self, *args, **kwargs):
"""
Constructor
"""
super().__init__(*args, **kwargs)

self.source_event: Event = None
self.current_leaders: List[User] = []
self.main_leader_fields: List[Field] = []

# Unique identifier for the editing session
# Useful to associate to temporary data when creating a new event
if not self.edit_session_id.data:
Expand All @@ -200,6 +202,9 @@ def __init__(self, *args, **kwargs):

self.update_choices()

if "obj" not in kwargs or not kwargs["obj"].photo:
del self.remove_photo

if not self.can_switch_multi_activity_mode():
self.multi_activities_mode.data = True

Expand Down
9 changes: 9 additions & 0 deletions collectives/models/event/misc.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Module for misc Event methods which does not fit in another submodule"""

import os
from typing import List
from flask_uploads import UploadSet, IMAGES
from werkzeug.datastructures import FileStorage
Expand Down Expand Up @@ -72,6 +73,12 @@ def is_visible_to(self, user: User) -> bool:
user_activities = user.activities_with_role()
return any(activity in user_activities for activity in self.activity_types)

def delete_photo(self):
"""Remove and dereference an event photo."""
if self.photo:
os.remove(photos.path(self.photo))
self.photo = None

def save_photo(self, file: FileStorage) -> bool:
"""Save event photo from a raw file

Expand All @@ -86,6 +93,8 @@ def save_photo(self, file: FileStorage) -> bool:
if not is_valid_image(file.stream):
return False

self.delete_photo() # remove existing

filename = photos.save(file, name="event-" + str(self.id) + ".")
self.photo = filename
return True
Expand Down
10 changes: 3 additions & 7 deletions collectives/models/event/role.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,15 @@ def has_edit_rights(self, user):
return True
return self.is_leader(user) or self.is_supervisor(user)

def has_delete_rights(self, user):
def has_delete_rights(self, user: User) -> bool:
"""Check if a user can delete this event.

Returns true if either:
- user supervises any of this event activities
- user is moderator
Equivalent to :func:`has_edit_rights`

:param user: User which will be tested.
:type user: :py:class:`collectives.models.user.User`
:return: True if user can delete the event.
:rtype: boolean
"""
return user.is_moderator() or self.is_supervisor(user)
return self.has_edit_rights(user)

def can_remove_leader(self, user, leader):
"""
Expand Down
4 changes: 4 additions & 0 deletions collectives/routes/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,10 @@ def manage_event(event_id=None):
for tag in form.tag_list.data:
event.tag_refs.append(EventTag(tag))

# delete existing photo if requested
if form.remove_photo and form.remove_photo.data:
event.delete_photo()

# We have to save new event before add the photo, or id is not defined
db.session.add(event)
update_waiting_list(event)
Expand Down
4 changes: 4 additions & 0 deletions collectives/templates/event/editevent.html
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,10 @@ <h4 class="heading-4">Description</h4>
<span class="label-help">&#9432; Photo uniquement, d'une taille maximum de 2Mo. Uniquement libre de droit ou dont vous avez les droits.</span>
</label>
{{ form.photo_file(onchange="checkFileFieldSize(this)") }}
{%if form.remove_photo %}
<div class="inline_field">
{{form.remove_photo}} {{form.remove_photo.label}}
</div>{% endif %}
</div>
<div class="controls">
<label for="{{form.tag_list.id_for_label}}">Labels:</label>{{ form.tag_list }}
Expand Down