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
|
||||
|
||||
# Host's scripts
|
||||
scripts
|
||||
scripts/Windows
|
||||
scripts/Unix
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -60,7 +60,6 @@ htmlcov/
|
|||
.tox/
|
||||
.nox/
|
||||
.scrapy
|
||||
.coverage.*
|
||||
.cover
|
||||
.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
|
||||
|
||||
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 ./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
|
||||
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
|
||||
|
||||
|
|
@ -20,7 +21,7 @@ cases and learning Django skills. The project is easy to customize, allowing for
|
|||
|
||||
## 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.
|
||||
- **Asynchronous Task Processing**: Integrated Celery workers and beat scheduler for background tasks.
|
||||
- **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
|
||||
|
||||
- Docker and Docker Compose installed on your machine - that's it!
|
||||
- Docker and Docker Compose are installed on your machine.
|
||||
|
||||
### 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 >
|
||||
```
|
||||
|
||||
3. Generate your .env file. Check and confirm the contents afterwards.
|
||||
3. Generate your .env file. Check and confirm the contents afterward.
|
||||
|
||||
- Windows
|
||||
```powershell
|
||||
|
|
@ -85,29 +86,29 @@ cases and learning Django skills. The project is easy to customize, allowing for
|
|||
6. Bring to production.
|
||||
|
||||
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
|
||||
|
||||
### 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`
|
||||
before running installment scripts
|
||||
|
||||
### 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!
|
||||
|
||||
### .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.
|
||||
|
||||
## 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
|
||||
2. www.your-domain.com
|
||||
|
|
|
|||
|
|
@ -35,9 +35,10 @@ class PostAdmin(admin.ModelAdmin):
|
|||
|
||||
def preview_html(self, obj):
|
||||
html = obj.content.html or "<em>{}</em>".format(_("(no content yet)"))
|
||||
# noinspection DjangoSafeString
|
||||
return mark_safe(html)
|
||||
|
||||
preview_html.short_description = _("rendered HTML")
|
||||
preview_html.short_description = _("rendered HTML") # type: ignore
|
||||
|
||||
|
||||
@admin.register(PostTag)
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language: ar-AR\n"
|
||||
"Language: ar-ar\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(لا يوجد محتوى بعد)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "تم تقديمه بتنسيق HTML"
|
||||
|
||||
|
|
@ -25,47 +25,52 @@ msgstr "تم تقديمه بتنسيق HTML"
|
|||
msgid "blog"
|
||||
msgstr "المدونة"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "عنوان المنشور"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "العنوان"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "المنشور"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr "يجب توفير ملف ترميز أو محتوى ترميز مخفض - متنافيان"
|
||||
|
||||
#: blog/models.py:78
|
||||
#: blog/models.py:82
|
||||
msgid "internal tag identifier for the post tag"
|
||||
msgstr "معرّف العلامة الداخلي لعلامة المنشور"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "اسم العلامة"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
msgstr "اسم سهل الاستخدام لعلامة المنشور"
|
||||
|
||||
#: blog/models.py:84
|
||||
#: blog/models.py:88
|
||||
msgid "tag display name"
|
||||
msgstr "اسم عرض العلامة"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "علامة المشاركة"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "علامات المشاركة"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language: cs-CZ\n"
|
||||
"Language: cs-cz\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(zatím bez obsahu)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "Vykreslené HTML"
|
||||
|
||||
|
|
@ -25,49 +25,54 @@ msgstr "Vykreslené HTML"
|
|||
msgid "blog"
|
||||
msgstr "Blog"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "Název příspěvku"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "Název"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "Příspěvek"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr ""
|
||||
"musí být poskytnut soubor markdown nebo obsah markdown - vzájemně se "
|
||||
"vylučují."
|
||||
|
||||
#: blog/models.py:78
|
||||
#: blog/models.py:82
|
||||
msgid "internal tag identifier for the post tag"
|
||||
msgstr "interní identifikátor tagu pro tag příspěvku"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "Název štítku"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
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"
|
||||
msgstr "Zobrazení názvu štítku"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "Označení příspěvku"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "Štítky příspěvků"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language: da-DK\n"
|
||||
"Language: da-dk\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(intet indhold endnu)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "Rendered HTML"
|
||||
|
||||
|
|
@ -25,48 +25,52 @@ msgstr "Rendered HTML"
|
|||
msgid "blog"
|
||||
msgstr "Blog"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "Indlæggets titel"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "Titel"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "Indlæg"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr ""
|
||||
"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"
|
||||
msgstr "intern tag-identifikator for indlægs-tagget"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "Tag-navn"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
msgstr "Brugervenligt navn til posttagget"
|
||||
|
||||
#: blog/models.py:84
|
||||
#: blog/models.py:88
|
||||
msgid "tag display name"
|
||||
msgstr "Navn på tag-visning"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "Tag til indlæg"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "Tags til indlæg"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language: de-DE\n"
|
||||
"Language: de-de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(noch kein Inhalt)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "Gerendertes HTML"
|
||||
|
||||
|
|
@ -25,49 +25,55 @@ msgstr "Gerendertes HTML"
|
|||
msgid "blog"
|
||||
msgstr "Blog"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "Titel des Beitrags"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "Titel"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "Beitrag"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr ""
|
||||
"eine Markdown-Datei oder ein Markdown-Inhalt muss bereitgestellt werden - "
|
||||
"beide schließen sich gegenseitig aus"
|
||||
|
||||
#: blog/models.py:78
|
||||
#: blog/models.py:82
|
||||
msgid "internal tag identifier for the post tag"
|
||||
msgstr "interner Tag-Bezeichner für den Post-Tag"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "Tag name"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
msgstr "Benutzerfreundlicher Name für das Post-Tag"
|
||||
|
||||
#: blog/models.py:84
|
||||
#: blog/models.py:88
|
||||
msgid "tag display name"
|
||||
msgstr "Tag-Anzeigename"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "Tag eintragen"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "Tags eintragen"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -5,9 +5,9 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
|
|
@ -21,7 +21,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(no content yet)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "Rendered HTML"
|
||||
|
||||
|
|
@ -29,48 +29,52 @@ msgstr "Rendered HTML"
|
|||
msgid "blog"
|
||||
msgstr "Blog"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "Post's title"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "Title"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "Post"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr ""
|
||||
"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"
|
||||
msgstr "internal tag identifier for the post tag"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "Tag name"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "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"
|
||||
msgstr "Tag display name"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "Post tag"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "Post tags"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language: en-US\n"
|
||||
"Language: en-us\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(no content yet)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "Rendered HTML"
|
||||
|
||||
|
|
@ -25,48 +25,52 @@ msgstr "Rendered HTML"
|
|||
msgid "blog"
|
||||
msgstr "Blog"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "Post's title"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "Title"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "Post"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr ""
|
||||
"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"
|
||||
msgstr "internal tag identifier for the post tag"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "Tag name"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "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"
|
||||
msgstr "Tag display name"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "Post tag"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "Post tags"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language: es-ES\n"
|
||||
"Language: es-es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(sin contenido aún)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "HTML renderizado"
|
||||
|
||||
|
|
@ -25,49 +25,54 @@ msgstr "HTML renderizado"
|
|||
msgid "blog"
|
||||
msgstr "Blog"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "Título del mensaje"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "Título"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "Publicar en"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr ""
|
||||
"se debe proporcionar un archivo markdown o contenido markdown - mutuamente "
|
||||
"excluyentes"
|
||||
|
||||
#: blog/models.py:78
|
||||
#: blog/models.py:82
|
||||
msgid "internal tag identifier for the post tag"
|
||||
msgstr "identificador interno de la etiqueta post"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "Nombre de la etiqueta"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
msgstr "Nombre fácil de usar para la etiqueta de la entrada"
|
||||
|
||||
#: blog/models.py:84
|
||||
#: blog/models.py:88
|
||||
msgid "tag display name"
|
||||
msgstr "Nombre de la etiqueta"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "Etiqueta postal"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "Etiquetas"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language: fr-FR\n"
|
||||
"Language: fr-fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(pas encore de contenu)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "HTML rendu"
|
||||
|
||||
|
|
@ -25,49 +25,55 @@ msgstr "HTML rendu"
|
|||
msgid "blog"
|
||||
msgstr "Blog"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "Titre du message"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "Titre"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "Poste"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr ""
|
||||
"un fichier markdown ou un contenu markdown doit être fourni - ils s'excluent "
|
||||
"mutuellement"
|
||||
"un fichier markdown ou un contenu markdown doit être fourni - ils s'excluent"
|
||||
" mutuellement"
|
||||
|
||||
#: blog/models.py:78
|
||||
#: blog/models.py:82
|
||||
msgid "internal tag identifier for the post tag"
|
||||
msgstr "identifiant interne de la balise post"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "Nom du jour"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
msgstr "Nom convivial pour la balise post"
|
||||
|
||||
#: blog/models.py:84
|
||||
#: blog/models.py:88
|
||||
msgid "tag display name"
|
||||
msgstr "Nom d'affichage de l'étiquette"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "Tag de poste"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "Tags de la poste"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -5,9 +5,9 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
|
|
@ -20,7 +20,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr ""
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -28,47 +28,51 @@ msgstr ""
|
|||
msgid "blog"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:78
|
||||
#: blog/models.py:82
|
||||
msgid "internal tag identifier for the post tag"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:84
|
||||
#: blog/models.py:88
|
||||
msgid "tag display name"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr ""
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language: it-IT\n"
|
||||
"Language: it-it\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(ancora senza contenuti)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "HTML renderizzato"
|
||||
|
||||
|
|
@ -25,49 +25,53 @@ msgstr "HTML renderizzato"
|
|||
msgid "blog"
|
||||
msgstr "Blog"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "Titolo del post"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "Titolo"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "Posta"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr ""
|
||||
"deve essere fornito un file markdown o un contenuto markdown - si escludono "
|
||||
"a vicenda"
|
||||
|
||||
#: blog/models.py:78
|
||||
#: blog/models.py:82
|
||||
msgid "internal tag identifier for the post tag"
|
||||
msgstr "identificatore interno del tag post"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "Nome del tag"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
msgstr "Nome intuitivo per il tag del post"
|
||||
|
||||
#: blog/models.py:84
|
||||
#: blog/models.py:88
|
||||
msgid "tag display name"
|
||||
msgstr "Nome del tag"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "Post tag"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "Tag dei post"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language: ja-JP\n"
|
||||
"Language: ja-jp\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(内容はまだありません)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "レンダリングされたHTML"
|
||||
|
||||
|
|
@ -25,49 +25,51 @@ msgstr "レンダリングされたHTML"
|
|||
msgid "blog"
|
||||
msgstr "ブログ"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "投稿タイトル"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "タイトル"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "ポスト"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"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"
|
||||
msgstr "投稿タグの内部タグ識別子"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "タグ名"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
msgstr "投稿タグのユーザーフレンドリーな名前"
|
||||
|
||||
#: blog/models.py:84
|
||||
#: blog/models.py:88
|
||||
msgid "tag display name"
|
||||
msgstr "タグ表示名"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "投稿タグ"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "投稿タグ"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -5,9 +5,9 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
|
|
@ -20,7 +20,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr ""
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -28,47 +28,51 @@ msgstr ""
|
|||
msgid "blog"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:78
|
||||
#: blog/models.py:82
|
||||
msgid "internal tag identifier for the post tag"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:84
|
||||
#: blog/models.py:88
|
||||
msgid "tag display name"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr ""
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr ""
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language: nl-NL\n"
|
||||
"Language: nl-nl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(nog geen inhoud)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "HTML weergeven"
|
||||
|
||||
|
|
@ -25,49 +25,55 @@ msgstr "HTML weergeven"
|
|||
msgid "blog"
|
||||
msgstr "Blog"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "Titel van de post"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "Titel"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "Plaats"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr ""
|
||||
"er moet een markdown-bestand of markdown-inhoud worden geleverd - wederzijds "
|
||||
"exclusief"
|
||||
"er moet een markdown-bestand of markdown-inhoud worden geleverd - wederzijds"
|
||||
" exclusief"
|
||||
|
||||
#: blog/models.py:78
|
||||
#: blog/models.py:82
|
||||
msgid "internal tag identifier for the post tag"
|
||||
msgstr "interne tagidentifier voor de posttag"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "Tag naam"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
msgstr "Gebruiksvriendelijke naam voor de posttag"
|
||||
|
||||
#: blog/models.py:84
|
||||
#: blog/models.py:88
|
||||
msgid "tag display name"
|
||||
msgstr "Tag weergavenaam"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "Post tag"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "Post tags"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language: pl-PL\n"
|
||||
"Language: pl-pl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(brak treści)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "Renderowany HTML"
|
||||
|
||||
|
|
@ -25,49 +25,54 @@ msgstr "Renderowany HTML"
|
|||
msgid "blog"
|
||||
msgstr "Blog"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "Tytuł postu"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "Tytuł"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "Post"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr ""
|
||||
"należy dostarczyć plik markdown lub zawartość markdown - wzajemnie się "
|
||||
"wykluczające"
|
||||
|
||||
#: blog/models.py:78
|
||||
#: blog/models.py:82
|
||||
msgid "internal tag identifier for the post tag"
|
||||
msgstr "wewnętrzny identyfikator tagu posta"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "Nazwa tagu"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
msgstr "Przyjazna dla użytkownika nazwa tagu posta"
|
||||
|
||||
#: blog/models.py:84
|
||||
#: blog/models.py:88
|
||||
msgid "tag display name"
|
||||
msgstr "Wyświetlana nazwa znacznika"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "Tag posta"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "Tagi postów"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language: pt-BR\n"
|
||||
"Language: pt-br\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(ainda não há conteúdo)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "HTML renderizado"
|
||||
|
||||
|
|
@ -25,48 +25,54 @@ msgstr "HTML renderizado"
|
|||
msgid "blog"
|
||||
msgstr "Blog"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "Título da postagem"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "Título"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "Postar"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr ""
|
||||
"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"
|
||||
msgstr "identificador de tag interno para a tag de postagem"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "Nome da etiqueta"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
msgstr "Nome de fácil utilização para a tag de postagem"
|
||||
|
||||
#: blog/models.py:84
|
||||
#: blog/models.py:88
|
||||
msgid "tag display name"
|
||||
msgstr "Nome de exibição da tag"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "Etiqueta de postagem"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "Tags de postagem"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language: ro-RO\n"
|
||||
"Language: ro-ro\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(fără conținut încă)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "HTML redat"
|
||||
|
||||
|
|
@ -25,49 +25,55 @@ msgstr "HTML redat"
|
|||
msgid "blog"
|
||||
msgstr "Blog"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "Titlul postului"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "Titlul"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "Post"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr ""
|
||||
"trebuie furnizat un fișier markdown sau conținut markdown - se exclud "
|
||||
"reciproc"
|
||||
|
||||
#: blog/models.py:78
|
||||
#: blog/models.py:82
|
||||
msgid "internal tag identifier for the post tag"
|
||||
msgstr "identificator intern de etichetă pentru eticheta postului"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "Nume etichetă"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
msgstr "Nume ușor de utilizat pentru eticheta postului"
|
||||
|
||||
#: blog/models.py:84
|
||||
#: blog/models.py:88
|
||||
msgid "tag display name"
|
||||
msgstr "Nume afișare etichetă"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "Etichetă post"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "Etichete poștale"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,13 +1,13 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language: ru-RU\n"
|
||||
"Language: ru-ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(пока без содержания)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "Рендеринг HTML"
|
||||
|
||||
|
|
@ -25,49 +25,55 @@ msgstr "Рендеринг HTML"
|
|||
msgid "blog"
|
||||
msgstr "Блог"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "Заголовок сообщения"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "Название"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "Пост"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr ""
|
||||
"необходимо предоставить файл разметки или содержимое разметки - "
|
||||
"взаимоисключающие варианты"
|
||||
|
||||
#: blog/models.py:78
|
||||
#: blog/models.py:82
|
||||
msgid "internal tag identifier for the post tag"
|
||||
msgstr "внутренний идентификатор тега для тега post"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "Название тега"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
msgstr "Удобное для пользователя название тега поста"
|
||||
|
||||
#: blog/models.py:84
|
||||
#: blog/models.py:88
|
||||
msgid "tag display name"
|
||||
msgstr "Отображаемое имя тега"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "Тэг поста"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "Тэги постов"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,9 +1,9 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: EVIBES 2.8.5\n"
|
||||
"Report-Msgid-Bugs-To: <CONTACT@FUREUNOIR.COM> \n"
|
||||
"POT-Creation-Date: 2025-05-27 13:42+0100\n"
|
||||
"Project-Id-Version: EVIBES 2.8.9\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-06-18 12:55+0100\n"
|
||||
"PO-Revision-Date: 2025-06-16 08:59+0100\n"
|
||||
"Last-Translator: EGOR GORBUNOV <CONTACT@FUREUNOIR.COM>\n"
|
||||
"Language-Team: LANGUAGE <CONTACT@FUREUNOIR.COM>\n"
|
||||
|
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
msgid "(no content yet)"
|
||||
msgstr "(暂无内容)"
|
||||
|
||||
#: blog/admin.py:40
|
||||
#: blog/admin.py:41
|
||||
msgid "rendered HTML"
|
||||
msgstr "渲染的 HTML"
|
||||
|
||||
|
|
@ -25,47 +25,51 @@ msgstr "渲染的 HTML"
|
|||
msgid "blog"
|
||||
msgstr "博客"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "post title"
|
||||
msgstr "帖子标题"
|
||||
|
||||
#: blog/models.py:15
|
||||
#: blog/models.py:17
|
||||
msgid "title"
|
||||
msgstr "标题"
|
||||
|
||||
#: blog/models.py:62
|
||||
#: blog/models.py:64
|
||||
msgid "post"
|
||||
msgstr "职位"
|
||||
|
||||
#: blog/models.py:63
|
||||
#: blog/models.py:65
|
||||
msgid "posts"
|
||||
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 ""
|
||||
"a markdown file or markdown content must be provided - mutually exclusive"
|
||||
msgstr "必须提供标记符文件或标记符内容 - 相互排斥"
|
||||
|
||||
#: blog/models.py:78
|
||||
#: blog/models.py:82
|
||||
msgid "internal tag identifier for the post tag"
|
||||
msgstr "职位标签的内部标签标识符"
|
||||
|
||||
#: blog/models.py:79
|
||||
#: blog/models.py:83
|
||||
msgid "tag name"
|
||||
msgstr "标签名称"
|
||||
|
||||
#: blog/models.py:83
|
||||
#: blog/models.py:87
|
||||
msgid "user-friendly name for the post tag"
|
||||
msgstr "方便用户使用的帖子标签名称"
|
||||
|
||||
#: blog/models.py:84
|
||||
#: blog/models.py:88
|
||||
msgid "tag display name"
|
||||
msgstr "标签显示名称"
|
||||
|
||||
#: blog/models.py:92
|
||||
#: blog/models.py:96
|
||||
msgid "post tag"
|
||||
msgstr "职位标签"
|
||||
|
||||
#: blog/models.py:93
|
||||
#: blog/models.py:97
|
||||
msgid "post tags"
|
||||
msgstr "帖子标签"
|
||||
|
|
|
|||
|
|
@ -10,11 +10,13 @@ from core.abstract import NiceModel
|
|||
class Post(NiceModel):
|
||||
is_publicly_visible = True
|
||||
|
||||
author = ForeignKey(to="vibes_auth.User", on_delete=CASCADE, blank=False, null=False, related_name="posts")
|
||||
title = CharField(
|
||||
author: ForeignKey = ForeignKey(
|
||||
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")
|
||||
)
|
||||
content = MarkdownField(
|
||||
content: MarkdownField = MarkdownField(
|
||||
"content",
|
||||
extensions=[
|
||||
TocExtension(toc_depth=3),
|
||||
|
|
@ -51,9 +53,9 @@ class Post(NiceModel):
|
|||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
file = FileField(upload_to="posts/", blank=True, null=True)
|
||||
slug = AutoSlugField(populate_from="title", allow_unicode=True, unique=True, editable=False)
|
||||
tags = ManyToManyField(to="blog.PostTag", blank=True, related_name="posts")
|
||||
file: FileField = FileField(upload_to="posts/", blank=True, null=True)
|
||||
slug: AutoSlugField = AutoSlugField(populate_from="title", allow_unicode=True, unique=True, editable=False)
|
||||
tags: ManyToManyField = ManyToManyField(to="blog.PostTag", blank=True, related_name="posts")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.title} | {self.author.first_name} {self.author.last_name}"
|
||||
|
|
@ -63,6 +65,8 @@ class Post(NiceModel):
|
|||
verbose_name_plural = _("posts")
|
||||
|
||||
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]):
|
||||
raise ValueError(_("a markdown file or markdown content must be provided - mutually exclusive"))
|
||||
super().save(**kwargs)
|
||||
|
|
@ -71,14 +75,14 @@ class Post(NiceModel):
|
|||
class PostTag(NiceModel):
|
||||
is_publicly_visible = True
|
||||
|
||||
tag_name = CharField(
|
||||
tag_name: CharField = CharField(
|
||||
blank=False,
|
||||
null=False,
|
||||
max_length=255,
|
||||
help_text=_("internal tag identifier for the post tag"),
|
||||
verbose_name=_("tag name"),
|
||||
)
|
||||
name = CharField(
|
||||
name: CharField = CharField(
|
||||
max_length=255,
|
||||
help_text=_("user-friendly name for the post tag"),
|
||||
verbose_name=_("tag display name"),
|
||||
|
|
|
|||
|
|
@ -31,4 +31,5 @@ class MarkdownEditorWidget(forms.Textarea):
|
|||
}});
|
||||
</script>
|
||||
"""
|
||||
# noinspection DjangoSafeString
|
||||
return mark_safe(textarea_html + init_js)
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@ from django_extensions.db.fields import CreationDateTimeField, ModificationDateT
|
|||
|
||||
class NiceModel(Model):
|
||||
id = None
|
||||
uuid = UUIDField(
|
||||
uuid: UUIDField = UUIDField(
|
||||
verbose_name=_("unique id"),
|
||||
help_text=_("unique id is used to surely identify any database object"),
|
||||
primary_key=True,
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
)
|
||||
is_active = BooleanField(
|
||||
is_active: BooleanField = BooleanField(
|
||||
default=True,
|
||||
verbose_name=_("is active"),
|
||||
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):
|
||||
return instance.name
|
||||
|
||||
indented_title.short_description = _("name")
|
||||
indented_title.admin_order_field = "name"
|
||||
indented_title.short_description = _("name") # type: ignore
|
||||
indented_title.admin_order_field = "name" # type: ignore
|
||||
|
||||
|
||||
@admin.register(Brand)
|
||||
|
|
@ -191,12 +191,12 @@ class ProductAdmin(BasicModelAdmin, TabbedTranslationAdmin):
|
|||
def price(self, obj):
|
||||
return obj.price
|
||||
|
||||
price.short_description = _("price")
|
||||
price.short_description = _("price") # type: ignore
|
||||
|
||||
def rating(self, obj):
|
||||
return obj.rating
|
||||
|
||||
rating.short_description = _("rating")
|
||||
rating.short_description = _("rating") # type: ignore
|
||||
|
||||
fieldsets = (
|
||||
(
|
||||
|
|
@ -278,7 +278,7 @@ class OrderAdmin(BasicModelAdmin):
|
|||
def is_business(self, obj):
|
||||
return obj.is_business
|
||||
|
||||
is_business.short_description = _("is business")
|
||||
is_business.short_description = _("is business") # type: ignore
|
||||
|
||||
def get_queryset(self, request):
|
||||
qs = super().get_queryset(request)
|
||||
|
|
@ -431,6 +431,6 @@ class ConstanceConfig:
|
|||
admin.site.unregister([Config]) # 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.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()
|
||||
|
||||
# Collect results, guard against None values
|
||||
results = {"products": [], "categories": [], "brands": [], "posts": []}
|
||||
results: dict = {"products": [], "categories": [], "brands": [], "posts": []}
|
||||
for hit in response.hits:
|
||||
obj_uuid = getattr(hit, "uuid", None) or hit.meta.id
|
||||
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"]
|
||||
|
||||
def filter_whole_categories(self, queryset, _name, value):
|
||||
has_own_products = Exists(
|
||||
Product.objects.filter(category=OuterRef('pk'))
|
||||
)
|
||||
has_own_products = Exists(Product.objects.filter(category=OuterRef("pk")))
|
||||
has_desc_products = Exists(
|
||||
Product.objects.filter(
|
||||
is_active=True,
|
||||
category__tree_id=OuterRef('tree_id'),
|
||||
category__lft__gt=OuterRef('lft'),
|
||||
category__rght__lt=OuterRef('rght'),
|
||||
category__tree_id=OuterRef("tree_id"),
|
||||
category__lft__gt=OuterRef("lft"),
|
||||
category__rght__lt=OuterRef("rght"),
|
||||
)
|
||||
)
|
||||
annotated = queryset.annotate(
|
||||
has_products=has_own_products | has_desc_products
|
||||
)
|
||||
annotated = queryset.annotate(has_products=has_own_products | has_desc_products)
|
||||
if value:
|
||||
return annotated.filter(has_products=True).distinct()
|
||||
return annotated.filter(has_products=False).distinct()
|
||||
|
|
|
|||
|
|
@ -619,6 +619,7 @@ class ContactUs(BaseMutation):
|
|||
return ContactUs(received=False, error=str(e))
|
||||
|
||||
|
||||
# noinspection PyArgumentList
|
||||
class Search(BaseMutation):
|
||||
class Arguments:
|
||||
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)
|
||||
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)
|
||||
redis_thread = threading.Thread(target=wait_for_redis)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ from django.core.management.base import BaseCommand, CommandError
|
|||
|
||||
# Patterns to identify placeholders
|
||||
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
|
||||
]
|
||||
|
||||
|
||||
def extract_placeholders(text: str) -> set[str]:
|
||||
"""
|
||||
Extract all placeholders from given text.
|
||||
Extract all placeholders from the given text.
|
||||
"""
|
||||
phs: list[str] = []
|
||||
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.
|
||||
Raises CommandError if still unparsable.
|
||||
"""
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
return polib.pofile(path)
|
||||
except Exception:
|
||||
# read raw text
|
||||
try:
|
||||
with open(path, encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
except OSError as e:
|
||||
raise CommandError(f"{path}: cannot read file ({e})")
|
||||
# fix fuzzy flags and empty header entries
|
||||
text = re.sub(r"^#,(?!\s)", "#, ", text, flags=re.MULTILINE)
|
||||
parts = text.split("\n\n", 1)
|
||||
header = parts[0]
|
||||
rest = parts[1] if len(parts) > 1 else ""
|
||||
rest = re.sub(r"^msgid \"\"\s*\nmsgstr \"\"\s*\n?", "", rest, flags=re.MULTILINE)
|
||||
sanitized = header + "\n\n" + rest
|
||||
tmp = NamedTemporaryFile(mode="w+", delete=False, suffix=".po", encoding="utf-8") # noqa: SIM115
|
||||
try:
|
||||
tmp.write(sanitized)
|
||||
tmp.flush()
|
||||
tmp.close()
|
||||
return polib.pofile(tmp.name)
|
||||
except Exception as e:
|
||||
raise CommandError(f"{path}: syntax error after sanitization ({e})")
|
||||
finally:
|
||||
with contextlib.suppress(OSError):
|
||||
os.unlink(tmp.name)
|
||||
|
||||
# read raw text
|
||||
try:
|
||||
with open(path, encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
except OSError as e:
|
||||
raise CommandError(f"{path}: cannot read file ({e})")
|
||||
# fix fuzzy flags and empty header entries
|
||||
text = re.sub(r"^#,(?!\s)", "#, ", text, flags=re.MULTILINE)
|
||||
parts = text.split("\n\n", 1)
|
||||
header = parts[0]
|
||||
rest = parts[1] if len(parts) > 1 else ""
|
||||
rest = re.sub(r"^msgid \"\"\s*\nmsgstr \"\"\s*\n?", "", rest, flags=re.MULTILINE)
|
||||
sanitized = header + "\n\n" + rest
|
||||
tmp = NamedTemporaryFile(mode="w+", delete=False, suffix=".po", encoding="utf-8") # noqa: SIM115
|
||||
try:
|
||||
tmp.write(sanitized)
|
||||
tmp.flush()
|
||||
tmp.close()
|
||||
return polib.pofile(tmp.name)
|
||||
except Exception as e:
|
||||
raise CommandError(f"{path}: syntax error after sanitization ({e})")
|
||||
finally:
|
||||
with contextlib.suppress(OSError):
|
||||
os.unlink(tmp.name)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
|
|
|||
|
|
@ -145,6 +145,9 @@ class Command(BaseCommand):
|
|||
self.stdout.write(f"• {app_conf.label}: loading English PO…")
|
||||
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]
|
||||
if missing:
|
||||
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["Language"] = target_lang
|
||||
|
||||
for e in entries:
|
||||
prev = old_tgt.find(e.msgid) if old_tgt else None
|
||||
for entry in entries:
|
||||
prev = old_tgt.find(entry.msgid) if old_tgt else None
|
||||
new_po.append(
|
||||
polib.POEntry(
|
||||
msgid=e.msgid,
|
||||
msgid=entry.msgid,
|
||||
msgstr=prev.msgstr if prev and prev.msgstr else "",
|
||||
msgctxt=e.msgctxt,
|
||||
comment=e.comment,
|
||||
tcomment=e.tcomment,
|
||||
occurrences=e.occurrences,
|
||||
flags=e.flags,
|
||||
msgctxt=entry.msgctxt,
|
||||
comment=entry.comment,
|
||||
tcomment=entry.tcomment,
|
||||
occurrences=entry.occurrences,
|
||||
flags=entry.flags,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ logger = logging.getLogger("django.request")
|
|||
|
||||
|
||||
class AddressManager(models.Manager):
|
||||
def create(self, raw_data: str, **kwargs):
|
||||
def create(self, raw_data: str, **kwargs): # type: ignore
|
||||
if not raw_data:
|
||||
raise ValueError("'raw_data' (address string) must be provided.")
|
||||
|
||||
params = {
|
||||
params: dict[str, str | int] = {
|
||||
"format": "json",
|
||||
"addressdetails": 1,
|
||||
"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):
|
||||
is_publicly_visible = True
|
||||
|
||||
parent = ForeignKey(
|
||||
parent: ForeignKey = ForeignKey(
|
||||
"self",
|
||||
on_delete=CASCADE,
|
||||
null=True,
|
||||
|
|
@ -65,7 +65,7 @@ class AttributeGroup(ExportModelOperationsMixin("attribute_group"), NiceModel):
|
|||
help_text=_("parent of this group"),
|
||||
verbose_name=_("parent attribute group"),
|
||||
)
|
||||
name = CharField(
|
||||
name: CharField = CharField(
|
||||
max_length=255,
|
||||
verbose_name=_("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):
|
||||
is_publicly_visible = True
|
||||
|
||||
categories = ManyToManyField(
|
||||
categories: ManyToManyField = ManyToManyField(
|
||||
"core.Category",
|
||||
related_name="attributes",
|
||||
help_text=_("category of this attribute"),
|
||||
verbose_name=_("categories"),
|
||||
)
|
||||
|
||||
group = ForeignKey(
|
||||
group: ForeignKey = ForeignKey(
|
||||
"core.AttributeGroup",
|
||||
on_delete=CASCADE,
|
||||
related_name="attributes",
|
||||
help_text=_("group of this attribute"),
|
||||
verbose_name=_("attribute group"),
|
||||
)
|
||||
value_type = CharField(
|
||||
value_type: CharField = CharField(
|
||||
max_length=50,
|
||||
choices=[
|
||||
("string", _("string")),
|
||||
|
|
@ -111,7 +111,7 @@ class Attribute(ExportModelOperationsMixin("attribute"), NiceModel):
|
|||
verbose_name=_("value type"),
|
||||
)
|
||||
|
||||
name = CharField(
|
||||
name: CharField = CharField(
|
||||
max_length=255,
|
||||
help_text=_("name of this attribute"),
|
||||
verbose_name=_("attribute's name"),
|
||||
|
|
@ -129,14 +129,14 @@ class Attribute(ExportModelOperationsMixin("attribute"), NiceModel):
|
|||
class AttributeValue(ExportModelOperationsMixin("attribute_value"), NiceModel):
|
||||
is_publicly_visible = True
|
||||
|
||||
attribute = ForeignKey(
|
||||
attribute: ForeignKey = ForeignKey(
|
||||
"core.Attribute",
|
||||
on_delete=CASCADE,
|
||||
related_name="values",
|
||||
help_text=_("attribute of this value"),
|
||||
verbose_name=_("attribute"),
|
||||
)
|
||||
product = ForeignKey(
|
||||
product: ForeignKey = ForeignKey(
|
||||
"core.Product",
|
||||
on_delete=CASCADE,
|
||||
blank=False,
|
||||
|
|
@ -145,7 +145,7 @@ class AttributeValue(ExportModelOperationsMixin("attribute_value"), NiceModel):
|
|||
verbose_name=_("associated product"),
|
||||
related_name="attributes",
|
||||
)
|
||||
value = TextField(
|
||||
value: TextField = TextField(
|
||||
verbose_name=_("attribute value"),
|
||||
help_text=_("the specific value for this attribute"),
|
||||
)
|
||||
|
|
@ -169,7 +169,7 @@ class Category(ExportModelOperationsMixin("category"), NiceModel, MPTTModel):
|
|||
validators=[validate_category_image_dimensions],
|
||||
verbose_name=_("category image"),
|
||||
)
|
||||
markup_percent = IntegerField(
|
||||
markup_percent: IntegerField = IntegerField(
|
||||
default=0,
|
||||
validators=[MinValueValidator(0), MaxValueValidator(100)],
|
||||
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"),
|
||||
)
|
||||
|
||||
name = CharField(
|
||||
name: CharField = CharField(
|
||||
max_length=255,
|
||||
verbose_name=_("category name"),
|
||||
help_text=_("provide a name for this category"),
|
||||
unique=True,
|
||||
)
|
||||
|
||||
description = TextField( # noqa: DJ001
|
||||
description: TextField = TextField(
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_("add a detailed description for this category"),
|
||||
verbose_name=_("category description"),
|
||||
)
|
||||
|
||||
slug = AutoSlugField(
|
||||
slug: AutoSlugField = AutoSlugField(
|
||||
populate_from=("uuid", "name"),
|
||||
allow_unicode=True,
|
||||
unique=True,
|
||||
editable=False,
|
||||
null=True,
|
||||
)
|
||||
tags = ManyToManyField(
|
||||
tags: ManyToManyField = ManyToManyField(
|
||||
"core.CategoryTag",
|
||||
blank=True,
|
||||
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):
|
||||
is_publicly_visible = True
|
||||
|
||||
name = CharField(
|
||||
name: CharField = CharField(
|
||||
max_length=255,
|
||||
help_text=_("name of this brand"),
|
||||
verbose_name=_("brand name"),
|
||||
|
|
@ -252,13 +252,13 @@ class Brand(ExportModelOperationsMixin("brand"), NiceModel):
|
|||
validators=[validate_category_image_dimensions],
|
||||
verbose_name=_("brand big image"),
|
||||
)
|
||||
description = TextField( # noqa: DJ001
|
||||
description: TextField = TextField(
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_("add a detailed description of the brand"),
|
||||
verbose_name=_("brand description"),
|
||||
)
|
||||
categories = ManyToManyField(
|
||||
categories: ManyToManyField = ManyToManyField(
|
||||
"core.Category",
|
||||
blank=True,
|
||||
help_text=_("optional categories that this brand is associated with"),
|
||||
|
|
@ -276,14 +276,14 @@ class Brand(ExportModelOperationsMixin("brand"), NiceModel):
|
|||
class Product(ExportModelOperationsMixin("product"), NiceModel):
|
||||
is_publicly_visible = True
|
||||
|
||||
category = ForeignKey(
|
||||
category: ForeignKey = ForeignKey(
|
||||
"core.Category",
|
||||
on_delete=CASCADE,
|
||||
help_text=_("category this product belongs to"),
|
||||
verbose_name=_("category"),
|
||||
related_name="products",
|
||||
)
|
||||
brand = ForeignKey(
|
||||
brand: ForeignKey = ForeignKey(
|
||||
"core.Brand",
|
||||
on_delete=CASCADE,
|
||||
blank=True,
|
||||
|
|
@ -291,31 +291,31 @@ class Product(ExportModelOperationsMixin("product"), NiceModel):
|
|||
help_text=_("optionally associate this product with a brand"),
|
||||
verbose_name=_("brand"),
|
||||
)
|
||||
tags = ManyToManyField(
|
||||
tags: ManyToManyField = ManyToManyField(
|
||||
"core.ProductTag",
|
||||
blank=True,
|
||||
help_text=_("tags that help describe or group this product"),
|
||||
verbose_name=_("product tags"),
|
||||
)
|
||||
is_digital = BooleanField(
|
||||
is_digital: BooleanField = BooleanField(
|
||||
default=False,
|
||||
help_text=_("indicates whether this product is digitally delivered"),
|
||||
verbose_name=_("is product digital"),
|
||||
blank=False,
|
||||
null=False,
|
||||
)
|
||||
name = CharField(
|
||||
name: CharField = CharField(
|
||||
max_length=255,
|
||||
help_text=_("provide a clear identifying name for the product"),
|
||||
verbose_name=_("product name"),
|
||||
)
|
||||
description = TextField( # noqa: DJ001
|
||||
description: TextField = TextField(
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_("add a detailed description of the product"),
|
||||
verbose_name=_("product description"),
|
||||
)
|
||||
partnumber = CharField( # noqa: DJ001
|
||||
partnumber: CharField = CharField(
|
||||
unique=True,
|
||||
default=None,
|
||||
blank=False,
|
||||
|
|
@ -388,13 +388,13 @@ class Vendor(ExportModelOperationsMixin("vendor"), NiceModel):
|
|||
help_text=_("stores credentials and endpoints required for vendor communication"),
|
||||
verbose_name=_("authentication info"),
|
||||
)
|
||||
markup_percent = IntegerField(
|
||||
markup_percent: IntegerField = IntegerField(
|
||||
default=0,
|
||||
validators=[MinValueValidator(0), MaxValueValidator(100)],
|
||||
help_text=_("define the markup for products retrieved from this vendor"),
|
||||
verbose_name=_("vendor markup percentage"),
|
||||
)
|
||||
name = CharField(
|
||||
name: CharField = CharField(
|
||||
max_length=255,
|
||||
help_text=_("name of this vendor"),
|
||||
verbose_name=_("vendor name"),
|
||||
|
|
@ -417,13 +417,13 @@ class Vendor(ExportModelOperationsMixin("vendor"), NiceModel):
|
|||
class Feedback(ExportModelOperationsMixin("feedback"), NiceModel):
|
||||
is_publicly_visible = True
|
||||
|
||||
comment = TextField( # noqa: DJ001
|
||||
comment: TextField = TextField(
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_("user-provided comments about their experience with the product"),
|
||||
verbose_name=_("feedback comments"),
|
||||
)
|
||||
order_product = OneToOneField(
|
||||
order_product: OneToOneField = OneToOneField(
|
||||
"core.OrderProduct",
|
||||
on_delete=CASCADE,
|
||||
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"),
|
||||
verbose_name=_("related order product"),
|
||||
)
|
||||
rating = FloatField(
|
||||
rating: FloatField = FloatField(
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_("user-assigned rating for the product"),
|
||||
|
|
@ -450,7 +450,7 @@ class Feedback(ExportModelOperationsMixin("feedback"), NiceModel):
|
|||
class Order(ExportModelOperationsMixin("order"), NiceModel):
|
||||
is_publicly_visible = False
|
||||
|
||||
billing_address = ForeignKey(
|
||||
billing_address: ForeignKey = ForeignKey(
|
||||
"core.Address",
|
||||
on_delete=CASCADE,
|
||||
blank=True,
|
||||
|
|
@ -459,7 +459,7 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
|||
help_text=_("the billing address used for this order"),
|
||||
verbose_name=_("billing address"),
|
||||
)
|
||||
promo_code = ForeignKey(
|
||||
promo_code: ForeignKey = ForeignKey(
|
||||
"core.PromoCode",
|
||||
on_delete=PROTECT,
|
||||
blank=True,
|
||||
|
|
@ -467,7 +467,7 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
|||
help_text=_("optional promo code applied to this order"),
|
||||
verbose_name=_("applied promo code"),
|
||||
)
|
||||
shipping_address = ForeignKey(
|
||||
shipping_address: ForeignKey = ForeignKey(
|
||||
"core.Address",
|
||||
on_delete=CASCADE,
|
||||
blank=True,
|
||||
|
|
@ -476,7 +476,7 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
|||
help_text=_("the shipping address used for this order"),
|
||||
verbose_name=_("shipping address"),
|
||||
)
|
||||
status = CharField(
|
||||
status: CharField = CharField(
|
||||
default="PENDING",
|
||||
max_length=64,
|
||||
choices=ORDER_STATUS_CHOICES,
|
||||
|
|
@ -495,7 +495,7 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
|||
help_text=_("json representation of order attributes for this order"),
|
||||
verbose_name=_("attributes"),
|
||||
)
|
||||
user = ForeignKey(
|
||||
user: ForeignKey = ForeignKey(
|
||||
"vibes_auth.User",
|
||||
on_delete=CASCADE,
|
||||
help_text=_("the user who placed the order"),
|
||||
|
|
@ -504,14 +504,14 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
|||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
buy_time = DateTimeField(
|
||||
buy_time: DateTimeField = DateTimeField(
|
||||
help_text=_("the timestamp when the order was finalized"),
|
||||
verbose_name=_("buy time"),
|
||||
default=None,
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
human_readable_id = CharField(
|
||||
human_readable_id: CharField = CharField(
|
||||
max_length=8,
|
||||
help_text=_("a human-readable identifier for the order"),
|
||||
verbose_name=_("human readable id"),
|
||||
|
|
@ -524,7 +524,7 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
|||
verbose_name_plural = _("orders")
|
||||
|
||||
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
|
||||
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")
|
||||
|
||||
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(
|
||||
product=product,
|
||||
|
|
@ -597,7 +597,12 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
|||
name = "Product"
|
||||
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"]:
|
||||
raise ValueError(_("you cannot remove products from an order that is not a pending one"))
|
||||
try:
|
||||
|
|
@ -818,31 +823,31 @@ class Order(ExportModelOperationsMixin("order"), NiceModel):
|
|||
class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel):
|
||||
is_publicly_visible = False
|
||||
|
||||
buy_price = FloatField(
|
||||
buy_price: FloatField = FloatField(
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_("the price paid by the customer for this product at purchase time"),
|
||||
verbose_name=_("purchase price at order time"),
|
||||
)
|
||||
comments = TextField( # noqa: DJ001
|
||||
comments: TextField = TextField(
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_("internal comments for admins about this ordered product"),
|
||||
verbose_name=_("internal comments"),
|
||||
)
|
||||
notifications = JSONField(
|
||||
notifications: JSONField = JSONField(
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_("json structure of notifications to display to users"),
|
||||
verbose_name=_("user notifications"),
|
||||
)
|
||||
attributes = JSONField(
|
||||
attributes: JSONField = JSONField(
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_("json representation of this item's attributes"),
|
||||
verbose_name=_("ordered product attributes"),
|
||||
)
|
||||
order = ForeignKey(
|
||||
order: ForeignKey = ForeignKey(
|
||||
"core.Order",
|
||||
on_delete=CASCADE,
|
||||
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",
|
||||
null=True,
|
||||
)
|
||||
product = ForeignKey(
|
||||
product: ForeignKey = ForeignKey(
|
||||
"core.Product",
|
||||
on_delete=PROTECT,
|
||||
blank=True,
|
||||
|
|
@ -858,14 +863,14 @@ class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel):
|
|||
help_text=_("the specific product associated with this order line"),
|
||||
verbose_name=_("associated product"),
|
||||
)
|
||||
quantity = PositiveIntegerField(
|
||||
quantity: PositiveIntegerField = PositiveIntegerField(
|
||||
blank=False,
|
||||
null=False,
|
||||
default=1,
|
||||
help_text=_("quantity of this specific product in the order"),
|
||||
verbose_name=_("product quantity"),
|
||||
)
|
||||
status = CharField(
|
||||
status: CharField = CharField(
|
||||
max_length=128,
|
||||
blank=False,
|
||||
null=False,
|
||||
|
|
@ -921,7 +926,7 @@ class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel):
|
|||
return self.download.url
|
||||
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"]:
|
||||
raise ValueError(_(f"wrong action specified for feedback: {action}"))
|
||||
if action == "remove" and self.feedback:
|
||||
|
|
@ -938,14 +943,14 @@ class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel):
|
|||
class ProductTag(ExportModelOperationsMixin("product_tag"), NiceModel):
|
||||
is_publicly_visible = True
|
||||
|
||||
tag_name = CharField(
|
||||
tag_name: CharField = CharField(
|
||||
blank=False,
|
||||
null=False,
|
||||
max_length=255,
|
||||
help_text=_("internal tag identifier for the product tag"),
|
||||
verbose_name=_("tag name"),
|
||||
)
|
||||
name = CharField(
|
||||
name: CharField = CharField(
|
||||
max_length=255,
|
||||
help_text=_("user-friendly name for the product tag"),
|
||||
verbose_name=_("tag display name"),
|
||||
|
|
@ -963,14 +968,14 @@ class ProductTag(ExportModelOperationsMixin("product_tag"), NiceModel):
|
|||
class CategoryTag(ExportModelOperationsMixin("category_tag"), NiceModel):
|
||||
is_publicly_visible = True
|
||||
|
||||
tag_name = CharField(
|
||||
tag_name: CharField = CharField(
|
||||
blank=False,
|
||||
null=False,
|
||||
max_length=255,
|
||||
help_text=_("internal tag identifier for the product tag"),
|
||||
verbose_name=_("tag name"),
|
||||
)
|
||||
name = CharField(
|
||||
name: CharField = CharField(
|
||||
max_length=255,
|
||||
help_text=_("user-friendly name for the product tag"),
|
||||
verbose_name=_("tag display name"),
|
||||
|
|
@ -988,7 +993,7 @@ class CategoryTag(ExportModelOperationsMixin("category_tag"), NiceModel):
|
|||
class ProductImage(ExportModelOperationsMixin("product_image"), NiceModel):
|
||||
is_publicly_visible = True
|
||||
|
||||
alt = CharField(
|
||||
alt: CharField = CharField(
|
||||
max_length=255,
|
||||
help_text=_("provide alternative text for the image for accessibility"),
|
||||
verbose_name=_("image alt text"),
|
||||
|
|
@ -998,13 +1003,13 @@ class ProductImage(ExportModelOperationsMixin("product_image"), NiceModel):
|
|||
verbose_name=_("product image"),
|
||||
upload_to=get_product_uuid_as_path,
|
||||
)
|
||||
priority = IntegerField(
|
||||
priority: IntegerField = IntegerField(
|
||||
default=1,
|
||||
validators=[MinValueValidator(1)],
|
||||
help_text=_("determines the order in which images are displayed"),
|
||||
verbose_name=_("display priority"),
|
||||
)
|
||||
product = ForeignKey(
|
||||
product: ForeignKey = ForeignKey(
|
||||
"core.Product",
|
||||
on_delete=CASCADE,
|
||||
help_text=_("the product that this image represents"),
|
||||
|
|
@ -1027,7 +1032,7 @@ class ProductImage(ExportModelOperationsMixin("product_image"), NiceModel):
|
|||
class PromoCode(ExportModelOperationsMixin("promocode"), NiceModel):
|
||||
is_publicly_visible = False
|
||||
|
||||
code = CharField(
|
||||
code: CharField = CharField(
|
||||
max_length=20,
|
||||
unique=True,
|
||||
default=get_random_code,
|
||||
|
|
@ -1042,7 +1047,7 @@ class PromoCode(ExportModelOperationsMixin("promocode"), NiceModel):
|
|||
help_text=_("fixed discount amount applied if percent is not used"),
|
||||
verbose_name=_("fixed discount amount"),
|
||||
)
|
||||
discount_percent = IntegerField(
|
||||
discount_percent: IntegerField = IntegerField(
|
||||
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
||||
blank=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"),
|
||||
verbose_name=_("usage timestamp"),
|
||||
)
|
||||
user = ForeignKey(
|
||||
user: ForeignKey = ForeignKey(
|
||||
"vibes_auth.User",
|
||||
on_delete=CASCADE,
|
||||
help_text=_("user assigned to this promocode if applicable"),
|
||||
|
|
@ -1125,12 +1130,12 @@ class PromoCode(ExportModelOperationsMixin("promocode"), NiceModel):
|
|||
class Promotion(ExportModelOperationsMixin("promotion"), NiceModel):
|
||||
is_publicly_visible = True
|
||||
|
||||
discount_percent = IntegerField(
|
||||
discount_percent: IntegerField = IntegerField(
|
||||
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
||||
help_text=_("percentage discount for the selected products"),
|
||||
verbose_name=_("discount percentage"),
|
||||
)
|
||||
name = CharField(
|
||||
name: CharField = CharField(
|
||||
max_length=256,
|
||||
unique=True,
|
||||
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"),
|
||||
verbose_name=_("promotion description"),
|
||||
)
|
||||
products = ManyToManyField(
|
||||
products: ManyToManyField = ManyToManyField(
|
||||
"core.Product",
|
||||
blank=True,
|
||||
help_text=_("select which products are included in this promotion"),
|
||||
|
|
@ -1162,7 +1167,7 @@ class Promotion(ExportModelOperationsMixin("promotion"), NiceModel):
|
|||
class Stock(ExportModelOperationsMixin("stock"), NiceModel):
|
||||
is_publicly_visible = False
|
||||
|
||||
vendor = ForeignKey(
|
||||
vendor: ForeignKey = ForeignKey(
|
||||
"core.Vendor",
|
||||
on_delete=CASCADE,
|
||||
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"),
|
||||
verbose_name=_("selling price"),
|
||||
)
|
||||
product = ForeignKey(
|
||||
product: ForeignKey = ForeignKey(
|
||||
"core.Product",
|
||||
on_delete=CASCADE,
|
||||
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"),
|
||||
verbose_name=_("vendor purchase price"),
|
||||
)
|
||||
quantity = IntegerField(
|
||||
quantity: IntegerField = IntegerField(
|
||||
default=0,
|
||||
help_text=_("available quantity of the product in stock"),
|
||||
verbose_name=_("quantity in stock"),
|
||||
)
|
||||
sku = CharField(
|
||||
sku: CharField = CharField(
|
||||
max_length=255,
|
||||
help_text=_("vendor-assigned SKU for identifying the product"),
|
||||
verbose_name=_("vendor sku"),
|
||||
|
|
@ -1217,13 +1222,13 @@ class Stock(ExportModelOperationsMixin("stock"), NiceModel):
|
|||
class Wishlist(ExportModelOperationsMixin("wishlist"), NiceModel):
|
||||
is_publicly_visible = False
|
||||
|
||||
products = ManyToManyField(
|
||||
products: ManyToManyField = ManyToManyField(
|
||||
"core.Product",
|
||||
blank=True,
|
||||
help_text=_("products that the user has marked as wanted"),
|
||||
verbose_name=_("wishlisted products"),
|
||||
)
|
||||
user = OneToOneField(
|
||||
user: OneToOneField = OneToOneField(
|
||||
"vibes_auth.User",
|
||||
on_delete=CASCADE,
|
||||
blank=True,
|
||||
|
|
@ -1278,8 +1283,8 @@ class Wishlist(ExportModelOperationsMixin("wishlist"), NiceModel):
|
|||
class DigitalAssetDownload(ExportModelOperationsMixin("attribute_group"), NiceModel):
|
||||
is_publicly_visible = False
|
||||
|
||||
order_product = OneToOneField(to=OrderProduct, on_delete=CASCADE, related_name="download")
|
||||
num_downloads = IntegerField(default=0)
|
||||
order_product: OneToOneField = OneToOneField(to=OrderProduct, on_delete=CASCADE, related_name="download")
|
||||
num_downloads: IntegerField = IntegerField(default=0)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("download")
|
||||
|
|
@ -1293,13 +1298,15 @@ class DigitalAssetDownload(ExportModelOperationsMixin("attribute_group"), NiceMo
|
|||
if self.order_product.status != "FINISHED":
|
||||
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):
|
||||
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)
|
||||
|
||||
class Meta:
|
||||
|
|
@ -1326,22 +1333,26 @@ class Address(ExportModelOperationsMixin("address"), NiceModel):
|
|||
help_text=_("address line for the customer"),
|
||||
verbose_name=_("address line"),
|
||||
)
|
||||
street = CharField(_("street"), max_length=255, null=True) # noqa: DJ001
|
||||
district = CharField(_("district"), max_length=255, null=True) # noqa: DJ001
|
||||
city = CharField(_("city"), max_length=100, null=True) # noqa: DJ001
|
||||
region = CharField(_("region"), max_length=100, null=True) # noqa: DJ001
|
||||
postal_code = CharField(_("postal code"), max_length=20, null=True) # noqa: DJ001
|
||||
country = CharField(_("country"), max_length=40, null=True) # noqa: DJ001
|
||||
street: CharField = CharField(_("street"), max_length=255, null=True)
|
||||
district: CharField = CharField(_("district"), max_length=255, null=True)
|
||||
city: CharField = CharField(_("city"), max_length=100, null=True)
|
||||
region: CharField = CharField(_("region"), max_length=100, null=True)
|
||||
postal_code: CharField = CharField(_("postal code"), max_length=20, null=True)
|
||||
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)")
|
||||
)
|
||||
|
||||
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()
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ class IsOwnerOrReadOnly(permissions.BasePermission):
|
|||
return obj.user == request.user
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
class EvibesPermission(permissions.BasePermission):
|
||||
ACTION_PERM_MAP = {
|
||||
"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 .simple 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)
|
||||
from .utility import * # noqa: F403
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import logging
|
||||
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.db.models.functions import Length
|
||||
from rest_framework.fields import JSONField, SerializerMethodField
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
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.utility import AddressSerializer
|
||||
from vibes_auth.models import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -77,8 +80,11 @@ class CategoryDetailSerializer(ModelSerializer):
|
|||
if filterable_results:
|
||||
return filterable_results
|
||||
|
||||
request = self.context.get("request")
|
||||
user = getattr(request, "user", None)
|
||||
request: Request | None = self.context.get("request")
|
||||
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)
|
||||
|
||||
|
|
@ -100,12 +106,15 @@ class CategoryDetailSerializer(ModelSerializer):
|
|||
}
|
||||
)
|
||||
|
||||
if user is None:
|
||||
user = AnonymousUser()
|
||||
|
||||
if not user.has_perm("view_attribute"):
|
||||
cache.set(f"{obj.uuid}_filterable_results", filterable_results, 86400)
|
||||
|
||||
return filterable_results
|
||||
|
||||
def get_children(self, obj) -> list[dict]:
|
||||
def get_children(self, obj) -> Collection[Any]:
|
||||
request = self.context.get("request")
|
||||
if request is not None and request.user.has_perm("view_category"):
|
||||
children = obj.children.all()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from contextlib import suppress
|
||||
from typing import Optional
|
||||
from typing import Collection, Optional
|
||||
|
||||
from rest_framework.fields import JSONField, SerializerMethodField
|
||||
from rest_framework.relations import PrimaryKeyRelatedField
|
||||
|
|
@ -27,8 +27,8 @@ from core.serializers.utility import AddressSerializer
|
|||
|
||||
|
||||
class AttributeGroupSimpleSerializer(ModelSerializer):
|
||||
parent = PrimaryKeyRelatedField(read_only=True)
|
||||
children = PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
parent: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)
|
||||
children: PrimaryKeyRelatedField = PrimaryKeyRelatedField(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = AttributeGroup
|
||||
|
|
@ -59,7 +59,7 @@ class CategorySimpleSerializer(ModelSerializer):
|
|||
return obj.image.url
|
||||
return None
|
||||
|
||||
def get_children(self, obj) -> list[dict]:
|
||||
def get_children(self, obj) -> Collection:
|
||||
request = self.context.get("request")
|
||||
if request is not None and request.user.has_perm("view_category"):
|
||||
children = obj.children.all()
|
||||
|
|
@ -111,7 +111,7 @@ class ProductTagSimpleSerializer(ModelSerializer):
|
|||
|
||||
|
||||
class ProductImageSimpleSerializer(ModelSerializer):
|
||||
product = PrimaryKeyRelatedField(read_only=True)
|
||||
product: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = ProductImage
|
||||
|
|
@ -139,7 +139,7 @@ class AttributeSimpleSerializer(ModelSerializer):
|
|||
|
||||
class AttributeValueSimpleSerializer(ModelSerializer):
|
||||
attribute = AttributeSimpleSerializer(read_only=True)
|
||||
product = PrimaryKeyRelatedField(read_only=True)
|
||||
product: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = AttributeValue
|
||||
|
|
@ -246,7 +246,7 @@ class PromotionSimpleSerializer(ModelSerializer):
|
|||
|
||||
|
||||
class WishlistSimpleSerializer(ModelSerializer):
|
||||
user = PrimaryKeyRelatedField(read_only=True)
|
||||
user: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)
|
||||
products = ProductSimpleSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
|
|
@ -259,7 +259,7 @@ class WishlistSimpleSerializer(ModelSerializer):
|
|||
|
||||
|
||||
class FeedbackSimpleSerializer(ModelSerializer):
|
||||
order_product = PrimaryKeyRelatedField(read_only=True)
|
||||
order_product: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Feedback
|
||||
|
|
@ -285,7 +285,7 @@ class OrderProductSimpleSerializer(ModelSerializer):
|
|||
|
||||
|
||||
class OrderSimpleSerializer(ModelSerializer):
|
||||
user = PrimaryKeyRelatedField(read_only=True)
|
||||
user: PrimaryKeyRelatedField = PrimaryKeyRelatedField(read_only=True)
|
||||
promo_code = PromoCodeSimpleSerializer(read_only=True)
|
||||
order_products = OrderProductSimpleSerializer(many=True, read_only=True)
|
||||
billing_address = AddressSerializer(read_only=True, required=False)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,17 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import CharField, DictField, FloatField, IntegerField
|
||||
from rest_framework.serializers import ModelSerializer, Serializer
|
||||
from rest_framework.fields import (
|
||||
BooleanField,
|
||||
CharField,
|
||||
DictField,
|
||||
Field,
|
||||
FloatField,
|
||||
IntegerField,
|
||||
JSONField,
|
||||
ListField,
|
||||
UUIDField,
|
||||
)
|
||||
from rest_framework.serializers import ListSerializer, ModelSerializer, Serializer
|
||||
|
||||
from core.models import Address
|
||||
|
||||
|
|
@ -75,3 +85,99 @@ class DoFeedbackSerializer(Serializer):
|
|||
def validate(self, data):
|
||||
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."))
|
||||
|
||||
|
||||
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>
|
||||
<!--suppress JSSuspiciousNameCombination -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Maintenance</title>
|
||||
<meta name="viewport" content="width=device-height, width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@500&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
background: #2a2a3a;
|
||||
color: #fff;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Maintenance</title>
|
||||
<meta name="viewport"
|
||||
content="width=device-height, width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@500&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
background: #2a2a3a;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||
<script>
|
||||
(function() {
|
||||
let scene, camera, renderer;
|
||||
let particleSystem, textGroup, fontLoader;
|
||||
const clock = new THREE.Clock();
|
||||
let targetRotX = 0, targetRotY = 0;
|
||||
let currentRotX = 0, currentRotY = 0;
|
||||
let isMobile = 'ontouchstart' in window;
|
||||
(function () {
|
||||
let scene, camera, renderer;
|
||||
let particleSystem, textGroup, fontLoader;
|
||||
const clock = new THREE.Clock();
|
||||
let targetRotX = 0, targetRotY = 0;
|
||||
let currentRotX = 0, currentRotY = 0;
|
||||
let isMobile = 'ontouchstart' in window;
|
||||
|
||||
function init() {
|
||||
scene = new THREE.Scene();
|
||||
camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 2000);
|
||||
camera.position.z = 70;
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
function init() {
|
||||
scene = new THREE.Scene();
|
||||
camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 2000);
|
||||
camera.position.z = 70;
|
||||
renderer = new THREE.WebGLRenderer({antialias: true});
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
|
||||
createGradientBackground();
|
||||
createParticleField();
|
||||
createGradientBackground();
|
||||
createParticleField();
|
||||
|
||||
fontLoader = new THREE.FontLoader();
|
||||
fontLoader.load('https://api.evibes.com/static/Source%20Code%20Pro%20ExtraLight_Regular.json', f => {
|
||||
create3DText(f);
|
||||
fitCameraToText();
|
||||
});
|
||||
fontLoader = new THREE.FontLoader();
|
||||
fontLoader.load('https://api.evibes.com/static/Source%20Code%20Pro%20ExtraLight_Regular.json', f => {
|
||||
create3DText(f);
|
||||
fitCameraToText();
|
||||
});
|
||||
|
||||
window.addEventListener('resize', onWindowResize);
|
||||
if (isMobile) {
|
||||
window.addEventListener('touchmove', onTouchMove, { passive: false });
|
||||
} else {
|
||||
window.addEventListener('mousemove', onMouseMove);
|
||||
}
|
||||
onWindowResize();
|
||||
animate();
|
||||
}
|
||||
window.addEventListener('resize', onWindowResize);
|
||||
if (isMobile) {
|
||||
window.addEventListener('touchmove', onTouchMove, {passive: false});
|
||||
} else {
|
||||
window.addEventListener('mousemove', onMouseMove);
|
||||
}
|
||||
onWindowResize();
|
||||
animate();
|
||||
}
|
||||
|
||||
function createGradientBackground() {
|
||||
const c = document.createElement('canvas');
|
||||
c.width = 2;
|
||||
c.height = 2;
|
||||
const ctx = c.getContext('2d');
|
||||
const gradient = ctx.createLinearGradient(0, 0, 0, 2);
|
||||
gradient.addColorStop(0, '#1E1E2A');
|
||||
gradient.addColorStop(1, '#4C4C6A');
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, 2, 2);
|
||||
const texture = new THREE.Texture(c);
|
||||
texture.needsUpdate = true;
|
||||
scene.background = texture;
|
||||
}
|
||||
function createGradientBackground() {
|
||||
const c = document.createElement('canvas');
|
||||
c.width = 2;
|
||||
c.height = 2;
|
||||
const ctx = c.getContext('2d');
|
||||
const gradient = ctx.createLinearGradient(0, 0, 0, 2);
|
||||
gradient.addColorStop(0, '#1E1E2A');
|
||||
gradient.addColorStop(1, '#4C4C6A');
|
||||
ctx.fillStyle = gradient;
|
||||
ctx.fillRect(0, 0, 2, 2);
|
||||
const texture = new THREE.Texture(c);
|
||||
texture.needsUpdate = true;
|
||||
scene.background = texture;
|
||||
}
|
||||
|
||||
function createParticleField() {
|
||||
const particleCount = 15000;
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
const positions = new Float32Array(particleCount * 3);
|
||||
const colors = new Float32Array(particleCount * 3);
|
||||
const colorStart = new THREE.Color(0x34000d);
|
||||
const colorEnd = new THREE.Color(0x02066F);
|
||||
function createParticleField() {
|
||||
const particleCount = 15000;
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
const positions = new Float32Array(particleCount * 3);
|
||||
const colors = new Float32Array(particleCount * 3);
|
||||
const colorStart = new THREE.Color(0x34000d);
|
||||
const colorEnd = new THREE.Color(0x02066F);
|
||||
|
||||
for (let i = 0; i < particleCount; i++) {
|
||||
positions[i * 3] = (Math.random() - 0.5) * 400;
|
||||
positions[i * 3 + 1] = (Math.random() - 0.5) * 400;
|
||||
positions[i * 3 + 2] = (Math.random() - 0.5) * 400;
|
||||
const mixRatio = i / particleCount;
|
||||
const color = colorStart.clone().lerp(colorEnd, mixRatio);
|
||||
colors[i * 3] = color.r;
|
||||
colors[i * 3 + 1] = color.g;
|
||||
colors[i * 3 + 2] = color.b;
|
||||
}
|
||||
for (let i = 0; i < particleCount; i++) {
|
||||
positions[i * 3] = (Math.random() - 0.5) * 400;
|
||||
positions[i * 3 + 1] = (Math.random() - 0.5) * 400;
|
||||
positions[i * 3 + 2] = (Math.random() - 0.5) * 400;
|
||||
const mixRatio = i / particleCount;
|
||||
const color = colorStart.clone().lerp(colorEnd, mixRatio);
|
||||
colors[i * 3] = color.r;
|
||||
colors[i * 3 + 1] = color.g;
|
||||
colors[i * 3 + 2] = color.b;
|
||||
}
|
||||
|
||||
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
||||
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
||||
const material = new THREE.PointsMaterial({
|
||||
size: 0.7,
|
||||
vertexColors: true,
|
||||
transparent: true,
|
||||
opacity: 0.7,
|
||||
blending: THREE.AdditiveBlending
|
||||
});
|
||||
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
||||
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
||||
const material = new THREE.PointsMaterial({
|
||||
size: 0.7,
|
||||
vertexColors: true,
|
||||
transparent: true,
|
||||
opacity: 0.7,
|
||||
blending: THREE.AdditiveBlending
|
||||
});
|
||||
|
||||
particleSystem = new THREE.Points(geometry, material);
|
||||
scene.add(particleSystem);
|
||||
}
|
||||
particleSystem = new THREE.Points(geometry, material);
|
||||
scene.add(particleSystem);
|
||||
}
|
||||
|
||||
function create3DText(font) {
|
||||
textGroup = new THREE.Group();
|
||||
function create3DText(font) {
|
||||
textGroup = new THREE.Group();
|
||||
|
||||
const text1 = new THREE.TextGeometry("We’ll Be Back Soon", {
|
||||
font,
|
||||
size: 5,
|
||||
height: 1,
|
||||
curveSegments: 12,
|
||||
bevelEnabled: true,
|
||||
bevelThickness: 0.2,
|
||||
bevelSize: 0.1,
|
||||
bevelSegments: 5
|
||||
});
|
||||
const material1 = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x111111 });
|
||||
const mesh1 = new THREE.Mesh(text1, material1);
|
||||
mesh1.geometry.center();
|
||||
mesh1.position.y = 5;
|
||||
textGroup.add(mesh1);
|
||||
const text1 = new THREE.TextGeometry("We’ll Be Back Soon", {
|
||||
font,
|
||||
size: 5,
|
||||
height: 1,
|
||||
curveSegments: 12,
|
||||
bevelEnabled: true,
|
||||
bevelThickness: 0.2,
|
||||
bevelSize: 0.1,
|
||||
bevelSegments: 5
|
||||
});
|
||||
const material1 = new THREE.MeshPhongMaterial({color: 0xffffff, emissive: 0x111111});
|
||||
const mesh1 = new THREE.Mesh(text1, material1);
|
||||
mesh1.geometry.center();
|
||||
mesh1.position.y = 5;
|
||||
textGroup.add(mesh1);
|
||||
|
||||
const text2 = new THREE.TextGeometry("An update is in progress. Please check back later.", {
|
||||
font,
|
||||
size: 2,
|
||||
height: 0.5,
|
||||
curveSegments: 12,
|
||||
bevelEnabled: false
|
||||
});
|
||||
const material2 = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x111111 });
|
||||
const mesh2 = new THREE.Mesh(text2, material2);
|
||||
mesh2.geometry.center();
|
||||
mesh2.position.y = -5;
|
||||
textGroup.add(mesh2);
|
||||
const text2 = new THREE.TextGeometry("An update is in progress. Please check back later.", {
|
||||
font,
|
||||
size: 2,
|
||||
height: 0.5,
|
||||
curveSegments: 12,
|
||||
bevelEnabled: false
|
||||
});
|
||||
const material2 = new THREE.MeshPhongMaterial({color: 0xffffff, emissive: 0x111111});
|
||||
const mesh2 = new THREE.Mesh(text2, material2);
|
||||
mesh2.geometry.center();
|
||||
mesh2.position.y = -5;
|
||||
textGroup.add(mesh2);
|
||||
|
||||
scene.add(textGroup);
|
||||
scene.add(textGroup);
|
||||
|
||||
const ambientLight = new THREE.AmbientLight(0x404040, 2);
|
||||
scene.add(ambientLight);
|
||||
const ambientLight = new THREE.AmbientLight(0x404040, 2);
|
||||
scene.add(ambientLight);
|
||||
|
||||
const spotLight = new THREE.SpotLight(0xffffff, 1);
|
||||
spotLight.position.set(100, 100, 100);
|
||||
scene.add(spotLight);
|
||||
}
|
||||
const spotLight = new THREE.SpotLight(0xffffff, 1);
|
||||
spotLight.position.set(100, 100, 100);
|
||||
scene.add(spotLight);
|
||||
}
|
||||
|
||||
function fitCameraToText() {
|
||||
if (!textGroup) return;
|
||||
const box = new THREE.Box3().setFromObject(textGroup);
|
||||
const size = box.getSize(new THREE.Vector3());
|
||||
const center = box.getCenter(new THREE.Vector3());
|
||||
function fitCameraToText() {
|
||||
if (!textGroup) return;
|
||||
const box = new THREE.Box3().setFromObject(textGroup);
|
||||
const size = box.getSize(new THREE.Vector3());
|
||||
const center = box.getCenter(new THREE.Vector3());
|
||||
|
||||
const halfSizeToFitOnScreen = size.length() * 0.5;
|
||||
const halfFov = THREE.MathUtils.degToRad(camera.fov * 0.5);
|
||||
let distance = halfSizeToFitOnScreen / Math.sin(halfFov);
|
||||
distance *= 1.5;
|
||||
camera.position.set(center.x, center.y, distance);
|
||||
camera.lookAt(center);
|
||||
}
|
||||
const halfSizeToFitOnScreen = size.length() * 0.5;
|
||||
const halfFov = THREE.MathUtils.degToRad(camera.fov * 0.5);
|
||||
let distance = halfSizeToFitOnScreen / Math.sin(halfFov);
|
||||
distance *= 1.5;
|
||||
camera.position.set(center.x, center.y, distance);
|
||||
camera.lookAt(center);
|
||||
}
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
const delta = clock.getDelta();
|
||||
if (particleSystem) {
|
||||
particleSystem.rotation.y += 0.02 * delta;
|
||||
}
|
||||
currentRotX += (targetRotX - currentRotX) * 0.1;
|
||||
currentRotY += (targetRotY - currentRotY) * 0.1;
|
||||
if (textGroup) {
|
||||
textGroup.rotation.x = currentRotY;
|
||||
textGroup.rotation.y = currentRotX;
|
||||
}
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
const delta = clock.getDelta();
|
||||
if (particleSystem) {
|
||||
particleSystem.rotation.y += 0.02 * delta;
|
||||
}
|
||||
currentRotX += (targetRotX - currentRotX) * 0.1;
|
||||
currentRotY += (targetRotY - currentRotY) * 0.1;
|
||||
if (textGroup) {
|
||||
textGroup.rotation.x = currentRotY;
|
||||
textGroup.rotation.y = currentRotX;
|
||||
}
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
function onWindowResize() {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
fitCameraToText();
|
||||
}
|
||||
function onWindowResize() {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
fitCameraToText();
|
||||
}
|
||||
|
||||
function onMouseMove(event) {
|
||||
targetRotX = ((event.clientX / window.innerWidth) - 0.5) * 2 * 0.3;
|
||||
targetRotY = ((event.clientY / window.innerHeight) - 0.5) * 2 * 0.3;
|
||||
}
|
||||
function onMouseMove(event) {
|
||||
targetRotX = ((event.clientX / window.innerWidth) - 0.5) * 2 * 0.3;
|
||||
targetRotY = ((event.clientY / window.innerHeight) - 0.5) * 2 * 0.3;
|
||||
}
|
||||
|
||||
function onTouchMove(event) {
|
||||
if (event.touches.length > 0) {
|
||||
const touch = event.touches[0];
|
||||
targetRotX = ((touch.clientX / window.innerWidth) - 0.5) * 2 * 0.3;
|
||||
targetRotY = ((touch.clientY / window.innerHeight) - 0.5) * 2 * 0.3;
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
function onTouchMove(event) {
|
||||
if (event.touches.length > 0) {
|
||||
const touch = event.touches[0];
|
||||
targetRotX = ((touch.clientX / window.innerWidth) - 0.5) * 2 * 0.3;
|
||||
targetRotY = ((touch.clientY / window.innerHeight) - 0.5) * 2 * 0.3;
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
init();
|
||||
})();
|
||||
init();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ def update_products_task():
|
|||
includes invoking the `update_stock` method of vendor classes and removing
|
||||
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
|
||||
:rtype: tuple[bool, str]
|
||||
|
|
@ -64,7 +64,7 @@ def update_orderproducts_task():
|
|||
`vendors_classes`. Each vendor class in the `vendors_classes` list is
|
||||
instantiated, and the `update_order_products_statuses` method of the
|
||||
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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
: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:
|
||||
return False, "Not enough products to choose from [< 48]."
|
||||
|
||||
selected_products = []
|
||||
selected_products: list = []
|
||||
|
||||
while len(selected_products) < 48:
|
||||
product = eligible_products.order_by("?").first()
|
||||
|
|
@ -211,8 +211,9 @@ def process_promotions() -> tuple[bool, str]:
|
|||
|
||||
promotion = Promotion.objects.update_or_create(
|
||||
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."
|
||||
|
|
|
|||
|
|
@ -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