agendas: use custom urls in bookings (#56820)
This commit is contained in:
parent
9bf248a095
commit
3b9c1370c9
|
@ -49,6 +49,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from django.utils.translation import ungettext
|
||||
|
||||
from chrono.interval import Interval, IntervalSet
|
||||
from chrono.utils.publik_urls import translate_from_publik_url
|
||||
from chrono.utils.requests_wrapper import requests as requests_wrapper
|
||||
|
||||
AGENDA_KINDS = (
|
||||
|
@ -1898,6 +1899,12 @@ class Booking(models.Model):
|
|||
else:
|
||||
return ugettext('booked')
|
||||
|
||||
def get_form_url(self):
|
||||
return translate_from_publik_url(self.form_url)
|
||||
|
||||
def get_backoffice_url(self):
|
||||
return translate_from_publik_url(self.backoffice_url)
|
||||
|
||||
|
||||
OpeningHour = collections.namedtuple('OpeningHour', ['begin', 'end'])
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ You have a booking for event "{{ event }}", on {{ date }} at {{ time }}.
|
|||
|
||||
{% if booking.form_url %}
|
||||
{% with _("Edit or cancel booking") as button_label %}
|
||||
{% include "emails/button-link.html" with url=booking.form_url label=button_label %}
|
||||
{% include "emails/button-link.html" with url=booking.get_form_url label=button_label %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -20,7 +20,7 @@ You have a booking for event "{{ event }}", on {{ date }} at {{ time }}.
|
|||
{% trans "More information:" %} {{ booking.event.url }}
|
||||
{% endif %}
|
||||
{% if booking.form_url %}
|
||||
{% trans "If in need to cancel it, you can do so here:" %} {{ booking.form_url }}
|
||||
{% trans "If in need to cancel it, you can do so here:" %} {{ booking.get_form_url }}
|
||||
{% endif %}
|
||||
{% endautoescape %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -52,6 +52,7 @@ from chrono.agendas.models import (
|
|||
from chrono.api import serializers
|
||||
from chrono.api.utils import APIError, Response
|
||||
from chrono.interval import IntervalSet
|
||||
from chrono.utils.publik_urls import translate_to_publik_url
|
||||
|
||||
|
||||
def format_response_datetime(dt):
|
||||
|
@ -667,9 +668,9 @@ def make_booking(event, payload, extra_data, primary_booking=None, in_waiting_li
|
|||
user_last_name=payload.get('user_last_name') or payload.get('user_name') or '',
|
||||
user_email=payload.get('user_email', ''),
|
||||
user_phone_number=payload.get('user_phone_number', ''),
|
||||
form_url=payload.get('form_url', ''),
|
||||
backoffice_url=payload.get('backoffice_url', ''),
|
||||
cancel_callback_url=payload.get('cancel_callback_url', ''),
|
||||
form_url=translate_to_publik_url(payload.get('form_url', '')),
|
||||
backoffice_url=translate_to_publik_url(payload.get('backoffice_url', '')),
|
||||
cancel_callback_url=translate_to_publik_url(payload.get('cancel_callback_url', '')),
|
||||
user_display_label=payload.get('user_display_label', ''),
|
||||
extra_data=extra_data,
|
||||
color=color,
|
||||
|
|
|
@ -7,15 +7,15 @@
|
|||
|
||||
{% block content %}
|
||||
<form method="post">
|
||||
{% if object.backoffice_url and not object.cancel_callback_url %}
|
||||
{% if object.get_backoffice_url and not object.cancel_callback_url %}
|
||||
{% if not user.is_staff %}
|
||||
<p>{% trans "This booking has no callback url configured, cancellation must be handled from corresponding form." %}</p>
|
||||
<p><a href="{{ object.backoffice_url }}">{% trans "Open form" %}</a></p>
|
||||
<p><a href="{{ object.get_backoffice_url }}">{% trans "Open form" %}</a></p>
|
||||
{% else %}
|
||||
<div class="warningnotice">
|
||||
<p>{% trans "This booking has no callback url configured, cancellation should be handled from corresponding form in order to garantee a coherent situation." %}</p>
|
||||
</div>
|
||||
<p><a href="{{ object.backoffice_url }}">{% trans "Open form" %}</a></p>
|
||||
<p><a href="{{ object.get_backoffice_url }}">{% trans "Open form" %}</a></p>
|
||||
<p>{% trans "However, since you are an administrator, you can choose to cancel it anyway." %}</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
|
@ -26,7 +26,7 @@
|
|||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if not object.backoffice_url or object.cancel_callback_url or user.is_staff %}
|
||||
{% if not object.get_backoffice_url or object.cancel_callback_url or user.is_staff %}
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="next" value="{% firstof request.POST.next request.GET.next %}">
|
||||
{{ form.as_p }}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<p>{% trans "Cancellation failed for the following bookings:" %}</p>
|
||||
<ul>
|
||||
{% for booking, error in errors.items %}
|
||||
<li><a href="{{ booking.backoffice_url }}">{{ booking.events_display }}</a>: {{ error }}</li>
|
||||
<li><a href="{{ booking.get_backoffice_url }}">{{ booking.events_display }}</a>: {{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<ul class="objects-list single-links">
|
||||
{% for booking in booked %}
|
||||
<li>
|
||||
<a {% if booking.backoffice_url %}href="{{ booking.backoffice_url }}"{% endif %}>{{ booking.events_display }}</a>
|
||||
<a {% if booking.get_backoffice_url %}href="{{ booking.get_backoffice_url }}"{% endif %}>{{ booking.events_display }}</a>
|
||||
{% if not booking.primary_booking %}
|
||||
<a rel="popup" class="delete" href="{% url 'chrono-manager-booking-cancel' pk=agenda.id booking_pk=booking.id %}?next={{ request.path }}">{% trans "Cancel" %}</a>
|
||||
{% else %}
|
||||
|
@ -53,7 +53,7 @@
|
|||
<div>
|
||||
<ul class="objects-list single-links">
|
||||
{% for booking in waiting %}
|
||||
<li><a {% if booking.backoffice_url %}href="{{ booking.backoffice_url }}"{% endif %}>{{ booking.events_display }}</a></li>
|
||||
<li><a {% if booking.get_backoffice_url %}href="{{ booking.get_backoffice_url }}"{% endif %}>{{ booking.events_display }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
<div class="booking{% if booking.color %} booking-color-{{ booking.color.index }}{% endif %}"
|
||||
style="height: {{ booking.css_height }}%; min-height: {{ booking.css_height }}%; top: {{ booking.css_top }}%;"
|
||||
><span class="start-time">{{booking.event.start_datetime|date:"TIME_FORMAT"}}</span>
|
||||
<a {% if booking.backoffice_url %}href="{{booking.backoffice_url}}"{% endif %}>{{ booking.meetings_display }}</a>
|
||||
<a {% if booking.get_backoffice_url %}href="{{booking.get_backoffice_url}}"{% endif %}>{{ booking.meetings_display }}</a>
|
||||
<a rel="popup" class="cancel" href="{% url 'chrono-manager-booking-cancel' pk=booking.event.agenda_id booking_pk=booking.pk %}?next={{ request.path }}">{% trans "Cancel" %}</a>
|
||||
{% if booking.color %}<span class="booking-color-label booking-bg-color-{{ booking.color.index }}">{{ booking.color }}</span>{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
{% for slot in day.infos.booked_slots %}
|
||||
<div class="booking{% if slot.booking.color %} booking-color-{{ slot.booking.color.index }}{% endif %}" style="left:{{ slot.css_left|stringformat:".1f" }}%;height:{{ slot.css_height|stringformat:".1f" }}%;min-height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%;width:{{ slot.css_width|stringformat:".1f" }}%;">
|
||||
<span class="start-time">{{slot.booking.event.start_datetime|date:"TIME_FORMAT"}}</span>
|
||||
<a {% if slot.booking.backoffice_url %}href="{{slot.booking.backoffice_url}}"{% endif %}>{{ slot.booking.meetings_display }}</a>
|
||||
<a {% if slot.booking.get_backoffice_url %}href="{{slot.booking.get_backoffice_url}}"{% endif %}>{{ slot.booking.meetings_display }}</a>
|
||||
<a rel="popup" class="cancel" href="{% url 'chrono-manager-booking-cancel' pk=slot.booking.event.agenda_id booking_pk=slot.booking.id %}?next={{ request.path }}">{% trans "Cancel" %}</a>
|
||||
{% if not single_desk %}<span class="desk">{{ slot.desk }}</span>{% endif %}
|
||||
{% if slot.booking.color %}<span class="booking-color-label booking-bg-color-{{ slot.booking.color.index }}">{{ slot.booking.color }}</span>{% endif %}
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
<div class="booking"
|
||||
style="height: {{ booking.css_height }}%; min-height: {{ booking.css_height }}%; top: {{ booking.css_top }}%;"
|
||||
><span class="start-time">{{ booking.event.start_datetime|date:"TIME_FORMAT" }}</span>
|
||||
<a {% if booking.backoffice_url %}href="{{ booking.backoffice_url }}"{% endif %}>{{ booking.meetings_display }}</a>
|
||||
<a {% if booking.get_backoffice_url %}href="{{ booking.get_backoffice_url }}"{% endif %}>{{ booking.meetings_display }}</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</td>
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
{% for slot in day.infos.booked_slots %}
|
||||
<div class="booking" style="height:{{ slot.css_height|stringformat:".1f" }}%;min-height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%">
|
||||
<span class="start-time">{{ slot.booking.event.start_datetime|date:"TIME_FORMAT" }}</span>
|
||||
<a {% if slot.booking.backoffice_url %}href="{{ slot.booking.backoffice_url }}"{% endif %}>{{ booking.meetings_display }}</a>
|
||||
<a {% if slot.booking.get_backoffice_url %}href="{{ slot.booking.get_backoffice_url }}"{% endif %}>{{ booking.meetings_display }}</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
# chrono - agendas system
|
||||
# Copyright (C) 2021 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import urllib.parse
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def translate_from_publik_url(url):
|
||||
if not url:
|
||||
return ''
|
||||
source_url = urllib.parse.urlparse(url)
|
||||
if source_url.scheme != 'publik':
|
||||
return url
|
||||
known_services = getattr(settings, 'KNOWN_SERVICES', None)
|
||||
if not known_services:
|
||||
return url
|
||||
for data in known_services.values():
|
||||
for slug, service_data in data.items():
|
||||
if slug == source_url.netloc:
|
||||
service_url = urllib.parse.urlparse(service_data['url'])
|
||||
return urllib.parse.urlunparse(
|
||||
(service_url.scheme, service_url.netloc, source_url.path, '', '', None)
|
||||
)
|
||||
return ''
|
||||
|
||||
|
||||
def translate_to_publik_url(url):
|
||||
if not url:
|
||||
return ''
|
||||
known_services = getattr(settings, 'KNOWN_SERVICES', None)
|
||||
if not known_services:
|
||||
return url
|
||||
source_url = urllib.parse.urlparse(url)
|
||||
for data in known_services.values():
|
||||
for slug, service_data in data.items():
|
||||
service_url = urllib.parse.urlparse(service_data['url'])
|
||||
if source_url.netloc == service_url.netloc and source_url.scheme == service_url.scheme:
|
||||
return str(urllib.parse.urlunparse(('publik', slug, source_url.path, '', '', None)))
|
||||
return url
|
|
@ -27,6 +27,7 @@ from requests import Response
|
|||
from requests import Session as RequestsSession
|
||||
from requests.auth import AuthBase
|
||||
|
||||
from .publik_urls import translate_from_publik_url
|
||||
from .signature import sign_url
|
||||
|
||||
|
||||
|
@ -45,6 +46,7 @@ class PublikSignature(AuthBase):
|
|||
|
||||
class Requests(RequestsSession):
|
||||
def request(self, method, url, **kwargs):
|
||||
url = translate_from_publik_url(url)
|
||||
remote_service = kwargs.pop('remote_service', None)
|
||||
cache_duration = kwargs.pop('cache_duration', 15)
|
||||
invalidate_cache = kwargs.pop('invalidate_cache', False)
|
||||
|
|
|
@ -2507,3 +2507,37 @@ def test_api_events_fillslots_multiple_agendas(app, user):
|
|||
resp.json['err_desc']
|
||||
== 'Some events belong to agendas that are not present in querystring: second-agenda'
|
||||
)
|
||||
|
||||
|
||||
def test_url_translation(app, some_data, user):
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
agenda_id = Agenda.objects.filter(label='Foo bar')[0].id
|
||||
assert Booking.objects.count() == 0
|
||||
resp = app.get('/api/agenda/%s/datetimes/' % agenda_id)
|
||||
fillslot_url = resp.json['data'][0]['api']['fillslot_url']
|
||||
|
||||
params = {
|
||||
'backoffice_url': 'https://demarches.example.com/backoffice/foo/bar',
|
||||
'cancel_callback_url': 'https://demarches.example.com/foo/bar/jump',
|
||||
'form_url': 'https://demarches.example.com/foo/bar',
|
||||
}
|
||||
|
||||
# https://demarches.example.com not in KNOWN_SERVICES, no URL translation
|
||||
resp = app.post_json(fillslot_url, params=params)
|
||||
booking = Booking.objects.get(pk=resp.json['booking_id'])
|
||||
assert booking.backoffice_url == 'https://demarches.example.com/backoffice/foo/bar'
|
||||
assert booking.cancel_callback_url == 'https://demarches.example.com/foo/bar/jump'
|
||||
assert booking.form_url == 'https://demarches.example.com/foo/bar'
|
||||
|
||||
# http://example.org/ is in KNOWN_SERVICES, translation happens
|
||||
params = {
|
||||
'backoffice_url': 'http://example.org/backoffice/foo/bar',
|
||||
'cancel_callback_url': 'http://example.org/foo/bar/jump',
|
||||
'form_url': 'http://example.org/foo/bar',
|
||||
}
|
||||
|
||||
resp = app.post_json(fillslot_url, params=params)
|
||||
booking = Booking.objects.get(pk=resp.json['booking_id'])
|
||||
assert booking.backoffice_url == 'publik://default/backoffice/foo/bar'
|
||||
assert booking.cancel_callback_url == 'publik://default/foo/bar/jump'
|
||||
assert booking.form_url == 'publik://default/foo/bar'
|
||||
|
|
|
@ -1195,6 +1195,62 @@ def test_agenda_day_view(app, admin_user, manager_user, api_user):
|
|||
assert resp.pyquery.find('.exception-hours span')[1].text == 'Exception for the afternoon'
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'view',
|
||||
(
|
||||
'/manage/agendas/%(agenda)s/%(year)d/%(month)d/%(day)d/',
|
||||
'/manage/agendas/%(agenda)s/%(year)d/%(month)d/',
|
||||
),
|
||||
)
|
||||
def test_agenda_day_month_view_backoffice_url_translation(
|
||||
app, admin_user, manager_user, api_user, settings, view
|
||||
):
|
||||
agenda = Agenda.objects.create(label='New Example', kind='meetings')
|
||||
desk = Desk.objects.create(agenda=agenda, label='New Desk')
|
||||
desk.save()
|
||||
today = datetime.date.today()
|
||||
meetingtype = MeetingType(agenda=agenda, label='Bar', duration=30)
|
||||
meetingtype.save()
|
||||
timeperiod = TimePeriod.objects.create(
|
||||
desk=desk, weekday=today.weekday(), start_time=datetime.time(10, 0), end_time=datetime.time(18, 30)
|
||||
)
|
||||
timeperiod.save()
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
login(app)
|
||||
|
||||
resp = app.get('/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meetingtype.slug))
|
||||
booking_url = resp.json['data'][0]['api']['fillslot_url']
|
||||
|
||||
# unkown service, backoffice url stored and displayed as is
|
||||
backoffice_url = 'http://example.net/foo/bar/'
|
||||
resp = app.post(booking_url, params={'backoffice_url': backoffice_url})
|
||||
cancel_url = resp.json['api']['cancel_url']
|
||||
booking_id = resp.json['booking_id']
|
||||
booking = Booking.objects.get(pk=booking_id)
|
||||
assert booking.backoffice_url == backoffice_url
|
||||
date = booking.event.start_datetime
|
||||
url = view % {'agenda': agenda.id, 'year': date.year, 'month': date.month, 'day': date.day}
|
||||
resp = app.get(url)
|
||||
assert resp.text.count('div class="booking') == 1
|
||||
assert backoffice_url in resp.text
|
||||
|
||||
# reset booking
|
||||
resp = app.post(cancel_url)
|
||||
assert resp.json['err'] == 0
|
||||
|
||||
# known service, backoffice url stored translated and displayed as it was passed
|
||||
backoffice_url = 'http://example.org/backoffice/bar/'
|
||||
resp = app.post(booking_url, params={'backoffice_url': backoffice_url})
|
||||
cancel_url = resp.json['api']['cancel_url']
|
||||
booking_id = resp.json['booking_id']
|
||||
booking = Booking.objects.get(pk=booking_id)
|
||||
assert booking.backoffice_url == 'publik://default/backoffice/bar/'
|
||||
date = booking.event.start_datetime
|
||||
resp = app.get(url)
|
||||
assert resp.text.count('div class="booking') == 1
|
||||
assert backoffice_url in resp.text
|
||||
|
||||
|
||||
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
|
||||
def test_agenda_day_view_late_meeting(app, admin_user, kind):
|
||||
today = datetime.date.today()
|
||||
|
@ -2673,6 +2729,38 @@ def test_booking_cancellation_meetings_agenda(app, admin_user, manager_user, man
|
|||
app.get('/manage/agendas/%s/bookings/%s/cancel' % (agenda.pk, booking4.pk), status=404)
|
||||
|
||||
|
||||
def test_booking_cancellation_meetings_agenda_backoffice_url_translation(
|
||||
app, admin_user, manager_user, managers_group, api_user
|
||||
):
|
||||
agenda = Agenda.objects.create(label='Passeports', kind='meetings', view_role=managers_group)
|
||||
desk = Desk.objects.create(agenda=agenda, label='Desk A')
|
||||
meetingtype = MeetingType(agenda=agenda, label='passeport', duration=20)
|
||||
meetingtype.save()
|
||||
today = datetime.date(2018, 11, 10) # fixed day
|
||||
timeperiod_weekday = today.weekday()
|
||||
timeperiod = TimePeriod(
|
||||
desk=desk, weekday=timeperiod_weekday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
|
||||
)
|
||||
timeperiod.save()
|
||||
|
||||
# book a slot
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
bookings_resp = app.get('/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meetingtype.slug))
|
||||
booking_url = bookings_resp.json['data'][0]['api']['fillslot_url']
|
||||
booking_json = app.post_json(booking_url, params={'backoffice_url': 'http://example.org/'}).json
|
||||
|
||||
booking = Booking.objects.get(pk=booking_json['booking_id'])
|
||||
assert booking.backoffice_url == 'publik://default/'
|
||||
date = booking.event.start_datetime
|
||||
month_view_url = '/manage/agendas/%s/%d/%d/' % (agenda.id, date.year, date.month)
|
||||
|
||||
app.reset()
|
||||
login(app, username='admin', password='admin')
|
||||
resp = app.get(month_view_url)
|
||||
resp = resp.click('Cancel')
|
||||
assert 'http://example.org/' in resp.text
|
||||
|
||||
|
||||
def test_agenda_notifications(app, admin_user, managers_group):
|
||||
agenda = Agenda.objects.create(label='Events', kind='events')
|
||||
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
||||
|
|
|
@ -413,6 +413,18 @@ def test_event_classes(app, admin_user):
|
|||
assert 'overbooking' in resp.text
|
||||
|
||||
|
||||
def test_event_detail_backoffice_url_translation(app, admin_user):
|
||||
agenda = Agenda(label='Foo bar')
|
||||
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)
|
||||
event.save()
|
||||
Booking.objects.create(event=event, backoffice_url='publik://default/foo/')
|
||||
app = login(app)
|
||||
resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk))
|
||||
assert 'http://example.org/foo/' in resp.text
|
||||
|
||||
|
||||
def test_delete_event(app, admin_user):
|
||||
agenda = Agenda(label='Foo bar')
|
||||
agenda.save()
|
||||
|
@ -988,6 +1000,52 @@ def test_event_cancellation_error_report(app, admin_user):
|
|||
assert Booking.objects.filter(cancellation_datetime__isnull=False).count() == 5
|
||||
|
||||
|
||||
def test_event_cancellation_error_report_backofice_url_translation(app, admin_user):
|
||||
agenda = Agenda.objects.create(label='Events', kind='events')
|
||||
event = Event.objects.create(
|
||||
label='xyz', start_datetime=now() + datetime.timedelta(days=1), places=10, agenda=agenda
|
||||
)
|
||||
day = event.start_datetime
|
||||
|
||||
def mocked_requests_connection_error(*args, **kwargs):
|
||||
raise requests.exceptions.ConnectionError('unreachable')
|
||||
|
||||
for _ in range(5):
|
||||
Booking.objects.create(
|
||||
event=event,
|
||||
cancel_callback_url='http://example.org/jump/trigger/',
|
||||
backoffice_url='publik://default/',
|
||||
)
|
||||
|
||||
login(app)
|
||||
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
|
||||
resp = resp.click('Cancellation error reports')
|
||||
assert 'No error report' in resp.text
|
||||
|
||||
resp = app.get('/manage/agendas/%d/events/%d/cancel' % (agenda.pk, event.pk))
|
||||
resp = resp.form.submit().follow()
|
||||
assert 'Cancellation in progress' in resp.text
|
||||
|
||||
with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send:
|
||||
mock_response = mock.Mock(status_code=200)
|
||||
mock_send.return_value = mock_response
|
||||
mock_send.side_effect = mocked_requests_connection_error
|
||||
call_command('cancel_events')
|
||||
|
||||
event.refresh_from_db()
|
||||
assert not event.cancelled and not event.cancellation_scheduled
|
||||
assert not Booking.objects.filter(cancellation_datetime__isnull=False).exists()
|
||||
|
||||
resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
|
||||
assert 'Errors occured during cancellation of event "xyz".' in resp.text
|
||||
|
||||
resp = resp.click('Cancellation error reports')
|
||||
assert '(5 failures)' in resp.text
|
||||
|
||||
resp = resp.click(str(event))
|
||||
assert 'http://example.org/' in resp.text
|
||||
|
||||
|
||||
def test_event_cancellation_forbidden(app, admin_user):
|
||||
agenda = Agenda.objects.create(label='Events', kind='events')
|
||||
event = Event.objects.create(
|
||||
|
|
|
@ -560,3 +560,41 @@ def test_meetings_agenda_resources(app, admin_user):
|
|||
resp = resp.follow()
|
||||
assert '/manage/resource/%s/' % resource.pk not in resp.text
|
||||
assert '/manage/agendas/%s/resource/%s/delete/' % (agenda.pk, resource.pk) not in resp.text
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'view',
|
||||
(
|
||||
'/manage/resource/%(resource)s/%(year)d/%(month)d/%(day)d/',
|
||||
'/manage/resource/%(resource)s/%(year)d/%(month)d/',
|
||||
),
|
||||
)
|
||||
def test_agenda_day_month_view_backoffice_url_translation(
|
||||
app, admin_user, manager_user, api_user, settings, view
|
||||
):
|
||||
today = datetime.date.today()
|
||||
resource = Resource.objects.create(label='Foo bar')
|
||||
agenda = Agenda.objects.create(label='Agenda', kind='meetings')
|
||||
desk = Desk.objects.create(agenda=agenda, label='Desk')
|
||||
meetingtype = MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
|
||||
TimePeriod.objects.create(
|
||||
desk=desk, weekday=today.weekday(), start_time=datetime.time(10, 0), end_time=datetime.time(18, 30)
|
||||
)
|
||||
|
||||
login(app)
|
||||
url = view % {'resource': resource.id, 'year': today.year, 'month': today.month, 'day': today.day}
|
||||
|
||||
# book some slots
|
||||
for hour, minute in [(10, 30), (14, 0)]:
|
||||
event = Event.objects.create(
|
||||
agenda=agenda,
|
||||
places=1,
|
||||
desk=desk,
|
||||
meeting_type=meetingtype,
|
||||
start_datetime=now().replace(hour=hour, minute=minute),
|
||||
)
|
||||
event.resources.add(resource)
|
||||
Booking.objects.create(event=event, backoffice_url='publik://default/foo/')
|
||||
|
||||
resp = app.get(url)
|
||||
assert 'http://example.org/foo/' in resp.text
|
||||
|
|
|
@ -1784,6 +1784,19 @@ def test_agenda_reminders_email_content(mailoutbox, freezer):
|
|||
assert 'Edit or cancel booking' in mail.alternatives[0][0]
|
||||
assert 'href="https://example.org/"' in mail.alternatives[0][0]
|
||||
|
||||
# check url translation
|
||||
Booking.objects.all().delete()
|
||||
mailoutbox.clear()
|
||||
freezer.move_to('2020-01-01 14:00')
|
||||
Booking.objects.create(event=event, user_email='t@test.org', form_url='publik://default/someform/1/')
|
||||
freezer.move_to('2020-01-02 15:00')
|
||||
call_command('send_booking_reminders')
|
||||
|
||||
mail = mailoutbox[0]
|
||||
assert 'If in need to cancel it, you can do so here: http://example.org/someform/1/' in mail.body
|
||||
assert 'Edit or cancel booking' in mail.alternatives[0][0]
|
||||
assert 'href="http://example.org/someform/1/"' in mail.alternatives[0][0]
|
||||
|
||||
|
||||
@override_settings(SMS_URL='https://passerelle.test.org/sms/send/', SMS_SENDER='EO', TIME_ZONE='UTC')
|
||||
def test_agenda_reminders_sms_content(freezer):
|
||||
|
|
Loading…
Reference in New Issue