Features: 1) 2.8.9 update
Fixes: 1) wtf go read diff; Extra: ???
This commit is contained in:
parent
6ce7b7a6f9
commit
328ccaa615
373 changed files with 13931 additions and 20027 deletions
|
|
@ -81,4 +81,5 @@ media/
|
||||||
.env
|
.env
|
||||||
|
|
||||||
# Host's scripts
|
# Host's scripts
|
||||||
scripts
|
scripts/Windows
|
||||||
|
scripts/Unix
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -60,7 +60,6 @@ htmlcov/
|
||||||
.tox/
|
.tox/
|
||||||
.nox/
|
.nox/
|
||||||
.scrapy
|
.scrapy
|
||||||
.coverage.*
|
|
||||||
.cover
|
.cover
|
||||||
.pybuilder/
|
.pybuilder/
|
||||||
|
|
||||||
|
|
|
||||||
49
Dockerfiles/Dockerfile.app
Normal file
49
Dockerfiles/Dockerfile.app
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM python:3.12-bookworm
|
||||||
|
LABEL authors="fureunoir"
|
||||||
|
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PYTHONUNBUFFERED=1 \
|
||||||
|
LANG=C.UTF-8 \
|
||||||
|
DEBIAN_FRONTEND=noninteractive \
|
||||||
|
PATH="/root/.local/bin:$PATH"
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
sed -i 's|https://deb.debian.org/debian|https://ftp.uk.debian.org/debian|g' /etc/apt/sources.list.d/debian.sources; \
|
||||||
|
apt-get update; \
|
||||||
|
apt-get install -y --no-install-recommends wget gnupg; \
|
||||||
|
wget -qO - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -; \
|
||||||
|
echo "deb http://apt.postgresql.org/pub/repos/apt bookworm-pgdg main" \
|
||||||
|
> /etc/apt/sources.list.d/pgdg.list; \
|
||||||
|
apt-get update; \
|
||||||
|
apt-get install -y --no-install-recommends --fix-missing \
|
||||||
|
build-essential \
|
||||||
|
libpq-dev \
|
||||||
|
gettext \
|
||||||
|
libgettextpo-dev \
|
||||||
|
graphviz-dev \
|
||||||
|
libgts-dev \
|
||||||
|
libpq5 \
|
||||||
|
graphviz \
|
||||||
|
binutils \
|
||||||
|
libproj-dev \
|
||||||
|
postgresql-client-17 \
|
||||||
|
gdal-bin; \
|
||||||
|
rm -rf /var/lib/apt/lists/*; \
|
||||||
|
pip install --upgrade pip; \
|
||||||
|
curl -sSL https://install.python-poetry.org | python3
|
||||||
|
|
||||||
|
COPY pyproject.toml pyproject.toml
|
||||||
|
COPY poetry.lock poetry.lock
|
||||||
|
|
||||||
|
RUN poetry config virtualenvs.create false
|
||||||
|
RUN poetry install --extras="graph worker openai testing" --no-interaction --no-ansi
|
||||||
|
|
||||||
|
COPY ./scripts/Docker/app-entrypoint.sh /usr/local/bin/app-entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/app-entrypoint.sh
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
ENTRYPOINT ["app-entrypoint.sh"]
|
||||||
|
|
@ -39,6 +39,11 @@ COPY pyproject.toml pyproject.toml
|
||||||
COPY poetry.lock poetry.lock
|
COPY poetry.lock poetry.lock
|
||||||
|
|
||||||
RUN poetry config virtualenvs.create false
|
RUN poetry config virtualenvs.create false
|
||||||
RUN poetry install -E graph -E worker -E AI --no-interaction --no-ansi
|
RUN poetry install --extras="worker openai" --no-interaction --no-ansi
|
||||||
|
|
||||||
COPY . .
|
COPY ./scripts/Docker/beat-entrypoint.sh /usr/local/bin/beat-entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/beat-entrypoint.sh
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
ENTRYPOINT ["beat-entrypoint.sh"]
|
||||||
49
Dockerfiles/Dockerfile.worker
Normal file
49
Dockerfiles/Dockerfile.worker
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM python:3.12-bookworm
|
||||||
|
LABEL authors="fureunoir"
|
||||||
|
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PYTHONUNBUFFERED=1 \
|
||||||
|
LANG=C.UTF-8 \
|
||||||
|
DEBIAN_FRONTEND=noninteractive \
|
||||||
|
PATH="/root/.local/bin:$PATH"
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
sed -i 's|https://deb.debian.org/debian|https://ftp.uk.debian.org/debian|g' /etc/apt/sources.list.d/debian.sources; \
|
||||||
|
apt-get update; \
|
||||||
|
apt-get install -y --no-install-recommends wget gnupg; \
|
||||||
|
wget -qO - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -; \
|
||||||
|
echo "deb http://apt.postgresql.org/pub/repos/apt bookworm-pgdg main" \
|
||||||
|
> /etc/apt/sources.list.d/pgdg.list; \
|
||||||
|
apt-get update; \
|
||||||
|
apt-get install -y --no-install-recommends --fix-missing \
|
||||||
|
build-essential \
|
||||||
|
libpq-dev \
|
||||||
|
gettext \
|
||||||
|
libgettextpo-dev \
|
||||||
|
graphviz-dev \
|
||||||
|
libgts-dev \
|
||||||
|
libpq5 \
|
||||||
|
graphviz \
|
||||||
|
binutils \
|
||||||
|
libproj-dev \
|
||||||
|
postgresql-client-17 \
|
||||||
|
gdal-bin; \
|
||||||
|
rm -rf /var/lib/apt/lists/*; \
|
||||||
|
pip install --upgrade pip; \
|
||||||
|
curl -sSL https://install.python-poetry.org | python3
|
||||||
|
|
||||||
|
COPY pyproject.toml pyproject.toml
|
||||||
|
COPY poetry.lock poetry.lock
|
||||||
|
|
||||||
|
RUN poetry config virtualenvs.create false
|
||||||
|
RUN poetry install --extras="worker openai" --no-interaction --no-ansi
|
||||||
|
|
||||||
|
COPY ./scripts/Docker/worker-entrypoint.sh /usr/local/bin/worker-entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/worker-entrypoint.sh
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
ENTRYPOINT ["worker-entrypoint.sh"]
|
||||||
19
README.md
19
README.md
|
|
@ -3,7 +3,8 @@
|
||||||

|

|
||||||
|
|
||||||
eVibes is an eCommerce backend service built with Django. It’s designed for flexibility, making it ideal for various use
|
eVibes is an eCommerce backend service built with Django. It’s designed for flexibility, making it ideal for various use
|
||||||
cases and learning Django skills. The project is easy to customize, allowing for straightforward editing and extension.
|
cases and learning Django skills. The project is straightforward to customize, allowing for straightforward editing and
|
||||||
|
extension.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
|
|
@ -20,7 +21,7 @@ cases and learning Django skills. The project is easy to customize, allowing for
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Modular Architecture**: Easily extend and customize the backend to fit your needs.
|
- **Modular Architecture**: Extend and customize the backend to fit your needs.
|
||||||
- **Dockerized Deployment**: Quick setup and deployment using Docker and Docker Compose.
|
- **Dockerized Deployment**: Quick setup and deployment using Docker and Docker Compose.
|
||||||
- **Asynchronous Task Processing**: Integrated Celery workers and beat scheduler for background tasks.
|
- **Asynchronous Task Processing**: Integrated Celery workers and beat scheduler for background tasks.
|
||||||
- **GraphQL and REST APIs**: Supports both GraphQL and RESTful API endpoints.
|
- **GraphQL and REST APIs**: Supports both GraphQL and RESTful API endpoints.
|
||||||
|
|
@ -32,7 +33,7 @@ cases and learning Django skills. The project is easy to customize, allowing for
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- Docker and Docker Compose installed on your machine - that's it!
|
- Docker and Docker Compose are installed on your machine.
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
|
|
@ -49,7 +50,7 @@ cases and learning Django skills. The project is easy to customize, allowing for
|
||||||
git checkout storefront-<option: astro, nuxt, remix, svelte, solid, analog >
|
git checkout storefront-<option: astro, nuxt, remix, svelte, solid, analog >
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Generate your .env file. Check and confirm the contents afterwards.
|
3. Generate your .env file. Check and confirm the contents afterward.
|
||||||
|
|
||||||
- Windows
|
- Windows
|
||||||
```powershell
|
```powershell
|
||||||
|
|
@ -85,29 +86,29 @@ cases and learning Django skills. The project is easy to customize, allowing for
|
||||||
6. Bring to production.
|
6. Bring to production.
|
||||||
|
|
||||||
Include `nginx` file to your nginx configuration, you really want to install and
|
Include `nginx` file to your nginx configuration, you really want to install and
|
||||||
run [Certbot](https://certbot.eff.org/) afterwards!
|
run [Certbot](https://certbot.eff.org/) afterward!
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
### Dockerfile
|
### Dockerfile
|
||||||
|
|
||||||
Don't forget to change the
|
Remember to change the
|
||||||
`RUN sed -i 's|https://deb.debian.org/debian|https://ftp.<locale>.debian.org/debian|g' /etc/apt/sources.list.d/debian.sources`
|
`RUN sed -i 's|https://deb.debian.org/debian|https://ftp.<locale>.debian.org/debian|g' /etc/apt/sources.list.d/debian.sources`
|
||||||
before running installment scripts
|
before running installment scripts
|
||||||
|
|
||||||
### nginx
|
### nginx
|
||||||
|
|
||||||
Please comment-out SSL-related lines, then apply needed configurations, run `certbot --cert-only --nginx`,
|
Please comment-out SSL-related lines, then apply necessary configurations, run `certbot --cert-only --nginx`,
|
||||||
decomment previously commented lines and enjoy eVibes over HTTPS!
|
decomment previously commented lines and enjoy eVibes over HTTPS!
|
||||||
|
|
||||||
### .env
|
### .env
|
||||||
|
|
||||||
After .env file generation, you may want to edit some of it's values such as macroservices' API keys, database password,
|
After .env file generation, you may want to edit some of its values, such as macroservices' API keys, database password,
|
||||||
redis password, etc.
|
redis password, etc.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
- Add needed subdomains to DNS-settings of your domain, those are:
|
- Add necessary subdomains to DNS-settings of your domain, those are:
|
||||||
|
|
||||||
1. @.your-domain.com
|
1. @.your-domain.com
|
||||||
2. www.your-domain.com
|
2. www.your-domain.com
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,10 @@ class PostAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
def preview_html(self, obj):
|
def preview_html(self, obj):
|
||||||
html = obj.content.html or "<em>{}</em>".format(_("(no content yet)"))
|
html = obj.content.html or "<em>{}</em>".format(_("(no content yet)"))
|
||||||
|
# noinspection DjangoSafeString
|
||||||
return mark_safe(html)
|
return mark_safe(html)
|
||||||
|
|
||||||
preview_html.short_description = _("rendered HTML")
|
preview_html.short_description = _("rendered HTML") # type: ignore
|
||||||
|
|
||||||
|
|
||||||
@admin.register(PostTag)
|
@admin.register(PostTag)
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language: ar-AR\n"
|
"Language: ar-ar\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(لا يوجد محتوى بعد)"
|
msgstr "(لا يوجد محتوى بعد)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "تم تقديمه بتنسيق HTML"
|
msgstr "تم تقديمه بتنسيق HTML"
|
||||||
|
|
||||||
|
|
@ -25,47 +25,52 @@ msgstr "تم تقديمه بتنسيق HTML"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "المدونة"
|
msgstr "المدونة"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "عنوان المنشور"
|
msgstr "عنوان المنشور"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "العنوان"
|
msgstr "العنوان"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "المنشور"
|
msgstr "المنشور"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "المنشورات"
|
msgstr "المنشورات"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr ""
|
||||||
|
"ملفات تخفيض السعر غير مدعومة Yer - استخدم محتوى تخفيض السعر بدلاً من ذلك!"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr "يجب توفير ملف ترميز أو محتوى ترميز مخفض - متنافيان"
|
msgstr "يجب توفير ملف ترميز أو محتوى ترميز مخفض - متنافيان"
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "معرّف العلامة الداخلي لعلامة المنشور"
|
msgstr "معرّف العلامة الداخلي لعلامة المنشور"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "اسم العلامة"
|
msgstr "اسم العلامة"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "اسم سهل الاستخدام لعلامة المنشور"
|
msgstr "اسم سهل الاستخدام لعلامة المنشور"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "اسم عرض العلامة"
|
msgstr "اسم عرض العلامة"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "علامة المشاركة"
|
msgstr "علامة المشاركة"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "علامات المشاركة"
|
msgstr "علامات المشاركة"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language: cs-CZ\n"
|
"Language: cs-cz\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(zatím bez obsahu)"
|
msgstr "(zatím bez obsahu)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "Vykreslené HTML"
|
msgstr "Vykreslené HTML"
|
||||||
|
|
||||||
|
|
@ -25,49 +25,54 @@ msgstr "Vykreslené HTML"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "Blog"
|
msgstr "Blog"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "Název příspěvku"
|
msgstr "Název příspěvku"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "Název"
|
msgstr "Název"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "Příspěvek"
|
msgstr "Příspěvek"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "Příspěvky"
|
msgstr "Příspěvky"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr ""
|
||||||
|
"Soubory Markdown nejsou podporovány - místo toho použijte obsah Markdown!"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"musí být poskytnut soubor markdown nebo obsah markdown - vzájemně se "
|
"musí být poskytnut soubor markdown nebo obsah markdown - vzájemně se "
|
||||||
"vylučují."
|
"vylučují."
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "interní identifikátor tagu pro tag příspěvku"
|
msgstr "interní identifikátor tagu pro tag příspěvku"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "Název štítku"
|
msgstr "Název štítku"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "Uživatelsky přívětivý název pro značku příspěvku"
|
msgstr "Uživatelsky přívětivý název pro značku příspěvku"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "Zobrazení názvu štítku"
|
msgstr "Zobrazení názvu štítku"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "Označení příspěvku"
|
msgstr "Označení příspěvku"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "Štítky příspěvků"
|
msgstr "Štítky příspěvků"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language: da-DK\n"
|
"Language: da-dk\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(intet indhold endnu)"
|
msgstr "(intet indhold endnu)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "Rendered HTML"
|
msgstr "Rendered HTML"
|
||||||
|
|
||||||
|
|
@ -25,48 +25,52 @@ msgstr "Rendered HTML"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "Blog"
|
msgstr "Blog"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "Indlæggets titel"
|
msgstr "Indlæggets titel"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "Titel"
|
msgstr "Titel"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "Indlæg"
|
msgstr "Indlæg"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "Indlæg"
|
msgstr "Indlæg"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr "Markdown-filer understøttes ikke - brug markdown-indhold i stedet!"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"en markdown-fil eller markdown-indhold skal leveres - gensidigt udelukkende"
|
"en markdown-fil eller markdown-indhold skal leveres - gensidigt udelukkende"
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "intern tag-identifikator for indlægs-tagget"
|
msgstr "intern tag-identifikator for indlægs-tagget"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "Tag-navn"
|
msgstr "Tag-navn"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "Brugervenligt navn til posttagget"
|
msgstr "Brugervenligt navn til posttagget"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "Navn på tag-visning"
|
msgstr "Navn på tag-visning"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "Tag til indlæg"
|
msgstr "Tag til indlæg"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "Tags til indlæg"
|
msgstr "Tags til indlæg"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language: de-DE\n"
|
"Language: de-de\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(noch kein Inhalt)"
|
msgstr "(noch kein Inhalt)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "Gerendertes HTML"
|
msgstr "Gerendertes HTML"
|
||||||
|
|
||||||
|
|
@ -25,49 +25,55 @@ msgstr "Gerendertes HTML"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "Blog"
|
msgstr "Blog"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "Titel des Beitrags"
|
msgstr "Titel des Beitrags"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "Titel"
|
msgstr "Titel"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "Beitrag"
|
msgstr "Beitrag"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "Beiträge"
|
msgstr "Beiträge"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr ""
|
||||||
|
"Markdown-Dateien werden nicht unterstützt - verwenden Sie stattdessen "
|
||||||
|
"Markdown-Inhalte!"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"eine Markdown-Datei oder ein Markdown-Inhalt muss bereitgestellt werden - "
|
"eine Markdown-Datei oder ein Markdown-Inhalt muss bereitgestellt werden - "
|
||||||
"beide schließen sich gegenseitig aus"
|
"beide schließen sich gegenseitig aus"
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "interner Tag-Bezeichner für den Post-Tag"
|
msgstr "interner Tag-Bezeichner für den Post-Tag"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "Tag name"
|
msgstr "Tag name"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "Benutzerfreundlicher Name für das Post-Tag"
|
msgstr "Benutzerfreundlicher Name für das Post-Tag"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "Tag-Anzeigename"
|
msgstr "Tag-Anzeigename"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "Tag eintragen"
|
msgstr "Tag eintragen"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "Tags eintragen"
|
msgstr "Tags eintragen"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -5,9 +5,9 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
|
|
@ -21,7 +21,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(no content yet)"
|
msgstr "(no content yet)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "Rendered HTML"
|
msgstr "Rendered HTML"
|
||||||
|
|
||||||
|
|
@ -29,48 +29,52 @@ msgstr "Rendered HTML"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "Blog"
|
msgstr "Blog"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "Post's title"
|
msgstr "Post's title"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "Title"
|
msgstr "Title"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "Post"
|
msgstr "Post"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "Posts"
|
msgstr "Posts"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr "Markdown files are not supported yer - use markdown content instead!"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "internal tag identifier for the post tag"
|
msgstr "internal tag identifier for the post tag"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "Tag name"
|
msgstr "Tag name"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "User-friendly name for the post tag"
|
msgstr "User-friendly name for the post tag"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "Tag display name"
|
msgstr "Tag display name"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "Post tag"
|
msgstr "Post tag"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "Post tags"
|
msgstr "Post tags"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language: en-US\n"
|
"Language: en-us\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(no content yet)"
|
msgstr "(no content yet)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "Rendered HTML"
|
msgstr "Rendered HTML"
|
||||||
|
|
||||||
|
|
@ -25,48 +25,52 @@ msgstr "Rendered HTML"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "Blog"
|
msgstr "Blog"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "Post's title"
|
msgstr "Post's title"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "Title"
|
msgstr "Title"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "Post"
|
msgstr "Post"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "Posts"
|
msgstr "Posts"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr "Markdown files are not supported yer - use markdown content instead!"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "internal tag identifier for the post tag"
|
msgstr "internal tag identifier for the post tag"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "Tag name"
|
msgstr "Tag name"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "User-friendly name for the post tag"
|
msgstr "User-friendly name for the post tag"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "Tag display name"
|
msgstr "Tag display name"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "Post tag"
|
msgstr "Post tag"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "Post tags"
|
msgstr "Post tags"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language: es-ES\n"
|
"Language: es-es\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(sin contenido aún)"
|
msgstr "(sin contenido aún)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "HTML renderizado"
|
msgstr "HTML renderizado"
|
||||||
|
|
||||||
|
|
@ -25,49 +25,54 @@ msgstr "HTML renderizado"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "Blog"
|
msgstr "Blog"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "Título del mensaje"
|
msgstr "Título del mensaje"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "Título"
|
msgstr "Título"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "Publicar en"
|
msgstr "Publicar en"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "Puestos"
|
msgstr "Puestos"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr ""
|
||||||
|
"No se admiten archivos Markdown - ¡utiliza contenido Markdown en su lugar!"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"se debe proporcionar un archivo markdown o contenido markdown - mutuamente "
|
"se debe proporcionar un archivo markdown o contenido markdown - mutuamente "
|
||||||
"excluyentes"
|
"excluyentes"
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "identificador interno de la etiqueta post"
|
msgstr "identificador interno de la etiqueta post"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "Nombre de la etiqueta"
|
msgstr "Nombre de la etiqueta"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "Nombre fácil de usar para la etiqueta de la entrada"
|
msgstr "Nombre fácil de usar para la etiqueta de la entrada"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "Nombre de la etiqueta"
|
msgstr "Nombre de la etiqueta"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "Etiqueta postal"
|
msgstr "Etiqueta postal"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "Etiquetas"
|
msgstr "Etiquetas"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language: fr-FR\n"
|
"Language: fr-fr\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(pas encore de contenu)"
|
msgstr "(pas encore de contenu)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "HTML rendu"
|
msgstr "HTML rendu"
|
||||||
|
|
||||||
|
|
@ -25,49 +25,55 @@ msgstr "HTML rendu"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "Blog"
|
msgstr "Blog"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "Titre du message"
|
msgstr "Titre du message"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "Titre"
|
msgstr "Titre"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "Poste"
|
msgstr "Poste"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "Postes"
|
msgstr "Postes"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr ""
|
||||||
|
"Les fichiers Markdown ne sont pas pris en charge - utilisez plutôt du "
|
||||||
|
"contenu Markdown !"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"un fichier markdown ou un contenu markdown doit être fourni - ils s'excluent "
|
"un fichier markdown ou un contenu markdown doit être fourni - ils s'excluent"
|
||||||
"mutuellement"
|
" mutuellement"
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "identifiant interne de la balise post"
|
msgstr "identifiant interne de la balise post"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "Nom du jour"
|
msgstr "Nom du jour"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "Nom convivial pour la balise post"
|
msgstr "Nom convivial pour la balise post"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "Nom d'affichage de l'étiquette"
|
msgstr "Nom d'affichage de l'étiquette"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "Tag de poste"
|
msgstr "Tag de poste"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "Tags de la poste"
|
msgstr "Tags de la poste"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -2,12 +2,12 @@
|
||||||
# Copyright (C) 2025 EGOR <FUREUNOIR> GORBUNOV
|
# Copyright (C) 2025 EGOR <FUREUNOIR> GORBUNOV
|
||||||
# This file is distributed under the same license as the EVIBES package.
|
# This file is distributed under the same license as the EVIBES package.
|
||||||
# EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>, 2025.
|
# EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>, 2025.
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
|
|
@ -20,7 +20,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -28,47 +28,51 @@ msgstr ""
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language: it-IT\n"
|
"Language: it-it\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(ancora senza contenuti)"
|
msgstr "(ancora senza contenuti)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "HTML renderizzato"
|
msgstr "HTML renderizzato"
|
||||||
|
|
||||||
|
|
@ -25,49 +25,53 @@ msgstr "HTML renderizzato"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "Blog"
|
msgstr "Blog"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "Titolo del post"
|
msgstr "Titolo del post"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "Titolo"
|
msgstr "Titolo"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "Posta"
|
msgstr "Posta"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "Messaggi"
|
msgstr "Messaggi"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr "I file Markdown non sono supportati: usa invece i contenuti Markdown!"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"deve essere fornito un file markdown o un contenuto markdown - si escludono "
|
"deve essere fornito un file markdown o un contenuto markdown - si escludono "
|
||||||
"a vicenda"
|
"a vicenda"
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "identificatore interno del tag post"
|
msgstr "identificatore interno del tag post"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "Nome del tag"
|
msgstr "Nome del tag"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "Nome intuitivo per il tag del post"
|
msgstr "Nome intuitivo per il tag del post"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "Nome del tag"
|
msgstr "Nome del tag"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "Post tag"
|
msgstr "Post tag"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "Tag dei post"
|
msgstr "Tag dei post"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language: ja-JP\n"
|
"Language: ja-jp\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(内容はまだありません)"
|
msgstr "(内容はまだありません)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "レンダリングされたHTML"
|
msgstr "レンダリングされたHTML"
|
||||||
|
|
||||||
|
|
@ -25,49 +25,51 @@ msgstr "レンダリングされたHTML"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "ブログ"
|
msgstr "ブログ"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "投稿タイトル"
|
msgstr "投稿タイトル"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "タイトル"
|
msgstr "タイトル"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "ポスト"
|
msgstr "ポスト"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "投稿"
|
msgstr "投稿"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr "マークダウン・ファイルはサポートされていません - 代わりにマークダウン・コンテンツを使用してください!"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr "マークダウン・ファイルまたはマークダウン・コンテンツを提供しなければならない。"
|
||||||
"マークダウン・ファイルまたはマークダウン・コンテンツを提供しなければならな"
|
|
||||||
"い。"
|
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "投稿タグの内部タグ識別子"
|
msgstr "投稿タグの内部タグ識別子"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "タグ名"
|
msgstr "タグ名"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "投稿タグのユーザーフレンドリーな名前"
|
msgstr "投稿タグのユーザーフレンドリーな名前"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "タグ表示名"
|
msgstr "タグ表示名"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "投稿タグ"
|
msgstr "投稿タグ"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "投稿タグ"
|
msgstr "投稿タグ"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -2,12 +2,12 @@
|
||||||
# Copyright (C) 2025 EGOR <FUREUNOIR> GORBUNOV
|
# Copyright (C) 2025 EGOR <FUREUNOIR> GORBUNOV
|
||||||
# This file is distributed under the same license as the EVIBES package.
|
# This file is distributed under the same license as the EVIBES package.
|
||||||
# EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>, 2025.
|
# EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>, 2025.
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
|
|
@ -20,7 +20,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -28,47 +28,51 @@ msgstr ""
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language: nl-NL\n"
|
"Language: nl-nl\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(nog geen inhoud)"
|
msgstr "(nog geen inhoud)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "HTML weergeven"
|
msgstr "HTML weergeven"
|
||||||
|
|
||||||
|
|
@ -25,49 +25,55 @@ msgstr "HTML weergeven"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "Blog"
|
msgstr "Blog"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "Titel van de post"
|
msgstr "Titel van de post"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "Titel"
|
msgstr "Titel"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "Plaats"
|
msgstr "Plaats"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "Berichten"
|
msgstr "Berichten"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr ""
|
||||||
|
"Markdown-bestanden worden niet ondersteund - gebruik in plaats daarvan "
|
||||||
|
"markdown-inhoud!"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"er moet een markdown-bestand of markdown-inhoud worden geleverd - wederzijds "
|
"er moet een markdown-bestand of markdown-inhoud worden geleverd - wederzijds"
|
||||||
"exclusief"
|
" exclusief"
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "interne tagidentifier voor de posttag"
|
msgstr "interne tagidentifier voor de posttag"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "Tag naam"
|
msgstr "Tag naam"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "Gebruiksvriendelijke naam voor de posttag"
|
msgstr "Gebruiksvriendelijke naam voor de posttag"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "Tag weergavenaam"
|
msgstr "Tag weergavenaam"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "Post tag"
|
msgstr "Post tag"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "Post tags"
|
msgstr "Post tags"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language: pl-PL\n"
|
"Language: pl-pl\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(brak treści)"
|
msgstr "(brak treści)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "Renderowany HTML"
|
msgstr "Renderowany HTML"
|
||||||
|
|
||||||
|
|
@ -25,49 +25,54 @@ msgstr "Renderowany HTML"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "Blog"
|
msgstr "Blog"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "Tytuł postu"
|
msgstr "Tytuł postu"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "Tytuł"
|
msgstr "Tytuł"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "Post"
|
msgstr "Post"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "Posty"
|
msgstr "Posty"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr ""
|
||||||
|
"Pliki Markdown nie są obsługiwane - zamiast tego użyj zawartości Markdown!"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"należy dostarczyć plik markdown lub zawartość markdown - wzajemnie się "
|
"należy dostarczyć plik markdown lub zawartość markdown - wzajemnie się "
|
||||||
"wykluczające"
|
"wykluczające"
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "wewnętrzny identyfikator tagu posta"
|
msgstr "wewnętrzny identyfikator tagu posta"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "Nazwa tagu"
|
msgstr "Nazwa tagu"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "Przyjazna dla użytkownika nazwa tagu posta"
|
msgstr "Przyjazna dla użytkownika nazwa tagu posta"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "Wyświetlana nazwa znacznika"
|
msgstr "Wyświetlana nazwa znacznika"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "Tag posta"
|
msgstr "Tag posta"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "Tagi postów"
|
msgstr "Tagi postów"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language: pt-BR\n"
|
"Language: pt-br\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(ainda não há conteúdo)"
|
msgstr "(ainda não há conteúdo)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "HTML renderizado"
|
msgstr "HTML renderizado"
|
||||||
|
|
||||||
|
|
@ -25,48 +25,54 @@ msgstr "HTML renderizado"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "Blog"
|
msgstr "Blog"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "Título da postagem"
|
msgstr "Título da postagem"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "Título"
|
msgstr "Título"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "Postar"
|
msgstr "Postar"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "Publicações"
|
msgstr "Publicações"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr ""
|
||||||
|
"Os arquivos markdown não são suportados - use conteúdo markdown em vez "
|
||||||
|
"disso!"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"um arquivo ou conteúdo de markdown deve ser fornecido - mutuamente exclusivo"
|
"um arquivo ou conteúdo de markdown deve ser fornecido - mutuamente exclusivo"
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "identificador de tag interno para a tag de postagem"
|
msgstr "identificador de tag interno para a tag de postagem"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "Nome da etiqueta"
|
msgstr "Nome da etiqueta"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "Nome de fácil utilização para a tag de postagem"
|
msgstr "Nome de fácil utilização para a tag de postagem"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "Nome de exibição da tag"
|
msgstr "Nome de exibição da tag"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "Etiqueta de postagem"
|
msgstr "Etiqueta de postagem"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "Tags de postagem"
|
msgstr "Tags de postagem"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language: ro-RO\n"
|
"Language: ro-ro\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(fără conținut încă)"
|
msgstr "(fără conținut încă)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "HTML redat"
|
msgstr "HTML redat"
|
||||||
|
|
||||||
|
|
@ -25,49 +25,55 @@ msgstr "HTML redat"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "Blog"
|
msgstr "Blog"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "Titlul postului"
|
msgstr "Titlul postului"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "Titlul"
|
msgstr "Titlul"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "Post"
|
msgstr "Post"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "Mesaje"
|
msgstr "Mesaje"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr ""
|
||||||
|
"Fișierele Markdown nu sunt acceptate - utilizați în schimb conținut "
|
||||||
|
"Markdown!"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"trebuie furnizat un fișier markdown sau conținut markdown - se exclud "
|
"trebuie furnizat un fișier markdown sau conținut markdown - se exclud "
|
||||||
"reciproc"
|
"reciproc"
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "identificator intern de etichetă pentru eticheta postului"
|
msgstr "identificator intern de etichetă pentru eticheta postului"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "Nume etichetă"
|
msgstr "Nume etichetă"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "Nume ușor de utilizat pentru eticheta postului"
|
msgstr "Nume ușor de utilizat pentru eticheta postului"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "Nume afișare etichetă"
|
msgstr "Nume afișare etichetă"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "Etichetă post"
|
msgstr "Etichetă post"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "Etichete poștale"
|
msgstr "Etichete poștale"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language: ru-RU\n"
|
"Language: ru-ru\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(пока без содержания)"
|
msgstr "(пока без содержания)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "Рендеринг HTML"
|
msgstr "Рендеринг HTML"
|
||||||
|
|
||||||
|
|
@ -25,49 +25,55 @@ msgstr "Рендеринг HTML"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "Блог"
|
msgstr "Блог"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "Заголовок сообщения"
|
msgstr "Заголовок сообщения"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "Название"
|
msgstr "Название"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "Пост"
|
msgstr "Пост"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "Посты"
|
msgstr "Посты"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr ""
|
||||||
|
"Файлы в формате Markdown не поддерживаются - используйте вместо них "
|
||||||
|
"содержимое в формате Markdown!"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"необходимо предоставить файл разметки или содержимое разметки - "
|
"необходимо предоставить файл разметки или содержимое разметки - "
|
||||||
"взаимоисключающие варианты"
|
"взаимоисключающие варианты"
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "внутренний идентификатор тега для тега post"
|
msgstr "внутренний идентификатор тега для тега post"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "Название тега"
|
msgstr "Название тега"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "Удобное для пользователя название тега поста"
|
msgstr "Удобное для пользователя название тега поста"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "Отображаемое имя тега"
|
msgstr "Отображаемое имя тега"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "Тэг поста"
|
msgstr "Тэг поста"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "Тэги постов"
|
msgstr "Тэги постов"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,9 +1,9 @@
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
msgid "(no content yet)"
|
msgid "(no content yet)"
|
||||||
msgstr "(暂无内容)"
|
msgstr "(暂无内容)"
|
||||||
|
|
||||||
#: blog/admin.py:40
|
#: blog/admin.py:41
|
||||||
msgid "rendered HTML"
|
msgid "rendered HTML"
|
||||||
msgstr "渲染的 HTML"
|
msgstr "渲染的 HTML"
|
||||||
|
|
||||||
|
|
@ -25,47 +25,51 @@ msgstr "渲染的 HTML"
|
||||||
msgid "blog"
|
msgid "blog"
|
||||||
msgstr "博客"
|
msgstr "博客"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "post title"
|
msgid "post title"
|
||||||
msgstr "帖子标题"
|
msgstr "帖子标题"
|
||||||
|
|
||||||
#: blog/models.py:15
|
#: blog/models.py:17
|
||||||
msgid "title"
|
msgid "title"
|
||||||
msgstr "标题"
|
msgstr "标题"
|
||||||
|
|
||||||
#: blog/models.py:62
|
#: blog/models.py:64
|
||||||
msgid "post"
|
msgid "post"
|
||||||
msgstr "职位"
|
msgstr "职位"
|
||||||
|
|
||||||
#: blog/models.py:63
|
#: blog/models.py:65
|
||||||
msgid "posts"
|
msgid "posts"
|
||||||
msgstr "职位"
|
msgstr "职位"
|
||||||
|
|
||||||
#: blog/models.py:67
|
#: blog/models.py:69
|
||||||
|
msgid "markdown files are not supported yet - use markdown content instead"
|
||||||
|
msgstr "不支持 Markdown 文件,请使用 Markdown 内容!"
|
||||||
|
|
||||||
|
#: blog/models.py:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||||
msgstr "必须提供标记符文件或标记符内容 - 相互排斥"
|
msgstr "必须提供标记符文件或标记符内容 - 相互排斥"
|
||||||
|
|
||||||
#: blog/models.py:78
|
#: blog/models.py:82
|
||||||
msgid "internal tag identifier for the post tag"
|
msgid "internal tag identifier for the post tag"
|
||||||
msgstr "职位标签的内部标签标识符"
|
msgstr "职位标签的内部标签标识符"
|
||||||
|
|
||||||
#: blog/models.py:79
|
#: blog/models.py:83
|
||||||
msgid "tag name"
|
msgid "tag name"
|
||||||
msgstr "标签名称"
|
msgstr "标签名称"
|
||||||
|
|
||||||
#: blog/models.py:83
|
#: blog/models.py:87
|
||||||
msgid "user-friendly name for the post tag"
|
msgid "user-friendly name for the post tag"
|
||||||
msgstr "方便用户使用的帖子标签名称"
|
msgstr "方便用户使用的帖子标签名称"
|
||||||
|
|
||||||
#: blog/models.py:84
|
#: blog/models.py:88
|
||||||
msgid "tag display name"
|
msgid "tag display name"
|
||||||
msgstr "标签显示名称"
|
msgstr "标签显示名称"
|
||||||
|
|
||||||
#: blog/models.py:92
|
#: blog/models.py:96
|
||||||
msgid "post tag"
|
msgid "post tag"
|
||||||
msgstr "职位标签"
|
msgstr "职位标签"
|
||||||
|
|
||||||
#: blog/models.py:93
|
#: blog/models.py:97
|
||||||
msgid "post tags"
|
msgid "post tags"
|
||||||
msgstr "帖子标签"
|
msgstr "帖子标签"
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,13 @@ from core.abstract import NiceModel
|
||||||
class Post(NiceModel):
|
class Post(NiceModel):
|
||||||
is_publicly_visible = True
|
is_publicly_visible = True
|
||||||
|
|
||||||
author = ForeignKey(to="vibes_auth.User", on_delete=CASCADE, blank=False, null=False, related_name="posts")
|
author: ForeignKey = ForeignKey(
|
||||||
title = CharField(
|
to="vibes_auth.User", on_delete=CASCADE, blank=False, null=False, related_name="posts"
|
||||||
|
)
|
||||||
|
title: CharField = CharField(
|
||||||
unique=True, max_length=128, blank=False, null=False, help_text=_("post title"), verbose_name=_("title")
|
unique=True, max_length=128, blank=False, null=False, help_text=_("post title"), verbose_name=_("title")
|
||||||
)
|
)
|
||||||
content = MarkdownField(
|
content: MarkdownField = MarkdownField(
|
||||||
"content",
|
"content",
|
||||||
extensions=[
|
extensions=[
|
||||||
TocExtension(toc_depth=3),
|
TocExtension(toc_depth=3),
|
||||||
|
|
@ -51,9 +53,9 @@ class Post(NiceModel):
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
file = FileField(upload_to="posts/", blank=True, null=True)
|
file: FileField = FileField(upload_to="posts/", blank=True, null=True)
|
||||||
slug = AutoSlugField(populate_from="title", allow_unicode=True, unique=True, editable=False)
|
slug: AutoSlugField = AutoSlugField(populate_from="title", allow_unicode=True, unique=True, editable=False)
|
||||||
tags = ManyToManyField(to="blog.PostTag", blank=True, related_name="posts")
|
tags: ManyToManyField = ManyToManyField(to="blog.PostTag", blank=True, related_name="posts")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.title} | {self.author.first_name} {self.author.last_name}"
|
return f"{self.title} | {self.author.first_name} {self.author.last_name}"
|
||||||
|
|
@ -63,6 +65,8 @@ class Post(NiceModel):
|
||||||
verbose_name_plural = _("posts")
|
verbose_name_plural = _("posts")
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
|
if self.file:
|
||||||
|
raise ValueError(_("markdown files are not supported yet - use markdown content instead"))
|
||||||
if not any([self.file, self.content]) or all([self.file, self.content]):
|
if not any([self.file, self.content]) or all([self.file, self.content]):
|
||||||
raise ValueError(_("a markdown file or markdown content must be provided - mutually exclusive"))
|
raise ValueError(_("a markdown file or markdown content must be provided - mutually exclusive"))
|
||||||
super().save(**kwargs)
|
super().save(**kwargs)
|
||||||
|
|
@ -71,14 +75,14 @@ class Post(NiceModel):
|
||||||
class PostTag(NiceModel):
|
class PostTag(NiceModel):
|
||||||
is_publicly_visible = True
|
is_publicly_visible = True
|
||||||
|
|
||||||
tag_name = CharField(
|
tag_name: CharField = CharField(
|
||||||
blank=False,
|
blank=False,
|
||||||
null=False,
|
null=False,
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=_("internal tag identifier for the post tag"),
|
help_text=_("internal tag identifier for the post tag"),
|
||||||
verbose_name=_("tag name"),
|
verbose_name=_("tag name"),
|
||||||
)
|
)
|
||||||
name = CharField(
|
name: CharField = CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=_("user-friendly name for the post tag"),
|
help_text=_("user-friendly name for the post tag"),
|
||||||
verbose_name=_("tag display name"),
|
verbose_name=_("tag display name"),
|
||||||
|
|
|
||||||
|
|
@ -31,4 +31,5 @@ class MarkdownEditorWidget(forms.Textarea):
|
||||||
}});
|
}});
|
||||||
</script>
|
</script>
|
||||||
"""
|
"""
|
||||||
|
# noinspection DjangoSafeString
|
||||||
return mark_safe(textarea_html + init_js)
|
return mark_safe(textarea_html + init_js)
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,14 @@ from django_extensions.db.fields import CreationDateTimeField, ModificationDateT
|
||||||
|
|
||||||
class NiceModel(Model):
|
class NiceModel(Model):
|
||||||
id = None
|
id = None
|
||||||
uuid = UUIDField(
|
uuid: UUIDField = UUIDField(
|
||||||
verbose_name=_("unique id"),
|
verbose_name=_("unique id"),
|
||||||
help_text=_("unique id is used to surely identify any database object"),
|
help_text=_("unique id is used to surely identify any database object"),
|
||||||
primary_key=True,
|
primary_key=True,
|
||||||
default=uuid.uuid4,
|
default=uuid.uuid4,
|
||||||
editable=False,
|
editable=False,
|
||||||
)
|
)
|
||||||
is_active = BooleanField(
|
is_active: BooleanField = BooleanField(
|
||||||
default=True,
|
default=True,
|
||||||
verbose_name=_("is active"),
|
verbose_name=_("is active"),
|
||||||
help_text=_("if set to false, this object can't be seen by users without needed permission"),
|
help_text=_("if set to false, this object can't be seen by users without needed permission"),
|
||||||
|
|
|
||||||
|
|
@ -134,8 +134,8 @@ class CategoryAdmin(DraggableMPTTAdmin, BasicModelAdmin, TabbedTranslationAdmin)
|
||||||
def indented_title(self, instance):
|
def indented_title(self, instance):
|
||||||
return instance.name
|
return instance.name
|
||||||
|
|
||||||
indented_title.short_description = _("name")
|
indented_title.short_description = _("name") # type: ignore
|
||||||
indented_title.admin_order_field = "name"
|
indented_title.admin_order_field = "name" # type: ignore
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Brand)
|
@admin.register(Brand)
|
||||||
|
|
@ -191,12 +191,12 @@ class ProductAdmin(BasicModelAdmin, TabbedTranslationAdmin):
|
||||||
def price(self, obj):
|
def price(self, obj):
|
||||||
return obj.price
|
return obj.price
|
||||||
|
|
||||||
price.short_description = _("price")
|
price.short_description = _("price") # type: ignore
|
||||||
|
|
||||||
def rating(self, obj):
|
def rating(self, obj):
|
||||||
return obj.rating
|
return obj.rating
|
||||||
|
|
||||||
rating.short_description = _("rating")
|
rating.short_description = _("rating") # type: ignore
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(
|
(
|
||||||
|
|
@ -278,7 +278,7 @@ class OrderAdmin(BasicModelAdmin):
|
||||||
def is_business(self, obj):
|
def is_business(self, obj):
|
||||||
return obj.is_business
|
return obj.is_business
|
||||||
|
|
||||||
is_business.short_description = _("is business")
|
is_business.short_description = _("is business") # type: ignore
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
qs = super().get_queryset(request)
|
qs = super().get_queryset(request)
|
||||||
|
|
@ -431,6 +431,6 @@ class ConstanceConfig:
|
||||||
admin.site.unregister([Config]) # type: ignore
|
admin.site.unregister([Config]) # type: ignore
|
||||||
admin.site.register([ConstanceConfig], ConstanceAdmin) # type: ignore
|
admin.site.register([ConstanceConfig], ConstanceAdmin) # type: ignore
|
||||||
|
|
||||||
admin.site.site_title = f"{CONSTANCE_CONFIG.get('PROJECT_NAME')[0]}"
|
admin.site.site_title = f"{CONSTANCE_CONFIG.get('PROJECT_NAME')[0]}" # type: ignore
|
||||||
admin.site.site_header = "eVibes"
|
admin.site.site_header = "eVibes"
|
||||||
admin.site.index_title = f"{CONSTANCE_CONFIG.get('PROJECT_NAME')[0]}"
|
admin.site.index_title = f"{CONSTANCE_CONFIG.get('PROJECT_NAME')[0]}" # type: ignore
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ def process_query(query: str = ""):
|
||||||
response = search.execute()
|
response = search.execute()
|
||||||
|
|
||||||
# Collect results, guard against None values
|
# Collect results, guard against None values
|
||||||
results = {"products": [], "categories": [], "brands": [], "posts": []}
|
results: dict = {"products": [], "categories": [], "brands": [], "posts": []}
|
||||||
for hit in response.hits:
|
for hit in response.hits:
|
||||||
obj_uuid = getattr(hit, "uuid", None) or hit.meta.id
|
obj_uuid = getattr(hit, "uuid", None) or hit.meta.id
|
||||||
obj_name = getattr(hit, "name", None) or getattr(hit, "title", None) or "N/A"
|
obj_name = getattr(hit, "name", None) or getattr(hit, "title", None) or "N/A"
|
||||||
|
|
|
||||||
|
|
@ -335,20 +335,16 @@ class CategoryFilter(FilterSet):
|
||||||
fields = ["uuid", "name", "parent_uuid", "slug", "tags", "level", "order_by", "whole"]
|
fields = ["uuid", "name", "parent_uuid", "slug", "tags", "level", "order_by", "whole"]
|
||||||
|
|
||||||
def filter_whole_categories(self, queryset, _name, value):
|
def filter_whole_categories(self, queryset, _name, value):
|
||||||
has_own_products = Exists(
|
has_own_products = Exists(Product.objects.filter(category=OuterRef("pk")))
|
||||||
Product.objects.filter(category=OuterRef('pk'))
|
|
||||||
)
|
|
||||||
has_desc_products = Exists(
|
has_desc_products = Exists(
|
||||||
Product.objects.filter(
|
Product.objects.filter(
|
||||||
is_active=True,
|
is_active=True,
|
||||||
category__tree_id=OuterRef('tree_id'),
|
category__tree_id=OuterRef("tree_id"),
|
||||||
category__lft__gt=OuterRef('lft'),
|
category__lft__gt=OuterRef("lft"),
|
||||||
category__rght__lt=OuterRef('rght'),
|
category__rght__lt=OuterRef("rght"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
annotated = queryset.annotate(
|
annotated = queryset.annotate(has_products=has_own_products | has_desc_products)
|
||||||
has_products=has_own_products | has_desc_products
|
|
||||||
)
|
|
||||||
if value:
|
if value:
|
||||||
return annotated.filter(has_products=True).distinct()
|
return annotated.filter(has_products=True).distinct()
|
||||||
return annotated.filter(has_products=False).distinct()
|
return annotated.filter(has_products=False).distinct()
|
||||||
|
|
|
||||||
|
|
@ -619,6 +619,7 @@ class ContactUs(BaseMutation):
|
||||||
return ContactUs(received=False, error=str(e))
|
return ContactUs(received=False, error=str(e))
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyArgumentList
|
||||||
class Search(BaseMutation):
|
class Search(BaseMutation):
|
||||||
class Arguments:
|
class Arguments:
|
||||||
query = String(required=True)
|
query = String(required=True)
|
||||||
|
|
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
|
@ -50,7 +50,7 @@ class Command(BaseCommand):
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
self.stdout.write(self.style.SUCCESS("Redis available!"))
|
self.stdout.write(self.style.SUCCESS("Redis available!"))
|
||||||
|
|
||||||
# Create and start threads for database and Redis
|
# Create and start threads for the database and Redis
|
||||||
db_thread = threading.Thread(target=wait_for_db)
|
db_thread = threading.Thread(target=wait_for_db)
|
||||||
redis_thread = threading.Thread(target=wait_for_redis)
|
redis_thread = threading.Thread(target=wait_for_redis)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,14 @@ from django.core.management.base import BaseCommand, CommandError
|
||||||
|
|
||||||
# Patterns to identify placeholders
|
# Patterns to identify placeholders
|
||||||
PLACEHOLDER_REGEXES = [
|
PLACEHOLDER_REGEXES = [
|
||||||
re.compile(r"\{[^}]+\}"), # {name}, {type(instance)!s}, etc.
|
re.compile(r"\{[^}]+"), # {name}, {type(instance)!s}, etc.
|
||||||
re.compile(r"%\([^)]+\)[sd]"), # %(verbose_name)s, %(count)d
|
re.compile(r"%\([^)]+\)[sd]"), # %(verbose_name)s, %(count)d
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def extract_placeholders(text: str) -> set[str]:
|
def extract_placeholders(text: str) -> set[str]:
|
||||||
"""
|
"""
|
||||||
Extract all placeholders from given text.
|
Extract all placeholders from the given text.
|
||||||
"""
|
"""
|
||||||
phs: list[str] = []
|
phs: list[str] = []
|
||||||
for rx in PLACEHOLDER_REGEXES:
|
for rx in PLACEHOLDER_REGEXES:
|
||||||
|
|
@ -29,33 +29,33 @@ def load_po_sanitized(path: str) -> polib.POFile:
|
||||||
Load a .po file via polib, sanitizing on parse errors.
|
Load a .po file via polib, sanitizing on parse errors.
|
||||||
Raises CommandError if still unparsable.
|
Raises CommandError if still unparsable.
|
||||||
"""
|
"""
|
||||||
try:
|
with contextlib.suppress(Exception):
|
||||||
return polib.pofile(path)
|
return polib.pofile(path)
|
||||||
except Exception:
|
|
||||||
# read raw text
|
# read raw text
|
||||||
try:
|
try:
|
||||||
with open(path, encoding="utf-8") as f:
|
with open(path, encoding="utf-8") as f:
|
||||||
text = f.read()
|
text = f.read()
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
raise CommandError(f"{path}: cannot read file ({e})")
|
raise CommandError(f"{path}: cannot read file ({e})")
|
||||||
# fix fuzzy flags and empty header entries
|
# fix fuzzy flags and empty header entries
|
||||||
text = re.sub(r"^#,(?!\s)", "#, ", text, flags=re.MULTILINE)
|
text = re.sub(r"^#,(?!\s)", "#, ", text, flags=re.MULTILINE)
|
||||||
parts = text.split("\n\n", 1)
|
parts = text.split("\n\n", 1)
|
||||||
header = parts[0]
|
header = parts[0]
|
||||||
rest = parts[1] if len(parts) > 1 else ""
|
rest = parts[1] if len(parts) > 1 else ""
|
||||||
rest = re.sub(r"^msgid \"\"\s*\nmsgstr \"\"\s*\n?", "", rest, flags=re.MULTILINE)
|
rest = re.sub(r"^msgid \"\"\s*\nmsgstr \"\"\s*\n?", "", rest, flags=re.MULTILINE)
|
||||||
sanitized = header + "\n\n" + rest
|
sanitized = header + "\n\n" + rest
|
||||||
tmp = NamedTemporaryFile(mode="w+", delete=False, suffix=".po", encoding="utf-8") # noqa: SIM115
|
tmp = NamedTemporaryFile(mode="w+", delete=False, suffix=".po", encoding="utf-8") # noqa: SIM115
|
||||||
try:
|
try:
|
||||||
tmp.write(sanitized)
|
tmp.write(sanitized)
|
||||||
tmp.flush()
|
tmp.flush()
|
||||||
tmp.close()
|
tmp.close()
|
||||||
return polib.pofile(tmp.name)
|
return polib.pofile(tmp.name)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise CommandError(f"{path}: syntax error after sanitization ({e})")
|
raise CommandError(f"{path}: syntax error after sanitization ({e})")
|
||||||
finally:
|
finally:
|
||||||
with contextlib.suppress(OSError):
|
with contextlib.suppress(OSError):
|
||||||
os.unlink(tmp.name)
|
os.unlink(tmp.name)
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,9 @@ class Command(BaseCommand):
|
||||||
self.stdout.write(f"• {app_conf.label}: loading English PO…")
|
self.stdout.write(f"• {app_conf.label}: loading English PO…")
|
||||||
en_po = load_po_sanitized(en_path)
|
en_po = load_po_sanitized(en_path)
|
||||||
|
|
||||||
|
if not en_po:
|
||||||
|
raise CommandError(f"Failed to load en_GB PO for {app_conf.label}")
|
||||||
|
|
||||||
missing = [e for e in en_po if e.msgid and not e.msgstr and not e.obsolete]
|
missing = [e for e in en_po if e.msgid and not e.msgstr and not e.obsolete]
|
||||||
if missing:
|
if missing:
|
||||||
self.stdout.write(self.style.NOTICE(f"⚠️ {len(missing)} missing in en_GB"))
|
self.stdout.write(self.style.NOTICE(f"⚠️ {len(missing)} missing in en_GB"))
|
||||||
|
|
@ -176,17 +179,17 @@ class Command(BaseCommand):
|
||||||
new_po.metadata = en_po.metadata.copy()
|
new_po.metadata = en_po.metadata.copy()
|
||||||
new_po.metadata["Language"] = target_lang
|
new_po.metadata["Language"] = target_lang
|
||||||
|
|
||||||
for e in entries:
|
for entry in entries:
|
||||||
prev = old_tgt.find(e.msgid) if old_tgt else None
|
prev = old_tgt.find(entry.msgid) if old_tgt else None
|
||||||
new_po.append(
|
new_po.append(
|
||||||
polib.POEntry(
|
polib.POEntry(
|
||||||
msgid=e.msgid,
|
msgid=entry.msgid,
|
||||||
msgstr=prev.msgstr if prev and prev.msgstr else "",
|
msgstr=prev.msgstr if prev and prev.msgstr else "",
|
||||||
msgctxt=e.msgctxt,
|
msgctxt=entry.msgctxt,
|
||||||
comment=e.comment,
|
comment=entry.comment,
|
||||||
tcomment=e.tcomment,
|
tcomment=entry.tcomment,
|
||||||
occurrences=e.occurrences,
|
occurrences=entry.occurrences,
|
||||||
flags=e.flags,
|
flags=entry.flags,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@ logger = logging.getLogger("django.request")
|
||||||
|
|
||||||
|
|
||||||
class AddressManager(models.Manager):
|
class AddressManager(models.Manager):
|
||||||
def create(self, raw_data: str, **kwargs):
|
def create(self, raw_data: str, **kwargs): # type: ignore
|
||||||
if not raw_data:
|
if not raw_data:
|
||||||
raise ValueError("'raw_data' (address string) must be provided.")
|
raise ValueError("'raw_data' (address string) must be provided.")
|
||||||
|
|
||||||
params = {
|
params: dict[str, str | int] = {
|
||||||
"format": "json",
|
"format": "json",
|
||||||
"addressdetails": 1,
|
"addressdetails": 1,
|
||||||
"q": raw_data,
|
"q": raw_data,
|
||||||
|
|
|
||||||
173
core/models.py
173
core/models.py
|
|
@ -56,7 +56,7 @@ logger = logging.getLogger(__name__)
|
||||||
class AttributeGroup(ExportModelOperationsMixin("attribute_group"), NiceModel):
|
class AttributeGroup(ExportModelOperationsMixin("attribute_group"), NiceModel):
|
||||||
is_publicly_visible = True
|
is_publicly_visible = True
|
||||||
|
|
||||||
parent = ForeignKey(
|
parent: ForeignKey = ForeignKey(
|
||||||
"self",
|
"self",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
null=True,
|
null=True,
|
||||||
|
|
@ -65,7 +65,7 @@ class AttributeGroup(ExportModelOperationsMixin("attribute_group"), NiceModel):
|
||||||
help_text=_("parent of this group"),
|
help_text=_("parent of this group"),
|
||||||
verbose_name=_("parent attribute group"),
|
verbose_name=_("parent attribute group"),
|
||||||
)
|
)
|
||||||
name = CharField(
|
name: CharField = CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
verbose_name=_("attribute group's name"),
|
verbose_name=_("attribute group's name"),
|
||||||
help_text=_("attribute group's name"),
|
help_text=_("attribute group's name"),
|
||||||
|
|
@ -83,21 +83,21 @@ class AttributeGroup(ExportModelOperationsMixin("attribute_group"), NiceModel):
|
||||||
class Attribute(ExportModelOperationsMixin("attribute"), NiceModel):
|
class Attribute(ExportModelOperationsMixin("attribute"), NiceModel):
|
||||||
is_publicly_visible = True
|
is_publicly_visible = True
|
||||||
|
|
||||||
categories = ManyToManyField(
|
categories: ManyToManyField = ManyToManyField(
|
||||||
"core.Category",
|
"core.Category",
|
||||||
related_name="attributes",
|
related_name="attributes",
|
||||||
help_text=_("category of this attribute"),
|
help_text=_("category of this attribute"),
|
||||||
verbose_name=_("categories"),
|
verbose_name=_("categories"),
|
||||||
)
|
)
|
||||||
|
|
||||||
group = ForeignKey(
|
group: ForeignKey = ForeignKey(
|
||||||
"core.AttributeGroup",
|
"core.AttributeGroup",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
related_name="attributes",
|
related_name="attributes",
|
||||||
help_text=_("group of this attribute"),
|
help_text=_("group of this attribute"),
|
||||||
verbose_name=_("attribute group"),
|
verbose_name=_("attribute group"),
|
||||||
)
|
)
|
||||||
value_type = CharField(
|
value_type: CharField = CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=[
|
choices=[
|
||||||
("string", _("string")),
|
("string", _("string")),
|
||||||
|
|
@ -111,7 +111,7 @@ class Attribute(ExportModelOperationsMixin("attribute"), NiceModel):
|
||||||
verbose_name=_("value type"),
|
verbose_name=_("value type"),
|
||||||
)
|
)
|
||||||
|
|
||||||
name = CharField(
|
name: CharField = CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=_("name of this attribute"),
|
help_text=_("name of this attribute"),
|
||||||
verbose_name=_("attribute's name"),
|
verbose_name=_("attribute's name"),
|
||||||
|
|
@ -129,14 +129,14 @@ class Attribute(ExportModelOperationsMixin("attribute"), NiceModel):
|
||||||
class AttributeValue(ExportModelOperationsMixin("attribute_value"), NiceModel):
|
class AttributeValue(ExportModelOperationsMixin("attribute_value"), NiceModel):
|
||||||
is_publicly_visible = True
|
is_publicly_visible = True
|
||||||
|
|
||||||
attribute = ForeignKey(
|
attribute: ForeignKey = ForeignKey(
|
||||||
"core.Attribute",
|
"core.Attribute",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
related_name="values",
|
related_name="values",
|
||||||
help_text=_("attribute of this value"),
|
help_text=_("attribute of this value"),
|
||||||
verbose_name=_("attribute"),
|
verbose_name=_("attribute"),
|
||||||
)
|
)
|
||||||
product = ForeignKey(
|
product: ForeignKey = ForeignKey(
|
||||||
"core.Product",
|
"core.Product",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
blank=False,
|
blank=False,
|
||||||
|
|
@ -145,7 +145,7 @@ class AttributeValue(ExportModelOperationsMixin("attribute_value"), NiceModel):
|
||||||
verbose_name=_("associated product"),
|
verbose_name=_("associated product"),
|
||||||
related_name="attributes",
|
related_name="attributes",
|
||||||
)
|
)
|
||||||
value = TextField(
|
value: TextField = TextField(
|
||||||
verbose_name=_("attribute value"),
|
verbose_name=_("attribute value"),
|
||||||
help_text=_("the specific value for this attribute"),
|
help_text=_("the specific value for this attribute"),
|
||||||
)
|
)
|
||||||
|
|
@ -169,7 +169,7 @@ class Category(ExportModelOperationsMixin("category"), NiceModel, MPTTModel):
|
||||||
validators=[validate_category_image_dimensions],
|
validators=[validate_category_image_dimensions],
|
||||||
verbose_name=_("category image"),
|
verbose_name=_("category image"),
|
||||||
)
|
)
|
||||||
markup_percent = IntegerField(
|
markup_percent: IntegerField = IntegerField(
|
||||||
default=0,
|
default=0,
|
||||||
validators=[MinValueValidator(0), MaxValueValidator(100)],
|
validators=[MinValueValidator(0), MaxValueValidator(100)],
|
||||||
help_text=_("define a markup percentage for products in this category"),
|
help_text=_("define a markup percentage for products in this category"),
|
||||||
|
|
@ -185,28 +185,28 @@ class Category(ExportModelOperationsMixin("category"), NiceModel, MPTTModel):
|
||||||
verbose_name=_("parent category"),
|
verbose_name=_("parent category"),
|
||||||
)
|
)
|
||||||
|
|
||||||
name = CharField(
|
name: CharField = CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
verbose_name=_("category name"),
|
verbose_name=_("category name"),
|
||||||
help_text=_("provide a name for this category"),
|
help_text=_("provide a name for this category"),
|
||||||
unique=True,
|
unique=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
description = TextField( # noqa: DJ001
|
description: TextField = TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text=_("add a detailed description for this category"),
|
help_text=_("add a detailed description for this category"),
|
||||||
verbose_name=_("category description"),
|
verbose_name=_("category description"),
|
||||||
)
|
)
|
||||||
|
|
||||||
slug = AutoSlugField(
|
slug: AutoSlugField = AutoSlugField(
|
||||||
populate_from=("uuid", "name"),
|
populate_from=("uuid", "name"),
|
||||||
allow_unicode=True,
|
allow_unicode=True,
|
||||||
unique=True,
|
unique=True,
|
||||||
editable=False,
|
editable=False,
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
tags = ManyToManyField(
|
tags: ManyToManyField = ManyToManyField(
|
||||||
"core.CategoryTag",
|
"core.CategoryTag",
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_("tags that help describe or group this category"),
|
help_text=_("tags that help describe or group this category"),
|
||||||
|
|
@ -230,7 +230,7 @@ class Category(ExportModelOperationsMixin("category"), NiceModel, MPTTModel):
|
||||||
class Brand(ExportModelOperationsMixin("brand"), NiceModel):
|
class Brand(ExportModelOperationsMixin("brand"), NiceModel):
|
||||||
is_publicly_visible = True
|
is_publicly_visible = True
|
||||||
|
|
||||||
name = CharField(
|
name: CharField = CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=_("name of this brand"),
|
help_text=_("name of this brand"),
|
||||||
verbose_name=_("brand name"),
|
verbose_name=_("brand name"),
|
||||||
|
|
@ -252,13 +252,13 @@ class Brand(ExportModelOperationsMixin("brand"), NiceModel):
|
||||||
validators=[validate_category_image_dimensions],
|
validators=[validate_category_image_dimensions],
|
||||||
verbose_name=_("brand big image"),
|
verbose_name=_("brand big image"),
|
||||||
)
|
)
|
||||||
description = TextField( # noqa: DJ001
|
description: TextField = TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text=_("add a detailed description of the brand"),
|
help_text=_("add a detailed description of the brand"),
|
||||||
verbose_name=_("brand description"),
|
verbose_name=_("brand description"),
|
||||||
)
|
)
|
||||||
categories = ManyToManyField(
|
categories: ManyToManyField = ManyToManyField(
|
||||||
"core.Category",
|
"core.Category",
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_("optional categories that this brand is associated with"),
|
help_text=_("optional categories that this brand is associated with"),
|
||||||
|
|
@ -276,14 +276,14 @@ class Brand(ExportModelOperationsMixin("brand"), NiceModel):
|
||||||
class Product(ExportModelOperationsMixin("product"), NiceModel):
|
class Product(ExportModelOperationsMixin("product"), NiceModel):
|
||||||
is_publicly_visible = True
|
is_publicly_visible = True
|
||||||
|
|
||||||
category = ForeignKey(
|
category: ForeignKey = ForeignKey(
|
||||||
"core.Category",
|
"core.Category",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
help_text=_("category this product belongs to"),
|
help_text=_("category this product belongs to"),
|
||||||
verbose_name=_("category"),
|
verbose_name=_("category"),
|
||||||
related_name="products",
|
related_name="products",
|
||||||
)
|
)
|
||||||
brand = ForeignKey(
|
brand: ForeignKey = ForeignKey(
|
||||||
"core.Brand",
|
"core.Brand",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
blank=True,
|
blank=True,
|
||||||
|
|
@ -291,31 +291,31 @@ class Product(ExportModelOperationsMixin("product"), NiceModel):
|
||||||
help_text=_("optionally associate this product with a brand"),
|
help_text=_("optionally associate this product with a brand"),
|
||||||
verbose_name=_("brand"),
|
verbose_name=_("brand"),
|
||||||
)
|
)
|
||||||
tags = ManyToManyField(
|
tags: ManyToManyField = ManyToManyField(
|
||||||
"core.ProductTag",
|
"core.ProductTag",
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_("tags that help describe or group this product"),
|
help_text=_("tags that help describe or group this product"),
|
||||||
verbose_name=_("product tags"),
|
verbose_name=_("product tags"),
|
||||||
)
|
)
|
||||||
is_digital = BooleanField(
|
is_digital: BooleanField = BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
help_text=_("indicates whether this product is digitally delivered"),
|
help_text=_("indicates whether this product is digitally delivered"),
|
||||||
verbose_name=_("is product digital"),
|
verbose_name=_("is product digital"),
|
||||||
blank=False,
|
blank=False,
|
||||||
null=False,
|
null=False,
|
||||||
)
|
)
|
||||||
name = CharField(
|
name: CharField = CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=_("provide a clear identifying name for the product"),
|
help_text=_("provide a clear identifying name for the product"),
|
||||||
verbose_name=_("product name"),
|
verbose_name=_("product name"),
|
||||||
)
|
)
|
||||||
description = TextField( # noqa: DJ001
|
description: TextField = TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text=_("add a detailed description of the product"),
|
help_text=_("add a detailed description of the product"),
|
||||||
verbose_name=_("product description"),
|
verbose_name=_("product description"),
|
||||||
)
|
)
|
||||||
partnumber = CharField( # noqa: DJ001
|
partnumber: CharField = CharField(
|
||||||
unique=True,
|
unique=True,
|
||||||
default=None,
|
default=None,
|
||||||
blank=False,
|
blank=False,
|
||||||
|
|
@ -388,13 +388,13 @@ class Vendor(ExportModelOperationsMixin("vendor"), NiceModel):
|
||||||
help_text=_("stores credentials and endpoints required for vendor communication"),
|
help_text=_("stores credentials and endpoints required for vendor communication"),
|
||||||
verbose_name=_("authentication info"),
|
verbose_name=_("authentication info"),
|
||||||
)
|
)
|
||||||
markup_percent = IntegerField(
|
markup_percent: IntegerField = IntegerField(
|
||||||
default=0,
|
default=0,
|
||||||
validators=[MinValueValidator(0), MaxValueValidator(100)],
|
validators=[MinValueValidator(0), MaxValueValidator(100)],
|
||||||
help_text=_("define the markup for products retrieved from this vendor"),
|
help_text=_("define the markup for products retrieved from this vendor"),
|
||||||
verbose_name=_("vendor markup percentage"),
|
verbose_name=_("vendor markup percentage"),
|
||||||
)
|
)
|
||||||
name = CharField(
|
name: CharField = CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=_("name of this vendor"),
|
help_text=_("name of this vendor"),
|
||||||
verbose_name=_("vendor name"),
|
verbose_name=_("vendor name"),
|
||||||
|
|
@ -417,13 +417,13 @@ class Vendor(ExportModelOperationsMixin("vendor"), NiceModel):
|
||||||
class Feedback(ExportModelOperationsMixin("feedback"), NiceModel):
|
class Feedback(ExportModelOperationsMixin("feedback"), NiceModel):
|
||||||
is_publicly_visible = True
|
is_publicly_visible = True
|
||||||
|
|
||||||
comment = TextField( # noqa: DJ001
|
comment: TextField = TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text=_("user-provided comments about their experience with the product"),
|
help_text=_("user-provided comments about their experience with the product"),
|
||||||
verbose_name=_("feedback comments"),
|
verbose_name=_("feedback comments"),
|
||||||
)
|
)
|
||||||
order_product = OneToOneField(
|
order_product: OneToOneField = OneToOneField(
|
||||||
"core.OrderProduct",
|
"core.OrderProduct",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
blank=False,
|
blank=False,
|
||||||
|
|
@ -431,7 +431,7 @@ class Feedback(ExportModelOperationsMixin("feedback"), NiceModel):
|
||||||
help_text=_("references the specific product in an order that this feedback is about"),
|
help_text=_("references the specific product in an order that this feedback is about"),
|
||||||
verbose_name=_("related order product"),
|
verbose_name=_("related order product"),
|
||||||
)
|
)
|
||||||
rating = FloatField(
|
rating: FloatField = FloatField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text=_("user-assigned rating for the product"),
|
help_text=_("user-assigned rating for the product"),
|
||||||
|
|
@ -450,7 +450,7 @@ class Feedback(ExportModelOperationsMixin("feedback"), NiceModel):
|
||||||
class Order(ExportModelOperationsMixin("order"), NiceModel):
|
class Order(ExportModelOperationsMixin("order"), NiceModel):
|
||||||
is_publicly_visible = False
|
is_publicly_visible = False
|
||||||
|
|
||||||
billing_address = ForeignKey(
|
billing_address: ForeignKey = ForeignKey(
|
||||||
"core.Address",
|
"core.Address",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
blank=True,
|
blank=True,
|
||||||
|
|
@ -459,7 +459,7 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
||||||
help_text=_("the billing address used for this order"),
|
help_text=_("the billing address used for this order"),
|
||||||
verbose_name=_("billing address"),
|
verbose_name=_("billing address"),
|
||||||
)
|
)
|
||||||
promo_code = ForeignKey(
|
promo_code: ForeignKey = ForeignKey(
|
||||||
"core.PromoCode",
|
"core.PromoCode",
|
||||||
on_delete=PROTECT,
|
on_delete=PROTECT,
|
||||||
blank=True,
|
blank=True,
|
||||||
|
|
@ -467,7 +467,7 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
||||||
help_text=_("optional promo code applied to this order"),
|
help_text=_("optional promo code applied to this order"),
|
||||||
verbose_name=_("applied promo code"),
|
verbose_name=_("applied promo code"),
|
||||||
)
|
)
|
||||||
shipping_address = ForeignKey(
|
shipping_address: ForeignKey = ForeignKey(
|
||||||
"core.Address",
|
"core.Address",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
blank=True,
|
blank=True,
|
||||||
|
|
@ -476,7 +476,7 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
||||||
help_text=_("the shipping address used for this order"),
|
help_text=_("the shipping address used for this order"),
|
||||||
verbose_name=_("shipping address"),
|
verbose_name=_("shipping address"),
|
||||||
)
|
)
|
||||||
status = CharField(
|
status: CharField = CharField(
|
||||||
default="PENDING",
|
default="PENDING",
|
||||||
max_length=64,
|
max_length=64,
|
||||||
choices=ORDER_STATUS_CHOICES,
|
choices=ORDER_STATUS_CHOICES,
|
||||||
|
|
@ -495,7 +495,7 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
||||||
help_text=_("json representation of order attributes for this order"),
|
help_text=_("json representation of order attributes for this order"),
|
||||||
verbose_name=_("attributes"),
|
verbose_name=_("attributes"),
|
||||||
)
|
)
|
||||||
user = ForeignKey(
|
user: ForeignKey = ForeignKey(
|
||||||
"vibes_auth.User",
|
"vibes_auth.User",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
help_text=_("the user who placed the order"),
|
help_text=_("the user who placed the order"),
|
||||||
|
|
@ -504,14 +504,14 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
buy_time = DateTimeField(
|
buy_time: DateTimeField = DateTimeField(
|
||||||
help_text=_("the timestamp when the order was finalized"),
|
help_text=_("the timestamp when the order was finalized"),
|
||||||
verbose_name=_("buy time"),
|
verbose_name=_("buy time"),
|
||||||
default=None,
|
default=None,
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
human_readable_id = CharField(
|
human_readable_id: CharField = CharField(
|
||||||
max_length=8,
|
max_length=8,
|
||||||
help_text=_("a human-readable identifier for the order"),
|
help_text=_("a human-readable identifier for the order"),
|
||||||
verbose_name=_("human readable id"),
|
verbose_name=_("human readable id"),
|
||||||
|
|
@ -524,7 +524,7 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
||||||
verbose_name_plural = _("orders")
|
verbose_name_plural = _("orders")
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"#{self.pk} for {self.user.email if self.user else 'unregistered user'}"
|
return f"#{self.pk} for {self.user.email if self.user else 'unregistered user'}" # type: ignore
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_business(self) -> bool:
|
def is_business(self) -> bool:
|
||||||
|
|
@ -576,7 +576,7 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
||||||
promotions = Promotion.objects.filter(is_active=True, products__in=[product]).order_by("discount_percent")
|
promotions = Promotion.objects.filter(is_active=True, products__in=[product]).order_by("discount_percent")
|
||||||
|
|
||||||
if promotions.exists():
|
if promotions.exists():
|
||||||
buy_price -= round(product.price * (promotions.first().discount_percent / 100), 2)
|
buy_price -= round(product.price * (promotions.first().discount_percent / 100), 2) # type: ignore
|
||||||
|
|
||||||
order_product, is_created = OrderProduct.objects.get_or_create(
|
order_product, is_created = OrderProduct.objects.get_or_create(
|
||||||
product=product,
|
product=product,
|
||||||
|
|
@ -597,7 +597,12 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
||||||
name = "Product"
|
name = "Product"
|
||||||
raise Http404(_(f"{name} does not exist: {product_uuid}"))
|
raise Http404(_(f"{name} does not exist: {product_uuid}"))
|
||||||
|
|
||||||
def remove_product(self, product_uuid: str | None = None, attributes: dict = dict, zero_quantity: bool = False):
|
def remove_product(
|
||||||
|
self, product_uuid: str | None = None, attributes: dict | None = None, zero_quantity: bool = False
|
||||||
|
):
|
||||||
|
if attributes is None:
|
||||||
|
attributes = {}
|
||||||
|
|
||||||
if self.status not in ["PENDING", "MOMENTAL"]:
|
if self.status not in ["PENDING", "MOMENTAL"]:
|
||||||
raise ValueError(_("you cannot remove products from an order that is not a pending one"))
|
raise ValueError(_("you cannot remove products from an order that is not a pending one"))
|
||||||
try:
|
try:
|
||||||
|
|
@ -818,31 +823,31 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
||||||
class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel):
|
class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel):
|
||||||
is_publicly_visible = False
|
is_publicly_visible = False
|
||||||
|
|
||||||
buy_price = FloatField(
|
buy_price: FloatField = FloatField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text=_("the price paid by the customer for this product at purchase time"),
|
help_text=_("the price paid by the customer for this product at purchase time"),
|
||||||
verbose_name=_("purchase price at order time"),
|
verbose_name=_("purchase price at order time"),
|
||||||
)
|
)
|
||||||
comments = TextField( # noqa: DJ001
|
comments: TextField = TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text=_("internal comments for admins about this ordered product"),
|
help_text=_("internal comments for admins about this ordered product"),
|
||||||
verbose_name=_("internal comments"),
|
verbose_name=_("internal comments"),
|
||||||
)
|
)
|
||||||
notifications = JSONField(
|
notifications: JSONField = JSONField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text=_("json structure of notifications to display to users"),
|
help_text=_("json structure of notifications to display to users"),
|
||||||
verbose_name=_("user notifications"),
|
verbose_name=_("user notifications"),
|
||||||
)
|
)
|
||||||
attributes = JSONField(
|
attributes: JSONField = JSONField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text=_("json representation of this item's attributes"),
|
help_text=_("json representation of this item's attributes"),
|
||||||
verbose_name=_("ordered product attributes"),
|
verbose_name=_("ordered product attributes"),
|
||||||
)
|
)
|
||||||
order = ForeignKey(
|
order: ForeignKey = ForeignKey(
|
||||||
"core.Order",
|
"core.Order",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
help_text=_("reference to the parent order that contains this product"),
|
help_text=_("reference to the parent order that contains this product"),
|
||||||
|
|
@ -850,7 +855,7 @@ class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel):
|
||||||
related_name="order_products",
|
related_name="order_products",
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
product = ForeignKey(
|
product: ForeignKey = ForeignKey(
|
||||||
"core.Product",
|
"core.Product",
|
||||||
on_delete=PROTECT,
|
on_delete=PROTECT,
|
||||||
blank=True,
|
blank=True,
|
||||||
|
|
@ -858,14 +863,14 @@ class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel):
|
||||||
help_text=_("the specific product associated with this order line"),
|
help_text=_("the specific product associated with this order line"),
|
||||||
verbose_name=_("associated product"),
|
verbose_name=_("associated product"),
|
||||||
)
|
)
|
||||||
quantity = PositiveIntegerField(
|
quantity: PositiveIntegerField = PositiveIntegerField(
|
||||||
blank=False,
|
blank=False,
|
||||||
null=False,
|
null=False,
|
||||||
default=1,
|
default=1,
|
||||||
help_text=_("quantity of this specific product in the order"),
|
help_text=_("quantity of this specific product in the order"),
|
||||||
verbose_name=_("product quantity"),
|
verbose_name=_("product quantity"),
|
||||||
)
|
)
|
||||||
status = CharField(
|
status: CharField = CharField(
|
||||||
max_length=128,
|
max_length=128,
|
||||||
blank=False,
|
blank=False,
|
||||||
null=False,
|
null=False,
|
||||||
|
|
@ -921,7 +926,7 @@ class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel):
|
||||||
return self.download.url
|
return self.download.url
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def do_feedback(self, rating: int = 10, comment: str = "", action: str = "add"):
|
def do_feedback(self, rating: int = 10, comment: str = "", action: str = "add") -> None:
|
||||||
if action not in ["add", "remove"]:
|
if action not in ["add", "remove"]:
|
||||||
raise ValueError(_(f"wrong action specified for feedback: {action}"))
|
raise ValueError(_(f"wrong action specified for feedback: {action}"))
|
||||||
if action == "remove" and self.feedback:
|
if action == "remove" and self.feedback:
|
||||||
|
|
@ -938,14 +943,14 @@ class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel):
|
||||||
class ProductTag(ExportModelOperationsMixin("product_tag"), NiceModel):
|
class ProductTag(ExportModelOperationsMixin("product_tag"), NiceModel):
|
||||||
is_publicly_visible = True
|
is_publicly_visible = True
|
||||||
|
|
||||||
tag_name = CharField(
|
tag_name: CharField = CharField(
|
||||||
blank=False,
|
blank=False,
|
||||||
null=False,
|
null=False,
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=_("internal tag identifier for the product tag"),
|
help_text=_("internal tag identifier for the product tag"),
|
||||||
verbose_name=_("tag name"),
|
verbose_name=_("tag name"),
|
||||||
)
|
)
|
||||||
name = CharField(
|
name: CharField = CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=_("user-friendly name for the product tag"),
|
help_text=_("user-friendly name for the product tag"),
|
||||||
verbose_name=_("tag display name"),
|
verbose_name=_("tag display name"),
|
||||||
|
|
@ -963,14 +968,14 @@ class ProductTag(ExportModelOperationsMixin("product_tag"), NiceModel):
|
||||||
class CategoryTag(ExportModelOperationsMixin("category_tag"), NiceModel):
|
class CategoryTag(ExportModelOperationsMixin("category_tag"), NiceModel):
|
||||||
is_publicly_visible = True
|
is_publicly_visible = True
|
||||||
|
|
||||||
tag_name = CharField(
|
tag_name: CharField = CharField(
|
||||||
blank=False,
|
blank=False,
|
||||||
null=False,
|
null=False,
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=_("internal tag identifier for the product tag"),
|
help_text=_("internal tag identifier for the product tag"),
|
||||||
verbose_name=_("tag name"),
|
verbose_name=_("tag name"),
|
||||||
)
|
)
|
||||||
name = CharField(
|
name: CharField = CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=_("user-friendly name for the product tag"),
|
help_text=_("user-friendly name for the product tag"),
|
||||||
verbose_name=_("tag display name"),
|
verbose_name=_("tag display name"),
|
||||||
|
|
@ -988,7 +993,7 @@ class CategoryTag(ExportModelOperationsMixin("category_tag"), NiceModel):
|
||||||
class ProductImage(ExportModelOperationsMixin("product_image"), NiceModel):
|
class ProductImage(ExportModelOperationsMixin("product_image"), NiceModel):
|
||||||
is_publicly_visible = True
|
is_publicly_visible = True
|
||||||
|
|
||||||
alt = CharField(
|
alt: CharField = CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=_("provide alternative text for the image for accessibility"),
|
help_text=_("provide alternative text for the image for accessibility"),
|
||||||
verbose_name=_("image alt text"),
|
verbose_name=_("image alt text"),
|
||||||
|
|
@ -998,13 +1003,13 @@ class ProductImage(ExportModelOperationsMixin("product_image"), NiceModel):
|
||||||
verbose_name=_("product image"),
|
verbose_name=_("product image"),
|
||||||
upload_to=get_product_uuid_as_path,
|
upload_to=get_product_uuid_as_path,
|
||||||
)
|
)
|
||||||
priority = IntegerField(
|
priority: IntegerField = IntegerField(
|
||||||
default=1,
|
default=1,
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
help_text=_("determines the order in which images are displayed"),
|
help_text=_("determines the order in which images are displayed"),
|
||||||
verbose_name=_("display priority"),
|
verbose_name=_("display priority"),
|
||||||
)
|
)
|
||||||
product = ForeignKey(
|
product: ForeignKey = ForeignKey(
|
||||||
"core.Product",
|
"core.Product",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
help_text=_("the product that this image represents"),
|
help_text=_("the product that this image represents"),
|
||||||
|
|
@ -1027,7 +1032,7 @@ class ProductImage(ExportModelOperationsMixin("product_image"), NiceModel):
|
||||||
class PromoCode(ExportModelOperationsMixin("promocode"), NiceModel):
|
class PromoCode(ExportModelOperationsMixin("promocode"), NiceModel):
|
||||||
is_publicly_visible = False
|
is_publicly_visible = False
|
||||||
|
|
||||||
code = CharField(
|
code: CharField = CharField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
unique=True,
|
unique=True,
|
||||||
default=get_random_code,
|
default=get_random_code,
|
||||||
|
|
@ -1042,7 +1047,7 @@ class PromoCode(ExportModelOperationsMixin("promocode"), NiceModel):
|
||||||
help_text=_("fixed discount amount applied if percent is not used"),
|
help_text=_("fixed discount amount applied if percent is not used"),
|
||||||
verbose_name=_("fixed discount amount"),
|
verbose_name=_("fixed discount amount"),
|
||||||
)
|
)
|
||||||
discount_percent = IntegerField(
|
discount_percent: IntegerField = IntegerField(
|
||||||
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
|
|
@ -1067,7 +1072,7 @@ class PromoCode(ExportModelOperationsMixin("promocode"), NiceModel):
|
||||||
help_text=_("timestamp when the promocode was used, blank if not used yet"),
|
help_text=_("timestamp when the promocode was used, blank if not used yet"),
|
||||||
verbose_name=_("usage timestamp"),
|
verbose_name=_("usage timestamp"),
|
||||||
)
|
)
|
||||||
user = ForeignKey(
|
user: ForeignKey = ForeignKey(
|
||||||
"vibes_auth.User",
|
"vibes_auth.User",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
help_text=_("user assigned to this promocode if applicable"),
|
help_text=_("user assigned to this promocode if applicable"),
|
||||||
|
|
@ -1125,12 +1130,12 @@ class PromoCode(ExportModelOperationsMixin("promocode"), NiceModel):
|
||||||
class Promotion(ExportModelOperationsMixin("promotion"), NiceModel):
|
class Promotion(ExportModelOperationsMixin("promotion"), NiceModel):
|
||||||
is_publicly_visible = True
|
is_publicly_visible = True
|
||||||
|
|
||||||
discount_percent = IntegerField(
|
discount_percent: IntegerField = IntegerField(
|
||||||
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
||||||
help_text=_("percentage discount for the selected products"),
|
help_text=_("percentage discount for the selected products"),
|
||||||
verbose_name=_("discount percentage"),
|
verbose_name=_("discount percentage"),
|
||||||
)
|
)
|
||||||
name = CharField(
|
name: CharField = CharField(
|
||||||
max_length=256,
|
max_length=256,
|
||||||
unique=True,
|
unique=True,
|
||||||
help_text=_("provide a unique name for this promotion"),
|
help_text=_("provide a unique name for this promotion"),
|
||||||
|
|
@ -1142,7 +1147,7 @@ class Promotion(ExportModelOperationsMixin("promotion"), NiceModel):
|
||||||
help_text=_("add a detailed description of the product"),
|
help_text=_("add a detailed description of the product"),
|
||||||
verbose_name=_("promotion description"),
|
verbose_name=_("promotion description"),
|
||||||
)
|
)
|
||||||
products = ManyToManyField(
|
products: ManyToManyField = ManyToManyField(
|
||||||
"core.Product",
|
"core.Product",
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_("select which products are included in this promotion"),
|
help_text=_("select which products are included in this promotion"),
|
||||||
|
|
@ -1162,7 +1167,7 @@ class Promotion(ExportModelOperationsMixin("promotion"), NiceModel):
|
||||||
class Stock(ExportModelOperationsMixin("stock"), NiceModel):
|
class Stock(ExportModelOperationsMixin("stock"), NiceModel):
|
||||||
is_publicly_visible = False
|
is_publicly_visible = False
|
||||||
|
|
||||||
vendor = ForeignKey(
|
vendor: ForeignKey = ForeignKey(
|
||||||
"core.Vendor",
|
"core.Vendor",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
help_text=_("the vendor supplying this product stock"),
|
help_text=_("the vendor supplying this product stock"),
|
||||||
|
|
@ -1173,7 +1178,7 @@ class Stock(ExportModelOperationsMixin("stock"), NiceModel):
|
||||||
help_text=_("final price to the customer after markups"),
|
help_text=_("final price to the customer after markups"),
|
||||||
verbose_name=_("selling price"),
|
verbose_name=_("selling price"),
|
||||||
)
|
)
|
||||||
product = ForeignKey(
|
product: ForeignKey = ForeignKey(
|
||||||
"core.Product",
|
"core.Product",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
help_text=_("the product associated with this stock entry"),
|
help_text=_("the product associated with this stock entry"),
|
||||||
|
|
@ -1187,12 +1192,12 @@ class Stock(ExportModelOperationsMixin("stock"), NiceModel):
|
||||||
help_text=_("the price paid to the vendor for this product"),
|
help_text=_("the price paid to the vendor for this product"),
|
||||||
verbose_name=_("vendor purchase price"),
|
verbose_name=_("vendor purchase price"),
|
||||||
)
|
)
|
||||||
quantity = IntegerField(
|
quantity: IntegerField = IntegerField(
|
||||||
default=0,
|
default=0,
|
||||||
help_text=_("available quantity of the product in stock"),
|
help_text=_("available quantity of the product in stock"),
|
||||||
verbose_name=_("quantity in stock"),
|
verbose_name=_("quantity in stock"),
|
||||||
)
|
)
|
||||||
sku = CharField(
|
sku: CharField = CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=_("vendor-assigned SKU for identifying the product"),
|
help_text=_("vendor-assigned SKU for identifying the product"),
|
||||||
verbose_name=_("vendor sku"),
|
verbose_name=_("vendor sku"),
|
||||||
|
|
@ -1217,13 +1222,13 @@ class Stock(ExportModelOperationsMixin("stock"), NiceModel):
|
||||||
class Wishlist(ExportModelOperationsMixin("wishlist"), NiceModel):
|
class Wishlist(ExportModelOperationsMixin("wishlist"), NiceModel):
|
||||||
is_publicly_visible = False
|
is_publicly_visible = False
|
||||||
|
|
||||||
products = ManyToManyField(
|
products: ManyToManyField = ManyToManyField(
|
||||||
"core.Product",
|
"core.Product",
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_("products that the user has marked as wanted"),
|
help_text=_("products that the user has marked as wanted"),
|
||||||
verbose_name=_("wishlisted products"),
|
verbose_name=_("wishlisted products"),
|
||||||
)
|
)
|
||||||
user = OneToOneField(
|
user: OneToOneField = OneToOneField(
|
||||||
"vibes_auth.User",
|
"vibes_auth.User",
|
||||||
on_delete=CASCADE,
|
on_delete=CASCADE,
|
||||||
blank=True,
|
blank=True,
|
||||||
|
|
@ -1278,8 +1283,8 @@ class Wishlist(ExportModelOperationsMixin("wishlist"), NiceModel):
|
||||||
class DigitalAssetDownload(ExportModelOperationsMixin("attribute_group"), NiceModel):
|
class DigitalAssetDownload(ExportModelOperationsMixin("attribute_group"), NiceModel):
|
||||||
is_publicly_visible = False
|
is_publicly_visible = False
|
||||||
|
|
||||||
order_product = OneToOneField(to=OrderProduct, on_delete=CASCADE, related_name="download")
|
order_product: OneToOneField = OneToOneField(to=OrderProduct, on_delete=CASCADE, related_name="download")
|
||||||
num_downloads = IntegerField(default=0)
|
num_downloads: IntegerField = IntegerField(default=0)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("download")
|
verbose_name = _("download")
|
||||||
|
|
@ -1293,13 +1298,15 @@ class DigitalAssetDownload(ExportModelOperationsMixin("attribute_group"), NiceMo
|
||||||
if self.order_product.status != "FINISHED":
|
if self.order_product.status != "FINISHED":
|
||||||
raise ValueError(_("you can not download a digital asset for a non-finished order"))
|
raise ValueError(_("you can not download a digital asset for a non-finished order"))
|
||||||
|
|
||||||
return f"https://api.{config.BASE_URL}/download/{urlsafe_base64_encode(force_bytes(self.order_product.uuid))}"
|
return (
|
||||||
|
f"https://api.{config.BASE_DOMAIN}/download/{urlsafe_base64_encode(force_bytes(self.order_product.uuid))}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Documentary(ExportModelOperationsMixin("attribute_group"), NiceModel):
|
class Documentary(ExportModelOperationsMixin("attribute_group"), NiceModel):
|
||||||
is_publicly_visible = True
|
is_publicly_visible = True
|
||||||
|
|
||||||
product = ForeignKey(to=Product, on_delete=CASCADE, related_name="documentaries")
|
product: ForeignKey = ForeignKey(to=Product, on_delete=CASCADE, related_name="documentaries")
|
||||||
document = FileField(upload_to=get_product_uuid_as_path)
|
document = FileField(upload_to=get_product_uuid_as_path)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -1326,22 +1333,26 @@ class Address(ExportModelOperationsMixin("address"), NiceModel):
|
||||||
help_text=_("address line for the customer"),
|
help_text=_("address line for the customer"),
|
||||||
verbose_name=_("address line"),
|
verbose_name=_("address line"),
|
||||||
)
|
)
|
||||||
street = CharField(_("street"), max_length=255, null=True) # noqa: DJ001
|
street: CharField = CharField(_("street"), max_length=255, null=True)
|
||||||
district = CharField(_("district"), max_length=255, null=True) # noqa: DJ001
|
district: CharField = CharField(_("district"), max_length=255, null=True)
|
||||||
city = CharField(_("city"), max_length=100, null=True) # noqa: DJ001
|
city: CharField = CharField(_("city"), max_length=100, null=True)
|
||||||
region = CharField(_("region"), max_length=100, null=True) # noqa: DJ001
|
region: CharField = CharField(_("region"), max_length=100, null=True)
|
||||||
postal_code = CharField(_("postal code"), max_length=20, null=True) # noqa: DJ001
|
postal_code: CharField = CharField(_("postal code"), max_length=20, null=True)
|
||||||
country = CharField(_("country"), max_length=40, null=True) # noqa: DJ001
|
country: CharField = CharField(_("country"), max_length=40, null=True)
|
||||||
|
|
||||||
location = PointField(
|
location: PointField = PointField(
|
||||||
geography=True, srid=4326, null=True, blank=True, help_text=_("geolocation point: (longitude, latitude)")
|
geography=True, srid=4326, null=True, blank=True, help_text=_("geolocation point: (longitude, latitude)")
|
||||||
)
|
)
|
||||||
|
|
||||||
raw_data = JSONField(blank=True, null=True, help_text=_("full JSON response from geocoder for this address"))
|
raw_data: JSONField = JSONField(
|
||||||
|
blank=True, null=True, help_text=_("full JSON response from geocoder for this address")
|
||||||
|
)
|
||||||
|
|
||||||
api_response = JSONField(blank=True, null=True, help_text=_("stored JSON response from the geocoding service"))
|
api_response: JSONField = JSONField(
|
||||||
|
blank=True, null=True, help_text=_("stored JSON response from the geocoding service")
|
||||||
|
)
|
||||||
|
|
||||||
user = ForeignKey(to="vibes_auth.User", on_delete=CASCADE, blank=True, null=True)
|
user: ForeignKey = ForeignKey(to="vibes_auth.User", on_delete=CASCADE, blank=True, null=True)
|
||||||
|
|
||||||
objects = AddressManager()
|
objects = AddressManager()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ class IsOwnerOrReadOnly(permissions.BasePermission):
|
||||||
return obj.user == request.user
|
return obj.user == request.user
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyProtectedMember
|
||||||
class EvibesPermission(permissions.BasePermission):
|
class EvibesPermission(permissions.BasePermission):
|
||||||
ACTION_PERM_MAP = {
|
ACTION_PERM_MAP = {
|
||||||
"retrieve": "view",
|
"retrieve": "view",
|
||||||
|
|
|
||||||
|
|
@ -1,109 +1,3 @@
|
||||||
from rest_framework.fields import (
|
|
||||||
BooleanField,
|
|
||||||
CharField,
|
|
||||||
Field,
|
|
||||||
IntegerField,
|
|
||||||
JSONField,
|
|
||||||
ListField,
|
|
||||||
UUIDField,
|
|
||||||
)
|
|
||||||
from rest_framework.serializers import ListSerializer, Serializer
|
|
||||||
|
|
||||||
from .detail import * # noqa: F403
|
from .detail import * # noqa: F403
|
||||||
from .simple import * # noqa: F403
|
from .simple import * # noqa: F403
|
||||||
|
from .utility import * # noqa: F403
|
||||||
|
|
||||||
class CacheOperatorSerializer(Serializer):
|
|
||||||
key = CharField(required=True)
|
|
||||||
data = JSONField(required=False)
|
|
||||||
timeout = IntegerField(required=False)
|
|
||||||
|
|
||||||
|
|
||||||
class ContactUsSerializer(Serializer):
|
|
||||||
email = CharField(required=True)
|
|
||||||
name = CharField(required=True)
|
|
||||||
subject = CharField(required=True)
|
|
||||||
phone_number = CharField(required=False)
|
|
||||||
message = CharField(required=True)
|
|
||||||
|
|
||||||
|
|
||||||
class LanguageSerializer(Serializer):
|
|
||||||
code = CharField(required=True)
|
|
||||||
name = CharField(required=True)
|
|
||||||
flag = CharField()
|
|
||||||
|
|
||||||
|
|
||||||
class RecursiveField(Field):
|
|
||||||
def to_representation(self, value):
|
|
||||||
parent = self.parent
|
|
||||||
if isinstance(parent, ListSerializer):
|
|
||||||
parent = parent.parent
|
|
||||||
|
|
||||||
serializer_class = parent.__class__
|
|
||||||
return serializer_class(value, context=self.context).data
|
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
class AddOrderProductSerializer(Serializer):
|
|
||||||
product_uuid = CharField(required=True)
|
|
||||||
attributes = JSONField(required=False, default=dict)
|
|
||||||
|
|
||||||
|
|
||||||
class BulkAddOrderProductsSerializer(Serializer):
|
|
||||||
products = ListField(child=AddOrderProductSerializer(), required=True)
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveOrderProductSerializer(Serializer):
|
|
||||||
product_uuid = CharField(required=True)
|
|
||||||
attributes = JSONField(required=False, default=dict)
|
|
||||||
|
|
||||||
|
|
||||||
class BulkRemoveOrderProductsSerializer(Serializer):
|
|
||||||
products = ListField(child=RemoveOrderProductSerializer(), required=True)
|
|
||||||
|
|
||||||
|
|
||||||
class AddWishlistProductSerializer(Serializer):
|
|
||||||
product_uuid = CharField(required=True)
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveWishlistProductSerializer(Serializer):
|
|
||||||
product_uuid = CharField(required=True)
|
|
||||||
|
|
||||||
|
|
||||||
class BulkAddWishlistProductSerializer(Serializer):
|
|
||||||
product_uuids = ListField(child=CharField(required=True), allow_empty=False, max_length=64)
|
|
||||||
|
|
||||||
|
|
||||||
class BulkRemoveWishlistProductSerializer(Serializer):
|
|
||||||
product_uuids = ListField(child=CharField(required=True), allow_empty=False, max_length=64)
|
|
||||||
|
|
||||||
|
|
||||||
class BuyOrderSerializer(Serializer):
|
|
||||||
force_balance = BooleanField(required=False, default=False)
|
|
||||||
force_payment = BooleanField(required=False, default=False)
|
|
||||||
promocode_uuid = CharField(required=False)
|
|
||||||
shipping_address_uuid = CharField(required=False)
|
|
||||||
billing_address_uuid = CharField(required=False)
|
|
||||||
|
|
||||||
|
|
||||||
class BuyUnregisteredOrderSerializer(Serializer):
|
|
||||||
products = ListField(child=AddOrderProductSerializer(), required=True)
|
|
||||||
promocode_uuid = UUIDField(required=False)
|
|
||||||
customer_name = CharField(required=True)
|
|
||||||
customer_email = CharField(required=True)
|
|
||||||
customer_phone_number = CharField(required=True)
|
|
||||||
billing_customer_address_uuid = CharField(required=False)
|
|
||||||
shipping_customer_address_uuid = CharField(required=False)
|
|
||||||
payment_method = CharField(required=True)
|
|
||||||
|
|
||||||
|
|
||||||
class BuyAsBusinessOrderSerializer(Serializer):
|
|
||||||
products = ListField(child=AddOrderProductSerializer(), required=True)
|
|
||||||
business_inn = CharField(required=True)
|
|
||||||
business_email = CharField(required=True)
|
|
||||||
business_phone_number = CharField(required=True)
|
|
||||||
billing_business_address_uuid = CharField(required=False)
|
|
||||||
shipping_business_address_uuid = CharField(required=False)
|
|
||||||
payment_method = CharField(required=True)
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import logging
|
import logging
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from typing import Optional
|
from typing import Any, Collection, Optional
|
||||||
|
|
||||||
|
from django.contrib.auth.models import AnonymousUser
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.db.models.functions import Length
|
from django.db.models.functions import Length
|
||||||
from rest_framework.fields import JSONField, SerializerMethodField
|
from rest_framework.fields import JSONField, SerializerMethodField
|
||||||
|
from rest_framework.request import Request
|
||||||
from rest_framework.serializers import ModelSerializer
|
from rest_framework.serializers import ModelSerializer
|
||||||
from rest_framework_recursive.fields import RecursiveField
|
from rest_framework_recursive.fields import RecursiveField
|
||||||
|
|
||||||
|
|
@ -28,6 +30,7 @@ from core.models import (
|
||||||
)
|
)
|
||||||
from core.serializers.simple import CategorySimpleSerializer, ProductSimpleSerializer
|
from core.serializers.simple import CategorySimpleSerializer, ProductSimpleSerializer
|
||||||
from core.serializers.utility import AddressSerializer
|
from core.serializers.utility import AddressSerializer
|
||||||
|
from vibes_auth.models import User
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -77,8 +80,11 @@ class CategoryDetailSerializer(ModelSerializer):
|
||||||
if filterable_results:
|
if filterable_results:
|
||||||
return filterable_results
|
return filterable_results
|
||||||
|
|
||||||
request = self.context.get("request")
|
request: Request | None = self.context.get("request")
|
||||||
user = getattr(request, "user", None)
|
user: User | AnonymousUser | None = getattr(request, "user") # noqa: B009
|
||||||
|
|
||||||
|
if user is None:
|
||||||
|
user = AnonymousUser()
|
||||||
|
|
||||||
attributes = obj.attributes.all() if user.has_perm("view_attribute") else obj.attributes.filter(is_active=True)
|
attributes = obj.attributes.all() if user.has_perm("view_attribute") else obj.attributes.filter(is_active=True)
|
||||||
|
|
||||||
|
|
@ -100,12 +106,15 @@ class CategoryDetailSerializer(ModelSerializer):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if user is None:
|
||||||
|
user = AnonymousUser()
|
||||||
|
|
||||||
if not user.has_perm("view_attribute"):
|
if not user.has_perm("view_attribute"):
|
||||||
cache.set(f"{obj.uuid}_filterable_results", filterable_results, 86400)
|
cache.set(f"{obj.uuid}_filterable_results", filterable_results, 86400)
|
||||||
|
|
||||||
return filterable_results
|
return filterable_results
|
||||||
|
|
||||||
def get_children(self, obj) -> list[dict]:
|
def get_children(self, obj) -> Collection[Any]:
|
||||||
request = self.context.get("request")
|
request = self.context.get("request")
|
||||||
if request is not None and request.user.has_perm("view_category"):
|
if request is not None and request.user.has_perm("view_category"):
|
||||||
children = obj.children.all()
|
children = obj.children.all()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from typing import Optional
|
from typing import Collection, Optional
|
||||||
|
|
||||||
from rest_framework.fields import JSONField, SerializerMethodField
|
from rest_framework.fields import JSONField, SerializerMethodField
|
||||||
from rest_framework.relations import PrimaryKeyRelatedField
|
from rest_framework.relations import PrimaryKeyRelatedField
|
||||||
|
|
@ -27,8 +27,8 @@ from core.serializers.utility import AddressSerializer
|
||||||
|
|
||||||
|
|
||||||
class AttributeGroupSimpleSerializer(ModelSerializer):
|
class AttributeGroupSimpleSerializer(ModelSerializer):
|
||||||
parent = PrimaryKeyRelatedField(read_only=True)
|
parent: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)
|
||||||
children = PrimaryKeyRelatedField(many=True, read_only=True)
|
children: PrimaryKeyRelatedField = PrimaryKeyRelatedField(many=True, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AttributeGroup
|
model = AttributeGroup
|
||||||
|
|
@ -59,7 +59,7 @@ class CategorySimpleSerializer(ModelSerializer):
|
||||||
return obj.image.url
|
return obj.image.url
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_children(self, obj) -> list[dict]:
|
def get_children(self, obj) -> Collection:
|
||||||
request = self.context.get("request")
|
request = self.context.get("request")
|
||||||
if request is not None and request.user.has_perm("view_category"):
|
if request is not None and request.user.has_perm("view_category"):
|
||||||
children = obj.children.all()
|
children = obj.children.all()
|
||||||
|
|
@ -111,7 +111,7 @@ class ProductTagSimpleSerializer(ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class ProductImageSimpleSerializer(ModelSerializer):
|
class ProductImageSimpleSerializer(ModelSerializer):
|
||||||
product = PrimaryKeyRelatedField(read_only=True)
|
product: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ProductImage
|
model = ProductImage
|
||||||
|
|
@ -139,7 +139,7 @@ class AttributeSimpleSerializer(ModelSerializer):
|
||||||
|
|
||||||
class AttributeValueSimpleSerializer(ModelSerializer):
|
class AttributeValueSimpleSerializer(ModelSerializer):
|
||||||
attribute = AttributeSimpleSerializer(read_only=True)
|
attribute = AttributeSimpleSerializer(read_only=True)
|
||||||
product = PrimaryKeyRelatedField(read_only=True)
|
product: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AttributeValue
|
model = AttributeValue
|
||||||
|
|
@ -246,7 +246,7 @@ class PromotionSimpleSerializer(ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class WishlistSimpleSerializer(ModelSerializer):
|
class WishlistSimpleSerializer(ModelSerializer):
|
||||||
user = PrimaryKeyRelatedField(read_only=True)
|
user: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)
|
||||||
products = ProductSimpleSerializer(many=True, read_only=True)
|
products = ProductSimpleSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -259,7 +259,7 @@ class WishlistSimpleSerializer(ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class FeedbackSimpleSerializer(ModelSerializer):
|
class FeedbackSimpleSerializer(ModelSerializer):
|
||||||
order_product = PrimaryKeyRelatedField(read_only=True)
|
order_product: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Feedback
|
model = Feedback
|
||||||
|
|
@ -285,7 +285,7 @@ class OrderProductSimpleSerializer(ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class OrderSimpleSerializer(ModelSerializer):
|
class OrderSimpleSerializer(ModelSerializer):
|
||||||
user = PrimaryKeyRelatedField(read_only=True)
|
user: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)
|
||||||
promo_code = PromoCodeSimpleSerializer(read_only=True)
|
promo_code = PromoCodeSimpleSerializer(read_only=True)
|
||||||
order_products = OrderProductSimpleSerializer(many=True, read_only=True)
|
order_products = OrderProductSimpleSerializer(many=True, read_only=True)
|
||||||
billing_address = AddressSerializer(read_only=True, required=False)
|
billing_address = AddressSerializer(read_only=True, required=False)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,17 @@
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.fields import CharField, DictField, FloatField, IntegerField
|
from rest_framework.fields import (
|
||||||
from rest_framework.serializers import ModelSerializer, Serializer
|
BooleanField,
|
||||||
|
CharField,
|
||||||
|
DictField,
|
||||||
|
Field,
|
||||||
|
FloatField,
|
||||||
|
IntegerField,
|
||||||
|
JSONField,
|
||||||
|
ListField,
|
||||||
|
UUIDField,
|
||||||
|
)
|
||||||
|
from rest_framework.serializers import ListSerializer, ModelSerializer, Serializer
|
||||||
|
|
||||||
from core.models import Address
|
from core.models import Address
|
||||||
|
|
||||||
|
|
@ -75,3 +85,99 @@ class DoFeedbackSerializer(Serializer):
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
if data["action"] == "add" and not all([data["comment"], data["rating"]]):
|
if data["action"] == "add" and not all([data["comment"], data["rating"]]):
|
||||||
raise ValidationError(_("you must provide a comment, rating, and order product uuid to add feedback."))
|
raise ValidationError(_("you must provide a comment, rating, and order product uuid to add feedback."))
|
||||||
|
|
||||||
|
|
||||||
|
class CacheOperatorSerializer(Serializer):
|
||||||
|
key = CharField(required=True)
|
||||||
|
data = JSONField(required=False) # type: ignore
|
||||||
|
timeout = IntegerField(required=False)
|
||||||
|
|
||||||
|
|
||||||
|
class ContactUsSerializer(Serializer):
|
||||||
|
email = CharField(required=True)
|
||||||
|
name = CharField(required=True)
|
||||||
|
subject = CharField(required=True)
|
||||||
|
phone_number = CharField(required=False)
|
||||||
|
message = CharField(required=True)
|
||||||
|
|
||||||
|
|
||||||
|
class LanguageSerializer(Serializer):
|
||||||
|
code = CharField(required=True)
|
||||||
|
name = CharField(required=True)
|
||||||
|
flag = CharField()
|
||||||
|
|
||||||
|
|
||||||
|
class RecursiveField(Field):
|
||||||
|
def to_representation(self, value):
|
||||||
|
parent = self.parent
|
||||||
|
if isinstance(parent, ListSerializer):
|
||||||
|
parent = parent.parent
|
||||||
|
|
||||||
|
serializer_class = parent.__class__
|
||||||
|
return serializer_class(value, context=self.context).data
|
||||||
|
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class AddOrderProductSerializer(Serializer):
|
||||||
|
product_uuid = CharField(required=True)
|
||||||
|
attributes = JSONField(required=False, default=dict)
|
||||||
|
|
||||||
|
|
||||||
|
class BulkAddOrderProductsSerializer(Serializer):
|
||||||
|
products = ListField(child=AddOrderProductSerializer(), required=True)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveOrderProductSerializer(Serializer):
|
||||||
|
product_uuid = CharField(required=True)
|
||||||
|
attributes = JSONField(required=False, default=dict)
|
||||||
|
|
||||||
|
|
||||||
|
class BulkRemoveOrderProductsSerializer(Serializer):
|
||||||
|
products = ListField(child=RemoveOrderProductSerializer(), required=True)
|
||||||
|
|
||||||
|
|
||||||
|
class AddWishlistProductSerializer(Serializer):
|
||||||
|
product_uuid = CharField(required=True)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveWishlistProductSerializer(Serializer):
|
||||||
|
product_uuid = CharField(required=True)
|
||||||
|
|
||||||
|
|
||||||
|
class BulkAddWishlistProductSerializer(Serializer):
|
||||||
|
product_uuids = ListField(child=CharField(required=True), allow_empty=False, max_length=64)
|
||||||
|
|
||||||
|
|
||||||
|
class BulkRemoveWishlistProductSerializer(Serializer):
|
||||||
|
product_uuids = ListField(child=CharField(required=True), allow_empty=False, max_length=64)
|
||||||
|
|
||||||
|
|
||||||
|
class BuyOrderSerializer(Serializer):
|
||||||
|
force_balance = BooleanField(required=False, default=False)
|
||||||
|
force_payment = BooleanField(required=False, default=False)
|
||||||
|
promocode_uuid = CharField(required=False)
|
||||||
|
shipping_address_uuid = CharField(required=False)
|
||||||
|
billing_address_uuid = CharField(required=False)
|
||||||
|
|
||||||
|
|
||||||
|
class BuyUnregisteredOrderSerializer(Serializer):
|
||||||
|
products = ListField(child=AddOrderProductSerializer(), required=True)
|
||||||
|
promocode_uuid = UUIDField(required=False)
|
||||||
|
customer_name = CharField(required=True)
|
||||||
|
customer_email = CharField(required=True)
|
||||||
|
customer_phone_number = CharField(required=True)
|
||||||
|
billing_customer_address_uuid = CharField(required=False)
|
||||||
|
shipping_customer_address_uuid = CharField(required=False)
|
||||||
|
payment_method = CharField(required=True)
|
||||||
|
|
||||||
|
|
||||||
|
class BuyAsBusinessOrderSerializer(Serializer):
|
||||||
|
products = ListField(child=AddOrderProductSerializer(), required=True)
|
||||||
|
business_inn = CharField(required=True)
|
||||||
|
business_email = CharField(required=True)
|
||||||
|
business_phone_number = CharField(required=True)
|
||||||
|
billing_business_address_uuid = CharField(required=False)
|
||||||
|
shipping_business_address_uuid = CharField(required=False)
|
||||||
|
payment_method = CharField(required=True)
|
||||||
|
|
|
||||||
|
|
@ -1,209 +1,212 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
<!--suppress JSSuspiciousNameCombination -->
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8"/>
|
||||||
<title>Maintenance</title>
|
<title>Maintenance</title>
|
||||||
<meta name="viewport" content="width=device-height, width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<meta name="viewport"
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
content="width=device-height, width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@500&display=swap" rel="stylesheet">
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<style>
|
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@500&display=swap" rel="stylesheet">
|
||||||
html, body {
|
<style>
|
||||||
margin: 0;
|
html, body {
|
||||||
padding: 0;
|
margin: 0;
|
||||||
width: 100%;
|
padding: 0;
|
||||||
height: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
height: 100%;
|
||||||
font-family: 'Source Code Pro', monospace;
|
overflow: hidden;
|
||||||
background: #2a2a3a;
|
font-family: 'Source Code Pro', monospace;
|
||||||
color: #fff;
|
background: #2a2a3a;
|
||||||
}
|
color: #fff;
|
||||||
canvas {
|
}
|
||||||
display: block;
|
|
||||||
}
|
canvas {
|
||||||
</style>
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function () {
|
||||||
let scene, camera, renderer;
|
let scene, camera, renderer;
|
||||||
let particleSystem, textGroup, fontLoader;
|
let particleSystem, textGroup, fontLoader;
|
||||||
const clock = new THREE.Clock();
|
const clock = new THREE.Clock();
|
||||||
let targetRotX = 0, targetRotY = 0;
|
let targetRotX = 0, targetRotY = 0;
|
||||||
let currentRotX = 0, currentRotY = 0;
|
let currentRotX = 0, currentRotY = 0;
|
||||||
let isMobile = 'ontouchstart' in window;
|
let isMobile = 'ontouchstart' in window;
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
scene = new THREE.Scene();
|
scene = new THREE.Scene();
|
||||||
camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 2000);
|
camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 2000);
|
||||||
camera.position.z = 70;
|
camera.position.z = 70;
|
||||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
renderer = new THREE.WebGLRenderer({antialias: true});
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
renderer.setPixelRatio(window.devicePixelRatio);
|
renderer.setPixelRatio(window.devicePixelRatio);
|
||||||
document.body.appendChild(renderer.domElement);
|
document.body.appendChild(renderer.domElement);
|
||||||
|
|
||||||
createGradientBackground();
|
createGradientBackground();
|
||||||
createParticleField();
|
createParticleField();
|
||||||
|
|
||||||
fontLoader = new THREE.FontLoader();
|
fontLoader = new THREE.FontLoader();
|
||||||
fontLoader.load('https://api.evibes.com/static/Source%20Code%20Pro%20ExtraLight_Regular.json', f => {
|
fontLoader.load('https://api.evibes.com/static/Source%20Code%20Pro%20ExtraLight_Regular.json', f => {
|
||||||
create3DText(f);
|
create3DText(f);
|
||||||
fitCameraToText();
|
fitCameraToText();
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('resize', onWindowResize);
|
window.addEventListener('resize', onWindowResize);
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
window.addEventListener('touchmove', onTouchMove, { passive: false });
|
window.addEventListener('touchmove', onTouchMove, {passive: false});
|
||||||
} else {
|
} else {
|
||||||
window.addEventListener('mousemove', onMouseMove);
|
window.addEventListener('mousemove', onMouseMove);
|
||||||
}
|
}
|
||||||
onWindowResize();
|
onWindowResize();
|
||||||
animate();
|
animate();
|
||||||
}
|
}
|
||||||
|
|
||||||
function createGradientBackground() {
|
function createGradientBackground() {
|
||||||
const c = document.createElement('canvas');
|
const c = document.createElement('canvas');
|
||||||
c.width = 2;
|
c.width = 2;
|
||||||
c.height = 2;
|
c.height = 2;
|
||||||
const ctx = c.getContext('2d');
|
const ctx = c.getContext('2d');
|
||||||
const gradient = ctx.createLinearGradient(0, 0, 0, 2);
|
const gradient = ctx.createLinearGradient(0, 0, 0, 2);
|
||||||
gradient.addColorStop(0, '#1E1E2A');
|
gradient.addColorStop(0, '#1E1E2A');
|
||||||
gradient.addColorStop(1, '#4C4C6A');
|
gradient.addColorStop(1, '#4C4C6A');
|
||||||
ctx.fillStyle = gradient;
|
ctx.fillStyle = gradient;
|
||||||
ctx.fillRect(0, 0, 2, 2);
|
ctx.fillRect(0, 0, 2, 2);
|
||||||
const texture = new THREE.Texture(c);
|
const texture = new THREE.Texture(c);
|
||||||
texture.needsUpdate = true;
|
texture.needsUpdate = true;
|
||||||
scene.background = texture;
|
scene.background = texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createParticleField() {
|
function createParticleField() {
|
||||||
const particleCount = 15000;
|
const particleCount = 15000;
|
||||||
const geometry = new THREE.BufferGeometry();
|
const geometry = new THREE.BufferGeometry();
|
||||||
const positions = new Float32Array(particleCount * 3);
|
const positions = new Float32Array(particleCount * 3);
|
||||||
const colors = new Float32Array(particleCount * 3);
|
const colors = new Float32Array(particleCount * 3);
|
||||||
const colorStart = new THREE.Color(0x34000d);
|
const colorStart = new THREE.Color(0x34000d);
|
||||||
const colorEnd = new THREE.Color(0x02066F);
|
const colorEnd = new THREE.Color(0x02066F);
|
||||||
|
|
||||||
for (let i = 0; i < particleCount; i++) {
|
for (let i = 0; i < particleCount; i++) {
|
||||||
positions[i * 3] = (Math.random() - 0.5) * 400;
|
positions[i * 3] = (Math.random() - 0.5) * 400;
|
||||||
positions[i * 3 + 1] = (Math.random() - 0.5) * 400;
|
positions[i * 3 + 1] = (Math.random() - 0.5) * 400;
|
||||||
positions[i * 3 + 2] = (Math.random() - 0.5) * 400;
|
positions[i * 3 + 2] = (Math.random() - 0.5) * 400;
|
||||||
const mixRatio = i / particleCount;
|
const mixRatio = i / particleCount;
|
||||||
const color = colorStart.clone().lerp(colorEnd, mixRatio);
|
const color = colorStart.clone().lerp(colorEnd, mixRatio);
|
||||||
colors[i * 3] = color.r;
|
colors[i * 3] = color.r;
|
||||||
colors[i * 3 + 1] = color.g;
|
colors[i * 3 + 1] = color.g;
|
||||||
colors[i * 3 + 2] = color.b;
|
colors[i * 3 + 2] = color.b;
|
||||||
}
|
}
|
||||||
|
|
||||||
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
||||||
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
||||||
const material = new THREE.PointsMaterial({
|
const material = new THREE.PointsMaterial({
|
||||||
size: 0.7,
|
size: 0.7,
|
||||||
vertexColors: true,
|
vertexColors: true,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
opacity: 0.7,
|
opacity: 0.7,
|
||||||
blending: THREE.AdditiveBlending
|
blending: THREE.AdditiveBlending
|
||||||
});
|
});
|
||||||
|
|
||||||
particleSystem = new THREE.Points(geometry, material);
|
particleSystem = new THREE.Points(geometry, material);
|
||||||
scene.add(particleSystem);
|
scene.add(particleSystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
function create3DText(font) {
|
function create3DText(font) {
|
||||||
textGroup = new THREE.Group();
|
textGroup = new THREE.Group();
|
||||||
|
|
||||||
const text1 = new THREE.TextGeometry("We’ll Be Back Soon", {
|
const text1 = new THREE.TextGeometry("We’ll Be Back Soon", {
|
||||||
font,
|
font,
|
||||||
size: 5,
|
size: 5,
|
||||||
height: 1,
|
height: 1,
|
||||||
curveSegments: 12,
|
curveSegments: 12,
|
||||||
bevelEnabled: true,
|
bevelEnabled: true,
|
||||||
bevelThickness: 0.2,
|
bevelThickness: 0.2,
|
||||||
bevelSize: 0.1,
|
bevelSize: 0.1,
|
||||||
bevelSegments: 5
|
bevelSegments: 5
|
||||||
});
|
});
|
||||||
const material1 = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x111111 });
|
const material1 = new THREE.MeshPhongMaterial({color: 0xffffff, emissive: 0x111111});
|
||||||
const mesh1 = new THREE.Mesh(text1, material1);
|
const mesh1 = new THREE.Mesh(text1, material1);
|
||||||
mesh1.geometry.center();
|
mesh1.geometry.center();
|
||||||
mesh1.position.y = 5;
|
mesh1.position.y = 5;
|
||||||
textGroup.add(mesh1);
|
textGroup.add(mesh1);
|
||||||
|
|
||||||
const text2 = new THREE.TextGeometry("An update is in progress. Please check back later.", {
|
const text2 = new THREE.TextGeometry("An update is in progress. Please check back later.", {
|
||||||
font,
|
font,
|
||||||
size: 2,
|
size: 2,
|
||||||
height: 0.5,
|
height: 0.5,
|
||||||
curveSegments: 12,
|
curveSegments: 12,
|
||||||
bevelEnabled: false
|
bevelEnabled: false
|
||||||
});
|
});
|
||||||
const material2 = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x111111 });
|
const material2 = new THREE.MeshPhongMaterial({color: 0xffffff, emissive: 0x111111});
|
||||||
const mesh2 = new THREE.Mesh(text2, material2);
|
const mesh2 = new THREE.Mesh(text2, material2);
|
||||||
mesh2.geometry.center();
|
mesh2.geometry.center();
|
||||||
mesh2.position.y = -5;
|
mesh2.position.y = -5;
|
||||||
textGroup.add(mesh2);
|
textGroup.add(mesh2);
|
||||||
|
|
||||||
scene.add(textGroup);
|
scene.add(textGroup);
|
||||||
|
|
||||||
const ambientLight = new THREE.AmbientLight(0x404040, 2);
|
const ambientLight = new THREE.AmbientLight(0x404040, 2);
|
||||||
scene.add(ambientLight);
|
scene.add(ambientLight);
|
||||||
|
|
||||||
const spotLight = new THREE.SpotLight(0xffffff, 1);
|
const spotLight = new THREE.SpotLight(0xffffff, 1);
|
||||||
spotLight.position.set(100, 100, 100);
|
spotLight.position.set(100, 100, 100);
|
||||||
scene.add(spotLight);
|
scene.add(spotLight);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fitCameraToText() {
|
function fitCameraToText() {
|
||||||
if (!textGroup) return;
|
if (!textGroup) return;
|
||||||
const box = new THREE.Box3().setFromObject(textGroup);
|
const box = new THREE.Box3().setFromObject(textGroup);
|
||||||
const size = box.getSize(new THREE.Vector3());
|
const size = box.getSize(new THREE.Vector3());
|
||||||
const center = box.getCenter(new THREE.Vector3());
|
const center = box.getCenter(new THREE.Vector3());
|
||||||
|
|
||||||
const halfSizeToFitOnScreen = size.length() * 0.5;
|
const halfSizeToFitOnScreen = size.length() * 0.5;
|
||||||
const halfFov = THREE.MathUtils.degToRad(camera.fov * 0.5);
|
const halfFov = THREE.MathUtils.degToRad(camera.fov * 0.5);
|
||||||
let distance = halfSizeToFitOnScreen / Math.sin(halfFov);
|
let distance = halfSizeToFitOnScreen / Math.sin(halfFov);
|
||||||
distance *= 1.5;
|
distance *= 1.5;
|
||||||
camera.position.set(center.x, center.y, distance);
|
camera.position.set(center.x, center.y, distance);
|
||||||
camera.lookAt(center);
|
camera.lookAt(center);
|
||||||
}
|
}
|
||||||
|
|
||||||
function animate() {
|
function animate() {
|
||||||
requestAnimationFrame(animate);
|
requestAnimationFrame(animate);
|
||||||
const delta = clock.getDelta();
|
const delta = clock.getDelta();
|
||||||
if (particleSystem) {
|
if (particleSystem) {
|
||||||
particleSystem.rotation.y += 0.02 * delta;
|
particleSystem.rotation.y += 0.02 * delta;
|
||||||
}
|
}
|
||||||
currentRotX += (targetRotX - currentRotX) * 0.1;
|
currentRotX += (targetRotX - currentRotX) * 0.1;
|
||||||
currentRotY += (targetRotY - currentRotY) * 0.1;
|
currentRotY += (targetRotY - currentRotY) * 0.1;
|
||||||
if (textGroup) {
|
if (textGroup) {
|
||||||
textGroup.rotation.x = currentRotY;
|
textGroup.rotation.x = currentRotY;
|
||||||
textGroup.rotation.y = currentRotX;
|
textGroup.rotation.y = currentRotX;
|
||||||
}
|
}
|
||||||
renderer.render(scene, camera);
|
renderer.render(scene, camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onWindowResize() {
|
function onWindowResize() {
|
||||||
camera.aspect = window.innerWidth / window.innerHeight;
|
camera.aspect = window.innerWidth / window.innerHeight;
|
||||||
camera.updateProjectionMatrix();
|
camera.updateProjectionMatrix();
|
||||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||||
fitCameraToText();
|
fitCameraToText();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMouseMove(event) {
|
function onMouseMove(event) {
|
||||||
targetRotX = ((event.clientX / window.innerWidth) - 0.5) * 2 * 0.3;
|
targetRotX = ((event.clientX / window.innerWidth) - 0.5) * 2 * 0.3;
|
||||||
targetRotY = ((event.clientY / window.innerHeight) - 0.5) * 2 * 0.3;
|
targetRotY = ((event.clientY / window.innerHeight) - 0.5) * 2 * 0.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTouchMove(event) {
|
function onTouchMove(event) {
|
||||||
if (event.touches.length > 0) {
|
if (event.touches.length > 0) {
|
||||||
const touch = event.touches[0];
|
const touch = event.touches[0];
|
||||||
targetRotX = ((touch.clientX / window.innerWidth) - 0.5) * 2 * 0.3;
|
targetRotX = ((touch.clientX / window.innerWidth) - 0.5) * 2 * 0.3;
|
||||||
targetRotY = ((touch.clientY / window.innerHeight) - 0.5) * 2 * 0.3;
|
targetRotY = ((touch.clientY / window.innerHeight) - 0.5) * 2 * 0.3;
|
||||||
}
|
}
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init();
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ def update_products_task():
|
||||||
includes invoking the `update_stock` method of vendor classes and removing
|
includes invoking the `update_stock` method of vendor classes and removing
|
||||||
stale products. Finally, it clears the flag in the cache.
|
stale products. Finally, it clears the flag in the cache.
|
||||||
|
|
||||||
Just write integrations with your vendors' APIs into core/vendors/<vendor_name>.py and use it here :)
|
Write integrations with your vendors' APIs into core/vendors/<vendor_name>.py and use it here :)
|
||||||
|
|
||||||
:return: A tuple consisting of a status boolean and a message string
|
:return: A tuple consisting of a status boolean and a message string
|
||||||
:rtype: tuple[bool, str]
|
:rtype: tuple[bool, str]
|
||||||
|
|
@ -64,7 +64,7 @@ def update_orderproducts_task():
|
||||||
`vendors_classes`. Each vendor class in the `vendors_classes` list is
|
`vendors_classes`. Each vendor class in the `vendors_classes` list is
|
||||||
instantiated, and the `update_order_products_statuses` method of the
|
instantiated, and the `update_order_products_statuses` method of the
|
||||||
respective vendor instance is executed to handle the update process.
|
respective vendor instance is executed to handle the update process.
|
||||||
Just write integrations with your vendors' APIs into core/vendors/<vendor_name>.py and use it here :)
|
Write integrations with your vendors' APIs into core/vendors/<vendor_name>.py and use it here :)
|
||||||
|
|
||||||
:return: A tuple containing a boolean indicating success and a string
|
:return: A tuple containing a boolean indicating success and a string
|
||||||
message confirming the successful execution of the task.
|
message confirming the successful execution of the task.
|
||||||
|
|
@ -104,7 +104,7 @@ def remove_stale_product_images():
|
||||||
The task scans the product images directory to locate subdirectories named after
|
The task scans the product images directory to locate subdirectories named after
|
||||||
product UUIDs. It verifies whether each UUID is part of the database's current
|
product UUIDs. It verifies whether each UUID is part of the database's current
|
||||||
product records. If a directory's UUID is not found in the database, it deletes
|
product records. If a directory's UUID is not found in the database, it deletes
|
||||||
the directory, as it is considered stale. This helps in maintaining a clean storage
|
the directory, as it is considered stale. This helps in maintaining clean storage
|
||||||
and removing unused image data.
|
and removing unused image data.
|
||||||
|
|
||||||
:raises ValueError: If a directory name is not a valid UUID.
|
:raises ValueError: If a directory name is not a valid UUID.
|
||||||
|
|
@ -203,7 +203,7 @@ def process_promotions() -> tuple[bool, str]:
|
||||||
if eligible_products.count() < 48:
|
if eligible_products.count() < 48:
|
||||||
return False, "Not enough products to choose from [< 48]."
|
return False, "Not enough products to choose from [< 48]."
|
||||||
|
|
||||||
selected_products = []
|
selected_products: list = []
|
||||||
|
|
||||||
while len(selected_products) < 48:
|
while len(selected_products) < 48:
|
||||||
product = eligible_products.order_by("?").first()
|
product = eligible_products.order_by("?").first()
|
||||||
|
|
@ -211,8 +211,9 @@ def process_promotions() -> tuple[bool, str]:
|
||||||
|
|
||||||
promotion = Promotion.objects.update_or_create(
|
promotion = Promotion.objects.update_or_create(
|
||||||
name=promotion_name, defaults={"discount_percent": discount_percent, "is_active": True}
|
name=promotion_name, defaults={"discount_percent": discount_percent, "is_active": True}
|
||||||
)
|
)[0]
|
||||||
|
|
||||||
promotion.products.set(selected_products)
|
for product in selected_products:
|
||||||
|
promotion.products.add(product)
|
||||||
|
|
||||||
return True, "Promotions updated successfully."
|
return True, "Promotions updated successfully."
|
||||||
|
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
{% extends "admin/base_site.html" %}
|
|
||||||
{% load admin_list static i18n %}
|
|
||||||
|
|
||||||
{% block extrastyle %}
|
|
||||||
{{ block.super }}
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/changelists.css' %}">
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/forms.css' %}">
|
|
||||||
{{ media.css }}
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/constance.css' %}">
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block extrahead %}
|
|
||||||
{% url 'admin:jsi18n' as jsi18nurl %}
|
|
||||||
<script type="text/javascript" src="{{ jsi18nurl|default:'../../jsi18n/' }}"></script>
|
|
||||||
{{ block.super }}
|
|
||||||
{{ media.js }}
|
|
||||||
<script type="text/javascript" src="{% static 'admin/js/constance.js' %}"></script>
|
|
||||||
{% if django_version < "5.1" %}
|
|
||||||
<script type="text/javascript" src="{% static 'admin/js/collapse.js' %}"></script>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block bodyclass %}{{ block.super }} change-list{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div id="content-main" class="constance">
|
|
||||||
<div class="module" id="changelist">
|
|
||||||
<form id="changelist-form" action="" method="post" enctype="multipart/form-data">{% csrf_token %}
|
|
||||||
{% if form.non_field_errors %}
|
|
||||||
<ul class="errorlist">
|
|
||||||
{% for error in form.non_field_errors %}
|
|
||||||
<li>{{ error }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
{% if form.errors %}
|
|
||||||
<ul class="errorlist">
|
|
||||||
{% endif %}
|
|
||||||
{% for field in form.hidden_fields %}
|
|
||||||
{% for error in field.errors %}
|
|
||||||
<li>{{ error }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
{{ field }}
|
|
||||||
{% endfor %}
|
|
||||||
{% if form.errors %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<fieldset class="module">
|
|
||||||
<h2>{% trans "configuration" %}</h2>
|
|
||||||
{% include "admin/constance/includes/results_list.html" %}
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<p class="paginator sticky-footer">
|
|
||||||
<input type="submit" name="_save" class="default" value="{% trans 'save' %}">
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block breadcrumbs %}
|
|
||||||
<div class="breadcrumbs">
|
|
||||||
<a href="{% url 'admin:index' %}">{% trans 'home' %}</a>
|
|
||||||
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
|
|
||||||
› {{ opts.verbose_name_plural|capfirst }}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue