diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml new file mode 100644 index 0000000..43921cc --- /dev/null +++ b/.github/workflows/style.yml @@ -0,0 +1,108 @@ +name: Code Style + +on: + push: + branches: [ main, master ] + paths-ignore: + - 'pretix_digital_items/locale/**' + - 'pretix_digital_items/static/**' + pull_request: + branches: [ main, master ] + paths-ignore: + - 'pretix_digital_items/locale/**' + - 'pretix_digital_items/static/**' + +jobs: + isort: + name: isort + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - uses: actions/cache@v1 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Install pretix + run: pip3 install pretix + - name: Install Dependencies + run: pip3 install isort -Ue . + - name: Run isort + run: isort -c . + flake: + name: flake8 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - uses: actions/cache@v1 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Install pretix + run: pip3 install pretix + - name: Install Dependencies + run: pip3 install flake8 -Ue . + - name: Run flake8 + run: flake8 . + working-directory: . + black: + name: black + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - uses: actions/cache@v1 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Install pretix + run: pip3 install pretix + - name: Install Dependencies + run: pip3 install black -Ue . + - name: Run black + run: black --check . + working-directory: . + packaging: + name: packaging + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - uses: actions/cache@v1 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Install pretix + run: pip3 install pretix + - name: Install Dependencies + run: pip3 install twine check-manifest -Ue . + - name: Run check-manifest + run: check-manifest . + working-directory: . + - name: Build package + run: python setup.py sdist + working-directory: . + - name: Check package + run: twine check dist/* + working-directory: . diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..67965da --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,36 @@ +name: Tests + +on: + push: + branches: [ main, master ] + paths-ignore: + - 'pretix_digital_items/locale/**' + pull_request: + branches: [ main, master ] + paths-ignore: + - 'pretix_digital_items/locale/**' + +jobs: + test: + runs-on: ubuntu-latest + name: Tests + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - uses: actions/cache@v1 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Install system dependencies + run: sudo apt update && sudo apt install gettext + - name: Install pretix + run: pip3 install pretix + - name: Install Dependencies + run: pip3 install pytest pytest-django -Ue . + - name: Run checks + run: py.test tests diff --git a/.gitignore b/.gitignore index cc7fbd7..0f6b8f8 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ coverage.xml # Django stuff: *.log +data/ # Sphinx documentation docs/_build/ diff --git a/.install-hooks.sh b/.install-hooks.sh new file mode 100755 index 0000000..2e423ff --- /dev/null +++ b/.install-hooks.sh @@ -0,0 +1,16 @@ +#!/bin/sh +REPO_DIR=$(git rev-parse --show-toplevel) +GIT_DIR=$REPO_DIR/.git +VENV_ACTIVATE=$VIRTUAL_ENV/bin/activate +if [[ ! -f $VENV_ACTIVATE ]] +then + echo "Could not find your virtual environment" +fi + +echo "#!/bin/sh" >> $GIT_DIR/hooks/pre-commit +echo "set -e" >> $GIT_DIR/hooks/pre-commit +echo "source $VENV_ACTIVATE" >> $GIT_DIR/hooks/pre-commit +echo "black --check ." >> $GIT_DIR/hooks/pre-commit +echo "isort -c ." >> $GIT_DIR/hooks/pre-commit +echo "flake8 ." >> $GIT_DIR/hooks/pre-commit +chmod +x $GIT_DIR/hooks/pre-commit diff --git a/.update-locales.sh b/.update-locales.sh new file mode 100755 index 0000000..f1df3de --- /dev/null +++ b/.update-locales.sh @@ -0,0 +1,37 @@ +#!/bin/sh +COMPONENTS=pretix/pretix-plugin-pretix-digital-items +DIR=pretix_digital_items/locale +# Renerates .po files used for translating the plugin +set -e +set -x + +# Lock Weblate +for c in $COMPONENTS; do + wlc lock $c; +done + +# Push changes from Weblate to GitHub +for c in $COMPONENTS; do + wlc commit $c; +done + +# Pull changes from GitHub +git pull --rebase + +# Update po files itself +make localegen + +# Commit changes +git add $DIR/*/*/*.po +git add $DIR/*.pot + +git commit -s -m "Update po files +[CI skip]" + +# Push changes +git push + +# Unlock Weblate +for c in $COMPONENTS; do + wlc unlock $c; +done diff --git a/LICENSE b/LICENSE index 65d5d6c..f0abd0f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ -Copyright 2020 _Bastler +Copyright 2023 _Bastler Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/MANIFEST.in b/MANIFEST.in index 17ba0c0..a79b079 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,5 @@ recursive-include pretix_digital_items/static * recursive-include pretix_digital_items/templates * recursive-include pretix_digital_items/locale * +include LICENSE +exclude .gitlab-ci.yml diff --git a/README.rst b/README.rst index c6fb469..c23c0f3 100644 --- a/README.rst +++ b/README.rst @@ -10,7 +10,7 @@ Development setup 1. Make sure that you have a working `pretix development setup`_. -2. Clone this repository, eg to ``local/pretix-digital-items``. +2. Clone this repository. 3. Activate the virtual environment you use for pretix development. @@ -21,12 +21,29 @@ Development setup 6. Restart your local pretix server. You can now use the plugin from this repository for your events by enabling it in the 'plugins' tab in the settings. +This plugin has CI set up to enforce a few code style rules. To check locally, you need these packages installed:: + + pip install flake8 isort black + +To check your plugin for rule violations, run:: + + black --check . + isort -c . + flake8 . + +You can auto-fix some of these issues by running:: + + isort . + black . + +To automatically check for these issues before you commit, you can run ``.install-hooks``. + License ------- -Copyright 2020 _Bastler +Copyright 2023 _Bastler Released under the terms of the Apache License 2.0 diff --git a/pretix_digital_items/__init__.py b/pretix_digital_items/__init__.py index d29d1f0..8c0d5d5 100644 --- a/pretix_digital_items/__init__.py +++ b/pretix_digital_items/__init__.py @@ -1,28 +1 @@ -from django.utils.translation import gettext_lazy - -try: - from pretix.base.plugins import PluginConfig -except ImportError: - raise RuntimeError("Please use pretix 2.7 or above to run this plugin!") - -__version__ = '1.0.0' - - -class PluginApp(PluginConfig): - name = 'pretix_digital_items' - verbose_name = 'Digital Items' - - class PretixPluginMeta: - name = gettext_lazy('Digital Items') - author = '_Bastler' - description = gettext_lazy('Sell digital Items by using secrets as tokens. Adds secrets to email placeholders, output secret as simple TXT file.') - visible = True - version = __version__ - category = 'FEATURE' - compatibility = "pretix>=2.7.0" - - def ready(self): - from . import signals # NOQA - - -default_app_config = 'pretix_digital_items.PluginApp' +__version__ = "2.0.0" diff --git a/pretix_digital_items/apps.py b/pretix_digital_items/apps.py new file mode 100644 index 0000000..283f7c2 --- /dev/null +++ b/pretix_digital_items/apps.py @@ -0,0 +1,27 @@ +from django.utils.translation import gettext_lazy +from . import __version__ + +try: + from pretix.base.plugins import PluginConfig +except ImportError: + raise RuntimeError("Please use pretix 2.7 or above to run this plugin!") + + +class PluginApp(PluginConfig): + default = True + name = "pretix_digital_items" + verbose_name = "Digital Items" + + class PretixPluginMeta: + name = gettext_lazy("Digital Items") + author = "_Bastler" + description = gettext_lazy("Sell digital Items by using secrets as tokens. Adds secrets to email placeholders, output secret as simple TXT file.") + visible = True + version = __version__ + category = "FEATURE" + compatibility = "pretix>=2.7.0" + + def ready(self): + from . import signals # NOQA + + diff --git a/pretix_digital_items/forms.py b/pretix_digital_items/forms.py index 3c3486c..0a4e1c9 100644 --- a/pretix_digital_items/forms.py +++ b/pretix_digital_items/forms.py @@ -1,16 +1,30 @@ from django import forms -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from pretix.base.forms import SettingsForm -class PretixDigitalItemsSettingsForm(SettingsForm): - digitalitems_token_mail_format = forms.CharField( - label=_("Token mail format"), - help_text=_("This text will be included for every token in an email. Available placeholder are {name} for the item name and {secret} for the token code."), - widget=forms.TextInput(attrs={'placeholder': '{name}: {secret}'}), - required=False) - digitalitems_token_export_format = forms.CharField( - label=_("Token export format"), - help_text=_("This text will be included for every token in a TXT export. Available placeholder are {name} for the item name and {secret} for the token code."), - widget=forms.TextInput(attrs={'placeholder': '{name}: {secret}'}), - required=False) +class PretixDigitalItemsSettingsForm(SettingsForm): + digitalitems_token_url_format = forms.CharField( + label=_("Token url format"), + help_text=_( + "This should return an url to link to. Available placeholder are {name} for the item name and {secret} for the token code." + ), + widget=forms.TextInput(attrs={"placeholder": "http://localhost?secret={secret}"}), + required=False, + ) + digitalitems_token_mail_format = forms.CharField( + label=_("Token mail format"), + help_text=_( + "This text will be included for every token in an email. Available placeholder are {name} for the item name and {secret} for the token code." + ), + widget=forms.TextInput(attrs={"placeholder": "{name}: {secret}"}), + required=False, + ) + digitalitems_token_export_format = forms.CharField( + label=_("Token export format"), + help_text=_( + "This text will be included for every token in a TXT export. Available placeholder are {name} for the item name and {secret} for the token code." + ), + widget=forms.TextInput(attrs={"placeholder": "{name}: {secret}"}), + required=False, + ) diff --git a/pretix_digital_items/forms.py.save b/pretix_digital_items/forms.py.save deleted file mode 100644 index 2c265af..0000000 --- a/pretix_digital_items/forms.py.save +++ /dev/null @@ -1,11 +0,0 @@ -from django import forms -from django.utils.translation import ugettext_lazy as _ -from pretix.base.forms import SettingsForm - - -class PretixDigitalItemsSettingsForm(SettingsForm): - digitalitems_token_mail_format = forms.CharField(lel=_("Token mail format"), - help_text=_("This text will be included for every token in an email. Available placeholder are {name} for the item name and {secret} for the token code.") - widget=forms.TextInput(attrs={'placeholder': '{name}: {secret}'}), - required=False) - digitalitems_token_export_format = forms.CharField(label=_("Token export format"), widget=forms.TextInput(attrs={'placeholder': '{name}: {secret}'}), required=False) diff --git a/pretix_digital_items/locale/de/LC_MESSAGES/django.po b/pretix_digital_items/locale/de/LC_MESSAGES/django.po index b6643aa..50b163b 100644 --- a/pretix_digital_items/locale/de/LC_MESSAGES/django.po +++ b/pretix_digital_items/locale/de/LC_MESSAGES/django.po @@ -38,3 +38,14 @@ msgid "" "This text will be included for every token in a TXT export. Available placeholder are {name} for the item name and {secret} for the token code." msgstr "Dieser Text wird für jedes Token in einem TXT Export eingefügt. Verfügbare Platzhalter sind {name} für den Item-Namen und {secret} für den Token-Code." +#: pretix_digital_items/templates/tokens.html:1 +msgid "Tokens" +msgstr "Tokens" + +#: pretix_digital_items/templates/tokens.html:2 +msgid "Token already redeemed" +msgstr "Token bereits eingelöst" + +#: pretix_digital_items/templates/tokens.html:3 +msgid "Redeem token" +msgstr "Token einlösen" \ No newline at end of file diff --git a/pretix_digital_items/locale/de_Informal/LC_MESSAGES/django.po b/pretix_digital_items/locale/de_Informal/LC_MESSAGES/django.po index b6643aa..50b163b 100644 --- a/pretix_digital_items/locale/de_Informal/LC_MESSAGES/django.po +++ b/pretix_digital_items/locale/de_Informal/LC_MESSAGES/django.po @@ -38,3 +38,14 @@ msgid "" "This text will be included for every token in a TXT export. Available placeholder are {name} for the item name and {secret} for the token code." msgstr "Dieser Text wird für jedes Token in einem TXT Export eingefügt. Verfügbare Platzhalter sind {name} für den Item-Namen und {secret} für den Token-Code." +#: pretix_digital_items/templates/tokens.html:1 +msgid "Tokens" +msgstr "Tokens" + +#: pretix_digital_items/templates/tokens.html:2 +msgid "Token already redeemed" +msgstr "Token bereits eingelöst" + +#: pretix_digital_items/templates/tokens.html:3 +msgid "Redeem token" +msgstr "Token einlösen" \ No newline at end of file diff --git a/pretix_digital_items/signals.py b/pretix_digital_items/signals.py index 40dac30..a89dfb1 100644 --- a/pretix_digital_items/signals.py +++ b/pretix_digital_items/signals.py @@ -1,32 +1,105 @@ from django.dispatch import receiver from django.urls import resolve, reverse -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ -from pretix.base.signals import (register_mail_placeholders, register_ticket_outputs) +from pretix.base.models import Order +from pretix.base.signals import register_mail_placeholders, register_ticket_outputs +from pretix.base.email import SimpleFunctionalMailTextPlaceholder +from pretix.control.signals import nav_event_settings +from pretix.presale.signals import order_info_top +from django.template.loader import get_template + +from pretix.base.signals import register_mail_placeholders, register_ticket_outputs from pretix.base.email import SimpleFunctionalMailTextPlaceholder from pretix.control.signals import nav_event_settings -@receiver(register_mail_placeholders, dispatch_uid="pretix_digital_items_mail_placeholder") + +@receiver( + register_mail_placeholders, dispatch_uid="pretix_digital_items_mail_placeholder" +) def register_mail_secrets(sender, **kwargs): - token_mail_format = "{name}: {secret}" - if sender.settings.digitalitems_token_mail_format: - token_mail_format = sender.settings.digitalitems_token_mail_format - return SimpleFunctionalMailTextPlaceholder('secrets', ['order'], lambda order: '\n' + '\n\n'.join(token_mail_format.format(secret = position.secret, name = str(position.item.name)) for position in order.positions.all() if position.item.admission), '\nej7f73xs8vfmwvkk2p73yeve4fueq849\n\np8yct9752a897rpsmqzf7beby34a2w25\n\nwwqxtw6guy5s8c5tdarfhyez2ft2juap') + token_mail_format = "{name}: {secret}" + if sender.settings.digitalitems_token_mail_format: + token_mail_format = sender.settings.digitalitems_token_mail_format + return SimpleFunctionalMailTextPlaceholder( + "secrets", + ["order"], + lambda order: "\n" + + "\n\n".join( + token_mail_format.format( + secret=position.secret, name=str(position.item.name) + ) + for position in order.positions.all() + if position.item.admission + ), + "\nej7f73xs8vfmwvkk2p73yeve4fueq849\n\np8yct9752a897rpsmqzf7beby34a2w25\n\nwwqxtw6guy5s8c5tdarfhyez2ft2juap", + ) -@receiver(register_ticket_outputs, dispatch_uid="pretix_digital_items_ticket_output") -def register_ticket_outputs(sender, **kwargs): - from .textticketoutput import TextTicketOutput - return TextTicketOutput -@receiver(nav_event_settings, dispatch_uid='pretix_digital_items_nav_settings') +# @receiver(register_ticket_outputs, dispatch_uid="pretix_digital_items_ticket_output") +# def register_ticket_outputs(sender, **kwargs): +# from .textticketoutput import TextTicketOutput +# +# return TextTicketOutput + + +@receiver(nav_event_settings, dispatch_uid="pretix_digital_items_nav_settings") def navbar_settings(sender, request, **kwargs): url = resolve(request.path_info) - return [{ - 'label': _('Digital Items'), - 'url': reverse('plugins:pretix_digital_items:settings', kwargs={ - 'event': request.event.slug, - 'organizer': request.organizer.slug, - }), - 'active': url.namespace == 'plugins:pretix_digital_items' and url.url_name == 'settings', - }] + return [ + { + "label": _("Digital Items"), + "url": reverse( + "plugins:pretix_digital_items:settings", + kwargs={ + "event": request.event.slug, + "organizer": request.organizer.slug, + }, + ), + "active": url.namespace == "plugins:pretix_digital_items" + and url.url_name == "settings", + } + ] + + +@receiver(order_info_top, dispatch_uid="pretix_digital_items_order_tokens") +def list_tokens(sender, request, order: Order, **kwargs): + if (order.status != Order.STATUS_PAID) or not order.positions.exists(): + return + + positions = [ + p for p in order.positions.filter(item__admission=True, addon_to__isnull=True) + ] + + if not positions: + return + + token_url_format = "http://localhost?token={secret}" + if sender.settings.digitalitems_token_url_format: + token_url_format = sender.settings.digitalitems_token_url_format + + tokenUrls = list( + map( + lambda position: token_url_format.format( + secret=position.secret, name=str(position.item.name) + ), + positions, + ) + ) + + checkins = list( + map( + lambda position: position.checkins.count() != 0, + positions, + ) + ) + + data = zip(positions, tokenUrls, checkins) + + template = get_template("pretix_digital_items/tokens.html") + ctx = { + "data": data, + } + + return template.render(ctx, request=request) diff --git a/pretix_digital_items/templates/pretix_digital_items/settings.html b/pretix_digital_items/templates/pretix_digital_items/settings.html index 8b18284..81fcc0c 100644 --- a/pretix_digital_items/templates/pretix_digital_items/settings.html +++ b/pretix_digital_items/templates/pretix_digital_items/settings.html @@ -5,7 +5,7 @@
+ {{ p.item.name }}: {{ p.secret }}
+ ({% trans "Token already redeemed" %})
+
+ {{ p.item.name }}: {{ p.secret }}
+ {% trans "Redeem token" %}
+