Porównaj commity

...

46 Commity
0.3.0 ... main

Autor SHA1 Wiadomość Data
mtyton f8f3d35935 Merge pull request 'Renamed project to wagtail_store' (#23) from feature/project_rename into main
Reviewed-on: #23
2023-11-27 21:45:19 +00:00
mtyton 6bc385e47d Renamed project to wagtail_store 2023-11-27 22:34:44 +01:00
mtyton 345dbe3f46 Merge pull request 'Fixed tests' (#22) from feature/form_improvements into main
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/tag/build Pipeline was successful Szczegóły
Reviewed-on: #22
2023-11-12 09:30:30 +00:00
mtyton a5e243c55c Fixed tests
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline was successful Szczegóły
2023-11-12 10:29:59 +01:00
mtyton 1ab8537c64 Merge pull request 'form anty-spam, hiden field rendering, added help_text showing' (#21) from feature/form_improvements into main
ci/woodpecker/push/build Pipeline was successful Szczegóły
Reviewed-on: #21
2023-11-12 09:08:56 +00:00
mtyton d365a1ee98 form anty-spam, hiden field rendering, added help_text showing
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline failed Szczegóły
2023-11-12 09:51:09 +01:00
mtyton 19078bd03f Added submission_id to email context
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/tag/build Pipeline was successful Szczegóły
2023-11-05 20:03:03 +01:00
mtyton dd0998b96b Merge pull request 'Fixed menu translation' (#20) from fix/menu_translation into main
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/tag/build Pipeline was successful Szczegóły
Reviewed-on: #20
2023-11-03 22:05:42 +01:00
mtyton b07561d34c Fixed form styling
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline was successful Szczegóły
2023-11-01 21:50:51 +01:00
mtyton 80ddd65ea6 Fixed menu translation
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline was successful Szczegóły
2023-10-31 23:37:23 +01:00
mtyton c762438439 Added database depndency for beat
ci/woodpecker/push/build Pipeline was successful Szczegóły
2023-10-22 21:14:41 +02:00
mtyton d98f1a42d2 Merge pull request 'Added basic dynamic form config' (#18) from feature/dynamic_forms into main
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/tag/build Pipeline was successful Szczegóły
Reviewed-on: #18
2023-10-22 08:23:43 +00:00
mtyton f6f7878963 Fixed tests
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline was successful Szczegóły
2023-10-22 10:18:18 +02:00
mtyton cc87725603 Added some tests
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline failed Szczegóły
2023-10-18 21:16:19 +02:00
mtyton 46645bde17 Modified fiew steps, added possibility to attach files
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline failed Szczegóły
2023-10-14 00:28:53 +02:00
mtyton b149b85f53 Basic forms has been implemented
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline failed Szczegóły
2023-10-13 20:59:17 +02:00
mtyton 2ab84212b6 Added class to abstract form generation
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline failed Szczegóły
2023-10-12 22:59:04 +02:00
mtyton dc25fa01a7 WIP: modified form models idea 2023-10-10 22:28:02 +02:00
mtyton f509df6a9d Refactored forms models
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline was successful Szczegóły
2023-10-09 21:52:06 +02:00
mtyton 532180c605 Added basic dynamic form config 2023-10-09 20:06:00 +02:00
mtyton cfee29d09d Merge pull request 'updated django + wagtail' (#19) from feature/updated_django_wagtail into main
ci/woodpecker/push/build Pipeline was successful Szczegóły
Reviewed-on: #19
2023-10-09 18:04:40 +00:00
KarolG 22ce13904e updated django + wagtail
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline was successful Szczegóły
2023-10-09 19:40:09 +02:00
mtyton 9bc46af370 Merge pull request 'feature/translations' (#15) from feature/translations into main
ci/woodpecker/push/build Pipeline was successful Szczegóły
Reviewed-on: #15
2023-10-04 20:00:58 +00:00
mtyton 45a9918bda Fixed tests
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline was successful Szczegóły
2023-10-04 21:49:01 +02:00
mtyton 0b99786301 Merge branch 'main' into feature/translations 2023-10-04 21:36:39 +02:00
mtyton a004ae4037 Fixed woodpecker
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline failed Szczegóły
2023-10-03 22:32:15 +02:00
mtyton ec86deb2f2 Merge pull request 'Added woodpecker badge' (#16) from update-readme into main
ci/woodpecker/push/build Pipeline was successful Szczegóły
Reviewed-on: #16
2023-10-03 18:42:24 +00:00
mtyton 690af02153 Merge branch 'feature/translations' of ssh://forge.citizen4.eu:2222/mtyton/comfy into feature/translations
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline failed Szczegóły
2023-09-30 13:10:14 +02:00
mtyton b4edf01685 fixed woodpecker file 2023-09-30 13:10:11 +02:00
mtyton aae4396368 Merge branch 'feature/translations' of ssh://forge.citizen4.eu:2222/mtyton/comfy into feature/translations 2023-09-30 13:09:24 +02:00
mtyton 7f4c00547f fixed woodpecker file 2023-09-30 13:09:14 +02:00
mtyton 75469ace5e Merge branch 'main' into feature/translations 2023-09-30 11:06:01 +00:00
mtyton d0d77956ad Translations works properly now 2023-09-30 13:04:24 +02:00
mtyton d66379c440 Tranlations works, it need some bug fixing 2023-09-30 12:24:14 +02:00
gitesiek b5165607f6 Added woodpecker badge
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline was successful Szczegóły
2023-09-27 23:26:19 +00:00
mtyton e000b5aa45 get language change working
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline was successful Szczegóły
2023-09-27 22:10:27 +02:00
mtyton 074281257b started working on switching languages
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline was successful Szczegóły
2023-09-27 21:50:44 +02:00
gitesiek 0355d842d9 Update README.md
ci/woodpecker/push/build Pipeline was successful Szczegóły
2023-09-27 00:52:42 +00:00
gitesiek e811abacb5 Update README.md
ci/woodpecker/push/build Pipeline was successful Szczegóły
2023-09-27 00:51:45 +00:00
mtyton fdcd849a85 minor changes
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline was successful Szczegóły
2023-09-26 21:47:44 +02:00
mtyton 6bdedb343d Merge branch 'main' into feature/translations
ci/woodpecker/push/build Pipeline was successful Szczegóły
ci/woodpecker/pr/build Pipeline was successful Szczegóły
2023-09-26 20:55:25 +02:00
mtyton 315a80f66b Added celery to prod docker-compose
ci/woodpecker/push/build Pipeline was successful Szczegóły
2023-09-24 17:38:02 +02:00
KarolG 2e68e3ed6f fixed whitespaces 2023-06-20 15:48:33 +02:00
KarolG 064c4cfa50 Added ability to translate strings on websites 2023-06-20 15:47:43 +02:00
KarolG 750f709279 Added a link to translated pages to menu 2023-06-07 20:39:15 +02:00
KarolG 2e80c0f9b3 added ability to translate websites 2023-06-07 04:30:00 +02:00
208 zmienionych plików z 1377 dodań i 175 usunięć

6
.gitignore vendored
Wyświetl plik

@ -139,8 +139,8 @@ GitHub.sublime-settings
#postgres pass files
*.my_pgpass
*.sql
artel/static/
wagtail_store/static/
# media
artel/media/*
artel/store/data/*
wagtail_store/media/*
wagtail_store/store/data/*

Wyświetl plik

@ -1 +1,52 @@
# comfy-szop
# ComfyShop
ComfyShop is an open source project that combines a blog and shop using Django and Wagtail. It provides an easy-to-use interface for managing blog posts and products.
[![Python](https://img.shields.io/badge/Python-FFD43B?style=for-the-badge&logo=python&logoColor=blue)](https://www.python.org)
[![Django](https://img.shields.io/badge/Django-092E20?style=for-the-badge&logo=django&logoColor=green)](https://www.djangoproject.com/)
[![Woodpecker CI](https://ci.citizen4.eu/api/badges/21/status.svg)](https://ci.citizen4.eu/repos/21)
[![Release Version](https://img.shields.io/badge/Release%20Version-v0.2-blue)](https://forge.citizen4.eu/mtyton/comfy/releases/tag/0.2.0)
## Requirements
- Docker
- Docker Compose
## Installation
1. Clone the repository using the following command:
```git clone https://forge.citizen4.eu/mtyton/comfy.git```
3. Build the Docker image:
```docker-compose build```
4. Run the Docker container for development:
```docker-compose up```
For production, use the following command to run the Docker container:
```docker-compose -f docker-compose-prod.yml up -d```
4. Access the application at [http://127.0.0.1:8001/](http://127.0.0.1:8001/)
## Usage
- Visit the home page to view the blog posts and shop items.
## Customization
- Access the admin panel at [http://127.0.0.1:8001/admin/](http://127.0.0.1:8001/admin/)
- Log in with the superuser credentials (you can create a superuser within the Docker container if not already done).
- Create and manage blog posts and products through the admin panel.
## Contributing
Feel free to contribute to this project by opening issues or creating pull requests. Please follow the contribution guidelines and review the [CONTRIBUTING.md](CONTRIBUTING.md) file for details.
## Documentation
For more detailed information on how to use and customize the application, refer to the [documentation](https://forge.citizen4.eu/mtyton/comfy/wiki).
## License
This project is licensed under the [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl-3.0.en.html).

Wyświetl plik

@ -1,28 +0,0 @@
{% load static %}
{% load menu_tags %}
<div class="d-flex flex-column flex-shrink-0 p-3 mr-5">
<img src="{{logo}}" class="img-fluid rounded mx-auto d-block mt-3" style="width: 10rem; height: 10 rem;" alt="Portal Logo"/>
<hr>
<ul class="nav navbar-nav">
{% for item in menu_items %}
<li class="{{ item.active_class }}">
<a href="{{ item.href }}">{{ item.text }}</a>
{% if item.has_children_in_menu %}
<button class="btn btn-toggle" data-bs-target="#ddtoggle_{{ item.link_page.pk }}" data-bs-toggle="collapse"
aria-expanded={% if item.active_class %}"true" {% else %} "false" {% endif %}
aria-controls="#ddtoggle_{{ item.link_page.pk }}">
<img src = "{% static 'images/icons/caret-down.svg' %}" alt="&or;"/> </button>
{% sub_menu item template="menu/custom_submenu.html" %}
{% endif %}
</li>
{% endfor %}
</ul>
</div>
<div class="d-flex flex-column flex-shrink-0 p-3 mr-5">
<hr>
{% if shop_enabled %}
<a href={% url 'cart' %} alt="Koszyk" > Koszyk </a>
{% endif %}
</div>

Wyświetl plik

@ -1,7 +0,0 @@
{% load menu_tags %}
<ul class={% if item.active_class %} "sub-menu collapse show fw-normal pb-1 small" {% else %} "sub-menu collapse fw-normal pb-1 small" {% endif %}
id="ddtoggle_{{ item.link_page.pk }}">
{% for sub_item in menu_items %}
<li><a href="{{ sub_item.href }}" class="{{ sub_item.active_class }}">{{ sub_item.text }}</a></li>
{% endfor %}
</ul>

Wyświetl plik

@ -1,57 +0,0 @@
version: "3.8"
services:
db:
image: postgres
restart: always
environment:
- POSTGRES_ROOT_PASSWORD
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_DB
volumes:
- ../postgres/:/var/lib/postgresql
env_file:
- .env
networks:
- nginx_network
comfy:
build:
dockerfile: Dockerfile
context: ./
user: "${UID}:${GID}"
restart: always
ports:
- "8001:8000"
volumes:
- ./:/app
environment:
- SECRET_KEY
- DATABASE_URL
- DJANGO_SETTINGS_MODULE
env_file:
- .env
depends_on:
- db
networks:
- nginx_network
web:
image: nginx
restart: always
volumes:
- ../nginx/conf.d/:/etc/nginx/conf.d/
- ./static/:/opt/services/comfy/static
- ./media/:/opt/services/comfy/media
ports:
- "8000:80"
environment:
- NGINX_HOST=artel.tepewu.pl
- NGINX_PORT=80
networks:
- nginx_network
networks:
nginx_network:
driver: bridge

Wyświetl plik

@ -13,7 +13,8 @@ steps:
image: docker:24.0.6
secrets: []
commands:
- docker compose -f artel/docker-compose-test.yml run --rm test_comfy
- docker compose -f artel/docker-compose-test.yml run test_comfy
- docker compose -f ./artel/docker-compose-test.yml down
volumes:
- /var/run/docker.sock:/var/run/docker.sock
when:

Wyświetl plik

@ -28,6 +28,9 @@ RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-r
# Install the application server.
RUN pip install "gunicorn==20.0.4"
# Install gettext
RUN apt-get update && apt-get install -y gettext
# Install the project requirements.
COPY requirements.txt /
RUN pip install -r /requirements.txt

Wyświetl plik

@ -21,7 +21,10 @@ RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-r
libwebp-dev \
wkhtmltopdf \
&& rm -rf /var/lib/apt/lists/*
# Install gettext
RUN apt-get update && apt-get install -y gettext
# Install the project requirements.
COPY requirements.txt /
COPY requirements_dev.txt /

Wyświetl plik

@ -0,0 +1,110 @@
version: "3.8"
services:
db:
image: postgres
restart: always
environment:
- POSTGRES_ROOT_PASSWORD
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_DB
volumes:
- ../postgres/:/var/lib/postgresql
env_file:
- .env
networks:
- nginx_network
rabbit:
hostname: rabbit
image: rabbitmq:3.6.0
environment:
- RABBITMQ_DEFAULT_USER
- RABBITMQ_DEFAULT_PASS
ports:
- "5672:5672" # We forward this port because it's useful for debugging
- "15672:15672" # Here, we can access RabbitMQ management plugin
networks:
- nginx_network
comfy:
build:
dockerfile: Dockerfile
context: ./
user: "${UID}:${GID}"
restart: always
ports:
- "8001:8000"
volumes:
- ./:/app
environment:
- SECRET_KEY
- DATABASE_URL
- DJANGO_SETTINGS_MODULE
env_file:
- .env
depends_on:
- db
networks:
- nginx_network
web:
image: nginx
restart: always
volumes:
- ../nginx/conf.d/:/etc/nginx/conf.d/
- ./static/:/opt/services/comfy/static
- ./media/:/opt/services/comfy/media
ports:
- "8000:80"
environment:
- NGINX_HOST=artel.tepewu.pl
- NGINX_PORT=80
networks:
- nginx_network
beat:
build:
context: .
dockerfile: Dockerfile
command: celery -A artel beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
volumes:
- ./:/app
env_file:
- .env
environment:
- SECRET_KEY
- DATABASE_URL
depends_on:
- comfy
- rabbit
- db
networks:
- nginx_network
worker:
build:
context: .
dockerfile: Dockerfile
command: celery -A artel worker -l info
volumes:
- ./:/app
- ./media:/app/media
env_file:
- .env
environment:
- SECRET_KEY
- DATABASE_URL
depends_on:
- comfy
- rabbit
- db
- beat
networks:
- nginx_network
networks:
nginx_network:
driver: bridge

Wyświetl plik

@ -20,9 +20,9 @@ services:
environment:
- RABBITMQ_DEFAULT_USER
- RABBITMQ_DEFAULT_PASS
ports:
- "5672:5672" # We forward this port because it's useful for debugging
- "15672:15672" # Here, we can access RabbitMQ management plugin
# ports:
# - "5672:5672" # We forward this port because it's useful for debugging
# - "15672:15672" # Here, we can access RabbitMQ management plugin
smtp-server:
image: mailhog/mailhog

Wyświetl plik

@ -0,0 +1,45 @@
from wagtail.contrib.modeladmin.options import (
ModelAdmin,
ModelAdminGroup,
modeladmin_register
)
from dynamic_forms import models
class CustomEmailFormAdmin(ModelAdmin):
model = models.CustomEmailForm
menu_label = "Email Forms"
menu_icon = 'mail'
menu_order = 100
add_to_settings_menu = False
exclude_from_explorer = False
list_display = (
"title",
"slug",
)
search_fields = (
"title",
"slug",
)
list_filter = (
"title",
"slug",
)
form_fields = (
"slug",
"intro",
"thank_you_text",
"from_address",
"to_address",
"subject",
)
class CustomFormGroup(ModelAdminGroup):
menu_label = "Custom Forms"
menu_icon = 'tasks'
menu_order = 100
items = (
CustomEmailFormAdmin,
)
modeladmin_register(CustomFormGroup)

Wyświetl plik

@ -0,0 +1,6 @@
from django.apps import AppConfig
class DynamicFormsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "dynamic_forms"

Wyświetl plik

@ -0,0 +1,99 @@
from django import forms
from dynamic_forms.widgets import (
CheckboxSelectMultiple,
CheckboxInput,
RadioSelect
)
class MultipleFileInput(forms.ClearableFileInput):
allow_multiple_selected = True
class MultipleFileField(forms.FileField):
def __init__(self, *args, **kwargs):
kwargs.setdefault("widget", MultipleFileInput())
super().__init__(*args, **kwargs)
def clean(self, data, initial=None):
single_file_clean = super().clean
if isinstance(data, (list, tuple)):
result = [single_file_clean(d, initial) for d in data]
else:
result = single_file_clean(data, initial)
return result
class HoneypotField(forms.BooleanField):
default_widget = forms.HiddenInput(
{'style': 'display:none !important;', 'tabindex': '-1', 'autocomplete': 'off'}
)
def __init__(self, *args, **kwargs):
kwargs.setdefault('widget', HoneypotField.default_widget)
kwargs['required'] = False
super().__init__(*args, **kwargs)
def clean(self, value):
if cleaned_value := super().clean(value):
raise forms.ValidationError('')
else:
return cleaned_value
class DynamicForm(forms.Form):
FIELD_TYPE_MAPPING = {
"singleline": forms.CharField(max_length=50, widget=forms.TextInput(attrs={"class": "form-control"})),
"multiline": forms.CharField(max_length=255, widget=forms.Textarea(attrs={"class": "form-control"})),
"email": forms.EmailField(max_length=255, widget=forms.EmailInput(attrs={"class": "form-control"})),
"number": forms.IntegerField(widget=forms.NumberInput(attrs={"class": "form-control"})),
"url": forms.URLField(max_length=255, widget=forms.URLInput(attrs={"class": "form-control"})),
"checkbox": forms.BooleanField(required=False, widget=CheckboxInput(attrs={"class": "form-check"})),
"checkboxes": forms.MultipleChoiceField(required=False, widget=CheckboxSelectMultiple(attrs={"class": "form-check"})),
"dropdown": forms.ChoiceField(widget=forms.Select(attrs={"class": "form-control"})),
"multiselect": forms.MultipleChoiceField(widget=forms.SelectMultiple(attrs={"class": "form-control"})),
"radio": forms.ChoiceField(widget=RadioSelect(attrs={"class": "form-control"})),
"date": forms.DateField(widget=forms.DateInput(attrs={"class": "form-control"})),
"datetime": forms.DateTimeField(widget=forms.DateTimeInput(attrs={"class": "form-control"})),
"hidden": forms.CharField(widget=forms.HiddenInput()),
}
def __init__(self, *args, **kwargs) -> None:
kwargs.pop("page", "")
kwargs.pop("user", "")
field_list = kwargs.pop("field_list")
file_uploads = kwargs.pop("file_uploads", False)
super().__init__(*args, **kwargs)
for field in field_list:
f = self.FIELD_TYPE_MAPPING[field.field_type]
f.label = field.label
if hasattr(f, "choices"):
f.choices = [(v, v) for v in field.choices.split(",")]
f.required = field.required
f.help_text = field.help_text or ""
self.fields[field.clean_name] = f
if file_uploads:
self.fields["attachments"] = MultipleFileField(
required=True, widget=MultipleFileInput(
attrs={"class": "form-control"}
)
)
# add honeypot field
self.fields["secret_honey"] = HoneypotField()
def clean(self):
cleaned_data = super().clean()
new_cleaned_data = {}
for key, value in cleaned_data.items():
if isinstance(value, list):
if isinstance(self.fields[key], MultipleFileField):
continue
cleaned_data[key] = ",".join(value)
if key=="secret_honey":
continue
new_cleaned_data[key] = value
return new_cleaned_data

Wyświetl plik

@ -0,0 +1,132 @@
# Generated by Django 4.1.11 on 2023-10-12 17:23
import django.core.serializers.json
from django.db import migrations, models
import django.db.models.deletion
import modelcluster.fields
import wagtail.contrib.forms.models
import wagtail.fields
class Migration(migrations.Migration):
initial = True
dependencies = [
("wagtailcore", "0083_workflowcontenttype"),
]
operations = [
migrations.CreateModel(
name="CustomEmailForm",
fields=[
(
"page_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="wagtailcore.page",
),
),
("intro", wagtail.fields.RichTextField(blank=True)),
("thank_you_text", wagtail.fields.RichTextField(blank=True)),
("allow_attachments", models.BooleanField(default=False)),
("from_address", models.EmailField(blank=True, help_text="Sender email address", max_length=254)),
("to_address", models.CharField(help_text="Comma separated list of recipients", max_length=255)),
("subject", models.CharField(help_text="Subject of the email with data", max_length=255)),
],
options={
"abstract": False,
},
bases=(wagtail.contrib.forms.models.FormMixin, "wagtailcore.page"),
),
migrations.CreateModel(
name="EmailFormSubmission",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("form_data", models.JSONField(encoder=django.core.serializers.json.DjangoJSONEncoder)),
("submit_time", models.DateTimeField(auto_now_add=True, verbose_name="submit time")),
("page", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="wagtailcore.page")),
],
options={
"verbose_name": "form submission",
"verbose_name_plural": "form submissions",
"abstract": False,
},
),
migrations.CreateModel(
name="EmailFormField",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("sort_order", models.IntegerField(blank=True, editable=False, null=True)),
(
"clean_name",
models.CharField(
blank=True,
default="",
help_text="Safe name of the form field, the label converted to ascii_snake_case",
max_length=255,
verbose_name="name",
),
),
(
"label",
models.CharField(help_text="The label of the form field", max_length=255, verbose_name="label"),
),
(
"field_type",
models.CharField(
choices=[
("singleline", "Single line text"),
("multiline", "Multi-line text"),
("email", "Email"),
("number", "Number"),
("url", "URL"),
("checkbox", "Checkbox"),
("checkboxes", "Checkboxes"),
("dropdown", "Drop down"),
("multiselect", "Multiple select"),
("radio", "Radio buttons"),
("date", "Date"),
("datetime", "Date/time"),
("hidden", "Hidden field"),
],
max_length=16,
verbose_name="field type",
),
),
("required", models.BooleanField(default=True, verbose_name="required")),
(
"choices",
models.TextField(
blank=True,
help_text="Comma or new line separated list of choices. Only applicable in checkboxes, radio and dropdown.",
verbose_name="choices",
),
),
(
"default_value",
models.TextField(
blank=True,
help_text="Default value. Comma or new line separated values supported for checkboxes.",
verbose_name="default value",
),
),
("help_text", models.CharField(blank=True, max_length=255, verbose_name="help text")),
(
"form",
modelcluster.fields.ParentalKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="form_fields",
to="dynamic_forms.customemailform",
),
),
],
options={
"ordering": ["sort_order"],
"abstract": False,
},
),
]

Wyświetl plik

@ -0,0 +1,130 @@
import datetime
from django.db import models
from django.conf import settings
from django.utils.formats import date_format
from modelcluster.fields import ParentalKey
from wagtail.admin.panels import (
FieldPanel, FieldRowPanel,
InlinePanel, MultiFieldPanel
)
from wagtail.fields import RichTextField
from wagtail.contrib.forms.models import (
AbstractFormField,
FormMixin,
Page,
AbstractFormSubmission
)
from mailings.models import (
OutgoingEmail,
Attachment
)
from dynamic_forms.forms import DynamicForm
class Form(FormMixin, Page):
intro = RichTextField(blank=True)
thank_you_text = RichTextField(blank=True)
allow_attachments = models.BooleanField(default=False)
content_panels = Page.content_panels + [
FieldPanel('intro'),
InlinePanel('form_fields', label="Form fields"),
FieldPanel('thank_you_text'),
MultiFieldPanel([
FieldRowPanel([
FieldPanel('from_address', classname="col6"),
FieldPanel('to_address', classname="col6"),
]),
FieldPanel('subject'),
], "Email"),
FieldPanel("allow_attachments")
]
def get_form_class(self):
return DynamicForm
def get_form(self, *args, **kwargs):
form_class = self.get_form_class()
form_params = self.get_form_parameters()
form_params.update(kwargs)
form_params["field_list"] = self.get_form_fields()
form_params["file_uploads"] = self.allow_attachments
return form_class(*args, **form_params)
class Meta:
abstract = True
class EmailFormSubmission(AbstractFormSubmission):
# TODO - make this optional, allow to set pattern in admin
def get_submission_id(self, form_slug):
case_number_daily = EmailFormSubmission.objects.filter(submit_time__date=datetime.date.today()).count()
return f"{form_slug}-{datetime.date.today()}-{case_number_daily}"
def send_mail(self, data):
# modify this, get proper template
to_addresses = data.pop("to_address").split(",")
attachments = [
Attachment(
file.name, file.file.read(), file.content_type
)
for file in data.pop("attachments", [])
]
subject = data.pop("subject")
form_slug = data.pop("form_slug")
from_address = data.pop("from_address", settings.DEFAULT_FROM_EMAIL)
for address in to_addresses:
OutgoingEmail.objects.send(
subject=subject,
template_name="form_mail",
recipient=address,
sender=from_address,
context={"form_data": data, "submission_id": self.get_submission_id(form_slug)},
attachments=attachments
)
class CustomEmailForm(Form):
from_address = models.EmailField(
blank=True,
help_text="Sender email address"
)
to_address = models.CharField(
max_length=255,
help_text="Comma separated list of recipients"
)
subject = models.CharField(
max_length=255,
help_text="Subject of the email with data"
)
template = "forms/email_form_page.html"
def get_submission_class(self):
return EmailFormSubmission
def process_form_submission(self, form):
attachments = form.cleaned_data.pop("attachments", [])
submission = self.get_submission_class().objects.create(
form_data=form.cleaned_data,
page=self,
)
mail_data = form.cleaned_data.copy()
mail_data.update({
"from_address": self.from_address,
"to_address": self.to_address,
"subject": self.subject,
"attachments": attachments,
"form_slug": self.slug
})
submission.send_mail(data=mail_data)
return submission
class EmailFormField(AbstractFormField):
form = ParentalKey(
"CustomEmailForm", related_name="form_fields", on_delete=models.CASCADE
)

Wyświetl plik

@ -0,0 +1,40 @@
{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}
{% load i18n %}
{% block content %}
<h1>{{ page.title }}</h1>
<p class="meta">{{ page.date }}</p>
{% if form %}
{{form.errors}}
<div>{{ page.intro|richtext }}</div>
<form enctype="multipart/form-data" action="{% pageurl page %}" method="POST">
{% csrf_token %}
{% for field in form %}
{% if field.is_hidden %}
<!--Empty space for reason, we don't want to show anything on empty fields-->
{{field}}
{% else %}
<div class="form-group mt-3">
<label for="{{field.id}}" class="form-label">
{% trans field.label %}
</label>
{{field}}
<small id="emailHelp" class="form-text text-muted">
{% trans field.help_text %}
</small>
{% if field.error %}
{{error}}
{% endif %}
</div>
{% endif %}
{% endfor %}
<div class="text-end mt-3">
<input class="btn btn-lg btn-success" type="submit" value='{% trans "Submit" %}'>
</div>
</form>
{% else %}
<div>You can fill in the from only one time.</div>
{% endif %}
{% endblock %}

Wyświetl plik

@ -0,0 +1,25 @@
{% extends 'base.html' %}
{% load i18n %}
{% load wagtailcore_tags wagtailimages_tags %}
{% block content %}
<div class="d-flex justify-content-center align-items-center">
<div class="card col-9 bg-white shadow-md p-5">
<div class="mb-4 text-center">
<svg class="text-success" width="75" height="75"
fill="currentColor" class="bi bi-check-circle" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
<path
d="M10.97 4.97a.235.235 0 0 0-.02.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-1.071-1.05z" />
</svg>
</div>
<div class="text-center">
<div>
{{page.thank_you_text|richtext}}
</div>
<a class="btn btn-outline-primary mt-3" href="/">
{% trans "Homepage" %}
</a>
</div>
</div>
{% endblock %}

Wyświetl plik

@ -0,0 +1,7 @@
<div class="form-check">
<input class="form-check-input" type="checkbox" id="{{ widget.attrs.id }}"
name="{{ widget.name }}">
<label class="form-check-label" for="{{ widget.attrs.id }}">
{{ widget.name }}
</label>
</div>

Wyświetl plik

@ -0,0 +1,14 @@
{% for group, options, index in widget.optgroups %}
{% if group %}
<label>{{ group }}</label>
{% endif %}
{% for option in options %}
<div class="form-check">
<input class="form-check-input" type="checkbox" value="{{ option.value }}" id="{{ option.attrs.id }}"
name="{{ option.name }}">
<label class="form-check-label" for="{{ option.attrs.id }}">
{{ option.value }}
</label>
</div>
{% endfor %}
{% endfor %}

Wyświetl plik

@ -0,0 +1,15 @@
{% for group, options, index in widget.optgroups %}
{% if group %}
<label>{{ group }}</label>
{% endif %}
{% for option in options %}
<div class="form-check">
<input class="form-check-input" type="radio"
value="{{ option.value }}" id="{{ option.attrs.id }}"
name="{{ option.name }}">
<label class="form-check-label" for="{{ option.attrs.id }}">
{{ option.value }}
</label>
</div>
{% endfor %}
{% endfor %}

Wyświetl plik

@ -0,0 +1,304 @@
from wagtail.tests.utils import WagtailPageTests
from django import forms
from django.core.files.uploadedfile import SimpleUploadedFile
from dynamic_forms.models import (
CustomEmailForm,
EmailFormField
)
from dynamic_forms.forms import DynamicForm
class CustomEmailFormTestCase(WagtailPageTests):
def setUp(self):
self.form = CustomEmailForm.objects.create(
slug="test", title="Test Form", path="test",
depth=0, numchild=0, live=True, has_unpublished_changes=False,
from_address="comfy-test@egalitare.pl",
to_address="comfy-dest@egalitare.pl",
subject="Test Form", allow_attachments=False,
)
EmailFormField.objects.create(
label="Name",
field_type="singleline",
required=True,
form=self.form
)
EmailFormField.objects.create(
label="Message",
field_type="multiline",
required=True,
form=self.form
)
EmailFormField.objects.create(
label="Email",
field_type="email",
required=True,
form=self.form
)
EmailFormField.objects.create(
label="Number",
field_type="number",
required=True,
form=self.form
)
EmailFormField.objects.create(
label="URL",
field_type="url",
required=True,
form=self.form
)
EmailFormField.objects.create(
label="Checkbox",
field_type="checkbox",
required=True,
form=self.form
)
EmailFormField.objects.create(
label="Checkboxes",
field_type="checkboxes",
required=True,
choices="a,b,c",
form=self.form
)
EmailFormField.objects.create(
label="Dropdown",
field_type="dropdown",
required=True,
choices="a,b,c",
form=self.form
)
EmailFormField.objects.create(
label="MultiSelect",
field_type="multiselect",
required=True,
choices="a,b,c",
form=self.form
)
EmailFormField.objects.create(
label="Radio",
field_type="radio",
required=True,
choices="a,b,c",
form=self.form
)
EmailFormField.objects.create(
label="Date",
field_type="date",
required=True,
form=self.form
)
EmailFormField.objects.create(
label="DateTime",
field_type="datetime",
required=True,
form=self.form
)
EmailFormField.objects.create(
label="Hidden",
field_type="hidden",
required=True,
form=self.form
)
def test_generate_html_form_from_model(self):
html_form = self.form.get_form()
self.assertIsInstance(html_form, DynamicForm)
self.assertEqual(len(html_form.fields), 14)
self.assertEqual(html_form.fields["name"].label, "Name")
self.assertEqual(html_form.fields["name"].required, True)
self.assertEqual(html_form.fields["name"].widget.attrs["class"], "form-control")
self.assertIsInstance(
html_form.fields["name"],
forms.CharField
)
self.assertIsInstance(
html_form.fields["message"],
forms.CharField
)
self.assertIsInstance(
html_form.fields["email"].widget,
forms.EmailInput
)
self.assertIsInstance(
html_form.fields["number"].widget,
forms.NumberInput
)
def test_create_form_submission_success_without_files(self):
form_data = {
# generate data for this class self.form.get_form()
"name": "Test",
"message": "Test message",
"email": "test@test.com",
"number": 1,
"url": "http://example.com",
"checkbox": True,
"checkboxes": ["a", "b"],
"dropdown": "a",
"multiselect": ["a", "b"],
"radio": "a",
"date": "2020-01-01",
"datetime": "2020-01-01 00:00:00",
"hidden": "hidden",
}
form = self.form.get_form(form_data)
self.assertTrue(form.is_valid())
# change field not to be required
field = EmailFormField.objects.get(
label="Name", form=self.form
)
field.required = False
field.save()
form = self.form.get_form(form_data)
self.assertTrue(form.is_valid())
# it should also work without this field
form_data.pop("name")
form = self.form.get_form(form_data)
self.assertTrue(form.is_valid())
def test_create_form_submission_success_with_files(self):
self.form.allow_attachments = True
self.form.save()
form_data = {
# generate data for this class self.form.get_form()
"name": "Test",
"message": "Test message",
"email": "test@test.com",
"number": 1,
"url": "http://example.com",
"checkbox": True,
"checkboxes": ["a", "b"],
"dropdown": "a",
"multiselect": ["a", "b"],
"radio": "a",
"date": "2020-01-01",
"datetime": "2020-01-01 00:00:00",
"hidden": "hidden"
}
files = {
"attachments": [SimpleUploadedFile("test.txt", b"test")],
}
form = self.form.get_form(form_data, files=files)
self.assertTrue(form.is_valid())
def test_create_form_submission_failure_without_files_missing_data(self):
form_data = {
# generate data for this class self.form.get_form()
"message": "Test message",
"email": "test@test.com",
"number": 1,
"url": "http://example.com",
"checkbox": True,
"checkboxes": ["a", "b"],
"dropdown": "a",
"multiselect": ["a", "b"],
"radio": "a",
"date": "2020-01-01",
"datetime": "2020-01-01 00:00:00",
"hidden": "hidden",
}
form = self.form.get_form(form_data)
self.assertFalse(form.is_valid())
self.assertEqual(len(form.errors), 1)
self.assertEqual(form.errors["name"], ['This field is required.'])
form_data.pop("url")
form = self.form.get_form(form_data)
self.assertFalse(form.is_valid())
self.assertEqual(len(form.errors), 2)
self.assertEqual(form.errors["name"], ['This field is required.'])
self.assertEqual(form.errors["url"], ['This field is required.'])
# make Field not required
field = EmailFormField.objects.get(
label="Hidden", form=self.form
)
field.required = False
field.save()
# it should also work without this field
form_data.pop("hidden")
form = self.form.get_form(form_data)
self.assertFalse(form.is_valid())
self.assertEqual(len(form.errors), 2)
self.assertEqual(form.errors["name"], ['This field is required.'])
self.assertEqual(form.errors["url"], ['This field is required.'])
def test_create_form_submission_failure_with_files_missing_data(self):
self.form.allow_attachments = True
self.form.save()
form_data = {
# generate data for this class self.form.get_form()
"message": "Test message",
"email": "test@test.com",
"number": 1,
"url": "http://example.com",
"checkbox": True,
"checkboxes": ["a", "b"],
"dropdown": "a",
"multiselect": ["a", "b"],
"radio": "a",
"date": "2020-01-01",
"datetime": "2020-01-01 00:00:00",
"hidden": "hidden",
}
files = {
"attachments": [SimpleUploadedFile("test.txt", b"test")],
}
form = self.form.get_form(form_data, files=files)
self.assertFalse(form.is_valid())
self.assertEqual(len(form.errors), 1)
self.assertEqual(form.errors["name"], ['This field is required.'])
form_data.pop("url")
form = self.form.get_form(form_data, files=files)
self.assertFalse(form.is_valid())
self.assertEqual(len(form.errors), 2)
self.assertEqual(form.errors["name"], ['This field is required.'])
self.assertEqual(form.errors["url"], ['This field is required.'])
# make Field not required
field = EmailFormField.objects.get(
label="Hidden", form=self.form
)
field.required = False
field.save()
# it should also work without this field
form_data.pop("hidden")
form = self.form.get_form(form_data, files=files)
self.assertFalse(form.is_valid())
self.assertEqual(len(form.errors), 2)
self.assertEqual(form.errors["name"], ['This field is required.'])
self.assertEqual(form.errors["url"], ['This field is required.'])
# Now try without files
form = self.form.get_form(form_data, files={})
self.assertFalse(form.is_valid())
self.assertEqual(len(form.errors), 3)
self.assertEqual(form.errors["name"], ['This field is required.'])
self.assertEqual(form.errors["url"], ['This field is required.'])
self.assertEqual(form.errors["attachments"], ['This field is required.'])
def test_no_hidden_field_in_clean_data_success(self):
form_data = {
# generate data for this class self.form.get_form()
"name": "Test",
"message": "Test message",
"email": "test@test.com",
"number": 1,
"url": "http://example.com",
"checkbox": True,
"checkboxes": ["a", "b"],
"dropdown": "a",
"multiselect": ["a", "b"],
"radio": "a",
"date": "2020-01-01",
"datetime": "2020-01-01 00:00:00",
"hidden": "hidden",
}
form = self.form.get_form(form_data)
self.assertTrue(form.is_valid())
cleaned_data = form.cleaned_data
self.assertIn("hidden", cleaned_data)
self.assertNotIn("secret_honey", cleaned_data)
self.assertIn("hidden", form.fields)
self.assertIn("secret_honey", form.fields)

Wyświetl plik

@ -0,0 +1,13 @@
from django import forms
class CheckboxInput(forms.CheckboxInput):
template_name = "widgets/checkbox.html"
class CheckboxSelectMultiple(forms.CheckboxSelectMultiple):
template_name = "widgets/checkbox_multiple.html"
class RadioSelect(forms.RadioSelect):
template_name = "widgets/radio_multiple.html"

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1,189 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-20 13:43+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n"
"%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n"
"%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
#: artel/templates/menu/custom_main_menu.html:33
msgid "Cart"
msgstr "Koszyk"
#: home/templates/home/welcome_page.html:6
msgid "Visit the Wagtail website"
msgstr ""
#: home/templates/home/welcome_page.html:15
msgid "View the release notes"
msgstr ""
#: home/templates/home/welcome_page.html:27
msgid "Welcome to your new Wagtail site!"
msgstr ""
#: home/templates/home/welcome_page.html:28
msgid ""
"Please feel free to <a href=\"https://github.com/wagtail/wagtail/wiki/Slack"
"\">join our community on Slack</a>, or get started with one of the links "
"below."
msgstr ""
#: home/templates/home/welcome_page.html:35
msgid "Wagtail Documentation"
msgstr ""
#: home/templates/home/welcome_page.html:36
msgid "Topics, references, & how-tos"
msgstr ""
#: home/templates/home/welcome_page.html:42
msgid "Tutorial"
msgstr ""
#: home/templates/home/welcome_page.html:43
msgid "Build your first Wagtail site"
msgstr ""
#: home/templates/home/welcome_page.html:49
msgid "Admin Interface"
msgstr ""
#: home/templates/home/welcome_page.html:50
msgid "Create your superuser first!"
msgstr ""
#: search/templates/search/search.html:10
#: search/templates/search/search.html:14
msgid "Search"
msgstr "Wyszukaj"
#: search/templates/search/search.html:30
#: store/templates/store/product_list_page.html:39
msgid "Previous"
msgstr "Poprzednia"
#: search/templates/search/search.html:34
#: store/templates/store/product_list_page.html:45
msgid "Next"
msgstr "Następna"
#: search/templates/search/search.html:37
msgid "No results found"
msgstr "Nie znaleziono wyników"
#: store/forms.py:21
msgid "Name"
msgstr "Imię"
#: store/forms.py:25
msgid "Surname"
msgstr "Nazwisko"
#: store/forms.py:28 store/templates/store/order_confirm.html:42
msgid "Address"
msgstr "Adres"
#: store/forms.py:31
msgid "City"
msgstr "Miasto"
#: store/forms.py:34
msgid "Zip-code"
msgstr "Kod pocztowy"
#: store/forms.py:37
msgid "E-mail"
msgstr "Email"
#: store/forms.py:40
msgid "Phone number"
msgstr "Numer telefonu"
#: store/forms.py:43
msgid "Polska"
msgstr ""
#: store/forms.py:43
msgid "Country"
msgstr "Kraj"
#: store/templates/store/cart.html:9
msgid "Shopping Cart"
msgstr "Koszyk"
#: store/templates/store/cart.html:20
#: store/templates/store/order_confirm.html:66
msgid "To Pay:"
msgstr "Do zapłaty:"
#: store/templates/store/cart.html:23
msgid "Proceed to Pay"
msgstr "Przejdź do płatności"
#: store/templates/store/order.html:12
msgid "Order Data"
msgstr "Dane zamówienia"
#: store/templates/store/order.html:13
msgid "We won't share your data"
msgstr "Nie udostępnimy Twoich danych"
#: store/templates/store/order.html:58
msgid "Contact Details"
msgstr "Dane kontaktowe"
#: store/templates/store/order.html:88
#: store/templates/store/order_confirm.html:69
msgid "Confirm"
msgstr "Potwierdź"
#: store/templates/store/order_confirm.html:9
msgid "Customer Data"
msgstr "Dane klienta"
#: store/templates/store/order_confirm.html:15
msgid "Full Name"
msgstr "Pełne imię"
#: store/templates/store/order_confirm.html:24
msgid "Email"
msgstr "Email"
#: store/templates/store/order_confirm.html:33
msgid "Phone"
msgstr "Numer Telefonu"
#: store/templates/store/order_confirm.html:55
msgid "Order Items"
msgstr "Zamawiane przedmioty"
#: store/templates/store/product_list_page.html:10
msgid "Added to cart"
msgstr "Dodano do koszyka"
#: store/templates/store/product_list_page.html:16
msgid "Item has been added to cart."
msgstr "Przedmiot został dodany do koszyka"
#: store/templates/store/product_list_page.html:19
msgid "Continue shopping"
msgstr "Kontynuuj zakupy"
#: store/templates/store/product_list_page.html:20
msgid "Go to cart"
msgstr "Idź do koszyka"

Wyświetl plik

@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

Wyświetl plik

@ -3,7 +3,7 @@ import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "artel.settings.dev")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "wagtail_store.settings.dev")
from django.core.management import execute_from_command_line

Wyświetl plik

@ -1,5 +1,5 @@
Django>=4.1,<4.2
wagtail>=4.2,<4.3
Django==4.2.6
wagtail==5.1.2
wagtailmenus>=3.1.5,<=3.1.7
psycopg2-binary>=2.9.5,<=2.9.6
dj-database-url<=2.0.0
@ -15,3 +15,4 @@ easy_thumbnails==2.8.5
num2words==0.5.12
sentry-sdk==1.28.0
pandas==2.0.3
wagtail-localize==1.5.2

Wyświetl plik

@ -1,16 +1,17 @@
{% extends "base.html" %}
{% load static wagtailcore_tags %}
{% load i18n %}
{% block body_class %}template-searchresults{% endblock %}
{% block title %}Search{% endblock %}
{% block content %}
<h1>Search</h1>
<h1>{% trans "Search" %}</h1>
<form action="{% url 'search' %}" method="get">
<input type="text" name="query"{% if search_query %} value="{{ search_query }}"{% endif %}>
<input type="submit" value="Search" class="button">
<input type="submit" value=" {% trans "Search" %}" class="button">
</form>
{% if search_results %}
@ -26,13 +27,13 @@
</ul>
{% if search_results.has_previous %}
<a href="{% url 'search' %}?query={{ search_query|urlencode }}&amp;page={{ search_results.previous_page_number }}">Previous</a>
<a href="{% url 'search' %}?query={{ search_query|urlencode }}&amp;page={{ search_results.previous_page_number }}">{% trans "Previous" %}</a>
{% endif %}
{% if search_results.has_next %}
<a href="{% url 'search' %}?query={{ search_query|urlencode }}&amp;page={{ search_results.next_page_number }}">Next</a>
<a href="{% url 'search' %}?query={{ search_query|urlencode }}&amp;page={{ search_results.next_page_number }}">{% trans "Next" %}</a>
{% endif %}
{% elif search_query %}
No results found
{% trans "No results found" %}
{% endif %}
{% endblock %}

Wyświetl plik

@ -11,41 +11,42 @@ from store.models import (
DeliveryMethod
)
from django.utils.translation import gettext_lazy as _
class CustomerDataForm(forms.Form):
name = forms.CharField(
max_length=255, label="Imię", widget=forms.TextInput(attrs={"class": "form-control"})
max_length=255, label=_("Name"), widget=forms.TextInput(attrs={"class": "form-control"})
)
surname = forms.CharField(
max_length=255, label="Nazwisko", widget=forms.TextInput(attrs={"class": "form-control"})
max_length=255, label=_("Surname"), widget=forms.TextInput(attrs={"class": "form-control"})
)
street = forms.CharField(
max_length=255, label="Adres", widget=forms.TextInput(attrs={"class": "form-control"})
max_length=255, label=_("Address"), widget=forms.TextInput(attrs={"class": "form-control"})
)
city = forms.CharField(
max_length=255, label="Miasto", widget=forms.TextInput(attrs={"class": "form-control"})
max_length=255, label=_("City"), widget=forms.TextInput(attrs={"class": "form-control"})
)
zip_code = forms.CharField(
max_length=255, label="Kod pocztowy", widget=forms.TextInput(attrs={"class": "form-control"})
max_length=255, label=_("Zip-code"), widget=forms.TextInput(attrs={"class": "form-control"})
)
email = forms.EmailField(
max_length=255, label="E-mail", widget=forms.EmailInput(attrs={"class": "form-control"})
max_length=255, label=_("E-mail"), widget=forms.EmailInput(attrs={"class": "form-control"})
)
phone = PhoneNumberField(
region="PL", label="Numer telefonu", widget=forms.TextInput(attrs={"class": "form-control"})
region="PL", label=_("Phone number"), widget=forms.TextInput(attrs={"class": "form-control"})
)
country = forms.ChoiceField(
choices=(("PL", "Polska"), ), label="Kraj",
choices=(("PL", _("Polska")), ), label=_("Country"),
widget=forms.Select(attrs={"class": "form-control"})
)
payment_method = forms.ModelChoiceField(
queryset=PaymentMethod.objects.filter(active=True), label="Sposób płatności",
widget=forms.Select(attrs={"class": "form-control"})
)
delivery_method = forms.ModelChoiceField(
queryset=DeliveryMethod.objects.filter(active=True), label="Sposób dostawy",
widget=forms.Select(attrs={"class": "form-control"})

Some files were not shown because too many files have changed in this diff Show More