forked from CGM_Public/pretix_original
Compare commits
2 Commits
list-sorti
...
v2024.6.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e57ab582e | ||
|
|
d9d638a232 |
@@ -19,4 +19,4 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
__version__ = "2024.7.0.dev0"
|
||||
__version__ = "2024.6.1"
|
||||
|
||||
@@ -37,7 +37,6 @@ from decimal import Decimal
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.db.models import (
|
||||
Case, CharField, Count, DateTimeField, F, IntegerField, Max, Min, OuterRef,
|
||||
Q, Subquery, Sum, When,
|
||||
@@ -55,7 +54,7 @@ from openpyxl.comments import Comment
|
||||
from openpyxl.styles import Font, PatternFill
|
||||
|
||||
from pretix.base.models import (
|
||||
Checkin, GiftCard, GiftCardTransaction, Invoice, InvoiceAddress, Order,
|
||||
GiftCard, GiftCardTransaction, Invoice, InvoiceAddress, Order,
|
||||
OrderPosition, Question,
|
||||
)
|
||||
from pretix.base.models.orders import (
|
||||
@@ -542,22 +541,6 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
).order_by()
|
||||
qs = base_qs.annotate(
|
||||
payment_providers=Subquery(p_providers, output_field=CharField()),
|
||||
checked_in_lists=Subquery(
|
||||
Checkin.objects.filter(
|
||||
successful=True,
|
||||
type=Checkin.TYPE_ENTRY,
|
||||
position=OuterRef("pk"),
|
||||
).order_by().values("position").annotate(
|
||||
c=GroupConcat(
|
||||
"list__name",
|
||||
# These appear not to work properly on SQLite. Well, we don't support SQLite outside testing
|
||||
# anyways.
|
||||
ordered='sqlite' not in settings.DATABASES['default']['ENGINE'],
|
||||
distinct='sqlite' not in settings.DATABASES['default']['ENGINE'],
|
||||
delimiter=", "
|
||||
)
|
||||
).values("c")
|
||||
),
|
||||
).select_related(
|
||||
'order', 'order__invoice_address', 'order__customer', 'item', 'variation',
|
||||
'voucher', 'tax_rule'
|
||||
@@ -655,7 +638,6 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
_('Sales channel'), _('Order locale'),
|
||||
_('E-mail address verified'),
|
||||
_('External customer ID'),
|
||||
_('Check-in lists'),
|
||||
_('Payment providers'),
|
||||
]
|
||||
|
||||
@@ -794,7 +776,6 @@ class OrderListExporter(MultiSheetListExporter):
|
||||
_('Yes') if order.email_known_to_work else _('No'),
|
||||
str(order.customer.external_identifier) if order.customer and order.customer.external_identifier else '',
|
||||
]
|
||||
row.append(op.checked_in_lists or "")
|
||||
row.append(', '.join([
|
||||
str(self.providers.get(p, p)) for p in sorted(set((op.payment_providers or '').split(',')))
|
||||
if p and p != 'free'
|
||||
|
||||
@@ -38,6 +38,7 @@ from datetime import datetime
|
||||
from django import forms
|
||||
from django.utils.formats import get_format
|
||||
from django.utils.functional import lazy
|
||||
from django.utils.html import escape
|
||||
from django.utils.timezone import get_current_timezone, now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
@@ -64,7 +65,7 @@ def format_placeholders_help_text(placeholders, event=None):
|
||||
placeholders = [(k, v.render_sample(event) if event else v) for k, v in placeholders.items()]
|
||||
placeholders.sort(key=lambda x: x[0])
|
||||
phs = [
|
||||
'<button type="button" class="content-placeholder" title="%s">{%s}</button>' % (_("Sample: %s") % v if v else "", k)
|
||||
'<button type="button" class="content-placeholder" title="%s">{%s}</button>' % (escape(_("Sample: %s") % v) if v else "", escape(k))
|
||||
for k, v in placeholders
|
||||
]
|
||||
return _('Available placeholders: {list}').format(
|
||||
|
||||
@@ -122,16 +122,6 @@ class ItemCategory(LoggedModel):
|
||||
return _('{category} (Add-On products)').format(category=str(name))
|
||||
return str(name)
|
||||
|
||||
def get_category_type_display(self):
|
||||
if self.is_addon:
|
||||
return _('Add-On products')
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def category_type(self):
|
||||
return 'addon' if self.is_addon else 'normal'
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
super().delete(*args, **kwargs)
|
||||
if self.event:
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<tr>
|
||||
<th>{% trans "Product categories" %}</th>
|
||||
<th class="action-col-2"></th>
|
||||
<th class="action-col-2"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-dnd-url="{% url "control:event.items.categories.reorder" organizer=request.event.organizer.slug event=request.event.slug %}">
|
||||
@@ -40,16 +41,18 @@
|
||||
<td>
|
||||
<strong><a href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}">{{ c.internal_name|default:c.name }}</a></strong>
|
||||
</td>
|
||||
<td>
|
||||
<button formaction="{% url "control:event.items.categories.up" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 and not page_obj.has_previous %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button formaction="{% url "control:event.items.categories.down" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 and not page_obj.has_next %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container"></span>
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<button title="{% trans "Move up" %}" formaction="{% url "control:event.items.categories.up" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 and not page_obj.has_previous %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button title="{% trans "Move down" %}" formaction="{% url "control:event.items.categories.down" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 and not page_obj.has_next %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container" title="{% trans "Click and drag this button to reorder. Double click to show buttons for reordering." %}"></span>
|
||||
<a title="{% trans "Edit" %}" href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.categories.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ c.id }}"
|
||||
class="btn btn-sm btn-default" title="{% trans "Clone" %}" data-toggle="tooltip">
|
||||
<span class="fa fa-copy"></span>
|
||||
</a>
|
||||
<a title="{% trans "Delete" %}" href="{% url "control:event.items.categories.delete" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
<a href="{% url "control:event.items.categories.delete" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -56,7 +56,8 @@
|
||||
<th>{% trans "Internal name" %}</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th colspan="2">{% trans "Products" %}</th>
|
||||
<th>{% trans "Products" %}</th>
|
||||
<th class="action-col-2"></th>
|
||||
<th class="action-col-2"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -101,10 +102,9 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td {% if d.benefit_same_products %}colspan="2"{% endif %}>
|
||||
{% if not d.benefit_same_products %}{% trans "Condition:" %}{% endif %}
|
||||
<td>
|
||||
{% if d.condition_all_products %}
|
||||
<ul><li><em>{% trans "All" %}</em></li></ul>
|
||||
<em>{% trans "All" %}</em>
|
||||
{% else %}
|
||||
<ul>
|
||||
{% for item in d.condition_limit_products.all %}
|
||||
@@ -115,28 +115,18 @@
|
||||
</ul>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if not d.benefit_same_products %}
|
||||
<td>
|
||||
{% trans "Applies to:" %}
|
||||
<ul>
|
||||
{% for item in d.benefit_limit_products.all %}
|
||||
<li>
|
||||
<a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.id %}">{{ item }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="text-right flip">
|
||||
<td>
|
||||
<button formaction="{% url "control:event.items.discounts.up" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}"
|
||||
class="btn btn-default btn-sm sortable-up" title="{% trans "Move up" %}"
|
||||
class="btn btn-default btn-sm sortable-up"
|
||||
{% if forloop.counter0 == 0 and not page_obj.has_previous %}
|
||||
disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button formaction="{% url "control:event.items.discounts.down" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}"
|
||||
class="btn btn-default btn-sm sortable-down" title="{% trans "Move down" %}"
|
||||
class="btn btn-default btn-sm sortable-down"
|
||||
{% if forloop.revcounter0 == 0 and not page_obj.has_next %} disabled{% endif %}>
|
||||
<i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container" title="{% trans "Click and drag this button to reorder. Double click to show buttons for reordering." %}"></span>
|
||||
<span class="dnd-container"></span>
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% url "control:event.items.discounts.edit" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.discounts.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ d.id }}"
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
{% extends "pretixcontrol/items/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load money %}
|
||||
{% block title %}{% trans "Products" %}{% endblock %}
|
||||
{% block inside %}
|
||||
{% blocktrans asvar s_taxes %}taxes{% endblocktrans %}
|
||||
<h1>{% trans "Products" %}</h1>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Below, you find a list of all available products. You can click on a product name to inspect and change
|
||||
product details. You can also use the buttons on the right to change the order of products or move
|
||||
products to a different category.
|
||||
product details. You can also use the buttons on the right to change the order of products within a
|
||||
give category.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% if items|length == 0 %}
|
||||
@@ -31,7 +29,7 @@
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed table-hover table-items">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Product name" %}</th>
|
||||
@@ -39,24 +37,16 @@
|
||||
<th class="iconcol"></th>
|
||||
<th class="iconcol"></th>
|
||||
<th class="iconcol"></th>
|
||||
<th class="text-right flip">{% trans "Default price" %}</th>
|
||||
<th>{% trans "Category" %}</th>
|
||||
<th class="action-col-2"><span class="sr-only">Move</span></th>
|
||||
<th class="action-col-2"><span class="sr-only">Edit</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
{% for c, items in cat_list %}
|
||||
{% if c %}
|
||||
<tbody>
|
||||
<tr class="sortable-disabled"><th colspan="9" scope="colgroup" class="text-muted">
|
||||
{{ c.name }}{% if c.category_type != "normal" %} <span class="font-normal">({{ c.get_category_type_display }})</span>{% endif %}
|
||||
<a href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" title="{% trans "Edit" %}"><span class="fa fa-edit fa-fw"></span></a>
|
||||
</th></tr>
|
||||
</tbody>
|
||||
{% endif %}
|
||||
<tbody data-dnd-url="{% url "control:event.items.reorder" organizer=request.event.organizer.slug event=request.event.slug category=c.id|default:0 %}"
|
||||
data-dnd-group="items">
|
||||
{% for i in items %}
|
||||
{% if forloop.counter0 == 0 and i.category %}{% endif %}
|
||||
{% regroup items by category as cat_list %}
|
||||
{% for c in cat_list %}
|
||||
<tbody data-dnd-url="{% url "control:event.items.reorder" organizer=request.event.organizer.slug event=request.event.slug %}">
|
||||
{% for i in c.list %}
|
||||
{% if forloop.counter0 == 0 and i.category %}<tr class="sortable-disabled"><th colspan="8" scope="colgroup" class="text-muted">{{ i.category }}</th></tr>{% endif %}
|
||||
<tr data-dnd-id="{{ i.id }}" {% if not i.active %}class="row-muted"{% endif %}>
|
||||
<td><strong>
|
||||
{% if not i.active %}<strike>{% endif %}
|
||||
@@ -102,15 +92,15 @@
|
||||
</td>
|
||||
<td>
|
||||
{% if i.var_count %}
|
||||
<span class="fa fa-bars fa-fw text-muted" data-toggle="tooltip" title="{% trans "Product with variations" %}"></span>
|
||||
<span class="fa fa-th-large fa-fw text-muted" data-toggle="tooltip" title="{% trans "Product with variations" %}"></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if i.category.is_addon %}
|
||||
<span class="fa fa-plus-square fa-fw text-muted" data-toggle="tooltip"
|
||||
<span class="fa fa-puzzle-piece fa-fw text-muted" data-toggle="tooltip"
|
||||
title="{% trans "Only available as an add-on product" %}"></span>
|
||||
{% elif i.require_bundling %}
|
||||
<span class="fa fa-plus-square fa-fw text-muted" data-toggle="tooltip"
|
||||
<span class="fa fa-puzzle-piece fa-fw text-muted" data-toggle="tooltip"
|
||||
title="{% trans "Only available as part of a bundle" %}"></span>
|
||||
{% elif i.hide_without_voucher %}
|
||||
<span class="fa fa-tags fa-fw text-muted" data-toggle="tooltip"
|
||||
@@ -120,29 +110,16 @@
|
||||
title="{% trans "Can only be bought using a voucher" %}"></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
{% if i.free_price %}
|
||||
<span class="fa fa-edit fa-fw text-muted" data-toggle="tooltip" title="{% trans "Free price input" %}">
|
||||
</span>
|
||||
{% endif %}
|
||||
{{ i.default_price|money:request.event.currency }}
|
||||
{% if i.original_price %}<strike class="text-muted">{{ i.original_price|money:request.event.currency }}</strike>{% endif %}
|
||||
{% if i.tax_rule and i.default_price %}
|
||||
<br/>
|
||||
<small class="text-muted">
|
||||
{% blocktrans trimmed with rate=i.tax_rule.rate|floatformat:-2 taxname=i.tax_rule.name|default:s_taxes %}
|
||||
incl. {{ rate }}% {{ taxname }}
|
||||
{% endblocktrans %}
|
||||
</small>
|
||||
{% endif %}
|
||||
<td>{% if i.category %}{{ i.category.name }}{% endif %}</td>
|
||||
<td>
|
||||
<button formaction="{% url "control:event.items.up" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button formaction="{% url "control:event.items.down" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container"></span>
|
||||
</td>
|
||||
<td class="text-right flip col-actions">
|
||||
<button title="{% trans "Move up" %}" formaction="{% url "control:event.items.up" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button title="{% trans "Move down" %}" formaction="{% url "control:event.items.down" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container" title="{% trans "Click and drag this button to reorder. Double click to show buttons for reordering." %}"></span>
|
||||
<a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm" title="{% trans "Edit" %}"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ i.id }}" class="btn btn-default btn-sm" title="{% trans "Clone" %}" data-toggle="tooltip"><i class="fa fa-copy"></i></a>
|
||||
<a href="{% url "control:event.items.delete" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-danger btn-sm" title="{% trans "Delete" %}"><i class="fa fa-trash"></i></a>
|
||||
<a href="{% url "control:event.items.delete" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -51,9 +51,9 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<button title="{% trans "Move up" %}" formaction="{% url "control:organizer.property.up" organizer=request.organizer.slug property=p.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 and not page_obj.has_previous %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button title="{% trans "Move down" %}" formaction="{% url "control:organizer.property.down" organizer=request.organizer.slug property=p.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 and not page_obj.has_next %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container" title="{% trans "Click and drag this button to reorder. Double click to show buttons for reordering." %}"></span>
|
||||
<button formaction="{% url "control:organizer.property.up" organizer=request.organizer.slug property=p.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 and not page_obj.has_previous %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button formaction="{% url "control:organizer.property.down" organizer=request.organizer.slug property=p.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 and not page_obj.has_next %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container"></span>
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% url "control:organizer.property.edit" organizer=request.organizer.slug property=p.id %}"
|
||||
|
||||
@@ -293,7 +293,7 @@ urlpatterns = [
|
||||
re_path(r'^items/(?P<item>\d+)/$', item.ItemUpdateGeneral.as_view(), name='event.item'),
|
||||
re_path(r'^items/(?P<item>\d+)/up$', item.item_move_up, name='event.items.up'),
|
||||
re_path(r'^items/(?P<item>\d+)/down$', item.item_move_down, name='event.items.down'),
|
||||
re_path(r'^items/reorder/(?P<category>\d+)/$', item.reorder_items, name='event.items.reorder'),
|
||||
re_path(r'^items/reorder$', item.reorder_items, name='event.items.reorder'),
|
||||
re_path(r'^items/(?P<item>\d+)/delete$', item.ItemDelete.as_view(), name='event.items.delete'),
|
||||
re_path(r'^items/typeahead/meta/$', typeahead.item_meta_values, name='event.items.meta.typeahead'),
|
||||
re_path(r'^items/select2$', typeahead.items_select2, name='event.items.select2'),
|
||||
|
||||
@@ -62,6 +62,7 @@ from django.http import (
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import escape
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext, gettext_lazy as _, gettext_noop
|
||||
@@ -736,7 +737,7 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
else:
|
||||
ctx[p.identifier] = '<span class="placeholder" title="{}">{}</span>'.format(
|
||||
_('This value will be replaced based on dynamic parameters.'),
|
||||
s
|
||||
escape(s)
|
||||
)
|
||||
return ctx
|
||||
|
||||
@@ -786,7 +787,7 @@ class MailSettingsRendererPreview(MailSettingsPreview):
|
||||
def placeholders(self, item):
|
||||
ctx = {}
|
||||
for p in get_available_placeholders(self.request.event, MailSettingsForm.base_context[item]).values():
|
||||
ctx[p.identifier] = str(p.render_sample(self.request.event))
|
||||
ctx[p.identifier] = escape(str(p.render_sample(self.request.event)))
|
||||
return ctx
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
|
||||
import json
|
||||
from collections import OrderedDict, namedtuple
|
||||
from itertools import groupby
|
||||
from json.decoder import JSONDecodeError
|
||||
|
||||
from django.contrib import messages
|
||||
@@ -114,8 +113,6 @@ class ItemList(ListView):
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['sales_channels'] = get_all_sales_channels()
|
||||
items_by_category = {cat: list(items) for cat, items in groupby(ctx['items'], lambda item: item.category)}
|
||||
ctx['cat_list'] = [(cat, items_by_category.get(cat, [])) for cat in [None, *self.request.event.categories.all()]]
|
||||
return ctx
|
||||
|
||||
|
||||
@@ -172,7 +169,7 @@ def item_move_down(request, organizer, event, item):
|
||||
@transaction.atomic
|
||||
@event_permission_required("can_change_items")
|
||||
@require_http_methods(["POST"])
|
||||
def reorder_items(request, organizer, event, category):
|
||||
def reorder_items(request, organizer, event):
|
||||
try:
|
||||
ids = json.loads(request.body.decode('utf-8'))['ids']
|
||||
except (JSONDecodeError, KeyError, ValueError):
|
||||
@@ -183,21 +180,23 @@ def reorder_items(request, organizer, event, category):
|
||||
if len(input_items) != len(ids):
|
||||
raise Http404(_("Some of the provided object ids are invalid."))
|
||||
|
||||
if int(category):
|
||||
target_category = request.event.categories.get(id=category)
|
||||
else:
|
||||
target_category = None
|
||||
item_categories = {i.category_id for i in input_items}
|
||||
if len(item_categories) > 1:
|
||||
raise Http404(_("You cannot reorder items spanning different categories."))
|
||||
|
||||
# get first and only category
|
||||
item_category = next(iter(item_categories))
|
||||
if len(input_items) != request.event.items.filter(category=item_category).count():
|
||||
raise Http404(_("Not all objects have been selected."))
|
||||
|
||||
for i in input_items:
|
||||
pos = ids.index(str(i.pk))
|
||||
if pos != i.position or target_category != i.category: # Save unneccessary UPDATE queries
|
||||
if pos != i.position: # Save unneccessary UPDATE queries
|
||||
i.position = pos
|
||||
i.category = target_category
|
||||
i.save(update_fields=['position', 'category_id'])
|
||||
i.save(update_fields=['position'])
|
||||
i.log_action(
|
||||
'pretix.event.item.reordered', user=request.user, data={
|
||||
'position': i,
|
||||
'category': target_category and target_category.pk,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ from django.http import (
|
||||
from django.shortcuts import redirect, render
|
||||
from django.urls import resolve, reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import format_html
|
||||
from django.utils.html import escape, format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@@ -562,7 +562,7 @@ class VoucherBulkMailPreview(EventPermissionRequiredMixin, View):
|
||||
else:
|
||||
ctx[p.identifier] = '<span class="placeholder" title="{}">{}</span>'.format(
|
||||
_('This value will be replaced based on dynamic parameters.'),
|
||||
s
|
||||
escape(s)
|
||||
)
|
||||
return self.SafeDict(ctx)
|
||||
|
||||
|
||||
@@ -64,8 +64,7 @@ def casual_reads():
|
||||
|
||||
class GroupConcat(Aggregate):
|
||||
function = 'group_concat'
|
||||
template = '%(function)s(%(distinct)s%(field)s, "%(separator)s")'
|
||||
allow_distinct = True
|
||||
template = '%(function)s(%(field)s, "%(separator)s")'
|
||||
|
||||
def __init__(self, *expressions, ordered=False, **extra):
|
||||
self.ordered = ordered
|
||||
@@ -74,17 +73,19 @@ class GroupConcat(Aggregate):
|
||||
extra.update({'separator': ','})
|
||||
super().__init__(*expressions, **extra)
|
||||
|
||||
def as_postgresql(self, compiler, connection, **extra_context):
|
||||
def as_postgresql(self, compiler, connection):
|
||||
if self.ordered:
|
||||
template = "%(function)s(%(distinct)s%(field)s::text, '%(separator)s' ORDER BY %(field)s::text ASC)"
|
||||
return super().as_sql(
|
||||
compiler, connection,
|
||||
function='string_agg',
|
||||
template="%(function)s(%(field)s::text, '%(separator)s' ORDER BY %(field)s ASC)",
|
||||
)
|
||||
else:
|
||||
template = "%(function)s(%(distinct)s%(field)s::text, '%(separator)s')"
|
||||
return super().as_sql(
|
||||
compiler, connection,
|
||||
function='string_agg',
|
||||
template=template,
|
||||
**extra_context,
|
||||
)
|
||||
return super().as_sql(
|
||||
compiler, connection,
|
||||
function='string_agg',
|
||||
template="%(function)s(%(field)s::text, '%(separator)s')",
|
||||
)
|
||||
|
||||
|
||||
class ReplicaRouter:
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-06-24 08:20+0000\n"
|
||||
"PO-Revision-Date: 2024-06-24 15:12+0000\n"
|
||||
"Last-Translator: Mira <weller@rami.io>\n"
|
||||
"PO-Revision-Date: 2024-01-31 04:00+0000\n"
|
||||
"Last-Translator: Eduardo Fernandez <sistemas@oyasama.es>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"es/>\n"
|
||||
"Language: es\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.5.5\n"
|
||||
"X-Generator: Weblate 5.3.1\n"
|
||||
|
||||
#: pretix/_base_settings.py:78
|
||||
msgid "English"
|
||||
@@ -37,7 +37,7 @@ msgstr "Árabe"
|
||||
|
||||
#: pretix/_base_settings.py:82
|
||||
msgid "Catalan"
|
||||
msgstr "catalán"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/_base_settings.py:83
|
||||
msgid "Chinese (simplified)"
|
||||
@@ -117,7 +117,7 @@ msgstr "Ruso"
|
||||
|
||||
#: pretix/_base_settings.py:102
|
||||
msgid "Slovak"
|
||||
msgstr "eslovaco"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/_base_settings.py:103
|
||||
msgid "Spanish"
|
||||
@@ -2876,7 +2876,7 @@ msgstr "Código del cupón"
|
||||
#: pretix/base/forms/__init__.py:118
|
||||
#, python-brace-format
|
||||
msgid "You can use {markup_name} in this field."
|
||||
msgstr "Se puede usar {markup_name} en este campo."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/forms/__init__.py:178
|
||||
#, python-format
|
||||
@@ -3095,10 +3095,6 @@ msgid ""
|
||||
"up. Please note: to use literal \"{\" or \"}\", you need to double them as "
|
||||
"\"{{\" and \"}}\"."
|
||||
msgstr ""
|
||||
"Hay un error con el sintaxis de marcador de posición. Por favor, asegúrase "
|
||||
"de que las llaves de apertura «{» y las llaves de cierre «}» de sus "
|
||||
"marcadores de posición coinciden. Tenga en cuenta: para usar «{» o «}» "
|
||||
"literalmente, se debe teclearlas dos veces como «{{» o «}}» respectivamente."
|
||||
|
||||
#: pretix/base/forms/validators.py:72 pretix/control/views/event.py:771
|
||||
#, fuzzy, python-format
|
||||
@@ -4605,7 +4601,7 @@ msgstr "Esperando, producto no disponible"
|
||||
#: pretix/base/models/items.py:382
|
||||
#: pretix/control/templates/pretixcontrol/subevents/fragment_unavail_mode_indicator.html:5
|
||||
msgid "Show info text if unavailable"
|
||||
msgstr "Muestra el texto informativo, si no es disponible"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/models/items.py:389 pretix/base/models/items.py:704
|
||||
msgid "Don't use re-usable media, use regular one-off tickets"
|
||||
@@ -5968,9 +5964,6 @@ msgid ""
|
||||
"up two-factor authentication or leave the team. The setting may take a few "
|
||||
"minutes to become effective for all users."
|
||||
msgstr ""
|
||||
"Si activado, todos los miembros del equipo requerirán configurar la "
|
||||
"autenticación de dos factores o dejar el equipo. El ajuste podría tardar "
|
||||
"pocos minutos para activar para todos los usuarios."
|
||||
|
||||
#: pretix/base/models/organizer.py:275
|
||||
msgid "Can create events"
|
||||
@@ -6247,9 +6240,6 @@ msgid ""
|
||||
"of a specific product, you can also select a quota. In this case, all "
|
||||
"products assigned to this quota can be selected."
|
||||
msgstr ""
|
||||
"Este producto añadirá al carrito del usuario si está usado el vale. En vez "
|
||||
"de un producto específico, se puede seleccionar una cuota. En este caso, "
|
||||
"todos los productos que están asignados al esta cuota se pueden seleccionar."
|
||||
|
||||
#: pretix/base/models/vouchers.py:265
|
||||
msgid "This variation of the product select above is being used."
|
||||
@@ -9661,11 +9651,6 @@ msgid ""
|
||||
"still people on the waiting list. Vouchers that have already been sent "
|
||||
"remain active."
|
||||
msgstr ""
|
||||
"La lista de espera estará deshabilitada después de esta fecha. Esto "
|
||||
"significa que nadie puede añadirse a la lista; sin embargo, las entradas "
|
||||
"estarán disponibles para la venta de nuevo si permite la cuota, incluso si "
|
||||
"hay alguien en la lista de espera. Los vales que ya se han enviado "
|
||||
"permanecerán activos."
|
||||
|
||||
#: pretix/base/settings.py:1420
|
||||
msgid "Ask for a name"
|
||||
@@ -9909,7 +9894,7 @@ msgstr "Ocultar todas las fechas pasadas del calendario"
|
||||
|
||||
#: pretix/base/settings.py:1664 pretix/base/settings.py:1673
|
||||
msgid "No modifications after order was submitted"
|
||||
msgstr "Ningunas modificaciones se entregaron después del pedido"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/settings.py:1665 pretix/base/settings.py:1674
|
||||
#, fuzzy
|
||||
@@ -9919,7 +9904,7 @@ msgstr "Sólo las órdenes pendientes o pagadas pueden ser cambiadas."
|
||||
|
||||
#: pretix/base/settings.py:1666 pretix/base/settings.py:1675
|
||||
msgid "Both the attendee and the person who ordered can make changes"
|
||||
msgstr "Ambos el/la asistente y el/la comprador(a) pueden hacer cambios"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/base/settings.py:1670
|
||||
#, fuzzy
|
||||
@@ -9938,8 +9923,6 @@ msgid ""
|
||||
"By default, no more modifications are possible for an order as soon as one "
|
||||
"of the tickets in the order has been checked in."
|
||||
msgstr ""
|
||||
"Por defecto, las modificaciones del pedido no se estarán permitidas una vez "
|
||||
"que una de las entradas del pedido se hayan canjeado."
|
||||
|
||||
#: pretix/base/settings.py:1696
|
||||
msgid "Last date of modifications"
|
||||
@@ -16037,8 +16020,6 @@ msgid ""
|
||||
"A first login using {agent_type} on {os_type} from {country} has been "
|
||||
"detected."
|
||||
msgstr ""
|
||||
"Se ha detectado un inicio de sesión nuevo por {agent_type} usando {os_type} "
|
||||
"de {country}."
|
||||
|
||||
#: pretix/control/logdisplay.py:460 pretix/control/views/user.py:489
|
||||
#: pretix/control/views/user.py:549 pretix/control/views/user.py:608
|
||||
@@ -18129,21 +18110,6 @@ msgid ""
|
||||
"Best regards, \n"
|
||||
"Your %(instance)s team\n"
|
||||
msgstr ""
|
||||
"Hola,\n"
|
||||
"\n"
|
||||
"Se ha detectado un inicio de sesión de tu cuenta %(instance)s por una "
|
||||
"ubicación nueva o inusual. Se inició el sesión por %(agent)s de %(os)s de "
|
||||
"%(country)s.\n"
|
||||
"\n"
|
||||
"Si tú hiciste esto, puedes ignorar este correo.\n"
|
||||
"\n"
|
||||
"Si no fuiste tú, recomendamos que cambies la contraseña en los ajustes de "
|
||||
"cuenta:\n"
|
||||
"\n"
|
||||
"%(url)s\n"
|
||||
"\n"
|
||||
"Saludos cordiales,\n"
|
||||
"Su equipo de %(instance)s\n"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/email/security_notice.txt:1
|
||||
#, python-format
|
||||
@@ -20004,51 +19970,52 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/global_sysreport.html:16
|
||||
msgid "January"
|
||||
msgstr "enero"
|
||||
msgstr "Enero"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/global_sysreport.html:17
|
||||
msgid "February"
|
||||
msgstr "febrero"
|
||||
msgstr "Febrero"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/global_sysreport.html:18
|
||||
#, fuzzy
|
||||
msgid "March"
|
||||
msgstr "marzo"
|
||||
msgstr "Marzo"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/global_sysreport.html:19
|
||||
msgid "April"
|
||||
msgstr "abril"
|
||||
msgstr "Abril"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/global_sysreport.html:20
|
||||
msgid "May"
|
||||
msgstr "mayo"
|
||||
msgstr "Mayo"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/global_sysreport.html:21
|
||||
msgid "June"
|
||||
msgstr "junio"
|
||||
msgstr "Junio"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/global_sysreport.html:22
|
||||
msgid "July"
|
||||
msgstr "julio"
|
||||
msgstr "Julio"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/global_sysreport.html:23
|
||||
msgid "August"
|
||||
msgstr "agosto"
|
||||
msgstr "Agosto"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/global_sysreport.html:24
|
||||
msgid "September"
|
||||
msgstr "septiembre"
|
||||
msgstr "Septiembre"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/global_sysreport.html:25
|
||||
msgid "October"
|
||||
msgstr "octubre"
|
||||
msgstr "Octubre"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/global_sysreport.html:26
|
||||
msgid "November"
|
||||
msgstr "noviembre"
|
||||
msgstr "Noviembre"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/global_sysreport.html:27
|
||||
msgid "December"
|
||||
msgstr "diciembre"
|
||||
msgstr "Diciembre"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/global_sysreport.html:32
|
||||
#, fuzzy
|
||||
|
||||
@@ -8,16 +8,16 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-06-24 08:20+0000\n"
|
||||
"PO-Revision-Date: 2024-06-24 15:12+0000\n"
|
||||
"Last-Translator: Mira <weller@rami.io>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/"
|
||||
"pretix-js/es/>\n"
|
||||
"PO-Revision-Date: 2023-11-07 14:00+0000\n"
|
||||
"Last-Translator: Zona Vip <contacto@zonavip.mx>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
"js/es/>\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.5.5\n"
|
||||
"X-Generator: Weblate 5.1.1\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -1069,51 +1069,51 @@ msgstr "Dom"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
||||
msgid "January"
|
||||
msgstr "enero"
|
||||
msgstr "Enero"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:79
|
||||
msgid "February"
|
||||
msgstr "febrero"
|
||||
msgstr "Febrero"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:80
|
||||
msgid "March"
|
||||
msgstr "marzo"
|
||||
msgstr "Marzo"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:81
|
||||
msgid "April"
|
||||
msgstr "abril"
|
||||
msgstr "Abril"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:82
|
||||
msgid "May"
|
||||
msgstr "mayo"
|
||||
msgstr "Mayo"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:83
|
||||
msgid "June"
|
||||
msgstr "junio"
|
||||
msgstr "Junio"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:84
|
||||
msgid "July"
|
||||
msgstr "julio"
|
||||
msgstr "Julio"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:85
|
||||
msgid "August"
|
||||
msgstr "agosto"
|
||||
msgstr "Agosto"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:86
|
||||
msgid "September"
|
||||
msgstr "septiembre"
|
||||
msgstr "Septiembre"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:87
|
||||
msgid "October"
|
||||
msgstr "octubre"
|
||||
msgstr "Octubre"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:88
|
||||
msgid "November"
|
||||
msgstr "noviembre"
|
||||
msgstr "Noviembre"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:89
|
||||
msgid "December"
|
||||
msgstr "diciembre"
|
||||
msgstr "Diciembre"
|
||||
|
||||
#~ msgctxt "widget"
|
||||
#~ msgid "See variations"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -46,6 +46,7 @@ from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.loader import get_template
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import escape
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _, ngettext
|
||||
from django.views.generic import DeleteView, FormView, ListView, TemplateView
|
||||
@@ -193,7 +194,7 @@ class BaseSenderView(EventPermissionRequiredMixin, FormView):
|
||||
for k, v in get_available_placeholders(self.request.event, self.context_parameters).items():
|
||||
context_dict[k] = '<span class="placeholder" title="{}">{}</span>'.format(
|
||||
_('This value will be replaced based on dynamic parameters.'),
|
||||
v.render_sample(self.request.event)
|
||||
escape(v.render_sample(self.request.event))
|
||||
)
|
||||
|
||||
subject = bleach.clean(form.cleaned_data['subject'].localize(l), tags=[])
|
||||
@@ -608,7 +609,7 @@ class CreateRule(EventPermissionRequiredMixin, CreateView):
|
||||
'position_or_address']).items():
|
||||
context_dict[k] = '<span class="placeholder" title="{}">{}</span>'.format(
|
||||
_('This value will be replaced based on dynamic parameters.'),
|
||||
v.render_sample(self.request.event)
|
||||
escape(v.render_sample(self.request.event))
|
||||
)
|
||||
|
||||
subject = bleach.clean(form.cleaned_data['subject'].localize(l), tags=[])
|
||||
@@ -684,7 +685,7 @@ class UpdateRule(EventPermissionRequiredMixin, UpdateView):
|
||||
for k, v in get_available_placeholders(self.request.event, ['event', 'order', 'position_or_address']).items():
|
||||
placeholders[k] = '<span class="placeholder" title="{}">{}</span>'.format(
|
||||
_('This value will be replaced based on dynamic parameters.'),
|
||||
v.render_sample(self.request.event)
|
||||
escape(v.render_sample(self.request.event))
|
||||
)
|
||||
|
||||
subject = bleach.clean(self.object.subject.localize(lang), tags=[])
|
||||
|
||||
@@ -1,78 +1,48 @@
|
||||
/*global $, Sortable*/
|
||||
$(function () {
|
||||
const allContainers = $("[data-dnd-url]");
|
||||
function updateAllSortButtonStates() {
|
||||
allContainers.each(function() { updateSortButtonState($(this)); });
|
||||
}
|
||||
function updateSortButtonState(container) {
|
||||
var disabledUp = container.find(".sortable-up:disabled"),
|
||||
firstUp = container.find(">tr[data-dnd-id] .sortable-up").first();
|
||||
if (disabledUp.length && disabledUp.get(0) !== firstUp.get(0)) {
|
||||
disabledUp.prop("disabled", false);
|
||||
firstUp.prop("disabled", true);
|
||||
}
|
||||
|
||||
var disabledDown = container.find(".sortable-down:disabled"),
|
||||
lastDown = container.find(">tr[data-dnd-id] .sortable-down").last();
|
||||
if (disabledDown.length && disabledDown.get(0) !== lastDown.get(0)) {
|
||||
disabledDown.prop("disabled", false);
|
||||
lastDown.prop("disabled", true);
|
||||
}
|
||||
}
|
||||
|
||||
let didSort = false, lastClick = 0;
|
||||
allContainers.each(function(){
|
||||
const container = $(this),
|
||||
$("[data-dnd-url]").each(function(){
|
||||
var container = $(this),
|
||||
url = container.data("dnd-url"),
|
||||
handle = $('<span class="btn btn-default btn-sm dnd-sort-handle"><i class="fa fa-arrows"></i></span>');
|
||||
|
||||
container.find(".dnd-container").append(handle);
|
||||
if (!sessionStorage.dndShowMoveButtons) {
|
||||
container.find(".sortable-up, .sortable-down").addClass("sr-only").on("click", function () {
|
||||
sessionStorage.dndShowMoveButtons = 'true';
|
||||
});
|
||||
}
|
||||
if (container.find("[data-dnd-id]").length < 2 && !container.data("dnd-group")) {
|
||||
if (container.find("[data-dnd-id]").length < 2) {
|
||||
handle.addClass("disabled");
|
||||
return;
|
||||
}
|
||||
function maybeShowSortButtons() {
|
||||
if (Date.now() - lastClick < 3000) {
|
||||
$("[data-dnd-url] .sortable-up, [data-dnd-url] .sortable-down").removeClass("sr-only");
|
||||
updateAllSortButtonStates();
|
||||
}
|
||||
lastClick = Date.now();
|
||||
}
|
||||
container.find(".dnd-sort-handle").on("mouseup", maybeShowSortButtons);
|
||||
const group = container.data("dnd-group");
|
||||
const containers = group ? container.parent().find('[data-dnd-group="' + group + '"]') : container;
|
||||
|
||||
Sortable.create(container.get(0), {
|
||||
filter: ".sortable-disabled",
|
||||
handle: ".dnd-sort-handle",
|
||||
group: group,
|
||||
onMove: function (evt) {
|
||||
return evt.related.className.indexOf('sortable-disabled') === -1;
|
||||
},
|
||||
onStart: function (evt) {
|
||||
containers.addClass("sortable-dragarea");
|
||||
container.addClass("sortable-dragarea");
|
||||
container.parent().addClass("sortable-sorting");
|
||||
didSort = false;
|
||||
},
|
||||
onEnd: function (evt) {
|
||||
containers.removeClass("sortable-dragarea");
|
||||
container.removeClass("sortable-dragarea");
|
||||
container.parent().removeClass("sortable-sorting");
|
||||
if (!didSort) {
|
||||
maybeShowSortButtons();
|
||||
} else {
|
||||
$("[data-dnd-url] .sortable-up, [data-dnd-url] .sortable-down").addClass("sr-only");
|
||||
delete sessionStorage.dndShowMoveButtons;
|
||||
|
||||
var disabledUp = container.find(".sortable-up:disabled"),
|
||||
firstUp = container.find(">tr[data-dnd-id] .sortable-up").first();
|
||||
if (disabledUp.length && disabledUp.get(0) !== firstUp.get(0)) {
|
||||
disabledUp.prop("disabled", false);
|
||||
firstUp.prop("disabled", true);
|
||||
}
|
||||
|
||||
var disabledDown = container.find(".sortable-down:disabled"),
|
||||
lastDown = container.find(">tr[data-dnd-id] .sortable-down").last();
|
||||
if (disabledDown.length && disabledDown.get(0) !== lastDown.get(0)) {
|
||||
disabledDown.prop("disabled", false);
|
||||
lastDown.prop("disabled", true);
|
||||
}
|
||||
},
|
||||
onSort: function (evt){
|
||||
if (evt.target !== evt.to) return;
|
||||
didSort = true;
|
||||
var container = $(evt.to),
|
||||
ids = container.find("[data-dnd-id]").toArray().map(function (e) { return e.dataset.dndId; });
|
||||
|
||||
const ids = container.find("[data-dnd-id]").toArray().map(function (e) { return e.dataset.dndId; });
|
||||
$.ajax(
|
||||
{
|
||||
'type': 'POST',
|
||||
|
||||
@@ -841,21 +841,9 @@ h1 .label {
|
||||
tbody[data-dnd-url] {
|
||||
transition: opacity 1s;
|
||||
}
|
||||
.table-items tbody + tbody {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
.table-items tbody[data-dnd-url]:after {
|
||||
content:'';
|
||||
display: table-row;
|
||||
height: 0.5em;
|
||||
}
|
||||
.sortable-sorting tbody:not(.sortable-dragarea) {
|
||||
opacity: .4;
|
||||
}
|
||||
.font-normal {
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
}
|
||||
tbody th {
|
||||
background: $table-bg-hover;
|
||||
}
|
||||
|
||||
@@ -402,24 +402,6 @@ class ItemsTest(ItemFormTest):
|
||||
self.item2.refresh_from_db()
|
||||
assert self.item1.position < self.item2.position
|
||||
|
||||
def test_reorder(self):
|
||||
self.client.post('/control/event/%s/%s/items/reorder/0/' % (self.orga1.slug, self.event1.slug), {
|
||||
'ids': [str(self.item2.id), str(self.item1.id)],
|
||||
}, content_type='application/json')
|
||||
self.item1.refresh_from_db()
|
||||
self.item2.refresh_from_db()
|
||||
assert self.item1.position > self.item2.position
|
||||
assert self.item1.category is None
|
||||
assert self.item2.category is None
|
||||
self.client.post('/control/event/%s/%s/items/reorder/%s/' % (self.orga1.slug, self.event1.slug, self.addoncat.id), {
|
||||
'ids': [str(self.item1.id), str(self.item2.id)],
|
||||
}, content_type='application/json')
|
||||
self.item1.refresh_from_db()
|
||||
self.item2.refresh_from_db()
|
||||
assert self.item1.position < self.item2.position
|
||||
assert self.item1.category.id == self.addoncat.id
|
||||
assert self.item2.category.id == self.addoncat.id
|
||||
|
||||
def test_create(self):
|
||||
self.client.post('/control/event/%s/%s/items/add' % (self.orga1.slug, self.event1.slug), {
|
||||
'name_0': 'T-Shirt',
|
||||
|
||||
@@ -323,7 +323,7 @@ event_permission_urls = [
|
||||
("can_change_items", "items/add", 200, HTTP_GET),
|
||||
("can_change_items", "items/1/up", 404, HTTP_POST),
|
||||
("can_change_items", "items/1/down", 404, HTTP_POST),
|
||||
("can_change_items", "items/reorder/2/", 400, HTTP_POST),
|
||||
("can_change_items", "items/reorder", 400, HTTP_POST),
|
||||
("can_change_items", "items/1/delete", 404, HTTP_GET),
|
||||
# ("can_change_items", "categories/", 200),
|
||||
# We don't have to create categories and similar objects
|
||||
|
||||
Reference in New Issue
Block a user