WIP: avoir un rôle d'administration (#66019) #252

Draft
vdeniaud wants to merge 6 commits from wip/66019-Affiner-la-granularite-des-droi into main
33 changed files with 586 additions and 287 deletions

View File

@ -20,7 +20,7 @@ class Migration(migrations.Migration):
on_delete=django.db.models.deletion.SET_NULL,
related_name='+',
to='auth.Group',
verbose_name='Edit Role',
verbose_name='Admin Role',
),
),
migrations.AlterField(

View File

@ -4,6 +4,8 @@ import django.contrib.postgres.fields
import django.db.models.deletion
from django.db import migrations, models
import chrono
class Migration(migrations.Migration):
dependencies = [
@ -22,11 +24,7 @@ class Migration(migrations.Migration):
'almost_full_event',
models.CharField(
blank=True,
choices=[
('edit-role', 'Edit Role'),
('view-role', 'View Role'),
('use-email-field', 'Specify email addresses manually'),
],
choices=chrono.agendas.models.AgendaNotificationsSettings.CHOICES,
max_length=16,
verbose_name='Almost full event (90%)',
),
@ -41,11 +39,7 @@ class Migration(migrations.Migration):
'full_event',
models.CharField(
blank=True,
choices=[
('edit-role', 'Edit Role'),
('view-role', 'View Role'),
('use-email-field', 'Specify email addresses manually'),
],
choices=chrono.agendas.models.AgendaNotificationsSettings.CHOICES,
max_length=16,
verbose_name='Full event',
),
@ -60,11 +54,7 @@ class Migration(migrations.Migration):
'cancelled_event',
models.CharField(
blank=True,
choices=[
('edit-role', 'Edit Role'),
('view-role', 'View Role'),
('use-email-field', 'Specify email addresses manually'),
],
choices=chrono.agendas.models.AgendaNotificationsSettings.CHOICES,
max_length=16,
verbose_name='Cancelled event',
),

View File

@ -0,0 +1,47 @@
# Generated by Django 3.2.18 on 2024-04-24 08:02
import django
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('agendas', '0171_snapshot_models'),
]
operations = [
migrations.AlterField(
model_name='agenda',
name='edit_role',
field=models.ForeignKey(
# remove db_index as RenameField will not do it
# https://code.djangoproject.com/ticket/23577
db_index=False,
blank=True,
default=None,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name='+',
to='auth.group',
verbose_name='Admin Role',
),
),
migrations.RenameField(
model_name='agenda',
old_name='edit_role',
new_name='admin_role',
),
migrations.AlterField(
model_name='agenda',
name='admin_role',
field=models.ForeignKey(
blank=True,
default=None,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name='+',
to='auth.group',
verbose_name='Admin Role',
),
),
]

View File

@ -0,0 +1,41 @@
# Generated by Django 3.2.18 on 2024-04-24 13:20
from django.db import migrations
def migrate_notification_settings_role(apps, schema_editor):
AgendaNotificationsSettings = apps.get_model('agendas', 'AgendaNotificationsSettings')
AgendaNotificationsSettings.objects.filter(almost_full_event='edit-role').update(
almost_full_event='admin-role',
)
AgendaNotificationsSettings.objects.filter(full_event='edit-role').update(
full_event='admin-role',
)
AgendaNotificationsSettings.objects.filter(cancelled_event='edit-role').update(
cancelled_event='admin-role',
)
def reverse_migrate_notification_settings_role(apps, schema_editor):
AgendaNotificationsSettings = apps.get_model('agendas', 'AgendaNotificationsSettings')
AgendaNotificationsSettings.objects.filter(almost_full_event='admin-role').update(
almost_full_event='edit-role',
)
AgendaNotificationsSettings.objects.filter(full_event='admin-role').update(
full_event='edit-role',
)
AgendaNotificationsSettings.objects.filter(cancelled_event='admin-role').update(
cancelled_event='edit-role',
)
class Migration(migrations.Migration):
dependencies = [
('agendas', '0172_rename_edit_role_agenda_admin_role'),
]
operations = [
migrations.RunPython(migrate_notification_settings_role, reverse_migrate_notification_settings_role),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 3.2.18 on 2024-04-24 13:20
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
('agendas', '0173_migrate_notification_settings_role'),
]
operations = [
migrations.AddField(
model_name='agenda',
name='edit_role',
field=models.ForeignKey(
blank=True,
default=None,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name='+',
to='auth.group',
verbose_name='Edit Role',
),
),
]

View File

@ -250,6 +250,15 @@ class Agenda(WithSnapshotMixin, WithApplicationMixin, WithInspectMixin, models.M
through='VirtualMember',
through_fields=('virtual_agenda', 'real_agenda'),
)
admin_role = models.ForeignKey(
Group,
blank=True,
null=True,
default=None,
related_name='+',
verbose_name=_('Admin Role'),
on_delete=models.SET_NULL,
)
edit_role = models.ForeignKey(
Group,
blank=True,
@ -407,10 +416,16 @@ class Agenda(WithSnapshotMixin, WithApplicationMixin, WithInspectMixin, models.M
if user.is_staff:
return True
group_ids = [x.id for x in user.groups.all()]
return bool(self.admin_role_id in group_ids)
def can_be_edited(self, user):
if self.can_be_managed(user):
return True
group_ids = [x.id for x in user.groups.all()]
return bool(self.edit_role_id in group_ids)
def can_be_viewed(self, user):
if self.can_be_managed(user):
if self.can_be_edited(user):
return True
group_ids = [x.id for x in user.groups.all()]
return bool(self.view_role_id in group_ids)
@ -491,6 +506,7 @@ class Agenda(WithSnapshotMixin, WithApplicationMixin, WithInspectMixin, models.M
def get_dependencies(self):
yield self.view_role
yield self.admin_role
yield self.edit_role
yield self.category
if self.kind == 'virtual':
@ -515,6 +531,7 @@ class Agenda(WithSnapshotMixin, WithApplicationMixin, WithInspectMixin, models.M
'permissions': {
'view': self.view_role.name if self.view_role else None,
'edit': self.edit_role.name if self.edit_role else None,
'admin': self.admin_role.name if self.admin_role else None,
},
'default_view': self.default_view,
}
@ -564,11 +581,9 @@ class Agenda(WithSnapshotMixin, WithApplicationMixin, WithInspectMixin, models.M
elif data['kind'] == 'virtual':
excluded_timeperiods = data.pop('excluded_timeperiods')
real_agendas = data.pop('real_agendas')
for permission in ('view', 'edit'):
for permission in ('view', 'admin', 'edit'):
if permissions.get(permission):
data[permission + '_role'] = Group.objects.get(name=permissions[permission])
if permissions.get('admin'):
data['edit_role'] = Group.objects.get(name=permissions['admin'])
resources_slug = data.pop('resources', [])
resources_by_slug = {r.slug: r for r in Resource.objects.filter(slug__in=resources_slug)}
for resource_slug in resources_slug:
@ -663,7 +678,7 @@ class Agenda(WithSnapshotMixin, WithApplicationMixin, WithInspectMixin, models.M
return keys
def get_permissions_inspect_fields(self):
yield from self.get_inspect_fields(keys=['edit_role', 'view_role'])
yield from self.get_inspect_fields(keys=['admin_role', 'edit_role', 'view_role'])
def get_display_inspect_fields(self):
keys = []
@ -3729,7 +3744,7 @@ class Resource(WithSnapshotMixin, WithApplicationMixin, WithInspectMixin, models
if user.is_staff:
return True
group_ids = [x.id for x in user.groups.all()]
return self.agenda_set.filter(edit_role_id__in=group_ids).exists()
return self.agenda_set.filter(admin_role_id__in=group_ids).exists()
def get_dependencies(self):
return []
@ -4469,11 +4484,13 @@ class NotificationType:
class AgendaNotificationsSettings(WithInspectMixin, models.Model):
EMAIL_FIELD = 'use-email-field'
VIEW_ROLE = 'view-role'
ADMIN_ROLE = 'admin-role'
EDIT_ROLE = 'edit-role'
CHOICES = [
(EDIT_ROLE, _('Edit Role')),
(ADMIN_ROLE, _('Admin Role')),
(VIEW_ROLE, _('View Role')),
(EDIT_ROLE, _('Edit Role')),
(EMAIL_FIELD, _('Specify email addresses manually')),
]
@ -4509,10 +4526,12 @@ class AgendaNotificationsSettings(WithInspectMixin, models.Model):
yield notification_type
def get_role_from_choice(self, choice):
if choice == self.EDIT_ROLE:
return self.agenda.edit_role
if choice == self.ADMIN_ROLE:
return self.agenda.admin_role
elif choice == self.VIEW_ROLE:
return self.agenda.view_role
elif choice == self.EDIT_ROLE:
return self.agenda.edit_role
@classmethod
def import_json(cls, data):

View File

@ -679,6 +679,7 @@ class EventSerializer(serializers.ModelSerializer):
class AgendaSerializer(serializers.ModelSerializer):
admin_role = serializers.CharField(required=False, max_length=150)
edit_role = serializers.CharField(required=False, max_length=150)
view_role = serializers.CharField(required=False, max_length=150)
category = serializers.SlugField(required=False, max_length=160)
@ -696,6 +697,7 @@ class AgendaSerializer(serializers.ModelSerializer):
'maximal_booking_delay',
'minimal_booking_time',
'anonymize_delay',
'admin_role',
'edit_role',
'view_role',
'category',
@ -712,6 +714,9 @@ class AgendaSerializer(serializers.ModelSerializer):
except Group.DoesNotExist:
raise serializers.ValidationError(_('unknown role: %s' % value))
def validate_admin_role(self, value):
return self.get_role(value)
def validate_edit_role(self, value):
return self.get_role(value)

View File

@ -79,6 +79,7 @@ def get_agenda_detail(request, agenda, check_events=False):
'minimal_booking_delay': agenda.minimal_booking_delay,
'maximal_booking_delay': agenda.maximal_booking_delay,
'minimal_booking_time': agenda.minimal_booking_time,
'admin_role': agenda.admin_role.name if agenda.admin_role else None,
'edit_role': agenda.edit_role.name if agenda.edit_role else None,
'view_role': agenda.view_role.name if agenda.view_role else None,
'category': agenda.category.slug if agenda.category else None,
@ -550,7 +551,7 @@ class Agendas(APIView):
def get(self, request, format=None):
agendas_queryset = (
Agenda.objects.all()
.select_related('category', 'edit_role', 'view_role', 'events_type')
.select_related('category', 'admin_role', 'edit_role', 'view_role', 'events_type')
.prefetch_related('resources')
.order_by('label')
)

View File

@ -77,6 +77,7 @@ from .widgets import SplitDateTimeField, WeekdaysWidget
class AgendaAddForm(forms.ModelForm):
admin_role = forms.ModelChoiceField(label=_('Admin Role'), required=False, queryset=get_role_queryset())
edit_role = forms.ModelChoiceField(label=_('Edit Role'), required=False, queryset=get_role_queryset())
view_role = forms.ModelChoiceField(label=_('View Role'), required=False, queryset=get_role_queryset())
@ -87,7 +88,7 @@ class AgendaAddForm(forms.ModelForm):
class Meta:
model = Agenda
fields = ['label', 'kind', 'category', 'edit_role', 'view_role']
fields = ['label', 'kind', 'category', 'admin_role', 'edit_role', 'view_role']
def clean(self):
super().clean()
@ -164,6 +165,7 @@ class AgendaRolesForm(AgendaAddForm):
class Meta:
model = Agenda
fields = [
'admin_role',
'edit_role',
'view_role',
]
@ -1672,7 +1674,7 @@ class AgendaNotificationsForm(forms.ModelForm):
def update_choices(choices, settings):
new_choices = []
for choice in choices:
if choice[0] in (settings.EDIT_ROLE, settings.VIEW_ROLE):
if choice[0] in (settings.ADMIN_ROLE, settings.EDIT_ROLE, settings.VIEW_ROLE):
role = settings.get_role_from_choice(choice[0]) or _('undefined')
choice = (choice[0], '%s (%s)' % (choice[1], role))
new_choices.append(choice)

View File

@ -7,7 +7,7 @@
<span class="actions">
<a class="extra-actions-menu-opener"></a>
<ul class="extra-actions-menu">
{% if user_can_manage %}
{% if user_can_edit %}
<li><a href="{{ agenda.get_settings_url }}">{% trans 'Settings' %}</a></li>
{% endif %}
{% block agenda-extra-menu-actions %}{% endblock %}

View File

@ -31,7 +31,7 @@
</div>
</div>
<div aria-labelledby="tab-settings" hidden id="panel-settings" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-settings" id="panel-settings" role="tabpanel" tabindex="0">
<div class="section">
{% if object.kind != 'virtual' %}
@ -106,7 +106,7 @@
</div>
</div>
<div aria-labelledby="tab-permissions" hidden id="panel-permissions" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-permissions" id="panel-permissions" role="tabpanel" tabindex="0">
<div class="section">
<ul>
{% for label, value in object.get_permissions_inspect_fields %}
@ -121,7 +121,7 @@
{% if object.kind == 'events' %}
<div aria-labelledby="tab-events" hidden id="panel-events" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-events" id="panel-events" role="tabpanel" tabindex="0">
<div class="section">
{% for event in object.event_set.all %}
<h4>{{ event }}</h4>
@ -150,7 +150,7 @@
</div>
</div>
<div aria-labelledby="tab-exceptions" hidden id="panel-exceptions" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-exceptions" id="panel-exceptions" role="tabpanel" tabindex="0">
<div class="section">
{% for desk in object.desk_set.all %}{% if desk.slug == '_exceptions_holder' %}
<h4>{% trans "Unavailability calendars" %}</h4>
@ -193,7 +193,7 @@
{% elif object.kind == 'meetings' %}
<div aria-labelledby="tab-meeting-types" hidden id="panel-meeting-types" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-meeting-types" id="panel-meeting-types" role="tabpanel" tabindex="0">
<div class="section">
{% for meeting_type in object.meetingtype_set.all %}
<h4>{{ meeting_type }}</h4>
@ -209,7 +209,7 @@
</div>
</div>
<div aria-labelledby="tab-desks" hidden id="panel-desks" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-desks" id="panel-desks" role="tabpanel" tabindex="0">
<div class="section">
{% for desk in object.desk_set.all %}
<h4>{{ desk }}</h4>
@ -274,7 +274,7 @@
</div>
</div>
<div aria-labelledby="tab-resources" hidden id="panel-resources" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-resources" id="panel-resources" role="tabpanel" tabindex="0">
<div class="section">
<ul>
{% for resource in object.resources.all %}
@ -288,7 +288,7 @@
{% elif object.kind == "virtual" %}
<div aria-labelledby="tab-agendas" hidden id="panel-agendas" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-agendas" id="panel-agendas" role="tabpanel" tabindex="0">
<div class="section">
<ul>
{% for agenda in object.real_agendas.all %}
@ -300,7 +300,7 @@
</div>
</div>
<div aria-labelledby="tab-time-periods" hidden id="panel-time-periods" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-time-periods" id="panel-time-periods" role="tabpanel" tabindex="0">
<div class="section">
{% for time_period in object.excluded_timeperiods.all %}
<h4>{{ time_period }}</h4>

View File

@ -14,19 +14,21 @@
<h2>{% trans "Settings" %}
<span class="identifier">[{% trans "identifier:" %} {{object.slug}}]</span>
</h2>
<span class="actions">
<a class="extra-actions-menu-opener"></a>
<ul class="extra-actions-menu">
{% block agenda-extra-menu-actions %}{% endblock %}
<li><a download href="{% url 'chrono-manager-agenda-export' pk=object.id %}">{% trans 'Export Configuration (JSON)' %}</a></li>
{% if object.kind == 'events' %}
<li><a download href="{% url 'chrono-manager-agenda-export-events' pk=object.pk %}">{% trans 'Export Events (CSV)' %}</a></li>
{% endif %}
{% if user.is_staff %}
<li><a rel="popup" href="{% url 'chrono-manager-agenda-delete' pk=object.id %}">{% trans 'Delete' %}</a></li>
{% endif %}
</ul>
</span>
{% if user_can_manage %}
<span class="actions">
<a class="extra-actions-menu-opener"></a>
<ul class="extra-actions-menu">
{% block agenda-extra-menu-actions %}{% endblock %}
<li><a download href="{% url 'chrono-manager-agenda-export' pk=object.id %}">{% trans 'Export Configuration (JSON)' %}</a></li>
{% if object.kind == 'events' %}
<li><a download href="{% url 'chrono-manager-agenda-export-events' pk=object.pk %}">{% trans 'Export Events (CSV)' %}</a></li>
{% endif %}
{% if user.is_staff %}
<li><a rel="popup" href="{% url 'chrono-manager-agenda-delete' pk=object.id %}">{% trans 'Delete' %}</a></li>
{% endif %}
</ul>
</span>
{% endif %}
{% endblock %}
{% block content %}
@ -39,14 +41,16 @@
<button aria-controls="panel-reminders" aria-selected="false" id="tab-reminders" role="tab" tabindex="-1">{% trans "Booking reminders" %}</button>
{% endif %}
<button aria-controls="panel-delays" aria-selected="false" id="tab-delays" role="tab" tabindex="-1">{% trans "Booking Delays" %}</button>
<button aria-controls="panel-permissions" aria-selected="false" id="tab-permissions" role="tab" tabindex="-1">{% trans "Permissions" %}</button>
{% if user_can_manage %}
<button aria-controls="panel-permissions" aria-selected="false" id="tab-permissions" role="tab" tabindex="-1">{% trans "Permissions" %}</button>
{% endif %}
</div>
<div class="pk-tabs--container">
{% block agenda-settings-extra-tab-list %}{% endblock %}
{% if object.kind != 'virtual' and not object.partial_bookings %}
<div aria-labelledby="tab-reminders" id="panel-reminders" role="tabpanel" tabindex="0" hidden="">
<div aria-labelledby="tab-reminders" id="panel-reminders" role="tabpanel" tabindex="0" "">
{% for info in agenda.reminder_settings.display_info %}
<p>{{ info }}</p>
{% empty %}
@ -71,7 +75,7 @@
</div>
{% endif %}
<div aria-labelledby="tab-delays" hidden="" id="panel-delays" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-delays" "" id="panel-delays" role="tabpanel" tabindex="0">
<ul>
<li>{% trans "Minimal booking delay:" %}
{% if agenda.minimal_booking_delay is not None %}
@ -95,15 +99,18 @@
</div>
</div>
<div aria-labelledby="tab-permissions" hidden="" id="panel-permissions" role="tabpanel" tabindex="0">
<ul>
<li>{% trans "Edit Role:" %} {% if agenda.edit_role %}{{ agenda.edit_role }}{% else %}<i>{% trans "undefined" %}</i>{% endif %}</li>
<li>{% trans "View Role:" %} {% if agenda.view_role %}{{ agenda.view_role }}{% else %}<i>{% trans "undefined" %}</i>{% endif %}</li>
</ul>
<div class="panel--buttons">
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-roles' pk=object.id %}">{% trans 'Configure' %}</a>
{% if user_can_manage %}
<div aria-labelledby="tab-permissions" "" id="panel-permissions" role="tabpanel" tabindex="0">
<ul>
<li>{% trans "Admin Role:" %} {% if agenda.admin_role %}{{ agenda.admin_role }}{% else %}<i>{% trans "undefined" %}</i>{% endif %}</li>
<li>{% trans "Edit Role:" %} {% if agenda.edit_role %}{{ agenda.edit_role }}{% else %}<i>{% trans "undefined" %}</i>{% endif %}</li>
<li>{% trans "View Role:" %} {% if agenda.view_role %}{{ agenda.view_role }}{% else %}<i>{% trans "undefined" %}</i>{% endif %}</li>
</ul>
<div class="panel--buttons">
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-roles' pk=object.id %}">{% trans 'Configure' %}</a>
</div>
</div>
</div>
{% endif %}
</div>
</div>
@ -112,22 +119,27 @@
{% block sidebar %}
<aside id="sidebar">
<h3>{% trans "Actions" %}</h3>
{% block agenda-extra-management-actions %}{% endblock %}
<a class="button button-paragraph" rel="popup" href="{% url 'chrono-manager-agenda-edit' pk=object.id %}">{% trans 'Options' %}</a>
{% if user_can_manage %}
<a class="button button-paragraph" rel="popup" href="{% url 'chrono-manager-agenda-edit' pk=object.id %}">{% trans 'Options' %}</a>
{% endif %}
{% if user.is_staff %}
<a class="button button-paragraph" rel="popup" class="action-duplicate" href="{% url 'chrono-manager-agenda-duplicate' pk=object.pk %}">{% trans 'Duplicate' %}</a>
{% endif %}
<h3>{% trans "Navigation" %}</h3>
{% if show_history %}
<a class="button button-paragraph" href="{% url 'chrono-manager-agenda-history' pk=agenda.pk %}">{% trans 'History' %}</a>
{% if user_can_manage %}
{% if show_history %}
<a class="button button-paragraph" href="{% url 'chrono-manager-agenda-history' pk=agenda.pk %}">{% trans 'History' %}</a>
{% endif %}
<a class="button button-paragraph" href="{% url 'chrono-manager-agenda-inspect' pk=agenda.pk %}">{% trans 'Inspect' %}</a>
{% endif %}
<a class="button button-paragraph" href="{% url 'chrono-manager-agenda-inspect' pk=agenda.pk %}">{% trans 'Inspect' %}</a>
{% block agenda-extra-navigation-actions %}{% endblock %}
{% url 'chrono-manager-homepage' as object_list_url %}
{% include 'chrono/includes/application_detail_fragment.html' %}
{% if user_can_manage %}
{% url 'chrono-manager-homepage' as object_list_url %}
{% include 'chrono/includes/application_detail_fragment.html' %}
{% endif %}
</aside>
{% endblock %}

View File

@ -7,7 +7,7 @@
<h2>{{ object.label }}</h2>
<span class="actions">
{% block actions %}
{% if user_can_manage %}
{% if user_can_edit %}
<a href="{{ object.get_settings_url }}">{% trans 'Settings' %}</a>
{% endif %}
{% endblock %}

View File

@ -2,14 +2,18 @@
{% load i18n %}
{% block agenda-extra-management-actions %}
<a class="button button-paragraph" rel="popup" href="{% url 'chrono-manager-agenda-import-events' pk=object.id %}">{% trans 'Import Events' %}</a>
{% if user_can_manage %}
<a class="button button-paragraph" rel="popup" href="{% url 'chrono-manager-agenda-import-events' pk=object.id %}">{% trans 'Import Events' %}</a>
{% endif %}
<a class="button button-paragraph" rel="popup" href="{% url 'chrono-manager-agenda-add-event' pk=object.id %}">{% trans 'New Event' %}</a>
{% endblock %}
{% block agenda-extra-navigation-actions %}
{% with lingo_url=object.get_lingo_url %}{% if lingo_url %}
<a class="button button-paragraph" href="{{ lingo_url }}">{% trans 'Pricing' context 'pricing' %}</a>
{% endif %}{% endwith %}
{% if user_can_manage %}
{% with lingo_url=object.get_lingo_url %}{% if lingo_url %}
<a class="button button-paragraph" href="{{ lingo_url }}">{% trans 'Pricing' context 'pricing' %}</a>
{% endif %}{% endwith %}
{% endif %}
{% endblock %}
{% block agenda-settings-extra-tab-buttons %}
@ -17,9 +21,11 @@
{% if has_recurring_events %}
<button aria-controls="panel-time-periods" aria-selected="false" id="tab-time-periods" role="tab" tabindex="-1">{% trans "Recurrence exceptions" %}</button>
{% endif %}
<button aria-controls="panel-display-options" aria-selected="false" id="tab-display-options" role="tab" tabindex="-1">{% trans "Display options" %}</button>
<button aria-controls="panel-booking-check-options" aria-selected="false" id="tab-booking-check-options" role="tab" tabindex="-1">{% trans "Booking check options" %}</button>
{% if agenda.partial_bookings %}
{% if user_can_manage %}
<button aria-controls="panel-display-options" aria-selected="false" id="tab-display-options" role="tab" tabindex="-1">{% trans "Display options" %}</button>
<button aria-controls="panel-booking-check-options" aria-selected="false" id="tab-booking-check-options" role="tab" tabindex="-1">{% trans "Booking check options" %}</button>
{% endif %}
{% if agenda.partial_bookings and user_can_manage %}
<button aria-controls="panel-invoicing-options" aria-selected="false" id="tab-invoicing-options" role="tab" tabindex="-1">{% trans "Invoicing options" %}</button>
{% else %}
<button aria-controls="panel-notifications" aria-selected="false" id="tab-notifications" role="tab" tabindex="-1">{% trans "Management notifications" %}</button>
@ -48,7 +54,7 @@
</div>
{% if has_recurring_events %}
<div aria-labelledby="tab-time-periods" hidden="" id="panel-time-periods" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-time-periods" "" id="panel-time-periods" role="tabpanel" tabindex="0">
{% if object.recurrence_exceptions_report.events.exists %}
<div class="warningnotice">
<p>{% trans "The following events exist despite exceptions because they have active bookings:" %}</p>
@ -80,60 +86,62 @@
</div>
{% endif %}
<div aria-labelledby="tab-display-options" hidden="" id="panel-display-options" role="tabpanel" tabindex="0">
<ul>
<li>
{% if agenda.event_display_template %}
{% trans "Event display template:" %}
{% if user_can_manage %}
<div aria-labelledby="tab-display-options" "" id="panel-display-options" role="tabpanel" tabindex="0">
<ul>
<li>
{% if agenda.event_display_template %}
{% trans "Event display template:" %}
<pre>{{ agenda.event_display_template }}</pre>
{% else %}
{% trans "No event display template configured for this agenda." %}
{% endif %}
</li>
{% if not agenda.partial_bookings %}
<li>
{% trans "Booking display template:" %}
{% else %}
{% trans "No event display template configured for this agenda." %}
{% endif %}
</li>
{% if not agenda.partial_bookings %}
<li>
{% trans "Booking display template:" %}
<pre>{{ agenda.get_booking_user_block_template }}</pre>
</li>
{% endif %}
</ul>
<div class="panel--buttons">
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-display-settings' pk=object.pk %}">{% trans 'Configure' %}</a>
</div>
</div>
<div aria-labelledby="tab-booking-check-options" hidden="" id="panel-booking-check-options" role="tabpanel" tabindex="0">
<ul>
{% with agenda.get_booking_check_filters as check_filters %}
{% if check_filters %}
<li>{% trans "Filters:" %}
<ul>
{% for key in check_filters %}
<li>{{ key }}</li>
{% endfor %}
</ul>
</li>
{% else %}
<li>{% trans "No filters configured for this agenda." %}</li>
{% endif %}
{% endwith %}
<li>{% trans "Automatically mark event as checked when all bookings have been checked:" %} {{ agenda.mark_event_checked_auto|yesno }}</li>
<li>{% trans "Prevent the check of bookings when event was marked as checked:" %} {{ agenda.disable_check_update|yesno }}</li>
{% if not agenda.partial_bookings %}
<li>{% trans "Enable the check of bookings when event has not passed:" %} {{ agenda.enable_check_for_future_events|yesno }}</li>
<li>
{% trans "Extra user block template:" %}
<pre>{{ agenda.booking_extra_user_block_template }}</pre>
</li>
{% endif %}
</ul>
<div class="panel--buttons">
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-booking-check-settings' pk=object.pk %}">{% trans 'Configure' %}</a>
</ul>
<div class="panel--buttons">
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-display-settings' pk=object.pk %}">{% trans 'Configure' %}</a>
</div>
</div>
</div>
{% if agenda.partial_bookings %}
<div aria-labelledby="tab-invoicing-options" hidden="" id="panel-invoicing-options" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-booking-check-options" "" id="panel-booking-check-options" role="tabpanel" tabindex="0">
<ul>
{% with agenda.get_booking_check_filters as check_filters %}
{% if check_filters %}
<li>{% trans "Filters:" %}
<ul>
{% for key in check_filters %}
<li>{{ key }}</li>
{% endfor %}
</ul>
</li>
{% else %}
<li>{% trans "No filters configured for this agenda." %}</li>
{% endif %}
{% endwith %}
<li>{% trans "Automatically mark event as checked when all bookings have been checked:" %} {{ agenda.mark_event_checked_auto|yesno }}</li>
<li>{% trans "Prevent the check of bookings when event was marked as checked:" %} {{ agenda.disable_check_update|yesno }}</li>
{% if not agenda.partial_bookings %}
<li>{% trans "Enable the check of bookings when event has not passed:" %} {{ agenda.enable_check_for_future_events|yesno }}</li>
<li>
{% trans "Extra user block template:" %}
<pre>{{ agenda.booking_extra_user_block_template }}</pre>
</li>
{% endif %}
</ul>
<div class="panel--buttons">
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-booking-check-settings' pk=object.pk %}">{% trans 'Configure' %}</a>
</div>
</div>
{% endif %}
{% if agenda.partial_bookings and user_can_manage %}
<div aria-labelledby="tab-invoicing-options" "" id="panel-invoicing-options" role="tabpanel" tabindex="0">
<ul>
<li>{% trans "Invoicing:" %} {{ agenda.get_invoicing_unit_display }}</li>
<li>{% trans "Tolerance:" %} {% blocktrans count num=agenda.invoicing_tolerance %}{{ num }} minute{% plural %}{{ num }} minutes{% endblocktrans %}</li>
@ -145,7 +153,7 @@
{% endif %}
{% if not agenda.partial_bookings %}
<div aria-labelledby="tab-notifications" hidden="" id="panel-notifications" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-notifications" "" id="panel-notifications" role="tabpanel" tabindex="0">
{% for notification_type in object.notifications_settings.get_notification_types %}
{% if forloop.first %}<ul>{% endif %}
<li>

View File

@ -19,7 +19,7 @@
</div>
</div>
<div aria-labelledby="tab-custom-fields" hidden id="panel-custom-fields" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-custom-fields" id="panel-custom-fields" role="tabpanel" tabindex="0">
<div class="section">
{% for value in object.get_custom_fields %}
<h4>{{ value.label }}</h4>

View File

@ -12,20 +12,31 @@
{% endif %}
{% endblock %}
{% block sidebar %}
{% if user_can_manage %}
{# show sidebar only if admin user #}
{{ block.super }}
{% endif %}
{% endblock %}
{% block agenda-extra-menu-actions %}
{% if object.desk_simple_management %}
<li><a href="{% url 'chrono-manager-agenda-desk-management-toggle-view' pk=object.pk %}">{% trans 'Switch to invididual desk management' %}</a></li>
{% elif agenda_is_available_for_simple_management %}
<li><a href="{% url 'chrono-manager-agenda-desk-management-toggle-view' pk=object.pk %}">{% trans 'Switch to global desk management' %}</a></li>
{% if user_can_manage %}
{% if object.desk_simple_management %}
<li><a href="{% url 'chrono-manager-agenda-desk-management-toggle-view' pk=object.pk %}">{% trans 'Switch to invididual desk management' %}</a></li>
{% elif agenda_is_available_for_simple_management %}
<li><a href="{% url 'chrono-manager-agenda-desk-management-toggle-view' pk=object.pk %}">{% trans 'Switch to global desk management' %}</a></li>
{% endif %}
{% endif %}
{% endblock %}
{% block agenda-settings-extra-tab-buttons %}
<button aria-controls="panel-meeting-types"
aria-selected="{{meeting_types|yesno:"false,true"}}"
id="tab-meeting-types" role="tab"
tabindex="{{meeting_types|yesno:"-1,0"}}"
>{% trans "Meeting Types" %}</button>
{% if user_can_manage %}
<button aria-controls="panel-meeting-types"
aria-selected="{{meeting_types|yesno:"false,true"}}"
id="tab-meeting-types" role="tab"
tabindex="{{meeting_types|yesno:"-1,0"}}"
>{% trans "Meeting Types" %}</button>
{% endif %}
{% if object.desk_simple_management %}
<button aria-controls="panel-desks" aria-selected="false" id="tab-desks" role="tab" tabindex="-1">{% trans "Desks" %}</button>
{% endif %}
@ -34,44 +45,48 @@
id="tab-time-periods" role="tab"
tabindex="{{meeting_types|yesno:"0"}}"
>{% trans "Opening hours" %}</button>
{% if has_resources %}
{% if has_resources and user_can_manage %}
<button aria-controls="panel-resources" aria-selected="false" id="tab-resources" role="tab" tabindex="-1">{% trans "Resources" %}</button>
{% endif %}
<button aria-controls="panel-display-options" aria-selected="false" id="tab-display-options" role="tab" tabindex="-1">{% trans "Display options" %}</button>
{% if user_can_manage %}
<button aria-controls="panel-display-options" aria-selected="false" id="tab-display-options" role="tab" tabindex="-1">{% trans "Display options" %}</button>
{% endif %}
{% endblock %}
{% block agenda-settings-extra-tab-list %}
<div aria-labelledby="tab-meeting-types" id="panel-meeting-types" role="tabpanel" tabindex="0"
{% if meeting_types %}hidden{% endif %}>
{% if meeting_types %}
<ul class="objects-list single-links">
{% for meeting_type in meeting_types %}
<li>
<a rel="popup" href="{% url 'chrono-manager-meeting-type-edit' pk=meeting_type.id %}">
{{meeting_type.label}}
<span class="duration">({{meeting_type.duration}} {% trans "minutes" %})</span>
<span class="identifier">[{% trans "identifier:" %} {{meeting_type.slug}}]</span>
</a>
<a rel="popup" class="delete" href="{% url 'chrono-manager-meeting-type-delete' pk=meeting_type.id %}">{% trans "remove" %}</a>
</li>
{% endfor %}
</ul>
{% else %}
<div class="big-msg-info">
{% blocktrans trimmed %}
This agenda doesn't have any meeting type yet. Click on the "New Meeting Type" button to add a first one.
{% endblocktrans %}
{% if user_can_manage %}
<div aria-labelledby="tab-meeting-types" id="panel-meeting-types" role="tabpanel" tabindex="0"
{% if meeting_types %}hidden{% endif %}>
{% if meeting_types %}
<ul class="objects-list single-links">
{% for meeting_type in meeting_types %}
<li>
<a rel="popup" href="{% url 'chrono-manager-meeting-type-edit' pk=meeting_type.id %}">
{{meeting_type.label}}
<span class="duration">({{meeting_type.duration}} {% trans "minutes" %})</span>
<span class="identifier">[{% trans "identifier:" %} {{meeting_type.slug}}]</span>
</a>
<a rel="popup" class="delete" href="{% url 'chrono-manager-meeting-type-delete' pk=meeting_type.id %}">{% trans "remove" %}</a>
</li>
{% endfor %}
</ul>
{% else %}
<div class="big-msg-info">
{% blocktrans trimmed %}
This agenda doesn't have any meeting type yet. Click on the "New Meeting Type" button to add a first one.
{% endblocktrans %}
</div>
{% endif %}
<div class="panel--buttons">
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-add-meeting-type' pk=object.id %}">{% trans 'New Meeting Type' %}</a>
</div>
{% endif %}
<div class="panel--buttons">
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-add-meeting-type' pk=object.id %}">{% trans 'New Meeting Type' %}</a>
</div>
</div>
</div>
{% endif %}
{% if object.desk_simple_management %}
<div aria-labelledby="tab-desks" hidden="" id="panel-desks" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-desks" "" id="panel-desks" role="tabpanel" tabindex="0">
<ul class="objects-list single-links">
{% for desk in object.prefetched_desks %}
<li>
@ -150,8 +165,8 @@
{% endif %}
</div>
{% if has_resources %}
<div aria-labelledby="tab-resources" hidden="" id="panel-resources" role="tabpanel" tabindex="0">
{% if has_resources and user_can_manage %}
<div aria-labelledby="tab-resources" "" id="panel-resources" role="tabpanel" tabindex="0">
{% with object.resources.all as agenda_resources %}
{% if agenda_resources %}
<ul class="objects-list single-links">
@ -179,16 +194,18 @@
</div>
{% endif %}
<div aria-labelledby="tab-display-options" hidden="" id="panel-display-options" role="tabpanel" tabindex="0">
<ul>
<li>
{% trans "Booking display template:" %}
{% if user_can_manage %}
<div aria-labelledby="tab-display-options" "" id="panel-display-options" role="tabpanel" tabindex="0">
<ul>
<li>
{% trans "Booking display template:" %}
<pre>{{ agenda.get_booking_user_block_template }}</pre>
</li>
</ul>
<div class="panel--buttons">
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-display-settings' pk=object.pk %}">{% trans 'Configure' %}</a>
</li>
</ul>
<div class="panel--buttons">
<a rel="popup" class="button" href="{% url 'chrono-manager-agenda-display-settings' pk=object.pk %}">{% trans 'Configure' %}</a>
</div>
</div>
</div>
{% endif %}
{% endblock %}

View File

@ -51,7 +51,7 @@
data-end-datetime="{{ end_datetime.isoformat }}"
>
{% if view.date.date == today %}
<div class="partial-booking--hour-indicator-wrapper" aria-hidden="true">
<div class="partial-booking--hour-indicator-wrapper" aria-"true">
<div class="partial-booking--hour-indicator" hidden></div>
<script>
const hour_indicator = (function() {

View File

@ -76,7 +76,7 @@
</div>
{% if has_holidays %}
<div aria-labelledby="tab-holidays" hidden="" id="panel-holidays" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-holidays" "" id="panel-holidays" role="tabpanel" tabindex="0">
{% if agenda.holiday_rules.all %}
<ul class="objects-list single-links">
{% for rule in agenda.holiday_rules.all %}
@ -99,7 +99,7 @@
</div>
{% endif %}
<div aria-labelledby="tab-time-periods" hidden="" id="panel-time-periods" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-time-periods" "" id="panel-time-periods" role="tabpanel" tabindex="0">
{% if exceptional_periods %}
<ul class="objects-list single-links">
{% for period in exceptional_periods %}

View File

@ -35,8 +35,8 @@
{{ field.errors }}
<p>{{ field.label_tag }} {{ field }}{% if field.name == 'start_datetime' %} <button type="button" id="allday">{% trans "All day" %}</button>{% endif%}</p>
{% endfor %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% for in form.hidden_fields %}
{{ hop }}
{% endfor %}
<div class="buttons">
<button class="submit-button">{% trans "Save" %}</button>

View File

@ -20,7 +20,7 @@
</div>
</div>
<div aria-labelledby="tab-permissions" hidden id="panel-permissions" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-permissions" id="panel-permissions" role="tabpanel" tabindex="0">
<div class="section">
<ul>
{% for label, value in object.get_permissions_inspect_fields %}
@ -33,7 +33,7 @@
</div>
</div>
<div aria-labelledby="tab-exceptions" hidden id="panel-exceptions" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-exceptions" id="panel-exceptions" role="tabpanel" tabindex="0">
<div class="section">
{% for exception in object.timeperiodexception_set.all %}
<h4>{{ exception }}</h4>

View File

@ -39,7 +39,7 @@
</div>
{% if virtual_members %}
<div aria-labelledby="tab-meeting-types" hidden="" id="panel-meeting-types" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-meeting-types" "" id="panel-meeting-types" role="tabpanel" tabindex="0">
{% if meeting_types %}
<ul class="objects-list single-links">
{% for meeting_type in meeting_types %}
@ -64,7 +64,7 @@
</div>
{% endif %}
<div aria-labelledby="tab-time-periods" hidden="" id="panel-time-periods" role="tabpanel" tabindex="0">
<div aria-labelledby="tab-time-periods" "" id="panel-time-periods" role="tabpanel" tabindex="0">
{% if agenda.excluded_timeperiods.count %}
<ul class="objects-list single-links">
{% for time_period in agenda.excluded_timeperiods.all %}

View File

@ -202,7 +202,9 @@ class HomepageView(WithApplicationsMixin, ListView):
queryset = self.with_applications_queryset()
if not self.request.user.is_staff:
group_ids = [x.id for x in self.request.user.groups.all()]
queryset = queryset.filter(Q(view_role_id__in=group_ids) | Q(edit_role_id__in=group_ids))
queryset = queryset.filter(
Q(view_role_id__in=group_ids) | Q(admin_role_id__in=group_ids) | Q(edit_role_id__in=group_ids)
)
return queryset.select_related('category').order_by('category__label', 'label')
def has_access_to_unavailability_calendars(self):
@ -1364,11 +1366,11 @@ class ViewableAgendaMixin:
return context
class ManagedAgendaMixin(ViewableAgendaMixin):
class EditableAgendaMixin(ViewableAgendaMixin):
tab_anchor = None
def check_permissions(self, user):
return self.agenda.can_be_managed(user)
return self.agenda.can_be_edited(user)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
@ -1384,6 +1386,11 @@ class ManagedAgendaMixin(ViewableAgendaMixin):
return url
class ManagedAgendaMixin(EditableAgendaMixin):
def check_permissions(self, user):
return self.agenda.can_be_managed(user)
class AgendaEditView(ManagedAgendaMixin, UpdateView):
template_name = 'chrono/manager_agenda_form.html'
title = _('Edit Agenda')
@ -1412,6 +1419,9 @@ class AgendaBookingDelaysView(AgendaEditView):
tab_anchor = 'delays'
comment = _('changed booking delays')
def check_permissions(self, user):
return self.agenda.can_be_edited(user)
agenda_booking_delays = AgendaBookingDelaysView.as_view()
@ -1632,7 +1642,7 @@ class AgendaDateView(DateMixin, ViewableAgendaMixin):
context['booking_colors'] = BookingColor.objects.filter(
bookings__event__in=self.object_list, bookings__cancellation_datetime__isnull=True
).distinct()
context['user_can_manage'] = self.agenda.can_be_managed(self.request.user)
context['user_can_edit'] = self.agenda.can_be_edited(self.request.user)
return context
def get_queryset(self):
@ -2432,13 +2442,16 @@ class AgendaOpenEventsView(ViewableAgendaMixin, DetailView):
agenda_open_events_view = AgendaOpenEventsView.as_view()
class ManagedAgendaSubobjectMixin:
class EditableAgendaSubobjectMixin:
agenda = None
tab_anchor = None
def check_permissions(self, user):
return self.agenda.can_be_edited(user)
def dispatch(self, request, *args, **kwargs):
self.agenda = self.get_object().agenda
if not self.agenda.can_be_managed(request.user):
if not self.check_permissions(request.user):
raise PermissionDenied()
return super().dispatch(request, *args, **kwargs)
@ -2454,6 +2467,11 @@ class ManagedAgendaSubobjectMixin:
return url
class ManagedAgendaSubobjectMixin(EditableAgendaSubobjectMixin):
def check_permissions(self, user):
return self.agenda.can_be_managed(user)
class ManagedDeskMixin:
desk = None
tab_anchor = None
@ -2463,7 +2481,7 @@ class ManagedDeskMixin:
self.desk = Desk.objects.get(id=kwargs.get('pk'))
except Desk.DoesNotExist:
raise Http404()
if not self.desk.agenda.can_be_managed(request.user):
if not self.desk.agenda.can_be_edited(request.user):
raise PermissionDenied()
return super().dispatch(request, *args, **kwargs)
@ -2497,7 +2515,7 @@ class ManagedTimePeriodMixin:
if self.time_period.desk:
self.agenda = self.time_period.desk.agenda
if not self.agenda.can_be_managed(request.user):
if not self.agenda.can_be_edited(request.user):
raise PermissionDenied()
return super().dispatch(request, *args, **kwargs)
@ -2522,7 +2540,7 @@ class ManagedTimePeriodExceptionMixin:
object_ = self.get_object()
if object_.desk:
self.desk = self.get_object().desk
if not self.desk.agenda.can_be_managed(request.user):
if not self.desk.agenda.can_be_edited(request.user):
raise PermissionDenied()
elif object_.unavailability_calendar:
self.unavailability_calendar = object_.unavailability_calendar
@ -2563,12 +2581,12 @@ class AgendaSettingsRedirectView(RedirectView):
agenda_settings_redirect_view = AgendaSettingsRedirectView.as_view()
class AgendaSettings(ManagedAgendaMixin, DetailView):
class AgendaSettings(EditableAgendaMixin, DetailView):
model = Agenda
def set_agenda(self, **kwargs):
self.agenda = get_object_or_404(
Agenda.objects.select_related('edit_role', 'view_role'),
Agenda.objects.select_related('admin_role', 'edit_role', 'view_role'),
pk=kwargs.get('pk'),
)
@ -2580,6 +2598,7 @@ class AgendaSettings(ManagedAgendaMixin, DetailView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['show_history'] = settings.SNAPSHOTS_ENABLED
context['user_can_manage'] = self.object.can_be_managed(self.request.user)
if self.agenda.accept_meetings():
context['meeting_types'] = self.object.iter_meetingtypes()
if self.agenda.kind == 'virtual':
@ -2665,7 +2684,7 @@ class AgendaDuplicate(FormView):
agenda_duplicate = AgendaDuplicate.as_view()
class AgendaAddEventView(ManagedAgendaMixin, CreateView):
class AgendaAddEventView(EditableAgendaMixin, CreateView):
template_name = 'chrono/manager_event_form.html'
model = Event
form_class = NewEventForm
@ -2679,7 +2698,7 @@ class AgendaAddEventView(ManagedAgendaMixin, CreateView):
agenda_add_event = AgendaAddEventView.as_view()
class AgendaEventDuplicateView(ManagedAgendaMixin, UpdateView):
class AgendaEventDuplicateView(EditableAgendaMixin, UpdateView):
model = Event
queryset = Event.objects.filter(primary_event__isnull=True)
pk_url_kwarg = 'event_pk'
@ -2834,7 +2853,7 @@ class AgendaDeskManagementToggleView(ManagedAgendaMixin, View):
agenda_desk_management_toggle_view = AgendaDeskManagementToggleView.as_view()
class AgendaNotificationsSettingsView(ManagedAgendaMixin, UpdateView):
class AgendaNotificationsSettingsView(EditableAgendaMixin, UpdateView):
template_name = 'chrono/manager_agenda_notifications_form.html'
model = AgendaNotificationsSettings
form_class = AgendaNotificationsForm
@ -2875,11 +2894,14 @@ class AgendaReminderSettingsView(AgendaEditView):
except AgendaReminderSettings.DoesNotExist:
return AgendaReminderSettings.objects.create(agenda=self.agenda)
def check_permissions(self, user):
return self.agenda.can_be_edited(user)
agenda_reminder_settings = AgendaReminderSettingsView.as_view()
class AgendaReminderTestView(ManagedAgendaMixin, FormView):
class AgendaReminderTestView(EditableAgendaMixin, FormView):
template_name = 'chrono/manager_send_reminder_form.html'
form_class = AgendaReminderTestForm
@ -2907,7 +2929,7 @@ class AgendaReminderTestView(ManagedAgendaMixin, FormView):
agenda_reminder_test = AgendaReminderTestView.as_view()
class AgendaReminderPreviewView(ManagedAgendaMixin, TemplateView):
class AgendaReminderPreviewView(EditableAgendaMixin, TemplateView):
template_name = 'chrono/manager_agenda_reminder_preview.html'
def dispatch(self, request, *args, **kwargs):
@ -3163,7 +3185,7 @@ class EventDetailRedirectView(RedirectView):
event_redirect_view = EventDetailRedirectView.as_view()
class EventEditView(ManagedAgendaMixin, UpdateView):
class EventEditView(EditableAgendaMixin, UpdateView):
template_name = 'chrono/manager_event_form.html'
model = Event
form_class = EventForm
@ -3188,7 +3210,7 @@ class EventEditView(ManagedAgendaMixin, UpdateView):
event_edit = EventEditView.as_view()
class EventDeleteView(ManagedAgendaMixin, DeleteView):
class EventDeleteView(EditableAgendaMixin, DeleteView):
template_name = 'chrono/manager_confirm_event_delete.html'
model = Event
pk_url_kwarg = 'event_pk'
@ -3805,7 +3827,7 @@ class AgendaDeskMixin:
return super().get_success_url()
class AgendaAddDesk(AgendaDeskMixin, ManagedAgendaMixin, CreateView):
class AgendaAddDesk(AgendaDeskMixin, EditableAgendaMixin, CreateView):
template_name = 'chrono/manager_desk_form.html'
model = Desk
form_class = NewDeskForm
@ -3824,7 +3846,7 @@ class AgendaAddDesk(AgendaDeskMixin, ManagedAgendaMixin, CreateView):
agenda_add_desk = AgendaAddDesk.as_view()
class DeskEditView(AgendaDeskMixin, ManagedAgendaSubobjectMixin, UpdateView):
class DeskEditView(AgendaDeskMixin, EditableAgendaSubobjectMixin, UpdateView):
template_name = 'chrono/manager_desk_form.html'
model = Desk
form_class = DeskForm
@ -3842,7 +3864,7 @@ class DeskEditView(AgendaDeskMixin, ManagedAgendaSubobjectMixin, UpdateView):
desk_edit = DeskEditView.as_view()
class DeskDeleteView(AgendaDeskMixin, ManagedAgendaSubobjectMixin, DeleteView):
class DeskDeleteView(AgendaDeskMixin, EditableAgendaSubobjectMixin, DeleteView):
template_name = 'chrono/manager_confirm_delete.html'
model = Desk
tab_anchor = 'desks'
@ -4058,7 +4080,7 @@ class TimePeriodExceptionDeleteView(ManagedTimePeriodExceptionMixin, DeleteView)
time_period_exception_delete = TimePeriodExceptionDeleteView.as_view()
class DeskImportTimePeriodExceptionsView(ManagedAgendaSubobjectMixin, UpdateView):
class DeskImportTimePeriodExceptionsView(EditableAgendaSubobjectMixin, UpdateView):
model = Desk
form_class = DeskExceptionsImportForm
template_name = 'chrono/manager_import_exceptions.html'
@ -4238,7 +4260,7 @@ class AgendaInspectView(ManagedAgendaMixin, DetailView):
def set_agenda(self, **kwargs):
self.agenda = get_object_or_404(
Agenda.objects.select_related(
'category', 'events_type', 'edit_role', 'view_role'
'category', 'events_type', 'admin_role', 'edit_role', 'view_role'
).prefetch_related(
'resources',
Prefetch(

View File

@ -22,6 +22,7 @@ pytestmark = pytest.mark.django_db
def test_agendas_api(settings, app):
admin_group = Group.objects.create(name='Admin')
edit_group = Group.objects.create(name='Edit')
view_group = Group.objects.create(name='View')
category_a = Category.objects.create(label='Category A')
@ -32,7 +33,7 @@ def test_agendas_api(settings, app):
label='Foo bar',
category=category_a,
events_type=events_type,
edit_role=edit_group,
admin_role=admin_group,
)
Desk.objects.create(agenda=event_agenda, slug='_exceptions_holder')
event_agenda2 = Agenda.objects.create(label='Foo bar 2', category=category_a, events_type=events_type2)
@ -52,6 +53,7 @@ def test_agendas_api(settings, app):
kind='virtual',
minimal_booking_delay=1,
maximal_booking_delay=56,
admin_role=admin_group,
edit_role=edit_group,
view_role=view_group,
)
@ -68,7 +70,8 @@ def test_agendas_api(settings, app):
'minimal_booking_delay_in_working_days': False,
'maximal_booking_delay': 56,
'minimal_booking_time': '00:00:00',
'edit_role': 'Edit',
'admin_role': 'Admin',
'edit_role': None,
'view_role': None,
'category': 'category-a',
'category_label': 'Category A',
@ -88,6 +91,7 @@ def test_agendas_api(settings, app):
'minimal_booking_delay_in_working_days': False,
'maximal_booking_delay': 56,
'minimal_booking_time': '00:00:00',
'admin_role': None,
'edit_role': None,
'view_role': None,
'category': 'category-a',
@ -108,6 +112,7 @@ def test_agendas_api(settings, app):
'minimal_booking_delay_in_working_days': False,
'maximal_booking_delay': 56,
'minimal_booking_time': '00:00:00',
'admin_role': None,
'edit_role': None,
'view_role': None,
'category': None,
@ -126,6 +131,7 @@ def test_agendas_api(settings, app):
'minimal_booking_delay': 1,
'maximal_booking_delay': 56,
'minimal_booking_time': '00:00:00',
'admin_role': None,
'edit_role': None,
'view_role': 'View',
'category': 'category-b',
@ -149,6 +155,7 @@ def test_agendas_api(settings, app):
'minimal_booking_delay': 1,
'maximal_booking_delay': 56,
'minimal_booking_time': '00:00:00',
'admin_role': None,
'edit_role': None,
'view_role': None,
'category': None,
@ -169,6 +176,7 @@ def test_agendas_api(settings, app):
'minimal_booking_delay': 1,
'maximal_booking_delay': 56,
'minimal_booking_time': '00:00:00',
'admin_role': 'Admin',
'edit_role': 'Edit',
'view_role': 'View',
'category': None,
@ -425,6 +433,7 @@ def test_virtual_agenda_detail(app, virtual_meetings_agenda):
'minimal_booking_delay': None,
'maximal_booking_delay': None,
'minimal_booking_time': '00:00:00',
'admin_role': None,
'edit_role': None,
'view_role': None,
'category': None,
@ -502,7 +511,8 @@ def test_add_agenda(app, user, settings):
'minimal_booking_delay': 'oups',
'minimal_booking_delay_in_working_days': 'oups',
'anonymize_delay': 'oups',
'edit_role': 'oups',
'admin_role': 'oups',
'edit_role': 'bla',
'view_role': 'plop',
'category': 'oups',
'events_type': 'oups',
@ -514,7 +524,8 @@ def test_add_agenda(app, user, settings):
'minimal_booking_delay': ['A valid integer is required.'],
'minimal_booking_delay_in_working_days': ['Must be a valid boolean.'],
'anonymize_delay': ['A valid integer is required.'],
'edit_role': ['unknown role: oups'],
'admin_role': ['unknown role: oups'],
'edit_role': ['unknown role: bla'],
'view_role': ['unknown role: plop'],
'category': ['unknown category: oups'],
'events_type': ['unknown events type: oups'],
@ -567,6 +578,7 @@ def test_add_agenda(app, user, settings):
assert AgendaSnapshot.objects.count() == 1
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
admin_group = Group.objects.create(name='Admin')
edit_group = Group.objects.create(name='Edit')
view_group = Group.objects.create(name='View')
@ -578,6 +590,7 @@ def test_add_agenda(app, user, settings):
'minimal_booking_delay': 1,
'maximal_booking_delay': 3,
'anonymize_delay': 30,
'admin_role': 'Admin',
'edit_role': 'Edit',
'view_role': 'View',
'category': 'category-a',
@ -592,6 +605,7 @@ def test_add_agenda(app, user, settings):
day=10, hour=0, minute=0, second=0, microsecond=0
)
assert agenda.minimal_booking_time == datetime.time(0)
assert agenda.admin_role == admin_group
assert agenda.edit_role == edit_group
assert agenda.view_role == view_group
assert agenda.category == category_a
@ -609,6 +623,7 @@ def test_add_agenda(app, user, settings):
'maximal_booking_delay': 3,
'minimal_booking_time': None,
'anonymize_delay': 30,
'admin_role': 'Admin',
'edit_role': 'Edit',
'view_role': 'View',
'category': 'category-a',
@ -622,6 +637,7 @@ def test_add_agenda(app, user, settings):
assert not resp.json['err']
assert len(resp.json['data']) == 1
agenda = Agenda.objects.get(slug='foo-events')
assert agenda.admin_role == admin_group
assert agenda.edit_role == edit_group
assert agenda.view_role == view_group
assert agenda.min_booking_datetime == localtime(now()).replace(

View File

@ -174,10 +174,13 @@ def test_export_agenda(app, admin_user):
app.authorization = ('Basic', ('admin', 'admin'))
group1 = Group.objects.create(name='group1')
group2 = Group.objects.create(name='group2')
Agenda.objects.create(label='Rdv', slug='rdv', kind='meetings', edit_role=group1, view_role=group2)
group3 = Group.objects.create(name='group3')
Agenda.objects.create(
label='Rdv', slug='rdv', kind='meetings', admin_role=group1, view_role=group2, edit_role=group3
)
resp = app.get('/api/export-import/agendas/rdv/')
assert resp.json['data']['label'] == 'Rdv'
assert resp.json['data']['permissions'] == {'view': 'group2', 'edit': 'group1'}
assert resp.json['data']['permissions'] == {'view': 'group2', 'admin': 'group1', 'edit': 'group3'}
def test_export_minor_components(app, admin_user):
@ -294,7 +297,10 @@ def test_agenda_dependencies_groups(app, admin_user):
app.authorization = ('Basic', ('admin', 'admin'))
group1 = Group.objects.create(name='group1')
group2 = Group.objects.create(name='group2')
Agenda.objects.create(label='Rdv', slug='rdv', kind='meetings', edit_role=group1, view_role=group2)
group3 = Group.objects.create(name='group3')
Agenda.objects.create(
label='Rdv', slug='rdv', kind='meetings', admin_role=group1, view_role=group2, edit_role=group3
)
resp = app.get('/api/export-import/agendas/rdv/dependencies/')
# note: with hobo.agent.common installed, 'groups' will contain group slugs,
# not group id
@ -302,6 +308,7 @@ def test_agenda_dependencies_groups(app, admin_user):
'data': [
{'id': group2.id, 'text': group2.name, 'type': 'roles', 'urls': {}, 'uuid': None},
{'id': group1.id, 'text': group1.name, 'type': 'roles', 'urls': {}, 'uuid': None},
{'id': group3.id, 'text': group3.name, 'type': 'roles', 'urls': {}, 'uuid': None},
],
'err': 0,
}
@ -448,7 +455,7 @@ def create_bundle(app, admin_user, visible=True, version_number='42.0'):
category, _ = Category.objects.get_or_create(slug='foo', label='Foo')
meetings_agenda, _ = Agenda.objects.get_or_create(
slug='rdv', label='Rdv', kind='meetings', edit_role=group, category=category
slug='rdv', label='Rdv', kind='meetings', admin_role=group, category=category
)
resource, _ = Resource.objects.get_or_create(slug='foo', label='Foo')
meetings_agenda.resources.add(resource)

View File

@ -58,15 +58,21 @@ def test_manager_user_access(app, manager_user):
assert app.get('/manage/', status=403)
agenda.view_role = manager_user.groups.all()[0]
agenda.edit_role = None
agenda.admin_role = None
agenda.save()
assert app.get('/manage/', status=200)
agenda.edit_role = None
agenda.admin_role = None
agenda.view_role = manager_user.groups.all()[0]
agenda.save()
assert app.get('/manage/', status=200)
agenda.admin_role = None
agenda.view_role = None
agenda.edit_role = manager_user.groups.all()[0]
agenda.save()
assert app.get('/manage/', status=200)
def test_home_redirect(app):
assert app.get('/', status=302).location.endswith('/manage/')
@ -477,12 +483,12 @@ def test_add_agenda_and_set_role(app, admin_user, manager_user):
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
resp = resp.click('Configure', href='roles')
resp.form['edit_role'] = manager_user.groups.all()[0].pk
resp.form['admin_role'] = manager_user.groups.all()[0].pk
resp = resp.form.submit().follow()
assert 'Edit Role: Managers' in resp.text
assert 'Admin Role: Managers' in resp.text
assert AgendaSnapshot.objects.count() == 2
snapshot = AgendaSnapshot.objects.latest('pk')
assert snapshot.serialization['permissions'] == {'edit': 'Managers', 'view': None}
assert snapshot.serialization['permissions'] == {'admin': 'Managers', 'view': None, 'edit': None}
# still only one desk
assert agenda.desk_set.count() == 1
@ -497,9 +503,9 @@ def test_agenda_set_role_with_partial_booking(settings, app, admin_user):
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
resp = resp.click('Configure', href='roles')
resp.form['edit_role'] = group.pk
resp.form['admin_role'] = group.pk
resp = resp.form.submit().follow()
assert 'Edit Role: testgroup' in resp.text
assert 'Admin Role: testgroup' in resp.text
def test_options_agenda_redirect(app, admin_user):
@ -789,7 +795,7 @@ def test_options_agenda_as_manager(app, manager_user):
agenda.kind = 'events'
agenda.save()
agenda.edit_role = manager_user.groups.all()[0]
agenda.admin_role = manager_user.groups.all()[0]
agenda.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
@ -870,7 +876,7 @@ def test_inspect_agenda_as_manager(app, manager_user):
agenda.save()
app.get('/manage/agendas/%s/inspect/' % agenda.pk, status=403)
agenda.edit_role = manager_user.groups.all()[0]
agenda.admin_role = manager_user.groups.all()[0]
agenda.save()
app.get('/manage/agendas/%s/inspect/' % agenda.pk, status=200)
@ -981,7 +987,7 @@ def test_delete_busy_agenda(app, admin_user):
def test_delete_agenda_as_manager(app, manager_user):
agenda = Agenda(label='Foo bar')
agenda.edit_role = manager_user.groups.all()[0]
agenda.admin_role = manager_user.groups.all()[0]
agenda.save()
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
app = login(app, username='manager', password='manager')
@ -2596,7 +2602,7 @@ def test_agenda_view_edit_event(app, manager_user):
assert 'Options' not in resp.text
assert 'Delete' not in resp.text
agenda.edit_role = manager_user.groups.all()[0]
agenda.admin_role = manager_user.groups.all()[0]
agenda.save()
event_url = resp.request.url
resp = app.get(event_url)
@ -3368,7 +3374,7 @@ def test_duplicate_agenda(app, admin_user):
def test_duplicate_agenda_as_manager(app, manager_user):
agenda = Agenda(label='Foo bar')
agenda.edit_role = manager_user.groups.all()[0]
agenda.admin_role = manager_user.groups.all()[0]
agenda.save()
app = login(app, username='manager', password='manager')
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
@ -3550,29 +3556,29 @@ def test_agenda_notifications(app, admin_user, managers_group):
resp = resp.click('Configure', href='notifications')
resp.form['cancelled_event_emails'] = 'hop@entrouvert.com, top@entrouvert.com'
resp.form['almost_full_event'] = 'edit-role'
resp.form['almost_full_event'] = 'admin-role'
option = resp.form['almost_full_event'].selectedIndex
assert resp.form['almost_full_event'].options[option][2] == 'Edit Role (undefined)'
assert resp.form['almost_full_event'].options[option][2] == 'Admin Role (undefined)'
resp.form['full_event'] = 'view-role'
option = resp.form['full_event'].selectedIndex
assert resp.form['full_event'].options[option][2] == 'View Role (Managers)'
resp = resp.form.submit().follow()
settings = agenda.notifications_settings
assert settings.almost_full_event == 'edit-role'
assert settings.almost_full_event == 'admin-role'
assert settings.full_event == 'view-role'
assert settings.cancelled_event == 'use-email-field'
assert settings.cancelled_event_emails == ['hop@entrouvert.com', 'top@entrouvert.com']
assert 'Cancelled event: hop@entrouvert.com, top@entrouvert.com will be notified' in resp.text
assert 'Almost full event (90%): Edit Role (undefined) will be notified' in resp.text
assert 'Almost full event (90%): Admin Role (undefined) will be notified' in resp.text
assert 'Full event: View Role (Managers) will be notified' in resp.text
agenda.edit_role = Group.objects.create(name='hop')
agenda.admin_role = Group.objects.create(name='hop')
agenda.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
assert 'Almost full event (90%): Edit Role (hop) will be notified' in resp.text
assert 'Almost full event (90%): Admin Role (hop) will be notified' in resp.text
def test_agenda_notifications_no_old_events(app, admin_user, mailoutbox):
@ -3839,9 +3845,11 @@ def test_manager_agenda_roles(app, admin_user, manager_user):
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
resp = resp.click('Configure', href='roles')
resp.form['admin_role'] = manager_user.groups.all()[0].pk
resp.form['edit_role'] = manager_user.groups.all()[0].pk
resp = resp.form.submit().follow()
assert 'Admin Role: Managers' in resp.text
assert 'Edit Role: Managers' in resp.text
@ -4193,3 +4201,59 @@ def test_meeting_agenda_lease_display(app, admin_user, manager_user, api_user):
assert len(resp.pyquery('.booking')) == 1
assert len(resp.pyquery('.booking.lease')) == 1
assert 'Currently being booked...' in resp.pyquery('.booking.lease').text()
@freezegun.freeze_time('2024-05-01')
def test_meeting_agenda_edit_role(app, manager_user, managers_group):
agenda = Agenda.objects.create(
label='New Example', kind='meetings', edit_role=managers_group, desk_simple_management=True
)
meeting_type = MeetingType.objects.create(agenda=agenda, label='Meeting Type', duration=30)
app = login(app, username='manager', password='manager')
resp = app.get('/').follow()
resp = resp.click('New Example').follow().follow()
resp = resp.click('Settings')
assert len(resp.pyquery('#sidebar')) == 0
assert len(resp.pyquery('.extra-actions-menu-opener')) == 0
assert [x.text for x in resp.pyquery('.pk-tabs button')] == [
'Desks',
'Opening hours',
'Booking reminders',
'Booking Delays',
]
resp = resp.click('New Desk')
resp.form['label'] = 'Desk 1'
resp = resp.form.submit().follow()
assert 'Desk 1' in resp.text
resp = resp.click('Add a unique period', index=0)
resp.form['date'] = '2024-05-02'
resp.form['start_time'] = '10:00'
resp.form['end_time'] = '17:00'
resp = resp.form.submit().follow()
assert 'Thursday 02 May 2024 / 10 a.m. → 5 p.m.' in resp.text
resp = resp.click('Add repeating periods')
resp.form.get('weekdays', index=4).checked = True
resp.form['start_time'] = '10:00'
resp.form['end_time'] = '17:00'
resp = resp.form.submit().follow()
assert 'Friday / 10 a.m. → 5 p.m.' in resp.text
resp = resp.click('Add a time period exception')
resp.form['label'] = 'Exception 1'
resp.form['start_datetime_0'] = '2024-05-02'
resp.form['start_datetime_1'] = '08:00'
resp.form['start_datetime_0'] = '2024-05-02'
resp.form['end_datetime_1'] = '16:00'
resp = resp.form.submit().follow()
assert 'Exception 1' in resp.text
breakpoint()

View File

@ -127,7 +127,7 @@ def test_add_event_as_manager(app, manager_user):
resp = app.get('/manage/agendas/%s/' % agenda.id, status=302)
app.get('/manage/agendas/%s/add-event' % agenda.id, status=403)
agenda.edit_role = manager_user.groups.all()[0]
agenda.admin_role = manager_user.groups.all()[0]
agenda.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)
@ -270,7 +270,7 @@ def test_edit_event_as_manager(app, manager_user):
app = login(app, username='manager', password='manager')
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id), status=403)
agenda.edit_role = manager_user.groups.all()[0]
agenda.admin_role = manager_user.groups.all()[0]
agenda.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.id, status=200)
resp = resp.click('Feb. 15, 2016, 5 p.m.')
@ -722,7 +722,7 @@ def test_delete_recurring_event(app, admin_user, freezer):
def test_delete_event_as_manager(app, manager_user):
agenda = Agenda(label='Foo bar')
agenda.edit_role = manager_user.groups.all()[0]
agenda.admin_role = manager_user.groups.all()[0]
agenda.save()
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=10, agenda=agenda)

View File

@ -38,7 +38,7 @@ def test_options_meetings_agenda_delays(app, admin_user):
def test_options_meetings_agenda_num_queries(app, admin_user, managers_group):
agenda = Agenda.objects.create(
label='Agenda', kind='meetings', edit_role=managers_group, view_role=managers_group
label='Agenda', kind='meetings', admin_role=managers_group, view_role=managers_group
)
resource = Resource.objects.create(label='Resource')
agenda.resources.add(resource)

View File

@ -1010,7 +1010,7 @@ def test_resource_access_permission(app, manager_user):
assert app.get('/manage/resource/%s/' % resource.pk, status=403)
assert app.get('/manage/resource/%s/' % resource2.pk, status=403)
agenda.edit_role = manager_user.groups.all()[0]
agenda.admin_role = manager_user.groups.all()[0]
agenda.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)

View File

@ -117,7 +117,7 @@ def test_agenda_history_as_manager(app, manager_user):
status=403,
)
agenda.edit_role = manager_user.groups.all()[0]
agenda.admin_role = manager_user.groups.all()[0]
agenda.save()
app.get('/manage/agendas/%s/history/' % agenda.pk, status=200)
app.get(

View File

@ -106,7 +106,7 @@ def test_meetings_agenda_add_time_period_as_manager(app, manager_user):
app.get('/manage/timeperiods/%d/edit' % time_period.id, status=403)
app.get('/manage/timeperiods/%d/delete' % time_period.id, status=403)
# grant edit right to manager
agenda.edit_role = manager_user.groups.all()[0]
agenda.admin_role = manager_user.groups.all()[0]
agenda.save()
resp = app.get('/manage/agendas/%s/settings' % agenda.pk)

View File

@ -1244,13 +1244,14 @@ def test_timeperiodexception_creation_from_ics_with_recurrences_atreal():
def test_management_role_deletion():
group = Group(name='Group')
group.save()
agenda = Agenda(label='Test agenda', edit_role=group, view_role=group)
agenda = Agenda(label='Test agenda', admin_role=group, edit_role=group, view_role=group)
agenda.save()
Group.objects.all().delete()
assert Agenda.objects.get(id=agenda.id).view_role is None
assert Agenda.objects.get(id=agenda.id).edit_role is None
assert Agenda.objects.get(id=agenda.id).admin_role is None
def test_virtual_agenda_init():
@ -1948,11 +1949,11 @@ def test_agenda_notifications_role_email(mocked_role, emails_to_members, emails,
expected_recipients.append(user.email)
expected_email_count = 1 if emails else 0
agenda = Agenda.objects.create(label='Foo bar', kind='event', edit_role=group)
agenda = Agenda.objects.create(label='Foo bar', kind='event', admin_role=group)
event = Event.objects.create(agenda=agenda, places=10, start_datetime=now(), label='Hop')
settings = AgendaNotificationsSettings.objects.create(agenda=agenda)
settings.almost_full_event = AgendaNotificationsSettings.EDIT_ROLE
settings.almost_full_event = AgendaNotificationsSettings.ADMIN_ROLE
settings.save()
# book 9/10 places to reach almost full state

View File

@ -450,8 +450,10 @@ def test_import_export_permissions(app):
meetings_agenda = Agenda.objects.create(label='Foo Bar', kind='meetings')
group1 = Group.objects.create(name='gé1')
group2 = Group.objects.create(name='gé2')
group3 = Group.objects.create(name='gé3')
meetings_agenda.view_role = group1
meetings_agenda.edit_role = group2
meetings_agenda.admin_role = group2
meetings_agenda.edit_role = group3
meetings_agenda.save()
output = get_output_of_command('export_site')
assert len(json.loads(output)['agendas']) == 1
@ -467,59 +469,25 @@ def test_import_export_permissions(app):
group1.save()
with pytest.raises(AgendaImportError) as excinfo:
import_site(json.loads(output), overwrite=True)
assert '%s' % excinfo.value == 'Missing roles: "gé2"'
assert 'gé2' in str(excinfo.value) and 'gé3' in str(excinfo.value)
with tempfile.NamedTemporaryFile() as f:
f.write(force_bytes(output))
f.flush()
with pytest.raises(CommandError) as excinfo:
call_command('import_site', f.name)
assert '%s' % excinfo.value == 'Missing roles: "gé2"'
assert 'gé2' in str(excinfo.value) and 'gé3' in str(excinfo.value)
group2 = Group(name='gé2')
group2.save()
group3 = Group(name='gé3')
group3.save()
import_site(json.loads(output), overwrite=True)
agenda = Agenda.objects.get(slug=meetings_agenda.slug)
assert agenda.view_role == group1
assert agenda.edit_role == group2
def test_import_export_permissions_admin_role(app):
meetings_agenda = Agenda.objects.create(label='Foo Bar', kind='meetings')
group1 = Group.objects.create(name='gé1')
group2 = Group.objects.create(name='gé2')
group3 = Group.objects.create(name='gé3')
meetings_agenda.view_role = group1
meetings_agenda.edit_role = group2
meetings_agenda.save()
output = json.loads(get_output_of_command('export_site'))
# simulate newest export format
output['agendas'][0]['permissions'] = {
'admin': 'gé1', # new admin role permissions corresponds to current edit role permissions
'edit': 'gé2', # new edit role has no equivalent and should be ignored
'view': 'gé3', # view role corresponds to current view role
}
import_site(data={}, clean=True)
assert Agenda.objects.count() == 0
Group.objects.all().delete()
group1 = Group.objects.create(name='gé1')
group2 = Group.objects.create(name='gé2')
group3 = Group.objects.create(name='gé3')
import_site(output, overwrite=True)
agenda = Agenda.objects.get(slug=meetings_agenda.slug)
assert agenda.edit_role == group1
assert agenda.view_role == group3
group1.delete()
with pytest.raises(AgendaImportError) as excinfo:
import_site(output, overwrite=True)
assert '%s' % excinfo.value == 'Missing roles: "gé1"'
assert agenda.admin_role == group2
assert agenda.edit_role == group3
def test_import_export_agenda_with_resources(app):
@ -820,7 +788,7 @@ def test_import_export_notification_settings():
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
AgendaNotificationsSettings.objects.create(
agenda=agenda,
almost_full_event=AgendaNotificationsSettings.EDIT_ROLE,
almost_full_event=AgendaNotificationsSettings.ADMIN_ROLE,
full_event=AgendaNotificationsSettings.VIEW_ROLE,
cancelled_event=AgendaNotificationsSettings.EMAIL_FIELD,
cancelled_event_emails=['hop@entrouvert.com', 'top@entrouvert.com'],
@ -835,7 +803,7 @@ def test_import_export_notification_settings():
agenda = Agenda.objects.first()
AgendaNotificationsSettings.objects.get(
agenda=agenda,
almost_full_event=AgendaNotificationsSettings.EDIT_ROLE,
almost_full_event=AgendaNotificationsSettings.ADMIN_ROLE,
full_event=AgendaNotificationsSettings.VIEW_ROLE,
cancelled_event=AgendaNotificationsSettings.EMAIL_FIELD,
cancelled_event_emails=['hop@entrouvert.com', 'top@entrouvert.com'],

View File

@ -441,3 +441,55 @@ def test_migration_booking_check_data():
assert with_check_type.user_check.end_time == datetime.time(14, 0)
assert with_check_type.computed_start_time == datetime.time(12, 30)
assert with_check_type.computed_end_time == datetime.time(14, 30)
def test_migration_notification_settings_role():
app = 'agendas'
migrate_from = [(app, '0172_rename_edit_role_agenda_admin_role')]
migrate_to = [(app, '0173_migrate_notification_settings_role')]
executor = MigrationExecutor(connection)
old_apps = executor.loader.project_state(migrate_from).apps
executor.migrate(migrate_from)
Agenda = old_apps.get_model(app, 'Agenda')
AgendaNotificationsSettings = old_apps.get_model('agendas', 'AgendaNotificationsSettings')
agenda = Agenda.objects.create(slug='foo1')
empty_settings = AgendaNotificationsSettings.objects.create(agenda=agenda)
agenda = Agenda.objects.create(slug='foo2')
view_settings = AgendaNotificationsSettings.objects.create(
agenda=agenda,
almost_full_event='view-role',
full_event='view-role',
cancelled_event='view-role',
)
agenda = Agenda.objects.create(slug='foo3')
edit_settings = AgendaNotificationsSettings.objects.create(
agenda=agenda,
almost_full_event='edit-role',
full_event='edit-role',
cancelled_event='edit-role',
)
executor = MigrationExecutor(connection)
executor.migrate(migrate_to)
executor.loader.build_graph()
apps = executor.loader.project_state(migrate_to).apps
AgendaNotificationsSettings = apps.get_model('agendas', 'AgendaNotificationsSettings')
empty_settings = AgendaNotificationsSettings.objects.get(pk=empty_settings.pk)
assert empty_settings.almost_full_event == ''
assert empty_settings.full_event == ''
assert empty_settings.cancelled_event == ''
view_settings = AgendaNotificationsSettings.objects.get(pk=view_settings.pk)
assert view_settings.almost_full_event == 'view-role'
assert view_settings.full_event == 'view-role'
assert view_settings.cancelled_event == 'view-role'
edit_settings = AgendaNotificationsSettings.objects.get(pk=edit_settings.pk)
assert edit_settings.almost_full_event == 'admin-role'
assert edit_settings.full_event == 'admin-role'
assert edit_settings.cancelled_event == 'admin-role'