diff --git a/src/pretix/api/auth/devicesecurity.py b/src/pretix/api/auth/devicesecurity.py index 4db26fd9bd..045510b664 100644 --- a/src/pretix/api/auth/devicesecurity.py +++ b/src/pretix/api/auth/devicesecurity.py @@ -100,6 +100,8 @@ class PretixScanSecurityProfile(AllowListSecurityProfile): ('GET', 'api-v1:itemcategory-list'), ('GET', 'api-v1:item-list'), ('GET', 'api-v1:question-list'), + ('GET', 'api-v1:compatquestion-list'), + ('GET', 'api-v1:datafield-list'), ('GET', 'api-v1:questionnaire-list'), ('GET', 'api-v1:questionnairechild-list'), ('GET', 'api-v1:badgelayout-list'), @@ -143,6 +145,8 @@ class PretixScanNoSyncNoSearchSecurityProfile(AllowListSecurityProfile): ('GET', 'api-v1:itemcategory-list'), ('GET', 'api-v1:item-list'), ('GET', 'api-v1:question-list'), + ('GET', 'api-v1:compatquestion-list'), + ('GET', 'api-v1:datafield-list'), ('GET', 'api-v1:questionnaire-list'), ('GET', 'api-v1:questionnairechild-list'), ('GET', 'api-v1:badgelayout-list'), @@ -181,6 +185,8 @@ class PretixScanNoSyncSecurityProfile(AllowListSecurityProfile): ('GET', 'api-v1:itemcategory-list'), ('GET', 'api-v1:item-list'), ('GET', 'api-v1:question-list'), + ('GET', 'api-v1:compatquestion-list'), + ('GET', 'api-v1:datafield-list'), ('GET', 'api-v1:questionnaire-list'), ('GET', 'api-v1:questionnairechild-list'), ('GET', 'api-v1:badgelayout-list'), diff --git a/src/pretix/api/serializers/item.py b/src/pretix/api/serializers/item.py index f57aa92e97..4202d57f8a 100644 --- a/src/pretix/api/serializers/item.py +++ b/src/pretix/api/serializers/item.py @@ -712,7 +712,7 @@ class DatafieldSerializer(I18nAwareModelSerializer): options_data = validated_data.pop('options') if 'options' in validated_data else [] items = validated_data.pop('items', []) - question = Question.objects.create(**validated_data, container_type=Question.ContainerType.ORDERPOSITION) + question = Question.objects.create(**validated_data, container_type=self.context['container_type']) question.items.set(items) for opt_data in options_data: QuestionOption.objects.create(question=question, **opt_data) @@ -755,18 +755,18 @@ class RenderedMarkdownField(serializers.CharField): class InlineQuestionnaireChildSerializer(I18nAwareModelSerializer): - question = QuestionRefField(allow_null=True, source='*', queryset=Question.objects.none()) - dependency_question = QuestionRefField(allow_null=True, required=False, queryset=Question.objects.none()) + datafield = DatafieldRelatedField(allow_null=True, source='*', queryset=Question.objects.none()) + dependency_question = DatafieldRelatedField(allow_null=True, required=False, queryset=Question.objects.none()) rendered_help_text = RenderedMarkdownField(read_only=True, source='help_text') cooked_id = serializers.IntegerField(read_only=True) class Meta: model = QuestionnaireChild - fields = ('question', 'required', 'label', 'help_text', 'dependency_question', 'dependency_values', 'rendered_help_text', 'cooked_id') + fields = ('datafield', 'required', 'label', 'help_text', 'dependency_question', 'dependency_values', 'rendered_help_text', 'cooked_id') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields["question"].queryset = self.context["event"].questions.all() + self.fields["datafield"].queryset = self.context["event"].questions.all() self.fields["dependency_question"].queryset = self.context["event"].questions.all() def validate_dependency_question(self, value): diff --git a/src/pretix/api/urls.py b/src/pretix/api/urls.py index 36af31bf0d..2f03d853a1 100644 --- a/src/pretix/api/urls.py +++ b/src/pretix/api/urls.py @@ -80,7 +80,8 @@ event_router.register(r'subevents', event.SubEventViewSet) event_router.register(r'clone', event.CloneEventViewSet) event_router.register(r'items', item.ItemViewSet) event_router.register(r'categories', item.ItemCategoryViewSet) -event_router.register(r'datafields', item.DatafieldViewSet) +event_router.register(r'datafields', item.DatafieldViewSet, basename='datafield') +event_router.register(r'orderdatafields', item.OrderDatafieldViewSet, basename='orderdatafield') event_router.register(r'questionnaires', item.QuestionnaireViewSet) event_router.register(r'questions', item.CompatQuestionViewSet) event_router.register(r'discounts', discount.DiscountViewSet) diff --git a/src/pretix/api/views/item.py b/src/pretix/api/views/item.py index b75b1c725b..cf96195504 100644 --- a/src/pretix/api/views/item.py +++ b/src/pretix/api/views/item.py @@ -579,7 +579,7 @@ with scopes_disabled(): fields = ['identifier'] -class DatafieldViewSet(ConditionalListView, viewsets.ModelViewSet): +class BaseDatafieldViewSet(viewsets.ModelViewSet): serializer_class = DatafieldSerializer queryset = Question.objects.none() filter_backends = (DjangoFilterBackend, TotalOrderingFilter) @@ -588,12 +588,24 @@ class DatafieldViewSet(ConditionalListView, viewsets.ModelViewSet): ordering = ('id') permission = None write_permission = 'event.items:write' + container_type = Question.ContainerType.ORDERPOSITION def get_queryset(self): - return self.request.event.questions.filter( - # the container_type parameter is undocumented, this API is going to change in a later release - container_type=self.request.GET.get('container_type', Question.ContainerType.ORDERPOSITION), - ).prefetch_related('options').all() + return self.request.event.questions.filter(container_type=self.container_type).prefetch_related('options') + + def get_object(self): + queryset = self.filter_queryset(self.get_queryset()) + + lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field + + qid, dfid, sys_df = QuestionnaireChild.uncook_id(self.kwargs[lookup_url_kwarg]) + self.via_questionnaire_id = qid + obj = get_object_or_404(queryset, pk=dfid) + + # May raise a permission denied + self.check_object_permissions(self.request, obj) + + return obj @transaction.atomic() def perform_create(self, serializer): @@ -608,6 +620,7 @@ class DatafieldViewSet(ConditionalListView, viewsets.ModelViewSet): def get_serializer_context(self): ctx = super().get_serializer_context() ctx['event'] = self.request.event + ctx['container_type'] = self.container_type return ctx @transaction.atomic() @@ -630,6 +643,13 @@ class DatafieldViewSet(ConditionalListView, viewsets.ModelViewSet): super().perform_destroy(instance) +class DatafieldViewSet(ConditionalListView, BaseDatafieldViewSet): + pass + + +class OrderDatafieldViewSet(ConditionalListView, BaseDatafieldViewSet): + container_type = Question.ContainerType.ORDER + class QuestionOptionViewSet(viewsets.ModelViewSet): serializer_class = QuestionOptionSerializer queryset = QuestionOption.objects.none() diff --git a/src/pretix/static/pretixcontrol/js/ui/questionnaires/QuestionElement.vue b/src/pretix/static/pretixcontrol/js/ui/questionnaires/QuestionElement.vue index 8dfdda4eaa..52b2a7394d 100644 --- a/src/pretix/static/pretixcontrol/js/ui/questionnaires/QuestionElement.vue +++ b/src/pretix/static/pretixcontrol/js/ui/questionnaires/QuestionElement.vue @@ -16,10 +16,10 @@ const question = ref(props.question); const dlgEditDatafield = ref() -const df = computed(() => typeof question.value.question === 'number' ? - props.datafields.find(el => el.id === question.value.question) : - typeof question.value.question === 'string' ? - SYSTEM_DATAFIELDS[question.value.question] : +const df = computed(() => typeof question.value.datafield === 'number' ? + props.datafields.find(el => el.id === question.value.datafield) : + typeof question.value.datafield === 'string' ? + SYSTEM_DATAFIELDS[question.value.datafield] : null); const dependency_values_options = computed(() => props.datafields.find(el => el.id === question.value.dependency_question)?.options); @@ -107,7 +107,7 @@ const editor = ref();

-