Skip to content

Commit

Permalink
Enhance Accounts Feature and Enable Ticket Acquisition for Registered…
Browse files Browse the repository at this point in the history
… Users (fossasia#405)

* Implement feature allow organizer can buy tickets and view their orders
* Safely encode the locale_code
* simplify build_locale_url

---------

Co-authored-by: lcduong <[email protected]>
Co-authored-by: odkhang <odkhang>
Co-authored-by: lcduong <lcduong>
Co-authored-by: Mario Behling <[email protected]>
  • Loading branch information
3 people authored Nov 4, 2024
1 parent ce24ea2 commit 5df752e
Show file tree
Hide file tree
Showing 21 changed files with 335 additions and 36 deletions.
26 changes: 26 additions & 0 deletions src/pretix/base/templatetags/login_url.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from django import template
from django.urls import reverse
from django.utils.http import urlencode

register = template.Library()


@register.simple_tag(takes_context=True)
def build_login_url(context):
request = context['request']
next_path = request.path
query_string = request.META.get("QUERY_STRING", "")

# Construct the base login URL
login_url = reverse("control:auth.login")

# Encode the next parameter
if query_string:
next_param = f"{next_path}?{query_string}"
else:
next_param = next_path

# Full login URL with the encoded next parameter
full_url = f"{login_url}?{urlencode({'next': next_param})}"

return full_url
1 change: 1 addition & 0 deletions src/pretix/control/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ def _default_context(request):
ctx['js_time_format'] = get_javascript_format('TIME_INPUT_FORMATS')
ctx['js_locale'] = get_moment_locale()
ctx['select2locale'] = get_language()[:2]
ctx['base_path'] = settings.BASE_PATH

ctx['warning_update_available'] = False
ctx['warning_update_check_active'] = False
Expand Down
22 changes: 22 additions & 0 deletions src/pretix/control/forms/organizer_forms/user_orders_form.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from django import forms

from pretix.base.models import Event


class UserOrderFilterForm(forms.Form):
event = forms.ModelChoiceField(
queryset=None,
required=False,
label="Event",
widget=forms.Select(attrs={'class': 'form-control'}),
empty_label="Select an Event"
)

def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None) # Get the user from the kwargs
super().__init__(*args, **kwargs)

if user:
# Query distinct events based on the user's orders
events = Event.objects.filter(orders__email__iexact=user.email).distinct()
self.fields['event'].queryset = events
6 changes: 6 additions & 0 deletions src/pretix/control/navigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,12 @@ def get_global_navigation(request):
'active': (url.url_name == 'index'),
'icon': 'dashboard',
},
{
'label': _('My Orders'),
'url': reverse('control:user.settings.orders'),
'active': 'user.settings.orders' in url.url_name,
'icon': 'shopping-cart',
},
{
'label': _('Events'),
'url': reverse('control:events'),
Expand Down
3 changes: 2 additions & 1 deletion src/pretix/control/templates/pretixcontrol/auth/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
{% load i18n %}
{% load static %}
{% load urlreplace %}
{% load append_next_url %}
{% block content %}
<form class="form-signin" action="" method="post">
{% if backends|length > 1 %}
Expand Down Expand Up @@ -39,7 +40,7 @@
</a>
{% endif %}
{% if can_register %}
<a href="{% url "control:auth.register" %}" class="btn btn-link btn-block">
<a href='{% url "control:auth.register" %}{% append_next request.GET.next %}' class="btn btn-link btn-block">
{% trans "Register" %}
</a>
{% endif %}
Expand Down
12 changes: 5 additions & 7 deletions src/pretix/control/templates/pretixcontrol/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
<script type="text/javascript" src="{% static "fileupload/jquery.fileupload.js" %}"></script>
<script type="text/javascript" src="{% static "lightbox/js/lightbox.min.js" %}"></script>
<script type="text/javascript" src="{% static "are-you-sure/jquery.are-you-sure.js" %}"></script>
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/popover.js" %}"></script>
{% endcompress %}
{{ html_head|safe }}

Expand Down Expand Up @@ -206,15 +207,12 @@
</a>
</li>
{% endif %}
{{ 'popover-profile'|json_script:"popover_toggle" }}
{{ base_path|json_script:"base_path" }}
<li>
<a href="{% url 'control:user.settings' %}" title="{% trans "Account Settings" %}" >
<a target="_blank" class="header-nav btn btn-outline-success" data-toggle="popover-profile">
<i class="fa fa-user"></i> {{ request.user.get_full_name }}
</a>
</li>
<li>
<a href="{% url 'control:auth.logout' %}" title="{% trans "Log out" %}">
<i class="fa fa-sign-out"></i>
<span class="visible-xs-inline">{% trans "Log out" %}</span>
<i class="fa fa-solid fa-caret-down"></i>
</a>
</li>
</ul>
Expand Down
77 changes: 77 additions & 0 deletions src/pretix/control/templates/pretixcontrol/user/orders.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{% extends "pretixcontrol/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% load eventurl %}
{% load money %}
{% block title %}{% trans "My Orders" %}{% endblock %}
{% block content %}
<h1>{% trans "My Orders" %}</h1>
<div class="row filter-form">
<form action="" method="get">
<div class="col-md-3 col-xs-6">
<!-- Add the event filter here -->
{% bootstrap_field filter_form.event layout='inline' %}
</div>
<div class="col-md-2 col-xs-6">
<button type="submit" class="btn btn-primary">
<span class="fa fa-filter"></span>
</button>
</div>
</form>
</div>
<div class="table-responsive">
<table class="table table-condensed table-hover table-quotas">
<thead>
<tr>
<th>{% trans "Order code" %}</th>
<th>{% trans "Organizer" %}</th>
<th>{% trans "Event" %}</th>
<th>{% trans "Order date" %}</th>
<th class="text-right">{% trans "Order total" %}</th>
<th class="text-right">{% trans "Status" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for order in orders %}
<tr>
<td>
<strong>
<a href='{% abseventurl order.event "presale:event.order" order=order.code secret=order.secret %}' target="_blank">
{{ order.code }}
</a>
</strong>
</td>
<td>
{{ order.event.organizer }}
</td>
<td>
{{ order.event }}
</td>
<td>
{{ order.datetime|date:"SHORT_DATETIME_FORMAT" }}
</td>
<td class="text-right flip">
{{ order.total|money:order.event.currency }}
</td>
<td class="text-right flip">{% include "pretixpresale/event/fragment_order_status.html" with order=order event=order.event %}</td>
<td class="text-right flip">
<a href='{% abseventurl order.event "presale:event.order" order=order.code secret=order.secret %}'
target="_blank"
class="btn btn-default">
{% trans "Details" %}
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="7" class="text-center">
{% trans "You have not made any orders yet." %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% include "pretixcontrol/pagination.html" %}
{% endblock %}
12 changes: 12 additions & 0 deletions src/pretix/control/templatetags/append_next_url.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from urllib.parse import urlencode

from django import template

register = template.Library()


@register.simple_tag
def append_next(next_url=None):
if next_url and next_url.strip():
return f'?{urlencode({"next": next_url})}'
return ''
1 change: 1 addition & 0 deletions src/pretix/control/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
url(r'^pdf/editor/webfonts.css', pdf.FontsCSSView.as_view(), name='pdf.css'),
url(r'^settings/?$', user.UserSettings.as_view(), name='user.settings'),
url(r'^settings/history/$', user.UserHistoryView.as_view(), name='user.settings.history'),
url(r'^settings/orders/$', user.UserOrdersView.as_view(), name='user.settings.orders'),
url(r'^settings/notifications/$', user.UserNotificationsEditView.as_view(), name='user.settings.notifications'),
url(r'^settings/notifications/off/(?P<id>\d+)/(?P<token>[^/]+)/$', user.UserNotificationsDisableView.as_view(),
name='user.settings.notifications.off'),
Expand Down
2 changes: 1 addition & 1 deletion src/pretix/control/views/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def register(request):
request.session['pretix_auth_long_session'] = (
settings.PRETIX_LONG_SESSIONS and form.cleaned_data.get('keep_logged_in', False)
)
response = redirect('control:index')
response = redirect(request.GET.get("next", 'control:index'))
set_cookie_after_logged_in(request, response)
return response
else:
Expand Down
30 changes: 29 additions & 1 deletion src/pretix/control/views/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from django.contrib import messages
from django.contrib.auth import update_session_auth_hash
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.utils.crypto import get_random_string
Expand All @@ -28,10 +29,14 @@
from pretix.base.forms.auth import ReauthForm
from pretix.base.forms.user import User2FADeviceAddForm, UserSettingsForm
from pretix.base.models import (
Event, LogEntry, NotificationSetting, U2FDevice, User, WebAuthnDevice,
Event, LogEntry, NotificationSetting, Order, U2FDevice, User,
WebAuthnDevice,
)
from pretix.base.models.auth import StaffSession
from pretix.base.notifications import get_all_notification_types
from pretix.control.forms.organizer_forms.user_orders_form import (
UserOrderFilterForm,
)
from pretix.control.forms.users import StaffSessionForm
from pretix.control.permissions import (
AdministratorPermissionRequiredMixin, StaffMemberRequiredMixin,
Expand Down Expand Up @@ -757,3 +762,26 @@ def get_object(self, queryset=None):
return get_object_or_404(StaffSession, pk=self.kwargs['id'])
else:
return get_object_or_404(StaffSession, pk=self.kwargs['id'], user=self.request.user)


class UserOrdersView(ListView):
template_name = 'pretixcontrol/user/orders.html'
context_object_name = 'orders'
paginate_by = 20

def get_queryset(self):
qs = Order.objects.filter(
Q(email__iexact=self.request.user.email)
).select_related('event').order_by('-datetime')

# Filter by event if provided
event_id = self.request.GET.get('event')
if event_id:
qs = qs.filter(event_id=event_id)

return qs

def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['filter_form'] = UserOrderFilterForm(self.request.GET or None, user=self.request.user)
return ctx
1 change: 1 addition & 0 deletions src/pretix/presale/checkoutflowstep/questions_step.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def contact_form(self):
wd = self.cart_session.get('widget_data', {})
initial = {
'email': (
(self.request.user.email if self.request.user.is_authenticated else '') or
self.cart_session.get('email', '') or
wd.get('email', '')
),
Expand Down
5 changes: 5 additions & 0 deletions src/pretix/presale/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,9 @@ def _default_context(request):
ctx["settings"] = pretix_settings
ctx["django_settings"] = settings

# Check to show organizer area
ctx['show_organizer_area'] = False
if request.user and request.user.is_authenticated and hasattr(request, 'organizer') and request.organizer and hasattr(request, 'event') and request.event:
ctx['show_organizer_area'] = request.user.has_event_permission(request.organizer, request.event, 'can_change_event_settings', request=request)

return ctx
16 changes: 8 additions & 8 deletions src/pretix/presale/templates/pretixpresale/event/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,18 @@
</div>
{% endif %}
<div class="container page-header-links {% if event.settings.theme_color_background|upper != "#FFFFFF" or event.settings.logo_image_large %}page-header-links-outside{% endif %}">
{% if event.settings.locales|length > 1 %}
{% if event.settings.theme_color_background|upper != "#FFFFFF" or event.settings.logo_image_large %}
<div class="pull-right header-part flip hidden-print header-content">
<nav class="locales" aria-label="{% trans "select language" %}">
{% if event.settings.theme_color_background|upper != "#FFFFFF" or event.settings.logo_image_large %}
<div class="pull-right header-part flip hidden-print header-content">
<nav class="locales" aria-label="{% trans "select language" %}">
{% if event.settings.locales|length > 1 %}
{% for l in languages %}
<a href="{% url "presale:locale.set" %}?locale={{ l.code }}&next={{ request.path }}{% if request.META.QUERY_STRING %}%3F{{ request.META.QUERY_STRING|urlencode }}{% endif %}" class="{% if l.code == request.LANGUAGE_CODE %}active{% endif %}" rel="nofollow" lang="{{ l.code }}" hreflang="{{ l.code }}">
{{ l.name_local }}</a>
{% endfor %}
</nav>
</div>
{% endif %}
{% include "pretixpresale/fragment_login_status.html" %}
{% endif %}
</nav>
{% include "pretixpresale/fragment_login_status.html" %}
</div>
{% endif %}
{% if request.event.settings.organizer_link_back %}
<div class="pull-left header-part flip hidden-print">
Expand Down
17 changes: 9 additions & 8 deletions src/pretix/presale/templates/pretixpresale/event/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,15 @@ <h3 class="panel-title">
</form>
</div>
<div class="col-md-4 col-sm-6 col-md-offset-4 col-xs-12">
<a class="btn btn-block btn-primary btn-lg"
href="{% eventurl request.event "presale:event.checkout.start" cart_namespace=cart_namespace %}">
{% if has_addon_choices or cart.total == 0 %}
<i class="fa fa-shopping-cart" aria-hidden="true"></i> {% trans "Continue" %}
{% else %}
<i class="fa fa-shopping-cart" aria-hidden="true"></i> {% trans "Proceed with checkout" %}
{% endif %}
</a>
{% if guest_checkout_allowed or request.user.is_authenticated %}
<a class="btn btn-block btn-primary btn-lg" href="{% eventurl request.event 'presale:event.checkout.start' cart_namespace=cart_namespace %}">
Checkout
</a>
{% else %}
<a class="btn btn-block btn-primary btn-lg disabled" href="#">
Checkout
</a>
{% endif %}
</div>
<div class="visible-xs-block col-xs-12">
<form method="post" data-asynctask action="{% eventurl request.event "presale:event.cart.clear" cart_namespace=cart_namespace %}">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
{% load i18n %}
{% load eventurl %}
{% load login_url %}

{% if request.organizer.settings.customer_accounts %}
{{ request.event.organizer.slug|json_script:"organizer_name" }}
{{ request.event.slug|json_script:"event_slug" }}
{{ show_organizer_area|json_script:"show_organizer_area" }}
{{ 'popover-profile'|json_script:"popover_toggle" }}
{{ base_path|json_script:"base_path" }}
<nav class="login-hdr" aria-label='{% trans "account" %}'>
{% if request.user.is_authenticated %}
<div class="navigation-button">
<a target="_blank" class="header-nav btn btn-outline-success" data-toggle="popover-profile">
<i class="fa fa-user"></i> {{ request.user.fullname|default:request.user.email }}
<i class="fa fa-solid fa-caret-down"></i>
</a>
</div>
{% else %}
<a href="{% build_login_url %}">
{% trans "Log in" %}
</a>

{% endif %}
</nav>
{% endif %}
4 changes: 4 additions & 0 deletions src/pretix/presale/views/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import jwt
import pytz
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.db.models import (
Count, Exists, IntegerField, OuterRef, Prefetch, Q, Value,
Expand Down Expand Up @@ -468,6 +469,9 @@ def get_context_data(self, **kwargs):

context['guest_checkout_allowed'] = not self.request.event.settings.require_registered_account_for_tickets

if not context['guest_checkout_allowed'] and not self.request.user.is_authenticated:
messages.error(self.request, _("This event only available for registered users. Please login to continue."))

return context

def _subevent_list_context(self):
Expand Down
Loading

0 comments on commit 5df752e

Please sign in to comment.