Compare commits
1 Commits
v4.6.0
...
add/clean-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c96c3c1775 |
3
.github/.trivyignore
vendored
@@ -1,3 +0,0 @@
|
||||
CVE-2026-26996
|
||||
CVE-2026-27903
|
||||
CVE-2026-27904
|
||||
166
.github/workflows/docker-hub.yml
vendored
@@ -5,60 +5,144 @@ on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- 'main'
|
||||
tags:
|
||||
- "v*"
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- 'main'
|
||||
- 'ci/trivy-fails'
|
||||
|
||||
env:
|
||||
DOCKER_USER: 1001:127
|
||||
SHOULD_PUSH: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'preview') }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-and-push-backend:
|
||||
uses: ./.github/workflows/docker-publish.yml
|
||||
permissions:
|
||||
contents: read
|
||||
secrets: inherit
|
||||
with:
|
||||
image_name: lasuite/impress-backend
|
||||
context: .
|
||||
file: Dockerfile
|
||||
target: backend-production
|
||||
should_push: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'preview') }}
|
||||
docker_user: 1001:127
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: lasuite/impress-backend
|
||||
-
|
||||
name: Login to DockerHub
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'preview')
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USER }}
|
||||
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||
-
|
||||
name: Run trivy scan
|
||||
uses: numerique-gouv/action-trivy-cache@main
|
||||
with:
|
||||
docker-build-args: '--target backend-production -f Dockerfile'
|
||||
docker-image-name: 'docker.io/lasuite/impress-backend:${{ github.sha }}'
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
target: backend-production
|
||||
build-args: DOCKER_USER=${{ env.DOCKER_USER }}:-1000
|
||||
push: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'preview') }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
-
|
||||
name: Cleanup Docker after build
|
||||
if: always()
|
||||
run: |
|
||||
docker system prune -af
|
||||
docker volume prune -f
|
||||
|
||||
build-and-push-frontend:
|
||||
uses: ./.github/workflows/docker-publish.yml
|
||||
permissions:
|
||||
contents: read
|
||||
secrets: inherit
|
||||
with:
|
||||
image_name: lasuite/impress-frontend
|
||||
context: .
|
||||
file: src/frontend/Dockerfile
|
||||
target: frontend-production
|
||||
arm64_reuse_amd64_build_arg: "FRONTEND_IMAGE"
|
||||
should_push: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'preview') }}
|
||||
docker_user: 1001:127
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: lasuite/impress-frontend
|
||||
-
|
||||
name: Login to DockerHub
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'preview')
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USER }}
|
||||
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||
-
|
||||
name: Run trivy scan
|
||||
uses: numerique-gouv/action-trivy-cache@main
|
||||
with:
|
||||
docker-build-args: '-f src/frontend/Dockerfile --target frontend-production'
|
||||
docker-image-name: 'docker.io/lasuite/impress-frontend:${{ github.sha }}'
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./src/frontend/Dockerfile
|
||||
target: frontend-production
|
||||
build-args: |
|
||||
DOCKER_USER=${{ env.DOCKER_USER }}:-1000
|
||||
PUBLISH_AS_MIT=false
|
||||
push: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'preview') }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
-
|
||||
name: Cleanup Docker after build
|
||||
if: always()
|
||||
run: |
|
||||
docker system prune -af
|
||||
docker volume prune -f
|
||||
|
||||
build-and-push-y-provider:
|
||||
uses: ./.github/workflows/docker-publish.yml
|
||||
permissions:
|
||||
contents: read
|
||||
secrets: inherit
|
||||
with:
|
||||
image_name: lasuite/impress-y-provider
|
||||
context: .
|
||||
file: src/frontend/servers/y-provider/Dockerfile
|
||||
target: y-provider
|
||||
should_push: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'preview') }}
|
||||
docker_user: 1001:127
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: lasuite/impress-y-provider
|
||||
-
|
||||
name: Login to DockerHub
|
||||
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'preview')
|
||||
run: echo "${{ secrets.DOCKER_HUB_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_HUB_USER }}" --password-stdin
|
||||
-
|
||||
name: Run trivy scan
|
||||
uses: numerique-gouv/action-trivy-cache@main
|
||||
with:
|
||||
docker-build-args: '-f src/frontend/servers/y-provider/Dockerfile --target y-provider'
|
||||
docker-image-name: 'docker.io/lasuite/impress-y-provider:${{ github.sha }}'
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./src/frontend/servers/y-provider/Dockerfile
|
||||
target: y-provider
|
||||
build-args: DOCKER_USER=${{ env.DOCKER_USER }}:-1000
|
||||
push: ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'preview') }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
-
|
||||
name: Cleanup Docker after build
|
||||
if: always()
|
||||
run: |
|
||||
docker system prune -af
|
||||
docker volume prune -f
|
||||
|
||||
notify-argocd:
|
||||
needs:
|
||||
|
||||
142
.github/workflows/docker-publish.yml
vendored
@@ -1,142 +0,0 @@
|
||||
name: Build and Push Container Image
|
||||
description: Build and push a container image based on the input arguments provided
|
||||
|
||||
"on":
|
||||
workflow_call:
|
||||
inputs:
|
||||
image_name:
|
||||
type: string
|
||||
required: true
|
||||
description: The suffix for the image name, without the registry and without the repository path.
|
||||
context:
|
||||
type: string
|
||||
required: true
|
||||
description: The path to the context to start `docker build` into.
|
||||
file:
|
||||
type: string
|
||||
required: true
|
||||
description: The path to the Dockerfile
|
||||
target:
|
||||
type: string
|
||||
required: false
|
||||
default: ""
|
||||
description: The Dockerfile target stage to build the image for.
|
||||
should_push:
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
description: if the image should be pushed on the docker registry
|
||||
docker_user:
|
||||
type: string
|
||||
required: false
|
||||
default: ""
|
||||
description: The docker_user ARGUMENT to pass to the build step
|
||||
arm64_reuse_amd64_build_arg:
|
||||
type: string
|
||||
required: false
|
||||
default: ""
|
||||
description: "Build arg name to pass first amd64 tag to arm64 build (skips arch-independent build steps)"
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to DockerHub
|
||||
if: ${{ inputs.should_push }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USER }}
|
||||
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ inputs.image_name }}
|
||||
- name: Generate platform-specific tags
|
||||
id: platform-tags
|
||||
run: |
|
||||
AMD64_TAGS=$(echo "${{ steps.meta.outputs.tags }}" | sed 's/$/-amd64/')
|
||||
ARM64_TAGS=$(echo "${{ steps.meta.outputs.tags }}" | sed 's/$/-arm64/')
|
||||
FIRST_AMD64_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -1)-amd64
|
||||
{
|
||||
echo "amd64<<EOF"
|
||||
echo "$AMD64_TAGS"
|
||||
echo "EOF"
|
||||
echo "arm64<<EOF"
|
||||
echo "$ARM64_TAGS"
|
||||
echo "EOF"
|
||||
echo "amd64_first=$FIRST_AMD64_TAG"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
# - name: Run trivy scan
|
||||
# if: ${{ vars.TRIVY_SCAN_ENABLED }} == 'true'
|
||||
# uses: numerique-gouv/action-trivy-cache@main
|
||||
# with:
|
||||
# docker-build-args: "--target ${{ inputs.target }} -f ${{ inputs.file }}"
|
||||
# docker-image-name: "docker.io/${{ inputs.image_name }}:${{ github.sha }}"
|
||||
# trivyignores: ./.github/.trivyignore
|
||||
- name: Build and push (amd64)
|
||||
if: ${{ inputs.should_push }}||${{ vars.TRIVY_SCAN_ENABLED }} != 'true'
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ${{ inputs.context }}
|
||||
file: ${{ inputs.file }}
|
||||
target: ${{ inputs.target }}
|
||||
platforms: linux/amd64
|
||||
build-args: |
|
||||
DOCKER_USER=${{ inputs.docker_user }}
|
||||
PUBLISH_AS_MIT=false
|
||||
push: ${{ inputs.should_push }}
|
||||
provenance: false
|
||||
tags: ${{ steps.platform-tags.outputs.amd64 }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
- name: Build and push (arm64)
|
||||
if: ${{ inputs.should_push }}
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ${{ inputs.context }}
|
||||
file: ${{ inputs.file }}
|
||||
target: ${{ inputs.target }}
|
||||
platforms: linux/arm64
|
||||
build-args: |
|
||||
DOCKER_USER=${{ inputs.docker_user }}
|
||||
PUBLISH_AS_MIT=false
|
||||
${{ inputs.arm64_reuse_amd64_build_arg && format('{0}={1}', inputs.arm64_reuse_amd64_build_arg, steps.platform-tags.outputs.amd64_first) || '' }}
|
||||
push: ${{ inputs.should_push }}
|
||||
provenance: false
|
||||
tags: ${{ steps.platform-tags.outputs.arm64 }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
- name: Create multi-arch manifests
|
||||
if: ${{ inputs.should_push }}
|
||||
id: create-manifest
|
||||
run: |
|
||||
IMAGE="${{ inputs.image_name }}"
|
||||
readarray -t TAGS <<< "${{ steps.meta.outputs.tags }}"
|
||||
FIRST_TAG=""
|
||||
for tag in "${TAGS[@]}"; do
|
||||
[ -z "$tag" ] && continue
|
||||
docker buildx imagetools create -t "$tag" \
|
||||
"${tag}-amd64" "${tag}-arm64"
|
||||
if [ -z "$FIRST_TAG" ]; then
|
||||
FIRST_TAG="$tag"
|
||||
fi
|
||||
done
|
||||
# Get the digest of the multi-arch manifest for attestation
|
||||
# Note: --format '{{.Manifest.Digest}}' is broken (docker/buildx#1175),
|
||||
# so we compute it from the raw manifest JSON instead.
|
||||
if [ -n "$FIRST_TAG" ]; then
|
||||
DIGEST="sha256:$(docker buildx imagetools inspect "$FIRST_TAG" --raw | sha256sum | awk '{print $1}')"
|
||||
echo "digest=$DIGEST" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- name: Cleanup Docker after build
|
||||
if: always()
|
||||
run: |
|
||||
docker system prune -af
|
||||
docker volume prune -f
|
||||
157
.github/workflows/ghcr.yml
vendored
@@ -1,157 +0,0 @@
|
||||
name: Build and Push to GHCR
|
||||
run-name: Build and Push to GHCR
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
env:
|
||||
DOCKER_USER: 1001:127
|
||||
REGISTRY: ghcr.io
|
||||
|
||||
jobs:
|
||||
build-and-push-backend:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.repository.fork == true
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ github.repository }}/backend
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=sha
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
target: backend-production
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: DOCKER_USER=${{ env.DOCKER_USER }}
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
- name: Cleanup Docker after build
|
||||
if: always()
|
||||
run: |
|
||||
docker system prune -af
|
||||
docker volume prune -f
|
||||
|
||||
build-and-push-frontend:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.repository.fork == true
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ github.repository }}/frontend
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=sha
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./src/frontend/Dockerfile
|
||||
target: frontend-production
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: |
|
||||
DOCKER_USER=${{ env.DOCKER_USER }}
|
||||
PUBLISH_AS_MIT=false
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
- name: Cleanup Docker after build
|
||||
if: always()
|
||||
run: |
|
||||
docker system prune -af
|
||||
docker volume prune -f
|
||||
|
||||
build-and-push-y-provider:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.repository.fork == true
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ github.repository }}/y-provider
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=sha
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./src/frontend/servers/y-provider/Dockerfile
|
||||
target: y-provider
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: DOCKER_USER=${{ env.DOCKER_USER }}:-1000
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
- name: Cleanup Docker after build
|
||||
if: always()
|
||||
run: |
|
||||
docker system prune -af
|
||||
docker volume prune -f
|
||||
30
CHANGELOG.md
@@ -6,41 +6,20 @@ and this project adheres to
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v4.6.0] - 2026-03-03
|
||||
|
||||
### Added
|
||||
|
||||
- ✨(frontend) integrate new Blocknote AI feature #1847
|
||||
- 👷(docker) add arm64 platform support for image builds #1901
|
||||
- ✨(tracking) add UTM parameters to shared document links #1896
|
||||
- ✨(frontend) add floating bar with leftpanel collapse button #1876
|
||||
- ✨(frontend) Can print a doc #1832
|
||||
- ✨(backend) manage reconciliation requests for user accounts #1878
|
||||
- 👷(CI) add GHCR workflow for forked repo testing #1851
|
||||
- ✨(frontend) Move doc modal #1886
|
||||
- ⚡️(backend) remove content from Document serializer when asked #1910
|
||||
- ✨(backend) allow the duplication of subpages #1893
|
||||
- ✨(backend) Onboarding docs for new users #1891
|
||||
- 🩺(trivy) add trivyignore file and add minimatch CVE #1915
|
||||
- 🚩 Add feature flags for the AI feature #1922
|
||||
- 🍱(frontend) add icons ui-kit #1943
|
||||
- ✨(backend) add management command to reset a Document #1882
|
||||
|
||||
### Changed
|
||||
|
||||
- ♿️(frontend) prevent dates from being focusable #1855
|
||||
- ♿️(frontend) Focus main container after navigation #1864
|
||||
- 💄(frontend) align colors and logo with ui-kit v2 #1869
|
||||
- 🚸(backend) sort user search results by proximity with the active user #1802
|
||||
- 🚸(oidc) ignore case when fallback on email #1880
|
||||
- ⚡️(CI) optimize Docker Hub workflow #1919
|
||||
- ♿️(frontend) Focus main container after navigation #1854
|
||||
- 🚸(backend) sort user search results by proximity with the active user #1802
|
||||
|
||||
### Fixed
|
||||
|
||||
- 🐛(frontend) fix broadcast store sync #1846
|
||||
- 🐛(helm) use celery resources instead of backend resources #1887
|
||||
- 🐛(helm) reverse liveness and readiness for backend deployment #1887
|
||||
- 🐛(y-provider) use CONVERSION_FILE_MAX_SIZE settings #1913
|
||||
- 🐛(frontend) fix callout block spacing for old browsers #1914
|
||||
|
||||
## [v4.5.0] - 2026-01-28
|
||||
|
||||
@@ -1060,8 +1039,7 @@ and this project adheres to
|
||||
- ✨(frontend) Coming Soon page (#67)
|
||||
- 🚀 Impress, project to manage your documents easily and collaboratively.
|
||||
|
||||
[unreleased]: https://github.com/suitenumerique/docs/compare/v4.6.0...main
|
||||
[v4.6.0]: https://github.com/suitenumerique/docs/releases/v4.6.0
|
||||
[unreleased]: https://github.com/suitenumerique/docs/compare/v4.5.0...main
|
||||
[v4.5.0]: https://github.com/suitenumerique/docs/releases/v4.5.0
|
||||
[v4.4.0]: https://github.com/suitenumerique/docs/releases/v4.4.0
|
||||
[v4.3.0]: https://github.com/suitenumerique/docs/releases/v4.3.0
|
||||
|
||||
19
Dockerfile
@@ -151,7 +151,7 @@ RUN rm -rf /var/cache/apk/*
|
||||
|
||||
ARG IMPRESS_STATIC_ROOT=/data/static
|
||||
|
||||
# Gunicorn - not used by default but configuration file is provided
|
||||
# Gunicorn
|
||||
RUN mkdir -p /usr/local/etc/gunicorn
|
||||
COPY docker/files/usr/local/etc/gunicorn/impress.py /usr/local/etc/gunicorn/impress.py
|
||||
|
||||
@@ -165,18 +165,5 @@ COPY --from=link-collector ${IMPRESS_STATIC_ROOT} ${IMPRESS_STATIC_ROOT}
|
||||
# Copy impress mails
|
||||
COPY --from=mail-builder /mail/backend/core/templates/mail /app/core/templates/mail
|
||||
|
||||
# The default command runs uvicorn ASGI server in dics's main module
|
||||
# WEB_CONCURRENCY: number of workers to run <=> --workers=4
|
||||
ENV WEB_CONCURRENCY=4
|
||||
CMD [\
|
||||
"uvicorn",\
|
||||
"--app-dir=/app",\
|
||||
"--host=0.0.0.0",\
|
||||
"--timeout-graceful-shutdown=300",\
|
||||
"--limit-max-requests=20000",\
|
||||
"--lifespan=off",\
|
||||
"impress.asgi:application"\
|
||||
]
|
||||
|
||||
# To run using gunicorn WSGI server use this instead:
|
||||
#CMD ["gunicorn", "-c", "/usr/local/etc/gunicorn/conversations.py", "impress.wsgi:application"]
|
||||
# The default command runs gunicorn WSGI server in impress's main module
|
||||
CMD ["gunicorn", "-c", "/usr/local/etc/gunicorn/impress.py", "impress.wsgi:application"]
|
||||
|
||||
@@ -16,12 +16,6 @@ the following command inside your docker container:
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [4.6.0] - 2026-02-27
|
||||
|
||||
- ⚠️ Some setup have changed to offer a bigger flexibility and consistency, overriding the favicon and logo are now from the theme configuration.
|
||||
https://github.com/suitenumerique/docs/blob/f24b047a7cc146411412bf759b5b5248a45c3d99/src/backend/impress/configuration/theme/default.json#L129-L161
|
||||
|
||||
|
||||
## [4.0.0] - 2025-11-26
|
||||
|
||||
- ⚠️ We updated `@gouvfr-lasuite/ui-kit` to `0.18.0`, so if you are customizing Docs with a css layer or with a custom template, you need to update your customization to follow the new design system structure.
|
||||
|
||||
45
docs/env.md
@@ -7,21 +7,17 @@ Here we describe all environment variables that can be set for the docs applicat
|
||||
These are the environment variables you can set for the `impress-backend` container.
|
||||
|
||||
| Option | Description | default |
|
||||
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
||||
|-------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------|
|
||||
| AI_ALLOW_REACH_FROM | Users that can use AI must be this level. options are "public", "authenticated", "restricted" | authenticated |
|
||||
| AI_API_KEY | AI key to be used for AI Base url | |
|
||||
| AI_BASE_URL | OpenAI compatible AI base url | |
|
||||
| AI_BOT | Information to give to the frontend about the AI bot | { "name": "Docs AI", "color": "#8bc6ff" }
|
||||
| AI_FEATURE_ENABLED | Enable AI options | false |
|
||||
| AI_FEATURE_BLOCKNOTE_ENABLED | Enable Blocknote AI options | false |
|
||||
| AI_FEATURE_LEGACY_ENABLED | Enable legacyAI options | true |
|
||||
| AI_MODEL | AI Model to use | |
|
||||
| AI_VERCEL_SDK_VERSION | The vercel AI SDK version used | 6 |
|
||||
| ALLOW_LOGOUT_GET_METHOD | Allow get logout method | true |
|
||||
| API_USERS_LIST_LIMIT | Limit on API users | 5 |
|
||||
| API_USERS_LIST_THROTTLE_RATE_BURST | Throttle rate for api on burst | 30/minute |
|
||||
| API_USERS_LIST_THROTTLE_RATE_SUSTAINED | Throttle rate for api | 180/hour |
|
||||
| API_USERS_SEARCH_QUERY_MIN_LENGTH | Minimum characters to insert to search a user | 3 |
|
||||
| API_USERS_SEARCH_QUERY_MIN_LENGTH | Minimum characters to insert to search a user | 3 |
|
||||
| AWS_S3_ACCESS_KEY_ID | Access id for s3 endpoint | |
|
||||
| AWS_S3_ENDPOINT_URL | S3 endpoint | |
|
||||
| AWS_S3_REGION_NAME | Region name for s3 endpoint | |
|
||||
@@ -39,7 +35,7 @@ These are the environment variables you can set for the `impress-backend` contai
|
||||
| CONVERSION_API_SECURE | Require secure conversion api | false |
|
||||
| CONVERSION_API_TIMEOUT | Conversion api timeout | 30 |
|
||||
| CONVERSION_FILE_MAX_SIZE | The file max size allowed when uploaded to convert it | 20971520 (20MB) |
|
||||
| CONVERSION_FILE_EXTENSIONS_ALLOWED | Extension list managed by the conversion service | [".docx", ".md"] |
|
||||
| CONVERSION_FILE_EXTENSIONS_ALLOWED | Extension list managed by the conversion service | [".docx", ".md"]
|
||||
| CRISP_WEBSITE_ID | Crisp website id for support | |
|
||||
| DB_ENGINE | Engine to use for database connections | django.db.backends.postgresql_psycopg2 |
|
||||
| DB_HOST | Host of the database | localhost |
|
||||
@@ -62,22 +58,22 @@ These are the environment variables you can set for the `impress-backend` contai
|
||||
| DJANGO_EMAIL_HOST_USER | User to authenticate with on the email host | |
|
||||
| DJANGO_EMAIL_LOGO_IMG | Logo for the email | |
|
||||
| DJANGO_EMAIL_PORT | Port used to connect to email host | |
|
||||
| DJANGO_EMAIL_URL_APP | Url used in the email to go to the app | |
|
||||
| DJANGO_EMAIL_URL_APP | Url used in the email to go to the app | |
|
||||
| DJANGO_EMAIL_USE_SSL | Use ssl for email host connection | false |
|
||||
| DJANGO_EMAIL_USE_TLS | Use tls for email host connection | false |
|
||||
| DJANGO_SECRET_KEY | Secret key | |
|
||||
| DJANGO_SERVER_TO_SERVER_API_TOKENS | | [] |
|
||||
| DOCSPEC_API_URL | URL to endpoint of DocSpec conversion API | |
|
||||
| DOCSPEC_API_URL | URL to endpoint of DocSpec conversion API | |
|
||||
| DOCUMENT_IMAGE_MAX_SIZE | Maximum size of document in bytes | 10485760 |
|
||||
| FRONTEND_CSS_URL | To add a external css file to the app | |
|
||||
| FRONTEND_JS_URL | To add a external js file to the app | |
|
||||
| FRONTEND_JS_URL | To add a external js file to the app | |
|
||||
| FRONTEND_HOMEPAGE_FEATURE_ENABLED | Frontend feature flag to display the homepage | false |
|
||||
| FRONTEND_THEME | Frontend theme to use | |
|
||||
| LANGUAGE_CODE | Default language | en-us |
|
||||
| LANGFUSE_SECRET_KEY | The Langfuse secret key used by the sdk | None |
|
||||
| LANGFUSE_PUBLIC_KEY | The Langfuse public key used by the sdk | None |
|
||||
| LANGFUSE_BASE_URL | The Langfuse base url used by the sdk | None |
|
||||
| LASUITE_MARKETING_BACKEND | Backend used when SIGNUP_NEW_USER_TO_MARKETING_EMAIL is True. See https://github.com/suitenumerique/django-lasuite/blob/main/documentation/how-to-use-marketing-backend.md | lasuite.marketing.backends.dummy.DummyBackend |
|
||||
| LASUITE_MARKETING_BACKEND | Backend used when SIGNUP_NEW_USER_TO_MARKETING_EMAIL is True. See https://github.com/suitenumerique/django-lasuite/blob/main/documentation/how-to-use-marketing-backend.md | lasuite.marketing.backends.dummy.DummyBackend |
|
||||
| LASUITE_MARKETING_PARAMETERS | The parameters to configure LASUITE_MARKETING_BACKEND. See https://github.com/suitenumerique/django-lasuite/blob/main/documentation/how-to-use-marketing-backend.md | {} |
|
||||
| LOGGING_LEVEL_LOGGERS_APP | Application logging level. options are "DEBUG", "INFO", "WARN", "ERROR", "CRITICAL" | INFO |
|
||||
| LOGGING_LEVEL_LOGGERS_ROOT | Default logging level. options are "DEBUG", "INFO", "WARN", "ERROR", "CRITICAL" | INFO |
|
||||
@@ -124,12 +120,11 @@ These are the environment variables you can set for the `impress-backend` contai
|
||||
| THEME_CUSTOMIZATION_FILE_PATH | Full path to the file customizing the theme. An example is provided in src/backend/impress/configuration/theme/default.json | BASE_DIR/impress/configuration/theme/default.json |
|
||||
| TRASHBIN_CUTOFF_DAYS | Trashbin cutoff | 30 |
|
||||
| USER_OIDC_ESSENTIAL_CLAIMS | Essential claims in OIDC token | [] |
|
||||
| USER_ONBOARDING_DOCUMENTS | A list of documents IDs for which a read-only access will be created for new s | [] |
|
||||
| USER_ONBOARDING_SANDBOX_DOCUMENT | ID of a template sandbox document that will be duplicated for new users | |
|
||||
| USER_RECONCILIATION_FORM_URL | URL of a third-party form for user reconciliation requests | |
|
||||
| Y_PROVIDER_API_BASE_URL | Y Provider url | |
|
||||
| Y_PROVIDER_API_KEY | Y provider API key | |
|
||||
|
||||
|
||||
## impress-frontend image
|
||||
|
||||
These are the environment variables you can set to build the `impress-frontend` image.
|
||||
@@ -140,31 +135,31 @@ If you want to build the Docker image, this variable is used as an argument in t
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
docker build -f src/frontend/Dockerfile --target frontend-production --build-arg PUBLISH_AS_MIT=false docs-frontend:latest
|
||||
```
|
||||
docker build -f src/frontend/Dockerfile --target frontend-production --build-arg PUBLISH_AS_MIT=false docs-frontend:latest
|
||||
```
|
||||
|
||||
If you want to build the front-end application using the yarn build command, you can edit the file `src/frontend/apps/impress/.env` with the `NODE_ENV=production` environment variable and modify it. Alternatively, you can use the listed environment variables with the prefix `NEXT_PUBLIC_` (for example, `NEXT_PUBLIC_PUBLISH_AS_MIT=false`).
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
```
|
||||
cd src/frontend/apps/impress
|
||||
NODE_ENV=production NEXT_PUBLIC_PUBLISH_AS_MIT=false yarn build
|
||||
```
|
||||
|
||||
| Option | Description | default |
|
||||
| -------------- | ---------------------------------------------------------------------------------- | ------- |
|
||||
| API_ORIGIN | backend domain - it uses the current domain if not initialized | |
|
||||
| SW_DEACTIVATED | To not install the service worker | |
|
||||
| PUBLISH_AS_MIT | Removes packages whose licences are incompatible with the MIT licence (see below) | true |
|
||||
| Option | Description | default |
|
||||
| ----------------------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
|
||||
| API_ORIGIN | backend domain - it uses the current domain if not initialized | |
|
||||
| SW_DEACTIVATED | To not install the service worker | |
|
||||
| PUBLISH_AS_MIT | Removes packages whose licences are incompatible with the MIT licence (see below) | true |
|
||||
|
||||
Packages with licences incompatible with the MIT licence:
|
||||
|
||||
* `xl-docx-exporter`: [GPL](https://github.com/TypeCellOS/BlockNote/blob/main/packages/xl-docx-exporter/LICENSE),
|
||||
* `xl-pdf-exporter`: [GPL](https://github.com/TypeCellOS/BlockNote/blob/main/packages/xl-pdf-exporter/LICENSE),
|
||||
* `xl-multi-column`: [GPL](https://github.com/TypeCellOS/BlockNote/blob/main/packages/xl-multi-column/LICENSE).
|
||||
* `xl-docx-exporter`: [GPL](https://github.com/TypeCellOS/BlockNote/blob/main/packages/xl-docx-exporter/LICENSE),
|
||||
* `xl-pdf-exporter`: [GPL](https://github.com/TypeCellOS/BlockNote/blob/main/packages/xl-pdf-exporter/LICENSE),
|
||||
* `xl-multi-column`: [GPL](https://github.com/TypeCellOS/BlockNote/blob/main/packages/xl-multi-column/LICENSE).
|
||||
|
||||
In `.env.development`, `PUBLISH_AS_MIT` is set to `false`, allowing developers to test Docs with all its features.
|
||||
|
||||
⚠️ If you run Docs in production with `PUBLISH_AS_MIT` set to `false` make sure you fulfill your BlockNote licensing or [subscription](https://www.blocknotejs.org/about#partner-with-us) obligations.
|
||||
|
||||
|
||||
@@ -138,8 +138,6 @@ AI is disabled by default. To enable it, the following environment variables mus
|
||||
|
||||
```env
|
||||
AI_FEATURE_ENABLED=true # is false by default
|
||||
AI_FEATURE_BLOCKNOTE_ENABLED=true # is false by default
|
||||
AI_FEATURE_LEGACY_ENABLED=true # is true by default, AI_FEATURE_ENABLED must be set to true to enable it
|
||||
AI_BASE_URL=https://openaiendpoint.com
|
||||
AI_API_KEY=<API key>
|
||||
AI_MODEL=<model used> e.g. llama
|
||||
|
||||
@@ -64,8 +64,6 @@ USER_RECONCILIATION_FORM_URL=http://localhost:3000
|
||||
|
||||
# AI
|
||||
AI_FEATURE_ENABLED=true
|
||||
AI_FEATURE_BLOCKNOTE_ENABLED=true
|
||||
AI_FEATURE_LEGACY_ENABLED=true
|
||||
AI_BASE_URL=https://openaiendpoint.com
|
||||
AI_API_KEY=password
|
||||
AI_MODEL=llama
|
||||
|
||||
@@ -58,8 +58,6 @@ OIDC_REDIRECT_ALLOWED_HOSTS=["https://${DOCS_HOST}"]
|
||||
|
||||
# AI
|
||||
#AI_FEATURE_ENABLED=true # is false by default
|
||||
#AI_FEATURE_BLOCKNOTE_ENABLED=true # is false by default
|
||||
#AI_FEATURE_LEGACY_ENABLED=true # is true by default, AI_FEATURE_ENABLED must be set to true to enable it
|
||||
#AI_BASE_URL=https://openaiendpoint.com
|
||||
#AI_API_KEY=<API key>
|
||||
#AI_MODEL=<model used> e.g. llama
|
||||
|
||||
@@ -225,16 +225,8 @@ class DocumentSerializer(ListDocumentSerializer):
|
||||
fields = super().get_fields()
|
||||
|
||||
request = self.context.get("request")
|
||||
if request:
|
||||
if request.method == "POST":
|
||||
fields["id"].read_only = False
|
||||
if (
|
||||
serializers.BooleanField().to_internal_value(
|
||||
request.query_params.get("without_content", False)
|
||||
)
|
||||
is True
|
||||
):
|
||||
del fields["content"]
|
||||
if request and request.method == "POST":
|
||||
fields["id"].read_only = False
|
||||
|
||||
return fields
|
||||
|
||||
@@ -599,13 +591,10 @@ class LinkDocumentSerializer(serializers.ModelSerializer):
|
||||
class DocumentDuplicationSerializer(serializers.Serializer):
|
||||
"""
|
||||
Serializer for duplicating a document.
|
||||
Allows specifying whether to keep access permissions,
|
||||
and whether to duplicate descendant documents as well
|
||||
(deep copy) or not (shallow copy).
|
||||
Allows specifying whether to keep access permissions.
|
||||
"""
|
||||
|
||||
with_accesses = serializers.BooleanField(default=False)
|
||||
with_descendants = serializers.BooleanField(default=False)
|
||||
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
|
||||
@@ -38,7 +38,6 @@ from csp.decorators import csp_update
|
||||
from lasuite.malware_detection import malware_detection
|
||||
from lasuite.oidc_login.decorators import refresh_oidc_access_token
|
||||
from lasuite.tools.email import get_domain_from_email
|
||||
from pydantic import ValidationError as PydanticValidationError
|
||||
from rest_framework import filters, status, viewsets
|
||||
from rest_framework import response as drf_response
|
||||
from rest_framework.permissions import AllowAny
|
||||
@@ -476,9 +475,6 @@ class DocumentViewSet(
|
||||
Returns: JSON response with the translated text.
|
||||
Throttled by: AIDocumentRateThrottle, AIUserRateThrottle.
|
||||
|
||||
12. **AI Proxy**: Proxy an AI request to an external AI service.
|
||||
Example: POST /api/v1.0/documents/<resource_id>/ai-proxy
|
||||
|
||||
### Ordering: created_at, updated_at, is_favorite, title
|
||||
|
||||
Example:
|
||||
@@ -1212,7 +1208,11 @@ class DocumentViewSet(
|
||||
@transaction.atomic
|
||||
def duplicate(self, request, *args, **kwargs):
|
||||
"""
|
||||
Duplicate a document, alongside its descendants if requested.
|
||||
Duplicate a document and store the links to attached files in the duplicated
|
||||
document to allow cross-access.
|
||||
|
||||
Optionally duplicates accesses if `with_accesses` is set to true
|
||||
in the payload.
|
||||
"""
|
||||
# Get document while checking permissions
|
||||
document_to_duplicate = self.get_object()
|
||||
@@ -1221,43 +1221,8 @@ class DocumentViewSet(
|
||||
data=request.data, partial=True
|
||||
)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
user = request.user
|
||||
|
||||
duplicated_document = self._duplicate_document(
|
||||
document_to_duplicate=document_to_duplicate,
|
||||
serializer=serializer,
|
||||
user=user,
|
||||
)
|
||||
|
||||
return drf_response.Response(
|
||||
{"id": str(duplicated_document.id)}, status=status.HTTP_201_CREATED
|
||||
)
|
||||
|
||||
def _duplicate_document(
|
||||
self,
|
||||
document_to_duplicate,
|
||||
serializer,
|
||||
user,
|
||||
new_parent=None,
|
||||
):
|
||||
"""
|
||||
Duplicate a document and store the links to attached files in the duplicated
|
||||
document to allow cross-access.
|
||||
|
||||
Optionally duplicates accesses if `with_accesses` is set to true
|
||||
in the payload.
|
||||
|
||||
Optionally duplicates sub-documents if `with_descendants` is set to true in
|
||||
the payload. In this case, the whole subtree of the document will be duplicated,
|
||||
and the links to attached files will be stored in all duplicated documents.
|
||||
|
||||
The `with_accesses` option will also be applied to all duplicated documents
|
||||
if `with_descendants` is set to true.
|
||||
"""
|
||||
with_accesses = serializer.validated_data.get("with_accesses", False)
|
||||
with_descendants = serializer.validated_data.get("with_descendants", False)
|
||||
|
||||
user_role = document_to_duplicate.get_role(user)
|
||||
user_role = document_to_duplicate.get_role(request.user)
|
||||
is_owner_or_admin = user_role in models.PRIVILEGED_ROLES
|
||||
|
||||
base64_yjs_content = document_to_duplicate.content
|
||||
@@ -1276,41 +1241,11 @@ class DocumentViewSet(
|
||||
extracted_attachments & set(document_to_duplicate.attachments)
|
||||
)
|
||||
title = capfirst(_("copy of {title}").format(title=document_to_duplicate.title))
|
||||
# If parent_duplicate is provided we must add the duplicated document as a child
|
||||
if new_parent is not None:
|
||||
duplicated_document = new_parent.add_child(
|
||||
title=title,
|
||||
content=base64_yjs_content,
|
||||
attachments=attachments,
|
||||
duplicated_from=document_to_duplicate,
|
||||
creator=user,
|
||||
**link_kwargs,
|
||||
)
|
||||
|
||||
# Handle access duplication for this child
|
||||
if with_accesses and is_owner_or_admin:
|
||||
original_accesses = models.DocumentAccess.objects.filter(
|
||||
document=document_to_duplicate
|
||||
).exclude(user=user)
|
||||
|
||||
accesses_to_create = [
|
||||
models.DocumentAccess(
|
||||
document=duplicated_document,
|
||||
user_id=access.user_id,
|
||||
team=access.team,
|
||||
role=access.role,
|
||||
)
|
||||
for access in original_accesses
|
||||
]
|
||||
|
||||
if accesses_to_create:
|
||||
models.DocumentAccess.objects.bulk_create(accesses_to_create)
|
||||
|
||||
elif not document_to_duplicate.is_root() and choices.RoleChoices.get_priority(
|
||||
if not document_to_duplicate.is_root() and choices.RoleChoices.get_priority(
|
||||
user_role
|
||||
) < choices.RoleChoices.get_priority(models.RoleChoices.EDITOR):
|
||||
duplicated_document = models.Document.add_root(
|
||||
creator=user,
|
||||
creator=self.request.user,
|
||||
title=title,
|
||||
content=base64_yjs_content,
|
||||
attachments=attachments,
|
||||
@@ -1319,63 +1254,55 @@ class DocumentViewSet(
|
||||
)
|
||||
models.DocumentAccess.objects.create(
|
||||
document=duplicated_document,
|
||||
user=user,
|
||||
user=self.request.user,
|
||||
role=models.RoleChoices.OWNER,
|
||||
)
|
||||
else:
|
||||
duplicated_document = document_to_duplicate.add_sibling(
|
||||
"right",
|
||||
title=title,
|
||||
content=base64_yjs_content,
|
||||
attachments=attachments,
|
||||
duplicated_from=document_to_duplicate,
|
||||
creator=user,
|
||||
**link_kwargs,
|
||||
return drf_response.Response(
|
||||
{"id": str(duplicated_document.id)}, status=status.HTTP_201_CREATED
|
||||
)
|
||||
|
||||
# Always add the logged-in user as OWNER for root documents
|
||||
if document_to_duplicate.is_root():
|
||||
accesses_to_create = [
|
||||
duplicated_document = document_to_duplicate.add_sibling(
|
||||
"right",
|
||||
title=title,
|
||||
content=base64_yjs_content,
|
||||
attachments=attachments,
|
||||
duplicated_from=document_to_duplicate,
|
||||
creator=request.user,
|
||||
**link_kwargs,
|
||||
)
|
||||
|
||||
# Always add the logged-in user as OWNER for root documents
|
||||
if document_to_duplicate.is_root():
|
||||
accesses_to_create = [
|
||||
models.DocumentAccess(
|
||||
document=duplicated_document,
|
||||
user=request.user,
|
||||
role=models.RoleChoices.OWNER,
|
||||
)
|
||||
]
|
||||
|
||||
# If accesses should be duplicated, add other users' accesses as per original document
|
||||
if with_accesses and is_owner_or_admin:
|
||||
original_accesses = models.DocumentAccess.objects.filter(
|
||||
document=document_to_duplicate
|
||||
).exclude(user=request.user)
|
||||
|
||||
accesses_to_create.extend(
|
||||
models.DocumentAccess(
|
||||
document=duplicated_document,
|
||||
user=user,
|
||||
role=models.RoleChoices.OWNER,
|
||||
user_id=access.user_id,
|
||||
team=access.team,
|
||||
role=access.role,
|
||||
)
|
||||
]
|
||||
|
||||
# If accesses should be duplicated,
|
||||
# add other users' accesses as per original document
|
||||
if with_accesses and is_owner_or_admin:
|
||||
original_accesses = models.DocumentAccess.objects.filter(
|
||||
document=document_to_duplicate
|
||||
).exclude(user=user)
|
||||
|
||||
accesses_to_create.extend(
|
||||
models.DocumentAccess(
|
||||
document=duplicated_document,
|
||||
user_id=access.user_id,
|
||||
team=access.team,
|
||||
role=access.role,
|
||||
)
|
||||
for access in original_accesses
|
||||
)
|
||||
|
||||
# Bulk create all the duplicated accesses
|
||||
models.DocumentAccess.objects.bulk_create(accesses_to_create)
|
||||
|
||||
if with_descendants:
|
||||
for child in document_to_duplicate.get_children().filter(
|
||||
ancestors_deleted_at__isnull=True
|
||||
):
|
||||
# When duplicating descendants, attach duplicates under the duplicated_document
|
||||
self._duplicate_document(
|
||||
document_to_duplicate=child,
|
||||
serializer=serializer,
|
||||
user=user,
|
||||
new_parent=duplicated_document,
|
||||
for access in original_accesses
|
||||
)
|
||||
|
||||
return duplicated_document
|
||||
# Bulk create all the duplicated accesses
|
||||
models.DocumentAccess.objects.bulk_create(accesses_to_create)
|
||||
|
||||
return drf_response.Response(
|
||||
{"id": str(duplicated_document.id)}, status=status.HTTP_201_CREATED
|
||||
)
|
||||
|
||||
def _search_simple(self, request, text):
|
||||
"""
|
||||
@@ -1836,45 +1763,6 @@ class DocumentViewSet(
|
||||
|
||||
return drf.response.Response(body, status=drf.status.HTTP_200_OK)
|
||||
|
||||
@drf.decorators.action(
|
||||
detail=True,
|
||||
methods=["post"],
|
||||
name="Proxy AI requests to the AI provider",
|
||||
url_path="ai-proxy",
|
||||
throttle_classes=[utils.AIDocumentRateThrottle, utils.AIUserRateThrottle],
|
||||
)
|
||||
def ai_proxy(self, request, *args, **kwargs):
|
||||
"""
|
||||
POST /api/v1.0/documents/<resource_id>/ai-proxy
|
||||
Proxy AI requests to the configured AI provider.
|
||||
This endpoint forwards requests to the AI provider and returns the complete response.
|
||||
"""
|
||||
# Check permissions first
|
||||
self.get_object()
|
||||
|
||||
if not settings.AI_FEATURE_ENABLED or not settings.AI_FEATURE_BLOCKNOTE_ENABLED:
|
||||
raise ValidationError("AI feature is not enabled.")
|
||||
|
||||
ai_service = AIService()
|
||||
|
||||
try:
|
||||
stream = ai_service.stream(request)
|
||||
except PydanticValidationError as err:
|
||||
logger.info("pydantic validation error: %s", err)
|
||||
return drf.response.Response(
|
||||
{"detail": "Invalid submitted payload"},
|
||||
status=drf.status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
return StreamingHttpResponse(
|
||||
stream,
|
||||
content_type="text/event-stream",
|
||||
headers={
|
||||
"x-vercel-ai-data-stream": "v1", # This header is used for Vercel AI streaming,
|
||||
"X-Accel-Buffering": "no", # Prevent nginx buffering
|
||||
},
|
||||
)
|
||||
|
||||
@drf.decorators.action(
|
||||
detail=True,
|
||||
methods=["post"],
|
||||
@@ -2570,10 +2458,7 @@ class ConfigView(drf.views.APIView):
|
||||
Return a dictionary of public settings.
|
||||
"""
|
||||
array_settings = [
|
||||
"AI_BOT",
|
||||
"AI_FEATURE_ENABLED",
|
||||
"AI_FEATURE_BLOCKNOTE_ENABLED",
|
||||
"AI_FEATURE_LEGACY_ENABLED",
|
||||
"API_USERS_SEARCH_QUERY_MIN_LENGTH",
|
||||
"COLLABORATION_WS_URL",
|
||||
"COLLABORATION_WS_NOT_CONNECTED_READY_ONLY",
|
||||
|
||||
150
src/backend/core/management/commands/clean_document.py
Normal file
@@ -0,0 +1,150 @@
|
||||
"""Clean a document by resetting it (keeping its title) and deleting all descendants."""
|
||||
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.files.storage import default_storage
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.db import transaction
|
||||
from django.db.models import Q
|
||||
|
||||
from botocore.exceptions import ClientError
|
||||
|
||||
from core.choices import LinkReachChoices, LinkRoleChoices, RoleChoices
|
||||
from core.models import Document, DocumentAccess, Invitation, Thread
|
||||
|
||||
logger = logging.getLogger("impress.commands.clean_document")
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Reset a document (keeping its title) and delete all its descendants."""
|
||||
|
||||
help = __doc__
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""Define command arguments."""
|
||||
parser.add_argument(
|
||||
"document_id",
|
||||
type=str,
|
||||
help="UUID of the document to clean",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--force",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Force command execution despite DEBUG is set to False",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--title",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Update the document title to this value",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--link_reach",
|
||||
type=str,
|
||||
default=LinkReachChoices.RESTRICTED,
|
||||
choices=LinkReachChoices,
|
||||
help="Update the link_reach to this value",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--link_role",
|
||||
type=str,
|
||||
default=LinkRoleChoices.READER,
|
||||
choices=LinkRoleChoices,
|
||||
help="update the link_role to this value",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""Execute the clean_document command."""
|
||||
if not settings.DEBUG and not options["force"]:
|
||||
raise CommandError(
|
||||
"This command is not meant to be used in production environment "
|
||||
"except you know what you are doing, if so use --force parameter"
|
||||
)
|
||||
|
||||
document_id = options["document_id"]
|
||||
|
||||
try:
|
||||
document = Document.objects.get(pk=document_id)
|
||||
except (Document.DoesNotExist, ValueError) as err:
|
||||
raise CommandError(f"Document {document_id} does not exist.") from err
|
||||
|
||||
descendants = list(document.get_descendants())
|
||||
descendant_ids = [doc.id for doc in descendants]
|
||||
all_documents = [document, *descendants]
|
||||
|
||||
# Collect all attachment keys before the transaction clears them
|
||||
all_attachment_keys = []
|
||||
for doc in all_documents:
|
||||
all_attachment_keys.extend(doc.attachments)
|
||||
|
||||
self.stdout.write(
|
||||
f"Cleaning document {document_id} and deleting "
|
||||
f"{len(descendants)} descendant(s)..."
|
||||
)
|
||||
|
||||
with transaction.atomic():
|
||||
# Clean accesses and invitations on the root document
|
||||
access_count, _ = DocumentAccess.objects.filter(
|
||||
Q(document_id=document.id) & ~Q(role=RoleChoices.OWNER)
|
||||
).delete()
|
||||
self.stdout.write(f"Deleted {access_count} access(es) on root document.")
|
||||
|
||||
invitation_count, _ = Invitation.objects.filter(
|
||||
document_id=document.id
|
||||
).delete()
|
||||
self.stdout.write(
|
||||
f"Deleted {invitation_count} invitation(s) on root document."
|
||||
)
|
||||
|
||||
thread_count, _ = Thread.objects.filter(document_id=document.id).delete()
|
||||
self.stdout.write(f"Deleted {thread_count} thread(s) on root document.")
|
||||
|
||||
# Reset root document fields
|
||||
update_fields = {
|
||||
"excerpt": None,
|
||||
"link_reach": options["link_reach"],
|
||||
"link_role": options["link_role"],
|
||||
"attachments": [],
|
||||
}
|
||||
if options["title"] is not None:
|
||||
update_fields["title"] = options["title"]
|
||||
Document.objects.filter(id=document.id).update(**update_fields)
|
||||
|
||||
if options["title"] is not None:
|
||||
self.stdout.write(
|
||||
f'Reset fields on root document (title set to "{options["title"]}").'
|
||||
)
|
||||
else:
|
||||
self.stdout.write("Reset fields on root document (title kept).")
|
||||
|
||||
# Delete all descendants (cascades accesses and invitations)
|
||||
if descendants:
|
||||
deleted_count, _ = Document.objects.filter(
|
||||
id__in=descendant_ids
|
||||
).delete()
|
||||
self.stdout.write(f"Deleted {deleted_count} descendant(s).")
|
||||
|
||||
# Delete S3 content outside the transaction (S3 is not transactional)
|
||||
s3_client = default_storage.connection.meta.client
|
||||
bucket = default_storage.bucket_name
|
||||
|
||||
for doc in all_documents:
|
||||
try:
|
||||
s3_client.delete_object(Bucket=bucket, Key=doc.file_key)
|
||||
except ClientError:
|
||||
logger.warning("Failed to delete S3 file for document %s", doc.id)
|
||||
|
||||
self.stdout.write(f"Deleted S3 content for {len(all_documents)} document(s).")
|
||||
|
||||
for key in all_attachment_keys:
|
||||
try:
|
||||
s3_client.delete_object(Bucket=bucket, Key=key)
|
||||
except ClientError:
|
||||
logger.warning("Failed to delete S3 attachment %s", key)
|
||||
|
||||
self.stdout.write(f"Deleted {len(all_attachment_keys)} attachment(s) from S3.")
|
||||
self.stdout.write("Done.")
|
||||
@@ -19,21 +19,3 @@ class ForceSessionMiddleware:
|
||||
|
||||
response = self.get_response(request)
|
||||
return response
|
||||
|
||||
|
||||
class SaveRawBodyMiddleware:
|
||||
"""
|
||||
Save the raw request body to use it later.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
"""Initialize the middleware."""
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
"""Save the raw request body in the request to use it later."""
|
||||
if request.path.endswith(("/ai-proxy/", "/ai-proxy")):
|
||||
request.raw_body = request.body
|
||||
|
||||
response = self.get_response(request)
|
||||
return response
|
||||
|
||||
@@ -118,11 +118,11 @@ class UserManager(auth_models.UserManager):
|
||||
|
||||
if settings.OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION:
|
||||
try:
|
||||
return self.get(email__iexact=email)
|
||||
return self.get(email=email)
|
||||
except self.model.DoesNotExist:
|
||||
pass
|
||||
elif (
|
||||
self.filter(email__iexact=email).exists()
|
||||
self.filter(email=email).exists()
|
||||
and not settings.OIDC_ALLOW_DUPLICATE_EMAILS
|
||||
):
|
||||
raise DuplicateEmailError(
|
||||
@@ -209,77 +209,14 @@ class User(AbstractBaseUser, BaseModel, auth_models.PermissionsMixin):
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""
|
||||
If it's a new user, give its user access to the documents they were invited to.
|
||||
If it's a new user, give its user access to the documents to which s.he was invited.
|
||||
"""
|
||||
is_adding = self._state.adding
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
if is_adding:
|
||||
self._handle_onboarding_documents_access()
|
||||
self._duplicate_onboarding_sandbox_document()
|
||||
self._convert_valid_invitations()
|
||||
|
||||
def _handle_onboarding_documents_access(self):
|
||||
"""
|
||||
If the user is new and there are documents configured to be given to new users,
|
||||
give access to these documents and pin them as favorites for the user.
|
||||
"""
|
||||
if settings.USER_ONBOARDING_DOCUMENTS:
|
||||
onboarding_document_ids = set(settings.USER_ONBOARDING_DOCUMENTS)
|
||||
onboarding_accesses = []
|
||||
favorite_documents = []
|
||||
for document_id in onboarding_document_ids:
|
||||
try:
|
||||
document = Document.objects.get(id=document_id)
|
||||
except Document.DoesNotExist:
|
||||
logger.warning(
|
||||
"Onboarding document with id %s does not exist. Skipping.",
|
||||
document_id,
|
||||
)
|
||||
continue
|
||||
|
||||
onboarding_accesses.append(
|
||||
DocumentAccess(
|
||||
user=self, document=document, role=RoleChoices.READER
|
||||
)
|
||||
)
|
||||
favorite_documents.append(
|
||||
DocumentFavorite(user=self, document_id=document_id)
|
||||
)
|
||||
|
||||
DocumentAccess.objects.bulk_create(onboarding_accesses)
|
||||
DocumentFavorite.objects.bulk_create(favorite_documents)
|
||||
|
||||
def _duplicate_onboarding_sandbox_document(self):
|
||||
"""
|
||||
If the user is new and there is a sandbox document configured,
|
||||
duplicate the sandbox document for the user
|
||||
"""
|
||||
if settings.USER_ONBOARDING_SANDBOX_DOCUMENT:
|
||||
sandbox_id = settings.USER_ONBOARDING_SANDBOX_DOCUMENT
|
||||
try:
|
||||
template_document = Document.objects.get(id=sandbox_id)
|
||||
|
||||
except Document.DoesNotExist:
|
||||
logger.warning(
|
||||
"Onboarding sandbox document with id %s does not exist. Skipping.",
|
||||
sandbox_id,
|
||||
)
|
||||
return
|
||||
|
||||
sandbox_document = template_document.add_sibling(
|
||||
"right",
|
||||
title=template_document.title,
|
||||
content=template_document.content,
|
||||
attachments=template_document.attachments,
|
||||
duplicated_from=template_document,
|
||||
creator=self,
|
||||
)
|
||||
|
||||
DocumentAccess.objects.create(
|
||||
user=self, document=sandbox_document, role=RoleChoices.OWNER
|
||||
)
|
||||
|
||||
def _convert_valid_invitations(self):
|
||||
"""
|
||||
Convert valid invitations to document accesses.
|
||||
@@ -1282,7 +1219,6 @@ class Document(MP_Node, BaseModel):
|
||||
return {
|
||||
"accesses_manage": is_owner_or_admin,
|
||||
"accesses_view": has_access_role,
|
||||
"ai_proxy": ai_access,
|
||||
"ai_transform": ai_access,
|
||||
"ai_translate": ai_access,
|
||||
"attachment_upload": can_update,
|
||||
@@ -1324,7 +1260,7 @@ class Document(MP_Node, BaseModel):
|
||||
"brandname": settings.EMAIL_BRAND_NAME,
|
||||
"document": self,
|
||||
"domain": domain,
|
||||
"link": f"{domain}/docs/{self.id}/?utm_source=docssharelink&utm_campaign={self.id}",
|
||||
"link": f"{domain}/docs/{self.id}/",
|
||||
"link_label": self.title or str(_("Untitled Document")),
|
||||
"button_label": _("Open"),
|
||||
"logo_img": settings.EMAIL_LOGO_IMG,
|
||||
@@ -1969,7 +1905,7 @@ class Invitation(BaseModel):
|
||||
|
||||
# Check if an identity already exists for the provided email
|
||||
if (
|
||||
User.objects.filter(email__iexact=self.email).exists()
|
||||
User.objects.filter(email=self.email).exists()
|
||||
and not settings.OIDC_ALLOW_DUPLICATE_EMAILS
|
||||
):
|
||||
raise ValidationError(
|
||||
|
||||
@@ -1,68 +1,15 @@
|
||||
"""AI services."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import queue
|
||||
import threading
|
||||
from collections.abc import AsyncIterator, Iterator
|
||||
from typing import Any, Dict, Union
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
from langfuse import get_client
|
||||
from langfuse.openai import OpenAI as OpenAI_Langfuse
|
||||
from pydantic_ai import Agent, DeferredToolRequests
|
||||
from pydantic_ai.models.openai import OpenAIChatModel
|
||||
from pydantic_ai.providers.openai import OpenAIProvider
|
||||
from pydantic_ai.tools import ToolDefinition
|
||||
from pydantic_ai.toolsets.external import ExternalToolset
|
||||
from pydantic_ai.ui import SSE_CONTENT_TYPE
|
||||
from pydantic_ai.ui.vercel_ai import VercelAIAdapter
|
||||
from pydantic_ai.ui.vercel_ai.request_types import RequestData, TextUIPart, UIMessage
|
||||
from rest_framework.request import Request
|
||||
|
||||
from core import enums
|
||||
|
||||
if settings.LANGFUSE_PUBLIC_KEY:
|
||||
OpenAI = OpenAI_Langfuse
|
||||
from langfuse.openai import OpenAI
|
||||
else:
|
||||
from openai import OpenAI
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
BLOCKNOTE_TOOL_STRICT_PROMPT = """
|
||||
You are editing a BlockNote document via the tool applyDocumentOperations.
|
||||
|
||||
You MUST respond ONLY by calling applyDocumentOperations.
|
||||
The tool input MUST be valid JSON:
|
||||
{ "operations": [ ... ] }
|
||||
|
||||
Each operation MUST include "type" and it MUST be one of:
|
||||
- "update" (requires: id, block)
|
||||
- "add" (requires: referenceId, position, blocks)
|
||||
- "delete" (requires: id)
|
||||
|
||||
VALID SHAPES (FOLLOW EXACTLY):
|
||||
|
||||
Update:
|
||||
{ "type":"update", "id":"<id$>", "block":"<p>...</p>" }
|
||||
IMPORTANT: "block" MUST be a STRING containing a SINGLE valid HTML element.
|
||||
|
||||
Add:
|
||||
{ "type":"add", "referenceId":"<id$>", "position":"before|after", "blocks":["<p>...</p>"] }
|
||||
IMPORTANT: "blocks" MUST be an ARRAY OF STRINGS.
|
||||
Each item MUST be a STRING containing a SINGLE valid HTML element.
|
||||
|
||||
Delete:
|
||||
{ "type":"delete", "id":"<id$>" }
|
||||
|
||||
IDs ALWAYS end with "$". Use ids EXACTLY as provided.
|
||||
|
||||
Return ONLY the JSON tool input. No prose, no markdown.
|
||||
"""
|
||||
|
||||
AI_ACTIONS = {
|
||||
"prompt": (
|
||||
@@ -109,40 +56,6 @@ AI_TRANSLATE = (
|
||||
)
|
||||
|
||||
|
||||
def convert_async_generator_to_sync(async_gen: AsyncIterator[str]) -> Iterator[str]:
|
||||
"""Convert an async generator to a sync generator."""
|
||||
q: queue.Queue[str | object] = queue.Queue()
|
||||
sentinel = object()
|
||||
exc_sentinel = object()
|
||||
|
||||
async def run_async_gen():
|
||||
try:
|
||||
async for async_item in async_gen:
|
||||
q.put(async_item)
|
||||
except Exception as exc: # pylint: disable=broad-except #noqa: BLE001
|
||||
q.put((exc_sentinel, exc))
|
||||
finally:
|
||||
q.put(sentinel)
|
||||
|
||||
def start_async_loop():
|
||||
asyncio.run(run_async_gen())
|
||||
|
||||
thread = threading.Thread(target=start_async_loop, daemon=True)
|
||||
thread.start()
|
||||
|
||||
try:
|
||||
while True:
|
||||
item = q.get()
|
||||
if item is sentinel:
|
||||
break
|
||||
if isinstance(item, tuple) and item[0] is exc_sentinel:
|
||||
# re-raise the exception in the sync context
|
||||
raise item[1]
|
||||
yield item
|
||||
finally:
|
||||
thread.join()
|
||||
|
||||
|
||||
class AIService:
|
||||
"""Service class for AI-related operations."""
|
||||
|
||||
@@ -183,198 +96,3 @@ class AIService:
|
||||
language_display = enums.ALL_LANGUAGES.get(language, language)
|
||||
system_content = AI_TRANSLATE.format(language=language_display)
|
||||
return self.call_ai_api(system_content, text)
|
||||
|
||||
@staticmethod
|
||||
def inject_document_state_messages(
|
||||
messages: list[UIMessage],
|
||||
) -> list[UIMessage]:
|
||||
"""Inject document state context before user messages.
|
||||
|
||||
Port of BlockNote's injectDocumentStateMessages.
|
||||
For each user message carrying documentState metadata, an assistant
|
||||
message describing the current document/selection state is prepended
|
||||
so the LLM sees it as context.
|
||||
"""
|
||||
result: list[UIMessage] = []
|
||||
for message in messages:
|
||||
if (
|
||||
message.role == "user"
|
||||
and isinstance(message.metadata, dict)
|
||||
and "documentState" in message.metadata
|
||||
):
|
||||
doc_state = message.metadata["documentState"]
|
||||
selection = doc_state.get("selection")
|
||||
blocks = doc_state.get("blocks")
|
||||
|
||||
if selection:
|
||||
parts = [
|
||||
TextUIPart(
|
||||
text=(
|
||||
"This is the latest state of the selection "
|
||||
"(ignore previous selections, you MUST issue "
|
||||
"operations against this latest version of "
|
||||
"the selection):"
|
||||
),
|
||||
),
|
||||
TextUIPart(
|
||||
text=json.dumps(doc_state.get("selectedBlocks")),
|
||||
),
|
||||
TextUIPart(
|
||||
text=(
|
||||
"This is the latest state of the entire "
|
||||
"document (INCLUDING the selected text), you "
|
||||
"can use this to find the selected text to "
|
||||
"understand the context (but you MUST NOT "
|
||||
"issue operations against this document, you "
|
||||
"MUST issue operations against the selection):"
|
||||
),
|
||||
),
|
||||
TextUIPart(text=json.dumps(blocks)),
|
||||
]
|
||||
else:
|
||||
text = (
|
||||
"There is no active selection. This is the latest "
|
||||
"state of the document (ignore previous documents, "
|
||||
"you MUST issue operations against this latest "
|
||||
"version of the document). The cursor is BETWEEN "
|
||||
"two blocks as indicated by cursor: true."
|
||||
)
|
||||
if doc_state.get("isEmptyDocument"):
|
||||
text += (
|
||||
"Because the document is empty, YOU MUST first "
|
||||
"update the empty block before adding new blocks."
|
||||
)
|
||||
else:
|
||||
text += (
|
||||
"Prefer updating existing blocks over removing "
|
||||
"and adding (but this also depends on the "
|
||||
"user's question)."
|
||||
)
|
||||
parts = [
|
||||
TextUIPart(text=text),
|
||||
TextUIPart(text=json.dumps(blocks)),
|
||||
]
|
||||
|
||||
result.append(
|
||||
UIMessage(
|
||||
role="assistant",
|
||||
id=f"assistant-document-state-{message.id}",
|
||||
parts=parts,
|
||||
)
|
||||
)
|
||||
|
||||
result.append(message)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def tool_definitions_to_toolset(
|
||||
tool_definitions: Dict[str, Any],
|
||||
) -> ExternalToolset:
|
||||
"""Convert serialized tool definitions to a pydantic-ai ExternalToolset.
|
||||
|
||||
Port of BlockNote's toolDefinitionsToToolSet.
|
||||
Builds ToolDefinition objects from the JSON-Schema-based definitions
|
||||
sent by the frontend and wraps them in an ExternalToolset so that
|
||||
pydantic-ai advertises them to the LLM without trying to execute them
|
||||
server-side (execution is deferred to the frontend).
|
||||
"""
|
||||
tool_defs = [
|
||||
ToolDefinition(
|
||||
name=name,
|
||||
description=defn.get("description", ""),
|
||||
parameters_json_schema=defn.get("inputSchema", {}),
|
||||
kind="external",
|
||||
metadata={
|
||||
"output_schema": defn.get("outputSchema"),
|
||||
},
|
||||
)
|
||||
for name, defn in tool_definitions.items()
|
||||
]
|
||||
return ExternalToolset(tool_defs)
|
||||
|
||||
def _harden_messages(
|
||||
self, run_input: RequestData, tool_definitions: Dict[str, Any]
|
||||
):
|
||||
"""
|
||||
Harden messages if applyDocumentOperations tool is used.
|
||||
We would like the system_prompt property in the Agent initialization
|
||||
but for UI adapter, like vercel, the agent is ignoring it
|
||||
see https://github.com/pydantic/pydantic-ai/issues/3315
|
||||
|
||||
We have to inject it in the run_input.messages if needed.
|
||||
"""
|
||||
for name, _defn in tool_definitions.items():
|
||||
if name == "applyDocumentOperations":
|
||||
run_input.messages.insert(
|
||||
0,
|
||||
UIMessage(
|
||||
id="system-force-tool-usage",
|
||||
role="system",
|
||||
parts=[TextUIPart(text=BLOCKNOTE_TOOL_STRICT_PROMPT)],
|
||||
),
|
||||
)
|
||||
return
|
||||
|
||||
def _build_async_stream(self, request: Request) -> AsyncIterator[str]:
|
||||
"""Build the async stream from the AI provider."""
|
||||
instrument_enabled = settings.LANGFUSE_PUBLIC_KEY is not None
|
||||
|
||||
if instrument_enabled:
|
||||
langfuse = get_client()
|
||||
langfuse.auth_check()
|
||||
Agent.instrument_all()
|
||||
|
||||
model = OpenAIChatModel(
|
||||
settings.AI_MODEL,
|
||||
provider=OpenAIProvider(
|
||||
base_url=settings.AI_BASE_URL, api_key=settings.AI_API_KEY
|
||||
),
|
||||
)
|
||||
agent = Agent(model, instrument=instrument_enabled)
|
||||
|
||||
accept = request.META.get("HTTP_ACCEPT", SSE_CONTENT_TYPE)
|
||||
|
||||
run_input = VercelAIAdapter.build_run_input(request.raw_body)
|
||||
|
||||
# Inject document state context into the conversation
|
||||
run_input.messages = self.inject_document_state_messages(run_input.messages)
|
||||
|
||||
# Build an ExternalToolset from frontend-supplied tool definitions
|
||||
raw_tool_defs = (
|
||||
run_input.model_extra.get("toolDefinitions")
|
||||
if run_input.model_extra
|
||||
else None
|
||||
)
|
||||
toolset = (
|
||||
self.tool_definitions_to_toolset(raw_tool_defs) if raw_tool_defs else None
|
||||
)
|
||||
|
||||
if raw_tool_defs:
|
||||
self._harden_messages(run_input, raw_tool_defs)
|
||||
|
||||
adapter = VercelAIAdapter(
|
||||
agent=agent,
|
||||
run_input=run_input,
|
||||
accept=accept,
|
||||
sdk_version=settings.AI_VERCEL_SDK_VERSION,
|
||||
)
|
||||
|
||||
event_stream = adapter.run_stream(
|
||||
output_type=[str, DeferredToolRequests] if toolset else None,
|
||||
toolsets=[toolset] if toolset else None,
|
||||
)
|
||||
|
||||
return adapter.encode_stream(event_stream)
|
||||
|
||||
def stream(self, request: Request) -> Union[AsyncIterator[str], Iterator[str]]:
|
||||
"""Stream AI API requests to the configured AI provider.
|
||||
|
||||
Returns an async iterator when running in async mode (ASGI)
|
||||
or a sync iterator when running in sync mode (WSGI).
|
||||
"""
|
||||
async_stream = self._build_async_stream(request)
|
||||
|
||||
if os.environ.get("PYTHON_SERVER_MODE", "sync") == "async":
|
||||
return async_stream
|
||||
|
||||
return convert_async_generator_to_sync(async_stream)
|
||||
|
||||
@@ -68,30 +68,6 @@ def test_authentication_getter_existing_user_via_email(
|
||||
assert user == db_user
|
||||
|
||||
|
||||
def test_authentication_getter_existing_user_via_email_case_insensitive(
|
||||
django_assert_num_queries, monkeypatch
|
||||
):
|
||||
"""
|
||||
If an existing user doesn't match the sub but matches the email with different case,
|
||||
the user should be returned (case-insensitive email matching).
|
||||
"""
|
||||
|
||||
klass = OIDCAuthenticationBackend()
|
||||
db_user = UserFactory(email="john.doe@example.com")
|
||||
|
||||
def get_userinfo_mocked(*args):
|
||||
return {"sub": "123", "email": "JOHN.DOE@EXAMPLE.COM"}
|
||||
|
||||
monkeypatch.setattr(OIDCAuthenticationBackend, "get_userinfo", get_userinfo_mocked)
|
||||
|
||||
with django_assert_num_queries(4): # user by sub, user by mail, update sub
|
||||
user = klass.get_or_create_user(
|
||||
access_token="test-token", id_token=None, payload=None
|
||||
)
|
||||
|
||||
assert user == db_user
|
||||
|
||||
|
||||
def test_authentication_getter_email_none(monkeypatch):
|
||||
"""
|
||||
If no user is found with the sub and no email is provided, a new user should be created.
|
||||
@@ -181,39 +157,6 @@ def test_authentication_getter_existing_user_no_fallback_to_email_no_duplicate(
|
||||
assert models.User.objects.count() == 1
|
||||
|
||||
|
||||
def test_authentication_getter_existing_user_no_fallback_to_email_no_duplicate_case_insensitive(
|
||||
settings, monkeypatch
|
||||
):
|
||||
"""
|
||||
When the "OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION" setting is set to False,
|
||||
the system should detect duplicate emails even with different case.
|
||||
"""
|
||||
|
||||
klass = OIDCAuthenticationBackend()
|
||||
_db_user = UserFactory(email="john.doe@example.com")
|
||||
|
||||
# Set the setting to False
|
||||
settings.OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION = False
|
||||
settings.OIDC_ALLOW_DUPLICATE_EMAILS = False
|
||||
|
||||
def get_userinfo_mocked(*args):
|
||||
return {"sub": "123", "email": "JOHN.DOE@EXAMPLE.COM"}
|
||||
|
||||
monkeypatch.setattr(OIDCAuthenticationBackend, "get_userinfo", get_userinfo_mocked)
|
||||
|
||||
with pytest.raises(
|
||||
SuspiciousOperation,
|
||||
match=(
|
||||
"We couldn't find a user with this sub but the email is already associated "
|
||||
"with a registered user."
|
||||
),
|
||||
):
|
||||
klass.get_or_create_user(access_token="test-token", id_token=None, payload=None)
|
||||
|
||||
# Since the sub doesn't match, it should not create a new user
|
||||
assert models.User.objects.count() == 1
|
||||
|
||||
|
||||
def test_authentication_getter_existing_user_with_email(
|
||||
django_assert_num_queries, monkeypatch
|
||||
):
|
||||
|
||||
313
src/backend/core/tests/commands/test_clean_document.py
Normal file
@@ -0,0 +1,313 @@
|
||||
"""Unit tests for the `clean_document` management command."""
|
||||
|
||||
import random
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from django.core.management import CommandError, call_command
|
||||
|
||||
import pytest
|
||||
from botocore.exceptions import ClientError
|
||||
|
||||
from core import choices, factories, models
|
||||
from core.choices import LinkReachChoices, LinkRoleChoices
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_clean_document_with_descendants(settings):
|
||||
"""The command should reset the root (keeping title) and delete descendants."""
|
||||
settings.DEBUG = True
|
||||
|
||||
# Create a root document with subdocuments
|
||||
root = factories.DocumentFactory(
|
||||
title="Root",
|
||||
link_reach=LinkReachChoices.PUBLIC,
|
||||
link_role=LinkRoleChoices.EDITOR,
|
||||
)
|
||||
child = factories.DocumentFactory(
|
||||
parent=root,
|
||||
title="Child",
|
||||
link_reach=LinkReachChoices.AUTHENTICATED,
|
||||
link_role=LinkRoleChoices.EDITOR,
|
||||
)
|
||||
grandchild = factories.DocumentFactory(
|
||||
parent=child,
|
||||
title="Grandchild",
|
||||
)
|
||||
|
||||
# Create accesses and invitations
|
||||
factories.UserDocumentAccessFactory.create_batch(
|
||||
5,
|
||||
document=root,
|
||||
role=random.choice(
|
||||
[
|
||||
role
|
||||
for role in choices.RoleChoices
|
||||
if role not in choices.PRIVILEGED_ROLES
|
||||
],
|
||||
),
|
||||
)
|
||||
# One owner role
|
||||
factories.UserDocumentAccessFactory(document=root, role=choices.RoleChoices.OWNER)
|
||||
factories.UserDocumentAccessFactory(document=child)
|
||||
factories.InvitationFactory(document=root)
|
||||
factories.InvitationFactory(document=child)
|
||||
factories.ThreadFactory.create_batch(5, document=root)
|
||||
|
||||
assert models.Invitation.objects.filter(document=root).exists()
|
||||
assert models.Thread.objects.filter(document=root).exists()
|
||||
assert models.DocumentAccess.objects.filter(document=root).exists()
|
||||
|
||||
with mock.patch(
|
||||
"core.management.commands.clean_document.default_storage"
|
||||
) as mock_storage:
|
||||
call_command("clean_document", str(root.id), "--force")
|
||||
|
||||
# Root document should still exist with title kept and other fields reset
|
||||
root.refresh_from_db()
|
||||
assert root.title == "Root"
|
||||
assert root.excerpt is None
|
||||
assert root.link_reach == LinkReachChoices.RESTRICTED
|
||||
assert root.link_role == LinkRoleChoices.READER
|
||||
assert root.attachments == []
|
||||
|
||||
# Accesses and invitations on root should be deleted. Only owner should be kept
|
||||
keeping_accesses = list(models.DocumentAccess.objects.filter(document=root))
|
||||
assert len(keeping_accesses) == 1
|
||||
assert keeping_accesses[0].role == models.RoleChoices.OWNER
|
||||
assert not models.Invitation.objects.filter(document=root).exists()
|
||||
assert not models.Thread.objects.filter(document=root).exists()
|
||||
|
||||
# Descendants should be deleted entirely
|
||||
assert not models.Document.objects.filter(id__in=[child.id, grandchild.id]).exists()
|
||||
|
||||
# Root should have no descendants
|
||||
root.refresh_from_db()
|
||||
assert root.get_descendants().count() == 0
|
||||
|
||||
# S3 delete should have been called for document files + attachments
|
||||
delete_calls = mock_storage.connection.meta.client.delete_object.call_args_list
|
||||
assert len(delete_calls) == 3
|
||||
|
||||
|
||||
def test_clean_document_invalid_uuid(settings):
|
||||
"""The command should raise an error for a non-existent document."""
|
||||
settings.DEBUG = True
|
||||
|
||||
fake_id = str(uuid4())
|
||||
with pytest.raises(CommandError, match=f"Document {fake_id} does not exist."):
|
||||
call_command("clean_document", fake_id, "--force")
|
||||
|
||||
|
||||
def test_clean_document_no_force_in_production(settings):
|
||||
"""The command should require --force when DEBUG is False."""
|
||||
settings.DEBUG = False
|
||||
|
||||
doc = factories.DocumentFactory()
|
||||
with pytest.raises(CommandError, match="not meant to be used in production"):
|
||||
call_command("clean_document", str(doc.id))
|
||||
|
||||
|
||||
def test_clean_document_single_document(settings):
|
||||
"""The command should work on a single document without children."""
|
||||
settings.DEBUG = True
|
||||
|
||||
doc = factories.DocumentFactory(
|
||||
title="Single",
|
||||
link_reach=LinkReachChoices.PUBLIC,
|
||||
link_role=LinkRoleChoices.EDITOR,
|
||||
)
|
||||
factories.UserDocumentAccessFactory.create_batch(
|
||||
5,
|
||||
document=doc,
|
||||
role=random.choice(
|
||||
[
|
||||
role
|
||||
for role in choices.RoleChoices
|
||||
if role not in choices.PRIVILEGED_ROLES
|
||||
],
|
||||
),
|
||||
)
|
||||
# One owner role
|
||||
factories.UserDocumentAccessFactory(document=doc, role=choices.RoleChoices.OWNER)
|
||||
factories.ThreadFactory.create_batch(5, document=doc)
|
||||
factories.InvitationFactory(document=doc)
|
||||
|
||||
with mock.patch(
|
||||
"core.management.commands.clean_document.default_storage"
|
||||
) as mock_storage:
|
||||
call_command("clean_document", str(doc.id), "--force")
|
||||
|
||||
# Accesses and invitations on root should be deleted. Only owner should be kept
|
||||
keeping_accesses = list(models.DocumentAccess.objects.filter(document=doc))
|
||||
assert len(keeping_accesses) == 1
|
||||
assert keeping_accesses[0].role == models.RoleChoices.OWNER
|
||||
assert not models.Invitation.objects.filter(document=doc).exists()
|
||||
assert not models.Thread.objects.filter(document=doc).exists()
|
||||
|
||||
doc.refresh_from_db()
|
||||
assert doc.title == "Single"
|
||||
assert doc.excerpt is None
|
||||
assert doc.link_reach == LinkReachChoices.RESTRICTED
|
||||
assert doc.link_role == LinkRoleChoices.READER
|
||||
assert doc.attachments == []
|
||||
|
||||
mock_storage.connection.meta.client.delete_object.assert_called_once()
|
||||
|
||||
|
||||
def test_clean_document_with_title_option(settings):
|
||||
"""The --title option should update the document title."""
|
||||
settings.DEBUG = True
|
||||
|
||||
doc = factories.DocumentFactory(
|
||||
title="Old Title",
|
||||
link_reach=LinkReachChoices.PUBLIC,
|
||||
link_role=LinkRoleChoices.EDITOR,
|
||||
)
|
||||
|
||||
with mock.patch("core.management.commands.clean_document.default_storage"):
|
||||
call_command("clean_document", str(doc.id), "--force", "--title", "New Title")
|
||||
|
||||
doc.refresh_from_db()
|
||||
assert doc.title == "New Title"
|
||||
assert doc.excerpt is None
|
||||
assert doc.link_reach == LinkReachChoices.RESTRICTED
|
||||
assert doc.link_role == LinkRoleChoices.READER
|
||||
assert doc.attachments == []
|
||||
|
||||
|
||||
def test_clean_document_deletes_attachments_from_s3(settings):
|
||||
"""The command should delete attachment files from S3."""
|
||||
settings.DEBUG = True
|
||||
|
||||
root = factories.DocumentFactory(
|
||||
attachments=["root-id/attachments/file1.png", "root-id/attachments/file2.pdf"],
|
||||
)
|
||||
child = factories.DocumentFactory(
|
||||
parent=root,
|
||||
attachments=["child-id/attachments/file3.png"],
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"core.management.commands.clean_document.default_storage"
|
||||
) as mock_storage:
|
||||
call_command("clean_document", str(root.id), "--force")
|
||||
|
||||
delete_calls = mock_storage.connection.meta.client.delete_object.call_args_list
|
||||
deleted_keys = [call.kwargs["Key"] for call in delete_calls]
|
||||
|
||||
# Document files (root + child)
|
||||
assert root.file_key in deleted_keys
|
||||
assert child.file_key in deleted_keys
|
||||
|
||||
# Attachment files
|
||||
assert "root-id/attachments/file1.png" in deleted_keys
|
||||
assert "root-id/attachments/file2.pdf" in deleted_keys
|
||||
assert "child-id/attachments/file3.png" in deleted_keys
|
||||
|
||||
assert len(delete_calls) == 5
|
||||
|
||||
|
||||
def test_clean_document_s3_errors_do_not_stop_command(settings):
|
||||
"""S3 deletion errors should be logged but not stop the command."""
|
||||
settings.DEBUG = True
|
||||
|
||||
doc = factories.DocumentFactory(
|
||||
attachments=["doc-id/attachments/file1.png"],
|
||||
)
|
||||
|
||||
with mock.patch(
|
||||
"core.management.commands.clean_document.default_storage"
|
||||
) as mock_storage:
|
||||
mock_storage.connection.meta.client.delete_object.side_effect = ClientError(
|
||||
{"Error": {"Code": "500", "Message": "Internal Error"}},
|
||||
"DeleteObject",
|
||||
)
|
||||
# Command should complete without raising
|
||||
call_command("clean_document", str(doc.id), "--force")
|
||||
|
||||
|
||||
def test_clean_document_with_options(settings):
|
||||
"""Run the command using optional argument link_reach and link_role."""
|
||||
|
||||
settings.DEBUG = True
|
||||
|
||||
# Create a root document with subdocuments
|
||||
root = factories.DocumentFactory(
|
||||
title="Root",
|
||||
link_reach=LinkReachChoices.PUBLIC,
|
||||
link_role=LinkRoleChoices.READER,
|
||||
)
|
||||
child = factories.DocumentFactory(
|
||||
parent=root,
|
||||
title="Child",
|
||||
link_reach=LinkReachChoices.AUTHENTICATED,
|
||||
link_role=LinkRoleChoices.EDITOR,
|
||||
)
|
||||
grandchild = factories.DocumentFactory(
|
||||
parent=child,
|
||||
title="Grandchild",
|
||||
)
|
||||
|
||||
# Create accesses and invitations
|
||||
factories.UserDocumentAccessFactory.create_batch(
|
||||
5,
|
||||
document=root,
|
||||
role=random.choice(
|
||||
[
|
||||
role
|
||||
for role in choices.RoleChoices
|
||||
if role not in choices.PRIVILEGED_ROLES
|
||||
],
|
||||
),
|
||||
)
|
||||
# One owner role
|
||||
factories.UserDocumentAccessFactory(document=root, role=choices.RoleChoices.OWNER)
|
||||
factories.UserDocumentAccessFactory(document=child)
|
||||
factories.InvitationFactory(document=root)
|
||||
factories.InvitationFactory(document=child)
|
||||
factories.ThreadFactory.create_batch(5, document=root)
|
||||
|
||||
assert models.Invitation.objects.filter(document=root).exists()
|
||||
assert models.Thread.objects.filter(document=root).exists()
|
||||
assert models.DocumentAccess.objects.filter(document=root).exists()
|
||||
|
||||
with mock.patch(
|
||||
"core.management.commands.clean_document.default_storage"
|
||||
) as mock_storage:
|
||||
call_command(
|
||||
"clean_document",
|
||||
str(root.id),
|
||||
"--force",
|
||||
"--link_reach",
|
||||
"public",
|
||||
"--link_role",
|
||||
"editor",
|
||||
)
|
||||
|
||||
# Root document should still exist with title kept and other fields reset
|
||||
root.refresh_from_db()
|
||||
assert root.title == "Root"
|
||||
assert root.excerpt is None
|
||||
assert root.link_reach == LinkReachChoices.PUBLIC
|
||||
assert root.link_role == LinkRoleChoices.EDITOR
|
||||
assert root.attachments == []
|
||||
|
||||
# Accesses and invitations on root should be deleted. Only owner should be kept
|
||||
keeping_accesses = list(models.DocumentAccess.objects.filter(document=root))
|
||||
assert len(keeping_accesses) == 1
|
||||
assert keeping_accesses[0].role == models.RoleChoices.OWNER
|
||||
assert not models.Invitation.objects.filter(document=root).exists()
|
||||
assert not models.Thread.objects.filter(document=root).exists()
|
||||
|
||||
# Descendants should be deleted entirely
|
||||
assert not models.Document.objects.filter(id__in=[child.id, grandchild.id]).exists()
|
||||
|
||||
# Root should have no descendants
|
||||
root.refresh_from_db()
|
||||
assert root.get_descendants().count() == 0
|
||||
|
||||
# S3 delete should have been called for document files + attachments
|
||||
delete_calls = mock_storage.connection.meta.client.delete_object.call_args_list
|
||||
assert len(delete_calls) == 3
|
||||
@@ -596,38 +596,6 @@ def test_api_document_invitations_create_cannot_invite_existing_users():
|
||||
}
|
||||
|
||||
|
||||
def test_api_item_invitations_create_cannot_invite_existing_users_case_insensitive():
|
||||
"""
|
||||
It should not be possible to invite already existing users, even with different email case.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
document = factories.DocumentFactory(users=[(user, "owner")])
|
||||
existing_user = factories.UserFactory()
|
||||
|
||||
# Build an invitation to the email of an existing identity with different case
|
||||
invitation_values = {
|
||||
"email": existing_user.email.upper(),
|
||||
"role": random.choice(models.RoleChoices.values),
|
||||
}
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{document.id!s}/invitations/",
|
||||
invitation_values,
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert response.json() == {
|
||||
"email": ["This email is already associated to a registered user."]
|
||||
}
|
||||
|
||||
|
||||
def test_api_document_invitations_create_lower_email():
|
||||
"""
|
||||
No matter the case, the email should be converted to lowercase.
|
||||
|
||||
@@ -1,387 +0,0 @@
|
||||
"""
|
||||
Test AI proxy API endpoint for users in impress's core app.
|
||||
"""
|
||||
|
||||
import random
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.test import override_settings
|
||||
|
||||
import pytest
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from core import factories
|
||||
from core.tests.conftest import TEAM, USER, VIA
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def ai_settings(settings):
|
||||
"""Fixture to set AI settings."""
|
||||
settings.AI_MODEL = "llama"
|
||||
settings.AI_BASE_URL = "http://localhost-ai:12345/"
|
||||
settings.AI_API_KEY = "test-key"
|
||||
settings.AI_FEATURE_ENABLED = True
|
||||
settings.AI_FEATURE_BLOCKNOTE_ENABLED = True
|
||||
settings.AI_FEATURE_LEGACY_ENABLED = True
|
||||
settings.LANGFUSE_PUBLIC_KEY = None
|
||||
settings.AI_VERCEL_SDK_VERSION = 6
|
||||
|
||||
|
||||
@override_settings(
|
||||
AI_ALLOW_REACH_FROM=random.choice(["public", "authenticated", "restricted"])
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"reach, role",
|
||||
[
|
||||
("restricted", "reader"),
|
||||
("restricted", "editor"),
|
||||
("authenticated", "reader"),
|
||||
("authenticated", "editor"),
|
||||
("public", "reader"),
|
||||
],
|
||||
)
|
||||
def test_api_documents_ai_proxy_anonymous_forbidden(reach, role):
|
||||
"""
|
||||
Anonymous users should not be able to request AI proxy if the link reach
|
||||
and role don't allow it.
|
||||
"""
|
||||
document = factories.DocumentFactory(link_reach=reach, link_role=role)
|
||||
|
||||
url = f"/api/v1.0/documents/{document.id!s}/ai-proxy/"
|
||||
response = APIClient().post(
|
||||
url,
|
||||
{
|
||||
"messages": [{"role": "user", "content": "Hello"}],
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
assert response.json() == {
|
||||
"detail": "Authentication credentials were not provided."
|
||||
}
|
||||
|
||||
|
||||
@override_settings(AI_ALLOW_REACH_FROM="public")
|
||||
@patch("core.services.ai_services.AIService.stream")
|
||||
def test_api_documents_ai_proxy_anonymous_success(mock_stream):
|
||||
"""
|
||||
Anonymous users should be able to request AI proxy to a document
|
||||
if the link reach and role permit it.
|
||||
"""
|
||||
document = factories.DocumentFactory(link_reach="public", link_role="editor")
|
||||
|
||||
mock_stream.return_value = iter(["data: chunk1\n", "data: chunk2\n"])
|
||||
|
||||
url = f"/api/v1.0/documents/{document.id!s}/ai-proxy/"
|
||||
response = APIClient().post(
|
||||
url,
|
||||
b"{}",
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response["Content-Type"] == "text/event-stream"
|
||||
assert response["x-vercel-ai-data-stream"] == "v1"
|
||||
assert response["X-Accel-Buffering"] == "no"
|
||||
|
||||
content = b"".join(response.streaming_content).decode()
|
||||
assert "chunk1" in content
|
||||
assert "chunk2" in content
|
||||
mock_stream.assert_called_once()
|
||||
|
||||
|
||||
@override_settings(AI_ALLOW_REACH_FROM=random.choice(["authenticated", "restricted"]))
|
||||
def test_api_documents_ai_proxy_anonymous_limited_by_setting():
|
||||
"""
|
||||
Anonymous users should not be able to request AI proxy to a document
|
||||
if AI_ALLOW_REACH_FROM setting restricts it.
|
||||
"""
|
||||
document = factories.DocumentFactory(link_reach="public", link_role="editor")
|
||||
|
||||
url = f"/api/v1.0/documents/{document.id!s}/ai-proxy/"
|
||||
response = APIClient().post(
|
||||
url,
|
||||
b"{}",
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert response.status_code == 401
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"reach, role",
|
||||
[
|
||||
("restricted", "reader"),
|
||||
("restricted", "editor"),
|
||||
("authenticated", "reader"),
|
||||
("public", "reader"),
|
||||
],
|
||||
)
|
||||
def test_api_documents_ai_proxy_authenticated_forbidden(reach, role):
|
||||
"""
|
||||
Users who are not related to a document can't request AI proxy if the
|
||||
link reach and role don't allow it.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory(link_reach=reach, link_role=role)
|
||||
|
||||
url = f"/api/v1.0/documents/{document.id!s}/ai-proxy/"
|
||||
response = client.post(
|
||||
url,
|
||||
b"{}",
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"reach, role",
|
||||
[
|
||||
("authenticated", "editor"),
|
||||
("public", "editor"),
|
||||
],
|
||||
)
|
||||
@patch("core.services.ai_services.AIService.stream")
|
||||
def test_api_documents_ai_proxy_authenticated_success(mock_stream, reach, role):
|
||||
"""
|
||||
Authenticated users should be able to request AI proxy to a document
|
||||
if the link reach and role permit it.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory(link_reach=reach, link_role=role)
|
||||
|
||||
mock_stream.return_value = iter(["data: response\n"])
|
||||
|
||||
url = f"/api/v1.0/documents/{document.id!s}/ai-proxy/"
|
||||
response = client.post(
|
||||
url,
|
||||
b"{}",
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response["Content-Type"] == "text/event-stream"
|
||||
mock_stream.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
def test_api_documents_ai_proxy_reader(via, mock_user_teams):
|
||||
"""Users with reader access should not be able to request AI proxy."""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory(link_reach="restricted")
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(document=document, user=user, role="reader")
|
||||
elif via == TEAM:
|
||||
mock_user_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role="reader"
|
||||
)
|
||||
|
||||
url = f"/api/v1.0/documents/{document.id!s}/ai-proxy/"
|
||||
response = client.post(
|
||||
url,
|
||||
b"{}",
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.parametrize("role", ["editor", "administrator", "owner"])
|
||||
@pytest.mark.parametrize("via", VIA)
|
||||
@patch("core.services.ai_services.AIService.stream")
|
||||
def test_api_documents_ai_proxy_success(mock_stream, via, role, mock_user_teams):
|
||||
"""Users with sufficient permissions should be able to request AI proxy."""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory(link_reach="restricted")
|
||||
if via == USER:
|
||||
factories.UserDocumentAccessFactory(document=document, user=user, role=role)
|
||||
elif via == TEAM:
|
||||
mock_user_teams.return_value = ["lasuite", "unknown"]
|
||||
factories.TeamDocumentAccessFactory(
|
||||
document=document, team="lasuite", role=role
|
||||
)
|
||||
|
||||
mock_stream.return_value = iter(["data: success\n"])
|
||||
|
||||
url = f"/api/v1.0/documents/{document.id!s}/ai-proxy/"
|
||||
response = client.post(
|
||||
url,
|
||||
b"{}",
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response["Content-Type"] == "text/event-stream"
|
||||
assert response["x-vercel-ai-data-stream"] == "v1"
|
||||
assert response["X-Accel-Buffering"] == "no"
|
||||
|
||||
content = b"".join(response.streaming_content).decode()
|
||||
assert "success" in content
|
||||
mock_stream.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"setting_to_disable", ["AI_FEATURE_ENABLED", "AI_FEATURE_BLOCKNOTE_ENABLED"]
|
||||
)
|
||||
def test_api_documents_ai_proxy_ai_feature_disabled(settings, setting_to_disable):
|
||||
"""When AI_FEATURE_ENABLED is False, the endpoint returns 400."""
|
||||
setattr(settings, setting_to_disable, False)
|
||||
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory(link_reach="public", link_role="editor")
|
||||
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{document.id!s}/ai-proxy/",
|
||||
b"{}",
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert response.json() == ["AI feature is not enabled."]
|
||||
|
||||
|
||||
@override_settings(AI_DOCUMENT_RATE_THROTTLE_RATES={"minute": 3, "hour": 6, "day": 10})
|
||||
@patch("core.services.ai_services.AIService.stream")
|
||||
def test_api_documents_ai_proxy_throttling_document(mock_stream):
|
||||
"""
|
||||
Throttling per document should be triggered on the AI proxy endpoint.
|
||||
For full throttle class test see: `test_api_utils_ai_document_rate_throttles`
|
||||
"""
|
||||
client = APIClient()
|
||||
document = factories.DocumentFactory(link_reach="public", link_role="editor")
|
||||
|
||||
mock_stream.return_value = iter(["data: ok\n"])
|
||||
|
||||
url = f"/api/v1.0/documents/{document.id!s}/ai-proxy/"
|
||||
for _ in range(3):
|
||||
mock_stream.return_value = iter(["data: ok\n"])
|
||||
user = factories.UserFactory()
|
||||
client.force_login(user)
|
||||
response = client.post(
|
||||
url,
|
||||
b"{}",
|
||||
content_type="application/json",
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
user = factories.UserFactory()
|
||||
client.force_login(user)
|
||||
response = client.post(
|
||||
url,
|
||||
b"{}",
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert response.status_code == 429
|
||||
assert response.json() == {
|
||||
"detail": "Request was throttled. Expected available in 60 seconds."
|
||||
}
|
||||
|
||||
|
||||
@override_settings(AI_USER_RATE_THROTTLE_RATES={"minute": 3, "hour": 6, "day": 10})
|
||||
@patch("core.services.ai_services.AIService.stream")
|
||||
def test_api_documents_ai_proxy_throttling_user(mock_stream):
|
||||
"""
|
||||
Throttling per user should be triggered on the AI proxy endpoint.
|
||||
For full throttle class test see: `test_api_utils_ai_user_rate_throttles`
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
for _ in range(3):
|
||||
mock_stream.return_value = iter(["data: ok\n"])
|
||||
document = factories.DocumentFactory(link_reach="public", link_role="editor")
|
||||
url = f"/api/v1.0/documents/{document.id!s}/ai-proxy/"
|
||||
response = client.post(
|
||||
url,
|
||||
b"{}",
|
||||
content_type="application/json",
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
document = factories.DocumentFactory(link_reach="public", link_role="editor")
|
||||
url = f"/api/v1.0/documents/{document.id!s}/ai-proxy/"
|
||||
response = client.post(
|
||||
url,
|
||||
b"{}",
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert response.status_code == 429
|
||||
assert response.json() == {
|
||||
"detail": "Request was throttled. Expected available in 60 seconds."
|
||||
}
|
||||
|
||||
|
||||
@patch("core.services.ai_services.AIService.stream")
|
||||
def test_api_documents_ai_proxy_returns_streaming_response(mock_stream):
|
||||
"""AI proxy should return a StreamingHttpResponse with correct headers."""
|
||||
user = factories.UserFactory()
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
document = factories.DocumentFactory(link_reach="public", link_role="editor")
|
||||
|
||||
mock_stream.return_value = iter(["data: part1\n", "data: part2\n", "data: part3\n"])
|
||||
|
||||
url = f"/api/v1.0/documents/{document.id!s}/ai-proxy/"
|
||||
response = client.post(
|
||||
url,
|
||||
b"{}",
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response["Content-Type"] == "text/event-stream"
|
||||
assert response["x-vercel-ai-data-stream"] == "v1"
|
||||
assert response["X-Accel-Buffering"] == "no"
|
||||
|
||||
chunks = list(response.streaming_content)
|
||||
assert len(chunks) == 3
|
||||
|
||||
|
||||
def test_api_documents_ai_proxy_invalid_payload():
|
||||
"""AI Proxy should return a 400 if the payload is invalid."""
|
||||
|
||||
user = factories.UserFactory()
|
||||
|
||||
document = factories.DocumentFactory(users=[(user, "owner")])
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{document.id!s}/ai-proxy/",
|
||||
b'{"foo": "bar", "trigger": "submit-message"}',
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert response.json() == {"detail": "Invalid submitted payload"}
|
||||
@@ -318,424 +318,3 @@ def test_api_documents_duplicate_reader_non_root_document():
|
||||
assert duplicated_document.is_root()
|
||||
assert duplicated_document.accesses.count() == 1
|
||||
assert duplicated_document.accesses.get(user=user).role == "owner"
|
||||
|
||||
|
||||
def test_api_documents_duplicate_with_descendants_simple():
|
||||
"""
|
||||
Duplicating a document with descendants flag should recursively duplicate all children.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
# Create document tree
|
||||
root = factories.DocumentFactory(
|
||||
users=[(user, "owner")],
|
||||
title="Root Document",
|
||||
)
|
||||
child1 = factories.DocumentFactory(
|
||||
parent=root,
|
||||
title="Child 1",
|
||||
)
|
||||
child2 = factories.DocumentFactory(
|
||||
parent=root,
|
||||
title="Child 2",
|
||||
)
|
||||
|
||||
initial_count = models.Document.objects.count()
|
||||
assert initial_count == 3
|
||||
|
||||
# Duplicate with descendants
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{root.id!s}/duplicate/",
|
||||
{"with_descendants": True},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
duplicated_root = models.Document.objects.get(id=response.json()["id"])
|
||||
|
||||
# Check that all documents were duplicated (6 total: 3 original + 3 duplicated)
|
||||
assert models.Document.objects.count() == 6
|
||||
|
||||
# Check root duplication
|
||||
assert duplicated_root.title == "Copy of Root Document"
|
||||
assert duplicated_root.creator == user
|
||||
assert duplicated_root.duplicated_from == root
|
||||
assert duplicated_root.get_children().count() == 2
|
||||
|
||||
# Check children duplication
|
||||
duplicated_children = duplicated_root.get_children().order_by("title")
|
||||
assert duplicated_children.count() == 2
|
||||
|
||||
duplicated_child1 = duplicated_children.first()
|
||||
assert duplicated_child1.title == "Copy of Child 1"
|
||||
assert duplicated_child1.creator == user
|
||||
assert duplicated_child1.duplicated_from == child1
|
||||
assert duplicated_child1.get_parent() == duplicated_root
|
||||
|
||||
duplicated_child2 = duplicated_children.last()
|
||||
assert duplicated_child2.title == "Copy of Child 2"
|
||||
assert duplicated_child2.creator == user
|
||||
assert duplicated_child2.duplicated_from == child2
|
||||
assert duplicated_child2.get_parent() == duplicated_root
|
||||
|
||||
|
||||
def test_api_documents_duplicate_with_descendants_multi_level():
|
||||
"""
|
||||
Duplicating should recursively handle multiple levels of nesting.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
root = factories.DocumentFactory(
|
||||
users=[(user, "owner")],
|
||||
title="Level 0",
|
||||
)
|
||||
child = factories.DocumentFactory(
|
||||
parent=root,
|
||||
title="Level 1",
|
||||
)
|
||||
grandchild = factories.DocumentFactory(
|
||||
parent=child,
|
||||
title="Level 2",
|
||||
)
|
||||
great_grandchild = factories.DocumentFactory(
|
||||
parent=grandchild,
|
||||
title="Level 3",
|
||||
)
|
||||
|
||||
initial_count = models.Document.objects.count()
|
||||
assert initial_count == 4
|
||||
|
||||
# Duplicate with descendants
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{root.id!s}/duplicate/",
|
||||
{"with_descendants": True},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
duplicated_root = models.Document.objects.get(id=response.json()["id"])
|
||||
|
||||
# Check that all documents were duplicated
|
||||
assert models.Document.objects.count() == 8
|
||||
|
||||
# Verify the tree structure
|
||||
assert duplicated_root.depth == root.depth
|
||||
dup_children = duplicated_root.get_children()
|
||||
assert dup_children.count() == 1
|
||||
|
||||
dup_child = dup_children.first()
|
||||
assert dup_child.title == "Copy of Level 1"
|
||||
assert dup_child.duplicated_from == child
|
||||
dup_grandchildren = dup_child.get_children()
|
||||
assert dup_grandchildren.count() == 1
|
||||
|
||||
dup_grandchild = dup_grandchildren.first()
|
||||
assert dup_grandchild.title == "Copy of Level 2"
|
||||
assert dup_grandchild.duplicated_from == grandchild
|
||||
dup_great_grandchildren = dup_grandchild.get_children()
|
||||
assert dup_great_grandchildren.count() == 1
|
||||
|
||||
dup_great_grandchild = dup_great_grandchildren.first()
|
||||
assert dup_great_grandchild.title == "Copy of Level 3"
|
||||
assert dup_great_grandchild.duplicated_from == great_grandchild
|
||||
|
||||
|
||||
def test_api_documents_duplicate_with_descendants_and_attachments():
|
||||
"""
|
||||
Duplicating with descendants should properly handle attachments in all children.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
# Create documents with attachments
|
||||
root_id = uuid.uuid4()
|
||||
child_id = uuid.uuid4()
|
||||
image_key_root, image_url_root = get_image_refs(root_id)
|
||||
image_key_child, image_url_child = get_image_refs(child_id)
|
||||
|
||||
# Create root document with attachment
|
||||
ydoc = pycrdt.Doc()
|
||||
fragment = pycrdt.XmlFragment(
|
||||
[
|
||||
pycrdt.XmlElement("img", {"src": image_url_root}),
|
||||
]
|
||||
)
|
||||
ydoc["document-store"] = fragment
|
||||
update = ydoc.get_update()
|
||||
root_content = base64.b64encode(update).decode("utf-8")
|
||||
|
||||
root = factories.DocumentFactory(
|
||||
id=root_id,
|
||||
users=[(user, "owner")],
|
||||
title="Root with Image",
|
||||
content=root_content,
|
||||
attachments=[image_key_root],
|
||||
)
|
||||
|
||||
# Create child with different attachment
|
||||
ydoc_child = pycrdt.Doc()
|
||||
fragment_child = pycrdt.XmlFragment(
|
||||
[
|
||||
pycrdt.XmlElement("img", {"src": image_url_child}),
|
||||
]
|
||||
)
|
||||
ydoc_child["document-store"] = fragment_child
|
||||
update_child = ydoc_child.get_update()
|
||||
child_content = base64.b64encode(update_child).decode("utf-8")
|
||||
|
||||
child = factories.DocumentFactory(
|
||||
id=child_id,
|
||||
parent=root,
|
||||
title="Child with Image",
|
||||
content=child_content,
|
||||
attachments=[image_key_child],
|
||||
)
|
||||
|
||||
# Duplicate with descendants
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{root.id!s}/duplicate/",
|
||||
{"with_descendants": True},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
duplicated_root = models.Document.objects.get(id=response.json()["id"])
|
||||
|
||||
# Check root attachments
|
||||
assert duplicated_root.attachments == [image_key_root]
|
||||
assert duplicated_root.content == root_content
|
||||
|
||||
# Check child attachments
|
||||
dup_children = duplicated_root.get_children()
|
||||
assert dup_children.count() == 1
|
||||
dup_child = dup_children.first()
|
||||
assert dup_child.attachments == [image_key_child]
|
||||
assert dup_child.content == child_content
|
||||
|
||||
|
||||
def test_api_documents_duplicate_with_descendants_and_accesses():
|
||||
"""
|
||||
Duplicating with descendants and accesses should propagate accesses to all children.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
other_user = factories.UserFactory()
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
# Create document tree with accesses
|
||||
root = factories.DocumentFactory(
|
||||
users=[(user, "owner"), (other_user, "editor")],
|
||||
title="Root",
|
||||
)
|
||||
child = factories.DocumentFactory(
|
||||
parent=root,
|
||||
title="Child",
|
||||
)
|
||||
factories.UserDocumentAccessFactory(document=child, user=other_user, role="reader")
|
||||
|
||||
# Duplicate with descendants and accesses
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{root.id!s}/duplicate/",
|
||||
{"with_descendants": True, "with_accesses": True},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
duplicated_root = models.Document.objects.get(id=response.json()["id"])
|
||||
|
||||
# Check root accesses (should be duplicated)
|
||||
root_accesses = duplicated_root.accesses.order_by("user_id")
|
||||
assert root_accesses.count() == 2
|
||||
assert root_accesses.get(user=user).role == "owner"
|
||||
assert root_accesses.get(user=other_user).role == "editor"
|
||||
|
||||
# Check child accesses (should be duplicated)
|
||||
dup_children = duplicated_root.get_children()
|
||||
dup_child = dup_children.first()
|
||||
child_accesses = dup_child.accesses.order_by("user_id")
|
||||
assert child_accesses.count() == 1
|
||||
assert child_accesses.get(user=other_user).role == "reader"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("role", ["editor", "reader"])
|
||||
def test_api_documents_duplicate_with_descendants_non_root_document_becomes_root(role):
|
||||
"""
|
||||
When duplicating a non-root document with descendants as a reader/editor,
|
||||
it should become a root document and still duplicate its children.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
parent = factories.DocumentFactory(users=[(user, "owner")])
|
||||
child = factories.DocumentFactory(
|
||||
parent=parent,
|
||||
users=[(user, role)],
|
||||
title="Sub Document",
|
||||
)
|
||||
grandchild = factories.DocumentFactory(
|
||||
parent=child,
|
||||
title="Grandchild",
|
||||
)
|
||||
|
||||
assert child.is_child_of(parent)
|
||||
|
||||
# Duplicate the child (non-root) with descendants
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{child.id!s}/duplicate/",
|
||||
{"with_descendants": True},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
duplicated_child = models.Document.objects.get(id=response.json()["id"])
|
||||
|
||||
assert duplicated_child.title == "Copy of Sub Document"
|
||||
|
||||
dup_grandchildren = duplicated_child.get_children()
|
||||
assert dup_grandchildren.count() == 1
|
||||
dup_grandchild = dup_grandchildren.first()
|
||||
assert dup_grandchild.title == "Copy of Grandchild"
|
||||
assert dup_grandchild.duplicated_from == grandchild
|
||||
|
||||
|
||||
def test_api_documents_duplicate_without_descendants_should_not_duplicate_children():
|
||||
"""
|
||||
When with_descendants is not set or False, children should not be duplicated.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
# Create document tree
|
||||
root = factories.DocumentFactory(
|
||||
users=[(user, "owner")],
|
||||
title="Root",
|
||||
)
|
||||
child = factories.DocumentFactory(
|
||||
parent=root,
|
||||
title="Child",
|
||||
)
|
||||
|
||||
initial_count = models.Document.objects.count()
|
||||
assert initial_count == 2
|
||||
|
||||
# Duplicate without descendants (default behavior)
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{root.id!s}/duplicate/",
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
duplicated_root = models.Document.objects.get(id=response.json()["id"])
|
||||
|
||||
# Only root should be duplicated, not children
|
||||
assert models.Document.objects.count() == 3
|
||||
assert duplicated_root.get_children().count() == 0
|
||||
|
||||
|
||||
def test_api_documents_duplicate_with_descendants_preserves_link_configuration():
|
||||
"""
|
||||
Duplicating with descendants should preserve link configuration (link_reach, link_role)
|
||||
for all children when with_accesses is True.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
# Create document tree with specific link configurations
|
||||
root = factories.DocumentFactory(
|
||||
users=[(user, "owner")],
|
||||
title="Root",
|
||||
link_reach="public",
|
||||
link_role="reader",
|
||||
)
|
||||
child = factories.DocumentFactory(
|
||||
parent=root,
|
||||
title="Child",
|
||||
link_reach="restricted",
|
||||
link_role="editor",
|
||||
)
|
||||
|
||||
# Duplicate with descendants and accesses
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{root.id!s}/duplicate/",
|
||||
{"with_descendants": True, "with_accesses": True},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
duplicated_root = models.Document.objects.get(id=response.json()["id"])
|
||||
|
||||
# Check root link configuration
|
||||
assert duplicated_root.link_reach == root.link_reach
|
||||
assert duplicated_root.link_role == root.link_role
|
||||
|
||||
# Check child link configuration
|
||||
dup_children = duplicated_root.get_children()
|
||||
dup_child = dup_children.first()
|
||||
assert dup_child.link_reach == child.link_reach
|
||||
assert dup_child.link_role == child.link_role
|
||||
|
||||
|
||||
def test_api_documents_duplicate_with_descendants_complex_tree():
|
||||
"""
|
||||
Test duplication of a complex tree structure with multiple branches.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
# Create a complex tree:
|
||||
# root
|
||||
# / \
|
||||
# c1 c2
|
||||
# / \ \
|
||||
# gc1 gc2 gc3
|
||||
root = factories.DocumentFactory(
|
||||
users=[(user, "owner")],
|
||||
title="Root",
|
||||
)
|
||||
child1 = factories.DocumentFactory(parent=root, title="Child 1")
|
||||
child2 = factories.DocumentFactory(parent=root, title="Child 2")
|
||||
_grandchild1 = factories.DocumentFactory(parent=child1, title="GrandChild 1")
|
||||
_grandchild2 = factories.DocumentFactory(parent=child1, title="GrandChild 2")
|
||||
_grandchild3 = factories.DocumentFactory(parent=child2, title="GrandChild 3")
|
||||
|
||||
initial_count = models.Document.objects.count()
|
||||
assert initial_count == 6
|
||||
|
||||
# Duplicate with descendants
|
||||
response = client.post(
|
||||
f"/api/v1.0/documents/{root.id!s}/duplicate/",
|
||||
{"with_descendants": True},
|
||||
format="json",
|
||||
)
|
||||
|
||||
assert response.status_code == 201
|
||||
duplicated_root = models.Document.objects.get(id=response.json()["id"])
|
||||
|
||||
# All documents should be duplicated
|
||||
assert models.Document.objects.count() == 12
|
||||
|
||||
# Check structure is preserved
|
||||
dup_children = duplicated_root.get_children().order_by("title")
|
||||
assert dup_children.count() == 2
|
||||
|
||||
dup_child1 = dup_children.first()
|
||||
assert dup_child1.title == "Copy of Child 1"
|
||||
dup_grandchildren1 = dup_child1.get_children().order_by("title")
|
||||
assert dup_grandchildren1.count() == 2
|
||||
assert dup_grandchildren1.first().title == "Copy of GrandChild 1"
|
||||
assert dup_grandchildren1.last().title == "Copy of GrandChild 2"
|
||||
|
||||
dup_child2 = dup_children.last()
|
||||
assert dup_child2.title == "Copy of Child 2"
|
||||
dup_grandchildren2 = dup_child2.get_children()
|
||||
assert dup_grandchildren2.count() == 1
|
||||
assert dup_grandchildren2.first().title == "Copy of GrandChild 3"
|
||||
|
||||
@@ -29,7 +29,6 @@ def test_api_documents_retrieve_anonymous_public_standalone():
|
||||
"abilities": {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": False,
|
||||
"ai_proxy": False,
|
||||
"ai_transform": False,
|
||||
"ai_translate": False,
|
||||
"attachment_upload": document.link_role == "editor",
|
||||
@@ -108,7 +107,6 @@ def test_api_documents_retrieve_anonymous_public_parent():
|
||||
"abilities": {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": False,
|
||||
"ai_proxy": False,
|
||||
"ai_transform": False,
|
||||
"ai_translate": False,
|
||||
"attachment_upload": grand_parent.link_role == "editor",
|
||||
@@ -217,7 +215,6 @@ def test_api_documents_retrieve_authenticated_unrelated_public_or_authenticated(
|
||||
"abilities": {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": False,
|
||||
"ai_proxy": document.link_role == "editor",
|
||||
"ai_transform": document.link_role == "editor",
|
||||
"ai_translate": document.link_role == "editor",
|
||||
"attachment_upload": document.link_role == "editor",
|
||||
@@ -303,7 +300,6 @@ def test_api_documents_retrieve_authenticated_public_or_authenticated_parent(rea
|
||||
"abilities": {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": False,
|
||||
"ai_proxy": grand_parent.link_role == "editor",
|
||||
"ai_transform": grand_parent.link_role == "editor",
|
||||
"ai_translate": grand_parent.link_role == "editor",
|
||||
"attachment_upload": grand_parent.link_role == "editor",
|
||||
@@ -502,7 +498,6 @@ def test_api_documents_retrieve_authenticated_related_parent():
|
||||
"abilities": {
|
||||
"accesses_manage": access.role in ["administrator", "owner"],
|
||||
"accesses_view": True,
|
||||
"ai_proxy": access.role not in ["reader", "commenter"],
|
||||
"ai_transform": access.role not in ["reader", "commenter"],
|
||||
"ai_translate": access.role not in ["reader", "commenter"],
|
||||
"attachment_upload": access.role not in ["reader", "commenter"],
|
||||
@@ -1062,48 +1057,3 @@ def test_api_documents_retrieve_permanently_deleted_related(role, depth):
|
||||
|
||||
assert response.status_code == 404
|
||||
assert response.json() == {"detail": "Not found."}
|
||||
|
||||
|
||||
def test_api_documents_retrieve_without_content():
|
||||
"""
|
||||
Test retrieve using without_content query string should remove the content in the response
|
||||
"""
|
||||
|
||||
user = factories.UserFactory()
|
||||
|
||||
document = factories.DocumentFactory(creator=user, users=[(user, "owner")])
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
with mock.patch("core.models.Document.content") as mock_document_content:
|
||||
response = client.get(
|
||||
f"/api/v1.0/documents/{document.id!s}/?without_content=true"
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
payload = response.json()
|
||||
assert "content" not in payload
|
||||
mock_document_content.assert_not_called()
|
||||
|
||||
|
||||
def test_api_documents_retrieve_without_content_invalid_value():
|
||||
"""
|
||||
Test retrieve using without_content query string but an invalid value
|
||||
should return a 400
|
||||
"""
|
||||
|
||||
user = factories.UserFactory()
|
||||
|
||||
document = factories.DocumentFactory(creator=user, users=[(user, "owner")])
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(user)
|
||||
|
||||
response = client.get(
|
||||
f"/api/v1.0/documents/{document.id!s}/?without_content=invalid-value"
|
||||
)
|
||||
assert response.status_code == 400
|
||||
|
||||
assert response.json() == ["Must be a valid boolean."]
|
||||
|
||||
@@ -72,7 +72,6 @@ def test_api_documents_trashbin_format():
|
||||
"abilities": {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": False,
|
||||
"ai_proxy": False,
|
||||
"ai_transform": False,
|
||||
"ai_translate": False,
|
||||
"attachment_upload": False,
|
||||
|
||||
@@ -19,10 +19,7 @@ pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
@override_settings(
|
||||
AI_BOT={"name": "Test Bot", "color": "#000000"},
|
||||
AI_FEATURE_ENABLED=False,
|
||||
AI_FEATURE_BLOCKNOTE_ENABLED=False,
|
||||
AI_FEATURE_LEGACY_ENABLED=False,
|
||||
API_USERS_SEARCH_QUERY_MIN_LENGTH=6,
|
||||
COLLABORATION_WS_URL="http://testcollab/",
|
||||
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY=True,
|
||||
@@ -47,10 +44,7 @@ def test_api_config(is_authenticated):
|
||||
response = client.get("/api/v1.0/config/")
|
||||
assert response.status_code == HTTP_200_OK
|
||||
assert response.json() == {
|
||||
"AI_BOT": {"name": "Test Bot", "color": "#000000"},
|
||||
"AI_FEATURE_ENABLED": False,
|
||||
"AI_FEATURE_BLOCKNOTE_ENABLED": False,
|
||||
"AI_FEATURE_LEGACY_ENABLED": False,
|
||||
"API_USERS_SEARCH_QUERY_MIN_LENGTH": 6,
|
||||
"COLLABORATION_WS_URL": "http://testcollab/",
|
||||
"COLLABORATION_WS_NOT_CONNECTED_READY_ONLY": True,
|
||||
|
||||
@@ -155,7 +155,6 @@ def test_models_documents_get_abilities_forbidden(
|
||||
expected_abilities = {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": False,
|
||||
"ai_proxy": False,
|
||||
"ai_transform": False,
|
||||
"ai_translate": False,
|
||||
"attachment_upload": False,
|
||||
@@ -221,7 +220,6 @@ def test_models_documents_get_abilities_reader(
|
||||
expected_abilities = {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": False,
|
||||
"ai_proxy": False,
|
||||
"ai_transform": False,
|
||||
"ai_translate": False,
|
||||
"attachment_upload": False,
|
||||
@@ -292,7 +290,6 @@ def test_models_documents_get_abilities_commenter(
|
||||
expected_abilities = {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": False,
|
||||
"ai_proxy": False,
|
||||
"ai_transform": False,
|
||||
"ai_translate": False,
|
||||
"attachment_upload": False,
|
||||
@@ -360,7 +357,6 @@ def test_models_documents_get_abilities_editor(
|
||||
expected_abilities = {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": False,
|
||||
"ai_proxy": is_authenticated,
|
||||
"ai_transform": is_authenticated,
|
||||
"ai_translate": is_authenticated,
|
||||
"attachment_upload": True,
|
||||
@@ -417,7 +413,6 @@ def test_models_documents_get_abilities_owner(django_assert_num_queries):
|
||||
expected_abilities = {
|
||||
"accesses_manage": True,
|
||||
"accesses_view": True,
|
||||
"ai_proxy": True,
|
||||
"ai_transform": True,
|
||||
"ai_translate": True,
|
||||
"attachment_upload": True,
|
||||
@@ -460,7 +455,6 @@ def test_models_documents_get_abilities_owner(django_assert_num_queries):
|
||||
assert document.get_abilities(user) == {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": False,
|
||||
"ai_proxy": False,
|
||||
"ai_transform": False,
|
||||
"ai_translate": False,
|
||||
"attachment_upload": False,
|
||||
@@ -507,7 +501,6 @@ def test_models_documents_get_abilities_administrator(django_assert_num_queries)
|
||||
expected_abilities = {
|
||||
"accesses_manage": True,
|
||||
"accesses_view": True,
|
||||
"ai_proxy": True,
|
||||
"ai_transform": True,
|
||||
"ai_translate": True,
|
||||
"attachment_upload": True,
|
||||
@@ -564,7 +557,6 @@ def test_models_documents_get_abilities_editor_user(django_assert_num_queries):
|
||||
expected_abilities = {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": True,
|
||||
"ai_proxy": True,
|
||||
"ai_transform": True,
|
||||
"ai_translate": True,
|
||||
"attachment_upload": True,
|
||||
@@ -628,7 +620,6 @@ def test_models_documents_get_abilities_reader_user(
|
||||
"accesses_view": True,
|
||||
# If you get your editor rights from the link role and not your access role
|
||||
# You should not access AI if it's restricted to users with specific access
|
||||
"ai_proxy": access_from_link and ai_access_setting != "restricted",
|
||||
"ai_transform": access_from_link and ai_access_setting != "restricted",
|
||||
"ai_translate": access_from_link and ai_access_setting != "restricted",
|
||||
"attachment_upload": access_from_link,
|
||||
@@ -695,7 +686,6 @@ def test_models_documents_get_abilities_commenter_user(
|
||||
"accesses_view": True,
|
||||
# If you get your editor rights from the link role and not your access role
|
||||
# You should not access AI if it's restricted to users with specific access
|
||||
"ai_proxy": access_from_link and ai_access_setting != "restricted",
|
||||
"ai_transform": access_from_link and ai_access_setting != "restricted",
|
||||
"ai_translate": access_from_link and ai_access_setting != "restricted",
|
||||
"attachment_upload": access_from_link,
|
||||
@@ -757,7 +747,6 @@ def test_models_documents_get_abilities_preset_role(django_assert_num_queries):
|
||||
assert abilities == {
|
||||
"accesses_manage": False,
|
||||
"accesses_view": True,
|
||||
"ai_proxy": False,
|
||||
"ai_transform": False,
|
||||
"ai_translate": False,
|
||||
"attachment_upload": False,
|
||||
@@ -889,7 +878,6 @@ def test_models_document_get_abilities_ai_access_authenticated(is_authenticated,
|
||||
document = factories.DocumentFactory(link_reach=reach, link_role="editor")
|
||||
|
||||
abilities = document.get_abilities(user)
|
||||
assert abilities["ai_proxy"] is True
|
||||
assert abilities["ai_transform"] is True
|
||||
assert abilities["ai_translate"] is True
|
||||
|
||||
@@ -909,7 +897,6 @@ def test_models_document_get_abilities_ai_access_public(is_authenticated, reach)
|
||||
document = factories.DocumentFactory(link_reach=reach, link_role="editor")
|
||||
|
||||
abilities = document.get_abilities(user)
|
||||
assert abilities["ai_proxy"] == is_authenticated
|
||||
assert abilities["ai_transform"] == is_authenticated
|
||||
assert abilities["ai_translate"] == is_authenticated
|
||||
|
||||
@@ -1034,10 +1021,7 @@ def test_models_documents__email_invitation__success():
|
||||
f"Test Sender (sender@example.com) invited you with the role "editor" "
|
||||
f"on the following document: {document.title}" in email_content
|
||||
)
|
||||
assert (
|
||||
f"docs/{document.id}/?utm_source=docssharelink&utm_campaign={document.id}"
|
||||
in email_content
|
||||
)
|
||||
assert f"docs/{document.id}/" in email_content
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -1067,18 +1051,10 @@ def test_models_documents__email_invitation__url_app_param(email_url_app):
|
||||
|
||||
# Determine expected domain
|
||||
if email_url_app:
|
||||
expected_url = (
|
||||
f"https://test-example.com/docs/{document.id}/"
|
||||
f"?utm_source=docssharelink&utm_campaign={document.id}"
|
||||
)
|
||||
assert expected_url in email_content
|
||||
assert f"https://test-example.com/docs/{document.id}/" in email_content
|
||||
else:
|
||||
# Default Site domain is example.com
|
||||
expected_url = (
|
||||
f"example.com/docs/{document.id}/"
|
||||
f"?utm_source=docssharelink&utm_campaign={document.id}"
|
||||
)
|
||||
assert expected_url in email_content
|
||||
assert f"example.com/docs/{document.id}/" in email_content
|
||||
|
||||
|
||||
def test_models_documents__email_invitation__success_empty_title():
|
||||
@@ -1109,10 +1085,7 @@ def test_models_documents__email_invitation__success_empty_title():
|
||||
"Test Sender (sender@example.com) invited you with the role "editor" "
|
||||
"on the following document: Untitled Document" in email_content
|
||||
)
|
||||
assert (
|
||||
f"docs/{document.id}/?utm_source=docssharelink&utm_campaign={document.id}"
|
||||
in email_content
|
||||
)
|
||||
assert f"docs/{document.id}/" in email_content
|
||||
|
||||
|
||||
def test_models_documents__email_invitation__success_fr():
|
||||
@@ -1147,10 +1120,7 @@ def test_models_documents__email_invitation__success_fr():
|
||||
f"Test Sender2 (sender2@example.com) vous a invité avec le rôle "propriétaire" "
|
||||
f"sur le document suivant : {document.title}" in email_content
|
||||
)
|
||||
assert (
|
||||
f"docs/{document.id}/?utm_source=docssharelink&utm_campaign={document.id}"
|
||||
in email_content
|
||||
)
|
||||
assert f"docs/{document.id}/" in email_content
|
||||
|
||||
|
||||
@mock.patch(
|
||||
|
||||
@@ -2,11 +2,7 @@
|
||||
Unit tests for the User model
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.test.utils import override_settings
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -78,217 +74,3 @@ def test_modes_users_convert_valid_invitations():
|
||||
id=invitation_other_document.id
|
||||
).exists()
|
||||
assert models.Invitation.objects.filter(id=other_email_invitation.id).exists()
|
||||
|
||||
|
||||
@override_settings(USER_ONBOARDING_DOCUMENTS=[])
|
||||
def test_models_users_handle_onboarding_documents_access_empty_setting():
|
||||
"""
|
||||
When USER_ONBOARDING_DOCUMENTS is empty, no accesses should be created.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
assert models.DocumentAccess.objects.filter(user=user).count() == 0
|
||||
|
||||
|
||||
def test_models_users_handle_onboarding_documents_access_with_single_document():
|
||||
"""
|
||||
When USER_ONBOARDING_DOCUMENTS has a valid document ID,
|
||||
an access should be created for the new user with the READER role.
|
||||
|
||||
The document should be pinned as a favorite for the user.
|
||||
"""
|
||||
document = factories.DocumentFactory()
|
||||
|
||||
with override_settings(USER_ONBOARDING_DOCUMENTS=[str(document.id)]):
|
||||
user = factories.UserFactory()
|
||||
|
||||
assert (
|
||||
models.DocumentAccess.objects.filter(user=user, document=document).count() == 1
|
||||
)
|
||||
|
||||
access = models.DocumentAccess.objects.get(user=user, document=document)
|
||||
assert access.role == models.RoleChoices.READER
|
||||
|
||||
user_favorites = models.DocumentFavorite.objects.filter(user=user)
|
||||
assert user_favorites.count() == 1
|
||||
assert user_favorites.filter(document=document).exists()
|
||||
|
||||
|
||||
def test_models_users_handle_onboarding_documents_access_with_multiple_documents():
|
||||
"""
|
||||
When USER_ONBOARDING_DOCUMENTS has multiple valid document IDs,
|
||||
accesses should be created for all documents.
|
||||
|
||||
All accesses should have the READER role.
|
||||
All documents should be pinned as favorites for the user.
|
||||
"""
|
||||
document1 = factories.DocumentFactory(title="Document 1")
|
||||
document2 = factories.DocumentFactory(title="Document 2")
|
||||
document3 = factories.DocumentFactory(title="Document 3")
|
||||
|
||||
with override_settings(
|
||||
USER_ONBOARDING_DOCUMENTS=[
|
||||
str(document1.id),
|
||||
str(document2.id),
|
||||
str(document3.id),
|
||||
]
|
||||
):
|
||||
user = factories.UserFactory()
|
||||
|
||||
user_accesses = models.DocumentAccess.objects.filter(user=user)
|
||||
assert user_accesses.count() == 3
|
||||
|
||||
assert models.DocumentAccess.objects.filter(user=user, document=document1).exists()
|
||||
assert models.DocumentAccess.objects.filter(user=user, document=document2).exists()
|
||||
assert models.DocumentAccess.objects.filter(user=user, document=document3).exists()
|
||||
|
||||
for access in user_accesses:
|
||||
assert access.role == models.RoleChoices.READER
|
||||
|
||||
user_favorites = models.DocumentFavorite.objects.filter(user=user)
|
||||
assert user_favorites.count() == 3
|
||||
assert user_favorites.filter(document=document1).exists()
|
||||
assert user_favorites.filter(document=document2).exists()
|
||||
assert user_favorites.filter(document=document3).exists()
|
||||
|
||||
|
||||
def test_models_users_handle_onboarding_documents_access_with_invalid_document_id():
|
||||
"""
|
||||
When USER_ONBOARDING_DOCUMENTS has an invalid document ID,
|
||||
it should be skipped and logged, but not raise an exception.
|
||||
"""
|
||||
invalid_id = uuid.uuid4()
|
||||
|
||||
with override_settings(USER_ONBOARDING_DOCUMENTS=[str(invalid_id)]):
|
||||
with patch("core.models.logger") as mock_logger:
|
||||
user = factories.UserFactory()
|
||||
|
||||
mock_logger.warning.assert_called_once()
|
||||
call_args = mock_logger.warning.call_args
|
||||
assert "Onboarding document with id" in call_args[0][0]
|
||||
|
||||
assert models.DocumentAccess.objects.filter(user=user).count() == 0
|
||||
|
||||
|
||||
def test_models_users_handle_onboarding_documents_access_duplicate_prevention():
|
||||
"""
|
||||
If the same document is listed multiple times in USER_ONBOARDING_DOCUMENTS,
|
||||
it should only create one access (or handle duplicates gracefully).
|
||||
"""
|
||||
document = factories.DocumentFactory()
|
||||
|
||||
with override_settings(
|
||||
USER_ONBOARDING_DOCUMENTS=[str(document.id), str(document.id)]
|
||||
):
|
||||
user = factories.UserFactory()
|
||||
|
||||
user_accesses = models.DocumentAccess.objects.filter(user=user, document=document)
|
||||
|
||||
assert user_accesses.count() >= 1
|
||||
|
||||
|
||||
@override_settings(USER_ONBOARDING_SANDBOX_DOCUMENT=None)
|
||||
def test_models_users_duplicate_onboarding_sandbox_document_no_setting():
|
||||
"""
|
||||
When USER_ONBOARDING_SANDBOX_DOCUMENT is not set, no sandbox document should be created.
|
||||
"""
|
||||
user = factories.UserFactory()
|
||||
|
||||
assert (
|
||||
models.Document.objects.filter(creator=user, title__icontains="Sandbox").count()
|
||||
== 0
|
||||
)
|
||||
|
||||
initial_accesses = models.DocumentAccess.objects.filter(user=user).count()
|
||||
assert initial_accesses == 0
|
||||
|
||||
|
||||
def test_models_users_duplicate_onboarding_sandbox_document_creates_sandbox():
|
||||
"""
|
||||
When USER_ONBOARDING_SANDBOX_DOCUMENT is set with a valid template document,
|
||||
a new sandbox document should be created for the user with OWNER access.
|
||||
"""
|
||||
template_document = factories.DocumentFactory(title="Getting started with Docs")
|
||||
|
||||
with override_settings(USER_ONBOARDING_SANDBOX_DOCUMENT=str(template_document.id)):
|
||||
user = factories.UserFactory()
|
||||
|
||||
sandbox_docs = models.Document.objects.filter(
|
||||
creator=user, title="Getting started with Docs"
|
||||
)
|
||||
assert sandbox_docs.count() == 1
|
||||
|
||||
sandbox_doc = sandbox_docs.first()
|
||||
assert sandbox_doc.creator == user
|
||||
assert sandbox_doc.duplicated_from == template_document
|
||||
|
||||
access = models.DocumentAccess.objects.get(user=user, document=sandbox_doc)
|
||||
assert access.role == models.RoleChoices.OWNER
|
||||
|
||||
|
||||
def test_models_users_duplicate_onboarding_sandbox_document_with_invalid_template_id():
|
||||
"""
|
||||
When USER_ONBOARDING_SANDBOX_DOCUMENT has an invalid document ID,
|
||||
it should be skipped and logged, but not raise an exception.
|
||||
"""
|
||||
invalid_id = uuid.uuid4()
|
||||
|
||||
with override_settings(USER_ONBOARDING_SANDBOX_DOCUMENT=str(invalid_id)):
|
||||
with patch("core.models.logger") as mock_logger:
|
||||
user = factories.UserFactory()
|
||||
|
||||
mock_logger.warning.assert_called_once()
|
||||
call_args = mock_logger.warning.call_args
|
||||
assert "Onboarding sandbox document with id" in call_args[0][0]
|
||||
|
||||
sandbox_docs = models.Document.objects.filter(creator=user)
|
||||
assert sandbox_docs.count() == 0
|
||||
|
||||
|
||||
def test_models_users_duplicate_onboarding_sandbox_document_creates_unique_sandbox_per_user():
|
||||
"""
|
||||
Each new user should get their own independent sandbox document.
|
||||
"""
|
||||
template_document = factories.DocumentFactory(title="Getting started with Docs")
|
||||
|
||||
with override_settings(USER_ONBOARDING_SANDBOX_DOCUMENT=str(template_document.id)):
|
||||
user1 = factories.UserFactory()
|
||||
user2 = factories.UserFactory()
|
||||
|
||||
sandbox_docs_user1 = models.Document.objects.filter(
|
||||
creator=user1, title="Getting started with Docs"
|
||||
)
|
||||
sandbox_docs_user2 = models.Document.objects.filter(
|
||||
creator=user2, title="Getting started with Docs"
|
||||
)
|
||||
|
||||
assert sandbox_docs_user1.count() == 1
|
||||
assert sandbox_docs_user2.count() == 1
|
||||
|
||||
assert sandbox_docs_user1.first().id != sandbox_docs_user2.first().id
|
||||
|
||||
|
||||
def test_models_users_duplicate_onboarding_sandbox_document_integration_with_other_methods():
|
||||
"""
|
||||
Verify that sandbox creation works alongside other onboarding methods.
|
||||
"""
|
||||
template_document = factories.DocumentFactory(title="Getting started with Docs")
|
||||
onboarding_doc = factories.DocumentFactory(title="Onboarding Document")
|
||||
|
||||
with override_settings(
|
||||
USER_ONBOARDING_SANDBOX_DOCUMENT=str(template_document.id),
|
||||
USER_ONBOARDING_DOCUMENTS=[str(onboarding_doc.id)],
|
||||
):
|
||||
user = factories.UserFactory()
|
||||
|
||||
sandbox_doc = models.Document.objects.filter(
|
||||
creator=user, title="Getting started with Docs"
|
||||
).first()
|
||||
|
||||
user_accesses = models.DocumentAccess.objects.filter(user=user)
|
||||
assert user_accesses.count() == 2
|
||||
|
||||
sandbox_access = user_accesses.get(document=sandbox_doc)
|
||||
onboarding_access = user_accesses.get(document=onboarding_doc)
|
||||
|
||||
assert sandbox_access.role == models.RoleChoices.OWNER
|
||||
assert onboarding_access.role == models.RoleChoices.READER
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
"""
|
||||
Test AI services in the impress core app.
|
||||
Test ai API endpoints in the impress core app.
|
||||
"""
|
||||
# pylint: disable=protected-access
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
@@ -11,33 +9,12 @@ from django.test.utils import override_settings
|
||||
|
||||
import pytest
|
||||
from openai import OpenAIError
|
||||
from pydantic_ai.ui.vercel_ai.request_types import TextUIPart, UIMessage
|
||||
|
||||
from core.services.ai_services import (
|
||||
BLOCKNOTE_TOOL_STRICT_PROMPT,
|
||||
AIService,
|
||||
convert_async_generator_to_sync,
|
||||
)
|
||||
from core.services.ai_services import AIService
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def ai_settings(settings):
|
||||
"""Fixture to set AI settings."""
|
||||
settings.AI_MODEL = "llama"
|
||||
settings.AI_BASE_URL = "http://example.com"
|
||||
settings.AI_API_KEY = "test-key"
|
||||
settings.AI_FEATURE_ENABLED = True
|
||||
settings.AI_FEATURE_BLOCKNOTE_ENABLED = True
|
||||
settings.AI_FEATURE_LEGACY_ENABLED = True
|
||||
settings.LANGFUSE_PUBLIC_KEY = None
|
||||
settings.AI_VERCEL_SDK_VERSION = 6
|
||||
|
||||
|
||||
# -- AIService.__init__ --
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"setting_name, setting_value",
|
||||
[
|
||||
@@ -46,25 +23,22 @@ def ai_settings(settings):
|
||||
("AI_MODEL", None),
|
||||
],
|
||||
)
|
||||
def test_services_ai_setting_missing(setting_name, setting_value, settings):
|
||||
def test_api_ai_setting_missing(setting_name, setting_value):
|
||||
"""Setting should be set"""
|
||||
setattr(settings, setting_name, setting_value)
|
||||
|
||||
with pytest.raises(
|
||||
ImproperlyConfigured,
|
||||
match="AI configuration not set",
|
||||
):
|
||||
AIService()
|
||||
|
||||
|
||||
# -- AIService.transform --
|
||||
with override_settings(**{setting_name: setting_value}):
|
||||
with pytest.raises(
|
||||
ImproperlyConfigured,
|
||||
match="AI configuration not set",
|
||||
):
|
||||
AIService()
|
||||
|
||||
|
||||
@override_settings(
|
||||
AI_BASE_URL="http://example.com", AI_API_KEY="test-key", AI_MODEL="test-model"
|
||||
)
|
||||
@patch("openai.resources.chat.completions.Completions.create")
|
||||
def test_services_ai_client_error(mock_create):
|
||||
def test_api_ai__client_error(mock_create):
|
||||
"""Fail when the client raises an error"""
|
||||
|
||||
mock_create.side_effect = OpenAIError("Mocked client error")
|
||||
@@ -80,7 +54,7 @@ def test_services_ai_client_error(mock_create):
|
||||
AI_BASE_URL="http://example.com", AI_API_KEY="test-key", AI_MODEL="test-model"
|
||||
)
|
||||
@patch("openai.resources.chat.completions.Completions.create")
|
||||
def test_services_ai_client_invalid_response(mock_create):
|
||||
def test_api_ai__client_invalid_response(mock_create):
|
||||
"""Fail when the client response is invalid"""
|
||||
|
||||
mock_create.return_value = MagicMock(
|
||||
@@ -98,7 +72,7 @@ def test_services_ai_client_invalid_response(mock_create):
|
||||
AI_BASE_URL="http://example.com", AI_API_KEY="test-key", AI_MODEL="test-model"
|
||||
)
|
||||
@patch("openai.resources.chat.completions.Completions.create")
|
||||
def test_services_ai_success(mock_create):
|
||||
def test_api_ai__success(mock_create):
|
||||
"""The AI request should work as expect when called with valid arguments."""
|
||||
|
||||
mock_create.return_value = MagicMock(
|
||||
@@ -108,483 +82,3 @@ def test_services_ai_success(mock_create):
|
||||
response = AIService().transform("hello", "prompt")
|
||||
|
||||
assert response == {"answer": "Salut"}
|
||||
|
||||
|
||||
# -- AIService.translate --
|
||||
|
||||
|
||||
@patch("openai.resources.chat.completions.Completions.create")
|
||||
def test_services_ai_translate_success(mock_create):
|
||||
"""Translate should call the AI API with the correct language prompt."""
|
||||
|
||||
mock_create.return_value = MagicMock(
|
||||
choices=[MagicMock(message=MagicMock(content="Bonjour"))]
|
||||
)
|
||||
|
||||
response = AIService().translate("<p>Hello</p>", "fr")
|
||||
|
||||
assert response == {"answer": "Bonjour"}
|
||||
call_args = mock_create.call_args
|
||||
system_content = call_args[1]["messages"][0]["content"]
|
||||
assert "French" in system_content or "fr" in system_content
|
||||
|
||||
|
||||
@patch("openai.resources.chat.completions.Completions.create")
|
||||
def test_services_ai_translate_unknown_language(mock_create):
|
||||
"""Translate with an unknown language code should use the code as-is."""
|
||||
|
||||
mock_create.return_value = MagicMock(
|
||||
choices=[MagicMock(message=MagicMock(content="Translated"))]
|
||||
)
|
||||
|
||||
response = AIService().translate("<p>Hello</p>", "xx-unknown")
|
||||
|
||||
assert response == {"answer": "Translated"}
|
||||
call_args = mock_create.call_args
|
||||
system_content = call_args[1]["messages"][0]["content"]
|
||||
assert "xx-unknown" in system_content
|
||||
|
||||
|
||||
# -- convert_async_generator_to_sync --
|
||||
|
||||
|
||||
def test_convert_async_generator_to_sync_basic():
|
||||
"""Should convert an async generator yielding items to a sync iterator."""
|
||||
|
||||
async def async_gen():
|
||||
for item in ["hello", "world", "!"]:
|
||||
yield item
|
||||
|
||||
result = list(convert_async_generator_to_sync(async_gen()))
|
||||
assert result == ["hello", "world", "!"]
|
||||
|
||||
|
||||
def test_convert_async_generator_to_sync_empty():
|
||||
"""Should handle an empty async generator."""
|
||||
|
||||
async def async_gen():
|
||||
return
|
||||
yield
|
||||
|
||||
result = list(convert_async_generator_to_sync(async_gen()))
|
||||
assert not result
|
||||
|
||||
|
||||
def test_convert_async_generator_to_sync_exception():
|
||||
"""Should propagate exceptions from the async generator."""
|
||||
|
||||
async def async_gen():
|
||||
yield "first"
|
||||
raise ValueError("async error")
|
||||
|
||||
sync_iter = convert_async_generator_to_sync(async_gen())
|
||||
assert next(sync_iter) == "first"
|
||||
|
||||
with pytest.raises(ValueError, match="async error"):
|
||||
next(sync_iter)
|
||||
|
||||
|
||||
# -- AIService.inject_document_state_messages --
|
||||
|
||||
|
||||
def test_inject_document_state_messages_no_metadata():
|
||||
"""Messages without documentState metadata should pass through unchanged."""
|
||||
messages = [
|
||||
UIMessage(role="user", id="msg-1", parts=[TextUIPart(text="Hello")]),
|
||||
]
|
||||
|
||||
result = AIService.inject_document_state_messages(messages)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].id == "msg-1"
|
||||
|
||||
|
||||
def test_inject_document_state_messages_with_selection():
|
||||
"""A user message with documentState and selection should get an
|
||||
assistant context message prepended."""
|
||||
messages = [
|
||||
UIMessage(
|
||||
role="user",
|
||||
id="msg-1",
|
||||
parts=[TextUIPart(text="Fix this")],
|
||||
metadata={
|
||||
"documentState": {
|
||||
"selection": {"start": 0, "end": 5},
|
||||
"selectedBlocks": [{"type": "paragraph", "content": "Hello"}],
|
||||
"blocks": [
|
||||
{"type": "paragraph", "content": "Hello"},
|
||||
{"type": "paragraph", "content": "World"},
|
||||
],
|
||||
}
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
result = AIService.inject_document_state_messages(messages)
|
||||
|
||||
assert len(result) == 2
|
||||
# First message should be the injected assistant context
|
||||
assert result[0].role == "assistant"
|
||||
assert result[0].id == "assistant-document-state-msg-1"
|
||||
assert len(result[0].parts) == 4
|
||||
assert "selection" in result[0].parts[0].text.lower()
|
||||
# Second message should be the original user message
|
||||
assert result[1].id == "msg-1"
|
||||
|
||||
|
||||
def test_inject_document_state_messages_without_selection():
|
||||
"""A user message with documentState but no selection should describe
|
||||
the full document context."""
|
||||
messages = [
|
||||
UIMessage(
|
||||
role="user",
|
||||
id="msg-1",
|
||||
parts=[TextUIPart(text="Summarize")],
|
||||
metadata={
|
||||
"documentState": {
|
||||
"selection": None,
|
||||
"blocks": [
|
||||
{"type": "paragraph", "content": "Hello"},
|
||||
],
|
||||
"isEmptyDocument": False,
|
||||
}
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
result = AIService.inject_document_state_messages(messages)
|
||||
|
||||
assert len(result) == 2
|
||||
assistant_msg = result[0]
|
||||
assert assistant_msg.role == "assistant"
|
||||
assert len(assistant_msg.parts) == 2
|
||||
assert "no active selection" in assistant_msg.parts[0].text.lower()
|
||||
assert "prefer updating" in assistant_msg.parts[0].text.lower()
|
||||
|
||||
|
||||
def test_inject_document_state_messages_empty_document():
|
||||
"""When the document is empty, the injected message should instruct
|
||||
updating the empty block first."""
|
||||
messages = [
|
||||
UIMessage(
|
||||
role="user",
|
||||
id="msg-1",
|
||||
parts=[TextUIPart(text="Write something")],
|
||||
metadata={
|
||||
"documentState": {
|
||||
"selection": None,
|
||||
"blocks": [{"type": "paragraph", "content": ""}],
|
||||
"isEmptyDocument": True,
|
||||
}
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
result = AIService.inject_document_state_messages(messages)
|
||||
|
||||
assert len(result) == 2
|
||||
assistant_msg = result[0]
|
||||
assert "update the empty block" in assistant_msg.parts[0].text.lower()
|
||||
|
||||
|
||||
def test_inject_document_state_messages_mixed():
|
||||
"""Only user messages with documentState get assistant context;
|
||||
other messages pass through unchanged."""
|
||||
messages = [
|
||||
UIMessage(
|
||||
role="assistant",
|
||||
id="msg-0",
|
||||
parts=[TextUIPart(text="Previous response")],
|
||||
),
|
||||
UIMessage(
|
||||
role="user",
|
||||
id="msg-1",
|
||||
parts=[TextUIPart(text="Hello")],
|
||||
),
|
||||
UIMessage(
|
||||
role="user",
|
||||
id="msg-2",
|
||||
parts=[TextUIPart(text="Fix this")],
|
||||
metadata={
|
||||
"documentState": {
|
||||
"selection": {"start": 0, "end": 5},
|
||||
"selectedBlocks": [{"type": "paragraph", "content": "Hello"}],
|
||||
"blocks": [{"type": "paragraph", "content": "Hello"}],
|
||||
}
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
result = AIService.inject_document_state_messages(messages)
|
||||
|
||||
# 3 original + 1 injected assistant message before msg-2
|
||||
assert len(result) == 4
|
||||
assert result[0].id == "msg-0"
|
||||
assert result[1].id == "msg-1"
|
||||
assert result[2].role == "assistant"
|
||||
assert result[2].id == "assistant-document-state-msg-2"
|
||||
assert result[3].id == "msg-2"
|
||||
|
||||
|
||||
# -- AIService.tool_definitions_to_toolset --
|
||||
|
||||
|
||||
def test_tool_definitions_to_toolset():
|
||||
"""Should convert frontend tool definitions to an ExternalToolset."""
|
||||
tool_definitions = {
|
||||
"applyOperations": {
|
||||
"description": "Apply operations to the document",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"operations": {"type": "array"},
|
||||
},
|
||||
},
|
||||
"outputSchema": {"type": "object"},
|
||||
},
|
||||
"insertBlocks": {
|
||||
"description": "Insert blocks",
|
||||
"inputSchema": {"type": "object"},
|
||||
},
|
||||
}
|
||||
|
||||
toolset = AIService.tool_definitions_to_toolset(tool_definitions)
|
||||
|
||||
# The ExternalToolset wraps ToolDefinition objects
|
||||
assert toolset is not None
|
||||
# Access internal tool definitions
|
||||
tool_defs = toolset.tool_defs
|
||||
assert len(tool_defs) == 2
|
||||
|
||||
names = {td.name for td in tool_defs}
|
||||
assert names == {"applyOperations", "insertBlocks"}
|
||||
|
||||
for td in tool_defs:
|
||||
assert td.kind == "external"
|
||||
if td.name == "applyOperations":
|
||||
assert td.description == "Apply operations to the document"
|
||||
assert td.metadata == {"output_schema": {"type": "object"}}
|
||||
|
||||
|
||||
def test_tool_definitions_to_toolset_missing_fields():
|
||||
"""Should handle tool definitions with missing optional fields."""
|
||||
tool_definitions = {
|
||||
"myTool": {},
|
||||
}
|
||||
|
||||
toolset = AIService.tool_definitions_to_toolset(tool_definitions)
|
||||
|
||||
tool_defs = toolset.tool_defs
|
||||
assert len(tool_defs) == 1
|
||||
assert tool_defs[0].name == "myTool"
|
||||
assert tool_defs[0].description == ""
|
||||
assert tool_defs[0].parameters_json_schema == {}
|
||||
assert tool_defs[0].metadata == {"output_schema": None}
|
||||
|
||||
|
||||
# -- AIService.stream --
|
||||
|
||||
|
||||
@patch.object(AIService, "_build_async_stream")
|
||||
def test_services_ai_stream_sync_mode(mock_build, monkeypatch):
|
||||
"""In sync mode, stream() should return a sync iterator."""
|
||||
|
||||
async def mock_async_gen():
|
||||
yield "chunk1"
|
||||
yield "chunk2"
|
||||
|
||||
mock_build.return_value = mock_async_gen()
|
||||
monkeypatch.setenv("PYTHON_SERVER_MODE", "sync")
|
||||
|
||||
service = AIService()
|
||||
request = MagicMock()
|
||||
result = service.stream(request)
|
||||
|
||||
# Should be a regular (sync) iterator, not async
|
||||
assert not isinstance(result, AsyncIterator)
|
||||
assert list(result) == ["chunk1", "chunk2"]
|
||||
mock_build.assert_called_once_with(request)
|
||||
|
||||
|
||||
@patch.object(AIService, "_build_async_stream")
|
||||
def test_services_ai_stream_async_mode(mock_build, monkeypatch):
|
||||
"""In async mode, stream() should return the async iterator directly."""
|
||||
|
||||
async def mock_async_gen():
|
||||
yield "chunk1"
|
||||
yield "chunk2"
|
||||
|
||||
mock_async_iter = mock_async_gen()
|
||||
mock_build.return_value = mock_async_iter
|
||||
monkeypatch.setenv("PYTHON_SERVER_MODE", "async")
|
||||
|
||||
service = AIService()
|
||||
request = MagicMock()
|
||||
result = service.stream(request)
|
||||
|
||||
assert result is mock_async_iter
|
||||
mock_build.assert_called_once_with(request)
|
||||
|
||||
|
||||
@patch.object(AIService, "_build_async_stream")
|
||||
def test_services_ai_stream_defaults_to_sync(mock_build, monkeypatch):
|
||||
"""When PYTHON_SERVER_MODE is not set, stream() should default to sync."""
|
||||
|
||||
async def mock_async_gen():
|
||||
yield "data"
|
||||
|
||||
mock_build.return_value = mock_async_gen()
|
||||
monkeypatch.delenv("PYTHON_SERVER_MODE", raising=False)
|
||||
|
||||
service = AIService()
|
||||
request = MagicMock()
|
||||
result = service.stream(request)
|
||||
|
||||
# Default should be sync mode
|
||||
assert not isinstance(result, AsyncIterator)
|
||||
assert list(result) == ["data"]
|
||||
|
||||
|
||||
# -- AIService._build_async_stream --
|
||||
|
||||
|
||||
@patch("core.services.ai_services.VercelAIAdapter")
|
||||
def test_services_ai_build_async_stream(mock_adapter_cls):
|
||||
"""_build_async_stream should build the pydantic-ai streaming pipeline."""
|
||||
|
||||
async def mock_encode():
|
||||
yield "event-data"
|
||||
|
||||
mock_run_input = MagicMock()
|
||||
mock_run_input.model_extra = None
|
||||
mock_run_input.messages = []
|
||||
mock_adapter_cls.build_run_input.return_value = mock_run_input
|
||||
|
||||
mock_adapter_instance = MagicMock()
|
||||
mock_adapter_instance.run_stream.return_value = MagicMock()
|
||||
mock_adapter_instance.encode_stream.return_value = mock_encode()
|
||||
mock_adapter_cls.return_value = mock_adapter_instance
|
||||
|
||||
service = AIService()
|
||||
request = MagicMock()
|
||||
request.META = {"HTTP_ACCEPT": "text/event-stream"}
|
||||
request.raw_body = b'{"messages": []}'
|
||||
|
||||
result = service._build_async_stream(request)
|
||||
assert isinstance(result, AsyncIterator)
|
||||
mock_adapter_cls.build_run_input.assert_called_once_with(b'{"messages": []}')
|
||||
mock_adapter_instance.run_stream.assert_called_once()
|
||||
mock_adapter_instance.encode_stream.assert_called_once()
|
||||
|
||||
|
||||
@patch("core.services.ai_services.VercelAIAdapter")
|
||||
def test_services_ai_build_async_stream_with_tool_definitions(mock_adapter_cls):
|
||||
"""_build_async_stream should build an ExternalToolset when
|
||||
toolDefinitions are present in the request."""
|
||||
|
||||
async def mock_encode():
|
||||
yield "event-data"
|
||||
|
||||
mock_run_input = MagicMock()
|
||||
mock_run_input.model_extra = {
|
||||
"toolDefinitions": {
|
||||
"myTool": {
|
||||
"description": "A tool",
|
||||
"inputSchema": {"type": "object"},
|
||||
}
|
||||
}
|
||||
}
|
||||
mock_run_input.messages = []
|
||||
mock_adapter_cls.build_run_input.return_value = mock_run_input
|
||||
|
||||
mock_adapter_instance = MagicMock()
|
||||
mock_adapter_instance.run_stream.return_value = MagicMock()
|
||||
mock_adapter_instance.encode_stream.return_value = mock_encode()
|
||||
mock_adapter_cls.return_value = mock_adapter_instance
|
||||
|
||||
service = AIService()
|
||||
request = MagicMock()
|
||||
request.META = {}
|
||||
request.raw_body = b"{}"
|
||||
|
||||
service._build_async_stream(request)
|
||||
# run_stream should have been called with a toolset
|
||||
call_kwargs = mock_adapter_instance.run_stream.call_args[1]
|
||||
assert call_kwargs["toolsets"] is not None
|
||||
assert len(call_kwargs["toolsets"]) == 1
|
||||
|
||||
|
||||
@patch("core.services.ai_services.VercelAIAdapter")
|
||||
def test_services_ai_build_async_stream_with_tool_definitions_required_system_prompt(
|
||||
mock_adapter_cls,
|
||||
):
|
||||
"""The presence of the applyDocumentOperations tool must force the addition
|
||||
of a system prompt"""
|
||||
|
||||
async def mock_encode():
|
||||
yield "event-data"
|
||||
|
||||
mock_run_input = MagicMock()
|
||||
mock_run_input.model_extra = {
|
||||
"toolDefinitions": {
|
||||
"applyDocumentOperations": {
|
||||
"description": "A tool",
|
||||
"inputSchema": {"type": "object"},
|
||||
}
|
||||
}
|
||||
}
|
||||
mock_run_input.messages = []
|
||||
mock_adapter_cls.build_run_input.return_value = mock_run_input
|
||||
|
||||
mock_adapter_instance = MagicMock()
|
||||
mock_adapter_instance.run_stream.return_value = MagicMock()
|
||||
mock_adapter_instance.encode_stream.return_value = mock_encode()
|
||||
mock_adapter_cls.return_value = mock_adapter_instance
|
||||
|
||||
service = AIService()
|
||||
request = MagicMock()
|
||||
request.META = {}
|
||||
request.raw_body = b"{}"
|
||||
|
||||
service._build_async_stream(request)
|
||||
# run_stream should have been called with a toolset
|
||||
call_kwargs = mock_adapter_instance.run_stream.call_args[1]
|
||||
assert call_kwargs["toolsets"] is not None
|
||||
assert len(call_kwargs["toolsets"]) == 1
|
||||
assert len(mock_run_input.messages) == 1
|
||||
assert mock_run_input.messages[0].id == "system-force-tool-usage"
|
||||
assert mock_run_input.messages[0].role == "system"
|
||||
assert mock_run_input.messages[0].parts[0].text == BLOCKNOTE_TOOL_STRICT_PROMPT
|
||||
|
||||
|
||||
@patch("core.services.ai_services.Agent")
|
||||
@patch("core.services.ai_services.VercelAIAdapter")
|
||||
def test_services_ai_build_async_stream_langfuse_enabled(
|
||||
mock_adapter_cls, mock_agent_cls, settings
|
||||
):
|
||||
"""When LANGFUSE_PUBLIC_KEY is set, instrument should be enabled."""
|
||||
settings.LANGFUSE_PUBLIC_KEY = "pk-test-123"
|
||||
|
||||
async def mock_encode():
|
||||
yield "data"
|
||||
|
||||
mock_run_input = MagicMock()
|
||||
mock_run_input.model_extra = None
|
||||
mock_run_input.messages = []
|
||||
mock_adapter_cls.build_run_input.return_value = mock_run_input
|
||||
|
||||
mock_adapter_instance = MagicMock()
|
||||
mock_adapter_instance.run_stream.return_value = MagicMock()
|
||||
mock_adapter_instance.encode_stream.return_value = mock_encode()
|
||||
mock_adapter_cls.return_value = mock_adapter_instance
|
||||
|
||||
service = AIService()
|
||||
request = MagicMock()
|
||||
request.META = {}
|
||||
request.raw_body = b"{}"
|
||||
|
||||
service._build_async_stream(request)
|
||||
mock_agent_cls.instrument_all.assert_called_once()
|
||||
# Agent should be created with instrument=True
|
||||
mock_agent_cls.assert_called_once()
|
||||
assert mock_agent_cls.call_args[1]["instrument"] is True
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
"""
|
||||
ASGI config for impress project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/dev/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from configurations.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "impress.settings")
|
||||
os.environ.setdefault("DJANGO_CONFIGURATION", "Development")
|
||||
os.environ.setdefault("PYTHON_SERVER_MODE", "async")
|
||||
|
||||
application = get_asgi_application()
|
||||
@@ -3,8 +3,8 @@
|
||||
"default": {
|
||||
"logo": {
|
||||
"src": "/assets/icon-docs.svg",
|
||||
"width": "54px",
|
||||
"alt": "Docs Logo",
|
||||
"style": { "width": "54px", "height": "auto" },
|
||||
"withTitle": true
|
||||
},
|
||||
"externalLinks": [
|
||||
@@ -125,38 +125,5 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"with-proconnect": false,
|
||||
"icon-banner": {
|
||||
"src": "/assets/icon-docs.svg",
|
||||
"style": {
|
||||
"width": "64px",
|
||||
"height": "auto"
|
||||
},
|
||||
"alt": ""
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"logo": {},
|
||||
"icon": {
|
||||
"src": "/assets/icon-docs.svg",
|
||||
"style": {
|
||||
"width": "32px",
|
||||
"height": "auto"
|
||||
},
|
||||
"alt": "",
|
||||
"withTitle": true
|
||||
}
|
||||
},
|
||||
"favicon": {
|
||||
"light": {
|
||||
"href": "/assets/favicon-light.png",
|
||||
"type": "image/png"
|
||||
},
|
||||
"dark": {
|
||||
"href": "/assets/favicon-dark.png",
|
||||
"type": "image/png"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,7 +326,6 @@ class Base(Configuration):
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"core.middleware.ForceSessionMiddleware",
|
||||
"core.middleware.SaveRawBodyMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"dockerflow.django.middleware.DockerflowMiddleware",
|
||||
"csp.middleware.CSPMiddleware",
|
||||
@@ -688,48 +687,24 @@ class Base(Configuration):
|
||||
default=True, environ_name="ALLOW_LOGOUT_GET_METHOD", environ_prefix=None
|
||||
)
|
||||
|
||||
# AI settings
|
||||
# AI service
|
||||
AI_FEATURE_ENABLED = values.BooleanValue(
|
||||
default=False, environ_name="AI_FEATURE_ENABLED", environ_prefix=None
|
||||
)
|
||||
AI_API_KEY = SecretFileValue(None, environ_name="AI_API_KEY", environ_prefix=None)
|
||||
AI_BASE_URL = values.Value(None, environ_name="AI_BASE_URL", environ_prefix=None)
|
||||
AI_MODEL = values.Value(None, environ_name="AI_MODEL", environ_prefix=None)
|
||||
AI_ALLOW_REACH_FROM = values.Value(
|
||||
choices=("public", "authenticated", "restricted"),
|
||||
default="authenticated",
|
||||
environ_name="AI_ALLOW_REACH_FROM",
|
||||
environ_prefix=None,
|
||||
)
|
||||
AI_API_KEY = SecretFileValue(None, environ_name="AI_API_KEY", environ_prefix=None)
|
||||
AI_BASE_URL = values.Value(None, environ_name="AI_BASE_URL", environ_prefix=None)
|
||||
AI_BOT = values.DictValue(
|
||||
default={
|
||||
"name": _("Docs AI"),
|
||||
"color": "#8bc6ff",
|
||||
},
|
||||
environ_name="AI_BOT",
|
||||
environ_prefix=None,
|
||||
)
|
||||
AI_DOCUMENT_RATE_THROTTLE_RATES = {
|
||||
"minute": 5,
|
||||
"hour": 100,
|
||||
"day": 500,
|
||||
}
|
||||
# Master settings to enable AI features, if you set it to False,
|
||||
# all AI features will be disabled even if the other settings are enabled.
|
||||
AI_FEATURE_ENABLED = values.BooleanValue(
|
||||
default=False, environ_name="AI_FEATURE_ENABLED", environ_prefix=None
|
||||
)
|
||||
# Far better UI but more flaky for the moment
|
||||
# ⚠️ AGPL license, be sure to comply with the Blocknote license
|
||||
# if you enable it (https://www.blocknotejs.org/)
|
||||
AI_FEATURE_BLOCKNOTE_ENABLED = values.BooleanValue(
|
||||
default=False, environ_name="AI_FEATURE_BLOCKNOTE_ENABLED", environ_prefix=None
|
||||
)
|
||||
# UI with less features but more stable
|
||||
# MIT friendly license, you can enable it without worrying about the license
|
||||
AI_FEATURE_LEGACY_ENABLED = values.BooleanValue(
|
||||
default=True, environ_name="AI_FEATURE_LEGACY_ENABLED", environ_prefix=None
|
||||
)
|
||||
AI_MODEL = values.Value(None, environ_name="AI_MODEL", environ_prefix=None)
|
||||
AI_VERCEL_SDK_VERSION = values.IntegerValue(
|
||||
6, environ_name="AI_VERCEL_SDK_VERSION", environ_prefix=None
|
||||
)
|
||||
AI_USER_RATE_THROTTLE_RATES = {
|
||||
"minute": 3,
|
||||
"hour": 50,
|
||||
@@ -761,7 +736,7 @@ class Base(Configuration):
|
||||
|
||||
# Imported file settings
|
||||
CONVERSION_FILE_MAX_SIZE = values.IntegerValue(
|
||||
20 * MB,
|
||||
20 * MB, # 10MB
|
||||
environ_name="CONVERSION_FILE_MAX_SIZE",
|
||||
environ_prefix=None,
|
||||
)
|
||||
@@ -909,12 +884,7 @@ class Base(Configuration):
|
||||
USER_RECONCILIATION_FORM_URL = values.Value(
|
||||
None, environ_name="USER_RECONCILIATION_FORM_URL", environ_prefix=None
|
||||
)
|
||||
USER_ONBOARDING_DOCUMENTS = values.ListValue(
|
||||
[], environ_name="USER_ONBOARDING_DOCUMENTS", environ_prefix=None
|
||||
)
|
||||
USER_ONBOARDING_SANDBOX_DOCUMENT = values.Value(
|
||||
None, environ_name="USER_ONBOARDING_SANDBOX_DOCUMENT", environ_prefix=None
|
||||
)
|
||||
|
||||
# Marketing and communication settings
|
||||
SIGNUP_NEW_USER_TO_MARKETING_EMAIL = values.BooleanValue(
|
||||
False,
|
||||
|
||||
@@ -13,6 +13,5 @@ from configurations.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "impress.settings")
|
||||
os.environ.setdefault("DJANGO_CONFIGURATION", "Development")
|
||||
os.environ.setdefault("PYTHON_SERVER_MODE", "sync")
|
||||
|
||||
application = get_wsgi_application()
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lasuite-docs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
|
||||
"PO-Revision-Date: 2026-02-27 08:24\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0000\n"
|
||||
"PO-Revision-Date: 2026-01-28 20:12\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Breton\n"
|
||||
"Language: br_FR\n"
|
||||
@@ -17,65 +17,57 @@ msgstr ""
|
||||
"X-Crowdin-File: backend-impress.pot\n"
|
||||
"X-Crowdin-File-ID: 18\n"
|
||||
|
||||
#: build/lib/core/admin.py:30 core/admin.py:30
|
||||
#: build/lib/core/admin.py:28 core/admin.py:28
|
||||
msgid "Personal info"
|
||||
msgstr "Titouroù personel"
|
||||
|
||||
#: build/lib/core/admin.py:43 build/lib/core/admin.py:161 core/admin.py:43
|
||||
#: core/admin.py:161
|
||||
#: build/lib/core/admin.py:41 build/lib/core/admin.py:121 core/admin.py:41
|
||||
#: core/admin.py:121
|
||||
msgid "Permissions"
|
||||
msgstr "Aotreoù"
|
||||
|
||||
#: build/lib/core/admin.py:55 core/admin.py:55
|
||||
#: build/lib/core/admin.py:53 core/admin.py:53
|
||||
msgid "Important dates"
|
||||
msgstr "Deiziadoù a-bouez"
|
||||
|
||||
#: build/lib/core/admin.py:112 core/admin.py:112
|
||||
msgid "Import job created and queued."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:116 core/admin.py:116
|
||||
msgid "Process selected user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:171 core/admin.py:171
|
||||
#: build/lib/core/admin.py:131 core/admin.py:131
|
||||
msgid "Tree structure"
|
||||
msgstr "Gwezennadur"
|
||||
|
||||
#: build/lib/core/api/filters.py:48 core/api/filters.py:48
|
||||
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||
msgid "Title"
|
||||
msgstr "Titl"
|
||||
|
||||
#: build/lib/core/api/filters.py:62 core/api/filters.py:62
|
||||
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||
msgid "Creator is me"
|
||||
msgstr "Me eo an aozer"
|
||||
|
||||
#: build/lib/core/api/filters.py:65 core/api/filters.py:65
|
||||
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||
msgid "Masked"
|
||||
msgstr "Kuzhet"
|
||||
|
||||
#: build/lib/core/api/filters.py:68 core/api/filters.py:68
|
||||
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
|
||||
msgid "Favorite"
|
||||
msgstr "Sinedoù"
|
||||
|
||||
#: build/lib/core/api/serializers.py:513 core/api/serializers.py:513
|
||||
#: build/lib/core/api/serializers.py:505 core/api/serializers.py:505
|
||||
msgid "A new document was created on your behalf!"
|
||||
msgstr "Ur restr nevez a zo bet krouet ganeoc'h!"
|
||||
|
||||
#: build/lib/core/api/serializers.py:517 core/api/serializers.py:517
|
||||
#: build/lib/core/api/serializers.py:509 core/api/serializers.py:509
|
||||
msgid "You have been granted ownership of a new document:"
|
||||
msgstr "C'hwi zo bet disklaeriet perc'henn ur restr nevez:"
|
||||
|
||||
#: build/lib/core/api/serializers.py:553 core/api/serializers.py:553
|
||||
#: build/lib/core/api/serializers.py:545 core/api/serializers.py:545
|
||||
msgid "This field is required."
|
||||
msgstr "Ar vaezienn-mañ a zo rekis."
|
||||
|
||||
#: build/lib/core/api/serializers.py:564 core/api/serializers.py:564
|
||||
#: build/lib/core/api/serializers.py:556 core/api/serializers.py:556
|
||||
#, python-format
|
||||
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
|
||||
#: build/lib/core/api/viewsets.py:1122 core/api/viewsets.py:1122
|
||||
#, python-brace-format
|
||||
msgid "copy of {title}"
|
||||
msgstr "eilenn {title}"
|
||||
@@ -143,374 +135,262 @@ msgstr "Kleiz"
|
||||
msgid "Right"
|
||||
msgstr "Dehoù"
|
||||
|
||||
#: build/lib/core/models.py:80 core/models.py:80
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
msgid "id"
|
||||
msgstr "id"
|
||||
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
#: build/lib/core/models.py:82 core/models.py:82
|
||||
msgid "primary key for the record as UUID"
|
||||
msgstr "alc'hwez kentañ evit an enrollañ evel UIID"
|
||||
|
||||
#: build/lib/core/models.py:87 core/models.py:87
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
msgid "created on"
|
||||
msgstr "krouet d'ar/al"
|
||||
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
#: build/lib/core/models.py:89 core/models.py:89
|
||||
msgid "date and time at which a record was created"
|
||||
msgstr "deiziad hag eurvezh krouidigezh an enrolladenn"
|
||||
|
||||
#: build/lib/core/models.py:93 core/models.py:93
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
msgid "updated on"
|
||||
msgstr "hizivaet d'ar/al"
|
||||
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
#: build/lib/core/models.py:95 core/models.py:95
|
||||
msgid "date and time at which a record was last updated"
|
||||
msgstr "deiziad hag eurvezh m'eo bet hizivaet an enrolladenn"
|
||||
|
||||
#: build/lib/core/models.py:130 core/models.py:130
|
||||
#: build/lib/core/models.py:131 core/models.py:131
|
||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||
msgstr "N'hon eus kavet implijer ebet gant an isstrollad-mañ met ar postel a zo liammet ouzh un implijer enrollet."
|
||||
|
||||
#: build/lib/core/models.py:141 core/models.py:141
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
msgid "sub"
|
||||
msgstr "isstrollad"
|
||||
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
#: build/lib/core/models.py:143 core/models.py:143
|
||||
msgid "Required. 255 characters or fewer. ASCII characters only."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:150 core/models.py:150
|
||||
#: build/lib/core/models.py:151 core/models.py:151
|
||||
msgid "full name"
|
||||
msgstr "anv klok"
|
||||
|
||||
#: build/lib/core/models.py:152 core/models.py:152
|
||||
#: build/lib/core/models.py:153 core/models.py:153
|
||||
msgid "short name"
|
||||
msgstr "anv berr"
|
||||
|
||||
#: build/lib/core/models.py:155 core/models.py:155
|
||||
#: build/lib/core/models.py:156 core/models.py:156
|
||||
msgid "identity email address"
|
||||
msgstr "postel identelezh"
|
||||
|
||||
#: build/lib/core/models.py:160 core/models.py:160
|
||||
#: build/lib/core/models.py:161 core/models.py:161
|
||||
msgid "admin email address"
|
||||
msgstr "postel ar merour"
|
||||
|
||||
#: build/lib/core/models.py:167 core/models.py:167
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
msgid "language"
|
||||
msgstr "yezh"
|
||||
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
#: build/lib/core/models.py:169 core/models.py:169
|
||||
msgid "The language in which the user wants to see the interface."
|
||||
msgstr "Ar yezh a vo implijet evit etrefas an implijer."
|
||||
|
||||
#: build/lib/core/models.py:176 core/models.py:176
|
||||
#: build/lib/core/models.py:177 core/models.py:177
|
||||
msgid "The timezone in which the user wants to see times."
|
||||
msgstr "Ar gwerzhid-eur a vo implijet evit etrefas an implijer."
|
||||
|
||||
#: build/lib/core/models.py:179 core/models.py:179
|
||||
#: build/lib/core/models.py:180 core/models.py:180
|
||||
msgid "device"
|
||||
msgstr "trevnad"
|
||||
|
||||
#: build/lib/core/models.py:181 core/models.py:181
|
||||
#: build/lib/core/models.py:182 core/models.py:182
|
||||
msgid "Whether the user is a device or a real user."
|
||||
msgstr "Pe vefe an implijer un aparailh pe un implijer gwirion."
|
||||
|
||||
#: build/lib/core/models.py:184 core/models.py:184
|
||||
#: build/lib/core/models.py:185 core/models.py:185
|
||||
msgid "staff status"
|
||||
msgstr "statud ar skipailh"
|
||||
|
||||
#: build/lib/core/models.py:186 core/models.py:186
|
||||
#: build/lib/core/models.py:187 core/models.py:187
|
||||
msgid "Whether the user can log into this admin site."
|
||||
msgstr "Ma c'hall an implijer kevreañ ouzh al lec'hienn verañ-mañ."
|
||||
|
||||
#: build/lib/core/models.py:189 core/models.py:189
|
||||
#: build/lib/core/models.py:190 core/models.py:190
|
||||
msgid "active"
|
||||
msgstr "oberiant"
|
||||
|
||||
#: build/lib/core/models.py:192 core/models.py:192
|
||||
#: build/lib/core/models.py:193 core/models.py:193
|
||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||
msgstr "Ma rank bezañ tretet an implijer-mañ evel oberiant. Diziuzit an dra-mañ e-plas dilemel kontoù."
|
||||
|
||||
#: build/lib/core/models.py:204 core/models.py:204
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
msgid "user"
|
||||
msgstr "implijer"
|
||||
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
#: build/lib/core/models.py:206 core/models.py:206
|
||||
msgid "users"
|
||||
msgstr "implijerien"
|
||||
|
||||
#: build/lib/core/models.py:360 core/models.py:360
|
||||
msgid "Active email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:361 core/models.py:361
|
||||
msgid "Email address to deactivate"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:388 core/models.py:388
|
||||
msgid "Unique ID in the source file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:394 build/lib/core/models.py:692 core/models.py:394
|
||||
#: core/models.py:692
|
||||
msgid "Pending"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:395 core/models.py:395
|
||||
msgid "Ready"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:396 build/lib/core/models.py:694 core/models.py:396
|
||||
#: core/models.py:694
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:397 build/lib/core/models.py:695 core/models.py:397
|
||||
#: core/models.py:695
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:405 core/models.py:405
|
||||
msgid "user reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:406 core/models.py:406
|
||||
msgid "user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:644 core/models.py:644
|
||||
msgid "You have requested a reconciliation of your user accounts on Docs.\n"
|
||||
" To confirm that you are the one who initiated the request\n"
|
||||
" and that this email belongs to you:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:650 core/models.py:650
|
||||
msgid "Confirm by clicking the link to start the reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:655 build/lib/core/models.py:761 core/models.py:655
|
||||
#: core/models.py:761
|
||||
msgid "Click here"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:656 core/models.py:656
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:667 core/models.py:667
|
||||
msgid "Your reconciliation request has been processed.\n"
|
||||
" New documents are likely associated with your account:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:672 core/models.py:672
|
||||
msgid "Your accounts have been merged"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:677 core/models.py:677
|
||||
msgid "Click here to see"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:678 core/models.py:678
|
||||
msgid "See my documents"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:688 core/models.py:688
|
||||
msgid "CSV file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:693 core/models.py:693
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:703 core/models.py:703
|
||||
msgid "user reconciliation CSV import"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:704 core/models.py:704
|
||||
msgid "user reconciliation CSV imports"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:748 core/models.py:748
|
||||
#, python-brace-format
|
||||
msgid "Your request for reconciliation was unsuccessful.\n"
|
||||
" Reconciliation failed for the following email addresses:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Please check for typos.\n"
|
||||
" You can submit another request with the valid email addresses."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:756 core/models.py:756
|
||||
msgid "Reconciliation of your Docs accounts not completed"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:762 core/models.py:762
|
||||
msgid "Make a new request"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:861 core/models.py:861
|
||||
#: build/lib/core/models.py:362 core/models.py:362
|
||||
msgid "title"
|
||||
msgstr "titl"
|
||||
|
||||
#: build/lib/core/models.py:862 core/models.py:862
|
||||
#: build/lib/core/models.py:363 core/models.py:363
|
||||
msgid "excerpt"
|
||||
msgstr "bomm"
|
||||
|
||||
#: build/lib/core/models.py:911 core/models.py:911
|
||||
#: build/lib/core/models.py:412 core/models.py:412
|
||||
msgid "Document"
|
||||
msgstr "Restr"
|
||||
|
||||
#: build/lib/core/models.py:912 core/models.py:912
|
||||
#: build/lib/core/models.py:413 core/models.py:413
|
||||
msgid "Documents"
|
||||
msgstr "Restroù"
|
||||
|
||||
#: build/lib/core/models.py:924 build/lib/core/models.py:1328
|
||||
#: core/models.py:924 core/models.py:1328
|
||||
#: build/lib/core/models.py:425 build/lib/core/models.py:828 core/models.py:425
|
||||
#: core/models.py:828
|
||||
msgid "Untitled Document"
|
||||
msgstr "Restr hep titl"
|
||||
|
||||
#: build/lib/core/models.py:1329 core/models.py:1329
|
||||
#: build/lib/core/models.py:829 core/models.py:829
|
||||
msgid "Open"
|
||||
msgstr "Digeriñ"
|
||||
|
||||
#: build/lib/core/models.py:1364 core/models.py:1364
|
||||
#: build/lib/core/models.py:864 core/models.py:864
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you!"
|
||||
msgstr "{name} en deus rannet ur restr ganeoc'h!"
|
||||
|
||||
#: build/lib/core/models.py:1368 core/models.py:1368
|
||||
#: build/lib/core/models.py:868 core/models.py:868
|
||||
#, python-brace-format
|
||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||
msgstr "{name} en deus pedet ac'hanoc'h gant ar rol \"{role}\" war ar restr da-heul:"
|
||||
|
||||
#: build/lib/core/models.py:1374 core/models.py:1374
|
||||
#: build/lib/core/models.py:874 core/models.py:874
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you: {title}"
|
||||
msgstr "{name} en deus rannet ur restr ganeoc'h: {title}"
|
||||
|
||||
#: build/lib/core/models.py:1475 core/models.py:1475
|
||||
#: build/lib/core/models.py:975 core/models.py:975
|
||||
msgid "Document/user link trace"
|
||||
msgstr "Roud liamm ar restr/an implijer"
|
||||
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
#: build/lib/core/models.py:976 core/models.py:976
|
||||
msgid "Document/user link traces"
|
||||
msgstr "Roudoù liamm ar restr/an implijer"
|
||||
|
||||
#: build/lib/core/models.py:1482 core/models.py:1482
|
||||
#: build/lib/core/models.py:982 core/models.py:982
|
||||
msgid "A link trace already exists for this document/user."
|
||||
msgstr "Ur roud liamm a zo dija evit an restr/an implijer."
|
||||
|
||||
#: build/lib/core/models.py:1505 core/models.py:1505
|
||||
#: build/lib/core/models.py:1005 core/models.py:1005
|
||||
msgid "Document favorite"
|
||||
msgstr "Restr muiañ-karet"
|
||||
|
||||
#: build/lib/core/models.py:1506 core/models.py:1506
|
||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
||||
msgid "Document favorites"
|
||||
msgstr "Restroù muiañ-karet"
|
||||
|
||||
#: build/lib/core/models.py:1512 core/models.py:1512
|
||||
#: build/lib/core/models.py:1012 core/models.py:1012
|
||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||
msgstr "Ar restr-mañ a zo ur restr muiañ karet gant an implijer-mañ."
|
||||
|
||||
#: build/lib/core/models.py:1534 core/models.py:1534
|
||||
#: build/lib/core/models.py:1034 core/models.py:1034
|
||||
msgid "Document/user relation"
|
||||
msgstr "Liamm restr/implijer"
|
||||
|
||||
#: build/lib/core/models.py:1535 core/models.py:1535
|
||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
||||
msgid "Document/user relations"
|
||||
msgstr "Liammoù restr/implijer"
|
||||
|
||||
#: build/lib/core/models.py:1541 core/models.py:1541
|
||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
||||
msgid "This user is already in this document."
|
||||
msgstr "An implijer-mañ a zo dija er restr-mañ."
|
||||
|
||||
#: build/lib/core/models.py:1547 core/models.py:1547
|
||||
#: build/lib/core/models.py:1047 core/models.py:1047
|
||||
msgid "This team is already in this document."
|
||||
msgstr "Ar skipailh-mañ a zo dija en restr-mañ."
|
||||
|
||||
#: build/lib/core/models.py:1553 core/models.py:1553
|
||||
#: build/lib/core/models.py:1053 core/models.py:1053
|
||||
msgid "Either user or team must be set, not both."
|
||||
msgstr "An implijer pe ar skipailh a rank bezañ termenet, ket an daou avat."
|
||||
|
||||
#: build/lib/core/models.py:1704 core/models.py:1704
|
||||
#: build/lib/core/models.py:1204 core/models.py:1204
|
||||
msgid "Document ask for access"
|
||||
msgstr "Goulenn tizhout ar restr"
|
||||
|
||||
#: build/lib/core/models.py:1705 core/models.py:1705
|
||||
#: build/lib/core/models.py:1205 core/models.py:1205
|
||||
msgid "Document ask for accesses"
|
||||
msgstr "Goulennoù tizhout ar restr"
|
||||
|
||||
#: build/lib/core/models.py:1711 core/models.py:1711
|
||||
#: build/lib/core/models.py:1211 core/models.py:1211
|
||||
msgid "This user has already asked for access to this document."
|
||||
msgstr "An implijer en deus goulennet tizhout ar restr-mañ."
|
||||
|
||||
#: build/lib/core/models.py:1768 core/models.py:1768
|
||||
#: build/lib/core/models.py:1268 core/models.py:1268
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to a document!"
|
||||
msgstr "{name} en defe c'hoant da dizhout ar restr-mañ!"
|
||||
|
||||
#: build/lib/core/models.py:1772 core/models.py:1772
|
||||
#: build/lib/core/models.py:1272 core/models.py:1272
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to the following document:"
|
||||
msgstr "{name} en defe c'hoant da dizhout ar restr da-heul:"
|
||||
|
||||
#: build/lib/core/models.py:1778 core/models.py:1778
|
||||
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||
#, python-brace-format
|
||||
msgid "{name} is asking for access to the document: {title}"
|
||||
msgstr "{name} en defe c'hoant da dizhout ar restr: {title}"
|
||||
|
||||
#: build/lib/core/models.py:1820 core/models.py:1820
|
||||
#: build/lib/core/models.py:1320 core/models.py:1320
|
||||
msgid "Thread"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1821 core/models.py:1821
|
||||
#: build/lib/core/models.py:1321 core/models.py:1321
|
||||
msgid "Threads"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1824 build/lib/core/models.py:1876
|
||||
#: core/models.py:1824 core/models.py:1876
|
||||
#: build/lib/core/models.py:1324 build/lib/core/models.py:1376
|
||||
#: core/models.py:1324 core/models.py:1376
|
||||
msgid "Anonymous"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1871 core/models.py:1871
|
||||
#: build/lib/core/models.py:1371 core/models.py:1371
|
||||
msgid "Comment"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1872 core/models.py:1872
|
||||
#: build/lib/core/models.py:1372 core/models.py:1372
|
||||
msgid "Comments"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1921 core/models.py:1921
|
||||
#: build/lib/core/models.py:1421 core/models.py:1421
|
||||
msgid "This emoji has already been reacted to this comment."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1925 core/models.py:1925
|
||||
#: build/lib/core/models.py:1425 core/models.py:1425
|
||||
msgid "Reaction"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1926 core/models.py:1926
|
||||
#: build/lib/core/models.py:1426 core/models.py:1426
|
||||
msgid "Reactions"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1936 core/models.py:1936
|
||||
#: build/lib/core/models.py:1436 core/models.py:1436
|
||||
msgid "email address"
|
||||
msgstr "postel"
|
||||
|
||||
#: build/lib/core/models.py:1955 core/models.py:1955
|
||||
#: build/lib/core/models.py:1455 core/models.py:1455
|
||||
msgid "Document invitation"
|
||||
msgstr "Pedadenn d'ur restr"
|
||||
|
||||
#: build/lib/core/models.py:1956 core/models.py:1956
|
||||
#: build/lib/core/models.py:1456 core/models.py:1456
|
||||
msgid "Document invitations"
|
||||
msgstr "Pedadennoù d'ur restr"
|
||||
|
||||
#: build/lib/core/models.py:1976 core/models.py:1976
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
msgid "This email is already associated to a registered user."
|
||||
msgstr "Ar postel-mañ a zo liammet ouzh un implijer enskrivet."
|
||||
|
||||
#: build/lib/impress/settings.py:702 impress/settings.py:702
|
||||
msgid "Docs AI"
|
||||
msgstr ""
|
||||
|
||||
#: core/templates/mail/html/template.html:153
|
||||
#: core/templates/mail/text/template.txt:3
|
||||
msgid "Logo email"
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lasuite-docs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
|
||||
"PO-Revision-Date: 2026-02-27 08:24\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0000\n"
|
||||
"PO-Revision-Date: 2026-01-28 20:12\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de_DE\n"
|
||||
@@ -17,65 +17,57 @@ msgstr ""
|
||||
"X-Crowdin-File: backend-impress.pot\n"
|
||||
"X-Crowdin-File-ID: 18\n"
|
||||
|
||||
#: build/lib/core/admin.py:30 core/admin.py:30
|
||||
#: build/lib/core/admin.py:28 core/admin.py:28
|
||||
msgid "Personal info"
|
||||
msgstr "Persönliche Daten"
|
||||
|
||||
#: build/lib/core/admin.py:43 build/lib/core/admin.py:161 core/admin.py:43
|
||||
#: core/admin.py:161
|
||||
#: build/lib/core/admin.py:41 build/lib/core/admin.py:121 core/admin.py:41
|
||||
#: core/admin.py:121
|
||||
msgid "Permissions"
|
||||
msgstr "Berechtigungen"
|
||||
|
||||
#: build/lib/core/admin.py:55 core/admin.py:55
|
||||
#: build/lib/core/admin.py:53 core/admin.py:53
|
||||
msgid "Important dates"
|
||||
msgstr "Wichtige Daten"
|
||||
|
||||
#: build/lib/core/admin.py:112 core/admin.py:112
|
||||
msgid "Import job created and queued."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:116 core/admin.py:116
|
||||
msgid "Process selected user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:171 core/admin.py:171
|
||||
#: build/lib/core/admin.py:131 core/admin.py:131
|
||||
msgid "Tree structure"
|
||||
msgstr "Baumstruktur"
|
||||
|
||||
#: build/lib/core/api/filters.py:48 core/api/filters.py:48
|
||||
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||
msgid "Title"
|
||||
msgstr "Titel"
|
||||
|
||||
#: build/lib/core/api/filters.py:62 core/api/filters.py:62
|
||||
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||
msgid "Creator is me"
|
||||
msgstr "Ersteller bin ich"
|
||||
|
||||
#: build/lib/core/api/filters.py:65 core/api/filters.py:65
|
||||
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||
msgid "Masked"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/filters.py:68 core/api/filters.py:68
|
||||
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
|
||||
msgid "Favorite"
|
||||
msgstr "Favorit"
|
||||
|
||||
#: build/lib/core/api/serializers.py:513 core/api/serializers.py:513
|
||||
#: build/lib/core/api/serializers.py:505 core/api/serializers.py:505
|
||||
msgid "A new document was created on your behalf!"
|
||||
msgstr "Ein neues Dokument wurde in Ihrem Namen erstellt!"
|
||||
|
||||
#: build/lib/core/api/serializers.py:517 core/api/serializers.py:517
|
||||
#: build/lib/core/api/serializers.py:509 core/api/serializers.py:509
|
||||
msgid "You have been granted ownership of a new document:"
|
||||
msgstr "Sie sind Besitzer eines neuen Dokuments:"
|
||||
|
||||
#: build/lib/core/api/serializers.py:553 core/api/serializers.py:553
|
||||
#: build/lib/core/api/serializers.py:545 core/api/serializers.py:545
|
||||
msgid "This field is required."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/serializers.py:564 core/api/serializers.py:564
|
||||
#: build/lib/core/api/serializers.py:556 core/api/serializers.py:556
|
||||
#, python-format
|
||||
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
|
||||
#: build/lib/core/api/viewsets.py:1122 core/api/viewsets.py:1122
|
||||
#, python-brace-format
|
||||
msgid "copy of {title}"
|
||||
msgstr "Kopie von {title}"
|
||||
@@ -143,374 +135,262 @@ msgstr "Links"
|
||||
msgid "Right"
|
||||
msgstr "Rechts"
|
||||
|
||||
#: build/lib/core/models.py:80 core/models.py:80
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
msgid "id"
|
||||
msgstr "id"
|
||||
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
#: build/lib/core/models.py:82 core/models.py:82
|
||||
msgid "primary key for the record as UUID"
|
||||
msgstr "primärer Schlüssel für den Datensatz als UUID"
|
||||
|
||||
#: build/lib/core/models.py:87 core/models.py:87
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
msgid "created on"
|
||||
msgstr "Erstellt"
|
||||
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
#: build/lib/core/models.py:89 core/models.py:89
|
||||
msgid "date and time at which a record was created"
|
||||
msgstr "Datum und Uhrzeit, an dem ein Datensatz erstellt wurde"
|
||||
|
||||
#: build/lib/core/models.py:93 core/models.py:93
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
msgid "updated on"
|
||||
msgstr "Aktualisiert"
|
||||
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
#: build/lib/core/models.py:95 core/models.py:95
|
||||
msgid "date and time at which a record was last updated"
|
||||
msgstr "Datum und Uhrzeit, an dem zuletzt aktualisiert wurde"
|
||||
|
||||
#: build/lib/core/models.py:130 core/models.py:130
|
||||
#: build/lib/core/models.py:131 core/models.py:131
|
||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||
msgstr "Wir konnten keinen Benutzer mit diesem Abo finden, aber die E-Mail-Adresse ist bereits einem registrierten Benutzer zugeordnet."
|
||||
|
||||
#: build/lib/core/models.py:141 core/models.py:141
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
msgid "sub"
|
||||
msgstr "unter"
|
||||
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
#: build/lib/core/models.py:143 core/models.py:143
|
||||
msgid "Required. 255 characters or fewer. ASCII characters only."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:150 core/models.py:150
|
||||
#: build/lib/core/models.py:151 core/models.py:151
|
||||
msgid "full name"
|
||||
msgstr "Name"
|
||||
|
||||
#: build/lib/core/models.py:152 core/models.py:152
|
||||
#: build/lib/core/models.py:153 core/models.py:153
|
||||
msgid "short name"
|
||||
msgstr "Kurzbezeichnung"
|
||||
|
||||
#: build/lib/core/models.py:155 core/models.py:155
|
||||
#: build/lib/core/models.py:156 core/models.py:156
|
||||
msgid "identity email address"
|
||||
msgstr "Identitäts-E-Mail-Adresse"
|
||||
|
||||
#: build/lib/core/models.py:160 core/models.py:160
|
||||
#: build/lib/core/models.py:161 core/models.py:161
|
||||
msgid "admin email address"
|
||||
msgstr "Admin E-Mail-Adresse"
|
||||
|
||||
#: build/lib/core/models.py:167 core/models.py:167
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
msgid "language"
|
||||
msgstr "Sprache"
|
||||
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
#: build/lib/core/models.py:169 core/models.py:169
|
||||
msgid "The language in which the user wants to see the interface."
|
||||
msgstr "Die Sprache, in der der Benutzer die Benutzeroberfläche sehen möchte."
|
||||
|
||||
#: build/lib/core/models.py:176 core/models.py:176
|
||||
#: build/lib/core/models.py:177 core/models.py:177
|
||||
msgid "The timezone in which the user wants to see times."
|
||||
msgstr "Die Zeitzone, in der der Nutzer Zeiten sehen möchte."
|
||||
|
||||
#: build/lib/core/models.py:179 core/models.py:179
|
||||
#: build/lib/core/models.py:180 core/models.py:180
|
||||
msgid "device"
|
||||
msgstr "Gerät"
|
||||
|
||||
#: build/lib/core/models.py:181 core/models.py:181
|
||||
#: build/lib/core/models.py:182 core/models.py:182
|
||||
msgid "Whether the user is a device or a real user."
|
||||
msgstr "Ob der Benutzer ein Gerät oder ein echter Benutzer ist."
|
||||
|
||||
#: build/lib/core/models.py:184 core/models.py:184
|
||||
#: build/lib/core/models.py:185 core/models.py:185
|
||||
msgid "staff status"
|
||||
msgstr "Status des Teammitgliedes"
|
||||
|
||||
#: build/lib/core/models.py:186 core/models.py:186
|
||||
#: build/lib/core/models.py:187 core/models.py:187
|
||||
msgid "Whether the user can log into this admin site."
|
||||
msgstr "Gibt an, ob der Benutzer sich in diese Admin-Seite einloggen kann."
|
||||
|
||||
#: build/lib/core/models.py:189 core/models.py:189
|
||||
#: build/lib/core/models.py:190 core/models.py:190
|
||||
msgid "active"
|
||||
msgstr "aktiviert"
|
||||
|
||||
#: build/lib/core/models.py:192 core/models.py:192
|
||||
#: build/lib/core/models.py:193 core/models.py:193
|
||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||
msgstr "Ob dieser Benutzer als aktiviert behandelt werden soll. Deaktivieren Sie diese Option, anstatt Konten zu löschen."
|
||||
|
||||
#: build/lib/core/models.py:204 core/models.py:204
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
msgid "user"
|
||||
msgstr "Benutzer"
|
||||
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
#: build/lib/core/models.py:206 core/models.py:206
|
||||
msgid "users"
|
||||
msgstr "Benutzer"
|
||||
|
||||
#: build/lib/core/models.py:360 core/models.py:360
|
||||
msgid "Active email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:361 core/models.py:361
|
||||
msgid "Email address to deactivate"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:388 core/models.py:388
|
||||
msgid "Unique ID in the source file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:394 build/lib/core/models.py:692 core/models.py:394
|
||||
#: core/models.py:692
|
||||
msgid "Pending"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:395 core/models.py:395
|
||||
msgid "Ready"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:396 build/lib/core/models.py:694 core/models.py:396
|
||||
#: core/models.py:694
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:397 build/lib/core/models.py:695 core/models.py:397
|
||||
#: core/models.py:695
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:405 core/models.py:405
|
||||
msgid "user reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:406 core/models.py:406
|
||||
msgid "user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:644 core/models.py:644
|
||||
msgid "You have requested a reconciliation of your user accounts on Docs.\n"
|
||||
" To confirm that you are the one who initiated the request\n"
|
||||
" and that this email belongs to you:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:650 core/models.py:650
|
||||
msgid "Confirm by clicking the link to start the reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:655 build/lib/core/models.py:761 core/models.py:655
|
||||
#: core/models.py:761
|
||||
msgid "Click here"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:656 core/models.py:656
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:667 core/models.py:667
|
||||
msgid "Your reconciliation request has been processed.\n"
|
||||
" New documents are likely associated with your account:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:672 core/models.py:672
|
||||
msgid "Your accounts have been merged"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:677 core/models.py:677
|
||||
msgid "Click here to see"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:678 core/models.py:678
|
||||
msgid "See my documents"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:688 core/models.py:688
|
||||
msgid "CSV file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:693 core/models.py:693
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:703 core/models.py:703
|
||||
msgid "user reconciliation CSV import"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:704 core/models.py:704
|
||||
msgid "user reconciliation CSV imports"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:748 core/models.py:748
|
||||
#, python-brace-format
|
||||
msgid "Your request for reconciliation was unsuccessful.\n"
|
||||
" Reconciliation failed for the following email addresses:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Please check for typos.\n"
|
||||
" You can submit another request with the valid email addresses."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:756 core/models.py:756
|
||||
msgid "Reconciliation of your Docs accounts not completed"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:762 core/models.py:762
|
||||
msgid "Make a new request"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:861 core/models.py:861
|
||||
#: build/lib/core/models.py:362 core/models.py:362
|
||||
msgid "title"
|
||||
msgstr "Titel"
|
||||
|
||||
#: build/lib/core/models.py:862 core/models.py:862
|
||||
#: build/lib/core/models.py:363 core/models.py:363
|
||||
msgid "excerpt"
|
||||
msgstr "Auszug"
|
||||
|
||||
#: build/lib/core/models.py:911 core/models.py:911
|
||||
#: build/lib/core/models.py:412 core/models.py:412
|
||||
msgid "Document"
|
||||
msgstr "Dokument"
|
||||
|
||||
#: build/lib/core/models.py:912 core/models.py:912
|
||||
#: build/lib/core/models.py:413 core/models.py:413
|
||||
msgid "Documents"
|
||||
msgstr "Dokumente"
|
||||
|
||||
#: build/lib/core/models.py:924 build/lib/core/models.py:1328
|
||||
#: core/models.py:924 core/models.py:1328
|
||||
#: build/lib/core/models.py:425 build/lib/core/models.py:828 core/models.py:425
|
||||
#: core/models.py:828
|
||||
msgid "Untitled Document"
|
||||
msgstr "Unbenanntes Dokument"
|
||||
|
||||
#: build/lib/core/models.py:1329 core/models.py:1329
|
||||
#: build/lib/core/models.py:829 core/models.py:829
|
||||
msgid "Open"
|
||||
msgstr "Öffnen"
|
||||
|
||||
#: build/lib/core/models.py:1364 core/models.py:1364
|
||||
#: build/lib/core/models.py:864 core/models.py:864
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you!"
|
||||
msgstr "{name} hat ein Dokument mit Ihnen geteilt!"
|
||||
|
||||
#: build/lib/core/models.py:1368 core/models.py:1368
|
||||
#: build/lib/core/models.py:868 core/models.py:868
|
||||
#, python-brace-format
|
||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||
msgstr "{name} hat Sie mit der Rolle \"{role}\" zu folgendem Dokument eingeladen:"
|
||||
|
||||
#: build/lib/core/models.py:1374 core/models.py:1374
|
||||
#: build/lib/core/models.py:874 core/models.py:874
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you: {title}"
|
||||
msgstr "{name} hat ein Dokument mit Ihnen geteilt: {title}"
|
||||
|
||||
#: build/lib/core/models.py:1475 core/models.py:1475
|
||||
#: build/lib/core/models.py:975 core/models.py:975
|
||||
msgid "Document/user link trace"
|
||||
msgstr "Dokument/Benutzer Linkverfolgung"
|
||||
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
#: build/lib/core/models.py:976 core/models.py:976
|
||||
msgid "Document/user link traces"
|
||||
msgstr "Dokument/Benutzer Linkverfolgung"
|
||||
|
||||
#: build/lib/core/models.py:1482 core/models.py:1482
|
||||
#: build/lib/core/models.py:982 core/models.py:982
|
||||
msgid "A link trace already exists for this document/user."
|
||||
msgstr "Für dieses Dokument/ diesen Benutzer ist bereits eine Linkverfolgung vorhanden."
|
||||
|
||||
#: build/lib/core/models.py:1505 core/models.py:1505
|
||||
#: build/lib/core/models.py:1005 core/models.py:1005
|
||||
msgid "Document favorite"
|
||||
msgstr "Dokumentenfavorit"
|
||||
|
||||
#: build/lib/core/models.py:1506 core/models.py:1506
|
||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
||||
msgid "Document favorites"
|
||||
msgstr "Dokumentfavoriten"
|
||||
|
||||
#: build/lib/core/models.py:1512 core/models.py:1512
|
||||
#: build/lib/core/models.py:1012 core/models.py:1012
|
||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||
msgstr "Dieses Dokument ist bereits durch den gleichen Benutzer favorisiert worden."
|
||||
|
||||
#: build/lib/core/models.py:1534 core/models.py:1534
|
||||
#: build/lib/core/models.py:1034 core/models.py:1034
|
||||
msgid "Document/user relation"
|
||||
msgstr "Dokument/Benutzerbeziehung"
|
||||
|
||||
#: build/lib/core/models.py:1535 core/models.py:1535
|
||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
||||
msgid "Document/user relations"
|
||||
msgstr "Dokument/Benutzerbeziehungen"
|
||||
|
||||
#: build/lib/core/models.py:1541 core/models.py:1541
|
||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
||||
msgid "This user is already in this document."
|
||||
msgstr "Dieser Benutzer befindet sich bereits in diesem Dokument."
|
||||
|
||||
#: build/lib/core/models.py:1547 core/models.py:1547
|
||||
#: build/lib/core/models.py:1047 core/models.py:1047
|
||||
msgid "This team is already in this document."
|
||||
msgstr "Dieses Team befindet sich bereits in diesem Dokument."
|
||||
|
||||
#: build/lib/core/models.py:1553 core/models.py:1553
|
||||
#: build/lib/core/models.py:1053 core/models.py:1053
|
||||
msgid "Either user or team must be set, not both."
|
||||
msgstr "Benutzer oder Team müssen gesetzt werden, nicht beides."
|
||||
|
||||
#: build/lib/core/models.py:1704 core/models.py:1704
|
||||
#: build/lib/core/models.py:1204 core/models.py:1204
|
||||
msgid "Document ask for access"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1705 core/models.py:1705
|
||||
#: build/lib/core/models.py:1205 core/models.py:1205
|
||||
msgid "Document ask for accesses"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1711 core/models.py:1711
|
||||
#: build/lib/core/models.py:1211 core/models.py:1211
|
||||
msgid "This user has already asked for access to this document."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1768 core/models.py:1768
|
||||
#: build/lib/core/models.py:1268 core/models.py:1268
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to a document!"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1772 core/models.py:1772
|
||||
#: build/lib/core/models.py:1272 core/models.py:1272
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to the following document:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1778 core/models.py:1778
|
||||
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||
#, python-brace-format
|
||||
msgid "{name} is asking for access to the document: {title}"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1820 core/models.py:1820
|
||||
#: build/lib/core/models.py:1320 core/models.py:1320
|
||||
msgid "Thread"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1821 core/models.py:1821
|
||||
#: build/lib/core/models.py:1321 core/models.py:1321
|
||||
msgid "Threads"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1824 build/lib/core/models.py:1876
|
||||
#: core/models.py:1824 core/models.py:1876
|
||||
#: build/lib/core/models.py:1324 build/lib/core/models.py:1376
|
||||
#: core/models.py:1324 core/models.py:1376
|
||||
msgid "Anonymous"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1871 core/models.py:1871
|
||||
#: build/lib/core/models.py:1371 core/models.py:1371
|
||||
msgid "Comment"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1872 core/models.py:1872
|
||||
#: build/lib/core/models.py:1372 core/models.py:1372
|
||||
msgid "Comments"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1921 core/models.py:1921
|
||||
#: build/lib/core/models.py:1421 core/models.py:1421
|
||||
msgid "This emoji has already been reacted to this comment."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1925 core/models.py:1925
|
||||
#: build/lib/core/models.py:1425 core/models.py:1425
|
||||
msgid "Reaction"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1926 core/models.py:1926
|
||||
#: build/lib/core/models.py:1426 core/models.py:1426
|
||||
msgid "Reactions"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1936 core/models.py:1936
|
||||
#: build/lib/core/models.py:1436 core/models.py:1436
|
||||
msgid "email address"
|
||||
msgstr "E-Mail-Adresse"
|
||||
|
||||
#: build/lib/core/models.py:1955 core/models.py:1955
|
||||
#: build/lib/core/models.py:1455 core/models.py:1455
|
||||
msgid "Document invitation"
|
||||
msgstr "Einladung zum Dokument"
|
||||
|
||||
#: build/lib/core/models.py:1956 core/models.py:1956
|
||||
#: build/lib/core/models.py:1456 core/models.py:1456
|
||||
msgid "Document invitations"
|
||||
msgstr "Dokumenteinladungen"
|
||||
|
||||
#: build/lib/core/models.py:1976 core/models.py:1976
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
msgid "This email is already associated to a registered user."
|
||||
msgstr "Diese E-Mail ist bereits einem registrierten Benutzer zugeordnet."
|
||||
|
||||
#: build/lib/impress/settings.py:702 impress/settings.py:702
|
||||
msgid "Docs AI"
|
||||
msgstr ""
|
||||
|
||||
#: core/templates/mail/html/template.html:153
|
||||
#: core/templates/mail/text/template.txt:3
|
||||
msgid "Logo email"
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lasuite-docs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
|
||||
"PO-Revision-Date: 2026-02-27 08:24\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0000\n"
|
||||
"PO-Revision-Date: 2026-01-28 20:12\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: English\n"
|
||||
"Language: en_US\n"
|
||||
@@ -17,65 +17,57 @@ msgstr ""
|
||||
"X-Crowdin-File: backend-impress.pot\n"
|
||||
"X-Crowdin-File-ID: 18\n"
|
||||
|
||||
#: build/lib/core/admin.py:30 core/admin.py:30
|
||||
#: build/lib/core/admin.py:28 core/admin.py:28
|
||||
msgid "Personal info"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:43 build/lib/core/admin.py:161 core/admin.py:43
|
||||
#: core/admin.py:161
|
||||
#: build/lib/core/admin.py:41 build/lib/core/admin.py:121 core/admin.py:41
|
||||
#: core/admin.py:121
|
||||
msgid "Permissions"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:55 core/admin.py:55
|
||||
#: build/lib/core/admin.py:53 core/admin.py:53
|
||||
msgid "Important dates"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:112 core/admin.py:112
|
||||
msgid "Import job created and queued."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:116 core/admin.py:116
|
||||
msgid "Process selected user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:171 core/admin.py:171
|
||||
#: build/lib/core/admin.py:131 core/admin.py:131
|
||||
msgid "Tree structure"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/filters.py:48 core/api/filters.py:48
|
||||
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/filters.py:62 core/api/filters.py:62
|
||||
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||
msgid "Creator is me"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/filters.py:65 core/api/filters.py:65
|
||||
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||
msgid "Masked"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/filters.py:68 core/api/filters.py:68
|
||||
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
|
||||
msgid "Favorite"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/serializers.py:513 core/api/serializers.py:513
|
||||
#: build/lib/core/api/serializers.py:505 core/api/serializers.py:505
|
||||
msgid "A new document was created on your behalf!"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/serializers.py:517 core/api/serializers.py:517
|
||||
#: build/lib/core/api/serializers.py:509 core/api/serializers.py:509
|
||||
msgid "You have been granted ownership of a new document:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/serializers.py:553 core/api/serializers.py:553
|
||||
#: build/lib/core/api/serializers.py:545 core/api/serializers.py:545
|
||||
msgid "This field is required."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/serializers.py:564 core/api/serializers.py:564
|
||||
#: build/lib/core/api/serializers.py:556 core/api/serializers.py:556
|
||||
#, python-format
|
||||
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
|
||||
#: build/lib/core/api/viewsets.py:1122 core/api/viewsets.py:1122
|
||||
#, python-brace-format
|
||||
msgid "copy of {title}"
|
||||
msgstr ""
|
||||
@@ -143,374 +135,262 @@ msgstr ""
|
||||
msgid "Right"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:80 core/models.py:80
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
msgid "id"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
#: build/lib/core/models.py:82 core/models.py:82
|
||||
msgid "primary key for the record as UUID"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:87 core/models.py:87
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
msgid "created on"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
#: build/lib/core/models.py:89 core/models.py:89
|
||||
msgid "date and time at which a record was created"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:93 core/models.py:93
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
msgid "updated on"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
#: build/lib/core/models.py:95 core/models.py:95
|
||||
msgid "date and time at which a record was last updated"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:130 core/models.py:130
|
||||
#: build/lib/core/models.py:131 core/models.py:131
|
||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:141 core/models.py:141
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
msgid "sub"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
#: build/lib/core/models.py:143 core/models.py:143
|
||||
msgid "Required. 255 characters or fewer. ASCII characters only."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:150 core/models.py:150
|
||||
#: build/lib/core/models.py:151 core/models.py:151
|
||||
msgid "full name"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:152 core/models.py:152
|
||||
#: build/lib/core/models.py:153 core/models.py:153
|
||||
msgid "short name"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:155 core/models.py:155
|
||||
#: build/lib/core/models.py:156 core/models.py:156
|
||||
msgid "identity email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:160 core/models.py:160
|
||||
#: build/lib/core/models.py:161 core/models.py:161
|
||||
msgid "admin email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:167 core/models.py:167
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
msgid "language"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
#: build/lib/core/models.py:169 core/models.py:169
|
||||
msgid "The language in which the user wants to see the interface."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:176 core/models.py:176
|
||||
#: build/lib/core/models.py:177 core/models.py:177
|
||||
msgid "The timezone in which the user wants to see times."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:179 core/models.py:179
|
||||
#: build/lib/core/models.py:180 core/models.py:180
|
||||
msgid "device"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:181 core/models.py:181
|
||||
#: build/lib/core/models.py:182 core/models.py:182
|
||||
msgid "Whether the user is a device or a real user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:184 core/models.py:184
|
||||
#: build/lib/core/models.py:185 core/models.py:185
|
||||
msgid "staff status"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:186 core/models.py:186
|
||||
#: build/lib/core/models.py:187 core/models.py:187
|
||||
msgid "Whether the user can log into this admin site."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:189 core/models.py:189
|
||||
#: build/lib/core/models.py:190 core/models.py:190
|
||||
msgid "active"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:192 core/models.py:192
|
||||
#: build/lib/core/models.py:193 core/models.py:193
|
||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:204 core/models.py:204
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
msgid "user"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
#: build/lib/core/models.py:206 core/models.py:206
|
||||
msgid "users"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:360 core/models.py:360
|
||||
msgid "Active email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:361 core/models.py:361
|
||||
msgid "Email address to deactivate"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:388 core/models.py:388
|
||||
msgid "Unique ID in the source file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:394 build/lib/core/models.py:692 core/models.py:394
|
||||
#: core/models.py:692
|
||||
msgid "Pending"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:395 core/models.py:395
|
||||
msgid "Ready"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:396 build/lib/core/models.py:694 core/models.py:396
|
||||
#: core/models.py:694
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:397 build/lib/core/models.py:695 core/models.py:397
|
||||
#: core/models.py:695
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:405 core/models.py:405
|
||||
msgid "user reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:406 core/models.py:406
|
||||
msgid "user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:644 core/models.py:644
|
||||
msgid "You have requested a reconciliation of your user accounts on Docs.\n"
|
||||
" To confirm that you are the one who initiated the request\n"
|
||||
" and that this email belongs to you:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:650 core/models.py:650
|
||||
msgid "Confirm by clicking the link to start the reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:655 build/lib/core/models.py:761 core/models.py:655
|
||||
#: core/models.py:761
|
||||
msgid "Click here"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:656 core/models.py:656
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:667 core/models.py:667
|
||||
msgid "Your reconciliation request has been processed.\n"
|
||||
" New documents are likely associated with your account:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:672 core/models.py:672
|
||||
msgid "Your accounts have been merged"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:677 core/models.py:677
|
||||
msgid "Click here to see"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:678 core/models.py:678
|
||||
msgid "See my documents"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:688 core/models.py:688
|
||||
msgid "CSV file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:693 core/models.py:693
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:703 core/models.py:703
|
||||
msgid "user reconciliation CSV import"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:704 core/models.py:704
|
||||
msgid "user reconciliation CSV imports"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:748 core/models.py:748
|
||||
#, python-brace-format
|
||||
msgid "Your request for reconciliation was unsuccessful.\n"
|
||||
" Reconciliation failed for the following email addresses:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Please check for typos.\n"
|
||||
" You can submit another request with the valid email addresses."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:756 core/models.py:756
|
||||
msgid "Reconciliation of your Docs accounts not completed"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:762 core/models.py:762
|
||||
msgid "Make a new request"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:861 core/models.py:861
|
||||
#: build/lib/core/models.py:362 core/models.py:362
|
||||
msgid "title"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:862 core/models.py:862
|
||||
#: build/lib/core/models.py:363 core/models.py:363
|
||||
msgid "excerpt"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:911 core/models.py:911
|
||||
#: build/lib/core/models.py:412 core/models.py:412
|
||||
msgid "Document"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:912 core/models.py:912
|
||||
#: build/lib/core/models.py:413 core/models.py:413
|
||||
msgid "Documents"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:924 build/lib/core/models.py:1328
|
||||
#: core/models.py:924 core/models.py:1328
|
||||
#: build/lib/core/models.py:425 build/lib/core/models.py:828 core/models.py:425
|
||||
#: core/models.py:828
|
||||
msgid "Untitled Document"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1329 core/models.py:1329
|
||||
#: build/lib/core/models.py:829 core/models.py:829
|
||||
msgid "Open"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1364 core/models.py:1364
|
||||
#: build/lib/core/models.py:864 core/models.py:864
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you!"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1368 core/models.py:1368
|
||||
#: build/lib/core/models.py:868 core/models.py:868
|
||||
#, python-brace-format
|
||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1374 core/models.py:1374
|
||||
#: build/lib/core/models.py:874 core/models.py:874
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you: {title}"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1475 core/models.py:1475
|
||||
#: build/lib/core/models.py:975 core/models.py:975
|
||||
msgid "Document/user link trace"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
#: build/lib/core/models.py:976 core/models.py:976
|
||||
msgid "Document/user link traces"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1482 core/models.py:1482
|
||||
#: build/lib/core/models.py:982 core/models.py:982
|
||||
msgid "A link trace already exists for this document/user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1505 core/models.py:1505
|
||||
#: build/lib/core/models.py:1005 core/models.py:1005
|
||||
msgid "Document favorite"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1506 core/models.py:1506
|
||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
||||
msgid "Document favorites"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1512 core/models.py:1512
|
||||
#: build/lib/core/models.py:1012 core/models.py:1012
|
||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1534 core/models.py:1534
|
||||
#: build/lib/core/models.py:1034 core/models.py:1034
|
||||
msgid "Document/user relation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1535 core/models.py:1535
|
||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
||||
msgid "Document/user relations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1541 core/models.py:1541
|
||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
||||
msgid "This user is already in this document."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1547 core/models.py:1547
|
||||
#: build/lib/core/models.py:1047 core/models.py:1047
|
||||
msgid "This team is already in this document."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1553 core/models.py:1553
|
||||
#: build/lib/core/models.py:1053 core/models.py:1053
|
||||
msgid "Either user or team must be set, not both."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1704 core/models.py:1704
|
||||
#: build/lib/core/models.py:1204 core/models.py:1204
|
||||
msgid "Document ask for access"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1705 core/models.py:1705
|
||||
#: build/lib/core/models.py:1205 core/models.py:1205
|
||||
msgid "Document ask for accesses"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1711 core/models.py:1711
|
||||
#: build/lib/core/models.py:1211 core/models.py:1211
|
||||
msgid "This user has already asked for access to this document."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1768 core/models.py:1768
|
||||
#: build/lib/core/models.py:1268 core/models.py:1268
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to a document!"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1772 core/models.py:1772
|
||||
#: build/lib/core/models.py:1272 core/models.py:1272
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to the following document:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1778 core/models.py:1778
|
||||
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||
#, python-brace-format
|
||||
msgid "{name} is asking for access to the document: {title}"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1820 core/models.py:1820
|
||||
#: build/lib/core/models.py:1320 core/models.py:1320
|
||||
msgid "Thread"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1821 core/models.py:1821
|
||||
#: build/lib/core/models.py:1321 core/models.py:1321
|
||||
msgid "Threads"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1824 build/lib/core/models.py:1876
|
||||
#: core/models.py:1824 core/models.py:1876
|
||||
#: build/lib/core/models.py:1324 build/lib/core/models.py:1376
|
||||
#: core/models.py:1324 core/models.py:1376
|
||||
msgid "Anonymous"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1871 core/models.py:1871
|
||||
#: build/lib/core/models.py:1371 core/models.py:1371
|
||||
msgid "Comment"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1872 core/models.py:1872
|
||||
#: build/lib/core/models.py:1372 core/models.py:1372
|
||||
msgid "Comments"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1921 core/models.py:1921
|
||||
#: build/lib/core/models.py:1421 core/models.py:1421
|
||||
msgid "This emoji has already been reacted to this comment."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1925 core/models.py:1925
|
||||
#: build/lib/core/models.py:1425 core/models.py:1425
|
||||
msgid "Reaction"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1926 core/models.py:1926
|
||||
#: build/lib/core/models.py:1426 core/models.py:1426
|
||||
msgid "Reactions"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1936 core/models.py:1936
|
||||
#: build/lib/core/models.py:1436 core/models.py:1436
|
||||
msgid "email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1955 core/models.py:1955
|
||||
#: build/lib/core/models.py:1455 core/models.py:1455
|
||||
msgid "Document invitation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1956 core/models.py:1956
|
||||
#: build/lib/core/models.py:1456 core/models.py:1456
|
||||
msgid "Document invitations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1976 core/models.py:1976
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
msgid "This email is already associated to a registered user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/impress/settings.py:702 impress/settings.py:702
|
||||
msgid "Docs AI"
|
||||
msgstr ""
|
||||
|
||||
#: core/templates/mail/html/template.html:153
|
||||
#: core/templates/mail/text/template.txt:3
|
||||
msgid "Logo email"
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lasuite-docs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
|
||||
"PO-Revision-Date: 2026-02-27 08:24\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0000\n"
|
||||
"PO-Revision-Date: 2026-01-28 20:12\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Spanish\n"
|
||||
"Language: es_ES\n"
|
||||
@@ -17,65 +17,57 @@ msgstr ""
|
||||
"X-Crowdin-File: backend-impress.pot\n"
|
||||
"X-Crowdin-File-ID: 18\n"
|
||||
|
||||
#: build/lib/core/admin.py:30 core/admin.py:30
|
||||
#: build/lib/core/admin.py:28 core/admin.py:28
|
||||
msgid "Personal info"
|
||||
msgstr "Información Personal"
|
||||
|
||||
#: build/lib/core/admin.py:43 build/lib/core/admin.py:161 core/admin.py:43
|
||||
#: core/admin.py:161
|
||||
#: build/lib/core/admin.py:41 build/lib/core/admin.py:121 core/admin.py:41
|
||||
#: core/admin.py:121
|
||||
msgid "Permissions"
|
||||
msgstr "Permisos"
|
||||
|
||||
#: build/lib/core/admin.py:55 core/admin.py:55
|
||||
#: build/lib/core/admin.py:53 core/admin.py:53
|
||||
msgid "Important dates"
|
||||
msgstr "Fechas importantes"
|
||||
|
||||
#: build/lib/core/admin.py:112 core/admin.py:112
|
||||
msgid "Import job created and queued."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:116 core/admin.py:116
|
||||
msgid "Process selected user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:171 core/admin.py:171
|
||||
#: build/lib/core/admin.py:131 core/admin.py:131
|
||||
msgid "Tree structure"
|
||||
msgstr "Estructura en árbol"
|
||||
|
||||
#: build/lib/core/api/filters.py:48 core/api/filters.py:48
|
||||
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||
msgid "Title"
|
||||
msgstr "Título"
|
||||
|
||||
#: build/lib/core/api/filters.py:62 core/api/filters.py:62
|
||||
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||
msgid "Creator is me"
|
||||
msgstr "Yo soy el creador"
|
||||
|
||||
#: build/lib/core/api/filters.py:65 core/api/filters.py:65
|
||||
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||
msgid "Masked"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/filters.py:68 core/api/filters.py:68
|
||||
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
|
||||
msgid "Favorite"
|
||||
msgstr "Favorito"
|
||||
|
||||
#: build/lib/core/api/serializers.py:513 core/api/serializers.py:513
|
||||
#: build/lib/core/api/serializers.py:505 core/api/serializers.py:505
|
||||
msgid "A new document was created on your behalf!"
|
||||
msgstr "¡Un nuevo documento se ha creado por ti!"
|
||||
|
||||
#: build/lib/core/api/serializers.py:517 core/api/serializers.py:517
|
||||
#: build/lib/core/api/serializers.py:509 core/api/serializers.py:509
|
||||
msgid "You have been granted ownership of a new document:"
|
||||
msgstr "Se le ha concedido la propiedad de un nuevo documento :"
|
||||
|
||||
#: build/lib/core/api/serializers.py:553 core/api/serializers.py:553
|
||||
#: build/lib/core/api/serializers.py:545 core/api/serializers.py:545
|
||||
msgid "This field is required."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/serializers.py:564 core/api/serializers.py:564
|
||||
#: build/lib/core/api/serializers.py:556 core/api/serializers.py:556
|
||||
#, python-format
|
||||
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
|
||||
#: build/lib/core/api/viewsets.py:1122 core/api/viewsets.py:1122
|
||||
#, python-brace-format
|
||||
msgid "copy of {title}"
|
||||
msgstr "copia de {title}"
|
||||
@@ -143,374 +135,262 @@ msgstr "Izquierda"
|
||||
msgid "Right"
|
||||
msgstr "Derecha"
|
||||
|
||||
#: build/lib/core/models.py:80 core/models.py:80
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
msgid "id"
|
||||
msgstr "id"
|
||||
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
#: build/lib/core/models.py:82 core/models.py:82
|
||||
msgid "primary key for the record as UUID"
|
||||
msgstr "clave primaria para el registro como UUID"
|
||||
|
||||
#: build/lib/core/models.py:87 core/models.py:87
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
msgid "created on"
|
||||
msgstr "creado el"
|
||||
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
#: build/lib/core/models.py:89 core/models.py:89
|
||||
msgid "date and time at which a record was created"
|
||||
msgstr "fecha y hora en la que se creó un registro"
|
||||
|
||||
#: build/lib/core/models.py:93 core/models.py:93
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
msgid "updated on"
|
||||
msgstr "actualizado el"
|
||||
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
#: build/lib/core/models.py:95 core/models.py:95
|
||||
msgid "date and time at which a record was last updated"
|
||||
msgstr "fecha y hora en la que un registro fue actualizado por última vez"
|
||||
|
||||
#: build/lib/core/models.py:130 core/models.py:130
|
||||
#: build/lib/core/models.py:131 core/models.py:131
|
||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||
msgstr "No se ha podido encontrar un usuario con este sub (UUID), pero el correo electrónico ya está asociado con un usuario."
|
||||
|
||||
#: build/lib/core/models.py:141 core/models.py:141
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
msgid "sub"
|
||||
msgstr "sub (UUID)"
|
||||
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
#: build/lib/core/models.py:143 core/models.py:143
|
||||
msgid "Required. 255 characters or fewer. ASCII characters only."
|
||||
msgstr "Obligatorio. 255 caracteres o menos. Solo caracteres ASCII."
|
||||
|
||||
#: build/lib/core/models.py:150 core/models.py:150
|
||||
#: build/lib/core/models.py:151 core/models.py:151
|
||||
msgid "full name"
|
||||
msgstr "nombre completo"
|
||||
|
||||
#: build/lib/core/models.py:152 core/models.py:152
|
||||
#: build/lib/core/models.py:153 core/models.py:153
|
||||
msgid "short name"
|
||||
msgstr "nombre abreviado"
|
||||
|
||||
#: build/lib/core/models.py:155 core/models.py:155
|
||||
#: build/lib/core/models.py:156 core/models.py:156
|
||||
msgid "identity email address"
|
||||
msgstr "correo electrónico de identidad"
|
||||
|
||||
#: build/lib/core/models.py:160 core/models.py:160
|
||||
#: build/lib/core/models.py:161 core/models.py:161
|
||||
msgid "admin email address"
|
||||
msgstr "correo electrónico del administrador"
|
||||
|
||||
#: build/lib/core/models.py:167 core/models.py:167
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
msgid "language"
|
||||
msgstr "idioma"
|
||||
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
#: build/lib/core/models.py:169 core/models.py:169
|
||||
msgid "The language in which the user wants to see the interface."
|
||||
msgstr "El idioma en el que el usuario desea ver la interfaz."
|
||||
|
||||
#: build/lib/core/models.py:176 core/models.py:176
|
||||
#: build/lib/core/models.py:177 core/models.py:177
|
||||
msgid "The timezone in which the user wants to see times."
|
||||
msgstr "La zona horaria en la que el usuario quiere ver los tiempos."
|
||||
|
||||
#: build/lib/core/models.py:179 core/models.py:179
|
||||
#: build/lib/core/models.py:180 core/models.py:180
|
||||
msgid "device"
|
||||
msgstr "dispositivo"
|
||||
|
||||
#: build/lib/core/models.py:181 core/models.py:181
|
||||
#: build/lib/core/models.py:182 core/models.py:182
|
||||
msgid "Whether the user is a device or a real user."
|
||||
msgstr "Si el usuario es un dispositivo o un usuario real."
|
||||
|
||||
#: build/lib/core/models.py:184 core/models.py:184
|
||||
#: build/lib/core/models.py:185 core/models.py:185
|
||||
msgid "staff status"
|
||||
msgstr "rol en el equipo"
|
||||
|
||||
#: build/lib/core/models.py:186 core/models.py:186
|
||||
#: build/lib/core/models.py:187 core/models.py:187
|
||||
msgid "Whether the user can log into this admin site."
|
||||
msgstr "Si el usuario puede iniciar sesión en esta página web de administración."
|
||||
|
||||
#: build/lib/core/models.py:189 core/models.py:189
|
||||
#: build/lib/core/models.py:190 core/models.py:190
|
||||
msgid "active"
|
||||
msgstr "activo"
|
||||
|
||||
#: build/lib/core/models.py:192 core/models.py:192
|
||||
#: build/lib/core/models.py:193 core/models.py:193
|
||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||
msgstr "Si este usuario debe ser considerado como activo. Deseleccionar en lugar de eliminar cuentas."
|
||||
|
||||
#: build/lib/core/models.py:204 core/models.py:204
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
msgid "user"
|
||||
msgstr "usuario"
|
||||
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
#: build/lib/core/models.py:206 core/models.py:206
|
||||
msgid "users"
|
||||
msgstr "usuarios"
|
||||
|
||||
#: build/lib/core/models.py:360 core/models.py:360
|
||||
msgid "Active email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:361 core/models.py:361
|
||||
msgid "Email address to deactivate"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:388 core/models.py:388
|
||||
msgid "Unique ID in the source file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:394 build/lib/core/models.py:692 core/models.py:394
|
||||
#: core/models.py:692
|
||||
msgid "Pending"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:395 core/models.py:395
|
||||
msgid "Ready"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:396 build/lib/core/models.py:694 core/models.py:396
|
||||
#: core/models.py:694
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:397 build/lib/core/models.py:695 core/models.py:397
|
||||
#: core/models.py:695
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:405 core/models.py:405
|
||||
msgid "user reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:406 core/models.py:406
|
||||
msgid "user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:644 core/models.py:644
|
||||
msgid "You have requested a reconciliation of your user accounts on Docs.\n"
|
||||
" To confirm that you are the one who initiated the request\n"
|
||||
" and that this email belongs to you:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:650 core/models.py:650
|
||||
msgid "Confirm by clicking the link to start the reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:655 build/lib/core/models.py:761 core/models.py:655
|
||||
#: core/models.py:761
|
||||
msgid "Click here"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:656 core/models.py:656
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:667 core/models.py:667
|
||||
msgid "Your reconciliation request has been processed.\n"
|
||||
" New documents are likely associated with your account:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:672 core/models.py:672
|
||||
msgid "Your accounts have been merged"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:677 core/models.py:677
|
||||
msgid "Click here to see"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:678 core/models.py:678
|
||||
msgid "See my documents"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:688 core/models.py:688
|
||||
msgid "CSV file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:693 core/models.py:693
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:703 core/models.py:703
|
||||
msgid "user reconciliation CSV import"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:704 core/models.py:704
|
||||
msgid "user reconciliation CSV imports"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:748 core/models.py:748
|
||||
#, python-brace-format
|
||||
msgid "Your request for reconciliation was unsuccessful.\n"
|
||||
" Reconciliation failed for the following email addresses:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Please check for typos.\n"
|
||||
" You can submit another request with the valid email addresses."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:756 core/models.py:756
|
||||
msgid "Reconciliation of your Docs accounts not completed"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:762 core/models.py:762
|
||||
msgid "Make a new request"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:861 core/models.py:861
|
||||
#: build/lib/core/models.py:362 core/models.py:362
|
||||
msgid "title"
|
||||
msgstr "título"
|
||||
|
||||
#: build/lib/core/models.py:862 core/models.py:862
|
||||
#: build/lib/core/models.py:363 core/models.py:363
|
||||
msgid "excerpt"
|
||||
msgstr "resumen"
|
||||
|
||||
#: build/lib/core/models.py:911 core/models.py:911
|
||||
#: build/lib/core/models.py:412 core/models.py:412
|
||||
msgid "Document"
|
||||
msgstr "Documento"
|
||||
|
||||
#: build/lib/core/models.py:912 core/models.py:912
|
||||
#: build/lib/core/models.py:413 core/models.py:413
|
||||
msgid "Documents"
|
||||
msgstr "Documentos"
|
||||
|
||||
#: build/lib/core/models.py:924 build/lib/core/models.py:1328
|
||||
#: core/models.py:924 core/models.py:1328
|
||||
#: build/lib/core/models.py:425 build/lib/core/models.py:828 core/models.py:425
|
||||
#: core/models.py:828
|
||||
msgid "Untitled Document"
|
||||
msgstr "Documento sin título"
|
||||
|
||||
#: build/lib/core/models.py:1329 core/models.py:1329
|
||||
#: build/lib/core/models.py:829 core/models.py:829
|
||||
msgid "Open"
|
||||
msgstr "Abrir"
|
||||
|
||||
#: build/lib/core/models.py:1364 core/models.py:1364
|
||||
#: build/lib/core/models.py:864 core/models.py:864
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you!"
|
||||
msgstr "¡{name} ha compartido un documento contigo!"
|
||||
|
||||
#: build/lib/core/models.py:1368 core/models.py:1368
|
||||
#: build/lib/core/models.py:868 core/models.py:868
|
||||
#, python-brace-format
|
||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||
msgstr "Te ha invitado {name} al siguiente documento con el rol \"{role}\" :"
|
||||
|
||||
#: build/lib/core/models.py:1374 core/models.py:1374
|
||||
#: build/lib/core/models.py:874 core/models.py:874
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you: {title}"
|
||||
msgstr "{name} ha compartido un documento contigo: {title}"
|
||||
|
||||
#: build/lib/core/models.py:1475 core/models.py:1475
|
||||
#: build/lib/core/models.py:975 core/models.py:975
|
||||
msgid "Document/user link trace"
|
||||
msgstr "Traza del enlace de documento/usuario"
|
||||
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
#: build/lib/core/models.py:976 core/models.py:976
|
||||
msgid "Document/user link traces"
|
||||
msgstr "Trazas del enlace de documento/usuario"
|
||||
|
||||
#: build/lib/core/models.py:1482 core/models.py:1482
|
||||
#: build/lib/core/models.py:982 core/models.py:982
|
||||
msgid "A link trace already exists for this document/user."
|
||||
msgstr "Ya existe una traza de enlace para este documento/usuario."
|
||||
|
||||
#: build/lib/core/models.py:1505 core/models.py:1505
|
||||
#: build/lib/core/models.py:1005 core/models.py:1005
|
||||
msgid "Document favorite"
|
||||
msgstr "Documento favorito"
|
||||
|
||||
#: build/lib/core/models.py:1506 core/models.py:1506
|
||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
||||
msgid "Document favorites"
|
||||
msgstr "Documentos favoritos"
|
||||
|
||||
#: build/lib/core/models.py:1512 core/models.py:1512
|
||||
#: build/lib/core/models.py:1012 core/models.py:1012
|
||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||
msgstr "Este documento ya ha sido marcado como favorito por el usuario."
|
||||
|
||||
#: build/lib/core/models.py:1534 core/models.py:1534
|
||||
#: build/lib/core/models.py:1034 core/models.py:1034
|
||||
msgid "Document/user relation"
|
||||
msgstr "Relación documento/usuario"
|
||||
|
||||
#: build/lib/core/models.py:1535 core/models.py:1535
|
||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
||||
msgid "Document/user relations"
|
||||
msgstr "Relaciones documento/usuario"
|
||||
|
||||
#: build/lib/core/models.py:1541 core/models.py:1541
|
||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
||||
msgid "This user is already in this document."
|
||||
msgstr "Este usuario ya forma parte del documento."
|
||||
|
||||
#: build/lib/core/models.py:1547 core/models.py:1547
|
||||
#: build/lib/core/models.py:1047 core/models.py:1047
|
||||
msgid "This team is already in this document."
|
||||
msgstr "Este equipo ya forma parte del documento."
|
||||
|
||||
#: build/lib/core/models.py:1553 core/models.py:1553
|
||||
#: build/lib/core/models.py:1053 core/models.py:1053
|
||||
msgid "Either user or team must be set, not both."
|
||||
msgstr "Debe establecerse un usuario o un equipo, no ambos."
|
||||
|
||||
#: build/lib/core/models.py:1704 core/models.py:1704
|
||||
#: build/lib/core/models.py:1204 core/models.py:1204
|
||||
msgid "Document ask for access"
|
||||
msgstr "Solicitud de acceso"
|
||||
|
||||
#: build/lib/core/models.py:1705 core/models.py:1705
|
||||
#: build/lib/core/models.py:1205 core/models.py:1205
|
||||
msgid "Document ask for accesses"
|
||||
msgstr "Solicitud de accesos"
|
||||
|
||||
#: build/lib/core/models.py:1711 core/models.py:1711
|
||||
#: build/lib/core/models.py:1211 core/models.py:1211
|
||||
msgid "This user has already asked for access to this document."
|
||||
msgstr "Este usuario ya ha solicitado acceso a este documento."
|
||||
|
||||
#: build/lib/core/models.py:1768 core/models.py:1768
|
||||
#: build/lib/core/models.py:1268 core/models.py:1268
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to a document!"
|
||||
msgstr "¡{name} desea acceder a un documento!"
|
||||
|
||||
#: build/lib/core/models.py:1772 core/models.py:1772
|
||||
#: build/lib/core/models.py:1272 core/models.py:1272
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to the following document:"
|
||||
msgstr "{name} desea acceso al siguiente documento:"
|
||||
|
||||
#: build/lib/core/models.py:1778 core/models.py:1778
|
||||
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||
#, python-brace-format
|
||||
msgid "{name} is asking for access to the document: {title}"
|
||||
msgstr "{name} está pidiendo acceso al documento: {title}"
|
||||
|
||||
#: build/lib/core/models.py:1820 core/models.py:1820
|
||||
#: build/lib/core/models.py:1320 core/models.py:1320
|
||||
msgid "Thread"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1821 core/models.py:1821
|
||||
#: build/lib/core/models.py:1321 core/models.py:1321
|
||||
msgid "Threads"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1824 build/lib/core/models.py:1876
|
||||
#: core/models.py:1824 core/models.py:1876
|
||||
#: build/lib/core/models.py:1324 build/lib/core/models.py:1376
|
||||
#: core/models.py:1324 core/models.py:1376
|
||||
msgid "Anonymous"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1871 core/models.py:1871
|
||||
#: build/lib/core/models.py:1371 core/models.py:1371
|
||||
msgid "Comment"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1872 core/models.py:1872
|
||||
#: build/lib/core/models.py:1372 core/models.py:1372
|
||||
msgid "Comments"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1921 core/models.py:1921
|
||||
#: build/lib/core/models.py:1421 core/models.py:1421
|
||||
msgid "This emoji has already been reacted to this comment."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1925 core/models.py:1925
|
||||
#: build/lib/core/models.py:1425 core/models.py:1425
|
||||
msgid "Reaction"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1926 core/models.py:1926
|
||||
#: build/lib/core/models.py:1426 core/models.py:1426
|
||||
msgid "Reactions"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1936 core/models.py:1936
|
||||
#: build/lib/core/models.py:1436 core/models.py:1436
|
||||
msgid "email address"
|
||||
msgstr "dirección de correo electrónico"
|
||||
|
||||
#: build/lib/core/models.py:1955 core/models.py:1955
|
||||
#: build/lib/core/models.py:1455 core/models.py:1455
|
||||
msgid "Document invitation"
|
||||
msgstr "Invitación al documento"
|
||||
|
||||
#: build/lib/core/models.py:1956 core/models.py:1956
|
||||
#: build/lib/core/models.py:1456 core/models.py:1456
|
||||
msgid "Document invitations"
|
||||
msgstr "Invitaciones a documentos"
|
||||
|
||||
#: build/lib/core/models.py:1976 core/models.py:1976
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
msgid "This email is already associated to a registered user."
|
||||
msgstr "Este correo electrónico está asociado a un usuario registrado."
|
||||
|
||||
#: build/lib/impress/settings.py:702 impress/settings.py:702
|
||||
msgid "Docs AI"
|
||||
msgstr ""
|
||||
|
||||
#: core/templates/mail/html/template.html:153
|
||||
#: core/templates/mail/text/template.txt:3
|
||||
msgid "Logo email"
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lasuite-docs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
|
||||
"PO-Revision-Date: 2026-02-27 08:24\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0000\n"
|
||||
"PO-Revision-Date: 2026-01-28 20:12\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: French\n"
|
||||
"Language: fr_FR\n"
|
||||
@@ -17,65 +17,57 @@ msgstr ""
|
||||
"X-Crowdin-File: backend-impress.pot\n"
|
||||
"X-Crowdin-File-ID: 18\n"
|
||||
|
||||
#: build/lib/core/admin.py:30 core/admin.py:30
|
||||
#: build/lib/core/admin.py:28 core/admin.py:28
|
||||
msgid "Personal info"
|
||||
msgstr "Infos Personnelles"
|
||||
|
||||
#: build/lib/core/admin.py:43 build/lib/core/admin.py:161 core/admin.py:43
|
||||
#: core/admin.py:161
|
||||
#: build/lib/core/admin.py:41 build/lib/core/admin.py:121 core/admin.py:41
|
||||
#: core/admin.py:121
|
||||
msgid "Permissions"
|
||||
msgstr "Permissions"
|
||||
|
||||
#: build/lib/core/admin.py:55 core/admin.py:55
|
||||
#: build/lib/core/admin.py:53 core/admin.py:53
|
||||
msgid "Important dates"
|
||||
msgstr "Dates importantes"
|
||||
|
||||
#: build/lib/core/admin.py:112 core/admin.py:112
|
||||
msgid "Import job created and queued."
|
||||
msgstr "Tâche d'importation créée et mise en file d'attente."
|
||||
|
||||
#: build/lib/core/admin.py:116 core/admin.py:116
|
||||
msgid "Process selected user reconciliations"
|
||||
msgstr "Traiter les rapprochements de l'utilisateur sélectionné"
|
||||
|
||||
#: build/lib/core/admin.py:171 core/admin.py:171
|
||||
#: build/lib/core/admin.py:131 core/admin.py:131
|
||||
msgid "Tree structure"
|
||||
msgstr "Arborescence"
|
||||
|
||||
#: build/lib/core/api/filters.py:48 core/api/filters.py:48
|
||||
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||
msgid "Title"
|
||||
msgstr "Titre"
|
||||
|
||||
#: build/lib/core/api/filters.py:62 core/api/filters.py:62
|
||||
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||
msgid "Creator is me"
|
||||
msgstr "Je suis l'auteur"
|
||||
|
||||
#: build/lib/core/api/filters.py:65 core/api/filters.py:65
|
||||
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||
msgid "Masked"
|
||||
msgstr "Masqué"
|
||||
|
||||
#: build/lib/core/api/filters.py:68 core/api/filters.py:68
|
||||
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
|
||||
msgid "Favorite"
|
||||
msgstr "Favoris"
|
||||
|
||||
#: build/lib/core/api/serializers.py:513 core/api/serializers.py:513
|
||||
#: build/lib/core/api/serializers.py:505 core/api/serializers.py:505
|
||||
msgid "A new document was created on your behalf!"
|
||||
msgstr "Un nouveau document a été créé pour vous !"
|
||||
|
||||
#: build/lib/core/api/serializers.py:517 core/api/serializers.py:517
|
||||
#: build/lib/core/api/serializers.py:509 core/api/serializers.py:509
|
||||
msgid "You have been granted ownership of a new document:"
|
||||
msgstr "Vous avez été déclaré propriétaire d'un nouveau document :"
|
||||
|
||||
#: build/lib/core/api/serializers.py:553 core/api/serializers.py:553
|
||||
#: build/lib/core/api/serializers.py:545 core/api/serializers.py:545
|
||||
msgid "This field is required."
|
||||
msgstr "Ce champ est obligatoire."
|
||||
|
||||
#: build/lib/core/api/serializers.py:564 core/api/serializers.py:564
|
||||
#: build/lib/core/api/serializers.py:556 core/api/serializers.py:556
|
||||
#, python-format
|
||||
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
|
||||
msgstr "La portée du lien '%(link_reach)s' n'est pas autorisée en fonction de la configuration du document parent."
|
||||
|
||||
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
|
||||
#: build/lib/core/api/viewsets.py:1122 core/api/viewsets.py:1122
|
||||
#, python-brace-format
|
||||
msgid "copy of {title}"
|
||||
msgstr "copie de {title}"
|
||||
@@ -143,381 +135,262 @@ msgstr "Gauche"
|
||||
msgid "Right"
|
||||
msgstr "Droite"
|
||||
|
||||
#: build/lib/core/models.py:80 core/models.py:80
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
msgid "id"
|
||||
msgstr "identifiant/id"
|
||||
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
#: build/lib/core/models.py:82 core/models.py:82
|
||||
msgid "primary key for the record as UUID"
|
||||
msgstr "clé primaire pour l'enregistrement en tant que UUID"
|
||||
|
||||
#: build/lib/core/models.py:87 core/models.py:87
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
msgid "created on"
|
||||
msgstr "créé le"
|
||||
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
#: build/lib/core/models.py:89 core/models.py:89
|
||||
msgid "date and time at which a record was created"
|
||||
msgstr "date et heure de création de l'enregistrement"
|
||||
|
||||
#: build/lib/core/models.py:93 core/models.py:93
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
msgid "updated on"
|
||||
msgstr "mis à jour le"
|
||||
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
#: build/lib/core/models.py:95 core/models.py:95
|
||||
msgid "date and time at which a record was last updated"
|
||||
msgstr "date et heure de la dernière mise à jour de l'enregistrement"
|
||||
|
||||
#: build/lib/core/models.py:130 core/models.py:130
|
||||
#: build/lib/core/models.py:131 core/models.py:131
|
||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||
msgstr "Nous n'avons pas pu trouver un utilisateur avec ce sous-groupe mais l'e-mail est déjà associé à un utilisateur enregistré."
|
||||
|
||||
#: build/lib/core/models.py:141 core/models.py:141
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
msgid "sub"
|
||||
msgstr "sous-groupe"
|
||||
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
#: build/lib/core/models.py:143 core/models.py:143
|
||||
msgid "Required. 255 characters or fewer. ASCII characters only."
|
||||
msgstr "Obligatoire. 255 caractères ou moins. Caractères ASCII uniquement."
|
||||
|
||||
#: build/lib/core/models.py:150 core/models.py:150
|
||||
#: build/lib/core/models.py:151 core/models.py:151
|
||||
msgid "full name"
|
||||
msgstr "nom complet"
|
||||
|
||||
#: build/lib/core/models.py:152 core/models.py:152
|
||||
#: build/lib/core/models.py:153 core/models.py:153
|
||||
msgid "short name"
|
||||
msgstr "nom court"
|
||||
|
||||
#: build/lib/core/models.py:155 core/models.py:155
|
||||
#: build/lib/core/models.py:156 core/models.py:156
|
||||
msgid "identity email address"
|
||||
msgstr "adresse e-mail d'identité"
|
||||
|
||||
#: build/lib/core/models.py:160 core/models.py:160
|
||||
#: build/lib/core/models.py:161 core/models.py:161
|
||||
msgid "admin email address"
|
||||
msgstr "adresse e-mail de l'administrateur"
|
||||
|
||||
#: build/lib/core/models.py:167 core/models.py:167
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
msgid "language"
|
||||
msgstr "langue"
|
||||
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
#: build/lib/core/models.py:169 core/models.py:169
|
||||
msgid "The language in which the user wants to see the interface."
|
||||
msgstr "La langue dans laquelle l'utilisateur veut voir l'interface."
|
||||
|
||||
#: build/lib/core/models.py:176 core/models.py:176
|
||||
#: build/lib/core/models.py:177 core/models.py:177
|
||||
msgid "The timezone in which the user wants to see times."
|
||||
msgstr "Le fuseau horaire dans lequel l'utilisateur souhaite voir les heures."
|
||||
|
||||
#: build/lib/core/models.py:179 core/models.py:179
|
||||
#: build/lib/core/models.py:180 core/models.py:180
|
||||
msgid "device"
|
||||
msgstr "appareil"
|
||||
|
||||
#: build/lib/core/models.py:181 core/models.py:181
|
||||
#: build/lib/core/models.py:182 core/models.py:182
|
||||
msgid "Whether the user is a device or a real user."
|
||||
msgstr "Si l'utilisateur est un appareil ou un utilisateur réel."
|
||||
|
||||
#: build/lib/core/models.py:184 core/models.py:184
|
||||
#: build/lib/core/models.py:185 core/models.py:185
|
||||
msgid "staff status"
|
||||
msgstr "statut d'équipe"
|
||||
|
||||
#: build/lib/core/models.py:186 core/models.py:186
|
||||
#: build/lib/core/models.py:187 core/models.py:187
|
||||
msgid "Whether the user can log into this admin site."
|
||||
msgstr "Si l'utilisateur peut se connecter à ce site d'administration."
|
||||
|
||||
#: build/lib/core/models.py:189 core/models.py:189
|
||||
#: build/lib/core/models.py:190 core/models.py:190
|
||||
msgid "active"
|
||||
msgstr "actif"
|
||||
|
||||
#: build/lib/core/models.py:192 core/models.py:192
|
||||
#: build/lib/core/models.py:193 core/models.py:193
|
||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||
msgstr "Si cet utilisateur doit être traité comme actif. Désélectionnez ceci au lieu de supprimer des comptes."
|
||||
|
||||
#: build/lib/core/models.py:204 core/models.py:204
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
msgid "user"
|
||||
msgstr "utilisateur"
|
||||
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
#: build/lib/core/models.py:206 core/models.py:206
|
||||
msgid "users"
|
||||
msgstr "utilisateurs"
|
||||
|
||||
#: build/lib/core/models.py:360 core/models.py:360
|
||||
msgid "Active email address"
|
||||
msgstr "Adresse email active"
|
||||
|
||||
#: build/lib/core/models.py:361 core/models.py:361
|
||||
msgid "Email address to deactivate"
|
||||
msgstr "Adresse email à désactiver"
|
||||
|
||||
#: build/lib/core/models.py:388 core/models.py:388
|
||||
msgid "Unique ID in the source file"
|
||||
msgstr "Identifiant unique dans le fichier source"
|
||||
|
||||
#: build/lib/core/models.py:394 build/lib/core/models.py:692 core/models.py:394
|
||||
#: core/models.py:692
|
||||
msgid "Pending"
|
||||
msgstr "En attente"
|
||||
|
||||
#: build/lib/core/models.py:395 core/models.py:395
|
||||
msgid "Ready"
|
||||
msgstr "Prêt"
|
||||
|
||||
#: build/lib/core/models.py:396 build/lib/core/models.py:694 core/models.py:396
|
||||
#: core/models.py:694
|
||||
msgid "Done"
|
||||
msgstr "Terminé"
|
||||
|
||||
#: build/lib/core/models.py:397 build/lib/core/models.py:695 core/models.py:397
|
||||
#: core/models.py:695
|
||||
msgid "Error"
|
||||
msgstr "Erreur"
|
||||
|
||||
#: build/lib/core/models.py:405 core/models.py:405
|
||||
msgid "user reconciliation"
|
||||
msgstr "rapprochement de l'utilisateur"
|
||||
|
||||
#: build/lib/core/models.py:406 core/models.py:406
|
||||
msgid "user reconciliations"
|
||||
msgstr "rapprochements de l'utilisateur"
|
||||
|
||||
#: build/lib/core/models.py:644 core/models.py:644
|
||||
msgid "You have requested a reconciliation of your user accounts on Docs.\n"
|
||||
" To confirm that you are the one who initiated the request\n"
|
||||
" and that this email belongs to you:"
|
||||
msgstr "Vous avez demandé un rapprochement de vos comptes utilisateur sur Docs.\n"
|
||||
" Pour confirmer que vous êtes bien à l'origine de cette demande\n"
|
||||
" et que cet e-mail vous appartient :"
|
||||
|
||||
#: build/lib/core/models.py:650 core/models.py:650
|
||||
msgid "Confirm by clicking the link to start the reconciliation"
|
||||
msgstr "Confirmez en cliquant sur le lien pour commencer le rapprochement"
|
||||
|
||||
#: build/lib/core/models.py:655 build/lib/core/models.py:761 core/models.py:655
|
||||
#: core/models.py:761
|
||||
msgid "Click here"
|
||||
msgstr "Cliquez ici"
|
||||
|
||||
#: build/lib/core/models.py:656 core/models.py:656
|
||||
msgid "Confirm"
|
||||
msgstr "Confirmer"
|
||||
|
||||
#: build/lib/core/models.py:667 core/models.py:667
|
||||
msgid "Your reconciliation request has been processed.\n"
|
||||
" New documents are likely associated with your account:"
|
||||
msgstr "Votre demande de rapprochement a été traitée.\n"
|
||||
" De nouveaux documents sont probablement associés à votre compte :"
|
||||
|
||||
#: build/lib/core/models.py:672 core/models.py:672
|
||||
msgid "Your accounts have been merged"
|
||||
msgstr "Vos comptes ont été fusionnés"
|
||||
|
||||
#: build/lib/core/models.py:677 core/models.py:677
|
||||
msgid "Click here to see"
|
||||
msgstr "Cliquez ici pour voir"
|
||||
|
||||
#: build/lib/core/models.py:678 core/models.py:678
|
||||
msgid "See my documents"
|
||||
msgstr "Voir mes documents"
|
||||
|
||||
#: build/lib/core/models.py:688 core/models.py:688
|
||||
msgid "CSV file"
|
||||
msgstr "Fichier CSV"
|
||||
|
||||
#: build/lib/core/models.py:693 core/models.py:693
|
||||
msgid "Running"
|
||||
msgstr "En cours"
|
||||
|
||||
#: build/lib/core/models.py:703 core/models.py:703
|
||||
msgid "user reconciliation CSV import"
|
||||
msgstr "importation CSV de rapprochement utilisateur"
|
||||
|
||||
#: build/lib/core/models.py:704 core/models.py:704
|
||||
msgid "user reconciliation CSV imports"
|
||||
msgstr "importations CSV de rapprochement utilisateur"
|
||||
|
||||
#: build/lib/core/models.py:748 core/models.py:748
|
||||
#, python-brace-format
|
||||
msgid "Your request for reconciliation was unsuccessful.\n"
|
||||
" Reconciliation failed for the following email addresses:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Please check for typos.\n"
|
||||
" You can submit another request with the valid email addresses."
|
||||
msgstr "Votre demande de rapprochement n'a pas abouti.\n"
|
||||
" Le rapprochement a échoué pour les adresses e-mail suivantes :\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Veuillez vérifier qu'il n'y a pas de fautes de frappe.\n"
|
||||
" Vous pouvez envoyer une nouvelle demande avec des adresses e-mail valides."
|
||||
|
||||
#: build/lib/core/models.py:756 core/models.py:756
|
||||
msgid "Reconciliation of your Docs accounts not completed"
|
||||
msgstr "Le rapprochement de vos comptes Docs n'est pas terminé"
|
||||
|
||||
#: build/lib/core/models.py:762 core/models.py:762
|
||||
msgid "Make a new request"
|
||||
msgstr "Faire une nouvelle demande"
|
||||
|
||||
#: build/lib/core/models.py:861 core/models.py:861
|
||||
#: build/lib/core/models.py:362 core/models.py:362
|
||||
msgid "title"
|
||||
msgstr "titre"
|
||||
|
||||
#: build/lib/core/models.py:862 core/models.py:862
|
||||
#: build/lib/core/models.py:363 core/models.py:363
|
||||
msgid "excerpt"
|
||||
msgstr "extrait"
|
||||
|
||||
#: build/lib/core/models.py:911 core/models.py:911
|
||||
#: build/lib/core/models.py:412 core/models.py:412
|
||||
msgid "Document"
|
||||
msgstr "Document"
|
||||
|
||||
#: build/lib/core/models.py:912 core/models.py:912
|
||||
#: build/lib/core/models.py:413 core/models.py:413
|
||||
msgid "Documents"
|
||||
msgstr "Documents"
|
||||
|
||||
#: build/lib/core/models.py:924 build/lib/core/models.py:1328
|
||||
#: core/models.py:924 core/models.py:1328
|
||||
#: build/lib/core/models.py:425 build/lib/core/models.py:828 core/models.py:425
|
||||
#: core/models.py:828
|
||||
msgid "Untitled Document"
|
||||
msgstr "Document sans titre"
|
||||
|
||||
#: build/lib/core/models.py:1329 core/models.py:1329
|
||||
#: build/lib/core/models.py:829 core/models.py:829
|
||||
msgid "Open"
|
||||
msgstr "Ouvrir"
|
||||
|
||||
#: build/lib/core/models.py:1364 core/models.py:1364
|
||||
#: build/lib/core/models.py:864 core/models.py:864
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you!"
|
||||
msgstr "{name} a partagé un document avec vous!"
|
||||
|
||||
#: build/lib/core/models.py:1368 core/models.py:1368
|
||||
#: build/lib/core/models.py:868 core/models.py:868
|
||||
#, python-brace-format
|
||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||
msgstr "{name} vous a invité avec le rôle \"{role}\" sur le document suivant :"
|
||||
|
||||
#: build/lib/core/models.py:1374 core/models.py:1374
|
||||
#: build/lib/core/models.py:874 core/models.py:874
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you: {title}"
|
||||
msgstr "{name} a partagé un document avec vous : {title}"
|
||||
|
||||
#: build/lib/core/models.py:1475 core/models.py:1475
|
||||
#: build/lib/core/models.py:975 core/models.py:975
|
||||
msgid "Document/user link trace"
|
||||
msgstr "Trace du lien document/utilisateur"
|
||||
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
#: build/lib/core/models.py:976 core/models.py:976
|
||||
msgid "Document/user link traces"
|
||||
msgstr "Traces du lien document/utilisateur"
|
||||
|
||||
#: build/lib/core/models.py:1482 core/models.py:1482
|
||||
#: build/lib/core/models.py:982 core/models.py:982
|
||||
msgid "A link trace already exists for this document/user."
|
||||
msgstr "Une trace de lien existe déjà pour ce document/utilisateur."
|
||||
|
||||
#: build/lib/core/models.py:1505 core/models.py:1505
|
||||
#: build/lib/core/models.py:1005 core/models.py:1005
|
||||
msgid "Document favorite"
|
||||
msgstr "Document favori"
|
||||
|
||||
#: build/lib/core/models.py:1506 core/models.py:1506
|
||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
||||
msgid "Document favorites"
|
||||
msgstr "Documents favoris"
|
||||
|
||||
#: build/lib/core/models.py:1512 core/models.py:1512
|
||||
#: build/lib/core/models.py:1012 core/models.py:1012
|
||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||
msgstr "Ce document est déjà un favori de cet utilisateur."
|
||||
|
||||
#: build/lib/core/models.py:1534 core/models.py:1534
|
||||
#: build/lib/core/models.py:1034 core/models.py:1034
|
||||
msgid "Document/user relation"
|
||||
msgstr "Relation document/utilisateur"
|
||||
|
||||
#: build/lib/core/models.py:1535 core/models.py:1535
|
||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
||||
msgid "Document/user relations"
|
||||
msgstr "Relations document/utilisateur"
|
||||
|
||||
#: build/lib/core/models.py:1541 core/models.py:1541
|
||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
||||
msgid "This user is already in this document."
|
||||
msgstr "Cet utilisateur est déjà dans ce document."
|
||||
|
||||
#: build/lib/core/models.py:1547 core/models.py:1547
|
||||
#: build/lib/core/models.py:1047 core/models.py:1047
|
||||
msgid "This team is already in this document."
|
||||
msgstr "Cette équipe est déjà dans ce document."
|
||||
|
||||
#: build/lib/core/models.py:1553 core/models.py:1553
|
||||
#: build/lib/core/models.py:1053 core/models.py:1053
|
||||
msgid "Either user or team must be set, not both."
|
||||
msgstr "L'utilisateur ou l'équipe doivent être définis, pas les deux."
|
||||
|
||||
#: build/lib/core/models.py:1704 core/models.py:1704
|
||||
#: build/lib/core/models.py:1204 core/models.py:1204
|
||||
msgid "Document ask for access"
|
||||
msgstr "Demande d'accès au document"
|
||||
|
||||
#: build/lib/core/models.py:1705 core/models.py:1705
|
||||
#: build/lib/core/models.py:1205 core/models.py:1205
|
||||
msgid "Document ask for accesses"
|
||||
msgstr "Demande d'accès au document"
|
||||
|
||||
#: build/lib/core/models.py:1711 core/models.py:1711
|
||||
#: build/lib/core/models.py:1211 core/models.py:1211
|
||||
msgid "This user has already asked for access to this document."
|
||||
msgstr "Cet utilisateur a déjà demandé l'accès à ce document."
|
||||
|
||||
#: build/lib/core/models.py:1768 core/models.py:1768
|
||||
#: build/lib/core/models.py:1268 core/models.py:1268
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to a document!"
|
||||
msgstr "{name} souhaiterait accéder au document suivant !"
|
||||
|
||||
#: build/lib/core/models.py:1772 core/models.py:1772
|
||||
#: build/lib/core/models.py:1272 core/models.py:1272
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to the following document:"
|
||||
msgstr "{name} souhaiterait accéder au document suivant :"
|
||||
|
||||
#: build/lib/core/models.py:1778 core/models.py:1778
|
||||
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||
#, python-brace-format
|
||||
msgid "{name} is asking for access to the document: {title}"
|
||||
msgstr "{name} demande l'accès au document : {title}"
|
||||
|
||||
#: build/lib/core/models.py:1820 core/models.py:1820
|
||||
#: build/lib/core/models.py:1320 core/models.py:1320
|
||||
msgid "Thread"
|
||||
msgstr "Conversation"
|
||||
|
||||
#: build/lib/core/models.py:1821 core/models.py:1821
|
||||
#: build/lib/core/models.py:1321 core/models.py:1321
|
||||
msgid "Threads"
|
||||
msgstr "Conversations"
|
||||
|
||||
#: build/lib/core/models.py:1824 build/lib/core/models.py:1876
|
||||
#: core/models.py:1824 core/models.py:1876
|
||||
#: build/lib/core/models.py:1324 build/lib/core/models.py:1376
|
||||
#: core/models.py:1324 core/models.py:1376
|
||||
msgid "Anonymous"
|
||||
msgstr "Anonyme"
|
||||
|
||||
#: build/lib/core/models.py:1871 core/models.py:1871
|
||||
#: build/lib/core/models.py:1371 core/models.py:1371
|
||||
msgid "Comment"
|
||||
msgstr "Commentaire"
|
||||
|
||||
#: build/lib/core/models.py:1872 core/models.py:1872
|
||||
#: build/lib/core/models.py:1372 core/models.py:1372
|
||||
msgid "Comments"
|
||||
msgstr "Commentaires"
|
||||
|
||||
#: build/lib/core/models.py:1921 core/models.py:1921
|
||||
#: build/lib/core/models.py:1421 core/models.py:1421
|
||||
msgid "This emoji has already been reacted to this comment."
|
||||
msgstr "Cet émoji a déjà été réagi à ce commentaire."
|
||||
|
||||
#: build/lib/core/models.py:1925 core/models.py:1925
|
||||
#: build/lib/core/models.py:1425 core/models.py:1425
|
||||
msgid "Reaction"
|
||||
msgstr "Réaction"
|
||||
|
||||
#: build/lib/core/models.py:1926 core/models.py:1926
|
||||
#: build/lib/core/models.py:1426 core/models.py:1426
|
||||
msgid "Reactions"
|
||||
msgstr "Réactions"
|
||||
|
||||
#: build/lib/core/models.py:1936 core/models.py:1936
|
||||
#: build/lib/core/models.py:1436 core/models.py:1436
|
||||
msgid "email address"
|
||||
msgstr "adresse e-mail"
|
||||
|
||||
#: build/lib/core/models.py:1955 core/models.py:1955
|
||||
#: build/lib/core/models.py:1455 core/models.py:1455
|
||||
msgid "Document invitation"
|
||||
msgstr "Invitation à un document"
|
||||
|
||||
#: build/lib/core/models.py:1956 core/models.py:1956
|
||||
#: build/lib/core/models.py:1456 core/models.py:1456
|
||||
msgid "Document invitations"
|
||||
msgstr "Invitations à un document"
|
||||
|
||||
#: build/lib/core/models.py:1976 core/models.py:1976
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
msgid "This email is already associated to a registered user."
|
||||
msgstr "Cette adresse email est déjà associée à un utilisateur inscrit."
|
||||
|
||||
#: build/lib/impress/settings.py:702 impress/settings.py:702
|
||||
msgid "Docs AI"
|
||||
msgstr "Docs IA"
|
||||
|
||||
#: core/templates/mail/html/template.html:153
|
||||
#: core/templates/mail/text/template.txt:3
|
||||
msgid "Logo email"
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lasuite-docs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
|
||||
"PO-Revision-Date: 2026-02-27 08:24\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0000\n"
|
||||
"PO-Revision-Date: 2026-01-28 20:12\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Italian\n"
|
||||
"Language: it_IT\n"
|
||||
@@ -17,65 +17,57 @@ msgstr ""
|
||||
"X-Crowdin-File: backend-impress.pot\n"
|
||||
"X-Crowdin-File-ID: 18\n"
|
||||
|
||||
#: build/lib/core/admin.py:30 core/admin.py:30
|
||||
#: build/lib/core/admin.py:28 core/admin.py:28
|
||||
msgid "Personal info"
|
||||
msgstr "Informazioni personali"
|
||||
|
||||
#: build/lib/core/admin.py:43 build/lib/core/admin.py:161 core/admin.py:43
|
||||
#: core/admin.py:161
|
||||
#: build/lib/core/admin.py:41 build/lib/core/admin.py:121 core/admin.py:41
|
||||
#: core/admin.py:121
|
||||
msgid "Permissions"
|
||||
msgstr "Permessi"
|
||||
|
||||
#: build/lib/core/admin.py:55 core/admin.py:55
|
||||
#: build/lib/core/admin.py:53 core/admin.py:53
|
||||
msgid "Important dates"
|
||||
msgstr "Date importanti"
|
||||
|
||||
#: build/lib/core/admin.py:112 core/admin.py:112
|
||||
msgid "Import job created and queued."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:116 core/admin.py:116
|
||||
msgid "Process selected user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:171 core/admin.py:171
|
||||
#: build/lib/core/admin.py:131 core/admin.py:131
|
||||
msgid "Tree structure"
|
||||
msgstr "Struttura ad albero"
|
||||
|
||||
#: build/lib/core/api/filters.py:48 core/api/filters.py:48
|
||||
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||
msgid "Title"
|
||||
msgstr "Titolo"
|
||||
|
||||
#: build/lib/core/api/filters.py:62 core/api/filters.py:62
|
||||
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||
msgid "Creator is me"
|
||||
msgstr "Il creatore sono io"
|
||||
|
||||
#: build/lib/core/api/filters.py:65 core/api/filters.py:65
|
||||
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||
msgid "Masked"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/filters.py:68 core/api/filters.py:68
|
||||
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
|
||||
msgid "Favorite"
|
||||
msgstr "Preferiti"
|
||||
|
||||
#: build/lib/core/api/serializers.py:513 core/api/serializers.py:513
|
||||
#: build/lib/core/api/serializers.py:505 core/api/serializers.py:505
|
||||
msgid "A new document was created on your behalf!"
|
||||
msgstr "Un nuovo documento è stato creato a tuo nome!"
|
||||
|
||||
#: build/lib/core/api/serializers.py:517 core/api/serializers.py:517
|
||||
#: build/lib/core/api/serializers.py:509 core/api/serializers.py:509
|
||||
msgid "You have been granted ownership of a new document:"
|
||||
msgstr "Sei ora proprietario di un nuovo documento:"
|
||||
|
||||
#: build/lib/core/api/serializers.py:553 core/api/serializers.py:553
|
||||
#: build/lib/core/api/serializers.py:545 core/api/serializers.py:545
|
||||
msgid "This field is required."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/serializers.py:564 core/api/serializers.py:564
|
||||
#: build/lib/core/api/serializers.py:556 core/api/serializers.py:556
|
||||
#, python-format
|
||||
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
|
||||
#: build/lib/core/api/viewsets.py:1122 core/api/viewsets.py:1122
|
||||
#, python-brace-format
|
||||
msgid "copy of {title}"
|
||||
msgstr "copia di {title}"
|
||||
@@ -143,374 +135,262 @@ msgstr "Sinistra"
|
||||
msgid "Right"
|
||||
msgstr "Destra"
|
||||
|
||||
#: build/lib/core/models.py:80 core/models.py:80
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
msgid "id"
|
||||
msgstr "Id"
|
||||
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
#: build/lib/core/models.py:82 core/models.py:82
|
||||
msgid "primary key for the record as UUID"
|
||||
msgstr "chiave primaria per il record come UUID"
|
||||
|
||||
#: build/lib/core/models.py:87 core/models.py:87
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
msgid "created on"
|
||||
msgstr "creato il"
|
||||
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
#: build/lib/core/models.py:89 core/models.py:89
|
||||
msgid "date and time at which a record was created"
|
||||
msgstr "data e ora in cui è stato creato un record"
|
||||
|
||||
#: build/lib/core/models.py:93 core/models.py:93
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
msgid "updated on"
|
||||
msgstr "aggiornato il"
|
||||
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
#: build/lib/core/models.py:95 core/models.py:95
|
||||
msgid "date and time at which a record was last updated"
|
||||
msgstr "data e ora in cui l’ultimo record è stato aggiornato"
|
||||
|
||||
#: build/lib/core/models.py:130 core/models.py:130
|
||||
#: build/lib/core/models.py:131 core/models.py:131
|
||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:141 core/models.py:141
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
msgid "sub"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
#: build/lib/core/models.py:143 core/models.py:143
|
||||
msgid "Required. 255 characters or fewer. ASCII characters only."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:150 core/models.py:150
|
||||
#: build/lib/core/models.py:151 core/models.py:151
|
||||
msgid "full name"
|
||||
msgstr "nome completo"
|
||||
|
||||
#: build/lib/core/models.py:152 core/models.py:152
|
||||
#: build/lib/core/models.py:153 core/models.py:153
|
||||
msgid "short name"
|
||||
msgstr "nome"
|
||||
|
||||
#: build/lib/core/models.py:155 core/models.py:155
|
||||
#: build/lib/core/models.py:156 core/models.py:156
|
||||
msgid "identity email address"
|
||||
msgstr "indirizzo email di identità"
|
||||
|
||||
#: build/lib/core/models.py:160 core/models.py:160
|
||||
#: build/lib/core/models.py:161 core/models.py:161
|
||||
msgid "admin email address"
|
||||
msgstr "Indirizzo email dell'amministratore"
|
||||
|
||||
#: build/lib/core/models.py:167 core/models.py:167
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
msgid "language"
|
||||
msgstr "lingua"
|
||||
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
#: build/lib/core/models.py:169 core/models.py:169
|
||||
msgid "The language in which the user wants to see the interface."
|
||||
msgstr "La lingua in cui l'utente vuole vedere l'interfaccia."
|
||||
|
||||
#: build/lib/core/models.py:176 core/models.py:176
|
||||
#: build/lib/core/models.py:177 core/models.py:177
|
||||
msgid "The timezone in which the user wants to see times."
|
||||
msgstr "Il fuso orario in cui l'utente vuole vedere gli orari."
|
||||
|
||||
#: build/lib/core/models.py:179 core/models.py:179
|
||||
#: build/lib/core/models.py:180 core/models.py:180
|
||||
msgid "device"
|
||||
msgstr "dispositivo"
|
||||
|
||||
#: build/lib/core/models.py:181 core/models.py:181
|
||||
#: build/lib/core/models.py:182 core/models.py:182
|
||||
msgid "Whether the user is a device or a real user."
|
||||
msgstr "Se l'utente è un dispositivo o un utente reale."
|
||||
|
||||
#: build/lib/core/models.py:184 core/models.py:184
|
||||
#: build/lib/core/models.py:185 core/models.py:185
|
||||
msgid "staff status"
|
||||
msgstr "stato del personale"
|
||||
|
||||
#: build/lib/core/models.py:186 core/models.py:186
|
||||
#: build/lib/core/models.py:187 core/models.py:187
|
||||
msgid "Whether the user can log into this admin site."
|
||||
msgstr "Indica se l'utente può accedere a questo sito amministratore."
|
||||
|
||||
#: build/lib/core/models.py:189 core/models.py:189
|
||||
#: build/lib/core/models.py:190 core/models.py:190
|
||||
msgid "active"
|
||||
msgstr "attivo"
|
||||
|
||||
#: build/lib/core/models.py:192 core/models.py:192
|
||||
#: build/lib/core/models.py:193 core/models.py:193
|
||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||
msgstr "Indica se questo utente deve essere trattato come attivo. Deseleziona invece di eliminare gli account."
|
||||
|
||||
#: build/lib/core/models.py:204 core/models.py:204
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
msgid "user"
|
||||
msgstr "utente"
|
||||
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
#: build/lib/core/models.py:206 core/models.py:206
|
||||
msgid "users"
|
||||
msgstr "utenti"
|
||||
|
||||
#: build/lib/core/models.py:360 core/models.py:360
|
||||
msgid "Active email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:361 core/models.py:361
|
||||
msgid "Email address to deactivate"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:388 core/models.py:388
|
||||
msgid "Unique ID in the source file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:394 build/lib/core/models.py:692 core/models.py:394
|
||||
#: core/models.py:692
|
||||
msgid "Pending"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:395 core/models.py:395
|
||||
msgid "Ready"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:396 build/lib/core/models.py:694 core/models.py:396
|
||||
#: core/models.py:694
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:397 build/lib/core/models.py:695 core/models.py:397
|
||||
#: core/models.py:695
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:405 core/models.py:405
|
||||
msgid "user reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:406 core/models.py:406
|
||||
msgid "user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:644 core/models.py:644
|
||||
msgid "You have requested a reconciliation of your user accounts on Docs.\n"
|
||||
" To confirm that you are the one who initiated the request\n"
|
||||
" and that this email belongs to you:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:650 core/models.py:650
|
||||
msgid "Confirm by clicking the link to start the reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:655 build/lib/core/models.py:761 core/models.py:655
|
||||
#: core/models.py:761
|
||||
msgid "Click here"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:656 core/models.py:656
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:667 core/models.py:667
|
||||
msgid "Your reconciliation request has been processed.\n"
|
||||
" New documents are likely associated with your account:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:672 core/models.py:672
|
||||
msgid "Your accounts have been merged"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:677 core/models.py:677
|
||||
msgid "Click here to see"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:678 core/models.py:678
|
||||
msgid "See my documents"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:688 core/models.py:688
|
||||
msgid "CSV file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:693 core/models.py:693
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:703 core/models.py:703
|
||||
msgid "user reconciliation CSV import"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:704 core/models.py:704
|
||||
msgid "user reconciliation CSV imports"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:748 core/models.py:748
|
||||
#, python-brace-format
|
||||
msgid "Your request for reconciliation was unsuccessful.\n"
|
||||
" Reconciliation failed for the following email addresses:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Please check for typos.\n"
|
||||
" You can submit another request with the valid email addresses."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:756 core/models.py:756
|
||||
msgid "Reconciliation of your Docs accounts not completed"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:762 core/models.py:762
|
||||
msgid "Make a new request"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:861 core/models.py:861
|
||||
#: build/lib/core/models.py:362 core/models.py:362
|
||||
msgid "title"
|
||||
msgstr "titolo"
|
||||
|
||||
#: build/lib/core/models.py:862 core/models.py:862
|
||||
#: build/lib/core/models.py:363 core/models.py:363
|
||||
msgid "excerpt"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:911 core/models.py:911
|
||||
#: build/lib/core/models.py:412 core/models.py:412
|
||||
msgid "Document"
|
||||
msgstr "Documento"
|
||||
|
||||
#: build/lib/core/models.py:912 core/models.py:912
|
||||
#: build/lib/core/models.py:413 core/models.py:413
|
||||
msgid "Documents"
|
||||
msgstr "Documenti"
|
||||
|
||||
#: build/lib/core/models.py:924 build/lib/core/models.py:1328
|
||||
#: core/models.py:924 core/models.py:1328
|
||||
#: build/lib/core/models.py:425 build/lib/core/models.py:828 core/models.py:425
|
||||
#: core/models.py:828
|
||||
msgid "Untitled Document"
|
||||
msgstr "Documento senza titolo"
|
||||
|
||||
#: build/lib/core/models.py:1329 core/models.py:1329
|
||||
#: build/lib/core/models.py:829 core/models.py:829
|
||||
msgid "Open"
|
||||
msgstr "Apri"
|
||||
|
||||
#: build/lib/core/models.py:1364 core/models.py:1364
|
||||
#: build/lib/core/models.py:864 core/models.py:864
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you!"
|
||||
msgstr "{name} ha condiviso un documento con te!"
|
||||
|
||||
#: build/lib/core/models.py:1368 core/models.py:1368
|
||||
#: build/lib/core/models.py:868 core/models.py:868
|
||||
#, python-brace-format
|
||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||
msgstr "{name} ti ha invitato con il ruolo \"{role}\" nel seguente documento:"
|
||||
|
||||
#: build/lib/core/models.py:1374 core/models.py:1374
|
||||
#: build/lib/core/models.py:874 core/models.py:874
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you: {title}"
|
||||
msgstr "{name} ha condiviso un documento con te: {title}"
|
||||
|
||||
#: build/lib/core/models.py:1475 core/models.py:1475
|
||||
#: build/lib/core/models.py:975 core/models.py:975
|
||||
msgid "Document/user link trace"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
#: build/lib/core/models.py:976 core/models.py:976
|
||||
msgid "Document/user link traces"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1482 core/models.py:1482
|
||||
#: build/lib/core/models.py:982 core/models.py:982
|
||||
msgid "A link trace already exists for this document/user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1505 core/models.py:1505
|
||||
#: build/lib/core/models.py:1005 core/models.py:1005
|
||||
msgid "Document favorite"
|
||||
msgstr "Documento preferito"
|
||||
|
||||
#: build/lib/core/models.py:1506 core/models.py:1506
|
||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
||||
msgid "Document favorites"
|
||||
msgstr "Documenti preferiti"
|
||||
|
||||
#: build/lib/core/models.py:1512 core/models.py:1512
|
||||
#: build/lib/core/models.py:1012 core/models.py:1012
|
||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1534 core/models.py:1534
|
||||
#: build/lib/core/models.py:1034 core/models.py:1034
|
||||
msgid "Document/user relation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1535 core/models.py:1535
|
||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
||||
msgid "Document/user relations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1541 core/models.py:1541
|
||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
||||
msgid "This user is already in this document."
|
||||
msgstr "Questo utente è già presente in questo documento."
|
||||
|
||||
#: build/lib/core/models.py:1547 core/models.py:1547
|
||||
#: build/lib/core/models.py:1047 core/models.py:1047
|
||||
msgid "This team is already in this document."
|
||||
msgstr "Questo team è già presente in questo documento."
|
||||
|
||||
#: build/lib/core/models.py:1553 core/models.py:1553
|
||||
#: build/lib/core/models.py:1053 core/models.py:1053
|
||||
msgid "Either user or team must be set, not both."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1704 core/models.py:1704
|
||||
#: build/lib/core/models.py:1204 core/models.py:1204
|
||||
msgid "Document ask for access"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1705 core/models.py:1705
|
||||
#: build/lib/core/models.py:1205 core/models.py:1205
|
||||
msgid "Document ask for accesses"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1711 core/models.py:1711
|
||||
#: build/lib/core/models.py:1211 core/models.py:1211
|
||||
msgid "This user has already asked for access to this document."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1768 core/models.py:1768
|
||||
#: build/lib/core/models.py:1268 core/models.py:1268
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to a document!"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1772 core/models.py:1772
|
||||
#: build/lib/core/models.py:1272 core/models.py:1272
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to the following document:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1778 core/models.py:1778
|
||||
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||
#, python-brace-format
|
||||
msgid "{name} is asking for access to the document: {title}"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1820 core/models.py:1820
|
||||
#: build/lib/core/models.py:1320 core/models.py:1320
|
||||
msgid "Thread"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1821 core/models.py:1821
|
||||
#: build/lib/core/models.py:1321 core/models.py:1321
|
||||
msgid "Threads"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1824 build/lib/core/models.py:1876
|
||||
#: core/models.py:1824 core/models.py:1876
|
||||
#: build/lib/core/models.py:1324 build/lib/core/models.py:1376
|
||||
#: core/models.py:1324 core/models.py:1376
|
||||
msgid "Anonymous"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1871 core/models.py:1871
|
||||
#: build/lib/core/models.py:1371 core/models.py:1371
|
||||
msgid "Comment"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1872 core/models.py:1872
|
||||
#: build/lib/core/models.py:1372 core/models.py:1372
|
||||
msgid "Comments"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1921 core/models.py:1921
|
||||
#: build/lib/core/models.py:1421 core/models.py:1421
|
||||
msgid "This emoji has already been reacted to this comment."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1925 core/models.py:1925
|
||||
#: build/lib/core/models.py:1425 core/models.py:1425
|
||||
msgid "Reaction"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1926 core/models.py:1926
|
||||
#: build/lib/core/models.py:1426 core/models.py:1426
|
||||
msgid "Reactions"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1936 core/models.py:1936
|
||||
#: build/lib/core/models.py:1436 core/models.py:1436
|
||||
msgid "email address"
|
||||
msgstr "indirizzo e-mail"
|
||||
|
||||
#: build/lib/core/models.py:1955 core/models.py:1955
|
||||
#: build/lib/core/models.py:1455 core/models.py:1455
|
||||
msgid "Document invitation"
|
||||
msgstr "Invito al documento"
|
||||
|
||||
#: build/lib/core/models.py:1956 core/models.py:1956
|
||||
#: build/lib/core/models.py:1456 core/models.py:1456
|
||||
msgid "Document invitations"
|
||||
msgstr "Inviti al documento"
|
||||
|
||||
#: build/lib/core/models.py:1976 core/models.py:1976
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
msgid "This email is already associated to a registered user."
|
||||
msgstr "Questa email è già associata a un utente registrato."
|
||||
|
||||
#: build/lib/impress/settings.py:702 impress/settings.py:702
|
||||
msgid "Docs AI"
|
||||
msgstr ""
|
||||
|
||||
#: core/templates/mail/html/template.html:153
|
||||
#: core/templates/mail/text/template.txt:3
|
||||
msgid "Logo email"
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lasuite-docs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
|
||||
"PO-Revision-Date: 2026-02-27 08:24\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0000\n"
|
||||
"PO-Revision-Date: 2026-01-28 20:12\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Dutch\n"
|
||||
"Language: nl_NL\n"
|
||||
@@ -17,65 +17,57 @@ msgstr ""
|
||||
"X-Crowdin-File: backend-impress.pot\n"
|
||||
"X-Crowdin-File-ID: 18\n"
|
||||
|
||||
#: build/lib/core/admin.py:30 core/admin.py:30
|
||||
#: build/lib/core/admin.py:28 core/admin.py:28
|
||||
msgid "Personal info"
|
||||
msgstr "Persoonlijke informatie"
|
||||
|
||||
#: build/lib/core/admin.py:43 build/lib/core/admin.py:161 core/admin.py:43
|
||||
#: core/admin.py:161
|
||||
#: build/lib/core/admin.py:41 build/lib/core/admin.py:121 core/admin.py:41
|
||||
#: core/admin.py:121
|
||||
msgid "Permissions"
|
||||
msgstr "Machtigingen"
|
||||
|
||||
#: build/lib/core/admin.py:55 core/admin.py:55
|
||||
#: build/lib/core/admin.py:53 core/admin.py:53
|
||||
msgid "Important dates"
|
||||
msgstr "Belangrijke data"
|
||||
|
||||
#: build/lib/core/admin.py:112 core/admin.py:112
|
||||
msgid "Import job created and queued."
|
||||
msgstr "Import taak gemaakt en in de wachtrij geplaatst."
|
||||
|
||||
#: build/lib/core/admin.py:116 core/admin.py:116
|
||||
msgid "Process selected user reconciliations"
|
||||
msgstr "Verwerk geselecteerde gebruikers samenvoeging"
|
||||
|
||||
#: build/lib/core/admin.py:171 core/admin.py:171
|
||||
#: build/lib/core/admin.py:131 core/admin.py:131
|
||||
msgid "Tree structure"
|
||||
msgstr "Boomstructuur"
|
||||
|
||||
#: build/lib/core/api/filters.py:48 core/api/filters.py:48
|
||||
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||
msgid "Title"
|
||||
msgstr "Titel"
|
||||
|
||||
#: build/lib/core/api/filters.py:62 core/api/filters.py:62
|
||||
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||
msgid "Creator is me"
|
||||
msgstr "Ik ben eigenaar"
|
||||
|
||||
#: build/lib/core/api/filters.py:65 core/api/filters.py:65
|
||||
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||
msgid "Masked"
|
||||
msgstr "Gemaskeerd"
|
||||
|
||||
#: build/lib/core/api/filters.py:68 core/api/filters.py:68
|
||||
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
|
||||
msgid "Favorite"
|
||||
msgstr "Favoriet"
|
||||
|
||||
#: build/lib/core/api/serializers.py:513 core/api/serializers.py:513
|
||||
#: build/lib/core/api/serializers.py:505 core/api/serializers.py:505
|
||||
msgid "A new document was created on your behalf!"
|
||||
msgstr "Een nieuw document is namens u gemaakt!"
|
||||
|
||||
#: build/lib/core/api/serializers.py:517 core/api/serializers.py:517
|
||||
#: build/lib/core/api/serializers.py:509 core/api/serializers.py:509
|
||||
msgid "You have been granted ownership of a new document:"
|
||||
msgstr "U heeft eigenaarschap van een nieuw document gekregen:"
|
||||
|
||||
#: build/lib/core/api/serializers.py:553 core/api/serializers.py:553
|
||||
#: build/lib/core/api/serializers.py:545 core/api/serializers.py:545
|
||||
msgid "This field is required."
|
||||
msgstr "Dit veld is verplicht."
|
||||
|
||||
#: build/lib/core/api/serializers.py:564 core/api/serializers.py:564
|
||||
#: build/lib/core/api/serializers.py:556 core/api/serializers.py:556
|
||||
#, python-format
|
||||
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
|
||||
msgstr "Link bereik '%(link_reach)s' is niet toegestaan op basis van bovenliggende documentconfiguratie."
|
||||
|
||||
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
|
||||
#: build/lib/core/api/viewsets.py:1122 core/api/viewsets.py:1122
|
||||
#, python-brace-format
|
||||
msgid "copy of {title}"
|
||||
msgstr "kopie van {title}"
|
||||
@@ -143,381 +135,262 @@ msgstr "Links"
|
||||
msgid "Right"
|
||||
msgstr "Rechts"
|
||||
|
||||
#: build/lib/core/models.py:80 core/models.py:80
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
msgid "id"
|
||||
msgstr "id"
|
||||
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
#: build/lib/core/models.py:82 core/models.py:82
|
||||
msgid "primary key for the record as UUID"
|
||||
msgstr "primaire sleutel voor dossier als UUID"
|
||||
|
||||
#: build/lib/core/models.py:87 core/models.py:87
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
msgid "created on"
|
||||
msgstr "gecreëerd op"
|
||||
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
#: build/lib/core/models.py:89 core/models.py:89
|
||||
msgid "date and time at which a record was created"
|
||||
msgstr "datum en tijd waarop dossier is gecreeërd"
|
||||
|
||||
#: build/lib/core/models.py:93 core/models.py:93
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
msgid "updated on"
|
||||
msgstr "Laatst gewijzigd op"
|
||||
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
#: build/lib/core/models.py:95 core/models.py:95
|
||||
msgid "date and time at which a record was last updated"
|
||||
msgstr "datum en tijd waarop dossier laatst was gewijzigd"
|
||||
|
||||
#: build/lib/core/models.py:130 core/models.py:130
|
||||
#: build/lib/core/models.py:131 core/models.py:131
|
||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||
msgstr "Wij konden geen gebruiker vinden met dit id, maar de email is al geassocieerd met een geregistreerde gebruiker."
|
||||
|
||||
#: build/lib/core/models.py:141 core/models.py:141
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
msgid "sub"
|
||||
msgstr "id"
|
||||
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
#: build/lib/core/models.py:143 core/models.py:143
|
||||
msgid "Required. 255 characters or fewer. ASCII characters only."
|
||||
msgstr "Vereist. 255 tekens of minder. Alleen ASCII tekens."
|
||||
|
||||
#: build/lib/core/models.py:150 core/models.py:150
|
||||
#: build/lib/core/models.py:151 core/models.py:151
|
||||
msgid "full name"
|
||||
msgstr "volledige naam"
|
||||
|
||||
#: build/lib/core/models.py:152 core/models.py:152
|
||||
#: build/lib/core/models.py:153 core/models.py:153
|
||||
msgid "short name"
|
||||
msgstr "gebruikersnaam"
|
||||
|
||||
#: build/lib/core/models.py:155 core/models.py:155
|
||||
#: build/lib/core/models.py:156 core/models.py:156
|
||||
msgid "identity email address"
|
||||
msgstr "identiteit emailadres"
|
||||
|
||||
#: build/lib/core/models.py:160 core/models.py:160
|
||||
#: build/lib/core/models.py:161 core/models.py:161
|
||||
msgid "admin email address"
|
||||
msgstr "admin emailadres"
|
||||
|
||||
#: build/lib/core/models.py:167 core/models.py:167
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
msgid "language"
|
||||
msgstr "taal"
|
||||
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
#: build/lib/core/models.py:169 core/models.py:169
|
||||
msgid "The language in which the user wants to see the interface."
|
||||
msgstr "De taal waarin de gebruiker de interface wil zien."
|
||||
|
||||
#: build/lib/core/models.py:176 core/models.py:176
|
||||
#: build/lib/core/models.py:177 core/models.py:177
|
||||
msgid "The timezone in which the user wants to see times."
|
||||
msgstr "De tijdzone waarin de gebruiker de tijden wil zien."
|
||||
|
||||
#: build/lib/core/models.py:179 core/models.py:179
|
||||
#: build/lib/core/models.py:180 core/models.py:180
|
||||
msgid "device"
|
||||
msgstr "apparaat"
|
||||
|
||||
#: build/lib/core/models.py:181 core/models.py:181
|
||||
#: build/lib/core/models.py:182 core/models.py:182
|
||||
msgid "Whether the user is a device or a real user."
|
||||
msgstr "Of de gebruiker een apparaat is of een echte gebruiker."
|
||||
|
||||
#: build/lib/core/models.py:184 core/models.py:184
|
||||
#: build/lib/core/models.py:185 core/models.py:185
|
||||
msgid "staff status"
|
||||
msgstr "beheerder status"
|
||||
|
||||
#: build/lib/core/models.py:186 core/models.py:186
|
||||
#: build/lib/core/models.py:187 core/models.py:187
|
||||
msgid "Whether the user can log into this admin site."
|
||||
msgstr "Of de gebruiker kan inloggen in het beheer gedeelte."
|
||||
|
||||
#: build/lib/core/models.py:189 core/models.py:189
|
||||
#: build/lib/core/models.py:190 core/models.py:190
|
||||
msgid "active"
|
||||
msgstr "actief"
|
||||
|
||||
#: build/lib/core/models.py:192 core/models.py:192
|
||||
#: build/lib/core/models.py:193 core/models.py:193
|
||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||
msgstr "Of een gebruiker als actief moet worden beschouwd. Deselecteer dit in plaats van het account te deleten."
|
||||
|
||||
#: build/lib/core/models.py:204 core/models.py:204
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
msgid "user"
|
||||
msgstr "gebruiker"
|
||||
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
#: build/lib/core/models.py:206 core/models.py:206
|
||||
msgid "users"
|
||||
msgstr "gebruikers"
|
||||
|
||||
#: build/lib/core/models.py:360 core/models.py:360
|
||||
msgid "Active email address"
|
||||
msgstr "Actieve e-mail adres"
|
||||
|
||||
#: build/lib/core/models.py:361 core/models.py:361
|
||||
msgid "Email address to deactivate"
|
||||
msgstr "E-mailadres om te deactiveren"
|
||||
|
||||
#: build/lib/core/models.py:388 core/models.py:388
|
||||
msgid "Unique ID in the source file"
|
||||
msgstr "Unieke ID in het bronbestand"
|
||||
|
||||
#: build/lib/core/models.py:394 build/lib/core/models.py:692 core/models.py:394
|
||||
#: core/models.py:692
|
||||
msgid "Pending"
|
||||
msgstr "In behandeling"
|
||||
|
||||
#: build/lib/core/models.py:395 core/models.py:395
|
||||
msgid "Ready"
|
||||
msgstr "Klaar"
|
||||
|
||||
#: build/lib/core/models.py:396 build/lib/core/models.py:694 core/models.py:396
|
||||
#: core/models.py:694
|
||||
msgid "Done"
|
||||
msgstr "Klaar"
|
||||
|
||||
#: build/lib/core/models.py:397 build/lib/core/models.py:695 core/models.py:397
|
||||
#: core/models.py:695
|
||||
msgid "Error"
|
||||
msgstr "Fout"
|
||||
|
||||
#: build/lib/core/models.py:405 core/models.py:405
|
||||
msgid "user reconciliation"
|
||||
msgstr "gebruiker samenvoegen"
|
||||
|
||||
#: build/lib/core/models.py:406 core/models.py:406
|
||||
msgid "user reconciliations"
|
||||
msgstr "gebruikers samenvoegen"
|
||||
|
||||
#: build/lib/core/models.py:644 core/models.py:644
|
||||
msgid "You have requested a reconciliation of your user accounts on Docs.\n"
|
||||
" To confirm that you are the one who initiated the request\n"
|
||||
" and that this email belongs to you:"
|
||||
msgstr "Je hebt gevraagd om een samenvoeging van je gebruikersaccounts op Docs.\n"
|
||||
" Om te bevestigen dat u degene bent die het verzoek\n"
|
||||
" heeft geïnitieerd en dat deze e-mail van u is:"
|
||||
|
||||
#: build/lib/core/models.py:650 core/models.py:650
|
||||
msgid "Confirm by clicking the link to start the reconciliation"
|
||||
msgstr "Bevestig door te klikken op de link om de samenvoeging te starten"
|
||||
|
||||
#: build/lib/core/models.py:655 build/lib/core/models.py:761 core/models.py:655
|
||||
#: core/models.py:761
|
||||
msgid "Click here"
|
||||
msgstr "Klik hier"
|
||||
|
||||
#: build/lib/core/models.py:656 core/models.py:656
|
||||
msgid "Confirm"
|
||||
msgstr "Bevestig"
|
||||
|
||||
#: build/lib/core/models.py:667 core/models.py:667
|
||||
msgid "Your reconciliation request has been processed.\n"
|
||||
" New documents are likely associated with your account:"
|
||||
msgstr "Uw samenvoegingsverzoek is verwerkt.\n"
|
||||
" Nieuwe documenten worden waarschijnlijk geassocieerd met uw account:"
|
||||
|
||||
#: build/lib/core/models.py:672 core/models.py:672
|
||||
msgid "Your accounts have been merged"
|
||||
msgstr "Je accounts zijn samengevoegd"
|
||||
|
||||
#: build/lib/core/models.py:677 core/models.py:677
|
||||
msgid "Click here to see"
|
||||
msgstr "Klik hier om te bekijken"
|
||||
|
||||
#: build/lib/core/models.py:678 core/models.py:678
|
||||
msgid "See my documents"
|
||||
msgstr "Mijn documenten bekijken"
|
||||
|
||||
#: build/lib/core/models.py:688 core/models.py:688
|
||||
msgid "CSV file"
|
||||
msgstr "CSV bestand"
|
||||
|
||||
#: build/lib/core/models.py:693 core/models.py:693
|
||||
msgid "Running"
|
||||
msgstr "Bezig"
|
||||
|
||||
#: build/lib/core/models.py:703 core/models.py:703
|
||||
msgid "user reconciliation CSV import"
|
||||
msgstr "gebruiker samenvoeging CSV import"
|
||||
|
||||
#: build/lib/core/models.py:704 core/models.py:704
|
||||
msgid "user reconciliation CSV imports"
|
||||
msgstr "gebruiker reconciliation CSV imports"
|
||||
|
||||
#: build/lib/core/models.py:748 core/models.py:748
|
||||
#, python-brace-format
|
||||
msgid "Your request for reconciliation was unsuccessful.\n"
|
||||
" Reconciliation failed for the following email addresses:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Please check for typos.\n"
|
||||
" You can submit another request with the valid email addresses."
|
||||
msgstr "Uw verzoek tot verzoening is mislukt.\n"
|
||||
" Verzoening mislukt voor de volgende e-mailadressen:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Controleer op typefouten.\n"
|
||||
" U kunt een ander verzoek indienen met de geldige e-mailadressen."
|
||||
|
||||
#: build/lib/core/models.py:756 core/models.py:756
|
||||
msgid "Reconciliation of your Docs accounts not completed"
|
||||
msgstr "Samenvoeging van je Docs accounts is niet voltooid"
|
||||
|
||||
#: build/lib/core/models.py:762 core/models.py:762
|
||||
msgid "Make a new request"
|
||||
msgstr "Maak een nieuw verzoek"
|
||||
|
||||
#: build/lib/core/models.py:861 core/models.py:861
|
||||
#: build/lib/core/models.py:362 core/models.py:362
|
||||
msgid "title"
|
||||
msgstr "titel"
|
||||
|
||||
#: build/lib/core/models.py:862 core/models.py:862
|
||||
#: build/lib/core/models.py:363 core/models.py:363
|
||||
msgid "excerpt"
|
||||
msgstr "uittreksel"
|
||||
|
||||
#: build/lib/core/models.py:911 core/models.py:911
|
||||
#: build/lib/core/models.py:412 core/models.py:412
|
||||
msgid "Document"
|
||||
msgstr "Document"
|
||||
|
||||
#: build/lib/core/models.py:912 core/models.py:912
|
||||
#: build/lib/core/models.py:413 core/models.py:413
|
||||
msgid "Documents"
|
||||
msgstr "Documenten"
|
||||
|
||||
#: build/lib/core/models.py:924 build/lib/core/models.py:1328
|
||||
#: core/models.py:924 core/models.py:1328
|
||||
#: build/lib/core/models.py:425 build/lib/core/models.py:828 core/models.py:425
|
||||
#: core/models.py:828
|
||||
msgid "Untitled Document"
|
||||
msgstr "Naamloos Document"
|
||||
|
||||
#: build/lib/core/models.py:1329 core/models.py:1329
|
||||
#: build/lib/core/models.py:829 core/models.py:829
|
||||
msgid "Open"
|
||||
msgstr "Open"
|
||||
|
||||
#: build/lib/core/models.py:1364 core/models.py:1364
|
||||
#: build/lib/core/models.py:864 core/models.py:864
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you!"
|
||||
msgstr "{name} heeft een document met u gedeeld!"
|
||||
|
||||
#: build/lib/core/models.py:1368 core/models.py:1368
|
||||
#: build/lib/core/models.py:868 core/models.py:868
|
||||
#, python-brace-format
|
||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||
msgstr "{name} heeft u uitgenodigd met de rol \"{role}\" op het volgende document:"
|
||||
|
||||
#: build/lib/core/models.py:1374 core/models.py:1374
|
||||
#: build/lib/core/models.py:874 core/models.py:874
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you: {title}"
|
||||
msgstr "{name} heeft een document met u gedeeld: {title}"
|
||||
|
||||
#: build/lib/core/models.py:1475 core/models.py:1475
|
||||
#: build/lib/core/models.py:975 core/models.py:975
|
||||
msgid "Document/user link trace"
|
||||
msgstr "Document/gebruiker link"
|
||||
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
#: build/lib/core/models.py:976 core/models.py:976
|
||||
msgid "Document/user link traces"
|
||||
msgstr "Document/gebruiker link"
|
||||
|
||||
#: build/lib/core/models.py:1482 core/models.py:1482
|
||||
#: build/lib/core/models.py:982 core/models.py:982
|
||||
msgid "A link trace already exists for this document/user."
|
||||
msgstr "Een link bestaat al voor dit document/deze gebruiker."
|
||||
|
||||
#: build/lib/core/models.py:1505 core/models.py:1505
|
||||
#: build/lib/core/models.py:1005 core/models.py:1005
|
||||
msgid "Document favorite"
|
||||
msgstr "Document favoriet"
|
||||
|
||||
#: build/lib/core/models.py:1506 core/models.py:1506
|
||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
||||
msgid "Document favorites"
|
||||
msgstr "Document favorieten"
|
||||
|
||||
#: build/lib/core/models.py:1512 core/models.py:1512
|
||||
#: build/lib/core/models.py:1012 core/models.py:1012
|
||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||
msgstr "Dit document is al in gebruik als favoriet door dezelfde gebruiker."
|
||||
|
||||
#: build/lib/core/models.py:1534 core/models.py:1534
|
||||
#: build/lib/core/models.py:1034 core/models.py:1034
|
||||
msgid "Document/user relation"
|
||||
msgstr "Document/gebruiker relatie"
|
||||
|
||||
#: build/lib/core/models.py:1535 core/models.py:1535
|
||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
||||
msgid "Document/user relations"
|
||||
msgstr "Document/gebruiker relaties"
|
||||
|
||||
#: build/lib/core/models.py:1541 core/models.py:1541
|
||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
||||
msgid "This user is already in this document."
|
||||
msgstr "De gebruiker bestaat al in dit document."
|
||||
|
||||
#: build/lib/core/models.py:1547 core/models.py:1547
|
||||
#: build/lib/core/models.py:1047 core/models.py:1047
|
||||
msgid "This team is already in this document."
|
||||
msgstr "Dit team bestaat al in dit document."
|
||||
|
||||
#: build/lib/core/models.py:1553 core/models.py:1553
|
||||
#: build/lib/core/models.py:1053 core/models.py:1053
|
||||
msgid "Either user or team must be set, not both."
|
||||
msgstr "Een gebruiker of team moet gekozen worden, maar niet beide."
|
||||
|
||||
#: build/lib/core/models.py:1704 core/models.py:1704
|
||||
#: build/lib/core/models.py:1204 core/models.py:1204
|
||||
msgid "Document ask for access"
|
||||
msgstr "Document verzoekt om toegang"
|
||||
|
||||
#: build/lib/core/models.py:1705 core/models.py:1705
|
||||
#: build/lib/core/models.py:1205 core/models.py:1205
|
||||
msgid "Document ask for accesses"
|
||||
msgstr "Document verzoekt om toegangen"
|
||||
|
||||
#: build/lib/core/models.py:1711 core/models.py:1711
|
||||
#: build/lib/core/models.py:1211 core/models.py:1211
|
||||
msgid "This user has already asked for access to this document."
|
||||
msgstr "Deze gebruiker heeft al om toegang tot dit document gevraagd."
|
||||
|
||||
#: build/lib/core/models.py:1768 core/models.py:1768
|
||||
#: build/lib/core/models.py:1268 core/models.py:1268
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to a document!"
|
||||
msgstr "{name} verzoekt toegang tot een document!"
|
||||
|
||||
#: build/lib/core/models.py:1772 core/models.py:1772
|
||||
#: build/lib/core/models.py:1272 core/models.py:1272
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to the following document:"
|
||||
msgstr "{name} verzoekt toegang tot het volgende document:"
|
||||
|
||||
#: build/lib/core/models.py:1778 core/models.py:1778
|
||||
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||
#, python-brace-format
|
||||
msgid "{name} is asking for access to the document: {title}"
|
||||
msgstr "{name} verzoekt toegang tot het document: {title}"
|
||||
|
||||
#: build/lib/core/models.py:1820 core/models.py:1820
|
||||
#: build/lib/core/models.py:1320 core/models.py:1320
|
||||
msgid "Thread"
|
||||
msgstr "Kanaal"
|
||||
|
||||
#: build/lib/core/models.py:1821 core/models.py:1821
|
||||
#: build/lib/core/models.py:1321 core/models.py:1321
|
||||
msgid "Threads"
|
||||
msgstr "Kanalen"
|
||||
|
||||
#: build/lib/core/models.py:1824 build/lib/core/models.py:1876
|
||||
#: core/models.py:1824 core/models.py:1876
|
||||
#: build/lib/core/models.py:1324 build/lib/core/models.py:1376
|
||||
#: core/models.py:1324 core/models.py:1376
|
||||
msgid "Anonymous"
|
||||
msgstr "Anoniem"
|
||||
|
||||
#: build/lib/core/models.py:1871 core/models.py:1871
|
||||
#: build/lib/core/models.py:1371 core/models.py:1371
|
||||
msgid "Comment"
|
||||
msgstr "Reactie"
|
||||
|
||||
#: build/lib/core/models.py:1872 core/models.py:1872
|
||||
#: build/lib/core/models.py:1372 core/models.py:1372
|
||||
msgid "Comments"
|
||||
msgstr "Reacties"
|
||||
|
||||
#: build/lib/core/models.py:1921 core/models.py:1921
|
||||
#: build/lib/core/models.py:1421 core/models.py:1421
|
||||
msgid "This emoji has already been reacted to this comment."
|
||||
msgstr "Deze emoji is al op deze opmerking gereageerd."
|
||||
|
||||
#: build/lib/core/models.py:1925 core/models.py:1925
|
||||
#: build/lib/core/models.py:1425 core/models.py:1425
|
||||
msgid "Reaction"
|
||||
msgstr "Reactie"
|
||||
|
||||
#: build/lib/core/models.py:1926 core/models.py:1926
|
||||
#: build/lib/core/models.py:1426 core/models.py:1426
|
||||
msgid "Reactions"
|
||||
msgstr "Reacties"
|
||||
|
||||
#: build/lib/core/models.py:1936 core/models.py:1936
|
||||
#: build/lib/core/models.py:1436 core/models.py:1436
|
||||
msgid "email address"
|
||||
msgstr "e-mailadres"
|
||||
|
||||
#: build/lib/core/models.py:1955 core/models.py:1955
|
||||
#: build/lib/core/models.py:1455 core/models.py:1455
|
||||
msgid "Document invitation"
|
||||
msgstr "Document uitnodiging"
|
||||
|
||||
#: build/lib/core/models.py:1956 core/models.py:1956
|
||||
#: build/lib/core/models.py:1456 core/models.py:1456
|
||||
msgid "Document invitations"
|
||||
msgstr "Document uitnodigingen"
|
||||
|
||||
#: build/lib/core/models.py:1976 core/models.py:1976
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
msgid "This email is already associated to a registered user."
|
||||
msgstr "Deze email is al geassocieerd met een geregistreerde gebruiker."
|
||||
|
||||
#: build/lib/impress/settings.py:702 impress/settings.py:702
|
||||
msgid "Docs AI"
|
||||
msgstr ""
|
||||
|
||||
#: core/templates/mail/html/template.html:153
|
||||
#: core/templates/mail/text/template.txt:3
|
||||
msgid "Logo email"
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lasuite-docs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
|
||||
"PO-Revision-Date: 2026-02-27 08:24\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0000\n"
|
||||
"PO-Revision-Date: 2026-01-28 20:12\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Portuguese\n"
|
||||
"Language: pt_PT\n"
|
||||
@@ -17,65 +17,57 @@ msgstr ""
|
||||
"X-Crowdin-File: backend-impress.pot\n"
|
||||
"X-Crowdin-File-ID: 18\n"
|
||||
|
||||
#: build/lib/core/admin.py:30 core/admin.py:30
|
||||
#: build/lib/core/admin.py:28 core/admin.py:28
|
||||
msgid "Personal info"
|
||||
msgstr "Informações Pessoais"
|
||||
|
||||
#: build/lib/core/admin.py:43 build/lib/core/admin.py:161 core/admin.py:43
|
||||
#: core/admin.py:161
|
||||
#: build/lib/core/admin.py:41 build/lib/core/admin.py:121 core/admin.py:41
|
||||
#: core/admin.py:121
|
||||
msgid "Permissions"
|
||||
msgstr "Permissões"
|
||||
|
||||
#: build/lib/core/admin.py:55 core/admin.py:55
|
||||
#: build/lib/core/admin.py:53 core/admin.py:53
|
||||
msgid "Important dates"
|
||||
msgstr "Datas importantes"
|
||||
|
||||
#: build/lib/core/admin.py:112 core/admin.py:112
|
||||
msgid "Import job created and queued."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:116 core/admin.py:116
|
||||
msgid "Process selected user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:171 core/admin.py:171
|
||||
#: build/lib/core/admin.py:131 core/admin.py:131
|
||||
msgid "Tree structure"
|
||||
msgstr "Estrutura de árvore"
|
||||
|
||||
#: build/lib/core/api/filters.py:48 core/api/filters.py:48
|
||||
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||
msgid "Title"
|
||||
msgstr "Título"
|
||||
|
||||
#: build/lib/core/api/filters.py:62 core/api/filters.py:62
|
||||
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||
msgid "Creator is me"
|
||||
msgstr "Eu sou o criador"
|
||||
|
||||
#: build/lib/core/api/filters.py:65 core/api/filters.py:65
|
||||
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||
msgid "Masked"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/filters.py:68 core/api/filters.py:68
|
||||
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
|
||||
msgid "Favorite"
|
||||
msgstr "Favorito"
|
||||
|
||||
#: build/lib/core/api/serializers.py:513 core/api/serializers.py:513
|
||||
#: build/lib/core/api/serializers.py:505 core/api/serializers.py:505
|
||||
msgid "A new document was created on your behalf!"
|
||||
msgstr "Um novo documento foi criado em seu nome!"
|
||||
|
||||
#: build/lib/core/api/serializers.py:517 core/api/serializers.py:517
|
||||
#: build/lib/core/api/serializers.py:509 core/api/serializers.py:509
|
||||
msgid "You have been granted ownership of a new document:"
|
||||
msgstr "A propriedade de um novo documento foi concedida a você:"
|
||||
|
||||
#: build/lib/core/api/serializers.py:553 core/api/serializers.py:553
|
||||
#: build/lib/core/api/serializers.py:545 core/api/serializers.py:545
|
||||
msgid "This field is required."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/serializers.py:564 core/api/serializers.py:564
|
||||
#: build/lib/core/api/serializers.py:556 core/api/serializers.py:556
|
||||
#, python-format
|
||||
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
|
||||
#: build/lib/core/api/viewsets.py:1122 core/api/viewsets.py:1122
|
||||
#, python-brace-format
|
||||
msgid "copy of {title}"
|
||||
msgstr "cópia de {title}"
|
||||
@@ -143,374 +135,262 @@ msgstr ""
|
||||
msgid "Right"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:80 core/models.py:80
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
msgid "id"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
#: build/lib/core/models.py:82 core/models.py:82
|
||||
msgid "primary key for the record as UUID"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:87 core/models.py:87
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
msgid "created on"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
#: build/lib/core/models.py:89 core/models.py:89
|
||||
msgid "date and time at which a record was created"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:93 core/models.py:93
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
msgid "updated on"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
#: build/lib/core/models.py:95 core/models.py:95
|
||||
msgid "date and time at which a record was last updated"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:130 core/models.py:130
|
||||
#: build/lib/core/models.py:131 core/models.py:131
|
||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:141 core/models.py:141
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
msgid "sub"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
#: build/lib/core/models.py:143 core/models.py:143
|
||||
msgid "Required. 255 characters or fewer. ASCII characters only."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:150 core/models.py:150
|
||||
#: build/lib/core/models.py:151 core/models.py:151
|
||||
msgid "full name"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:152 core/models.py:152
|
||||
#: build/lib/core/models.py:153 core/models.py:153
|
||||
msgid "short name"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:155 core/models.py:155
|
||||
#: build/lib/core/models.py:156 core/models.py:156
|
||||
msgid "identity email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:160 core/models.py:160
|
||||
#: build/lib/core/models.py:161 core/models.py:161
|
||||
msgid "admin email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:167 core/models.py:167
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
msgid "language"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
#: build/lib/core/models.py:169 core/models.py:169
|
||||
msgid "The language in which the user wants to see the interface."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:176 core/models.py:176
|
||||
#: build/lib/core/models.py:177 core/models.py:177
|
||||
msgid "The timezone in which the user wants to see times."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:179 core/models.py:179
|
||||
#: build/lib/core/models.py:180 core/models.py:180
|
||||
msgid "device"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:181 core/models.py:181
|
||||
#: build/lib/core/models.py:182 core/models.py:182
|
||||
msgid "Whether the user is a device or a real user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:184 core/models.py:184
|
||||
#: build/lib/core/models.py:185 core/models.py:185
|
||||
msgid "staff status"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:186 core/models.py:186
|
||||
#: build/lib/core/models.py:187 core/models.py:187
|
||||
msgid "Whether the user can log into this admin site."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:189 core/models.py:189
|
||||
#: build/lib/core/models.py:190 core/models.py:190
|
||||
msgid "active"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:192 core/models.py:192
|
||||
#: build/lib/core/models.py:193 core/models.py:193
|
||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:204 core/models.py:204
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
msgid "user"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
#: build/lib/core/models.py:206 core/models.py:206
|
||||
msgid "users"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:360 core/models.py:360
|
||||
msgid "Active email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:361 core/models.py:361
|
||||
msgid "Email address to deactivate"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:388 core/models.py:388
|
||||
msgid "Unique ID in the source file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:394 build/lib/core/models.py:692 core/models.py:394
|
||||
#: core/models.py:692
|
||||
msgid "Pending"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:395 core/models.py:395
|
||||
msgid "Ready"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:396 build/lib/core/models.py:694 core/models.py:396
|
||||
#: core/models.py:694
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:397 build/lib/core/models.py:695 core/models.py:397
|
||||
#: core/models.py:695
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:405 core/models.py:405
|
||||
msgid "user reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:406 core/models.py:406
|
||||
msgid "user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:644 core/models.py:644
|
||||
msgid "You have requested a reconciliation of your user accounts on Docs.\n"
|
||||
" To confirm that you are the one who initiated the request\n"
|
||||
" and that this email belongs to you:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:650 core/models.py:650
|
||||
msgid "Confirm by clicking the link to start the reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:655 build/lib/core/models.py:761 core/models.py:655
|
||||
#: core/models.py:761
|
||||
msgid "Click here"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:656 core/models.py:656
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:667 core/models.py:667
|
||||
msgid "Your reconciliation request has been processed.\n"
|
||||
" New documents are likely associated with your account:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:672 core/models.py:672
|
||||
msgid "Your accounts have been merged"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:677 core/models.py:677
|
||||
msgid "Click here to see"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:678 core/models.py:678
|
||||
msgid "See my documents"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:688 core/models.py:688
|
||||
msgid "CSV file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:693 core/models.py:693
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:703 core/models.py:703
|
||||
msgid "user reconciliation CSV import"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:704 core/models.py:704
|
||||
msgid "user reconciliation CSV imports"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:748 core/models.py:748
|
||||
#, python-brace-format
|
||||
msgid "Your request for reconciliation was unsuccessful.\n"
|
||||
" Reconciliation failed for the following email addresses:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Please check for typos.\n"
|
||||
" You can submit another request with the valid email addresses."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:756 core/models.py:756
|
||||
msgid "Reconciliation of your Docs accounts not completed"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:762 core/models.py:762
|
||||
msgid "Make a new request"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:861 core/models.py:861
|
||||
#: build/lib/core/models.py:362 core/models.py:362
|
||||
msgid "title"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:862 core/models.py:862
|
||||
#: build/lib/core/models.py:363 core/models.py:363
|
||||
msgid "excerpt"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:911 core/models.py:911
|
||||
#: build/lib/core/models.py:412 core/models.py:412
|
||||
msgid "Document"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:912 core/models.py:912
|
||||
#: build/lib/core/models.py:413 core/models.py:413
|
||||
msgid "Documents"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:924 build/lib/core/models.py:1328
|
||||
#: core/models.py:924 core/models.py:1328
|
||||
#: build/lib/core/models.py:425 build/lib/core/models.py:828 core/models.py:425
|
||||
#: core/models.py:828
|
||||
msgid "Untitled Document"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1329 core/models.py:1329
|
||||
#: build/lib/core/models.py:829 core/models.py:829
|
||||
msgid "Open"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1364 core/models.py:1364
|
||||
#: build/lib/core/models.py:864 core/models.py:864
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you!"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1368 core/models.py:1368
|
||||
#: build/lib/core/models.py:868 core/models.py:868
|
||||
#, python-brace-format
|
||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1374 core/models.py:1374
|
||||
#: build/lib/core/models.py:874 core/models.py:874
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you: {title}"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1475 core/models.py:1475
|
||||
#: build/lib/core/models.py:975 core/models.py:975
|
||||
msgid "Document/user link trace"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
#: build/lib/core/models.py:976 core/models.py:976
|
||||
msgid "Document/user link traces"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1482 core/models.py:1482
|
||||
#: build/lib/core/models.py:982 core/models.py:982
|
||||
msgid "A link trace already exists for this document/user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1505 core/models.py:1505
|
||||
#: build/lib/core/models.py:1005 core/models.py:1005
|
||||
msgid "Document favorite"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1506 core/models.py:1506
|
||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
||||
msgid "Document favorites"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1512 core/models.py:1512
|
||||
#: build/lib/core/models.py:1012 core/models.py:1012
|
||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1534 core/models.py:1534
|
||||
#: build/lib/core/models.py:1034 core/models.py:1034
|
||||
msgid "Document/user relation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1535 core/models.py:1535
|
||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
||||
msgid "Document/user relations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1541 core/models.py:1541
|
||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
||||
msgid "This user is already in this document."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1547 core/models.py:1547
|
||||
#: build/lib/core/models.py:1047 core/models.py:1047
|
||||
msgid "This team is already in this document."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1553 core/models.py:1553
|
||||
#: build/lib/core/models.py:1053 core/models.py:1053
|
||||
msgid "Either user or team must be set, not both."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1704 core/models.py:1704
|
||||
#: build/lib/core/models.py:1204 core/models.py:1204
|
||||
msgid "Document ask for access"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1705 core/models.py:1705
|
||||
#: build/lib/core/models.py:1205 core/models.py:1205
|
||||
msgid "Document ask for accesses"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1711 core/models.py:1711
|
||||
#: build/lib/core/models.py:1211 core/models.py:1211
|
||||
msgid "This user has already asked for access to this document."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1768 core/models.py:1768
|
||||
#: build/lib/core/models.py:1268 core/models.py:1268
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to a document!"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1772 core/models.py:1772
|
||||
#: build/lib/core/models.py:1272 core/models.py:1272
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to the following document:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1778 core/models.py:1778
|
||||
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||
#, python-brace-format
|
||||
msgid "{name} is asking for access to the document: {title}"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1820 core/models.py:1820
|
||||
#: build/lib/core/models.py:1320 core/models.py:1320
|
||||
msgid "Thread"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1821 core/models.py:1821
|
||||
#: build/lib/core/models.py:1321 core/models.py:1321
|
||||
msgid "Threads"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1824 build/lib/core/models.py:1876
|
||||
#: core/models.py:1824 core/models.py:1876
|
||||
#: build/lib/core/models.py:1324 build/lib/core/models.py:1376
|
||||
#: core/models.py:1324 core/models.py:1376
|
||||
msgid "Anonymous"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1871 core/models.py:1871
|
||||
#: build/lib/core/models.py:1371 core/models.py:1371
|
||||
msgid "Comment"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1872 core/models.py:1872
|
||||
#: build/lib/core/models.py:1372 core/models.py:1372
|
||||
msgid "Comments"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1921 core/models.py:1921
|
||||
#: build/lib/core/models.py:1421 core/models.py:1421
|
||||
msgid "This emoji has already been reacted to this comment."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1925 core/models.py:1925
|
||||
#: build/lib/core/models.py:1425 core/models.py:1425
|
||||
msgid "Reaction"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1926 core/models.py:1926
|
||||
#: build/lib/core/models.py:1426 core/models.py:1426
|
||||
msgid "Reactions"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1936 core/models.py:1936
|
||||
#: build/lib/core/models.py:1436 core/models.py:1436
|
||||
msgid "email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1955 core/models.py:1955
|
||||
#: build/lib/core/models.py:1455 core/models.py:1455
|
||||
msgid "Document invitation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1956 core/models.py:1956
|
||||
#: build/lib/core/models.py:1456 core/models.py:1456
|
||||
msgid "Document invitations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1976 core/models.py:1976
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
msgid "This email is already associated to a registered user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/impress/settings.py:702 impress/settings.py:702
|
||||
msgid "Docs AI"
|
||||
msgstr ""
|
||||
|
||||
#: core/templates/mail/html/template.html:153
|
||||
#: core/templates/mail/text/template.txt:3
|
||||
msgid "Logo email"
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lasuite-docs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
|
||||
"PO-Revision-Date: 2026-02-27 08:24\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0000\n"
|
||||
"PO-Revision-Date: 2026-01-28 20:12\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Russian\n"
|
||||
"Language: ru_RU\n"
|
||||
@@ -17,65 +17,57 @@ msgstr ""
|
||||
"X-Crowdin-File: backend-impress.pot\n"
|
||||
"X-Crowdin-File-ID: 18\n"
|
||||
|
||||
#: build/lib/core/admin.py:30 core/admin.py:30
|
||||
#: build/lib/core/admin.py:28 core/admin.py:28
|
||||
msgid "Personal info"
|
||||
msgstr "Личная информация"
|
||||
|
||||
#: build/lib/core/admin.py:43 build/lib/core/admin.py:161 core/admin.py:43
|
||||
#: core/admin.py:161
|
||||
#: build/lib/core/admin.py:41 build/lib/core/admin.py:121 core/admin.py:41
|
||||
#: core/admin.py:121
|
||||
msgid "Permissions"
|
||||
msgstr "Разрешения"
|
||||
|
||||
#: build/lib/core/admin.py:55 core/admin.py:55
|
||||
#: build/lib/core/admin.py:53 core/admin.py:53
|
||||
msgid "Important dates"
|
||||
msgstr "Важные даты"
|
||||
|
||||
#: build/lib/core/admin.py:112 core/admin.py:112
|
||||
msgid "Import job created and queued."
|
||||
msgstr "Задание по импорту создано и поставлено в очередь."
|
||||
|
||||
#: build/lib/core/admin.py:116 core/admin.py:116
|
||||
msgid "Process selected user reconciliations"
|
||||
msgstr "Обработка выбранных пользовательских сверок"
|
||||
|
||||
#: build/lib/core/admin.py:171 core/admin.py:171
|
||||
#: build/lib/core/admin.py:131 core/admin.py:131
|
||||
msgid "Tree structure"
|
||||
msgstr "Древовидная структура"
|
||||
|
||||
#: build/lib/core/api/filters.py:48 core/api/filters.py:48
|
||||
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||
msgid "Title"
|
||||
msgstr "Заголовок"
|
||||
|
||||
#: build/lib/core/api/filters.py:62 core/api/filters.py:62
|
||||
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||
msgid "Creator is me"
|
||||
msgstr "Создатель - я"
|
||||
|
||||
#: build/lib/core/api/filters.py:65 core/api/filters.py:65
|
||||
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||
msgid "Masked"
|
||||
msgstr "Скрытый"
|
||||
|
||||
#: build/lib/core/api/filters.py:68 core/api/filters.py:68
|
||||
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
|
||||
msgid "Favorite"
|
||||
msgstr "Избранное"
|
||||
|
||||
#: build/lib/core/api/serializers.py:513 core/api/serializers.py:513
|
||||
#: build/lib/core/api/serializers.py:505 core/api/serializers.py:505
|
||||
msgid "A new document was created on your behalf!"
|
||||
msgstr "Новый документ был создан от вашего имени!"
|
||||
|
||||
#: build/lib/core/api/serializers.py:517 core/api/serializers.py:517
|
||||
#: build/lib/core/api/serializers.py:509 core/api/serializers.py:509
|
||||
msgid "You have been granted ownership of a new document:"
|
||||
msgstr "Вы назначены владельцем для нового документа:"
|
||||
|
||||
#: build/lib/core/api/serializers.py:553 core/api/serializers.py:553
|
||||
#: build/lib/core/api/serializers.py:545 core/api/serializers.py:545
|
||||
msgid "This field is required."
|
||||
msgstr "Это поле обязательное."
|
||||
|
||||
#: build/lib/core/api/serializers.py:564 core/api/serializers.py:564
|
||||
#: build/lib/core/api/serializers.py:556 core/api/serializers.py:556
|
||||
#, python-format
|
||||
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
|
||||
msgstr "Доступ по ссылке '%(link_reach)s' запрещён в соответствии с настройками родительского документа."
|
||||
|
||||
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
|
||||
#: build/lib/core/api/viewsets.py:1122 core/api/viewsets.py:1122
|
||||
#, python-brace-format
|
||||
msgid "copy of {title}"
|
||||
msgstr "копия {title}"
|
||||
@@ -143,381 +135,262 @@ msgstr "Слева"
|
||||
msgid "Right"
|
||||
msgstr "Справа"
|
||||
|
||||
#: build/lib/core/models.py:80 core/models.py:80
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
msgid "id"
|
||||
msgstr "id"
|
||||
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
#: build/lib/core/models.py:82 core/models.py:82
|
||||
msgid "primary key for the record as UUID"
|
||||
msgstr "первичный ключ для записи как UUID"
|
||||
|
||||
#: build/lib/core/models.py:87 core/models.py:87
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
msgid "created on"
|
||||
msgstr "создано"
|
||||
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
#: build/lib/core/models.py:89 core/models.py:89
|
||||
msgid "date and time at which a record was created"
|
||||
msgstr "дата и время создания записи"
|
||||
|
||||
#: build/lib/core/models.py:93 core/models.py:93
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
msgid "updated on"
|
||||
msgstr "обновлено"
|
||||
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
#: build/lib/core/models.py:95 core/models.py:95
|
||||
msgid "date and time at which a record was last updated"
|
||||
msgstr "дата и время последнего обновления записи"
|
||||
|
||||
#: build/lib/core/models.py:130 core/models.py:130
|
||||
#: build/lib/core/models.py:131 core/models.py:131
|
||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||
msgstr "Мы не смогли найти пользователя с этими данными, но этот адрес уже связан с зарегистрированным пользователем."
|
||||
|
||||
#: build/lib/core/models.py:141 core/models.py:141
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
msgid "sub"
|
||||
msgstr "вложение"
|
||||
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
#: build/lib/core/models.py:143 core/models.py:143
|
||||
msgid "Required. 255 characters or fewer. ASCII characters only."
|
||||
msgstr "Обязательно. 255 символов или меньше. Только ASCII символы."
|
||||
|
||||
#: build/lib/core/models.py:150 core/models.py:150
|
||||
#: build/lib/core/models.py:151 core/models.py:151
|
||||
msgid "full name"
|
||||
msgstr "полное имя"
|
||||
|
||||
#: build/lib/core/models.py:152 core/models.py:152
|
||||
#: build/lib/core/models.py:153 core/models.py:153
|
||||
msgid "short name"
|
||||
msgstr "короткое имя"
|
||||
|
||||
#: build/lib/core/models.py:155 core/models.py:155
|
||||
#: build/lib/core/models.py:156 core/models.py:156
|
||||
msgid "identity email address"
|
||||
msgstr "личный адрес электронной почты"
|
||||
|
||||
#: build/lib/core/models.py:160 core/models.py:160
|
||||
#: build/lib/core/models.py:161 core/models.py:161
|
||||
msgid "admin email address"
|
||||
msgstr "e-mail администратора"
|
||||
|
||||
#: build/lib/core/models.py:167 core/models.py:167
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
msgid "language"
|
||||
msgstr "язык"
|
||||
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
#: build/lib/core/models.py:169 core/models.py:169
|
||||
msgid "The language in which the user wants to see the interface."
|
||||
msgstr "Язык, на котором пользователь хочет видеть интерфейс."
|
||||
|
||||
#: build/lib/core/models.py:176 core/models.py:176
|
||||
#: build/lib/core/models.py:177 core/models.py:177
|
||||
msgid "The timezone in which the user wants to see times."
|
||||
msgstr "Часовой пояс, в котором пользователь хочет видеть время."
|
||||
|
||||
#: build/lib/core/models.py:179 core/models.py:179
|
||||
#: build/lib/core/models.py:180 core/models.py:180
|
||||
msgid "device"
|
||||
msgstr "устройство"
|
||||
|
||||
#: build/lib/core/models.py:181 core/models.py:181
|
||||
#: build/lib/core/models.py:182 core/models.py:182
|
||||
msgid "Whether the user is a device or a real user."
|
||||
msgstr "Пользователь является устройством или человеком."
|
||||
|
||||
#: build/lib/core/models.py:184 core/models.py:184
|
||||
#: build/lib/core/models.py:185 core/models.py:185
|
||||
msgid "staff status"
|
||||
msgstr "статус сотрудника"
|
||||
|
||||
#: build/lib/core/models.py:186 core/models.py:186
|
||||
#: build/lib/core/models.py:187 core/models.py:187
|
||||
msgid "Whether the user can log into this admin site."
|
||||
msgstr "Может ли пользователь войти на этот административный сайт."
|
||||
|
||||
#: build/lib/core/models.py:189 core/models.py:189
|
||||
#: build/lib/core/models.py:190 core/models.py:190
|
||||
msgid "active"
|
||||
msgstr "активный"
|
||||
|
||||
#: build/lib/core/models.py:192 core/models.py:192
|
||||
#: build/lib/core/models.py:193 core/models.py:193
|
||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||
msgstr "Должен ли пользователь рассматриваться как активный. Альтернатива удалению учётных записей."
|
||||
|
||||
#: build/lib/core/models.py:204 core/models.py:204
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
msgid "user"
|
||||
msgstr "пользователь"
|
||||
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
#: build/lib/core/models.py:206 core/models.py:206
|
||||
msgid "users"
|
||||
msgstr "пользователи"
|
||||
|
||||
#: build/lib/core/models.py:360 core/models.py:360
|
||||
msgid "Active email address"
|
||||
msgstr "Активный адрес электронной почты"
|
||||
|
||||
#: build/lib/core/models.py:361 core/models.py:361
|
||||
msgid "Email address to deactivate"
|
||||
msgstr "Адрес электронной почты для деактивации"
|
||||
|
||||
#: build/lib/core/models.py:388 core/models.py:388
|
||||
msgid "Unique ID in the source file"
|
||||
msgstr "Уникальный идентификатор в исходном файле"
|
||||
|
||||
#: build/lib/core/models.py:394 build/lib/core/models.py:692 core/models.py:394
|
||||
#: core/models.py:692
|
||||
msgid "Pending"
|
||||
msgstr "В обработке"
|
||||
|
||||
#: build/lib/core/models.py:395 core/models.py:395
|
||||
msgid "Ready"
|
||||
msgstr "Готово"
|
||||
|
||||
#: build/lib/core/models.py:396 build/lib/core/models.py:694 core/models.py:396
|
||||
#: core/models.py:694
|
||||
msgid "Done"
|
||||
msgstr "Выполнено"
|
||||
|
||||
#: build/lib/core/models.py:397 build/lib/core/models.py:695 core/models.py:397
|
||||
#: core/models.py:695
|
||||
msgid "Error"
|
||||
msgstr "Ошибка"
|
||||
|
||||
#: build/lib/core/models.py:405 core/models.py:405
|
||||
msgid "user reconciliation"
|
||||
msgstr "сверка данных пользователя"
|
||||
|
||||
#: build/lib/core/models.py:406 core/models.py:406
|
||||
msgid "user reconciliations"
|
||||
msgstr "сверки данных пользователя"
|
||||
|
||||
#: build/lib/core/models.py:644 core/models.py:644
|
||||
msgid "You have requested a reconciliation of your user accounts on Docs.\n"
|
||||
" To confirm that you are the one who initiated the request\n"
|
||||
" and that this email belongs to you:"
|
||||
msgstr "Вы запросили сверку учётных записей пользователя в Docs.\n"
|
||||
" Чтобы подтвердить факт того, что вы являетесь инициатором запроса\n"
|
||||
" и что этот адрес принадлежит вам:"
|
||||
|
||||
#: build/lib/core/models.py:650 core/models.py:650
|
||||
msgid "Confirm by clicking the link to start the reconciliation"
|
||||
msgstr "Чтобы начать сверку, подтвердите это, нажав на ссылку"
|
||||
|
||||
#: build/lib/core/models.py:655 build/lib/core/models.py:761 core/models.py:655
|
||||
#: core/models.py:761
|
||||
msgid "Click here"
|
||||
msgstr "Нажмите здесь"
|
||||
|
||||
#: build/lib/core/models.py:656 core/models.py:656
|
||||
msgid "Confirm"
|
||||
msgstr "Подтверждение"
|
||||
|
||||
#: build/lib/core/models.py:667 core/models.py:667
|
||||
msgid "Your reconciliation request has been processed.\n"
|
||||
" New documents are likely associated with your account:"
|
||||
msgstr "Ваш запрос на сверку был обработан.\n"
|
||||
" Новые документы, вероятно, связаны с вашей учётной записью:"
|
||||
|
||||
#: build/lib/core/models.py:672 core/models.py:672
|
||||
msgid "Your accounts have been merged"
|
||||
msgstr "Ваши учётные записи были объединены"
|
||||
|
||||
#: build/lib/core/models.py:677 core/models.py:677
|
||||
msgid "Click here to see"
|
||||
msgstr "Нажмите здесь, чтобы просмотреть"
|
||||
|
||||
#: build/lib/core/models.py:678 core/models.py:678
|
||||
msgid "See my documents"
|
||||
msgstr "Просмотреть мои документы"
|
||||
|
||||
#: build/lib/core/models.py:688 core/models.py:688
|
||||
msgid "CSV file"
|
||||
msgstr "CSV-файл"
|
||||
|
||||
#: build/lib/core/models.py:693 core/models.py:693
|
||||
msgid "Running"
|
||||
msgstr "Выполнение"
|
||||
|
||||
#: build/lib/core/models.py:703 core/models.py:703
|
||||
msgid "user reconciliation CSV import"
|
||||
msgstr "импорт из CSV сверки пользователей"
|
||||
|
||||
#: build/lib/core/models.py:704 core/models.py:704
|
||||
msgid "user reconciliation CSV imports"
|
||||
msgstr "импорты из CSV сверки пользователями"
|
||||
|
||||
#: build/lib/core/models.py:748 core/models.py:748
|
||||
#, python-brace-format
|
||||
msgid "Your request for reconciliation was unsuccessful.\n"
|
||||
" Reconciliation failed for the following email addresses:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Please check for typos.\n"
|
||||
" You can submit another request with the valid email addresses."
|
||||
msgstr "Ваш запрос на сверку не удался.\n"
|
||||
" Сверка не удалась для следующих email адресов:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Пожалуйста, проверьте, нет ли в них опечаток.\n"
|
||||
" Вы можете отправить ещё один запрос с действительными адресами электронной почты."
|
||||
|
||||
#: build/lib/core/models.py:756 core/models.py:756
|
||||
msgid "Reconciliation of your Docs accounts not completed"
|
||||
msgstr "Сверка ваших учётных записей Docs не завершена"
|
||||
|
||||
#: build/lib/core/models.py:762 core/models.py:762
|
||||
msgid "Make a new request"
|
||||
msgstr "Создать новый запрос"
|
||||
|
||||
#: build/lib/core/models.py:861 core/models.py:861
|
||||
#: build/lib/core/models.py:362 core/models.py:362
|
||||
msgid "title"
|
||||
msgstr "заголовок"
|
||||
|
||||
#: build/lib/core/models.py:862 core/models.py:862
|
||||
#: build/lib/core/models.py:363 core/models.py:363
|
||||
msgid "excerpt"
|
||||
msgstr "отрывок"
|
||||
|
||||
#: build/lib/core/models.py:911 core/models.py:911
|
||||
#: build/lib/core/models.py:412 core/models.py:412
|
||||
msgid "Document"
|
||||
msgstr "Документ"
|
||||
|
||||
#: build/lib/core/models.py:912 core/models.py:912
|
||||
#: build/lib/core/models.py:413 core/models.py:413
|
||||
msgid "Documents"
|
||||
msgstr "Документы"
|
||||
|
||||
#: build/lib/core/models.py:924 build/lib/core/models.py:1328
|
||||
#: core/models.py:924 core/models.py:1328
|
||||
#: build/lib/core/models.py:425 build/lib/core/models.py:828 core/models.py:425
|
||||
#: core/models.py:828
|
||||
msgid "Untitled Document"
|
||||
msgstr "Безымянный документ"
|
||||
|
||||
#: build/lib/core/models.py:1329 core/models.py:1329
|
||||
#: build/lib/core/models.py:829 core/models.py:829
|
||||
msgid "Open"
|
||||
msgstr "Открыть"
|
||||
|
||||
#: build/lib/core/models.py:1364 core/models.py:1364
|
||||
#: build/lib/core/models.py:864 core/models.py:864
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you!"
|
||||
msgstr "{name} делится с вами документом!"
|
||||
|
||||
#: build/lib/core/models.py:1368 core/models.py:1368
|
||||
#: build/lib/core/models.py:868 core/models.py:868
|
||||
#, python-brace-format
|
||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||
msgstr "{name} приглашает вас присоединиться к следующему документу с ролью \"{role}\":"
|
||||
|
||||
#: build/lib/core/models.py:1374 core/models.py:1374
|
||||
#: build/lib/core/models.py:874 core/models.py:874
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you: {title}"
|
||||
msgstr "{name} делится с вами документом: {title}"
|
||||
|
||||
#: build/lib/core/models.py:1475 core/models.py:1475
|
||||
#: build/lib/core/models.py:975 core/models.py:975
|
||||
msgid "Document/user link trace"
|
||||
msgstr "Трассировка связи документ/пользователь"
|
||||
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
#: build/lib/core/models.py:976 core/models.py:976
|
||||
msgid "Document/user link traces"
|
||||
msgstr "Трассировка связей документ/пользователь"
|
||||
|
||||
#: build/lib/core/models.py:1482 core/models.py:1482
|
||||
#: build/lib/core/models.py:982 core/models.py:982
|
||||
msgid "A link trace already exists for this document/user."
|
||||
msgstr "Для этого документа/пользователя уже существует трассировка ссылки."
|
||||
|
||||
#: build/lib/core/models.py:1505 core/models.py:1505
|
||||
#: build/lib/core/models.py:1005 core/models.py:1005
|
||||
msgid "Document favorite"
|
||||
msgstr "Избранный документ"
|
||||
|
||||
#: build/lib/core/models.py:1506 core/models.py:1506
|
||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
||||
msgid "Document favorites"
|
||||
msgstr "Избранные документы"
|
||||
|
||||
#: build/lib/core/models.py:1512 core/models.py:1512
|
||||
#: build/lib/core/models.py:1012 core/models.py:1012
|
||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||
msgstr "Этот документ уже помечен как избранный для этого пользователя."
|
||||
|
||||
#: build/lib/core/models.py:1534 core/models.py:1534
|
||||
#: build/lib/core/models.py:1034 core/models.py:1034
|
||||
msgid "Document/user relation"
|
||||
msgstr "Отношение документ/пользователь"
|
||||
|
||||
#: build/lib/core/models.py:1535 core/models.py:1535
|
||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
||||
msgid "Document/user relations"
|
||||
msgstr "Отношения документ/пользователь"
|
||||
|
||||
#: build/lib/core/models.py:1541 core/models.py:1541
|
||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
||||
msgid "This user is already in this document."
|
||||
msgstr "Этот пользователь уже имеет доступ к этому документу."
|
||||
|
||||
#: build/lib/core/models.py:1547 core/models.py:1547
|
||||
#: build/lib/core/models.py:1047 core/models.py:1047
|
||||
msgid "This team is already in this document."
|
||||
msgstr "Эта команда уже имеет доступ к этому документу."
|
||||
|
||||
#: build/lib/core/models.py:1553 core/models.py:1553
|
||||
#: build/lib/core/models.py:1053 core/models.py:1053
|
||||
msgid "Either user or team must be set, not both."
|
||||
msgstr "Может быть выбран либо пользователь, либо команда, но не оба варианта сразу."
|
||||
|
||||
#: build/lib/core/models.py:1704 core/models.py:1704
|
||||
#: build/lib/core/models.py:1204 core/models.py:1204
|
||||
msgid "Document ask for access"
|
||||
msgstr "Документ запрашивает доступ"
|
||||
|
||||
#: build/lib/core/models.py:1705 core/models.py:1705
|
||||
#: build/lib/core/models.py:1205 core/models.py:1205
|
||||
msgid "Document ask for accesses"
|
||||
msgstr "Документ запрашивает доступы"
|
||||
|
||||
#: build/lib/core/models.py:1711 core/models.py:1711
|
||||
#: build/lib/core/models.py:1211 core/models.py:1211
|
||||
msgid "This user has already asked for access to this document."
|
||||
msgstr "Этот пользователь уже запросил доступ к этому документу."
|
||||
|
||||
#: build/lib/core/models.py:1768 core/models.py:1768
|
||||
#: build/lib/core/models.py:1268 core/models.py:1268
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to a document!"
|
||||
msgstr "{name} хочет получить доступ к документу!"
|
||||
|
||||
#: build/lib/core/models.py:1772 core/models.py:1772
|
||||
#: build/lib/core/models.py:1272 core/models.py:1272
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to the following document:"
|
||||
msgstr "{name} хочет получить доступ к следующему документу:"
|
||||
|
||||
#: build/lib/core/models.py:1778 core/models.py:1778
|
||||
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||
#, python-brace-format
|
||||
msgid "{name} is asking for access to the document: {title}"
|
||||
msgstr "{name} запрашивает доступ к документу: {title}"
|
||||
|
||||
#: build/lib/core/models.py:1820 core/models.py:1820
|
||||
#: build/lib/core/models.py:1320 core/models.py:1320
|
||||
msgid "Thread"
|
||||
msgstr "Обсуждение"
|
||||
|
||||
#: build/lib/core/models.py:1821 core/models.py:1821
|
||||
#: build/lib/core/models.py:1321 core/models.py:1321
|
||||
msgid "Threads"
|
||||
msgstr "Обсуждения"
|
||||
|
||||
#: build/lib/core/models.py:1824 build/lib/core/models.py:1876
|
||||
#: core/models.py:1824 core/models.py:1876
|
||||
#: build/lib/core/models.py:1324 build/lib/core/models.py:1376
|
||||
#: core/models.py:1324 core/models.py:1376
|
||||
msgid "Anonymous"
|
||||
msgstr "Аноним"
|
||||
|
||||
#: build/lib/core/models.py:1871 core/models.py:1871
|
||||
#: build/lib/core/models.py:1371 core/models.py:1371
|
||||
msgid "Comment"
|
||||
msgstr "Комментарий"
|
||||
|
||||
#: build/lib/core/models.py:1872 core/models.py:1872
|
||||
#: build/lib/core/models.py:1372 core/models.py:1372
|
||||
msgid "Comments"
|
||||
msgstr "Комментарии"
|
||||
|
||||
#: build/lib/core/models.py:1921 core/models.py:1921
|
||||
#: build/lib/core/models.py:1421 core/models.py:1421
|
||||
msgid "This emoji has already been reacted to this comment."
|
||||
msgstr "Этот эмодзи уже использован в этом комментарии."
|
||||
|
||||
#: build/lib/core/models.py:1925 core/models.py:1925
|
||||
#: build/lib/core/models.py:1425 core/models.py:1425
|
||||
msgid "Reaction"
|
||||
msgstr "Реакция"
|
||||
|
||||
#: build/lib/core/models.py:1926 core/models.py:1926
|
||||
#: build/lib/core/models.py:1426 core/models.py:1426
|
||||
msgid "Reactions"
|
||||
msgstr "Реакции"
|
||||
|
||||
#: build/lib/core/models.py:1936 core/models.py:1936
|
||||
#: build/lib/core/models.py:1436 core/models.py:1436
|
||||
msgid "email address"
|
||||
msgstr "адрес электронной почты"
|
||||
|
||||
#: build/lib/core/models.py:1955 core/models.py:1955
|
||||
#: build/lib/core/models.py:1455 core/models.py:1455
|
||||
msgid "Document invitation"
|
||||
msgstr "Приглашение для документа"
|
||||
|
||||
#: build/lib/core/models.py:1956 core/models.py:1956
|
||||
#: build/lib/core/models.py:1456 core/models.py:1456
|
||||
msgid "Document invitations"
|
||||
msgstr "Приглашения для документов"
|
||||
|
||||
#: build/lib/core/models.py:1976 core/models.py:1976
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
msgid "This email is already associated to a registered user."
|
||||
msgstr "Этот адрес уже связан с зарегистрированным пользователем."
|
||||
|
||||
#: build/lib/impress/settings.py:702 impress/settings.py:702
|
||||
msgid "Docs AI"
|
||||
msgstr "Docs ИИ"
|
||||
|
||||
#: core/templates/mail/html/template.html:153
|
||||
#: core/templates/mail/text/template.txt:3
|
||||
msgid "Logo email"
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lasuite-docs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
|
||||
"PO-Revision-Date: 2026-02-27 08:24\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0000\n"
|
||||
"PO-Revision-Date: 2026-01-28 20:12\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Slovenian\n"
|
||||
"Language: sl_SI\n"
|
||||
@@ -17,65 +17,57 @@ msgstr ""
|
||||
"X-Crowdin-File: backend-impress.pot\n"
|
||||
"X-Crowdin-File-ID: 18\n"
|
||||
|
||||
#: build/lib/core/admin.py:30 core/admin.py:30
|
||||
#: build/lib/core/admin.py:28 core/admin.py:28
|
||||
msgid "Personal info"
|
||||
msgstr "Osebni podatki"
|
||||
|
||||
#: build/lib/core/admin.py:43 build/lib/core/admin.py:161 core/admin.py:43
|
||||
#: core/admin.py:161
|
||||
#: build/lib/core/admin.py:41 build/lib/core/admin.py:121 core/admin.py:41
|
||||
#: core/admin.py:121
|
||||
msgid "Permissions"
|
||||
msgstr "Dovoljenja"
|
||||
|
||||
#: build/lib/core/admin.py:55 core/admin.py:55
|
||||
#: build/lib/core/admin.py:53 core/admin.py:53
|
||||
msgid "Important dates"
|
||||
msgstr "Pomembni datumi"
|
||||
|
||||
#: build/lib/core/admin.py:112 core/admin.py:112
|
||||
msgid "Import job created and queued."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:116 core/admin.py:116
|
||||
msgid "Process selected user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:171 core/admin.py:171
|
||||
#: build/lib/core/admin.py:131 core/admin.py:131
|
||||
msgid "Tree structure"
|
||||
msgstr "Drevesna struktura"
|
||||
|
||||
#: build/lib/core/api/filters.py:48 core/api/filters.py:48
|
||||
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||
msgid "Title"
|
||||
msgstr "Naslov"
|
||||
|
||||
#: build/lib/core/api/filters.py:62 core/api/filters.py:62
|
||||
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||
msgid "Creator is me"
|
||||
msgstr "Ustvaril sem jaz"
|
||||
|
||||
#: build/lib/core/api/filters.py:65 core/api/filters.py:65
|
||||
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||
msgid "Masked"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/filters.py:68 core/api/filters.py:68
|
||||
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
|
||||
msgid "Favorite"
|
||||
msgstr "Priljubljena"
|
||||
|
||||
#: build/lib/core/api/serializers.py:513 core/api/serializers.py:513
|
||||
#: build/lib/core/api/serializers.py:505 core/api/serializers.py:505
|
||||
msgid "A new document was created on your behalf!"
|
||||
msgstr "Nov dokument je bil ustvarjen v vašem imenu!"
|
||||
|
||||
#: build/lib/core/api/serializers.py:517 core/api/serializers.py:517
|
||||
#: build/lib/core/api/serializers.py:509 core/api/serializers.py:509
|
||||
msgid "You have been granted ownership of a new document:"
|
||||
msgstr "Dodeljeno vam je bilo lastništvo nad novim dokumentom:"
|
||||
|
||||
#: build/lib/core/api/serializers.py:553 core/api/serializers.py:553
|
||||
#: build/lib/core/api/serializers.py:545 core/api/serializers.py:545
|
||||
msgid "This field is required."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/serializers.py:564 core/api/serializers.py:564
|
||||
#: build/lib/core/api/serializers.py:556 core/api/serializers.py:556
|
||||
#, python-format
|
||||
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
|
||||
#: build/lib/core/api/viewsets.py:1122 core/api/viewsets.py:1122
|
||||
#, python-brace-format
|
||||
msgid "copy of {title}"
|
||||
msgstr ""
|
||||
@@ -143,374 +135,262 @@ msgstr "Levo"
|
||||
msgid "Right"
|
||||
msgstr "Desno"
|
||||
|
||||
#: build/lib/core/models.py:80 core/models.py:80
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
msgid "id"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
#: build/lib/core/models.py:82 core/models.py:82
|
||||
msgid "primary key for the record as UUID"
|
||||
msgstr "primarni ključ za zapis kot UUID"
|
||||
|
||||
#: build/lib/core/models.py:87 core/models.py:87
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
msgid "created on"
|
||||
msgstr "ustvarjen na"
|
||||
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
#: build/lib/core/models.py:89 core/models.py:89
|
||||
msgid "date and time at which a record was created"
|
||||
msgstr "datum in čas, ko je bil zapis ustvarjen"
|
||||
|
||||
#: build/lib/core/models.py:93 core/models.py:93
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
msgid "updated on"
|
||||
msgstr "posodobljeno dne"
|
||||
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
#: build/lib/core/models.py:95 core/models.py:95
|
||||
msgid "date and time at which a record was last updated"
|
||||
msgstr "datum in čas, ko je bil zapis nazadnje posodobljen"
|
||||
|
||||
#: build/lib/core/models.py:130 core/models.py:130
|
||||
#: build/lib/core/models.py:131 core/models.py:131
|
||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||
msgstr "Nismo mogli najti uporabnika s tem sub, vendar je e-poštni naslov že povezan z registriranim uporabnikom."
|
||||
|
||||
#: build/lib/core/models.py:141 core/models.py:141
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
msgid "sub"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
#: build/lib/core/models.py:143 core/models.py:143
|
||||
msgid "Required. 255 characters or fewer. ASCII characters only."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:150 core/models.py:150
|
||||
#: build/lib/core/models.py:151 core/models.py:151
|
||||
msgid "full name"
|
||||
msgstr "polno ime"
|
||||
|
||||
#: build/lib/core/models.py:152 core/models.py:152
|
||||
#: build/lib/core/models.py:153 core/models.py:153
|
||||
msgid "short name"
|
||||
msgstr "kratko ime"
|
||||
|
||||
#: build/lib/core/models.py:155 core/models.py:155
|
||||
#: build/lib/core/models.py:156 core/models.py:156
|
||||
msgid "identity email address"
|
||||
msgstr "elektronski naslov identitete"
|
||||
|
||||
#: build/lib/core/models.py:160 core/models.py:160
|
||||
#: build/lib/core/models.py:161 core/models.py:161
|
||||
msgid "admin email address"
|
||||
msgstr "elektronski naslov skrbnika"
|
||||
|
||||
#: build/lib/core/models.py:167 core/models.py:167
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
msgid "language"
|
||||
msgstr "jezik"
|
||||
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
#: build/lib/core/models.py:169 core/models.py:169
|
||||
msgid "The language in which the user wants to see the interface."
|
||||
msgstr "Jezik, v katerem uporabnik želi videti vmesnik."
|
||||
|
||||
#: build/lib/core/models.py:176 core/models.py:176
|
||||
#: build/lib/core/models.py:177 core/models.py:177
|
||||
msgid "The timezone in which the user wants to see times."
|
||||
msgstr "Časovni pas, v katerem želi uporabnik videti uro."
|
||||
|
||||
#: build/lib/core/models.py:179 core/models.py:179
|
||||
#: build/lib/core/models.py:180 core/models.py:180
|
||||
msgid "device"
|
||||
msgstr "naprava"
|
||||
|
||||
#: build/lib/core/models.py:181 core/models.py:181
|
||||
#: build/lib/core/models.py:182 core/models.py:182
|
||||
msgid "Whether the user is a device or a real user."
|
||||
msgstr "Ali je uporabnik naprava ali pravi uporabnik."
|
||||
|
||||
#: build/lib/core/models.py:184 core/models.py:184
|
||||
#: build/lib/core/models.py:185 core/models.py:185
|
||||
msgid "staff status"
|
||||
msgstr "kadrovski status"
|
||||
|
||||
#: build/lib/core/models.py:186 core/models.py:186
|
||||
#: build/lib/core/models.py:187 core/models.py:187
|
||||
msgid "Whether the user can log into this admin site."
|
||||
msgstr "Ali se uporabnik lahko prijavi na to skrbniško mesto."
|
||||
|
||||
#: build/lib/core/models.py:189 core/models.py:189
|
||||
#: build/lib/core/models.py:190 core/models.py:190
|
||||
msgid "active"
|
||||
msgstr "aktivni"
|
||||
|
||||
#: build/lib/core/models.py:192 core/models.py:192
|
||||
#: build/lib/core/models.py:193 core/models.py:193
|
||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||
msgstr "Ali je treba tega uporabnika obravnavati kot aktivnega. Namesto brisanja računov počistite to izbiro."
|
||||
|
||||
#: build/lib/core/models.py:204 core/models.py:204
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
msgid "user"
|
||||
msgstr "uporabnik"
|
||||
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
#: build/lib/core/models.py:206 core/models.py:206
|
||||
msgid "users"
|
||||
msgstr "uporabniki"
|
||||
|
||||
#: build/lib/core/models.py:360 core/models.py:360
|
||||
msgid "Active email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:361 core/models.py:361
|
||||
msgid "Email address to deactivate"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:388 core/models.py:388
|
||||
msgid "Unique ID in the source file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:394 build/lib/core/models.py:692 core/models.py:394
|
||||
#: core/models.py:692
|
||||
msgid "Pending"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:395 core/models.py:395
|
||||
msgid "Ready"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:396 build/lib/core/models.py:694 core/models.py:396
|
||||
#: core/models.py:694
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:397 build/lib/core/models.py:695 core/models.py:397
|
||||
#: core/models.py:695
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:405 core/models.py:405
|
||||
msgid "user reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:406 core/models.py:406
|
||||
msgid "user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:644 core/models.py:644
|
||||
msgid "You have requested a reconciliation of your user accounts on Docs.\n"
|
||||
" To confirm that you are the one who initiated the request\n"
|
||||
" and that this email belongs to you:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:650 core/models.py:650
|
||||
msgid "Confirm by clicking the link to start the reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:655 build/lib/core/models.py:761 core/models.py:655
|
||||
#: core/models.py:761
|
||||
msgid "Click here"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:656 core/models.py:656
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:667 core/models.py:667
|
||||
msgid "Your reconciliation request has been processed.\n"
|
||||
" New documents are likely associated with your account:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:672 core/models.py:672
|
||||
msgid "Your accounts have been merged"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:677 core/models.py:677
|
||||
msgid "Click here to see"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:678 core/models.py:678
|
||||
msgid "See my documents"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:688 core/models.py:688
|
||||
msgid "CSV file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:693 core/models.py:693
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:703 core/models.py:703
|
||||
msgid "user reconciliation CSV import"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:704 core/models.py:704
|
||||
msgid "user reconciliation CSV imports"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:748 core/models.py:748
|
||||
#, python-brace-format
|
||||
msgid "Your request for reconciliation was unsuccessful.\n"
|
||||
" Reconciliation failed for the following email addresses:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Please check for typos.\n"
|
||||
" You can submit another request with the valid email addresses."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:756 core/models.py:756
|
||||
msgid "Reconciliation of your Docs accounts not completed"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:762 core/models.py:762
|
||||
msgid "Make a new request"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:861 core/models.py:861
|
||||
#: build/lib/core/models.py:362 core/models.py:362
|
||||
msgid "title"
|
||||
msgstr "naslov"
|
||||
|
||||
#: build/lib/core/models.py:862 core/models.py:862
|
||||
#: build/lib/core/models.py:363 core/models.py:363
|
||||
msgid "excerpt"
|
||||
msgstr "odlomek"
|
||||
|
||||
#: build/lib/core/models.py:911 core/models.py:911
|
||||
#: build/lib/core/models.py:412 core/models.py:412
|
||||
msgid "Document"
|
||||
msgstr "Dokument"
|
||||
|
||||
#: build/lib/core/models.py:912 core/models.py:912
|
||||
#: build/lib/core/models.py:413 core/models.py:413
|
||||
msgid "Documents"
|
||||
msgstr "Dokumenti"
|
||||
|
||||
#: build/lib/core/models.py:924 build/lib/core/models.py:1328
|
||||
#: core/models.py:924 core/models.py:1328
|
||||
#: build/lib/core/models.py:425 build/lib/core/models.py:828 core/models.py:425
|
||||
#: core/models.py:828
|
||||
msgid "Untitled Document"
|
||||
msgstr "Dokument brez naslova"
|
||||
|
||||
#: build/lib/core/models.py:1329 core/models.py:1329
|
||||
#: build/lib/core/models.py:829 core/models.py:829
|
||||
msgid "Open"
|
||||
msgstr "Odpri"
|
||||
|
||||
#: build/lib/core/models.py:1364 core/models.py:1364
|
||||
#: build/lib/core/models.py:864 core/models.py:864
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you!"
|
||||
msgstr "{name} je delil dokument z vami!"
|
||||
|
||||
#: build/lib/core/models.py:1368 core/models.py:1368
|
||||
#: build/lib/core/models.py:868 core/models.py:868
|
||||
#, python-brace-format
|
||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||
msgstr "{name} vas je povabil z vlogo \"{role}\" na naslednjem dokumentu:"
|
||||
|
||||
#: build/lib/core/models.py:1374 core/models.py:1374
|
||||
#: build/lib/core/models.py:874 core/models.py:874
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you: {title}"
|
||||
msgstr "{name} je delil dokument z vami: {title}"
|
||||
|
||||
#: build/lib/core/models.py:1475 core/models.py:1475
|
||||
#: build/lib/core/models.py:975 core/models.py:975
|
||||
msgid "Document/user link trace"
|
||||
msgstr "Dokument/sled povezave uporabnika"
|
||||
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
#: build/lib/core/models.py:976 core/models.py:976
|
||||
msgid "Document/user link traces"
|
||||
msgstr "Sledi povezav dokumenta/uporabnika"
|
||||
|
||||
#: build/lib/core/models.py:1482 core/models.py:1482
|
||||
#: build/lib/core/models.py:982 core/models.py:982
|
||||
msgid "A link trace already exists for this document/user."
|
||||
msgstr "Za ta dokument/uporabnika že obstaja sled povezave."
|
||||
|
||||
#: build/lib/core/models.py:1505 core/models.py:1505
|
||||
#: build/lib/core/models.py:1005 core/models.py:1005
|
||||
msgid "Document favorite"
|
||||
msgstr "Priljubljeni dokument"
|
||||
|
||||
#: build/lib/core/models.py:1506 core/models.py:1506
|
||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
||||
msgid "Document favorites"
|
||||
msgstr "Priljubljeni dokumenti"
|
||||
|
||||
#: build/lib/core/models.py:1512 core/models.py:1512
|
||||
#: build/lib/core/models.py:1012 core/models.py:1012
|
||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||
msgstr "Ta dokument je že ciljno usmerjen s priljubljenim primerkom relacije za istega uporabnika."
|
||||
|
||||
#: build/lib/core/models.py:1534 core/models.py:1534
|
||||
#: build/lib/core/models.py:1034 core/models.py:1034
|
||||
msgid "Document/user relation"
|
||||
msgstr "Odnos dokument/uporabnik"
|
||||
|
||||
#: build/lib/core/models.py:1535 core/models.py:1535
|
||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
||||
msgid "Document/user relations"
|
||||
msgstr "Odnosi dokument/uporabnik"
|
||||
|
||||
#: build/lib/core/models.py:1541 core/models.py:1541
|
||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
||||
msgid "This user is already in this document."
|
||||
msgstr "Ta uporabnik je že v tem dokumentu."
|
||||
|
||||
#: build/lib/core/models.py:1547 core/models.py:1547
|
||||
#: build/lib/core/models.py:1047 core/models.py:1047
|
||||
msgid "This team is already in this document."
|
||||
msgstr "Ta ekipa je že v tem dokumentu."
|
||||
|
||||
#: build/lib/core/models.py:1553 core/models.py:1553
|
||||
#: build/lib/core/models.py:1053 core/models.py:1053
|
||||
msgid "Either user or team must be set, not both."
|
||||
msgstr "Nastaviti je treba bodisi uporabnika ali ekipo, a ne obojega."
|
||||
|
||||
#: build/lib/core/models.py:1704 core/models.py:1704
|
||||
#: build/lib/core/models.py:1204 core/models.py:1204
|
||||
msgid "Document ask for access"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1705 core/models.py:1705
|
||||
#: build/lib/core/models.py:1205 core/models.py:1205
|
||||
msgid "Document ask for accesses"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1711 core/models.py:1711
|
||||
#: build/lib/core/models.py:1211 core/models.py:1211
|
||||
msgid "This user has already asked for access to this document."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1768 core/models.py:1768
|
||||
#: build/lib/core/models.py:1268 core/models.py:1268
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to a document!"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1772 core/models.py:1772
|
||||
#: build/lib/core/models.py:1272 core/models.py:1272
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to the following document:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1778 core/models.py:1778
|
||||
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||
#, python-brace-format
|
||||
msgid "{name} is asking for access to the document: {title}"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1820 core/models.py:1820
|
||||
#: build/lib/core/models.py:1320 core/models.py:1320
|
||||
msgid "Thread"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1821 core/models.py:1821
|
||||
#: build/lib/core/models.py:1321 core/models.py:1321
|
||||
msgid "Threads"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1824 build/lib/core/models.py:1876
|
||||
#: core/models.py:1824 core/models.py:1876
|
||||
#: build/lib/core/models.py:1324 build/lib/core/models.py:1376
|
||||
#: core/models.py:1324 core/models.py:1376
|
||||
msgid "Anonymous"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1871 core/models.py:1871
|
||||
#: build/lib/core/models.py:1371 core/models.py:1371
|
||||
msgid "Comment"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1872 core/models.py:1872
|
||||
#: build/lib/core/models.py:1372 core/models.py:1372
|
||||
msgid "Comments"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1921 core/models.py:1921
|
||||
#: build/lib/core/models.py:1421 core/models.py:1421
|
||||
msgid "This emoji has already been reacted to this comment."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1925 core/models.py:1925
|
||||
#: build/lib/core/models.py:1425 core/models.py:1425
|
||||
msgid "Reaction"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1926 core/models.py:1926
|
||||
#: build/lib/core/models.py:1426 core/models.py:1426
|
||||
msgid "Reactions"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1936 core/models.py:1936
|
||||
#: build/lib/core/models.py:1436 core/models.py:1436
|
||||
msgid "email address"
|
||||
msgstr "elektronski naslov"
|
||||
|
||||
#: build/lib/core/models.py:1955 core/models.py:1955
|
||||
#: build/lib/core/models.py:1455 core/models.py:1455
|
||||
msgid "Document invitation"
|
||||
msgstr "Vabilo na dokument"
|
||||
|
||||
#: build/lib/core/models.py:1956 core/models.py:1956
|
||||
#: build/lib/core/models.py:1456 core/models.py:1456
|
||||
msgid "Document invitations"
|
||||
msgstr "Vabila na dokument"
|
||||
|
||||
#: build/lib/core/models.py:1976 core/models.py:1976
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
msgid "This email is already associated to a registered user."
|
||||
msgstr "Ta e-poštni naslov je že povezan z registriranim uporabnikom."
|
||||
|
||||
#: build/lib/impress/settings.py:702 impress/settings.py:702
|
||||
msgid "Docs AI"
|
||||
msgstr ""
|
||||
|
||||
#: core/templates/mail/html/template.html:153
|
||||
#: core/templates/mail/text/template.txt:3
|
||||
msgid "Logo email"
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lasuite-docs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
|
||||
"PO-Revision-Date: 2026-02-27 08:24\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0000\n"
|
||||
"PO-Revision-Date: 2026-01-28 20:12\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Swedish\n"
|
||||
"Language: sv_SE\n"
|
||||
@@ -17,65 +17,57 @@ msgstr ""
|
||||
"X-Crowdin-File: backend-impress.pot\n"
|
||||
"X-Crowdin-File-ID: 18\n"
|
||||
|
||||
#: build/lib/core/admin.py:30 core/admin.py:30
|
||||
#: build/lib/core/admin.py:28 core/admin.py:28
|
||||
msgid "Personal info"
|
||||
msgstr "Personuppgifter"
|
||||
|
||||
#: build/lib/core/admin.py:43 build/lib/core/admin.py:161 core/admin.py:43
|
||||
#: core/admin.py:161
|
||||
#: build/lib/core/admin.py:41 build/lib/core/admin.py:121 core/admin.py:41
|
||||
#: core/admin.py:121
|
||||
msgid "Permissions"
|
||||
msgstr "Behörigheter"
|
||||
|
||||
#: build/lib/core/admin.py:55 core/admin.py:55
|
||||
#: build/lib/core/admin.py:53 core/admin.py:53
|
||||
msgid "Important dates"
|
||||
msgstr "Viktiga datum"
|
||||
|
||||
#: build/lib/core/admin.py:112 core/admin.py:112
|
||||
msgid "Import job created and queued."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:116 core/admin.py:116
|
||||
msgid "Process selected user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:171 core/admin.py:171
|
||||
#: build/lib/core/admin.py:131 core/admin.py:131
|
||||
msgid "Tree structure"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/filters.py:48 core/api/filters.py:48
|
||||
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||
msgid "Title"
|
||||
msgstr "Titel"
|
||||
|
||||
#: build/lib/core/api/filters.py:62 core/api/filters.py:62
|
||||
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||
msgid "Creator is me"
|
||||
msgstr "Skaparen är jag"
|
||||
|
||||
#: build/lib/core/api/filters.py:65 core/api/filters.py:65
|
||||
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||
msgid "Masked"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/filters.py:68 core/api/filters.py:68
|
||||
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
|
||||
msgid "Favorite"
|
||||
msgstr "Favoriter"
|
||||
|
||||
#: build/lib/core/api/serializers.py:513 core/api/serializers.py:513
|
||||
#: build/lib/core/api/serializers.py:505 core/api/serializers.py:505
|
||||
msgid "A new document was created on your behalf!"
|
||||
msgstr "Ett nytt dokument skapades åt dig!"
|
||||
|
||||
#: build/lib/core/api/serializers.py:517 core/api/serializers.py:517
|
||||
#: build/lib/core/api/serializers.py:509 core/api/serializers.py:509
|
||||
msgid "You have been granted ownership of a new document:"
|
||||
msgstr "Du har beviljats äganderätt till ett nytt dokument:"
|
||||
|
||||
#: build/lib/core/api/serializers.py:553 core/api/serializers.py:553
|
||||
#: build/lib/core/api/serializers.py:545 core/api/serializers.py:545
|
||||
msgid "This field is required."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/serializers.py:564 core/api/serializers.py:564
|
||||
#: build/lib/core/api/serializers.py:556 core/api/serializers.py:556
|
||||
#, python-format
|
||||
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
|
||||
#: build/lib/core/api/viewsets.py:1122 core/api/viewsets.py:1122
|
||||
#, python-brace-format
|
||||
msgid "copy of {title}"
|
||||
msgstr ""
|
||||
@@ -143,374 +135,262 @@ msgstr ""
|
||||
msgid "Right"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:80 core/models.py:80
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
msgid "id"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
#: build/lib/core/models.py:82 core/models.py:82
|
||||
msgid "primary key for the record as UUID"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:87 core/models.py:87
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
msgid "created on"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
#: build/lib/core/models.py:89 core/models.py:89
|
||||
msgid "date and time at which a record was created"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:93 core/models.py:93
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
msgid "updated on"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
#: build/lib/core/models.py:95 core/models.py:95
|
||||
msgid "date and time at which a record was last updated"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:130 core/models.py:130
|
||||
#: build/lib/core/models.py:131 core/models.py:131
|
||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:141 core/models.py:141
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
msgid "sub"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
#: build/lib/core/models.py:143 core/models.py:143
|
||||
msgid "Required. 255 characters or fewer. ASCII characters only."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:150 core/models.py:150
|
||||
#: build/lib/core/models.py:151 core/models.py:151
|
||||
msgid "full name"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:152 core/models.py:152
|
||||
#: build/lib/core/models.py:153 core/models.py:153
|
||||
msgid "short name"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:155 core/models.py:155
|
||||
#: build/lib/core/models.py:156 core/models.py:156
|
||||
msgid "identity email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:160 core/models.py:160
|
||||
#: build/lib/core/models.py:161 core/models.py:161
|
||||
msgid "admin email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:167 core/models.py:167
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
msgid "language"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
#: build/lib/core/models.py:169 core/models.py:169
|
||||
msgid "The language in which the user wants to see the interface."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:176 core/models.py:176
|
||||
#: build/lib/core/models.py:177 core/models.py:177
|
||||
msgid "The timezone in which the user wants to see times."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:179 core/models.py:179
|
||||
#: build/lib/core/models.py:180 core/models.py:180
|
||||
msgid "device"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:181 core/models.py:181
|
||||
#: build/lib/core/models.py:182 core/models.py:182
|
||||
msgid "Whether the user is a device or a real user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:184 core/models.py:184
|
||||
#: build/lib/core/models.py:185 core/models.py:185
|
||||
msgid "staff status"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:186 core/models.py:186
|
||||
#: build/lib/core/models.py:187 core/models.py:187
|
||||
msgid "Whether the user can log into this admin site."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:189 core/models.py:189
|
||||
#: build/lib/core/models.py:190 core/models.py:190
|
||||
msgid "active"
|
||||
msgstr "aktiv"
|
||||
|
||||
#: build/lib/core/models.py:192 core/models.py:192
|
||||
#: build/lib/core/models.py:193 core/models.py:193
|
||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:204 core/models.py:204
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
msgid "user"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
#: build/lib/core/models.py:206 core/models.py:206
|
||||
msgid "users"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:360 core/models.py:360
|
||||
msgid "Active email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:361 core/models.py:361
|
||||
msgid "Email address to deactivate"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:388 core/models.py:388
|
||||
msgid "Unique ID in the source file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:394 build/lib/core/models.py:692 core/models.py:394
|
||||
#: core/models.py:692
|
||||
msgid "Pending"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:395 core/models.py:395
|
||||
msgid "Ready"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:396 build/lib/core/models.py:694 core/models.py:396
|
||||
#: core/models.py:694
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:397 build/lib/core/models.py:695 core/models.py:397
|
||||
#: core/models.py:695
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:405 core/models.py:405
|
||||
msgid "user reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:406 core/models.py:406
|
||||
msgid "user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:644 core/models.py:644
|
||||
msgid "You have requested a reconciliation of your user accounts on Docs.\n"
|
||||
" To confirm that you are the one who initiated the request\n"
|
||||
" and that this email belongs to you:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:650 core/models.py:650
|
||||
msgid "Confirm by clicking the link to start the reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:655 build/lib/core/models.py:761 core/models.py:655
|
||||
#: core/models.py:761
|
||||
msgid "Click here"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:656 core/models.py:656
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:667 core/models.py:667
|
||||
msgid "Your reconciliation request has been processed.\n"
|
||||
" New documents are likely associated with your account:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:672 core/models.py:672
|
||||
msgid "Your accounts have been merged"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:677 core/models.py:677
|
||||
msgid "Click here to see"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:678 core/models.py:678
|
||||
msgid "See my documents"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:688 core/models.py:688
|
||||
msgid "CSV file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:693 core/models.py:693
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:703 core/models.py:703
|
||||
msgid "user reconciliation CSV import"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:704 core/models.py:704
|
||||
msgid "user reconciliation CSV imports"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:748 core/models.py:748
|
||||
#, python-brace-format
|
||||
msgid "Your request for reconciliation was unsuccessful.\n"
|
||||
" Reconciliation failed for the following email addresses:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Please check for typos.\n"
|
||||
" You can submit another request with the valid email addresses."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:756 core/models.py:756
|
||||
msgid "Reconciliation of your Docs accounts not completed"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:762 core/models.py:762
|
||||
msgid "Make a new request"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:861 core/models.py:861
|
||||
#: build/lib/core/models.py:362 core/models.py:362
|
||||
msgid "title"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:862 core/models.py:862
|
||||
#: build/lib/core/models.py:363 core/models.py:363
|
||||
msgid "excerpt"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:911 core/models.py:911
|
||||
#: build/lib/core/models.py:412 core/models.py:412
|
||||
msgid "Document"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:912 core/models.py:912
|
||||
#: build/lib/core/models.py:413 core/models.py:413
|
||||
msgid "Documents"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:924 build/lib/core/models.py:1328
|
||||
#: core/models.py:924 core/models.py:1328
|
||||
#: build/lib/core/models.py:425 build/lib/core/models.py:828 core/models.py:425
|
||||
#: core/models.py:828
|
||||
msgid "Untitled Document"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1329 core/models.py:1329
|
||||
#: build/lib/core/models.py:829 core/models.py:829
|
||||
msgid "Open"
|
||||
msgstr "Öppna"
|
||||
|
||||
#: build/lib/core/models.py:1364 core/models.py:1364
|
||||
#: build/lib/core/models.py:864 core/models.py:864
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you!"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1368 core/models.py:1368
|
||||
#: build/lib/core/models.py:868 core/models.py:868
|
||||
#, python-brace-format
|
||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1374 core/models.py:1374
|
||||
#: build/lib/core/models.py:874 core/models.py:874
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you: {title}"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1475 core/models.py:1475
|
||||
#: build/lib/core/models.py:975 core/models.py:975
|
||||
msgid "Document/user link trace"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
#: build/lib/core/models.py:976 core/models.py:976
|
||||
msgid "Document/user link traces"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1482 core/models.py:1482
|
||||
#: build/lib/core/models.py:982 core/models.py:982
|
||||
msgid "A link trace already exists for this document/user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1505 core/models.py:1505
|
||||
#: build/lib/core/models.py:1005 core/models.py:1005
|
||||
msgid "Document favorite"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1506 core/models.py:1506
|
||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
||||
msgid "Document favorites"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1512 core/models.py:1512
|
||||
#: build/lib/core/models.py:1012 core/models.py:1012
|
||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1534 core/models.py:1534
|
||||
#: build/lib/core/models.py:1034 core/models.py:1034
|
||||
msgid "Document/user relation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1535 core/models.py:1535
|
||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
||||
msgid "Document/user relations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1541 core/models.py:1541
|
||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
||||
msgid "This user is already in this document."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1547 core/models.py:1547
|
||||
#: build/lib/core/models.py:1047 core/models.py:1047
|
||||
msgid "This team is already in this document."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1553 core/models.py:1553
|
||||
#: build/lib/core/models.py:1053 core/models.py:1053
|
||||
msgid "Either user or team must be set, not both."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1704 core/models.py:1704
|
||||
#: build/lib/core/models.py:1204 core/models.py:1204
|
||||
msgid "Document ask for access"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1705 core/models.py:1705
|
||||
#: build/lib/core/models.py:1205 core/models.py:1205
|
||||
msgid "Document ask for accesses"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1711 core/models.py:1711
|
||||
#: build/lib/core/models.py:1211 core/models.py:1211
|
||||
msgid "This user has already asked for access to this document."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1768 core/models.py:1768
|
||||
#: build/lib/core/models.py:1268 core/models.py:1268
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to a document!"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1772 core/models.py:1772
|
||||
#: build/lib/core/models.py:1272 core/models.py:1272
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to the following document:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1778 core/models.py:1778
|
||||
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||
#, python-brace-format
|
||||
msgid "{name} is asking for access to the document: {title}"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1820 core/models.py:1820
|
||||
#: build/lib/core/models.py:1320 core/models.py:1320
|
||||
msgid "Thread"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1821 core/models.py:1821
|
||||
#: build/lib/core/models.py:1321 core/models.py:1321
|
||||
msgid "Threads"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1824 build/lib/core/models.py:1876
|
||||
#: core/models.py:1824 core/models.py:1876
|
||||
#: build/lib/core/models.py:1324 build/lib/core/models.py:1376
|
||||
#: core/models.py:1324 core/models.py:1376
|
||||
msgid "Anonymous"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1871 core/models.py:1871
|
||||
#: build/lib/core/models.py:1371 core/models.py:1371
|
||||
msgid "Comment"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1872 core/models.py:1872
|
||||
#: build/lib/core/models.py:1372 core/models.py:1372
|
||||
msgid "Comments"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1921 core/models.py:1921
|
||||
#: build/lib/core/models.py:1421 core/models.py:1421
|
||||
msgid "This emoji has already been reacted to this comment."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1925 core/models.py:1925
|
||||
#: build/lib/core/models.py:1425 core/models.py:1425
|
||||
msgid "Reaction"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1926 core/models.py:1926
|
||||
#: build/lib/core/models.py:1426 core/models.py:1426
|
||||
msgid "Reactions"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1936 core/models.py:1936
|
||||
#: build/lib/core/models.py:1436 core/models.py:1436
|
||||
msgid "email address"
|
||||
msgstr "e-postadress"
|
||||
|
||||
#: build/lib/core/models.py:1955 core/models.py:1955
|
||||
#: build/lib/core/models.py:1455 core/models.py:1455
|
||||
msgid "Document invitation"
|
||||
msgstr "Bjud in dokument"
|
||||
|
||||
#: build/lib/core/models.py:1956 core/models.py:1956
|
||||
#: build/lib/core/models.py:1456 core/models.py:1456
|
||||
msgid "Document invitations"
|
||||
msgstr "Inbjudningar dokument"
|
||||
|
||||
#: build/lib/core/models.py:1976 core/models.py:1976
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
msgid "This email is already associated to a registered user."
|
||||
msgstr "Denna e-postadress är redan associerad med en registrerad användare."
|
||||
|
||||
#: build/lib/impress/settings.py:702 impress/settings.py:702
|
||||
msgid "Docs AI"
|
||||
msgstr ""
|
||||
|
||||
#: core/templates/mail/html/template.html:153
|
||||
#: core/templates/mail/text/template.txt:3
|
||||
msgid "Logo email"
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lasuite-docs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
|
||||
"PO-Revision-Date: 2026-02-27 08:24\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0000\n"
|
||||
"PO-Revision-Date: 2026-01-28 20:12\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Turkish\n"
|
||||
"Language: tr_TR\n"
|
||||
@@ -17,65 +17,57 @@ msgstr ""
|
||||
"X-Crowdin-File: backend-impress.pot\n"
|
||||
"X-Crowdin-File-ID: 18\n"
|
||||
|
||||
#: build/lib/core/admin.py:30 core/admin.py:30
|
||||
#: build/lib/core/admin.py:28 core/admin.py:28
|
||||
msgid "Personal info"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:43 build/lib/core/admin.py:161 core/admin.py:43
|
||||
#: core/admin.py:161
|
||||
#: build/lib/core/admin.py:41 build/lib/core/admin.py:121 core/admin.py:41
|
||||
#: core/admin.py:121
|
||||
msgid "Permissions"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:55 core/admin.py:55
|
||||
#: build/lib/core/admin.py:53 core/admin.py:53
|
||||
msgid "Important dates"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:112 core/admin.py:112
|
||||
msgid "Import job created and queued."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:116 core/admin.py:116
|
||||
msgid "Process selected user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:171 core/admin.py:171
|
||||
#: build/lib/core/admin.py:131 core/admin.py:131
|
||||
msgid "Tree structure"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/filters.py:48 core/api/filters.py:48
|
||||
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/filters.py:62 core/api/filters.py:62
|
||||
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||
msgid "Creator is me"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/filters.py:65 core/api/filters.py:65
|
||||
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||
msgid "Masked"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/filters.py:68 core/api/filters.py:68
|
||||
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
|
||||
msgid "Favorite"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/serializers.py:513 core/api/serializers.py:513
|
||||
#: build/lib/core/api/serializers.py:505 core/api/serializers.py:505
|
||||
msgid "A new document was created on your behalf!"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/serializers.py:517 core/api/serializers.py:517
|
||||
#: build/lib/core/api/serializers.py:509 core/api/serializers.py:509
|
||||
msgid "You have been granted ownership of a new document:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/serializers.py:553 core/api/serializers.py:553
|
||||
#: build/lib/core/api/serializers.py:545 core/api/serializers.py:545
|
||||
msgid "This field is required."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/serializers.py:564 core/api/serializers.py:564
|
||||
#: build/lib/core/api/serializers.py:556 core/api/serializers.py:556
|
||||
#, python-format
|
||||
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
|
||||
#: build/lib/core/api/viewsets.py:1122 core/api/viewsets.py:1122
|
||||
#, python-brace-format
|
||||
msgid "copy of {title}"
|
||||
msgstr ""
|
||||
@@ -143,374 +135,262 @@ msgstr ""
|
||||
msgid "Right"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:80 core/models.py:80
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
msgid "id"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
#: build/lib/core/models.py:82 core/models.py:82
|
||||
msgid "primary key for the record as UUID"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:87 core/models.py:87
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
msgid "created on"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
#: build/lib/core/models.py:89 core/models.py:89
|
||||
msgid "date and time at which a record was created"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:93 core/models.py:93
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
msgid "updated on"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
#: build/lib/core/models.py:95 core/models.py:95
|
||||
msgid "date and time at which a record was last updated"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:130 core/models.py:130
|
||||
#: build/lib/core/models.py:131 core/models.py:131
|
||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:141 core/models.py:141
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
msgid "sub"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
#: build/lib/core/models.py:143 core/models.py:143
|
||||
msgid "Required. 255 characters or fewer. ASCII characters only."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:150 core/models.py:150
|
||||
#: build/lib/core/models.py:151 core/models.py:151
|
||||
msgid "full name"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:152 core/models.py:152
|
||||
#: build/lib/core/models.py:153 core/models.py:153
|
||||
msgid "short name"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:155 core/models.py:155
|
||||
#: build/lib/core/models.py:156 core/models.py:156
|
||||
msgid "identity email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:160 core/models.py:160
|
||||
#: build/lib/core/models.py:161 core/models.py:161
|
||||
msgid "admin email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:167 core/models.py:167
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
msgid "language"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
#: build/lib/core/models.py:169 core/models.py:169
|
||||
msgid "The language in which the user wants to see the interface."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:176 core/models.py:176
|
||||
#: build/lib/core/models.py:177 core/models.py:177
|
||||
msgid "The timezone in which the user wants to see times."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:179 core/models.py:179
|
||||
#: build/lib/core/models.py:180 core/models.py:180
|
||||
msgid "device"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:181 core/models.py:181
|
||||
#: build/lib/core/models.py:182 core/models.py:182
|
||||
msgid "Whether the user is a device or a real user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:184 core/models.py:184
|
||||
#: build/lib/core/models.py:185 core/models.py:185
|
||||
msgid "staff status"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:186 core/models.py:186
|
||||
#: build/lib/core/models.py:187 core/models.py:187
|
||||
msgid "Whether the user can log into this admin site."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:189 core/models.py:189
|
||||
#: build/lib/core/models.py:190 core/models.py:190
|
||||
msgid "active"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:192 core/models.py:192
|
||||
#: build/lib/core/models.py:193 core/models.py:193
|
||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:204 core/models.py:204
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
msgid "user"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
#: build/lib/core/models.py:206 core/models.py:206
|
||||
msgid "users"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:360 core/models.py:360
|
||||
msgid "Active email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:361 core/models.py:361
|
||||
msgid "Email address to deactivate"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:388 core/models.py:388
|
||||
msgid "Unique ID in the source file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:394 build/lib/core/models.py:692 core/models.py:394
|
||||
#: core/models.py:692
|
||||
msgid "Pending"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:395 core/models.py:395
|
||||
msgid "Ready"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:396 build/lib/core/models.py:694 core/models.py:396
|
||||
#: core/models.py:694
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:397 build/lib/core/models.py:695 core/models.py:397
|
||||
#: core/models.py:695
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:405 core/models.py:405
|
||||
msgid "user reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:406 core/models.py:406
|
||||
msgid "user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:644 core/models.py:644
|
||||
msgid "You have requested a reconciliation of your user accounts on Docs.\n"
|
||||
" To confirm that you are the one who initiated the request\n"
|
||||
" and that this email belongs to you:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:650 core/models.py:650
|
||||
msgid "Confirm by clicking the link to start the reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:655 build/lib/core/models.py:761 core/models.py:655
|
||||
#: core/models.py:761
|
||||
msgid "Click here"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:656 core/models.py:656
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:667 core/models.py:667
|
||||
msgid "Your reconciliation request has been processed.\n"
|
||||
" New documents are likely associated with your account:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:672 core/models.py:672
|
||||
msgid "Your accounts have been merged"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:677 core/models.py:677
|
||||
msgid "Click here to see"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:678 core/models.py:678
|
||||
msgid "See my documents"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:688 core/models.py:688
|
||||
msgid "CSV file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:693 core/models.py:693
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:703 core/models.py:703
|
||||
msgid "user reconciliation CSV import"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:704 core/models.py:704
|
||||
msgid "user reconciliation CSV imports"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:748 core/models.py:748
|
||||
#, python-brace-format
|
||||
msgid "Your request for reconciliation was unsuccessful.\n"
|
||||
" Reconciliation failed for the following email addresses:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Please check for typos.\n"
|
||||
" You can submit another request with the valid email addresses."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:756 core/models.py:756
|
||||
msgid "Reconciliation of your Docs accounts not completed"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:762 core/models.py:762
|
||||
msgid "Make a new request"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:861 core/models.py:861
|
||||
#: build/lib/core/models.py:362 core/models.py:362
|
||||
msgid "title"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:862 core/models.py:862
|
||||
#: build/lib/core/models.py:363 core/models.py:363
|
||||
msgid "excerpt"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:911 core/models.py:911
|
||||
#: build/lib/core/models.py:412 core/models.py:412
|
||||
msgid "Document"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:912 core/models.py:912
|
||||
#: build/lib/core/models.py:413 core/models.py:413
|
||||
msgid "Documents"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:924 build/lib/core/models.py:1328
|
||||
#: core/models.py:924 core/models.py:1328
|
||||
#: build/lib/core/models.py:425 build/lib/core/models.py:828 core/models.py:425
|
||||
#: core/models.py:828
|
||||
msgid "Untitled Document"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1329 core/models.py:1329
|
||||
#: build/lib/core/models.py:829 core/models.py:829
|
||||
msgid "Open"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1364 core/models.py:1364
|
||||
#: build/lib/core/models.py:864 core/models.py:864
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you!"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1368 core/models.py:1368
|
||||
#: build/lib/core/models.py:868 core/models.py:868
|
||||
#, python-brace-format
|
||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1374 core/models.py:1374
|
||||
#: build/lib/core/models.py:874 core/models.py:874
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you: {title}"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1475 core/models.py:1475
|
||||
#: build/lib/core/models.py:975 core/models.py:975
|
||||
msgid "Document/user link trace"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
#: build/lib/core/models.py:976 core/models.py:976
|
||||
msgid "Document/user link traces"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1482 core/models.py:1482
|
||||
#: build/lib/core/models.py:982 core/models.py:982
|
||||
msgid "A link trace already exists for this document/user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1505 core/models.py:1505
|
||||
#: build/lib/core/models.py:1005 core/models.py:1005
|
||||
msgid "Document favorite"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1506 core/models.py:1506
|
||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
||||
msgid "Document favorites"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1512 core/models.py:1512
|
||||
#: build/lib/core/models.py:1012 core/models.py:1012
|
||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1534 core/models.py:1534
|
||||
#: build/lib/core/models.py:1034 core/models.py:1034
|
||||
msgid "Document/user relation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1535 core/models.py:1535
|
||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
||||
msgid "Document/user relations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1541 core/models.py:1541
|
||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
||||
msgid "This user is already in this document."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1547 core/models.py:1547
|
||||
#: build/lib/core/models.py:1047 core/models.py:1047
|
||||
msgid "This team is already in this document."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1553 core/models.py:1553
|
||||
#: build/lib/core/models.py:1053 core/models.py:1053
|
||||
msgid "Either user or team must be set, not both."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1704 core/models.py:1704
|
||||
#: build/lib/core/models.py:1204 core/models.py:1204
|
||||
msgid "Document ask for access"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1705 core/models.py:1705
|
||||
#: build/lib/core/models.py:1205 core/models.py:1205
|
||||
msgid "Document ask for accesses"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1711 core/models.py:1711
|
||||
#: build/lib/core/models.py:1211 core/models.py:1211
|
||||
msgid "This user has already asked for access to this document."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1768 core/models.py:1768
|
||||
#: build/lib/core/models.py:1268 core/models.py:1268
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to a document!"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1772 core/models.py:1772
|
||||
#: build/lib/core/models.py:1272 core/models.py:1272
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to the following document:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1778 core/models.py:1778
|
||||
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||
#, python-brace-format
|
||||
msgid "{name} is asking for access to the document: {title}"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1820 core/models.py:1820
|
||||
#: build/lib/core/models.py:1320 core/models.py:1320
|
||||
msgid "Thread"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1821 core/models.py:1821
|
||||
#: build/lib/core/models.py:1321 core/models.py:1321
|
||||
msgid "Threads"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1824 build/lib/core/models.py:1876
|
||||
#: core/models.py:1824 core/models.py:1876
|
||||
#: build/lib/core/models.py:1324 build/lib/core/models.py:1376
|
||||
#: core/models.py:1324 core/models.py:1376
|
||||
msgid "Anonymous"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1871 core/models.py:1871
|
||||
#: build/lib/core/models.py:1371 core/models.py:1371
|
||||
msgid "Comment"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1872 core/models.py:1872
|
||||
#: build/lib/core/models.py:1372 core/models.py:1372
|
||||
msgid "Comments"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1921 core/models.py:1921
|
||||
#: build/lib/core/models.py:1421 core/models.py:1421
|
||||
msgid "This emoji has already been reacted to this comment."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1925 core/models.py:1925
|
||||
#: build/lib/core/models.py:1425 core/models.py:1425
|
||||
msgid "Reaction"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1926 core/models.py:1926
|
||||
#: build/lib/core/models.py:1426 core/models.py:1426
|
||||
msgid "Reactions"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1936 core/models.py:1936
|
||||
#: build/lib/core/models.py:1436 core/models.py:1436
|
||||
msgid "email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1955 core/models.py:1955
|
||||
#: build/lib/core/models.py:1455 core/models.py:1455
|
||||
msgid "Document invitation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1956 core/models.py:1956
|
||||
#: build/lib/core/models.py:1456 core/models.py:1456
|
||||
msgid "Document invitations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:1976 core/models.py:1976
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
msgid "This email is already associated to a registered user."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/impress/settings.py:702 impress/settings.py:702
|
||||
msgid "Docs AI"
|
||||
msgstr ""
|
||||
|
||||
#: core/templates/mail/html/template.html:153
|
||||
#: core/templates/mail/text/template.txt:3
|
||||
msgid "Logo email"
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lasuite-docs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
|
||||
"PO-Revision-Date: 2026-02-27 08:24\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0000\n"
|
||||
"PO-Revision-Date: 2026-01-28 20:12\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Ukrainian\n"
|
||||
"Language: uk_UA\n"
|
||||
@@ -17,65 +17,57 @@ msgstr ""
|
||||
"X-Crowdin-File: backend-impress.pot\n"
|
||||
"X-Crowdin-File-ID: 18\n"
|
||||
|
||||
#: build/lib/core/admin.py:30 core/admin.py:30
|
||||
#: build/lib/core/admin.py:28 core/admin.py:28
|
||||
msgid "Personal info"
|
||||
msgstr "Особисті дані"
|
||||
|
||||
#: build/lib/core/admin.py:43 build/lib/core/admin.py:161 core/admin.py:43
|
||||
#: core/admin.py:161
|
||||
#: build/lib/core/admin.py:41 build/lib/core/admin.py:121 core/admin.py:41
|
||||
#: core/admin.py:121
|
||||
msgid "Permissions"
|
||||
msgstr "Дозволи"
|
||||
|
||||
#: build/lib/core/admin.py:55 core/admin.py:55
|
||||
#: build/lib/core/admin.py:53 core/admin.py:53
|
||||
msgid "Important dates"
|
||||
msgstr "Важливі дати"
|
||||
|
||||
#: build/lib/core/admin.py:112 core/admin.py:112
|
||||
msgid "Import job created and queued."
|
||||
msgstr "Завдання імпорту створено і поставлено в чергу."
|
||||
|
||||
#: build/lib/core/admin.py:116 core/admin.py:116
|
||||
msgid "Process selected user reconciliations"
|
||||
msgstr "Обробити обрані узгодження користувача"
|
||||
|
||||
#: build/lib/core/admin.py:171 core/admin.py:171
|
||||
#: build/lib/core/admin.py:131 core/admin.py:131
|
||||
msgid "Tree structure"
|
||||
msgstr "Ієрархічна структура"
|
||||
|
||||
#: build/lib/core/api/filters.py:48 core/api/filters.py:48
|
||||
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||
msgid "Title"
|
||||
msgstr "Заголовок"
|
||||
|
||||
#: build/lib/core/api/filters.py:62 core/api/filters.py:62
|
||||
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||
msgid "Creator is me"
|
||||
msgstr "Творець — я"
|
||||
|
||||
#: build/lib/core/api/filters.py:65 core/api/filters.py:65
|
||||
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||
msgid "Masked"
|
||||
msgstr "Приховано"
|
||||
|
||||
#: build/lib/core/api/filters.py:68 core/api/filters.py:68
|
||||
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
|
||||
msgid "Favorite"
|
||||
msgstr "Обране"
|
||||
|
||||
#: build/lib/core/api/serializers.py:513 core/api/serializers.py:513
|
||||
#: build/lib/core/api/serializers.py:505 core/api/serializers.py:505
|
||||
msgid "A new document was created on your behalf!"
|
||||
msgstr "Новий документ був створений від вашого імені!"
|
||||
|
||||
#: build/lib/core/api/serializers.py:517 core/api/serializers.py:517
|
||||
#: build/lib/core/api/serializers.py:509 core/api/serializers.py:509
|
||||
msgid "You have been granted ownership of a new document:"
|
||||
msgstr "Ви тепер є власником нового документа:"
|
||||
|
||||
#: build/lib/core/api/serializers.py:553 core/api/serializers.py:553
|
||||
#: build/lib/core/api/serializers.py:545 core/api/serializers.py:545
|
||||
msgid "This field is required."
|
||||
msgstr "Це поле є обов’язковим."
|
||||
|
||||
#: build/lib/core/api/serializers.py:564 core/api/serializers.py:564
|
||||
#: build/lib/core/api/serializers.py:556 core/api/serializers.py:556
|
||||
#, python-format
|
||||
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
|
||||
msgstr "Доступ до посилання '%(link_reach)s' заборонено на основі конфігурації батьківського документа."
|
||||
|
||||
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
|
||||
#: build/lib/core/api/viewsets.py:1122 core/api/viewsets.py:1122
|
||||
#, python-brace-format
|
||||
msgid "copy of {title}"
|
||||
msgstr "копія {title}"
|
||||
@@ -143,381 +135,262 @@ msgstr "Ліворуч"
|
||||
msgid "Right"
|
||||
msgstr "Праворуч"
|
||||
|
||||
#: build/lib/core/models.py:80 core/models.py:80
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
msgid "id"
|
||||
msgstr "id"
|
||||
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
#: build/lib/core/models.py:82 core/models.py:82
|
||||
msgid "primary key for the record as UUID"
|
||||
msgstr "первинний ключ для запису як UUID"
|
||||
|
||||
#: build/lib/core/models.py:87 core/models.py:87
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
msgid "created on"
|
||||
msgstr "створено"
|
||||
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
#: build/lib/core/models.py:89 core/models.py:89
|
||||
msgid "date and time at which a record was created"
|
||||
msgstr "дата і час, коли запис було створено"
|
||||
|
||||
#: build/lib/core/models.py:93 core/models.py:93
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
msgid "updated on"
|
||||
msgstr "оновлено"
|
||||
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
#: build/lib/core/models.py:95 core/models.py:95
|
||||
msgid "date and time at which a record was last updated"
|
||||
msgstr "дата і час, коли запис був востаннє оновлений"
|
||||
|
||||
#: build/lib/core/models.py:130 core/models.py:130
|
||||
#: build/lib/core/models.py:131 core/models.py:131
|
||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||
msgstr "Ми не змогли знайти користувача з цими даними, але адреса вже пов'язана з зареєстрованим користувачем."
|
||||
|
||||
#: build/lib/core/models.py:141 core/models.py:141
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
msgid "sub"
|
||||
msgstr "вкладений документ"
|
||||
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
#: build/lib/core/models.py:143 core/models.py:143
|
||||
msgid "Required. 255 characters or fewer. ASCII characters only."
|
||||
msgstr "Обов'язкове. 255 символів або менше. Тільки символи ASCII."
|
||||
|
||||
#: build/lib/core/models.py:150 core/models.py:150
|
||||
#: build/lib/core/models.py:151 core/models.py:151
|
||||
msgid "full name"
|
||||
msgstr "повне ім'я"
|
||||
|
||||
#: build/lib/core/models.py:152 core/models.py:152
|
||||
#: build/lib/core/models.py:153 core/models.py:153
|
||||
msgid "short name"
|
||||
msgstr "коротке ім'я"
|
||||
|
||||
#: build/lib/core/models.py:155 core/models.py:155
|
||||
#: build/lib/core/models.py:156 core/models.py:156
|
||||
msgid "identity email address"
|
||||
msgstr "адреса електронної пошти особи"
|
||||
|
||||
#: build/lib/core/models.py:160 core/models.py:160
|
||||
#: build/lib/core/models.py:161 core/models.py:161
|
||||
msgid "admin email address"
|
||||
msgstr "електронна адреса адміністратора"
|
||||
|
||||
#: build/lib/core/models.py:167 core/models.py:167
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
msgid "language"
|
||||
msgstr "мова"
|
||||
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
#: build/lib/core/models.py:169 core/models.py:169
|
||||
msgid "The language in which the user wants to see the interface."
|
||||
msgstr "Мова, якою користувач хоче бачити інтерфейс."
|
||||
|
||||
#: build/lib/core/models.py:176 core/models.py:176
|
||||
#: build/lib/core/models.py:177 core/models.py:177
|
||||
msgid "The timezone in which the user wants to see times."
|
||||
msgstr "Часовий пояс, в якому користувач хоче бачити час."
|
||||
|
||||
#: build/lib/core/models.py:179 core/models.py:179
|
||||
#: build/lib/core/models.py:180 core/models.py:180
|
||||
msgid "device"
|
||||
msgstr "пристрій"
|
||||
|
||||
#: build/lib/core/models.py:181 core/models.py:181
|
||||
#: build/lib/core/models.py:182 core/models.py:182
|
||||
msgid "Whether the user is a device or a real user."
|
||||
msgstr "Чи є користувач пристроєм чи реальним користувачем."
|
||||
|
||||
#: build/lib/core/models.py:184 core/models.py:184
|
||||
#: build/lib/core/models.py:185 core/models.py:185
|
||||
msgid "staff status"
|
||||
msgstr "статус співробітника"
|
||||
|
||||
#: build/lib/core/models.py:186 core/models.py:186
|
||||
#: build/lib/core/models.py:187 core/models.py:187
|
||||
msgid "Whether the user can log into this admin site."
|
||||
msgstr "Чи може користувач увійти на цей сайт адміністратора."
|
||||
|
||||
#: build/lib/core/models.py:189 core/models.py:189
|
||||
#: build/lib/core/models.py:190 core/models.py:190
|
||||
msgid "active"
|
||||
msgstr "активний"
|
||||
|
||||
#: build/lib/core/models.py:192 core/models.py:192
|
||||
#: build/lib/core/models.py:193 core/models.py:193
|
||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||
msgstr "Чи слід ставитися до цього користувача як до активного. Зніміть вибір замість видалення облікового запису."
|
||||
|
||||
#: build/lib/core/models.py:204 core/models.py:204
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
msgid "user"
|
||||
msgstr "користувач"
|
||||
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
#: build/lib/core/models.py:206 core/models.py:206
|
||||
msgid "users"
|
||||
msgstr "користувачі"
|
||||
|
||||
#: build/lib/core/models.py:360 core/models.py:360
|
||||
msgid "Active email address"
|
||||
msgstr "Активна електронна адреса"
|
||||
|
||||
#: build/lib/core/models.py:361 core/models.py:361
|
||||
msgid "Email address to deactivate"
|
||||
msgstr "Електронна адреса, що буде деактивована"
|
||||
|
||||
#: build/lib/core/models.py:388 core/models.py:388
|
||||
msgid "Unique ID in the source file"
|
||||
msgstr "Унікальний ідентифікатор у вихідному файлі"
|
||||
|
||||
#: build/lib/core/models.py:394 build/lib/core/models.py:692 core/models.py:394
|
||||
#: core/models.py:692
|
||||
msgid "Pending"
|
||||
msgstr "В очікуванні"
|
||||
|
||||
#: build/lib/core/models.py:395 core/models.py:395
|
||||
msgid "Ready"
|
||||
msgstr "Готово"
|
||||
|
||||
#: build/lib/core/models.py:396 build/lib/core/models.py:694 core/models.py:396
|
||||
#: core/models.py:694
|
||||
msgid "Done"
|
||||
msgstr "Виконано"
|
||||
|
||||
#: build/lib/core/models.py:397 build/lib/core/models.py:695 core/models.py:397
|
||||
#: core/models.py:695
|
||||
msgid "Error"
|
||||
msgstr "Помилка"
|
||||
|
||||
#: build/lib/core/models.py:405 core/models.py:405
|
||||
msgid "user reconciliation"
|
||||
msgstr "узгодження користувачів"
|
||||
|
||||
#: build/lib/core/models.py:406 core/models.py:406
|
||||
msgid "user reconciliations"
|
||||
msgstr "узгодження користувачів"
|
||||
|
||||
#: build/lib/core/models.py:644 core/models.py:644
|
||||
msgid "You have requested a reconciliation of your user accounts on Docs.\n"
|
||||
" To confirm that you are the one who initiated the request\n"
|
||||
" and that this email belongs to you:"
|
||||
msgstr "Ви запросили узгодження своїх облікових записів користувачів у Docs.\n"
|
||||
" Щоб підтвердити, що саме ви ініціювали запит\n"
|
||||
" і що ця електронна адреса належить вам:"
|
||||
|
||||
#: build/lib/core/models.py:650 core/models.py:650
|
||||
msgid "Confirm by clicking the link to start the reconciliation"
|
||||
msgstr "Підтвердіть, натиснувши на посилання, щоб почати узгодження"
|
||||
|
||||
#: build/lib/core/models.py:655 build/lib/core/models.py:761 core/models.py:655
|
||||
#: core/models.py:761
|
||||
msgid "Click here"
|
||||
msgstr "Натисніть тут"
|
||||
|
||||
#: build/lib/core/models.py:656 core/models.py:656
|
||||
msgid "Confirm"
|
||||
msgstr "Підтвердження"
|
||||
|
||||
#: build/lib/core/models.py:667 core/models.py:667
|
||||
msgid "Your reconciliation request has been processed.\n"
|
||||
" New documents are likely associated with your account:"
|
||||
msgstr "Ваш запит на узгодження оброблено.\n"
|
||||
" Нові документи, ймовірно, пов'язані з вашим обліковим записом:"
|
||||
|
||||
#: build/lib/core/models.py:672 core/models.py:672
|
||||
msgid "Your accounts have been merged"
|
||||
msgstr "Ваші облікові записи були об'єднані"
|
||||
|
||||
#: build/lib/core/models.py:677 core/models.py:677
|
||||
msgid "Click here to see"
|
||||
msgstr "Натисніть тут, щоб переглянути"
|
||||
|
||||
#: build/lib/core/models.py:678 core/models.py:678
|
||||
msgid "See my documents"
|
||||
msgstr "Переглянути мої документи"
|
||||
|
||||
#: build/lib/core/models.py:688 core/models.py:688
|
||||
msgid "CSV file"
|
||||
msgstr "CSV-файл"
|
||||
|
||||
#: build/lib/core/models.py:693 core/models.py:693
|
||||
msgid "Running"
|
||||
msgstr "Виконується"
|
||||
|
||||
#: build/lib/core/models.py:703 core/models.py:703
|
||||
msgid "user reconciliation CSV import"
|
||||
msgstr "імпорт CSV для узгодження користувачів"
|
||||
|
||||
#: build/lib/core/models.py:704 core/models.py:704
|
||||
msgid "user reconciliation CSV imports"
|
||||
msgstr "імпорт CSV для узгодження користувачів"
|
||||
|
||||
#: build/lib/core/models.py:748 core/models.py:748
|
||||
#, python-brace-format
|
||||
msgid "Your request for reconciliation was unsuccessful.\n"
|
||||
" Reconciliation failed for the following email addresses:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Please check for typos.\n"
|
||||
" You can submit another request with the valid email addresses."
|
||||
msgstr "Ваш запит на узгодження не був виконаний.\n"
|
||||
" Узгодження не вдалося для наступних адрес електронної пошти:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Перевірте, чи немає помилок.\n"
|
||||
" Ви можете надіслати інший запит із дійсними адресами електронної пошти."
|
||||
|
||||
#: build/lib/core/models.py:756 core/models.py:756
|
||||
msgid "Reconciliation of your Docs accounts not completed"
|
||||
msgstr "Узгодження ваших облікових записів не завершено"
|
||||
|
||||
#: build/lib/core/models.py:762 core/models.py:762
|
||||
msgid "Make a new request"
|
||||
msgstr "Зробити новий запит"
|
||||
|
||||
#: build/lib/core/models.py:861 core/models.py:861
|
||||
#: build/lib/core/models.py:362 core/models.py:362
|
||||
msgid "title"
|
||||
msgstr "заголовок"
|
||||
|
||||
#: build/lib/core/models.py:862 core/models.py:862
|
||||
#: build/lib/core/models.py:363 core/models.py:363
|
||||
msgid "excerpt"
|
||||
msgstr "уривок"
|
||||
|
||||
#: build/lib/core/models.py:911 core/models.py:911
|
||||
#: build/lib/core/models.py:412 core/models.py:412
|
||||
msgid "Document"
|
||||
msgstr "Документ"
|
||||
|
||||
#: build/lib/core/models.py:912 core/models.py:912
|
||||
#: build/lib/core/models.py:413 core/models.py:413
|
||||
msgid "Documents"
|
||||
msgstr "Документи"
|
||||
|
||||
#: build/lib/core/models.py:924 build/lib/core/models.py:1328
|
||||
#: core/models.py:924 core/models.py:1328
|
||||
#: build/lib/core/models.py:425 build/lib/core/models.py:828 core/models.py:425
|
||||
#: core/models.py:828
|
||||
msgid "Untitled Document"
|
||||
msgstr "Документ без назви"
|
||||
|
||||
#: build/lib/core/models.py:1329 core/models.py:1329
|
||||
#: build/lib/core/models.py:829 core/models.py:829
|
||||
msgid "Open"
|
||||
msgstr "Відкрити"
|
||||
|
||||
#: build/lib/core/models.py:1364 core/models.py:1364
|
||||
#: build/lib/core/models.py:864 core/models.py:864
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you!"
|
||||
msgstr "{name} ділиться з вами документом!"
|
||||
|
||||
#: build/lib/core/models.py:1368 core/models.py:1368
|
||||
#: build/lib/core/models.py:868 core/models.py:868
|
||||
#, python-brace-format
|
||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||
msgstr "{name} запрошує вас для роботи з документом із роллю \"{role}\":"
|
||||
|
||||
#: build/lib/core/models.py:1374 core/models.py:1374
|
||||
#: build/lib/core/models.py:874 core/models.py:874
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you: {title}"
|
||||
msgstr "{name} ділиться з вами документом: {title}"
|
||||
|
||||
#: build/lib/core/models.py:1475 core/models.py:1475
|
||||
#: build/lib/core/models.py:975 core/models.py:975
|
||||
msgid "Document/user link trace"
|
||||
msgstr "Трасування посилання Документ/користувач"
|
||||
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
#: build/lib/core/models.py:976 core/models.py:976
|
||||
msgid "Document/user link traces"
|
||||
msgstr "Трасування посилань Документ/користувач"
|
||||
|
||||
#: build/lib/core/models.py:1482 core/models.py:1482
|
||||
#: build/lib/core/models.py:982 core/models.py:982
|
||||
msgid "A link trace already exists for this document/user."
|
||||
msgstr "Відстеження вже існуючих посилань для цього документа/користувача."
|
||||
|
||||
#: build/lib/core/models.py:1505 core/models.py:1505
|
||||
#: build/lib/core/models.py:1005 core/models.py:1005
|
||||
msgid "Document favorite"
|
||||
msgstr "Обраний документ"
|
||||
|
||||
#: build/lib/core/models.py:1506 core/models.py:1506
|
||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
||||
msgid "Document favorites"
|
||||
msgstr "Обрані документи"
|
||||
|
||||
#: build/lib/core/models.py:1512 core/models.py:1512
|
||||
#: build/lib/core/models.py:1012 core/models.py:1012
|
||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||
msgstr "Цей документ вже вказаний як обраний для одного користувача."
|
||||
|
||||
#: build/lib/core/models.py:1534 core/models.py:1534
|
||||
#: build/lib/core/models.py:1034 core/models.py:1034
|
||||
msgid "Document/user relation"
|
||||
msgstr "Відносини документ/користувач"
|
||||
|
||||
#: build/lib/core/models.py:1535 core/models.py:1535
|
||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
||||
msgid "Document/user relations"
|
||||
msgstr "Відносини документ/користувач"
|
||||
|
||||
#: build/lib/core/models.py:1541 core/models.py:1541
|
||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
||||
msgid "This user is already in this document."
|
||||
msgstr "Цей користувач вже має доступ до цього документу."
|
||||
|
||||
#: build/lib/core/models.py:1547 core/models.py:1547
|
||||
#: build/lib/core/models.py:1047 core/models.py:1047
|
||||
msgid "This team is already in this document."
|
||||
msgstr "Ця команда вже має доступ до цього документа."
|
||||
|
||||
#: build/lib/core/models.py:1553 core/models.py:1553
|
||||
#: build/lib/core/models.py:1053 core/models.py:1053
|
||||
msgid "Either user or team must be set, not both."
|
||||
msgstr "Вкажіть користувача або команду, а не обох."
|
||||
|
||||
#: build/lib/core/models.py:1704 core/models.py:1704
|
||||
#: build/lib/core/models.py:1204 core/models.py:1204
|
||||
msgid "Document ask for access"
|
||||
msgstr "Запит доступу до документа"
|
||||
|
||||
#: build/lib/core/models.py:1705 core/models.py:1705
|
||||
#: build/lib/core/models.py:1205 core/models.py:1205
|
||||
msgid "Document ask for accesses"
|
||||
msgstr "Запит доступу для документа"
|
||||
|
||||
#: build/lib/core/models.py:1711 core/models.py:1711
|
||||
#: build/lib/core/models.py:1211 core/models.py:1211
|
||||
msgid "This user has already asked for access to this document."
|
||||
msgstr "Цей користувач вже попросив доступ до цього документа."
|
||||
|
||||
#: build/lib/core/models.py:1768 core/models.py:1768
|
||||
#: build/lib/core/models.py:1268 core/models.py:1268
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to a document!"
|
||||
msgstr "{name} хоче отримати доступ до документа!"
|
||||
|
||||
#: build/lib/core/models.py:1772 core/models.py:1772
|
||||
#: build/lib/core/models.py:1272 core/models.py:1272
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to the following document:"
|
||||
msgstr "{name} бажає отримати доступ до наступного документа:"
|
||||
|
||||
#: build/lib/core/models.py:1778 core/models.py:1778
|
||||
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||
#, python-brace-format
|
||||
msgid "{name} is asking for access to the document: {title}"
|
||||
msgstr "{name} запитує доступ до документа: {title}"
|
||||
|
||||
#: build/lib/core/models.py:1820 core/models.py:1820
|
||||
#: build/lib/core/models.py:1320 core/models.py:1320
|
||||
msgid "Thread"
|
||||
msgstr "Обговорення"
|
||||
|
||||
#: build/lib/core/models.py:1821 core/models.py:1821
|
||||
#: build/lib/core/models.py:1321 core/models.py:1321
|
||||
msgid "Threads"
|
||||
msgstr "Обговорення"
|
||||
|
||||
#: build/lib/core/models.py:1824 build/lib/core/models.py:1876
|
||||
#: core/models.py:1824 core/models.py:1876
|
||||
#: build/lib/core/models.py:1324 build/lib/core/models.py:1376
|
||||
#: core/models.py:1324 core/models.py:1376
|
||||
msgid "Anonymous"
|
||||
msgstr "Анонім"
|
||||
|
||||
#: build/lib/core/models.py:1871 core/models.py:1871
|
||||
#: build/lib/core/models.py:1371 core/models.py:1371
|
||||
msgid "Comment"
|
||||
msgstr "Коментар"
|
||||
|
||||
#: build/lib/core/models.py:1872 core/models.py:1872
|
||||
#: build/lib/core/models.py:1372 core/models.py:1372
|
||||
msgid "Comments"
|
||||
msgstr "Коментарі"
|
||||
|
||||
#: build/lib/core/models.py:1921 core/models.py:1921
|
||||
#: build/lib/core/models.py:1421 core/models.py:1421
|
||||
msgid "This emoji has already been reacted to this comment."
|
||||
msgstr "Цим емодзі вже відреагували на цей коментар."
|
||||
|
||||
#: build/lib/core/models.py:1925 core/models.py:1925
|
||||
#: build/lib/core/models.py:1425 core/models.py:1425
|
||||
msgid "Reaction"
|
||||
msgstr "Реакція"
|
||||
|
||||
#: build/lib/core/models.py:1926 core/models.py:1926
|
||||
#: build/lib/core/models.py:1426 core/models.py:1426
|
||||
msgid "Reactions"
|
||||
msgstr "Реакції"
|
||||
|
||||
#: build/lib/core/models.py:1936 core/models.py:1936
|
||||
#: build/lib/core/models.py:1436 core/models.py:1436
|
||||
msgid "email address"
|
||||
msgstr "електронна адреса"
|
||||
|
||||
#: build/lib/core/models.py:1955 core/models.py:1955
|
||||
#: build/lib/core/models.py:1455 core/models.py:1455
|
||||
msgid "Document invitation"
|
||||
msgstr "Запрошення до редагування документа"
|
||||
|
||||
#: build/lib/core/models.py:1956 core/models.py:1956
|
||||
#: build/lib/core/models.py:1456 core/models.py:1456
|
||||
msgid "Document invitations"
|
||||
msgstr "Запрошення до редагування документів"
|
||||
|
||||
#: build/lib/core/models.py:1976 core/models.py:1976
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
msgid "This email is already associated to a registered user."
|
||||
msgstr "Ця електронна пошта вже пов'язана з зареєстрованим користувачем."
|
||||
|
||||
#: build/lib/impress/settings.py:702 impress/settings.py:702
|
||||
msgid "Docs AI"
|
||||
msgstr "Docs ШІ"
|
||||
|
||||
#: core/templates/mail/html/template.html:153
|
||||
#: core/templates/mail/text/template.txt:3
|
||||
msgid "Logo email"
|
||||
|
||||
@@ -2,8 +2,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: lasuite-docs\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-26 09:13+0000\n"
|
||||
"PO-Revision-Date: 2026-02-27 08:24\n"
|
||||
"POT-Creation-Date: 2026-01-21 09:53+0000\n"
|
||||
"PO-Revision-Date: 2026-01-28 20:12\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Chinese Simplified\n"
|
||||
"Language: zh_CN\n"
|
||||
@@ -17,65 +17,57 @@ msgstr ""
|
||||
"X-Crowdin-File: backend-impress.pot\n"
|
||||
"X-Crowdin-File-ID: 18\n"
|
||||
|
||||
#: build/lib/core/admin.py:30 core/admin.py:30
|
||||
#: build/lib/core/admin.py:28 core/admin.py:28
|
||||
msgid "Personal info"
|
||||
msgstr "個人資訊"
|
||||
|
||||
#: build/lib/core/admin.py:43 build/lib/core/admin.py:161 core/admin.py:43
|
||||
#: core/admin.py:161
|
||||
#: build/lib/core/admin.py:41 build/lib/core/admin.py:121 core/admin.py:41
|
||||
#: core/admin.py:121
|
||||
msgid "Permissions"
|
||||
msgstr "權限"
|
||||
|
||||
#: build/lib/core/admin.py:55 core/admin.py:55
|
||||
#: build/lib/core/admin.py:53 core/admin.py:53
|
||||
msgid "Important dates"
|
||||
msgstr "重要日期"
|
||||
|
||||
#: build/lib/core/admin.py:112 core/admin.py:112
|
||||
msgid "Import job created and queued."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:116 core/admin.py:116
|
||||
msgid "Process selected user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/admin.py:171 core/admin.py:171
|
||||
#: build/lib/core/admin.py:131 core/admin.py:131
|
||||
msgid "Tree structure"
|
||||
msgstr "樹狀結構"
|
||||
|
||||
#: build/lib/core/api/filters.py:48 core/api/filters.py:48
|
||||
#: build/lib/core/api/filters.py:47 core/api/filters.py:47
|
||||
msgid "Title"
|
||||
msgstr "標題"
|
||||
|
||||
#: build/lib/core/api/filters.py:62 core/api/filters.py:62
|
||||
#: build/lib/core/api/filters.py:61 core/api/filters.py:61
|
||||
msgid "Creator is me"
|
||||
msgstr "建立者是我"
|
||||
|
||||
#: build/lib/core/api/filters.py:65 core/api/filters.py:65
|
||||
#: build/lib/core/api/filters.py:64 core/api/filters.py:64
|
||||
msgid "Masked"
|
||||
msgstr "已隱藏"
|
||||
|
||||
#: build/lib/core/api/filters.py:68 core/api/filters.py:68
|
||||
#: build/lib/core/api/filters.py:67 core/api/filters.py:67
|
||||
msgid "Favorite"
|
||||
msgstr "我的最愛"
|
||||
|
||||
#: build/lib/core/api/serializers.py:513 core/api/serializers.py:513
|
||||
#: build/lib/core/api/serializers.py:505 core/api/serializers.py:505
|
||||
msgid "A new document was created on your behalf!"
|
||||
msgstr "已代表您建立新文件!"
|
||||
|
||||
#: build/lib/core/api/serializers.py:517 core/api/serializers.py:517
|
||||
#: build/lib/core/api/serializers.py:509 core/api/serializers.py:509
|
||||
msgid "You have been granted ownership of a new document:"
|
||||
msgstr "您已獲得新文件的所有權:"
|
||||
|
||||
#: build/lib/core/api/serializers.py:553 core/api/serializers.py:553
|
||||
#: build/lib/core/api/serializers.py:545 core/api/serializers.py:545
|
||||
msgid "This field is required."
|
||||
msgstr "此欄位為必填。"
|
||||
|
||||
#: build/lib/core/api/serializers.py:564 core/api/serializers.py:564
|
||||
#: build/lib/core/api/serializers.py:556 core/api/serializers.py:556
|
||||
#, python-format
|
||||
msgid "Link reach '%(link_reach)s' is not allowed based on parent document configuration."
|
||||
msgstr "根據父文件設定,不允許連結範圍「%(link_reach)s」。"
|
||||
|
||||
#: build/lib/core/api/viewsets.py:1278 core/api/viewsets.py:1278
|
||||
#: build/lib/core/api/viewsets.py:1122 core/api/viewsets.py:1122
|
||||
#, python-brace-format
|
||||
msgid "copy of {title}"
|
||||
msgstr "{title} 的副本"
|
||||
@@ -143,374 +135,262 @@ msgstr "左"
|
||||
msgid "Right"
|
||||
msgstr "右"
|
||||
|
||||
#: build/lib/core/models.py:80 core/models.py:80
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
msgid "id"
|
||||
msgstr "ID"
|
||||
|
||||
#: build/lib/core/models.py:81 core/models.py:81
|
||||
#: build/lib/core/models.py:82 core/models.py:82
|
||||
msgid "primary key for the record as UUID"
|
||||
msgstr "記錄的主鍵(UUID)"
|
||||
|
||||
#: build/lib/core/models.py:87 core/models.py:87
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
msgid "created on"
|
||||
msgstr "建立於"
|
||||
|
||||
#: build/lib/core/models.py:88 core/models.py:88
|
||||
#: build/lib/core/models.py:89 core/models.py:89
|
||||
msgid "date and time at which a record was created"
|
||||
msgstr "記錄建立的日期與時間"
|
||||
|
||||
#: build/lib/core/models.py:93 core/models.py:93
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
msgid "updated on"
|
||||
msgstr "更新於"
|
||||
|
||||
#: build/lib/core/models.py:94 core/models.py:94
|
||||
#: build/lib/core/models.py:95 core/models.py:95
|
||||
msgid "date and time at which a record was last updated"
|
||||
msgstr "記錄最後更新的日期與時間"
|
||||
|
||||
#: build/lib/core/models.py:130 core/models.py:130
|
||||
#: build/lib/core/models.py:131 core/models.py:131
|
||||
msgid "We couldn't find a user with this sub but the email is already associated with a registered user."
|
||||
msgstr "我們找不到具有此 sub 的使用者,但此電子郵件地址已與已註冊使用者關聯。"
|
||||
|
||||
#: build/lib/core/models.py:141 core/models.py:141
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
msgid "sub"
|
||||
msgstr "sub"
|
||||
|
||||
#: build/lib/core/models.py:142 core/models.py:142
|
||||
#: build/lib/core/models.py:143 core/models.py:143
|
||||
msgid "Required. 255 characters or fewer. ASCII characters only."
|
||||
msgstr "必填。255 個字元(含)以下。僅限 ASCII 字元。"
|
||||
|
||||
#: build/lib/core/models.py:150 core/models.py:150
|
||||
#: build/lib/core/models.py:151 core/models.py:151
|
||||
msgid "full name"
|
||||
msgstr "全名"
|
||||
|
||||
#: build/lib/core/models.py:152 core/models.py:152
|
||||
#: build/lib/core/models.py:153 core/models.py:153
|
||||
msgid "short name"
|
||||
msgstr "簡稱"
|
||||
|
||||
#: build/lib/core/models.py:155 core/models.py:155
|
||||
#: build/lib/core/models.py:156 core/models.py:156
|
||||
msgid "identity email address"
|
||||
msgstr "身份驗證電子郵件地址"
|
||||
|
||||
#: build/lib/core/models.py:160 core/models.py:160
|
||||
#: build/lib/core/models.py:161 core/models.py:161
|
||||
msgid "admin email address"
|
||||
msgstr "管理員電子郵件地址"
|
||||
|
||||
#: build/lib/core/models.py:167 core/models.py:167
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
msgid "language"
|
||||
msgstr "語言"
|
||||
|
||||
#: build/lib/core/models.py:168 core/models.py:168
|
||||
#: build/lib/core/models.py:169 core/models.py:169
|
||||
msgid "The language in which the user wants to see the interface."
|
||||
msgstr "使用者希望介面顯示的語言。"
|
||||
|
||||
#: build/lib/core/models.py:176 core/models.py:176
|
||||
#: build/lib/core/models.py:177 core/models.py:177
|
||||
msgid "The timezone in which the user wants to see times."
|
||||
msgstr "使用者希望時間顯示的時區。"
|
||||
|
||||
#: build/lib/core/models.py:179 core/models.py:179
|
||||
#: build/lib/core/models.py:180 core/models.py:180
|
||||
msgid "device"
|
||||
msgstr "裝置"
|
||||
|
||||
#: build/lib/core/models.py:181 core/models.py:181
|
||||
#: build/lib/core/models.py:182 core/models.py:182
|
||||
msgid "Whether the user is a device or a real user."
|
||||
msgstr "使用者是裝置還是真實使用者。"
|
||||
|
||||
#: build/lib/core/models.py:184 core/models.py:184
|
||||
#: build/lib/core/models.py:185 core/models.py:185
|
||||
msgid "staff status"
|
||||
msgstr "工作人員狀態"
|
||||
|
||||
#: build/lib/core/models.py:186 core/models.py:186
|
||||
#: build/lib/core/models.py:187 core/models.py:187
|
||||
msgid "Whether the user can log into this admin site."
|
||||
msgstr "使用者是否可以登入此管理後台。"
|
||||
|
||||
#: build/lib/core/models.py:189 core/models.py:189
|
||||
#: build/lib/core/models.py:190 core/models.py:190
|
||||
msgid "active"
|
||||
msgstr "啟用"
|
||||
|
||||
#: build/lib/core/models.py:192 core/models.py:192
|
||||
#: build/lib/core/models.py:193 core/models.py:193
|
||||
msgid "Whether this user should be treated as active. Unselect this instead of deleting accounts."
|
||||
msgstr "此使用者是否應被視為處於啟用狀態。請取消勾選此項而非刪除帳號。"
|
||||
|
||||
#: build/lib/core/models.py:204 core/models.py:204
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
msgid "user"
|
||||
msgstr "使用者"
|
||||
|
||||
#: build/lib/core/models.py:205 core/models.py:205
|
||||
#: build/lib/core/models.py:206 core/models.py:206
|
||||
msgid "users"
|
||||
msgstr "使用者"
|
||||
|
||||
#: build/lib/core/models.py:360 core/models.py:360
|
||||
msgid "Active email address"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:361 core/models.py:361
|
||||
msgid "Email address to deactivate"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:388 core/models.py:388
|
||||
msgid "Unique ID in the source file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:394 build/lib/core/models.py:692 core/models.py:394
|
||||
#: core/models.py:692
|
||||
msgid "Pending"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:395 core/models.py:395
|
||||
msgid "Ready"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:396 build/lib/core/models.py:694 core/models.py:396
|
||||
#: core/models.py:694
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:397 build/lib/core/models.py:695 core/models.py:397
|
||||
#: core/models.py:695
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:405 core/models.py:405
|
||||
msgid "user reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:406 core/models.py:406
|
||||
msgid "user reconciliations"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:644 core/models.py:644
|
||||
msgid "You have requested a reconciliation of your user accounts on Docs.\n"
|
||||
" To confirm that you are the one who initiated the request\n"
|
||||
" and that this email belongs to you:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:650 core/models.py:650
|
||||
msgid "Confirm by clicking the link to start the reconciliation"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:655 build/lib/core/models.py:761 core/models.py:655
|
||||
#: core/models.py:761
|
||||
msgid "Click here"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:656 core/models.py:656
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:667 core/models.py:667
|
||||
msgid "Your reconciliation request has been processed.\n"
|
||||
" New documents are likely associated with your account:"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:672 core/models.py:672
|
||||
msgid "Your accounts have been merged"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:677 core/models.py:677
|
||||
msgid "Click here to see"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:678 core/models.py:678
|
||||
msgid "See my documents"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:688 core/models.py:688
|
||||
msgid "CSV file"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:693 core/models.py:693
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:703 core/models.py:703
|
||||
msgid "user reconciliation CSV import"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:704 core/models.py:704
|
||||
msgid "user reconciliation CSV imports"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:748 core/models.py:748
|
||||
#, python-brace-format
|
||||
msgid "Your request for reconciliation was unsuccessful.\n"
|
||||
" Reconciliation failed for the following email addresses:\n"
|
||||
" {recipient_email}, {other_email}.\n"
|
||||
" Please check for typos.\n"
|
||||
" You can submit another request with the valid email addresses."
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:756 core/models.py:756
|
||||
msgid "Reconciliation of your Docs accounts not completed"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:762 core/models.py:762
|
||||
msgid "Make a new request"
|
||||
msgstr ""
|
||||
|
||||
#: build/lib/core/models.py:861 core/models.py:861
|
||||
#: build/lib/core/models.py:362 core/models.py:362
|
||||
msgid "title"
|
||||
msgstr "標題"
|
||||
|
||||
#: build/lib/core/models.py:862 core/models.py:862
|
||||
#: build/lib/core/models.py:363 core/models.py:363
|
||||
msgid "excerpt"
|
||||
msgstr "摘要"
|
||||
|
||||
#: build/lib/core/models.py:911 core/models.py:911
|
||||
#: build/lib/core/models.py:412 core/models.py:412
|
||||
msgid "Document"
|
||||
msgstr "文件"
|
||||
|
||||
#: build/lib/core/models.py:912 core/models.py:912
|
||||
#: build/lib/core/models.py:413 core/models.py:413
|
||||
msgid "Documents"
|
||||
msgstr "文件"
|
||||
|
||||
#: build/lib/core/models.py:924 build/lib/core/models.py:1328
|
||||
#: core/models.py:924 core/models.py:1328
|
||||
#: build/lib/core/models.py:425 build/lib/core/models.py:828 core/models.py:425
|
||||
#: core/models.py:828
|
||||
msgid "Untitled Document"
|
||||
msgstr "未命名文件"
|
||||
|
||||
#: build/lib/core/models.py:1329 core/models.py:1329
|
||||
#: build/lib/core/models.py:829 core/models.py:829
|
||||
msgid "Open"
|
||||
msgstr "開啟"
|
||||
|
||||
#: build/lib/core/models.py:1364 core/models.py:1364
|
||||
#: build/lib/core/models.py:864 core/models.py:864
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you!"
|
||||
msgstr "{name} 與您分享了一份文件!"
|
||||
|
||||
#: build/lib/core/models.py:1368 core/models.py:1368
|
||||
#: build/lib/core/models.py:868 core/models.py:868
|
||||
#, python-brace-format
|
||||
msgid "{name} invited you with the role \"{role}\" on the following document:"
|
||||
msgstr "{name} 邀請您以「{role}」角色參與以下文件:"
|
||||
|
||||
#: build/lib/core/models.py:1374 core/models.py:1374
|
||||
#: build/lib/core/models.py:874 core/models.py:874
|
||||
#, python-brace-format
|
||||
msgid "{name} shared a document with you: {title}"
|
||||
msgstr "{name} 與您分享了一份文件:{title}"
|
||||
|
||||
#: build/lib/core/models.py:1475 core/models.py:1475
|
||||
#: build/lib/core/models.py:975 core/models.py:975
|
||||
msgid "Document/user link trace"
|
||||
msgstr "文件/使用者連結追蹤"
|
||||
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
#: build/lib/core/models.py:976 core/models.py:976
|
||||
msgid "Document/user link traces"
|
||||
msgstr "文件/使用者連結追蹤"
|
||||
|
||||
#: build/lib/core/models.py:1482 core/models.py:1482
|
||||
#: build/lib/core/models.py:982 core/models.py:982
|
||||
msgid "A link trace already exists for this document/user."
|
||||
msgstr "此文件/使用者已存在連結追蹤。"
|
||||
|
||||
#: build/lib/core/models.py:1505 core/models.py:1505
|
||||
#: build/lib/core/models.py:1005 core/models.py:1005
|
||||
msgid "Document favorite"
|
||||
msgstr "文件收藏"
|
||||
|
||||
#: build/lib/core/models.py:1506 core/models.py:1506
|
||||
#: build/lib/core/models.py:1006 core/models.py:1006
|
||||
msgid "Document favorites"
|
||||
msgstr "文件收藏"
|
||||
|
||||
#: build/lib/core/models.py:1512 core/models.py:1512
|
||||
#: build/lib/core/models.py:1012 core/models.py:1012
|
||||
msgid "This document is already targeted by a favorite relation instance for the same user."
|
||||
msgstr "此使用者已將此文件加入收藏。"
|
||||
|
||||
#: build/lib/core/models.py:1534 core/models.py:1534
|
||||
#: build/lib/core/models.py:1034 core/models.py:1034
|
||||
msgid "Document/user relation"
|
||||
msgstr "文件/使用者關聯"
|
||||
|
||||
#: build/lib/core/models.py:1535 core/models.py:1535
|
||||
#: build/lib/core/models.py:1035 core/models.py:1035
|
||||
msgid "Document/user relations"
|
||||
msgstr "文件/使用者關聯"
|
||||
|
||||
#: build/lib/core/models.py:1541 core/models.py:1541
|
||||
#: build/lib/core/models.py:1041 core/models.py:1041
|
||||
msgid "This user is already in this document."
|
||||
msgstr "此使用者已在此文件中。"
|
||||
|
||||
#: build/lib/core/models.py:1547 core/models.py:1547
|
||||
#: build/lib/core/models.py:1047 core/models.py:1047
|
||||
msgid "This team is already in this document."
|
||||
msgstr "此團隊已在此文件中。"
|
||||
|
||||
#: build/lib/core/models.py:1553 core/models.py:1553
|
||||
#: build/lib/core/models.py:1053 core/models.py:1053
|
||||
msgid "Either user or team must be set, not both."
|
||||
msgstr "必須設定使用者或團隊其中之一,不能同時設定兩者。"
|
||||
|
||||
#: build/lib/core/models.py:1704 core/models.py:1704
|
||||
#: build/lib/core/models.py:1204 core/models.py:1204
|
||||
msgid "Document ask for access"
|
||||
msgstr "要求文件存取權"
|
||||
|
||||
#: build/lib/core/models.py:1705 core/models.py:1705
|
||||
#: build/lib/core/models.py:1205 core/models.py:1205
|
||||
msgid "Document ask for accesses"
|
||||
msgstr "要求文件存取權"
|
||||
|
||||
#: build/lib/core/models.py:1711 core/models.py:1711
|
||||
#: build/lib/core/models.py:1211 core/models.py:1211
|
||||
msgid "This user has already asked for access to this document."
|
||||
msgstr "此使用者已要求過存取此文件的權限。"
|
||||
|
||||
#: build/lib/core/models.py:1768 core/models.py:1768
|
||||
#: build/lib/core/models.py:1268 core/models.py:1268
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to a document!"
|
||||
msgstr "{name} 想要存取文件!"
|
||||
|
||||
#: build/lib/core/models.py:1772 core/models.py:1772
|
||||
#: build/lib/core/models.py:1272 core/models.py:1272
|
||||
#, python-brace-format
|
||||
msgid "{name} would like access to the following document:"
|
||||
msgstr "{name} 想要存取以下文件:"
|
||||
|
||||
#: build/lib/core/models.py:1778 core/models.py:1778
|
||||
#: build/lib/core/models.py:1278 core/models.py:1278
|
||||
#, python-brace-format
|
||||
msgid "{name} is asking for access to the document: {title}"
|
||||
msgstr "{name} 正要求存取文件:{title}"
|
||||
|
||||
#: build/lib/core/models.py:1820 core/models.py:1820
|
||||
#: build/lib/core/models.py:1320 core/models.py:1320
|
||||
msgid "Thread"
|
||||
msgstr "對話串"
|
||||
|
||||
#: build/lib/core/models.py:1821 core/models.py:1821
|
||||
#: build/lib/core/models.py:1321 core/models.py:1321
|
||||
msgid "Threads"
|
||||
msgstr "對話串"
|
||||
|
||||
#: build/lib/core/models.py:1824 build/lib/core/models.py:1876
|
||||
#: core/models.py:1824 core/models.py:1876
|
||||
#: build/lib/core/models.py:1324 build/lib/core/models.py:1376
|
||||
#: core/models.py:1324 core/models.py:1376
|
||||
msgid "Anonymous"
|
||||
msgstr "匿名"
|
||||
|
||||
#: build/lib/core/models.py:1871 core/models.py:1871
|
||||
#: build/lib/core/models.py:1371 core/models.py:1371
|
||||
msgid "Comment"
|
||||
msgstr "評論"
|
||||
|
||||
#: build/lib/core/models.py:1872 core/models.py:1872
|
||||
#: build/lib/core/models.py:1372 core/models.py:1372
|
||||
msgid "Comments"
|
||||
msgstr "評論"
|
||||
|
||||
#: build/lib/core/models.py:1921 core/models.py:1921
|
||||
#: build/lib/core/models.py:1421 core/models.py:1421
|
||||
msgid "This emoji has already been reacted to this comment."
|
||||
msgstr "此評論已標記過此表情符號。"
|
||||
|
||||
#: build/lib/core/models.py:1925 core/models.py:1925
|
||||
#: build/lib/core/models.py:1425 core/models.py:1425
|
||||
msgid "Reaction"
|
||||
msgstr "回應"
|
||||
|
||||
#: build/lib/core/models.py:1926 core/models.py:1926
|
||||
#: build/lib/core/models.py:1426 core/models.py:1426
|
||||
msgid "Reactions"
|
||||
msgstr "回應"
|
||||
|
||||
#: build/lib/core/models.py:1936 core/models.py:1936
|
||||
#: build/lib/core/models.py:1436 core/models.py:1436
|
||||
msgid "email address"
|
||||
msgstr "電子郵件地址"
|
||||
|
||||
#: build/lib/core/models.py:1955 core/models.py:1955
|
||||
#: build/lib/core/models.py:1455 core/models.py:1455
|
||||
msgid "Document invitation"
|
||||
msgstr "文件邀請"
|
||||
|
||||
#: build/lib/core/models.py:1956 core/models.py:1956
|
||||
#: build/lib/core/models.py:1456 core/models.py:1456
|
||||
msgid "Document invitations"
|
||||
msgstr "文件邀請"
|
||||
|
||||
#: build/lib/core/models.py:1976 core/models.py:1976
|
||||
#: build/lib/core/models.py:1476 core/models.py:1476
|
||||
msgid "This email is already associated to a registered user."
|
||||
msgstr "此電子郵件地址已與已註冊使用者關聯。"
|
||||
|
||||
#: build/lib/impress/settings.py:702 impress/settings.py:702
|
||||
msgid "Docs AI"
|
||||
msgstr ""
|
||||
|
||||
#: core/templates/mail/html/template.html:153
|
||||
#: core/templates/mail/text/template.txt:3
|
||||
msgid "Logo email"
|
||||
|
||||
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "impress"
|
||||
version = "4.6.0"
|
||||
version = "4.5.0"
|
||||
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
@@ -53,17 +53,14 @@ dependencies = [
|
||||
"markdown==3.10",
|
||||
"mozilla-django-oidc==5.0.2",
|
||||
"nested-multipart-parser==1.6.0",
|
||||
"openai==2.21.0",
|
||||
"openai==2.14.0",
|
||||
"psycopg[binary]==3.3.2",
|
||||
"pycrdt==0.12.44",
|
||||
"pydantic==2.12.5",
|
||||
"pydantic-ai-slim[openai,logfire,web]==1.58.0",
|
||||
"pycrdt==0.12.44",
|
||||
"PyJWT==2.10.1",
|
||||
"python-magic==0.4.27",
|
||||
"redis<6.0.0",
|
||||
"requests==2.32.5",
|
||||
"sentry-sdk==2.48.0",
|
||||
"uvicorn==0.40.0",
|
||||
"whitenoise==6.11.0",
|
||||
]
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
ARG FRONTEND_IMAGE=frontend-build-output
|
||||
|
||||
FROM node:24-alpine AS frontend-deps
|
||||
|
||||
# Upgrade system packages to install security updates
|
||||
@@ -34,7 +32,7 @@ EXPOSE 3000
|
||||
|
||||
CMD [ "yarn", "dev"]
|
||||
|
||||
# Tilt will rebuild impress target so, we dissociate impress and impress-builder
|
||||
# Tilt will rebuild impress target so, we dissociate impress and impress-builder
|
||||
# to avoid rebuilding the app at every changes.
|
||||
FROM impress AS impress-builder
|
||||
|
||||
@@ -51,14 +49,6 @@ ENV NEXT_PUBLIC_PUBLISH_AS_MIT=${PUBLISH_AS_MIT}
|
||||
|
||||
RUN yarn build
|
||||
|
||||
# Normalize output path to /app (matches the runtime-prod layout)
|
||||
FROM scratch AS frontend-build-output
|
||||
COPY --from=impress-builder /home/frontend/apps/impress/out /app
|
||||
|
||||
# When FRONTEND_IMAGE is set to an external image, BuildKit skips
|
||||
# frontend-deps,impress-builder, and frontend-build-output entirely
|
||||
FROM ${FRONTEND_IMAGE} AS frontend-source
|
||||
|
||||
# ---- Front-end image ----
|
||||
FROM nginxinc/nginx-unprivileged:alpine3.22 AS frontend-production
|
||||
|
||||
@@ -72,7 +62,9 @@ RUN apk update && \
|
||||
ARG DOCKER_USER
|
||||
USER ${DOCKER_USER}
|
||||
|
||||
COPY --from=frontend-source /app /app
|
||||
COPY --from=impress-builder \
|
||||
/home/frontend/apps/impress/out \
|
||||
/usr/share/nginx/html
|
||||
|
||||
COPY ./src/frontend/apps/impress/conf/default.conf /etc/nginx/conf.d
|
||||
COPY ./docker/files/usr/local/bin/entrypoint /usr/local/bin/entrypoint
|
||||
|
||||
@@ -192,10 +192,10 @@ endobj
|
||||
(react-pdf)
|
||||
endobj
|
||||
55 0 obj
|
||||
(D:20260210135720Z)
|
||||
(D:20260128100716Z)
|
||||
endobj
|
||||
56 0 obj
|
||||
(chromium-4728-0-doc-export-override-content)
|
||||
(chromium-8039-0-doc-export-override-content)
|
||||
endobj
|
||||
52 0 obj
|
||||
<<
|
||||
@@ -216,7 +216,7 @@ endobj
|
||||
58 0 obj
|
||||
<<
|
||||
/Type /FontDescriptor
|
||||
/FontName /XWNEXS+Inter18pt-Regular
|
||||
/FontName /FDAZSC+Inter18pt-Regular
|
||||
/Flags 4
|
||||
/FontBBox [-742.1875 -323.242187 2579.589844 1109.375]
|
||||
/ItalicAngle 0
|
||||
@@ -232,7 +232,7 @@ endobj
|
||||
<<
|
||||
/Type /Font
|
||||
/Subtype /CIDFontType2
|
||||
/BaseFont /XWNEXS+Inter18pt-Regular
|
||||
/BaseFont /FDAZSC+Inter18pt-Regular
|
||||
/CIDSystemInfo <<
|
||||
/Registry (Adobe)
|
||||
/Ordering (Identity)
|
||||
@@ -247,7 +247,7 @@ endobj
|
||||
<<
|
||||
/Type /Font
|
||||
/Subtype /Type0
|
||||
/BaseFont /XWNEXS+Inter18pt-Regular
|
||||
/BaseFont /FDAZSC+Inter18pt-Regular
|
||||
/Encoding /Identity-H
|
||||
/DescendantFonts [59 0 R]
|
||||
/ToUnicode 60 0 R
|
||||
@@ -256,7 +256,7 @@ endobj
|
||||
62 0 obj
|
||||
<<
|
||||
/Type /FontDescriptor
|
||||
/FontName /QGXPNV+Inter18pt-Bold
|
||||
/FontName /UEJHFC+Inter18pt-Bold
|
||||
/Flags 4
|
||||
/FontBBox [-790.527344 -334.472656 2580.566406 1114.746094]
|
||||
/ItalicAngle 0
|
||||
@@ -272,7 +272,7 @@ endobj
|
||||
<<
|
||||
/Type /Font
|
||||
/Subtype /CIDFontType2
|
||||
/BaseFont /QGXPNV+Inter18pt-Bold
|
||||
/BaseFont /UEJHFC+Inter18pt-Bold
|
||||
/CIDSystemInfo <<
|
||||
/Registry (Adobe)
|
||||
/Ordering (Identity)
|
||||
@@ -287,7 +287,7 @@ endobj
|
||||
<<
|
||||
/Type /Font
|
||||
/Subtype /Type0
|
||||
/BaseFont /QGXPNV+Inter18pt-Bold
|
||||
/BaseFont /UEJHFC+Inter18pt-Bold
|
||||
/Encoding /Identity-H
|
||||
/DescendantFonts [63 0 R]
|
||||
/ToUnicode 64 0 R
|
||||
@@ -296,7 +296,7 @@ endobj
|
||||
66 0 obj
|
||||
<<
|
||||
/Type /FontDescriptor
|
||||
/FontName /SLYFFZ+Inter18pt-Italic
|
||||
/FontName /EUMTON+Inter18pt-Italic
|
||||
/Flags 68
|
||||
/FontBBox [-747.558594 -323.242187 2595.703125 1109.375]
|
||||
/ItalicAngle -9.398804
|
||||
@@ -312,7 +312,7 @@ endobj
|
||||
<<
|
||||
/Type /Font
|
||||
/Subtype /CIDFontType2
|
||||
/BaseFont /SLYFFZ+Inter18pt-Italic
|
||||
/BaseFont /EUMTON+Inter18pt-Italic
|
||||
/CIDSystemInfo <<
|
||||
/Registry (Adobe)
|
||||
/Ordering (Identity)
|
||||
@@ -327,7 +327,7 @@ endobj
|
||||
<<
|
||||
/Type /Font
|
||||
/Subtype /Type0
|
||||
/BaseFont /SLYFFZ+Inter18pt-Italic
|
||||
/BaseFont /EUMTON+Inter18pt-Italic
|
||||
/Encoding /Identity-H
|
||||
/DescendantFonts [67 0 R]
|
||||
/ToUnicode 68 0 R
|
||||
@@ -336,7 +336,7 @@ endobj
|
||||
70 0 obj
|
||||
<<
|
||||
/Type /FontDescriptor
|
||||
/FontName /GPERZO+GeistMono-Regular
|
||||
/FontName /HIJACG+GeistMono-Regular
|
||||
/Flags 5
|
||||
/FontBBox [-1738 -247 654 1012]
|
||||
/ItalicAngle 0
|
||||
@@ -352,7 +352,7 @@ endobj
|
||||
<<
|
||||
/Type /Font
|
||||
/Subtype /CIDFontType2
|
||||
/BaseFont /GPERZO+GeistMono-Regular
|
||||
/BaseFont /HIJACG+GeistMono-Regular
|
||||
/CIDSystemInfo <<
|
||||
/Registry (Adobe)
|
||||
/Ordering (Identity)
|
||||
@@ -367,7 +367,7 @@ endobj
|
||||
<<
|
||||
/Type /Font
|
||||
/Subtype /Type0
|
||||
/BaseFont /GPERZO+GeistMono-Regular
|
||||
/BaseFont /HIJACG+GeistMono-Regular
|
||||
/Encoding /Identity-H
|
||||
/DescendantFonts [71 0 R]
|
||||
/ToUnicode 72 0 R
|
||||
@@ -376,7 +376,7 @@ endobj
|
||||
74 0 obj
|
||||
<<
|
||||
/Type /FontDescriptor
|
||||
/FontName /CNJFYA+Inter18pt-BoldItalic
|
||||
/FontName /IKVFNP+Inter18pt-BoldItalic
|
||||
/Flags 68
|
||||
/FontBBox [-795.898437 -334.472656 2596.191406 1114.746094]
|
||||
/ItalicAngle -9.398804
|
||||
@@ -392,7 +392,7 @@ endobj
|
||||
<<
|
||||
/Type /Font
|
||||
/Subtype /CIDFontType2
|
||||
/BaseFont /CNJFYA+Inter18pt-BoldItalic
|
||||
/BaseFont /IKVFNP+Inter18pt-BoldItalic
|
||||
/CIDSystemInfo <<
|
||||
/Registry (Adobe)
|
||||
/Ordering (Identity)
|
||||
@@ -407,7 +407,7 @@ endobj
|
||||
<<
|
||||
/Type /Font
|
||||
/Subtype /Type0
|
||||
/BaseFont /CNJFYA+Inter18pt-BoldItalic
|
||||
/BaseFont /IKVFNP+Inter18pt-BoldItalic
|
||||
/Encoding /Identity-H
|
||||
/DescendantFonts [75 0 R]
|
||||
/ToUnicode 76 0 R
|
||||
@@ -1403,7 +1403,7 @@ trailer
|
||||
/Size 87
|
||||
/Root 3 0 R
|
||||
/Info 52 0 R
|
||||
/ID [<4d0627755c809232c991979db9766911> <4d0627755c809232c991979db9766911>]
|
||||
/ID [<2f4ec8da7e87471807031f721b6c9ac2> <2f4ec8da7e87471807031f721b6c9ac2>]
|
||||
>>
|
||||
startxref
|
||||
101726
|
||||
|
||||
@@ -76,6 +76,28 @@ test.describe('Config', () => {
|
||||
expect(webSocket.url()).toContain('ws://localhost:4444/collaboration/ws/');
|
||||
});
|
||||
|
||||
test('it checks the AI feature flag from config endpoint', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await overrideConfig(page, {
|
||||
AI_FEATURE_ENABLED: false,
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await createDoc(page, 'doc-ai-feature', browserName, 1);
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('Anything');
|
||||
await page.getByText('Anything').selectText();
|
||||
expect(
|
||||
await page.locator('button[data-test="convertMarkdown"]').count(),
|
||||
).toBe(1);
|
||||
expect(await page.locator('button[data-test="ai-actions"]').count()).toBe(
|
||||
0,
|
||||
);
|
||||
});
|
||||
|
||||
test('it checks that Crisp is trying to init from config endpoint', async ({
|
||||
page,
|
||||
}) => {
|
||||
@@ -118,6 +140,26 @@ test.describe('Config', () => {
|
||||
).toBeAttached();
|
||||
});
|
||||
|
||||
test('it checks theme_customization.translations config', async ({
|
||||
page,
|
||||
}) => {
|
||||
await overrideConfig(page, {
|
||||
theme_customization: {
|
||||
translations: {
|
||||
en: {
|
||||
translation: {
|
||||
Docs: 'MyCustomDocs',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await expect(page.getByText('MyCustomDocs')).toBeAttached();
|
||||
});
|
||||
|
||||
test('it checks the config api is called', async ({ page }) => {
|
||||
const responsePromise = page.waitForResponse(
|
||||
(response) =>
|
||||
@@ -130,7 +172,11 @@ test.describe('Config', () => {
|
||||
expect(response.ok()).toBeTruthy();
|
||||
|
||||
const json = (await response.json()) as typeof CONFIG;
|
||||
expect(json).toStrictEqual(CONFIG);
|
||||
const { theme_customization, ...configApi } = json;
|
||||
expect(theme_customization).toBeDefined();
|
||||
const { theme_customization: _, ...CONFIG_LEFT } = CONFIG;
|
||||
|
||||
expect(configApi).toStrictEqual(CONFIG_LEFT);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -140,24 +186,14 @@ test.describe('Config: Not logged', () => {
|
||||
test('it checks that theme is configured from config endpoint', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/');
|
||||
|
||||
await expect(
|
||||
page.getByText('Collaborative writing, Simplified.'),
|
||||
).toHaveCSS('font-family', /Roboto/i, {
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
await overrideConfig(page, {
|
||||
FRONTEND_THEME: 'dsfr',
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await expect(
|
||||
page.getByText('Collaborative writing, Simplified.'),
|
||||
).toHaveCSS('font-family', /Marianne/i, {
|
||||
timeout: 10000,
|
||||
});
|
||||
const header = page.locator('header').first();
|
||||
// alt 'Gouvernement Logo' comes from the theme
|
||||
await expect(header.getByAltText('Gouvernement Logo')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,339 +0,0 @@
|
||||
/* eslint-disable playwright/no-conditional-expect */
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import {
|
||||
createDoc,
|
||||
mockedDocument,
|
||||
overrideConfig,
|
||||
verifyDocName,
|
||||
} from './utils-common';
|
||||
import {
|
||||
mockAIResponse,
|
||||
openSuggestionMenu,
|
||||
writeInEditor,
|
||||
} from './utils-editor';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test.describe('Doc AI feature', () => {
|
||||
[
|
||||
{
|
||||
AI_FEATURE_ENABLED: false,
|
||||
selector: 'Ask AI',
|
||||
},
|
||||
{
|
||||
AI_FEATURE_ENABLED: true,
|
||||
AI_FEATURE_BLOCKNOTE_ENABLED: false,
|
||||
selector: 'Ask AI',
|
||||
},
|
||||
{
|
||||
AI_FEATURE_ENABLED: true,
|
||||
AI_FEATURE_LEGACY_ENABLED: false,
|
||||
selector: 'AI',
|
||||
},
|
||||
].forEach((config) => {
|
||||
test(`it checks the AI feature flag from config endpoint: ${JSON.stringify(config)}`, async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await overrideConfig(page, config);
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await createDoc(page, 'doc-ai-feature', browserName, 1);
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('Anything');
|
||||
await page.getByText('Anything').selectText();
|
||||
expect(
|
||||
await page.locator('button[data-test="convertMarkdown"]').count(),
|
||||
).toBe(1);
|
||||
await expect(
|
||||
page.getByRole('button', { name: config.selector, exact: true }),
|
||||
).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
test('it checks the AI feature and accepts changes', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await overrideConfig(page, {
|
||||
AI_BOT: {
|
||||
name: 'Albert AI',
|
||||
color: '#8bc6ff',
|
||||
},
|
||||
});
|
||||
|
||||
await mockAIResponse(page);
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await createDoc(page, 'doc-ai', browserName, 1);
|
||||
|
||||
await openSuggestionMenu({ page });
|
||||
await page.getByText('Ask AI').click();
|
||||
await expect(
|
||||
page.getByRole('option', { name: 'Continue Writing' }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByRole('option', { name: 'Summarize' })).toBeVisible();
|
||||
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
const editor = await writeInEditor({ page, text: 'Hello World' });
|
||||
await editor.getByText('Hello World').selectText();
|
||||
|
||||
// Check from toolbar
|
||||
await page.getByRole('button', { name: 'Ask AI' }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('option', { name: 'Improve Writing' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('option', { name: 'Fix Spelling' }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByRole('option', { name: 'Translate' })).toBeVisible();
|
||||
|
||||
await page.getByRole('option', { name: 'Translate' }).click();
|
||||
await page
|
||||
.getByRole('textbox', { name: 'Ask anything...' })
|
||||
.fill('Translate into french');
|
||||
await page.getByRole('textbox', { name: 'Ask anything...' }).press('Enter');
|
||||
await expect(editor.getByText('Albert AI')).toBeVisible();
|
||||
await page
|
||||
.locator('p.bn-mt-suggestion-menu-item-title')
|
||||
.getByText('Accept')
|
||||
.click();
|
||||
|
||||
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
||||
|
||||
// Check Suggestion menu
|
||||
await page.locator('.bn-block-outer').last().fill('/');
|
||||
await expect(page.getByText('Write with AI')).toBeVisible();
|
||||
|
||||
// Reload the page to check that the AI change is still there
|
||||
await page.goto(page.url());
|
||||
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
||||
});
|
||||
|
||||
test('it reverts with the AI feature', async ({ page, browserName }) => {
|
||||
await overrideConfig(page, {
|
||||
AI_BOT: {
|
||||
name: 'Albert AI',
|
||||
color: '#8bc6ff',
|
||||
},
|
||||
});
|
||||
|
||||
await mockAIResponse(page);
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await createDoc(page, 'doc-ai', browserName, 1);
|
||||
|
||||
const editor = await writeInEditor({ page, text: 'Hello World' });
|
||||
await editor.getByText('Hello World').selectText();
|
||||
|
||||
// Check from toolbar
|
||||
await page.getByRole('button', { name: 'Ask AI' }).click();
|
||||
|
||||
await page.getByRole('option', { name: 'Translate' }).click();
|
||||
await page
|
||||
.getByRole('textbox', { name: 'Ask anything...' })
|
||||
.fill('Translate into french');
|
||||
await page.getByRole('textbox', { name: 'Ask anything...' }).press('Enter');
|
||||
await expect(editor.getByText('Albert AI')).toBeVisible();
|
||||
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
||||
await page
|
||||
.locator('p.bn-mt-suggestion-menu-item-title')
|
||||
.getByText('Revert')
|
||||
.click();
|
||||
|
||||
await expect(editor.getByText('Hello World')).toBeVisible();
|
||||
});
|
||||
|
||||
test('it checks the AI buttons feature legacy', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await page.route(/.*\/ai-translate\//, async (route) => {
|
||||
const request = route.request();
|
||||
if (request.method().includes('POST')) {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
answer: 'Hallo Welt',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await createDoc(page, 'doc-ai', browserName, 1);
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('Hello World');
|
||||
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.getByText('Hello').selectText();
|
||||
|
||||
await page.getByRole('button', { name: 'AI', exact: true }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Use as prompt' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Rephrase' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Summarize' }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByRole('menuitem', { name: 'Correct' })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Language' }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Language' }).hover();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'English', exact: true }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'French', exact: true }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'German', exact: true }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'German', exact: true }).click();
|
||||
|
||||
await expect(editor.getByText('Hallo Welt')).toBeVisible();
|
||||
});
|
||||
|
||||
[
|
||||
{ ai_transform: false, ai_translate: false },
|
||||
{ ai_transform: true, ai_translate: false },
|
||||
{ ai_transform: false, ai_translate: true },
|
||||
].forEach(({ ai_transform, ai_translate }) => {
|
||||
test(`it checks AI buttons when can transform is at "${ai_transform}" and can translate is at "${ai_translate}"`, async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await mockedDocument(page, {
|
||||
accesses: [
|
||||
{
|
||||
id: 'b0df4343-c8bd-4c20-9ff6-fbf94fc94egg',
|
||||
role: 'owner',
|
||||
user: {
|
||||
email: 'super@owner.com',
|
||||
full_name: 'Super Owner',
|
||||
},
|
||||
},
|
||||
],
|
||||
abilities: {
|
||||
destroy: true, // Means owner
|
||||
link_configuration: true,
|
||||
ai_transform,
|
||||
ai_translate,
|
||||
accesses_manage: true,
|
||||
accesses_view: true,
|
||||
update: true,
|
||||
partial_update: true,
|
||||
retrieve: true,
|
||||
},
|
||||
link_reach: 'restricted',
|
||||
link_role: 'editor',
|
||||
created_at: '2021-09-01T09:00:00Z',
|
||||
title: '',
|
||||
});
|
||||
|
||||
const [randomDoc] = await createDoc(
|
||||
page,
|
||||
'doc-editor-ai',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
|
||||
await verifyDocName(page, randomDoc);
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('Hello World');
|
||||
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.getByText('Hello').selectText();
|
||||
|
||||
if (!ai_transform && !ai_translate) {
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'AI', exact: true }),
|
||||
).toBeHidden();
|
||||
return;
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: 'AI', exact: true }).click();
|
||||
|
||||
if (ai_transform) {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Use as prompt' }),
|
||||
).toBeVisible();
|
||||
} else {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Use as prompt' }),
|
||||
).toBeHidden();
|
||||
}
|
||||
|
||||
if (ai_translate) {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Language' }),
|
||||
).toBeVisible();
|
||||
} else {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Language' }),
|
||||
).toBeHidden();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test(`it checks ai_proxy ability`, async ({ page, browserName }) => {
|
||||
await mockedDocument(page, {
|
||||
accesses: [
|
||||
{
|
||||
id: 'b0df4343-c8bd-4c20-9ff6-fbf94fc94egg',
|
||||
role: 'owner',
|
||||
user: {
|
||||
email: 'super@owner.com',
|
||||
full_name: 'Super Owner',
|
||||
},
|
||||
},
|
||||
],
|
||||
abilities: {
|
||||
destroy: true, // Means owner
|
||||
link_configuration: true,
|
||||
ai_proxy: false,
|
||||
accesses_manage: true,
|
||||
accesses_view: true,
|
||||
update: true,
|
||||
partial_update: true,
|
||||
retrieve: true,
|
||||
},
|
||||
link_reach: 'restricted',
|
||||
link_role: 'editor',
|
||||
created_at: '2021-09-01T09:00:00Z',
|
||||
title: '',
|
||||
});
|
||||
|
||||
const [randomDoc] = await createDoc(
|
||||
page,
|
||||
'doc-editor-ai-proxy',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
|
||||
await verifyDocName(page, randomDoc);
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('Hello World');
|
||||
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.getByText('Hello').selectText();
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Ask AI' })).toBeHidden();
|
||||
await page.locator('.bn-block-outer').last().fill('/');
|
||||
await expect(page.getByText('Write with AI')).toBeHidden();
|
||||
});
|
||||
});
|
||||
@@ -41,7 +41,7 @@ test.describe('Doc Comments', () => {
|
||||
// We add a comment with the first user
|
||||
const editor = await writeInEditor({ page, text: 'Hello World' });
|
||||
await editor.getByText('Hello').selectText();
|
||||
await page.getByRole('button', { name: 'Comment', exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Comment' }).click();
|
||||
|
||||
const thread = page.locator('.bn-thread');
|
||||
await thread.getByRole('paragraph').first().fill('This is a comment');
|
||||
@@ -124,7 +124,7 @@ test.describe('Doc Comments', () => {
|
||||
// Checks add react reaction
|
||||
const editor = await writeInEditor({ page, text: 'Hello' });
|
||||
await editor.getByText('Hello').selectText();
|
||||
await page.getByRole('button', { name: 'Comment', exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Comment' }).click();
|
||||
|
||||
const thread = page.locator('.bn-thread');
|
||||
await thread.getByRole('paragraph').first().fill('This is a comment');
|
||||
@@ -191,7 +191,7 @@ test.describe('Doc Comments', () => {
|
||||
|
||||
/* Delete the last comment remove the thread */
|
||||
await editor.getByText('Hello').selectText();
|
||||
await page.getByRole('button', { name: 'Comment', exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Comment' }).click();
|
||||
|
||||
await thread.getByRole('paragraph').first().fill('This is a new comment');
|
||||
await thread.locator('[data-test="save"]').click();
|
||||
@@ -249,9 +249,7 @@ test.describe('Doc Comments', () => {
|
||||
editor.getByText('Hello, I can edit the document'),
|
||||
).toBeVisible();
|
||||
await otherEditor.getByText('Hello').selectText();
|
||||
await otherPage
|
||||
.getByRole('button', { name: 'Comment', exact: true })
|
||||
.click();
|
||||
await otherPage.getByRole('button', { name: 'Comment' }).click();
|
||||
const otherThread = otherPage.locator('.bn-thread');
|
||||
await otherThread
|
||||
.getByRole('paragraph')
|
||||
@@ -282,7 +280,7 @@ test.describe('Doc Comments', () => {
|
||||
await expect(otherThread).toBeHidden();
|
||||
await otherEditor.getByText('Hello').selectText();
|
||||
await expect(
|
||||
otherPage.getByRole('button', { name: 'Comment', exact: true }),
|
||||
otherPage.getByRole('button', { name: 'Comment' }),
|
||||
).toBeHidden();
|
||||
|
||||
await otherPage.reload();
|
||||
@@ -336,7 +334,7 @@ test.describe('Doc Comments', () => {
|
||||
// We add a comment in the first document
|
||||
const editor1 = await writeInEditor({ page, text: 'Document One' });
|
||||
await editor1.getByText('Document One').selectText();
|
||||
await page.getByRole('button', { name: 'Comment', exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Comment' }).click();
|
||||
|
||||
const thread1 = page.locator('.bn-thread');
|
||||
await thread1.getByRole('paragraph').first().fill('Comment in Doc One');
|
||||
@@ -390,7 +388,7 @@ test.describe('Doc Comments mobile', () => {
|
||||
// Checks add react reaction
|
||||
const editor = await writeInEditor({ page, text: 'Hello' });
|
||||
await editor.getByText('Hello').selectText();
|
||||
await page.getByRole('button', { name: 'Comment', exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Comment' }).click();
|
||||
|
||||
const thread = page.locator('.bn-thread');
|
||||
await thread.getByRole('paragraph').first().fill('This is a comment');
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable playwright/no-conditional-expect */
|
||||
import path from 'path';
|
||||
|
||||
import { expect, test } from '@playwright/test';
|
||||
@@ -6,6 +7,7 @@ import cs from 'convert-stream';
|
||||
import {
|
||||
createDoc,
|
||||
goToGridDoc,
|
||||
mockedDocument,
|
||||
overrideConfig,
|
||||
verifyDocName,
|
||||
} from './utils-common';
|
||||
@@ -37,7 +39,6 @@ test.describe('Doc Editor', () => {
|
||||
.selectText();
|
||||
|
||||
const toolbar = page.locator('.bn-formatting-toolbar');
|
||||
await expect(toolbar.getByRole('button', { name: 'Ask AI' })).toBeVisible();
|
||||
await expect(
|
||||
toolbar.locator('button[data-test="comment-toolbar-button"]'),
|
||||
).toBeVisible();
|
||||
@@ -63,6 +64,9 @@ test.describe('Doc Editor', () => {
|
||||
await expect(
|
||||
toolbar.locator('button[data-test="createLink"]'),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
toolbar.locator('button[data-test="ai-actions"]'),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
toolbar.locator('button[data-test="convertMarkdown"]'),
|
||||
).toBeVisible();
|
||||
@@ -89,12 +93,14 @@ test.describe('Doc Editor', () => {
|
||||
|
||||
await expect(image).toHaveAttribute('role', 'presentation');
|
||||
|
||||
await image.click();
|
||||
await image.dblclick();
|
||||
|
||||
await expect(toolbar.getByRole('button', { name: 'Ask AI' })).toBeHidden();
|
||||
await expect(
|
||||
toolbar.locator('button[data-test="comment-toolbar-button"]'),
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
toolbar.locator('button[data-test="ai-actions"]'),
|
||||
).toBeHidden();
|
||||
await expect(
|
||||
toolbar.locator('button[data-test="convertMarkdown"]'),
|
||||
).toBeHidden();
|
||||
@@ -383,6 +389,139 @@ test.describe('Doc Editor', () => {
|
||||
await expect(image).toHaveAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
test('it checks the AI buttons', async ({ page, browserName }) => {
|
||||
await page.route(/.*\/ai-translate\//, async (route) => {
|
||||
const request = route.request();
|
||||
if (request.method().includes('POST')) {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
answer: 'Bonjour le monde',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await createDoc(page, 'doc-ai', browserName, 1);
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('Hello World');
|
||||
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.getByText('Hello').selectText();
|
||||
|
||||
await page.getByRole('button', { name: 'AI' }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Use as prompt' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Rephrase' }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Summarize' }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByRole('menuitem', { name: 'Correct' })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Language' }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Language' }).hover();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'English', exact: true }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'French', exact: true }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'German', exact: true }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'English', exact: true }).click();
|
||||
|
||||
await expect(editor.getByText('Bonjour le monde')).toBeVisible();
|
||||
});
|
||||
|
||||
[
|
||||
{ ai_transform: false, ai_translate: false },
|
||||
{ ai_transform: true, ai_translate: false },
|
||||
{ ai_transform: false, ai_translate: true },
|
||||
].forEach(({ ai_transform, ai_translate }) => {
|
||||
test(`it checks AI buttons when can transform is at "${ai_transform}" and can translate is at "${ai_translate}"`, async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await mockedDocument(page, {
|
||||
accesses: [
|
||||
{
|
||||
id: 'b0df4343-c8bd-4c20-9ff6-fbf94fc94egg',
|
||||
role: 'owner',
|
||||
user: {
|
||||
email: 'super@owner.com',
|
||||
full_name: 'Super Owner',
|
||||
},
|
||||
},
|
||||
],
|
||||
abilities: {
|
||||
destroy: true, // Means owner
|
||||
link_configuration: true,
|
||||
ai_transform,
|
||||
ai_translate,
|
||||
accesses_manage: true,
|
||||
accesses_view: true,
|
||||
update: true,
|
||||
partial_update: true,
|
||||
retrieve: true,
|
||||
},
|
||||
link_reach: 'restricted',
|
||||
link_role: 'editor',
|
||||
created_at: '2021-09-01T09:00:00Z',
|
||||
title: '',
|
||||
});
|
||||
|
||||
const [randomDoc] = await createDoc(
|
||||
page,
|
||||
'doc-editor-ai',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
|
||||
await verifyDocName(page, randomDoc);
|
||||
|
||||
await page.locator('.bn-block-outer').last().fill('Hello World');
|
||||
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.getByText('Hello').selectText();
|
||||
|
||||
if (!ai_transform && !ai_translate) {
|
||||
await expect(page.getByRole('button', { name: 'AI' })).toBeHidden();
|
||||
return;
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: 'AI' }).click();
|
||||
|
||||
if (ai_transform) {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Use as prompt' }),
|
||||
).toBeVisible();
|
||||
} else {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Use as prompt' }),
|
||||
).toBeHidden();
|
||||
}
|
||||
|
||||
if (ai_translate) {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Language' }),
|
||||
).toBeVisible();
|
||||
} else {
|
||||
await expect(
|
||||
page.getByRole('menuitem', { name: 'Language' }),
|
||||
).toBeHidden();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('it downloads unsafe files', async ({ page, browserName }) => {
|
||||
const [randomDoc] = await createDoc(page, 'doc-editor', browserName, 1);
|
||||
|
||||
|
||||
@@ -1,26 +1,10 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import {
|
||||
createDoc,
|
||||
getGridRow,
|
||||
getOtherBrowserName,
|
||||
mockedListDocs,
|
||||
toggleHeaderMenu,
|
||||
verifyDocName,
|
||||
} from './utils-common';
|
||||
import { writeInEditor } from './utils-editor';
|
||||
import {
|
||||
addNewMember,
|
||||
connectOtherUserToDoc,
|
||||
updateShareLink,
|
||||
} from './utils-share';
|
||||
import { createDoc, mockedListDocs, toggleHeaderMenu } from './utils-common';
|
||||
import { createRootSubPage } from './utils-sub-pages';
|
||||
|
||||
test.describe('Doc grid move', () => {
|
||||
test('it checks drag and drop functionality', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
test.describe('Doc grid dnd', () => {
|
||||
test('it creates a doc', async ({ page, browserName }) => {
|
||||
await page.goto('/');
|
||||
const header = page.locator('header').first();
|
||||
await createDoc(page, 'Draggable doc', browserName, 1);
|
||||
@@ -45,7 +29,7 @@ test.describe('Doc grid move', () => {
|
||||
await expect(draggableElement).toBeVisible();
|
||||
await expect(dropZone).toBeVisible();
|
||||
|
||||
// Get the position of the elements
|
||||
// Obtenir les positions des éléments
|
||||
const draggableBoundingBox = await draggableElement.boundingBox();
|
||||
const dropZoneBoundingBox = await dropZone.boundingBox();
|
||||
|
||||
@@ -62,7 +46,7 @@ test.describe('Doc grid move', () => {
|
||||
);
|
||||
await page.mouse.down();
|
||||
|
||||
// Move to the target zone
|
||||
// Déplacer vers la zone cible
|
||||
await page.mouse.move(
|
||||
dropZoneBoundingBox.x + dropZoneBoundingBox.width / 2,
|
||||
dropZoneBoundingBox.y + dropZoneBoundingBox.height / 2,
|
||||
@@ -177,213 +161,6 @@ test.describe('Doc grid move', () => {
|
||||
|
||||
await page.mouse.up();
|
||||
});
|
||||
|
||||
test('it moves a doc from the doc search modal', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await page.goto('/');
|
||||
|
||||
const [titleDoc1] = await createDoc(page, 'Draggable doc', browserName, 1);
|
||||
|
||||
const otherBrowserName = getOtherBrowserName(browserName);
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await addNewMember(page, 0, 'Administrator', otherBrowserName);
|
||||
await page
|
||||
.getByRole('dialog')
|
||||
.getByRole('button', { name: 'close' })
|
||||
.click();
|
||||
|
||||
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
||||
|
||||
const [titleDoc2] = await createDoc(page, 'Droppable doc', browserName, 1);
|
||||
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
||||
|
||||
const docsGrid = page.getByTestId('docs-grid');
|
||||
await expect(docsGrid.getByText(titleDoc1)).toBeVisible();
|
||||
await expect(docsGrid.getByText(titleDoc2)).toBeVisible();
|
||||
|
||||
const row = await getGridRow(page, titleDoc1);
|
||||
await row.getByText(`more_horiz`).click();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Move into a doc' }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('dialog').getByRole('heading', { name: 'Move' }),
|
||||
).toBeVisible();
|
||||
|
||||
const input = page.getByRole('combobox', { name: 'Quick search input' });
|
||||
await input.click();
|
||||
await input.fill(titleDoc2);
|
||||
|
||||
await expect(
|
||||
page.getByRole('option').first().getByText(titleDoc2),
|
||||
).toBeVisible();
|
||||
|
||||
// Select the first result
|
||||
await page.keyboard.press('Enter');
|
||||
// The CTA should get the focus
|
||||
await page.keyboard.press('Tab');
|
||||
// Validate the move action
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
await expect(
|
||||
page
|
||||
.getByRole('dialog')
|
||||
.getByText('it will lose its current access rights'),
|
||||
).toBeVisible();
|
||||
|
||||
await page
|
||||
.getByRole('dialog')
|
||||
.getByRole('button', { name: 'Move', exact: true })
|
||||
.first()
|
||||
.click();
|
||||
|
||||
await expect(docsGrid.getByText(titleDoc1)).toBeHidden();
|
||||
await docsGrid
|
||||
.getByRole('link', { name: `Open document ${titleDoc2}` })
|
||||
.click();
|
||||
|
||||
await verifyDocName(page, titleDoc2);
|
||||
|
||||
const docTree = page.getByTestId('doc-tree');
|
||||
await expect(docTree.getByText(titleDoc1)).toBeVisible();
|
||||
});
|
||||
|
||||
test('it proposes an access request when moving a doc without sufficient permissions', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
test.slow();
|
||||
await page.goto('/');
|
||||
|
||||
const [titleDoc1] = await createDoc(page, 'Move doc', browserName, 1);
|
||||
|
||||
const { otherPage, cleanup } = await connectOtherUserToDoc({
|
||||
docUrl: '/',
|
||||
browserName,
|
||||
});
|
||||
|
||||
// Another user creates a doc
|
||||
const [titleDoc2] = await createDoc(otherPage, 'Drop doc', browserName, 1);
|
||||
await writeInEditor({
|
||||
page: otherPage,
|
||||
text: 'Hello world',
|
||||
});
|
||||
// Make it public
|
||||
await otherPage.getByRole('button', { name: 'Share' }).click();
|
||||
await updateShareLink(otherPage, 'Public');
|
||||
await otherPage
|
||||
.getByRole('dialog')
|
||||
.getByRole('button', { name: 'close' })
|
||||
.click();
|
||||
const otherPageUrl = otherPage.url();
|
||||
|
||||
// The first user visit the doc to have it in his grid list
|
||||
await page.goto(otherPageUrl);
|
||||
await expect(page.getByText('Hello world')).toBeVisible();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
||||
|
||||
const docsGrid = page.getByTestId('docs-grid');
|
||||
await expect(docsGrid.getByText(titleDoc1)).toBeVisible();
|
||||
await expect(docsGrid.getByText(titleDoc2)).toBeVisible();
|
||||
|
||||
const row = await getGridRow(page, titleDoc1);
|
||||
await row.getByText(`more_horiz`).click();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Move into a doc' }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('dialog').getByRole('heading', { name: 'Move' }),
|
||||
).toBeVisible();
|
||||
|
||||
const input = page.getByRole('combobox', { name: 'Quick search input' });
|
||||
await input.click();
|
||||
await input.fill(titleDoc2);
|
||||
|
||||
await expect(
|
||||
page.getByRole('option').first().getByText(titleDoc2),
|
||||
).toBeVisible();
|
||||
|
||||
// Select the first result
|
||||
await page.keyboard.press('Enter');
|
||||
// The CTA should get the focus
|
||||
await page.keyboard.press('Tab');
|
||||
// Validate the move action
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// Request access modal should be visible
|
||||
await expect(
|
||||
page
|
||||
.getByRole('dialog')
|
||||
.getByText(
|
||||
'You need edit access to the destination. Request access, then try again.',
|
||||
),
|
||||
).toBeVisible();
|
||||
|
||||
await page
|
||||
.getByRole('dialog')
|
||||
.getByRole('button', { name: 'Request access', exact: true })
|
||||
.first()
|
||||
.click();
|
||||
|
||||
// The other user should receive the access request and be able to approve it
|
||||
await otherPage.getByRole('button', { name: 'Share' }).click();
|
||||
await expect(otherPage.getByText('Access Requests')).toBeVisible();
|
||||
await expect(otherPage.getByText(`E2E ${browserName}`)).toBeVisible();
|
||||
|
||||
const emailRequest = `user.test@${browserName}.test`;
|
||||
await expect(otherPage.getByText(emailRequest)).toBeVisible();
|
||||
const container = otherPage.getByTestId(
|
||||
`doc-share-access-request-row-${emailRequest}`,
|
||||
);
|
||||
await container.getByTestId('doc-role-dropdown').click();
|
||||
await otherPage.getByRole('menuitem', { name: 'Administrator' }).click();
|
||||
await container.getByRole('button', { name: 'Approve' }).click();
|
||||
|
||||
await expect(otherPage.getByText('Access Requests')).toBeHidden();
|
||||
await expect(otherPage.getByText('Share with 2 users')).toBeVisible();
|
||||
await expect(otherPage.getByText(`E2E ${browserName}`)).toBeVisible();
|
||||
|
||||
// The first user should now be able to move the doc
|
||||
await page.reload();
|
||||
await row.getByText(`more_horiz`).click();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Move into a doc' }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('dialog').getByRole('heading', { name: 'Move' }),
|
||||
).toBeVisible();
|
||||
|
||||
await input.click();
|
||||
await input.fill(titleDoc2);
|
||||
|
||||
await expect(
|
||||
page.getByRole('option').first().getByText(titleDoc2),
|
||||
).toBeVisible();
|
||||
|
||||
// Select the first result
|
||||
await page.keyboard.press('Enter');
|
||||
// The CTA should get the focus
|
||||
await page.keyboard.press('Tab');
|
||||
// Validate the move action
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
await expect(docsGrid.getByText(titleDoc1)).toBeHidden();
|
||||
await docsGrid
|
||||
.getByRole('link', { name: `Open document ${titleDoc2}` })
|
||||
.click();
|
||||
|
||||
await verifyDocName(page, titleDoc2);
|
||||
|
||||
const docTree = page.getByTestId('doc-tree');
|
||||
await expect(docTree.getByText(titleDoc1)).toBeVisible();
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Doc grid dnd mobile', () => {
|
||||
@@ -115,7 +115,7 @@ test.describe('Document grid item options', () => {
|
||||
|
||||
// Pin
|
||||
await row.getByText(`more_horiz`).click();
|
||||
await page.getByRole('menuitem', { name: 'Pin' }).click();
|
||||
await page.getByText('push_pin').click();
|
||||
|
||||
// Check is pinned
|
||||
await expect(row.getByTestId('doc-pinned-icon')).toBeVisible();
|
||||
@@ -264,7 +264,7 @@ test.describe('Documents Grid', () => {
|
||||
test('checks all the elements are visible', async ({ page }) => {
|
||||
void page.goto('/');
|
||||
|
||||
let docs: SmallDoc[];
|
||||
let docs: SmallDoc[] = [];
|
||||
const response = await page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().endsWith('documents/?page=1') &&
|
||||
@@ -290,7 +290,7 @@ test.describe('Documents Grid', () => {
|
||||
});
|
||||
|
||||
test('checks the infinite scroll', async ({ page }) => {
|
||||
let docs: SmallDoc[];
|
||||
let docs: SmallDoc[] = [];
|
||||
const responsePromisePage1 = page.waitForResponse((response) => {
|
||||
return (
|
||||
response.url().endsWith(`/documents/?page=1`) &&
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
mockedDocument,
|
||||
verifyDocName,
|
||||
} from './utils-common';
|
||||
import { writeInEditor } from './utils-editor';
|
||||
import {
|
||||
connectOtherUserToDoc,
|
||||
mockedAccesses,
|
||||
@@ -21,46 +20,6 @@ test.beforeEach(async ({ page }) => {
|
||||
});
|
||||
|
||||
test.describe('Doc Header', () => {
|
||||
test('toggles panel collapse from floating bar button', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
const [docTitle] = await createDoc(
|
||||
page,
|
||||
'doc-floating-bar',
|
||||
browserName,
|
||||
1,
|
||||
);
|
||||
|
||||
const cardCollapse = page.locator('.--docs--left-panel-collapse-button');
|
||||
const collapseButton = cardCollapse.getByTestId(
|
||||
'floating-bar-toggle-left-panel',
|
||||
);
|
||||
await expect(collapseButton).toBeVisible();
|
||||
|
||||
// Panel open
|
||||
await expect(collapseButton).toHaveAttribute('aria-expanded', 'true');
|
||||
await expect(collapseButton.getByText(docTitle)).toBeHidden();
|
||||
|
||||
// Collapse panel
|
||||
await collapseButton.click();
|
||||
await expect(collapseButton).toHaveAttribute('aria-expanded', 'false');
|
||||
await expect(cardCollapse.getByText(docTitle)).toBeHidden();
|
||||
|
||||
// When the title is not visible in the viewport, the button should show the title
|
||||
const editor = await writeInEditor({ page, text: 'Lorem ipsum' });
|
||||
for (let i = 0; i < 25; i++) {
|
||||
await editor.press('Enter');
|
||||
}
|
||||
await writeInEditor({ page, text: 'Lorem ipsum 2' });
|
||||
await expect(cardCollapse.getByText(docTitle)).toBeVisible();
|
||||
|
||||
// Expand panel and check the title is hidden again
|
||||
await collapseButton.click();
|
||||
await expect(collapseButton).toHaveAttribute('aria-expanded', 'true');
|
||||
await expect(cardCollapse.getByText(docTitle)).toBeHidden();
|
||||
});
|
||||
|
||||
test('it checks the element are correctly displayed', async ({
|
||||
page,
|
||||
browserName,
|
||||
@@ -546,7 +505,7 @@ test.describe('Doc Header', () => {
|
||||
.click();
|
||||
|
||||
// Pin
|
||||
await page.getByRole('menuitem', { name: 'Pin' }).click();
|
||||
await page.getByText('push_pin').click();
|
||||
await page
|
||||
.getByRole('button', { name: 'Open the document options' })
|
||||
.click();
|
||||
@@ -567,11 +526,11 @@ test.describe('Doc Header', () => {
|
||||
.click();
|
||||
|
||||
// Unpin
|
||||
await page.getByRole('menuitem', { name: 'Unpin' }).click();
|
||||
await page.getByText('Unpin').click();
|
||||
await page
|
||||
.getByRole('button', { name: 'Open the document options' })
|
||||
.click();
|
||||
await expect(page.getByRole('menuitem', { name: 'Pin' })).toBeVisible();
|
||||
await expect(page.getByText('push_pin')).toBeVisible();
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
|
||||
@@ -56,13 +56,14 @@ test.describe('Footer', () => {
|
||||
|
||||
test('checks the footer is correctly overrided', async ({ page }) => {
|
||||
await overrideConfig(page, {
|
||||
FRONTEND_THEME: 'dsfr',
|
||||
theme_customization: {
|
||||
footer: {
|
||||
default: {
|
||||
logo: {
|
||||
src: '/assets/logo-gouv.svg',
|
||||
width: '220px',
|
||||
alt: 'Gouvernement Logo',
|
||||
style: { width: '220px', height: 'auto' },
|
||||
},
|
||||
externalLinks: [
|
||||
{
|
||||
|
||||
@@ -34,15 +34,10 @@ test.describe('Header', () => {
|
||||
FRONTEND_THEME: 'dsfr',
|
||||
theme_customization: {
|
||||
header: {
|
||||
icon: {
|
||||
src: '/assets/icon-docs-v2.svg',
|
||||
style: {
|
||||
width: '100px',
|
||||
height: 'auto',
|
||||
},
|
||||
alt: '',
|
||||
withTitle: false,
|
||||
'data-testid': 'custom-testid-docs',
|
||||
logo: {
|
||||
src: '/assets/logo-gouv.svg',
|
||||
width: '220px',
|
||||
alt: 'Gouvernement Logo',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -51,11 +46,19 @@ test.describe('Header', () => {
|
||||
|
||||
const header = page.locator('header').first();
|
||||
|
||||
await expect(header.getByTestId('custom-testid-docs')).toHaveAttribute(
|
||||
'src',
|
||||
'/assets/icon-docs-v2.svg',
|
||||
await expect(header.getByTestId('header-icon-docs')).toBeVisible();
|
||||
await expect(header.locator('h1').getByText('Docs')).toHaveCSS(
|
||||
'font-family',
|
||||
/Marianne/i,
|
||||
);
|
||||
await expect(header.locator('h1')).toBeHidden();
|
||||
|
||||
await expect(
|
||||
header.getByRole('button', {
|
||||
name: 'Logout',
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(header.getByText('English')).toBeVisible();
|
||||
});
|
||||
|
||||
test('checks a custom waffle', async ({ page }) => {
|
||||
@@ -143,6 +146,32 @@ test.describe('Header', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Header mobile', () => {
|
||||
test.use({ viewport: { width: 500, height: 1200 } });
|
||||
|
||||
test('it checks the header when mobile with DSFR theme', async ({ page }) => {
|
||||
await overrideConfig(page, {
|
||||
FRONTEND_THEME: 'dsfr',
|
||||
theme_customization: {
|
||||
header: {
|
||||
logo: {
|
||||
src: '/assets/logo-gouv.svg',
|
||||
width: '220px',
|
||||
alt: 'Gouvernement Logo',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const header = page.locator('header').first();
|
||||
|
||||
await expect(header.getByLabel('Open the header menu')).toBeVisible();
|
||||
await expect(header.getByTestId('header-icon-docs')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Header: Log out', () => {
|
||||
test.use({ storageState: { cookies: [], origins: [] } });
|
||||
|
||||
|
||||
@@ -91,44 +91,21 @@ test.describe('Home page', () => {
|
||||
],
|
||||
},
|
||||
},
|
||||
header: {
|
||||
logo: {
|
||||
src: '/assets/logo-gouv.svg',
|
||||
alt: 'Gouvernement Logo',
|
||||
style: { width: '110px', height: 'auto' },
|
||||
},
|
||||
icon: {
|
||||
src: '/assets/icon-docs-dsfr-v2.png',
|
||||
style: {
|
||||
width: '100px',
|
||||
height: 'auto',
|
||||
},
|
||||
alt: '',
|
||||
withTitle: false,
|
||||
},
|
||||
},
|
||||
home: {
|
||||
'with-proconnect': true,
|
||||
'icon-banner': {
|
||||
src: '/assets/icon-docs.svg',
|
||||
style: {
|
||||
width: '64px',
|
||||
height: 'auto',
|
||||
},
|
||||
alt: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await page.goto('/docs/');
|
||||
|
||||
// Wait for the page to be fully loaded and responsive store to be initialized
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
// Wait a bit more for the responsive store to be initialized
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Check header content
|
||||
const header = page.locator('header').first();
|
||||
const footer = page.locator('footer').first();
|
||||
await expect(header).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
await expect(header).toBeVisible();
|
||||
|
||||
// Check for language picker - it should be visible on desktop
|
||||
// Use a more flexible selector that works with both Header and HomeHeader
|
||||
@@ -141,6 +118,7 @@ test.describe('Home page', () => {
|
||||
header.getByRole('img', { name: 'Gouvernement Logo' }),
|
||||
).toBeVisible();
|
||||
await expect(header.getByTestId('header-icon-docs')).toBeVisible();
|
||||
await expect(header.getByRole('heading', { name: 'Docs' })).toBeVisible();
|
||||
|
||||
// Check the titles
|
||||
const h2 = page.locator('h2');
|
||||
|
||||
@@ -13,32 +13,6 @@ test.describe('Language', () => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test('it checks theme_customization.translations config', async ({
|
||||
page,
|
||||
}) => {
|
||||
await overrideConfig(page, {
|
||||
theme_customization: {
|
||||
translations: {
|
||||
en: {
|
||||
translation: {
|
||||
Docs: 'MyCustomDocs',
|
||||
},
|
||||
},
|
||||
},
|
||||
header: {
|
||||
logo: {},
|
||||
icon: {
|
||||
withTitle: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
await expect(page.getByText('MyCustomDocs')).toBeAttached();
|
||||
});
|
||||
|
||||
test('checks language switching', async ({ page }) => {
|
||||
const header = page.locator('header').first();
|
||||
const languagePicker = header.locator('.--docs--language-picker-text');
|
||||
|
||||
@@ -3,19 +3,11 @@ import path from 'path';
|
||||
|
||||
import { Locator, Page, TestInfo, expect } from '@playwright/test';
|
||||
|
||||
import theme_customization from '../../../../../backend/impress/configuration/theme/default.json';
|
||||
|
||||
export type BrowserName = 'chromium' | 'firefox' | 'webkit';
|
||||
export const BROWSERS: BrowserName[] = ['chromium', 'webkit', 'firefox'];
|
||||
|
||||
export const CONFIG = {
|
||||
AI_BOT: {
|
||||
name: 'Docs AI',
|
||||
color: '#8bc6ff',
|
||||
},
|
||||
AI_FEATURE_ENABLED: true,
|
||||
AI_FEATURE_BLOCKNOTE_ENABLED: true,
|
||||
AI_FEATURE_LEGACY_ENABLED: true,
|
||||
API_USERS_SEARCH_QUERY_MIN_LENGTH: 3,
|
||||
CRISP_WEBSITE_ID: null,
|
||||
COLLABORATION_WS_URL: 'ws://localhost:4444/collaboration/ws/',
|
||||
@@ -40,7 +32,7 @@ export const CONFIG = {
|
||||
POSTHOG_KEY: {},
|
||||
SENTRY_DSN: null,
|
||||
TRASHBIN_CUTOFF_DAYS: 30,
|
||||
theme_customization,
|
||||
theme_customization: {},
|
||||
} as const;
|
||||
|
||||
export const overrideConfig = async (
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { Page } from '@playwright/test';
|
||||
|
||||
export const getEditor = async ({ page }: { page: Page }) => {
|
||||
@@ -32,55 +30,3 @@ export const writeInEditor = async ({
|
||||
.fill(text);
|
||||
return editor;
|
||||
};
|
||||
|
||||
export const mockAIResponse = async (page: Page) => {
|
||||
await page.route(/.*\/ai-proxy\//, async (route) => {
|
||||
const req = route.request();
|
||||
|
||||
if (req.method() !== 'POST') {
|
||||
return route.continue();
|
||||
}
|
||||
|
||||
// Extract the block ID from the request's selectedBlocks
|
||||
const requestData = req.postDataJSON();
|
||||
const messages = requestData?.messages || [];
|
||||
const userMessage = messages.find((msg: any) => msg.role === 'user');
|
||||
const documentState = userMessage?.metadata?.documentState;
|
||||
const selectedBlocks = documentState?.selectedBlocks || [];
|
||||
const blockId = selectedBlocks[0]?.id || 'initialBlockId$';
|
||||
|
||||
const sse = [
|
||||
`data: {"type":"start"}\n\n`,
|
||||
`data: {"type":"start-step"}\n\n`,
|
||||
`data: ${JSON.stringify({
|
||||
type: 'tool-input-available',
|
||||
toolCallId: 'chatcmpl-mock-0',
|
||||
toolName: 'applyDocumentOperations',
|
||||
input: {
|
||||
operations: [
|
||||
{
|
||||
type: 'update',
|
||||
id: blockId,
|
||||
block: '<p>Bonjour le monde</p>',
|
||||
},
|
||||
],
|
||||
},
|
||||
})}\n\n`,
|
||||
`data: {"type":"finish-step"}\n\n`,
|
||||
`data: {"type":"finish","finishReason":"tool-calls"}\n\n`,
|
||||
`data: [DONE]\n\n`,
|
||||
].join('');
|
||||
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream; charset=utf-8',
|
||||
'Cache-Control': 'no-cache, no-transform',
|
||||
'x-vercel-ai-data-stream': 'v1',
|
||||
'x-accel-buffering': 'no',
|
||||
Connection: 'keep-alive',
|
||||
},
|
||||
body: sse,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "app-e2e",
|
||||
"version": "4.6.0",
|
||||
"version": "4.5.0",
|
||||
"repository": "https://github.com/suitenumerique/docs",
|
||||
"author": "DINUM",
|
||||
"license": "MIT",
|
||||
@@ -15,7 +15,7 @@
|
||||
"test:ui::chromium": "yarn test:ui --project=chromium"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.58.2",
|
||||
"@playwright/test": "1.57.0",
|
||||
"@types/node": "*",
|
||||
"@types/pdf-parse": "1.1.5",
|
||||
"eslint-plugin-docs": "*",
|
||||
|
||||
@@ -3,7 +3,7 @@ server {
|
||||
listen 3000;
|
||||
server_name localhost;
|
||||
|
||||
root /app;
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
location / {
|
||||
try_files $uri index.html $uri/index.html =404;
|
||||
|
||||
@@ -19,6 +19,24 @@ const themeWhiteLabelLight = getUIKitThemesFromGlobals(whiteLabelGlobals, {
|
||||
'2xs': '0.375rem',
|
||||
},
|
||||
},
|
||||
components: {
|
||||
logo: {
|
||||
src: '',
|
||||
alt: '',
|
||||
widthHeader: '',
|
||||
widthFooter: '',
|
||||
},
|
||||
'home-proconnect': false,
|
||||
icon: {
|
||||
src: '/assets/icon-docs.svg',
|
||||
width: '32px',
|
||||
height: 'auto',
|
||||
},
|
||||
favicon: {
|
||||
'png-light': '/assets/favicon-light.png',
|
||||
'png-dark': '/assets/favicon-dark.png',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -38,6 +56,25 @@ const themesDSFRLight = getUIKitThemesFromGlobals(dsfrGlobals, {
|
||||
},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
logo: {
|
||||
src: '/assets/logo-gouv.svg',
|
||||
widthHeader: '110px',
|
||||
widthFooter: '220px',
|
||||
alt: 'Gouvernement Logo',
|
||||
},
|
||||
'home-proconnect': true,
|
||||
icon: {
|
||||
src: '/assets/icon-docs-dsfr.svg',
|
||||
width: '32px',
|
||||
height: 'auto',
|
||||
},
|
||||
favicon: {
|
||||
ico: '/assets/favicon-dsfr.ico',
|
||||
'png-light': '/assets/favicon-dsfr.png',
|
||||
'png-dark': '/assets/favicon-dark-dsfr.png',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "app-impress",
|
||||
"version": "4.6.0",
|
||||
"version": "4.5.0",
|
||||
"repository": "https://github.com/suitenumerique/docs",
|
||||
"author": "DINUM",
|
||||
"license": "MIT",
|
||||
@@ -19,34 +19,31 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ag-media/react-pdf-table": "2.0.3",
|
||||
"@ai-sdk/openai": "3.0.19",
|
||||
"@blocknote/code-block": "0.47.0",
|
||||
"@blocknote/core": "0.47.0",
|
||||
"@blocknote/mantine": "0.47.0",
|
||||
"@blocknote/react": "0.47.0",
|
||||
"@blocknote/xl-ai": "0.47.0",
|
||||
"@blocknote/xl-docx-exporter": "0.47.0",
|
||||
"@blocknote/xl-multi-column": "0.47.0",
|
||||
"@blocknote/xl-odt-exporter": "0.47.0",
|
||||
"@blocknote/xl-pdf-exporter": "0.47.0",
|
||||
"@blocknote/code-block": "0.46.2",
|
||||
"@blocknote/core": "0.46.2",
|
||||
"@blocknote/mantine": "0.46.2",
|
||||
"@blocknote/react": "0.46.2",
|
||||
"@blocknote/xl-docx-exporter": "0.46.2",
|
||||
"@blocknote/xl-multi-column": "0.46.2",
|
||||
"@blocknote/xl-odt-exporter": "0.46.2",
|
||||
"@blocknote/xl-pdf-exporter": "0.46.2",
|
||||
"@dnd-kit/core": "6.3.1",
|
||||
"@dnd-kit/modifiers": "9.0.0",
|
||||
"@emoji-mart/data": "1.2.1",
|
||||
"@emoji-mart/react": "1.1.1",
|
||||
"@fontsource-variable/inter": "5.2.8",
|
||||
"@fontsource-variable/material-symbols-outlined": "5.2.35",
|
||||
"@fontsource-variable/material-symbols-outlined": "5.2.31",
|
||||
"@fontsource/material-icons": "5.2.7",
|
||||
"@gouvfr-lasuite/cunningham-react": "4.2.0",
|
||||
"@gouvfr-lasuite/cunningham-react": "4.1.0",
|
||||
"@gouvfr-lasuite/integration": "1.0.3",
|
||||
"@gouvfr-lasuite/ui-kit": "0.19.6",
|
||||
"@hocuspocus/provider": "3.4.4",
|
||||
"@mantine/core": "8.3.14",
|
||||
"@mantine/hooks": "8.3.14",
|
||||
"@gouvfr-lasuite/ui-kit": "0.18.7",
|
||||
"@hocuspocus/provider": "3.4.3",
|
||||
"@mantine/core": "8.3.12",
|
||||
"@mantine/hooks": "8.3.12",
|
||||
"@react-pdf/renderer": "4.3.1",
|
||||
"@sentry/nextjs": "10.38.0",
|
||||
"@tanstack/react-query": "5.90.21",
|
||||
"@sentry/nextjs": "10.34.0",
|
||||
"@tanstack/react-query": "5.90.18",
|
||||
"@tiptap/extensions": "*",
|
||||
"ai": "6.0.49",
|
||||
"canvg": "4.0.3",
|
||||
"clsx": "2.1.1",
|
||||
"cmdk": "1.1.1",
|
||||
@@ -54,57 +51,56 @@
|
||||
"emoji-datasource-apple": "16.0.0",
|
||||
"emoji-mart": "5.6.0",
|
||||
"emoji-regex": "10.6.0",
|
||||
"i18next": "25.8.12",
|
||||
"i18next-browser-languagedetector": "8.2.1",
|
||||
"i18next": "25.7.4",
|
||||
"i18next-browser-languagedetector": "8.2.0",
|
||||
"idb": "8.0.3",
|
||||
"lodash": "4.17.23",
|
||||
"luxon": "3.7.2",
|
||||
"next": "15.5.10",
|
||||
"posthog-js": "1.347.2",
|
||||
"next": "15.5.9",
|
||||
"posthog-js": "1.325.0",
|
||||
"react": "*",
|
||||
"react-aria-components": "1.15.1",
|
||||
"react-aria-components": "1.14.0",
|
||||
"react-dom": "*",
|
||||
"react-dropzone": "15.0.0",
|
||||
"react-i18next": "16.5.4",
|
||||
"react-dropzone": "14.3.8",
|
||||
"react-i18next": "16.5.3",
|
||||
"react-intersection-observer": "10.0.2",
|
||||
"react-resizable-panels": "3.0.6",
|
||||
"react-select": "5.10.2",
|
||||
"styled-components": "6.3.9",
|
||||
"styled-components": "6.3.8",
|
||||
"use-debounce": "10.1.0",
|
||||
"uuid": "13.0.0",
|
||||
"y-protocols": "1.0.7",
|
||||
"yjs": "*",
|
||||
"zod": "3.25.28",
|
||||
"zustand": "5.0.11"
|
||||
"zustand": "5.0.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@svgr/webpack": "8.1.0",
|
||||
"@tanstack/react-query-devtools": "5.91.3",
|
||||
"@tanstack/react-query-devtools": "5.91.2",
|
||||
"@testing-library/dom": "10.4.1",
|
||||
"@testing-library/jest-dom": "6.9.1",
|
||||
"@testing-library/react": "16.3.2",
|
||||
"@testing-library/react": "16.3.1",
|
||||
"@testing-library/user-event": "14.6.1",
|
||||
"@types/lodash": "4.17.23",
|
||||
"@types/luxon": "3.7.1",
|
||||
"@types/node": "*",
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"@vitejs/plugin-react": "5.1.4",
|
||||
"@vitejs/plugin-react": "5.1.2",
|
||||
"copy-webpack-plugin": "13.0.1",
|
||||
"cross-env": "10.1.0",
|
||||
"dotenv": "17.3.1",
|
||||
"dotenv": "17.2.3",
|
||||
"eslint-plugin-docs": "*",
|
||||
"fetch-mock": "9.11.0",
|
||||
"jsdom": "28.1.0",
|
||||
"jsdom": "27.4.0",
|
||||
"node-fetch": "2.7.0",
|
||||
"prettier": "3.8.1",
|
||||
"prettier": "3.8.0",
|
||||
"stylelint": "16.26.1",
|
||||
"stylelint-config-standard": "39.0.1",
|
||||
"stylelint-prettier": "5.0.3",
|
||||
"typescript": "*",
|
||||
"vite-tsconfig-paths": "6.1.1",
|
||||
"vitest": "4.0.18",
|
||||
"webpack": "5.105.2",
|
||||
"vite-tsconfig-paths": "6.0.4",
|
||||
"vitest": "4.0.17",
|
||||
"webpack": "5.104.1",
|
||||
"workbox-webpack-plugin": "7.1.0"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22"
|
||||
|
||||
|
Before Width: | Height: | Size: 7.2 KiB |
@@ -1,34 +0,0 @@
|
||||
<svg
|
||||
width="100"
|
||||
height="40"
|
||||
viewBox="0 0 100 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M25.6305 32.8312C26.7983 32.5038 27.9166 31.9062 28.6505 30.8503C29.3749 29.8163 29.5789 28.5047 29.5789 27.2425V8.75099C29.5789 8.42358 29.5611 8.09557 29.5216 7.77148C30.1016 7.99961 30.5486 8.37658 30.8626 8.90239C31.2331 9.50024 31.4184 10.2876 31.4184 11.2643V30.0464C31.4184 31.3684 31.0942 32.3578 30.4458 33.0146C29.7974 33.6714 28.8207 33.9998 27.5155 33.9998H20.4209C20.5889 33.9704 20.7574 33.9401 20.9262 33.909C22.4067 33.6444 23.9713 33.2854 25.6185 32.8346L25.6305 32.8312Z"
|
||||
fill="#C83F49"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M8.58203 29.655V10.8477C8.58203 9.70251 8.88938 8.83519 9.50408 8.24575C10.1272 7.65631 10.9524 7.33212 11.9797 7.27318C13.4954 7.18055 14.9311 7.05425 16.2868 6.89425C17.6425 6.72584 18.9393 6.53217 20.1771 6.31324C21.4234 6.0943 22.6359 5.85011 23.8148 5.58065C25.0274 5.29435 25.9578 5.4375 26.6062 6.0101C27.2546 6.58269 27.5788 7.49632 27.5788 8.75099V27.2425C27.5788 28.3456 27.3893 29.1666 27.0104 29.7055C26.6315 30.2529 25.9915 30.6528 25.0905 30.9054C23.4906 31.3433 21.9833 31.6886 20.5687 31.9412C19.154 32.2022 17.7731 32.4001 16.4258 32.5348C15.0785 32.6696 13.6975 32.7748 12.2829 32.8506C11.1124 32.918 10.203 32.6738 9.5546 32.118C8.90622 31.5707 8.58203 30.7497 8.58203 29.655ZM13.2087 14.2624C15.0635 14.1444 16.7632 13.9631 18.3075 13.7183C18.6822 13.6572 19.0564 13.5936 19.4291 13.5276C19.8192 13.4585 20.1013 13.1186 20.1013 12.7234C20.1013 12.2115 19.638 11.8261 19.135 11.9119C18.846 11.9612 18.5555 12.0091 18.2635 12.0556C16.7346 12.2992 15.0452 12.48 13.1952 12.5977C12.9182 12.6156 12.6978 12.7019 12.5561 12.8739C12.4221 13.0366 12.3564 13.2323 12.3564 13.4553C12.3564 13.6821 12.433 13.8795 12.5857 14.0418L12.5878 14.0439C12.7534 14.2095 12.9637 14.2811 13.2087 14.2624ZM13.208 18.456C15.0631 18.338 16.763 18.1566 18.3075 17.9119C19.8588 17.6589 21.3936 17.3638 22.9112 17.0266C23.2191 16.9581 23.4498 16.8503 23.5652 16.683C23.6786 16.5221 23.7347 16.3376 23.7347 16.1332C23.7347 15.9026 23.6469 15.704 23.476 15.5426C23.2921 15.3689 23.0348 15.3284 22.7304 15.3911L22.7285 15.3915C21.2823 15.7194 19.794 16.0053 18.2635 16.2492C16.7346 16.4928 15.0452 16.6735 13.1952 16.7913C12.9182 16.8091 12.6978 16.8954 12.5561 17.0675C12.4228 17.2294 12.3564 17.4205 12.3564 17.6363C12.3564 17.8703 12.4321 18.0723 12.5856 18.2354L12.59 18.2396C12.755 18.3949 12.9632 18.4655 13.2055 18.4562L13.208 18.456ZM13.2085 22.6494C15.0634 22.5229 16.7631 22.3374 18.3075 22.0927C19.8589 21.8482 21.3934 21.5573 22.9112 21.22C23.2199 21.1514 23.4508 21.0391 23.566 20.8627C23.6783 20.7029 23.7347 20.5233 23.7347 20.3266C23.7347 20.0961 23.6469 19.8974 23.476 19.7361C23.2921 19.5623 23.0348 19.5218 22.7304 19.5845L22.729 19.5848C21.2827 19.9043 19.7942 20.1861 18.2635 20.43C16.7345 20.6736 15.045 20.8586 13.1949 20.9847C12.918 21.0026 12.6977 21.0889 12.5561 21.2609C12.4228 21.4228 12.3564 21.6139 12.3564 21.8297C12.3564 22.0637 12.4321 22.2658 12.5856 22.4289L12.59 22.433C12.755 22.5883 12.9632 22.6589 13.2055 22.6496L13.2085 22.6494ZM18.3075 26.257C16.7632 26.5018 15.0635 26.6831 13.2087 26.8012C12.9637 26.8198 12.7534 26.7482 12.5878 26.5826L12.5857 26.5805C12.433 26.4182 12.3564 26.2208 12.3564 25.9941C12.3564 25.771 12.4221 25.5753 12.5561 25.4126C12.6978 25.2406 12.9183 25.1543 13.1953 25.1364C15.0453 25.0187 16.7346 24.838 18.2635 24.5943C18.5555 24.5478 18.846 24.4999 19.135 24.4506C19.638 24.3648 20.1013 24.7503 20.1013 25.2621C20.1013 25.6573 19.8192 25.9972 19.4291 26.0663C19.0564 26.1323 18.6822 26.1959 18.3075 26.257Z"
|
||||
fill="#2845C1"
|
||||
/>
|
||||
<path
|
||||
d="M41.2 27.95C41.0895 27.95 41 27.8605 41 27.75V12.2C41 12.0895 41.0895 12 41.2 12H47.2205C48.4813 12 49.6282 12.2127 50.6611 12.638C51.6941 13.0633 52.5827 13.6482 53.3271 14.3925C54.0866 15.1216 54.6638 15.9647 55.0588 16.9217C55.4689 17.8787 55.674 18.8965 55.674 19.975C55.674 21.0535 55.4689 22.0713 55.0588 23.0283C54.6638 23.9853 54.0866 24.836 53.3271 25.5803C52.5827 26.3094 51.6941 26.8867 50.6611 27.312C49.6282 27.7373 48.4813 27.95 47.2205 27.95H41.2ZM47.2433 14.5292H44.0026C43.8922 14.5292 43.8026 14.6188 43.8026 14.7292V25.2208C43.8026 25.3312 43.8922 25.4208 44.0026 25.4208H47.2433C48.0484 25.4208 48.7851 25.2841 49.4535 25.0106C50.1371 24.722 50.7219 24.3271 51.208 23.8258C51.7093 23.3245 52.0966 22.7473 52.3701 22.0941C52.6435 21.4409 52.7802 20.7345 52.7802 19.975C52.7802 19.2155 52.6435 18.5091 52.3701 17.8559C52.0966 17.1875 51.7093 16.6103 51.208 16.1242C50.7219 15.6229 50.1371 15.2356 49.4535 14.9621C48.7851 14.6735 48.0484 14.5292 47.2433 14.5292Z"
|
||||
fill="#2845C1"
|
||||
/>
|
||||
<path
|
||||
d="M63.3939 25.6031C63.9104 25.6031 64.3889 25.5044 64.8294 25.3069C65.2699 25.0943 65.6497 24.8132 65.9687 24.4639C66.3029 24.1145 66.5611 23.7119 66.7434 23.2562C66.9257 22.7853 67.0168 22.284 67.0168 21.7524C67.0168 21.0232 66.8573 20.37 66.5383 19.7928C66.2193 19.2155 65.7864 18.7598 65.2395 18.4256C64.6927 18.0763 64.0775 17.9016 63.3939 17.9016C62.8622 17.9016 62.3686 18.0003 61.9128 18.1978C61.4723 18.3953 61.085 18.6687 60.7508 19.0181C60.4318 19.3674 60.1811 19.7776 59.9988 20.2485C59.8166 20.7194 59.7254 21.2207 59.7254 21.7524C59.7254 22.4663 59.8849 23.1195 60.2039 23.7119C60.5229 24.2892 60.9558 24.7525 61.5027 25.1019C62.0647 25.436 62.6951 25.6031 63.3939 25.6031ZM63.3711 15.5546C64.2977 15.5546 65.1408 15.7141 65.9003 16.0331C66.675 16.3521 67.3358 16.8003 67.8827 17.3775C68.4447 17.9395 68.8701 18.6003 69.1587 19.3599C69.4625 20.1042 69.6144 20.9017 69.6144 21.7524C69.6144 22.603 69.4625 23.4081 69.1587 24.1676C68.8701 24.912 68.4447 25.5728 67.8827 26.15C67.3358 26.712 66.675 27.1526 65.9003 27.4716C65.1408 27.7906 64.2977 27.9501 63.3711 27.9501C62.4445 27.9501 61.5938 27.7906 60.8191 27.4716C60.0596 27.1526 59.3988 26.712 58.8368 26.15C58.2747 25.5728 57.8418 24.912 57.538 24.1676C57.2494 23.4081 57.1051 22.603 57.1051 21.7524C57.1051 20.9017 57.2494 20.1042 57.538 19.3599C57.8418 18.6003 58.2747 17.9395 58.8368 17.3775C59.3988 16.8003 60.0596 16.3521 60.8191 16.0331C61.5938 15.7141 62.4445 15.5546 63.3711 15.5546Z"
|
||||
fill="#2845C1"
|
||||
/>
|
||||
<path
|
||||
d="M77.2852 15.5546C78.3333 15.5546 79.2675 15.7673 80.0878 16.1926C80.8611 16.5723 81.5043 17.082 82.0173 17.7219C82.086 17.8076 82.0681 17.9322 81.9806 17.9986L80.3269 19.2531C80.2346 19.3232 80.1027 19.2999 80.0326 19.2076C79.7582 18.8466 79.4045 18.548 78.9713 18.3117C78.4852 18.0383 77.9156 17.9016 77.2624 17.9016C76.7307 17.9016 76.237 18.0003 75.7813 18.1978C75.3408 18.3953 74.961 18.6687 74.642 19.0181C74.323 19.3674 74.0724 19.7776 73.8901 20.2485C73.7078 20.7042 73.6167 21.2055 73.6167 21.7524C73.6167 22.4815 73.7762 23.1423 74.0952 23.7347C74.4142 24.3119 74.8471 24.7677 75.394 25.1019C75.9408 25.436 76.5788 25.6031 77.308 25.6031C77.9308 25.6031 78.4852 25.4664 78.9713 25.193C79.4045 24.9567 79.7582 24.6581 80.0326 24.2971C80.1027 24.2048 80.2346 24.1815 80.3269 24.2516L81.979 25.5049C82.0671 25.5717 82.0845 25.6976 82.0145 25.7831C81.5019 26.4095 80.8597 26.9191 80.0878 27.3121C79.2675 27.7374 78.3333 27.9501 77.2852 27.9501C75.994 27.9501 74.8775 27.669 73.9357 27.107C72.9939 26.5298 72.2647 25.7702 71.7483 24.8284C71.247 23.8866 70.9963 22.8613 70.9963 21.7524C70.9963 20.9169 71.1406 20.127 71.4293 19.3826C71.7179 18.6383 72.1356 17.9775 72.6825 17.4003C73.2293 16.823 73.8901 16.3749 74.6648 16.0559C75.4395 15.7217 76.313 15.5546 77.2852 15.5546Z"
|
||||
fill="#2845C1"
|
||||
/>
|
||||
<path
|
||||
d="M85.9175 18.9041C85.9175 19.2687 86.0466 19.5725 86.3049 19.8156C86.5783 20.0434 86.9277 20.2409 87.353 20.408C87.7783 20.5751 88.2265 20.7574 88.6974 20.9549C89.1835 21.1371 89.6392 21.365 90.0645 21.6384C90.4898 21.8967 90.8316 22.246 91.0899 22.6866C91.3633 23.1119 91.5 23.6512 91.5 24.3044C91.5 25.0791 91.3101 25.7399 90.9304 26.2867C90.5658 26.8184 90.0797 27.2285 89.4721 27.5171C88.8644 27.8058 88.1961 27.9501 87.4669 27.9501C86.4795 27.9501 85.6213 27.7678 84.8921 27.4032C84.2203 27.0533 83.6259 26.5938 83.1088 26.0246C83.0352 25.9436 83.0442 25.8185 83.1263 25.7462L84.5297 24.5112C84.6154 24.4358 84.7464 24.4475 84.821 24.534C85.1512 24.917 85.5168 25.2354 85.9175 25.4892C86.3732 25.7778 86.8821 25.9221 87.4441 25.9221C87.9758 25.9221 88.3784 25.7854 88.6518 25.512C88.9404 25.2234 89.0847 24.874 89.0847 24.4639C89.0847 24.0841 88.948 23.7803 88.6746 23.5524C88.4163 23.3246 88.0745 23.1271 87.6492 22.96C87.2239 22.7929 86.7682 22.6182 86.2821 22.4359C85.8112 22.2384 85.363 22.003 84.9377 21.7296C84.5124 21.4561 84.163 21.1068 83.8896 20.6814C83.6313 20.2409 83.5022 19.6789 83.5022 18.9953C83.5022 18.3573 83.6693 17.78 84.0035 17.2636C84.3377 16.7319 84.801 16.3142 85.3934 16.0104C86.001 15.7065 86.6922 15.5546 87.4669 15.5546C88.3024 15.5546 89.0771 15.7369 89.7911 16.1015C90.4556 16.4338 90.9877 16.8481 91.3872 17.3444C91.4528 17.4258 91.4385 17.5439 91.359 17.6117L89.9551 18.8097C89.8666 18.8853 89.7328 18.8692 89.6595 18.7789C89.4001 18.4596 89.1022 18.19 88.7657 17.9699C88.3708 17.7117 87.9378 17.5826 87.4669 17.5826C87.1327 17.5826 86.8441 17.6433 86.6011 17.7649C86.3732 17.8864 86.1985 18.0459 86.077 18.2434C85.9707 18.4408 85.9175 18.6611 85.9175 18.9041Z"
|
||||
fill="#2845C1"
|
||||
/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.1 KiB |
248
src/frontend/apps/impress/public/assets/logo-pdf.svg
Normal file
@@ -0,0 +1,248 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 311.6 186.5"
|
||||
style="enable-background: new 0 0 311.6 186.5"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<style type="text/css">
|
||||
.st0 {
|
||||
fill: #ffffff;
|
||||
}
|
||||
.st1 {
|
||||
fill: #000091;
|
||||
}
|
||||
.st2 {
|
||||
fill: #e1000f;
|
||||
}
|
||||
.st3 {
|
||||
fill: #9c9b9b;
|
||||
}
|
||||
</style>
|
||||
<g id="Fond">
|
||||
<rect x="0" class="st0" width="311.6" height="186.5" />
|
||||
</g>
|
||||
<g id="Calque_1">
|
||||
<path
|
||||
id="Devise_Républicaine_1_"
|
||||
d="M100.5,150.8c0.6,0,1.1,0.4,0.8,1.5l-2.7,0.6C99.1,151.8,99.9,150.8,100.5,150.8 M102,155.2
|
||||
h-0.5c-0.7,0.8-1.4,1.4-2.1,1.4c-0.7,0-1.1-0.4-1.1-1.4c0-0.4,0-0.8,0.1-1.2l4.3-1.4c0.8-2-0.2-2.9-1.4-2.9c-2,0-4.4,3.4-4.4,6.4
|
||||
c0,1.3,0.6,2.1,1.6,2.1C99.8,158.2,101,157,102,155.2 M101.2,148.9l3-2.8v-0.3h-1.6l-1.9,3.2H101.2z M91.9,150.9h1.4l-2.3,6.2
|
||||
c-0.2,0.5,0.1,1.1,0.6,1.1c1.3,0,3.4-1.3,4.1-3h-0.4c-0.6,0.6-1.7,1.4-2.6,1.6l2.1-5.8h2.1l0.3-0.9h-2.1l0.8-2.2h-0.8l-1.5,2.2
|
||||
l-1.8,0.2V150.9z M89.9,150.6c0.2-0.6-0.2-0.9-0.5-0.9c-1.2,0-2.7,1.1-3.3,2.7h0.4c0.4-0.6,1.1-1.2,1.7-1.3l-2.4,6.2
|
||||
c-0.2,0.6,0.2,0.9,0.5,0.9c1.2,0,2.6-1.1,3.2-2.7h-0.4c-0.4,0.6-1.1,1.2-1.7,1.3L89.9,150.6z M90.4,147.5c0.6,0,1-0.5,1-1
|
||||
c0-0.6-0.5-1-1-1c-0.6,0-1,0.5-1,1C89.3,147,89.8,147.5,90.4,147.5 M76.6,157c-0.3,0.7,0,1.2,0.7,1.2c0.4,0,0.6-0.1,0.8-0.6
|
||||
l1.7-4.4c0.8-0.9,2.3-1.9,3-1.9c0.5,0,0.4,0.4,0.1,0.9l-2.5,4.9c-0.2,0.5,0.1,1.1,0.6,1.1c1.2,0,2.7-1.1,3.3-2.7h-0.4
|
||||
c-0.4,0.6-1.1,1.2-1.7,1.3l2.2-4.4c0.3-0.6,0.4-1.1,0.4-1.5c0-0.7-0.4-1.2-1.2-1.2c-1.1,0-2.2,1.2-3.5,2.7v-1.2
|
||||
c0-0.8-0.3-1.6-1-1.6c-0.5,0-0.9,0.4-1.3,1v0.2c0.8,0,1.2,1.2,0.6,2.4L76.6,157z M76.6,151.6c0.3-1,0.1-1.9-0.6-1.9
|
||||
c-0.9,0-1.2,0.7-2.1,2.7v-1.2c0-0.8-0.3-1.6-1-1.6c-0.9,0-1.7,1.4-2.3,2.7H71c0.4-0.6,0.8-1,1.1-1c0.4,0,0.6,0.6,0,1.9l-1.7,3.7
|
||||
c-0.3,0.7,0,1.2,0.7,1.2c0.4,0,0.6-0.1,0.8-0.6l1.7-4.4c0.5-0.6,0.9-1.1,1.4-1.6H76.6z M67,150.8c0.6,0,1.1,0.4,0.8,1.5l-2.7,0.6
|
||||
C65.6,151.8,66.4,150.8,67,150.8 M68.5,155.2H68c-0.7,0.8-1.4,1.4-2.1,1.4c-0.7,0-1.1-0.4-1.1-1.4c0-0.4,0-0.8,0.1-1.2l4.3-1.4
|
||||
c0.8-2-0.2-2.9-1.4-2.9c-2,0-4.4,3.4-4.4,6.4c0,1.3,0.6,2.1,1.6,2.1C66.3,158.2,67.5,157,68.5,155.2 M58.3,150.9h1.4l-2.3,6.2
|
||||
c-0.2,0.5,0.1,1.1,0.6,1.1c1.3,0,3.4-1.3,4.1-3h-0.4c-0.6,0.6-1.7,1.4-2.6,1.6l2.1-5.8h2.1l0.3-0.9h-2.1l0.8-2.2h-0.8l-1.5,2.2
|
||||
l-1.8,0.2V150.9z M50.5,155.7c0-1.9,2.1-4.5,3.3-4.5c0.3,0,0.5,0,0.7,0.1l-1.2,3.3c-0.7,0.9-1.8,1.9-2.3,1.9
|
||||
C50.7,156.6,50.5,156.3,50.5,155.7 M57,149.3l-0.7,0l-0.7,0.7h-0.2c-3.6,0-6.6,4-6.6,6.9c0,0.8,0.5,1.3,1.3,1.3
|
||||
c0.9,0,1.8-1.3,2.8-2.7l0,0.5c-0.1,1.4,0.3,2.2,1.1,2.2c0.9,0,1.7-1.4,2.3-2.7h-0.4c-0.4,0.6-0.8,1-1.1,1c-0.3,0-0.6-0.6,0-1.9
|
||||
L57,149.3z M49.5,151.6c0.3-1,0.1-1.9-0.6-1.9c-0.9,0-1.2,0.7-2.1,2.7v-1.2c0-0.8-0.3-1.6-1-1.6c-0.9,0-1.7,1.4-2.3,2.7h0.4
|
||||
c0.4-0.6,0.8-1,1.1-1c0.4,0,0.6,0.6,0,1.9l-1.7,3.7c-0.3,0.7,0,1.2,0.7,1.2c0.4,0,0.6-0.1,0.8-0.6l1.7-4.4c0.5-0.6,0.9-1.1,1.4-1.6
|
||||
H49.5z M37.5,157.9l0.2-0.5c-2.1-0.4-2.3-0.4-1.5-2.6l0.8-2.3h2.3c1,0,1,0.4,0.9,1.5h0.6l1.4-3.8h-0.6c-0.5,0.9-0.9,1.5-2,1.5h-2.3
|
||||
l1.2-3.3c0.4-1,0.6-1.3,2-1.3h1c1.4,0,1.6,0.4,1.6,1.9h0.6l0.5-2.6h-8.6l-0.2,0.5c1.7,0.3,1.8,0.5,1,2.6l-1.9,5.1
|
||||
c-0.8,2.1-1.1,2.3-3,2.6l-0.1,0.5H37.5z M79.7,131.4c0.6,0,1.1,0.4,0.8,1.5l-2.7,0.6C78.3,132.3,79.1,131.4,79.7,131.4 M81.2,135.7
|
||||
h-0.5c-0.7,0.8-1.4,1.4-2.1,1.4c-0.7,0-1.1-0.4-1.1-1.4c0-0.4,0-0.8,0.1-1.2l4.3-1.4c0.8-2-0.2-2.9-1.4-2.9c-2,0-4.4,3.4-4.4,6.4
|
||||
c0,1.3,0.6,2.1,1.6,2.1C79,138.7,80.2,137.6,81.2,135.7 M80.3,129.4l3-2.8v-0.3h-1.6l-1.9,3.2H80.3z M71,131.4h1.4l-2.3,6.2
|
||||
c-0.2,0.5,0.1,1.1,0.6,1.1c1.3,0,3.4-1.3,4.1-3h-0.4c-0.6,0.6-1.7,1.4-2.6,1.6l2.1-5.8h2.1l0.3-0.9h-2.1l0.8-2.2h-0.8l-1.5,2.2
|
||||
l-1.8,0.2V131.4z M69.1,131.1c0.2-0.6-0.2-0.9-0.5-0.9c-1.2,0-2.7,1.1-3.3,2.7h0.4c0.4-0.6,1.1-1.2,1.7-1.3l-2.4,6.2
|
||||
c-0.2,0.6,0.2,0.9,0.5,0.9c1.2,0,2.6-1.1,3.2-2.7h-0.4c-0.4,0.6-1.1,1.2-1.7,1.3L69.1,131.1z M69.5,128c0.6,0,1-0.5,1-1
|
||||
c0-0.6-0.5-1-1-1c-0.6,0-1,0.5-1,1C68.5,127.6,68.9,128,69.5,128 M61.2,137.3l4.3-11.4l-0.1-0.2l-2.7,0.3v0.3l0.5,0.4
|
||||
c0.5,0.4,0.3,0.7-0.1,1.9l-3.4,8.9c-0.2,0.5,0.1,1.1,0.6,1.1c1.2,0,2.6-1.1,3.2-2.7h-0.4C62.7,136.6,61.8,137.2,61.2,137.3
|
||||
M53,136.3c0-1.9,2.1-4.5,3.3-4.5c0.3,0,0.5,0,0.7,0.1l-1.2,3.3c-0.7,0.9-1.8,1.9-2.3,1.9C53.1,137.1,53,136.8,53,136.3
|
||||
M59.5,129.9l-0.7,0l-0.7,0.7H58c-3.6,0-6.6,4-6.6,6.9c0,0.8,0.5,1.3,1.3,1.3c0.9,0,1.8-1.3,2.8-2.7l0,0.5
|
||||
c-0.1,1.4,0.3,2.2,1.1,2.2c0.9,0,1.7-1.4,2.3-2.7h-0.4c-0.4,0.6-0.8,1-1.1,1c-0.3,0-0.6-0.6,0-1.9L59.5,129.9z M43.7,140.2
|
||||
c0-0.8,0.8-1.3,1.9-1.8c0.4,0.2,0.9,0.4,1.7,0.6c1.2,0.4,1.6,0.5,1.6,0.9c0,0.8-1.3,1.3-3.1,1.3C44.4,141.3,43.7,141,43.7,140.2
|
||||
M46.9,135.2c-0.5,0-0.7-0.4-0.7-0.9c0-1.4,0.7-3.4,1.9-3.4c0.5,0,0.7,0.4,0.7,0.9C48.8,133.1,48,135.2,46.9,135.2 M50.3,139.4
|
||||
c0-1-0.9-1.4-2.3-1.8c-1.2-0.4-1.8-0.5-1.8-0.9c0-0.3,0.3-0.7,0.8-1c2-0.1,3.4-1.9,3.4-3.5c0-0.3-0.1-0.6-0.2-0.9h1.7l0.3-0.9h-2.7
|
||||
c-0.3-0.2-0.7-0.3-1.1-0.3c-2.2,0-3.6,1.9-3.6,3.4c0,1.2,0.7,1.9,1.7,2.1c-1,0.5-1.6,1-1.6,1.6c0,0.4,0.1,0.6,0.4,0.8
|
||||
c-2.3,0.7-3.3,1.5-3.3,2.6c0,1.1,1.4,1.5,3.1,1.5C47.9,142.3,50.3,140.7,50.3,139.4 M39.4,132.8c1,0,1,0.4,0.9,1.5h0.6l1.4-3.8
|
||||
h-0.6c-0.5,0.9-0.9,1.5-2,1.5h-2.3l1.1-3.1c0.4-1,0.6-1.2,2-1.2h1c1.4,0,1.6,0.4,1.6,1.8h0.6l0.5-2.6h-8.6l-0.2,0.5
|
||||
c1.7,0.3,1.8,0.5,1,2.6l-1.9,5.1c-0.8,2.1-1.1,2.3-3,2.6l-0.1,0.5H41l1.7-2.7H42c-1.1,1-2.5,2-4.4,2c-2.5,0-2.3-0.1-1.5-2.4
|
||||
l0.9-2.5H39.4z M40.6,126.2l3-2.1v-0.3h-1.8l-1.7,2.4H40.6z M78.7,111.9c0.6,0,1.1,0.4,0.8,1.5l-2.7,0.6
|
||||
C77.3,112.8,78,111.9,78.7,111.9 M80.2,116.2h-0.5c-0.7,0.8-1.4,1.4-2.1,1.4c-0.7,0-1.1-0.4-1.1-1.4c0-0.4,0-0.8,0.1-1.2l4.3-1.4
|
||||
c0.8-2-0.2-2.9-1.4-2.9c-2,0-4.4,3.4-4.4,6.4c0,1.3,0.6,2.1,1.6,2.1C77.9,119.2,79.2,118.1,80.2,116.2 M79.3,110l3-2.8v-0.3h-1.6
|
||||
l-1.9,3.2H79.3z M70.4,111.9h1.1l-2.3,6.2c-0.2,0.5,0.1,1.1,0.6,1.1c1.3,0,3.4-1.3,4.1-3h-0.4c-0.6,0.6-1.7,1.4-2.6,1.6l2.1-5.8
|
||||
h2.1l0.3-0.9h-2.1l0.8-2.2h-0.8l-1.5,2.2l-1.5,0.2V111.9z M69.2,112.6c0.3-1,0.1-1.9-0.6-1.9c-0.9,0-1.2,0.7-2.1,2.7v-1.2
|
||||
c0-0.8-0.3-1.6-1-1.6c-0.9,0-1.7,1.4-2.3,2.7h0.4c0.4-0.6,0.8-1,1.1-1c0.4,0,0.6,0.6,0,1.9l-1.7,3.7c-0.3,0.7,0,1.2,0.7,1.2
|
||||
c0.4,0,0.6-0.1,0.8-0.6l1.7-4.4c0.5-0.6,0.9-1.1,1.4-1.6H69.2z M59.7,111.9c0.6,0,1.1,0.4,0.8,1.5l-2.7,0.6
|
||||
C58.3,112.8,59.1,111.9,59.7,111.9 M61.2,116.2h-0.5c-0.7,0.8-1.4,1.4-2.1,1.4c-0.7,0-1.1-0.4-1.1-1.4c0-0.4,0-0.8,0.1-1.2l4.3-1.4
|
||||
c0.8-2-0.2-2.9-1.4-2.9c-2,0-4.4,3.4-4.4,6.4c0,1.3,0.6,2.1,1.6,2.1C59,119.2,60.2,118.1,61.2,116.2 M50.6,118c-0.4,0-1-0.4-1-0.7
|
||||
c0-0.1,0.2-0.6,0.4-1.2l0.7-1.9c0.7-0.9,1.9-1.8,2.5-1.8c0.4,0,0.7,0.2,0.7,0.8C53.9,114.9,52.3,118,50.6,118 M55.5,112.5
|
||||
c0-1.3-0.5-1.7-1.3-1.7c-1.1,0-2.1,1.2-3.1,2.6l2.6-6.8l-0.1-0.2l-2.7,0.3v0.3l0.5,0.4c0.5,0.4,0.3,0.8-0.1,1.9l-2.8,7.2
|
||||
c-0.2,0.5-0.5,1.2-0.5,1.3c0,0.7,1,1.4,1.9,1.4C52,119.2,55.5,115.4,55.5,112.5 M47,111.6c0.2-0.6-0.2-0.9-0.5-0.9
|
||||
c-1.2,0-2.7,1.1-3.3,2.7h0.4c0.4-0.6,1.1-1.2,1.7-1.3l-2.4,6.2c-0.2,0.6,0.2,0.9,0.5,0.9c1.2,0,2.6-1.1,3.2-2.7h-0.4
|
||||
c-0.4,0.6-1.1,1.2-1.7,1.3L47,111.6z M47.5,108.5c0.6,0,1-0.5,1-1c0-0.6-0.5-1-1-1c-0.6,0-1,0.5-1,1
|
||||
C46.4,108.1,46.9,108.5,47.5,108.5 M41.2,107.5h-5.7l-0.2,0.5c1.7,0.3,1.8,0.5,1,2.6l-1.8,5.1c-0.8,2.1-1.1,2.3-3,2.6l-0.1,0.5H40
|
||||
l1.9-3.3h-0.7c-1.1,1.2-2.3,2.6-4.2,2.6c-1.4,0-1.6-0.2-0.8-2.4l1.8-5.1c0.8-2.1,1.1-2.3,3-2.6L41.2,107.5z"
|
||||
/>
|
||||
<path
|
||||
d="M40.9,88.2c1,0,1.9-0.2,2.7-0.5c0.8-0.3,1.5-0.8,2.1-1.3v-3.5h-5.3v-3.8h9.4v8.8c-1,1.3-2.2,2.3-3.8,3.1s-3.3,1.2-5.2,1.2
|
||||
c-1.7,0-3.2-0.3-4.6-0.9c-1.4-0.6-2.6-1.4-3.6-2.4c-1-1-1.8-2.1-2.3-3.5c-0.6-1.3-0.8-2.7-0.8-4.2c0-1.5,0.3-2.9,0.8-4.2
|
||||
c0.5-1.3,1.3-2.5,2.2-3.5c1-1,2.1-1.8,3.5-2.4s2.8-0.9,4.5-0.9c1.9,0,3.5,0.4,5,1.2c1.5,0.8,2.7,1.8,3.7,3L46,77
|
||||
c-0.6-0.8-1.3-1.5-2.3-2.1s-2-0.8-3.1-0.8c-1,0-1.9,0.2-2.7,0.5c-0.8,0.4-1.5,0.9-2.1,1.5c-0.6,0.6-1,1.4-1.4,2.2
|
||||
c-0.3,0.9-0.5,1.8-0.5,2.8s0.2,1.9,0.5,2.8c0.3,0.9,0.8,1.6,1.4,2.2c0.6,0.6,1.4,1.1,2.2,1.5C39,88,39.9,88.2,40.9,88.2z M64,70.3
|
||||
c1.6,0,3.1,0.3,4.4,0.9s2.5,1.4,3.5,2.4c1,1,1.7,2.1,2.2,3.5c0.5,1.3,0.8,2.7,0.8,4.2c0,1.5-0.3,2.9-0.8,4.2
|
||||
c-0.5,1.3-1.3,2.5-2.2,3.5c-1,1-2.1,1.8-3.5,2.4s-2.8,0.9-4.4,0.9c-1.6,0-3.1-0.3-4.5-0.9s-2.5-1.4-3.5-2.4c-1-1-1.7-2.1-2.2-3.5
|
||||
c-0.5-1.3-0.8-2.7-0.8-4.2c0-1.5,0.3-2.9,0.8-4.2c0.5-1.3,1.3-2.5,2.2-3.5c1-1,2.1-1.8,3.5-2.4S62.4,70.3,64,70.3z M64,88.2
|
||||
c1,0,1.9-0.2,2.7-0.5c0.8-0.4,1.5-0.9,2.1-1.5c0.6-0.6,1-1.4,1.4-2.2s0.5-1.8,0.5-2.8s-0.2-1.9-0.5-2.8c-0.3-0.9-0.8-1.6-1.4-2.2
|
||||
c-0.6-0.6-1.3-1.1-2.1-1.5c-0.8-0.4-1.7-0.5-2.7-0.5c-1,0-1.9,0.2-2.7,0.5c-0.8,0.4-1.5,0.9-2.1,1.5c-0.6,0.6-1,1.4-1.4,2.2
|
||||
c-0.3,0.9-0.5,1.8-0.5,2.8s0.2,1.9,0.5,2.8c0.3,0.9,0.8,1.6,1.4,2.2c0.6,0.6,1.3,1.1,2.1,1.5C62.1,88,63,88.2,64,88.2z M91.1,83.8
|
||||
V70.9h4.2v12.6c0,2.7-0.8,4.8-2.3,6.4c-1.5,1.5-3.5,2.3-6.1,2.3c-2.6,0-4.6-0.8-6.1-2.3s-2.2-3.7-2.2-6.4V70.9h4.2v12.9
|
||||
c0,1.4,0.4,2.5,1.1,3.2c0.7,0.8,1.8,1.2,3.1,1.2c1.3,0,2.3-0.4,3-1.2C90.8,86.3,91.1,85.2,91.1,83.8z M97.9,70.9h4.5l6.1,16.1
|
||||
l6.1-16.1h4.5l-7.8,20.7h-5.5L97.9,70.9z M122.2,91.5V70.9h12v3.6h-7.8v4.8h6.7v3.6h-6.7V88h7.8v3.6H122.2z M139.1,91.5V70.9h6.3
|
||||
c2.3,0,4.1,0.6,5.4,1.7c1.3,1.1,2,2.6,2,4.5c0,1.2-0.3,2.3-0.9,3.2c-0.6,0.9-1.4,1.6-2.4,2.1l6.5,9.1h-5l-5.5-8.3h-2.2v8.3H139.1z
|
||||
M145.7,74.4h-2.4v5.2h2.4c0.9,0,1.6-0.2,2.1-0.7c0.5-0.5,0.7-1.1,0.7-1.9c0-0.8-0.2-1.4-0.7-1.9C147.2,74.7,146.6,74.4,145.7,74.4
|
||||
z M158.6,91.5V70.9h5.4l9.2,14.8V70.9h4.2v20.7H172l-9.2-14.8v14.8H158.6z M183.1,91.5V70.9h12v3.6h-7.8v4.8h6.7v3.6h-6.7V88h7.8
|
||||
v3.6H183.1z M200,91.5V70.9h5.3l5,8.5l5-8.5h5.3v20.7h-4.2V76.8l-4.6,7.6h-3l-4.6-7.6v14.7H200z M226.3,91.5V70.9h12v3.6h-7.8v4.8
|
||||
h6.7v3.6h-6.7V88h7.8v3.6H226.3z M243.2,91.5V70.9h5.4l9.2,14.8V70.9h4.2v20.7h-5.4l-9.2-14.8v14.8H243.2z M265.7,74.7v-3.8h16.9
|
||||
v3.8h-6.4v16.8H272V74.7H265.7z"
|
||||
/>
|
||||
<g id="Marianne">
|
||||
<path
|
||||
id="Fond_2_"
|
||||
class="st0"
|
||||
d="M63.6,53.4c0.3-0.3,0.6-0.6,0.9-1h0c0.6-0.6,1.1-1.3,1.8-1.8c0.2-0.2,0.4-0.3,0.6-0.5
|
||||
c0.1-0.1,0.1-0.2,0.1-0.2c-0.3,0.1-0.4,0.3-0.7,0.4c-0.1,0-0.1-0.1-0.1-0.1c0.2-0.1,0.4-0.3,0.6-0.4c0,0,0,0,0,0
|
||||
c-0.1,0-0.1-0.1-0.1-0.1c-0.7-0.1-1.2,0.4-1.7,0.8c-0.1,0.1-0.2-0.1-0.3-0.1c-0.8,0.3-1.4,1-2.2,1.3v-0.1c-0.3,0.1-0.6,0.3-1,0.4
|
||||
c-0.5,0.1-0.9,0.1-1.3,0.1c-0.6,0.1-1.3,0.2-1.9,0.3c0,0,0,0-0.1,0c-0.3,0.1-0.7,0.2-1,0.4c0,0,0,0,0,0c0,0-0.1,0.1-0.1,0.1
|
||||
c-0.1,0.1-0.2,0.2-0.4,0.3c-0.3,0.2-0.6,0.5-0.9,0.7c0,0-0.1,0-0.1,0c-0.3,0.3-0.6,0.6-0.9,0.8c0,0-0.1,0-0.2,0c0,0,0,0,0,0
|
||||
c0,0,0,0,0-0.1c0-0.1,0.1-0.2,0.1-0.2c0.1-0.1,0.1-0.2,0.2-0.2c0.1-0.1,0.1-0.2,0.2-0.3c0,0,0-0.1,0-0.1c0,0,0,0-0.1,0
|
||||
c0.3-0.3,0.6-0.5,0.9-0.7v0c0,0-0.1,0-0.1-0.1c0,0,0.1-0.1,0.1-0.1c0,0,0,0,0,0c0,0,0,0,0,0c-0.1,0.1-0.2,0.1-0.3,0.2
|
||||
c-0.1,0.1-0.2,0.4-0.4,0.3c0,0-0.1,0-0.1,0c0,0,0,0-0.1,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0
|
||||
c0,0,0,0,0,0c0,0,0,0,0-0.1c0,0,0,0,0,0c0,0,0-0.1,0.1-0.1c0,0,0,0,0-0.1c0,0,0-0.1,0.1-0.1c0,0,0-0.1,0-0.1
|
||||
c0.1-0.1,0.2-0.2,0.4-0.3h0c0.2-0.1,0.4-0.2,0.6-0.3c0,0,0.1-0.1,0.1-0.1c-0.3,0.1-0.6,0.2-0.9,0.4c0,0-0.1,0-0.1,0
|
||||
c0,0-0.1,0-0.1,0c0,0,0,0,0,0c0.1-0.1,0.2-0.2,0.3-0.3c0.1,0,0.1,0,0.1,0.1c1.7-1.3,4.1-1,6.1-1.7c0.2-0.1,0.3-0.2,0.5-0.3
|
||||
c0.3-0.1,0.5-0.4,0.8-0.5c0.4-0.3,0.7-0.7,0.9-1.2c0-0.1-0.1-0.1-0.1-0.1c-0.7,0.8-1.5,1.3-2.4,1.8c-1.1,0.6-2.4,0.5-3.5,0.6
|
||||
c0.1-0.1,0.2-0.1,0.3-0.1c0-0.2,0.1-0.2,0.2-0.3H59c0.1,0,0.1-0.1,0.1-0.1c0.1,0,0.3-0.1,0.2-0.1c-0.2-0.2-0.5,0.2-0.8,0
|
||||
c0.1-0.1,0.1-0.3,0.2-0.3h0.2c0-0.1,0.1-0.2,0.1-0.2c0.8-0.5,1.6-0.9,2.3-1.3c-0.2,0-0.3,0.2-0.4,0.1c0.1,0,0-0.2,0.1-0.2
|
||||
c0.6-0.2,1.1-0.5,1.7-0.7c-0.2,0-0.4,0.2-0.6,0c0.1-0.1,0.2-0.2,0.3-0.2v-0.2c0-0.1,0.1-0.1,0.1-0.1c-0.1,0-0.1-0.1-0.1-0.1
|
||||
c0.1-0.1,0.2-0.1,0.3-0.2c-0.1,0-0.2,0-0.2-0.1c0.2-0.2,0.4-0.3,0.7-0.3c-0.1-0.1-0.2,0-0.2-0.1c0-0.1,0.1-0.1,0.1-0.1H63
|
||||
c-0.1-0.1-0.1-0.2-0.1-0.2c0.3-0.4,0.3-0.9,0.5-1.3c-0.1,0-0.1,0-0.1-0.1c-0.5,0.6-1.4,0.8-2.2,1h-0.3c-0.3,0.1-0.6,0.1-0.9-0.1
|
||||
c-0.2-0.1-0.3-0.3-0.5-0.4c-0.4-0.3-0.9-0.5-1.3-0.6c-1.3-0.4-2.7-0.6-4.1-0.6c0.6-0.3,1.2-0.3,1.9-0.5c0.9-0.3,1.8-0.6,2.7-0.5
|
||||
c-0.2-0.1-0.4,0-0.5,0c-0.8-0.1-1.5,0.2-2.3,0.3c-0.5,0.1-1,0.3-1.6,0.4c-0.3,0.1-0.5,0.4-0.9,0.4v-0.2c0.5-0.6,1.2-1.3,2-1.3
|
||||
c1-0.2,1.9,0,2.8,0.1c0.7,0.1,1.3,0.2,2,0.4c0.3,0,0.3,0.4,0.5,0.5c0.3,0.1,0.6,0,1,0.2c0-0.1-0.1-0.2,0-0.3
|
||||
c0.2-0.2,0.5,0.1,0.7-0.1c0.4-0.3-0.4-0.7-0.6-1.1c0-0.1,0.1-0.1,0.1-0.1c0.4,0.4,0.7,0.8,1.3,1.1c0.3,0.1,0.9,0.3,0.8-0.1
|
||||
c-0.3-0.6-0.8-1.1-1.2-1.6v-0.2c-0.1,0-0.1-0.1-0.2-0.1v-0.2c-0.2-0.1-0.2-0.3-0.3-0.5c-0.2-0.3-0.1-0.6-0.2-1
|
||||
C62.1,39.3,62,39,62,38.7c-0.2-0.9-0.4-1.7-0.5-2.6c-0.1-1,0.6-1.8,1.1-2.7c0.4-0.6,0.8-1.3,1.5-1.7c0.2-0.6,0.6-1.2,1-1.7
|
||||
c0.4-0.5,1.1-0.8,1.7-1.1c0.7-0.3,1.4-0.5,1.4-0.5l10.7,0c0,0,0.1,0,0.3,0.1c0.2,0.1,0.6,0.3,0.8,0.4c0.4,0.2,0.8,0.5,1,0.9
|
||||
c0.1,0.2,0.3,0.5,0.2,0.7c-0.1,0.3-0.2,0.7-0.4,0.8c-0.3,0.2-0.7,0.2-1.1,0.1c-0.2,0-0.4-0.1-0.6-0.1c0.8,0.3,1.6,0.7,2.1,1.4
|
||||
c0.1,0.1,0.3,0.2,0.5,0.2c0.1,0,0.1,0.1,0.1,0.2c-0.1,0.1-0.2,0.2-0.2,0.3h0.2c0.3-0.1,0.2-0.6,0.6-0.5c0.3,0.2,0.4,0.5,0.2,0.8
|
||||
c-0.2,0.2-0.4,0.4-0.6,0.5c-0.1,0.1-0.1,0.3,0,0.4c0.2,0.2,0.2,0.4,0.3,0.6c0.2,0.4,0.2,0.8,0.4,1.2c0.2,0.8,0.4,1.6,0.4,2.4
|
||||
c0,0.4-0.2,0.8-0.1,1.2c0.1,0.4,0.4,0.8,0.6,1.1c0.2,0.3,0.4,0.5,0.6,0.9c0.3,0.5,0.9,1.1,0.6,1.7c-0.2,0.4-0.8,0.3-1.1,0.5
|
||||
c-0.3,0.3-0.1,0.7,0.1,1c0.3,0.5-0.3,0.8-0.7,1c0.1,0.2,0.3,0.1,0.4,0.2c0.1,0.3,0.3,0.4,0.2,0.7c-0.2,0.3-0.9,0.5-0.5,1
|
||||
c0.2,0.4,0.1,0.8-0.1,1.2c-0.2,0.5-0.6,0.7-1,0.8c-0.3,0.1-0.7,0.1-1,0.1c-0.1-0.1-0.2-0.1-0.3-0.1c-0.9-0.1-1.8-0.4-2.7-0.4
|
||||
c-0.3,0.1-0.5,0.1-0.7,0.2c-0.2,0.2-0.5,0.4-0.6,0.6c0,0,0,0,0,0c0,0-0.1,0.1-0.1,0.1c0,0,0,0.1-0.1,0.1c0,0,0,0,0,0.1
|
||||
c-0.2,0.2-0.3,0.4-0.4,0.6c0,0,0,0,0,0c0,0,0,0.1,0,0.1c-0.2,0.3-0.3,0.7-0.4,1c-0.4,1.2-0.2,2.3,0.1,2.5c0.1,0.1,1.8,0.6,2.9,1.1
|
||||
c0.6,0.2,0.9,0.4,1.3,0.6l-22,0c1-0.7,2-1.1,3.5-1.8C61.6,54.6,63.1,53.9,63.6,53.4 M55.4,49.5c-0.1,0-0.3,0.1-0.3-0.1
|
||||
c0.1-0.3,0.4-0.3,0.6-0.4c0.1-0.1,0.3-0.2,0.4-0.1c0.1,0.2,0.3,0.1,0.4,0.2C56.2,49.5,55.8,49.4,55.4,49.5 M47.2,48.3
|
||||
c0,0-0.1-0.1-0.1-0.1c0.7-0.9,1.2-1.8,1.7-2.7c0.7-0.4,1.3-0.9,1.8-1.5c0.9-1,1.9-1.8,3-2.4c0.4-0.2,1-0.1,1.4,0.1
|
||||
c-0.2,0.2-0.4,0.2-0.6,0.3c-0.1,0-0.1,0-0.2-0.1c0.1-0.1,0.1-0.1,0.1-0.2c-0.5,0.6-1.3,0.9-1.7,1.6c-0.3,0.5-0.5,1.2-1.2,1.4
|
||||
c-0.2,0.1,0.1-0.2-0.1-0.1C49.6,45.7,48.5,46.9,47.2,48.3 M51.6,44.8c-0.1,0.1-0.1,0.1-0.2,0.2c-0.1,0.1-0.1,0.2-0.2,0.2
|
||||
c-0.1,0-0.1,0-0.1-0.1c0.1-0.2,0.2-0.4,0.4-0.5C51.6,44.7,51.6,44.8,51.6,44.8 M54.1,52.8c0,0.1-0.1,0.1-0.1,0.2
|
||||
c0.1,0,0.1,0,0.1,0.1c-0.1,0.1-0.2,0.2-0.4,0.3c0,0,0,0-0.1,0c-0.1,0.1-0.1,0.1-0.2,0.2c-0.1,0.1-0.4,0-0.3-0.1
|
||||
c0.1-0.1,0.3-0.3,0.4-0.4c0.1-0.1,0.2-0.1,0.2-0.2c0,0,0.1-0.1,0.1-0.1C53.9,52.8,54.2,52.7,54.1,52.8 M53.2,52.4
|
||||
C53.2,52.4,53.1,52.4,53.2,52.4c-0.2,0.2-0.4,0.3-0.6,0.4c-0.2,0.1-0.5,0.2-0.7,0.3c0,0,0,0,0,0c0,0-0.1,0-0.1,0
|
||||
c-0.2,0.1-0.4,0.3-0.5,0.4c0,0,0,0-0.1,0.1c0,0,0,0,0,0c0,0,0,0,0,0c0,0-0.1,0.1-0.1,0.1c0,0,0,0,0,0c0,0,0,0,0,0
|
||||
c0,0-0.1,0.1-0.1,0.1c0,0,0,0.1-0.1,0.1c0,0-0.1,0-0.1,0c0,0,0,0,0,0c0,0-0.1,0-0.1,0c0,0-0.1,0-0.1,0c0,0,0,0,0,0c0,0,0,0,0,0
|
||||
c-0.1,0.1-0.1,0.1-0.2,0.2c-0.1,0.1-0.2,0.2-0.3,0.3c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0.1
|
||||
l0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0-0.1,0-0.1l0,0c0,0,0,0,0,0c0.1-0.1,0.1-0.1,0.2-0.2c0,0,0,0,0,0c0,0,0,0,0.1-0.1
|
||||
c0,0,0.1-0.1,0.1-0.1c0,0,0,0,0,0c0.1-0.1,0.1-0.2,0.2-0.2c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0.1-0.1,0.1-0.1c0,0,0-0.1,0.1-0.1
|
||||
c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0-0.1,0.1-0.1c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0-0.1c0,0,0,0,0,0c0,0,0-0.1,0-0.1
|
||||
c0,0,0,0,0,0c0.1-0.1,0.1-0.2,0.2-0.3c0,0,0,0,0,0c-0.1,0-0.1,0.1-0.2,0.2c-0.1,0-0.2,0-0.1-0.1c0,0,0.1-0.1,0.1-0.1c0,0,0,0,0,0
|
||||
c0.1-0.1,0.2-0.2,0.3-0.3c0.1,0,0.1-0.1,0.2-0.1c0,0,0,0,0,0c0,0,0.1-0.1,0.1-0.1c0,0,0,0,0,0c0.5-0.5,1.4-0.5,2.1-0.8
|
||||
c0.3-0.1,0.6,0.1,0.9,0c0.2,0,0.3,0,0.5,0.1C54,51.8,53.6,52.1,53.2,52.4 M54.3,48.7c-0.1-0.1,0.2,0,0.2-0.1H54
|
||||
c-0.1,0-0.1-0.1-0.1-0.1c-0.3,0.1-0.6,0.2-0.9,0.2c-0.4,0.1-0.7,0.4-1.1,0.5c-0.6,0.2-1.1,0.7-1.7,0.9c-0.1,0-0.1-0.1-0.1-0.1
|
||||
c0.1-0.2,0.3-0.2,0.4-0.4c0-0.1,0-0.1-0.1-0.1c0.4-0.6,1-0.9,1.6-1.4v-0.2c0.2-0.2,0.4-0.3,0.5-0.6c0.1-0.2,0.3-0.4,0.5-0.5
|
||||
c-0.1-0.1-0.2-0.1-0.2-0.2c-0.2,0-0.4,0.1-0.6-0.1c0.1-0.1,0.2-0.2,0.3-0.2c0,0-0.1,0-0.1-0.1c-0.1-0.1,0.1-0.2,0.3-0.3
|
||||
c0.2-0.1,0.5-0.1,0.6-0.2c-0.4-0.1-0.8,0.1-1.2-0.1c0.3-0.7,0.7-1.3,1.3-1.6c0.1,0,0.2,0,0.2,0.1c0,0.3-0.2,0.5-0.4,0.5
|
||||
c0.4,0.1,0.9,0.1,1.3,0.3c-0.1,0.1-0.2,0.1-0.2,0.1c0.3,0.2,0.6,0.1,0.9,0.3c-0.2,0.2-0.3,0-0.5,0c1.7,0.5,3.4,0.9,4.8,1.9
|
||||
c-1.2,0.6-2.4,0.9-3.7,1.1c-0.2,0-0.3,0-0.4-0.1c0,0.1,0,0.2-0.1,0.2c-0.2,0-0.4,0-0.5,0.1C54.7,48.8,54.4,48.8,54.3,48.7"
|
||||
/>
|
||||
<path
|
||||
id="Rouge_1_"
|
||||
class="st1"
|
||||
d="M63.6,53.4c0.3-0.3,0.6-0.6,0.9-1h0c0.6-0.6,1.1-1.3,1.8-1.8c0.2-0.2,0.4-0.3,0.6-0.5
|
||||
c0.1-0.1,0.1-0.2,0.1-0.2c-0.3,0.1-0.4,0.3-0.7,0.4c-0.1,0-0.1-0.1-0.1-0.1c0.2-0.1,0.4-0.3,0.6-0.4c0,0,0,0,0,0
|
||||
c-0.1,0-0.1-0.1-0.1-0.1c-0.7-0.1-1.2,0.4-1.7,0.8c-0.1,0.1-0.2-0.1-0.3-0.1c-0.8,0.3-1.4,1-2.2,1.3v-0.1c-0.3,0.1-0.6,0.3-1,0.4
|
||||
c-0.5,0.1-0.9,0.1-1.3,0.1c-0.6,0.1-1.3,0.2-1.9,0.3c0,0,0,0-0.1,0c-0.3,0.1-0.7,0.2-1,0.4c0,0,0,0,0,0c0,0-0.1,0.1-0.1,0.1
|
||||
c-0.1,0.1-0.2,0.2-0.4,0.3c-0.3,0.2-0.6,0.5-0.9,0.7c0,0-0.1,0-0.1,0c-0.3,0.3-0.6,0.6-0.9,0.8c0,0-0.1,0-0.2,0c0,0,0,0,0,0
|
||||
c0,0,0,0,0-0.1c0-0.1,0.1-0.2,0.1-0.2c0.1-0.1,0.1-0.2,0.2-0.2c0.1-0.1,0.1-0.2,0.2-0.3c0,0,0-0.1,0-0.1c0,0,0,0-0.1,0
|
||||
c0.3-0.3,0.6-0.5,0.9-0.7v0c0,0-0.1,0-0.1-0.1c0,0,0.1-0.1,0.1-0.1c0,0,0,0,0,0c0,0,0,0,0,0c-0.1,0.1-0.2,0.1-0.3,0.2
|
||||
c-0.1,0.1-0.2,0.4-0.4,0.3c0,0-0.1,0-0.1,0c0,0,0,0-0.1,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0
|
||||
c0,0,0,0,0,0c0,0,0,0,0-0.1c0,0,0,0,0,0c0,0,0-0.1,0.1-0.1c0,0,0,0,0-0.1c0,0,0-0.1,0.1-0.1c0,0,0-0.1,0-0.1
|
||||
c0.1-0.1,0.2-0.2,0.4-0.3h0c0.2-0.1,0.4-0.2,0.6-0.3c0,0,0.1-0.1,0.1-0.1c-0.3,0.1-0.6,0.2-0.9,0.4c0,0-0.1,0-0.1,0
|
||||
c0,0-0.1,0-0.1,0c0,0,0,0,0,0c0.1-0.1,0.2-0.2,0.3-0.3c0.1,0,0.1,0,0.1,0.1c1.7-1.3,4.1-1,6.1-1.7c0.2-0.1,0.3-0.2,0.5-0.3
|
||||
c0.3-0.1,0.5-0.4,0.8-0.5c0.4-0.3,0.7-0.7,0.9-1.2c0-0.1-0.1-0.1-0.1-0.1c-0.7,0.8-1.5,1.3-2.4,1.8c-1.1,0.6-2.4,0.5-3.5,0.6
|
||||
c0.1-0.1,0.2-0.1,0.3-0.1c0-0.2,0.1-0.2,0.2-0.3H59c0.1,0,0.1-0.1,0.1-0.1c0.1,0,0.3-0.1,0.2-0.1c-0.2-0.2-0.5,0.2-0.8,0
|
||||
c0.1-0.1,0.1-0.3,0.2-0.3h0.2c0-0.1,0.1-0.2,0.1-0.2c0.8-0.5,1.6-0.9,2.3-1.3c-0.2,0-0.3,0.2-0.4,0.1c0.1,0,0-0.2,0.1-0.2
|
||||
c0.6-0.2,1.1-0.5,1.7-0.7c-0.2,0-0.4,0.2-0.6,0c0.1-0.1,0.2-0.2,0.3-0.2v-0.2c0-0.1,0.1-0.1,0.1-0.1c-0.1,0-0.1-0.1-0.1-0.1
|
||||
c0.1-0.1,0.2-0.1,0.3-0.2c-0.1,0-0.2,0-0.2-0.1c0.2-0.2,0.4-0.3,0.7-0.3c-0.1-0.1-0.2,0-0.2-0.1c0-0.1,0.1-0.1,0.1-0.1H63
|
||||
c-0.1-0.1-0.1-0.2-0.1-0.2c0.3-0.4,0.3-0.9,0.5-1.3c-0.1,0-0.1,0-0.1-0.1c-0.5,0.6-1.4,0.8-2.2,1h-0.3c-0.3,0.1-0.6,0.1-0.9-0.1
|
||||
c-0.2-0.1-0.3-0.3-0.5-0.4c-0.4-0.3-0.9-0.5-1.3-0.6c-1.3-0.4-2.7-0.6-4.1-0.6c0.6-0.3,1.2-0.3,1.9-0.5c0.9-0.3,1.8-0.6,2.7-0.5
|
||||
c-0.2-0.1-0.4,0-0.5,0c-0.8-0.1-1.5,0.2-2.3,0.3c-0.5,0.1-1,0.3-1.6,0.4c-0.3,0.1-0.5,0.4-0.9,0.4V44c0.5-0.6,1.2-1.3,2-1.3
|
||||
c1-0.2,1.9,0,2.8,0.1c0.7,0.1,1.3,0.2,2,0.4c0.3,0,0.3,0.4,0.5,0.5c0.3,0.1,0.6,0,1,0.2c0-0.1-0.1-0.2,0-0.3
|
||||
c0.2-0.2,0.5,0.1,0.7-0.1c0.4-0.3-0.4-0.7-0.6-1.1c0-0.1,0.1-0.1,0.1-0.1c0.4,0.4,0.7,0.8,1.3,1.1c0.3,0.1,0.9,0.3,0.8-0.1
|
||||
c-0.3-0.6-0.8-1.1-1.2-1.6v-0.2c-0.1,0-0.1-0.1-0.2-0.1v-0.2c-0.2-0.1-0.2-0.3-0.3-0.5c-0.2-0.3-0.1-0.6-0.2-1
|
||||
C62.1,39.3,62,39,62,38.7c-0.2-0.9-0.4-1.7-0.5-2.6c-0.1-1,0.6-1.8,1.1-2.7c0.4-0.6,0.8-1.3,1.5-1.7c0.2-0.6,0.6-1.2,1-1.7
|
||||
c0.4-0.5,1.1-0.8,1.7-1.1c0.7-0.3,1.4-0.5,1.4-0.5H31.4v28.3h26c1-0.7,2-1.1,3.5-1.8C61.6,54.6,63.1,53.9,63.6,53.4 M55.4,49.5
|
||||
c-0.1,0-0.3,0.1-0.3-0.1c0.1-0.3,0.4-0.3,0.6-0.4c0.1-0.1,0.3-0.2,0.4-0.1c0.1,0.2,0.3,0.1,0.4,0.2C56.2,49.5,55.8,49.4,55.4,49.5
|
||||
M47.2,48.3c0,0-0.1-0.1-0.1-0.1c0.7-0.9,1.2-1.8,1.7-2.7c0.7-0.4,1.3-0.9,1.8-1.5c0.9-1,1.9-1.8,3-2.4c0.4-0.2,1-0.1,1.4,0.1
|
||||
c-0.2,0.2-0.4,0.2-0.6,0.3c-0.1,0-0.1,0-0.2-0.1c0.1-0.1,0.1-0.1,0.1-0.2c-0.5,0.6-1.3,0.9-1.7,1.6c-0.3,0.5-0.5,1.2-1.2,1.4
|
||||
c-0.2,0.1,0.1-0.2-0.1-0.1C49.7,45.7,48.5,46.9,47.2,48.3 M51.6,44.8c-0.1,0.1-0.1,0.1-0.2,0.2c-0.1,0.1-0.1,0.2-0.2,0.2
|
||||
c-0.1,0-0.1,0-0.1-0.1c0.1-0.2,0.2-0.4,0.4-0.5C51.6,44.7,51.6,44.8,51.6,44.8 M54.1,52.8c0,0.1-0.1,0.1-0.1,0.2
|
||||
c0.1,0,0.1,0,0.1,0.1c-0.1,0.1-0.2,0.2-0.4,0.3c0,0,0,0-0.1,0c-0.1,0.1-0.1,0.1-0.2,0.2c-0.1,0.1-0.4,0-0.3-0.1
|
||||
c0.1-0.1,0.3-0.3,0.4-0.4c0.1-0.1,0.2-0.1,0.2-0.2c0,0,0.1-0.1,0.1-0.1C53.9,52.8,54.2,52.7,54.1,52.8 M53.2,52.4
|
||||
C53.2,52.4,53.2,52.4,53.2,52.4c-0.2,0.2-0.4,0.3-0.6,0.4c-0.2,0.1-0.5,0.2-0.7,0.3c0,0,0,0,0,0c0,0-0.1,0-0.1,0
|
||||
c-0.2,0.1-0.4,0.3-0.5,0.4c0,0,0,0-0.1,0.1c0,0,0,0,0,0c0,0,0,0,0,0c0,0-0.1,0.1-0.1,0.1c0,0,0,0,0,0c0,0,0,0,0,0
|
||||
c0,0-0.1,0.1-0.1,0.1c0,0,0,0.1-0.1,0.1c0,0-0.1,0-0.1,0c0,0,0,0,0,0c0,0-0.1,0-0.1,0c0,0-0.1,0-0.1,0c0,0,0,0,0,0c0,0,0,0,0,0
|
||||
c-0.1,0.1-0.1,0.1-0.2,0.2c-0.1,0.1-0.2,0.2-0.3,0.3c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0.1
|
||||
l0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0-0.1,0-0.1l0,0c0,0,0,0,0,0c0.1-0.1,0.1-0.1,0.2-0.2c0,0,0,0,0,0c0,0,0,0,0.1-0.1
|
||||
c0,0,0.1-0.1,0.1-0.1c0,0,0,0,0,0c0.1-0.1,0.1-0.2,0.2-0.2c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0.1-0.1,0.1-0.1c0,0,0-0.1,0.1-0.1
|
||||
c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0-0.1,0.1-0.1c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0-0.1c0,0,0,0,0,0c0,0,0-0.1,0-0.1
|
||||
c0,0,0,0,0,0c0.1-0.1,0.1-0.2,0.2-0.3c0,0,0,0,0,0c-0.1,0-0.1,0.1-0.2,0.2c-0.1,0-0.2,0-0.1-0.1c0,0,0.1-0.1,0.1-0.1c0,0,0,0,0,0
|
||||
c0.1-0.1,0.2-0.2,0.3-0.3c0.1,0,0.1-0.1,0.2-0.1c0,0,0,0,0,0c0,0,0.1-0.1,0.1-0.1c0,0,0,0,0,0c0.5-0.5,1.4-0.5,2.1-0.8
|
||||
c0.3-0.1,0.6,0.1,0.9,0c0.2,0,0.3,0,0.5,0.1C54,51.8,53.6,52.1,53.2,52.4 M54.3,48.7c-0.1-0.1,0.2,0,0.2-0.1H54
|
||||
c-0.1,0-0.1-0.1-0.1-0.1c-0.3,0.1-0.6,0.2-0.9,0.2c-0.4,0.1-0.7,0.4-1.1,0.5c-0.6,0.2-1.1,0.7-1.7,0.9c-0.1,0-0.1-0.1-0.1-0.1
|
||||
c0.1-0.2,0.3-0.2,0.4-0.4c0-0.1,0-0.1-0.1-0.1c0.4-0.6,1-0.9,1.6-1.4v-0.2c0.2-0.2,0.4-0.3,0.5-0.6c0.1-0.2,0.3-0.4,0.5-0.5
|
||||
c-0.1-0.1-0.2-0.1-0.2-0.2c-0.2,0-0.4,0.1-0.6-0.1c0.1-0.1,0.2-0.2,0.3-0.2c0,0-0.1,0-0.1-0.1c-0.1-0.1,0.1-0.2,0.3-0.3
|
||||
c0.2-0.1,0.5-0.1,0.6-0.2c-0.4-0.1-0.8,0.1-1.2-0.1c0.3-0.7,0.7-1.3,1.3-1.6c0.1,0,0.2,0,0.2,0.1c0,0.3-0.2,0.5-0.4,0.5
|
||||
c0.4,0.1,0.9,0.1,1.3,0.3c-0.1,0.1-0.2,0.1-0.2,0.1c0.3,0.2,0.6,0.1,0.9,0.3c-0.2,0.2-0.3,0-0.5,0c1.7,0.5,3.4,0.9,4.8,1.9
|
||||
c-1.2,0.6-2.4,0.9-3.7,1.1c-0.2,0-0.3,0-0.4-0.1c0,0.1,0,0.2-0.1,0.2c-0.2,0-0.4,0-0.5,0.1C54.7,48.8,54.4,48.8,54.3,48.7"
|
||||
/>
|
||||
<path
|
||||
id="Bleu_1_"
|
||||
class="st2"
|
||||
d="M109.3,28.4H78.9c0,0,0.1,0,0.3,0.1c0.2,0.1,0.6,0.3,0.8,0.4c0.4,0.2,0.8,0.5,1,0.9
|
||||
c0.1,0.2,0.3,0.5,0.2,0.7c-0.1,0.3-0.2,0.7-0.4,0.8c-0.3,0.2-0.7,0.2-1.1,0.1c-0.2,0-0.4-0.1-0.6-0.1c0.8,0.3,1.6,0.7,2.1,1.4
|
||||
c0.1,0.1,0.3,0.2,0.5,0.2c0.1,0,0.1,0.1,0.1,0.2c-0.1,0.1-0.2,0.2-0.2,0.3h0.2c0.3-0.1,0.2-0.6,0.6-0.5c0.3,0.2,0.4,0.5,0.2,0.8
|
||||
c-0.2,0.2-0.4,0.4-0.6,0.5c-0.1,0.1-0.1,0.3,0,0.4c0.2,0.2,0.2,0.4,0.3,0.6c0.2,0.4,0.2,0.8,0.4,1.2c0.2,0.8,0.4,1.6,0.4,2.4
|
||||
c0,0.4-0.2,0.8-0.1,1.2c0.1,0.4,0.4,0.8,0.6,1.1c0.2,0.3,0.4,0.5,0.6,0.9c0.3,0.5,0.9,1.1,0.6,1.7c-0.2,0.4-0.8,0.3-1.1,0.5
|
||||
c-0.3,0.3-0.1,0.7,0.1,1c0.3,0.5-0.3,0.8-0.7,1c0.1,0.2,0.3,0.1,0.4,0.2c0.1,0.3,0.3,0.4,0.2,0.7c-0.2,0.3-0.9,0.5-0.5,1
|
||||
c0.2,0.4,0.1,0.8-0.1,1.2c-0.2,0.5-0.6,0.7-1,0.8c-0.3,0.1-0.7,0.1-1,0.1c-0.1-0.1-0.2-0.1-0.3-0.1c-0.9-0.1-1.8-0.4-2.7-0.4
|
||||
c-0.3,0.1-0.5,0.1-0.7,0.2c-0.2,0.2-0.5,0.4-0.6,0.6c0,0,0,0,0,0c0,0-0.1,0.1-0.1,0.1c0,0,0,0.1-0.1,0.1c0,0,0,0,0,0.1
|
||||
c-0.2,0.2-0.3,0.4-0.4,0.6c0,0,0,0,0,0c0,0,0,0.1,0,0.1c-0.2,0.3-0.3,0.7-0.4,1c-0.4,1.2-0.2,2.3,0.1,2.5c0.1,0.1,1.8,0.6,2.9,1.1
|
||||
c0.6,0.2,0.9,0.4,1.3,0.6h29.9V28.4z"
|
||||
/>
|
||||
<path
|
||||
id="Yeux_1_"
|
||||
class="st3"
|
||||
d="M80.7,38.7c0.2,0.1,0.5,0.1,0.5,0.2c-0.1,0.4-0.7,0.5-1.1,1H80c-0.2,0.1-0.1,0.4-0.3,0.4
|
||||
c-0.2-0.1-0.3,0-0.5,0.1c0.2,0.2,0.5,0.4,0.8,0.3c0.1,0,0.2,0.1,0.2,0.2c0,0,0.1,0,0.1-0.1c0.1,0,0.1,0,0.1,0.1V41
|
||||
c-0.2,0.2-0.4,0.1-0.6,0.2c0.4,0.1,0.9,0.1,1.2,0c0.3-0.1,0-0.6,0.2-0.9c-0.1,0,0-0.2-0.1-0.2c0.1-0.1,0.2-0.3,0.3-0.3
|
||||
c0.1,0,0.3-0.1,0.3-0.2c0-0.1-0.2-0.2-0.2-0.3c0.3-0.2,0.6-0.5,0.5-0.9c-0.1-0.2-0.5-0.2-0.8-0.3c-0.3-0.1-0.6,0-0.9,0.1
|
||||
c-0.3,0-0.5,0.2-0.8,0.2c-0.4,0.1-0.7,0.3-1,0.5c0.4-0.2,0.8-0.2,1.2-0.3C80.1,38.7,80.3,38.6,80.7,38.7"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 23 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM11.751 7.14453C11.7159 7.14453 11.6808 7.15341 11.6504 7.1709L8.87988 8.7666C8.77836 8.82519 8.74931 8.95855 8.81641 9.05469L9.5166 10.0576C9.57558 10.1417 9.68874 10.1675 9.77832 10.1172L11.3301 9.24512C11.4633 9.17057 11.6279 9.26713 11.6279 9.41992V16.6553C11.6279 16.7657 11.7177 16.8555 11.8281 16.8555H13.2988C13.4093 16.8555 13.499 16.7657 13.499 16.6553V7.34473C13.499 7.23427 13.4093 7.14453 13.2988 7.14453H11.751Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 708 B |
@@ -1,4 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.7373 8.7207C15.1389 8.72072 15.465 8.85912 15.7148 9.13574C15.9735 9.4034 16.1651 9.78292 16.29 10.2734C16.4238 10.764 16.4912 11.34 16.4912 12C16.4912 12.4995 16.4552 12.9511 16.3838 13.3525C16.3124 13.7447 16.2013 14.0886 16.0498 14.3828C15.9071 14.6682 15.7239 14.8922 15.501 15.0527C15.2869 15.2043 15.0317 15.2803 14.7373 15.2803C14.3449 15.2802 14.0147 15.1454 13.7471 14.8779C13.4885 14.6015 13.292 14.2178 13.1582 13.7275C13.0333 13.2368 12.9707 12.6602 12.9707 12C12.9707 11.5007 13.0068 11.0545 13.0781 10.6621C13.1495 10.2607 13.2567 9.91636 13.3994 9.63086C13.551 9.33673 13.7381 9.11355 13.9609 8.96191C14.184 8.80134 14.4429 8.72076 14.7373 8.7207Z" fill="#222631"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM14.7373 7.04785C14.1217 7.04789 13.5859 7.17302 13.1309 7.42285C12.685 7.67265 12.3101 8.02473 12.0068 8.47949C11.7124 8.92563 11.4896 9.45288 11.3379 10.0596C11.1952 10.6572 11.1231 11.3043 11.123 12C11.1231 12.9278 11.253 13.7672 11.5117 14.5166C11.7794 15.266 12.1768 15.8597 12.7031 16.2969C13.2385 16.7339 13.9166 16.9531 14.7373 16.9531C15.353 16.9531 15.8887 16.8279 16.3438 16.5781C16.7988 16.3283 17.1743 15.9802 17.4688 15.5342C17.763 15.0793 17.9813 14.5525 18.124 13.9551C18.2757 13.3484 18.3515 12.6959 18.3516 12C18.3515 11.0633 18.2225 10.2239 17.9639 9.4834C17.7051 8.73407 17.3077 8.14031 16.7725 7.70312C16.237 7.2659 15.5582 7.04786 14.7373 7.04785ZM8.05957 7.30176C8.02452 7.30176 7.98935 7.31063 7.95898 7.32812L5.29004 8.86621C5.18837 8.92479 5.15837 9.05812 5.22559 9.1543L5.89453 10.1104C5.95344 10.1946 6.06657 10.2212 6.15625 10.1709L7.64062 9.33789C7.77387 9.26312 7.93829 9.35899 7.93848 9.51172V16.4854C7.93863 16.5957 8.02831 16.6855 8.13867 16.6855H9.5459C9.65626 16.6855 9.74594 16.5957 9.74609 16.4854V7.50098C9.74594 7.39065 9.65626 7.30176 9.5459 7.30176H8.05957Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM9.14453 7.30859C9.10959 7.30859 9.07522 7.31757 9.04492 7.33496L6.375 8.87305C6.27335 8.93161 6.24338 9.06495 6.31055 9.16113L6.97949 10.1182C7.03838 10.2025 7.15152 10.229 7.24121 10.1787L8.72559 9.34473C8.85879 9.26992 9.02315 9.36588 9.02344 9.51855V16.4922C9.0236 16.6025 9.11327 16.6924 9.22363 16.6924H10.6309C10.7412 16.6924 10.8309 16.6025 10.8311 16.4922V7.50781C10.8309 7.39749 10.7412 7.30859 10.6309 7.30859H9.14453ZM14.4648 7.30859C14.4299 7.30859 14.3955 7.31756 14.3652 7.33496L11.6953 8.87305C11.594 8.93169 11.5648 9.06507 11.6318 9.16113L12.2998 10.1182C12.3587 10.2025 12.4718 10.229 12.5615 10.1787L14.0469 9.34473C14.1801 9.26994 14.3445 9.36585 14.3447 9.51855V16.4922C14.3449 16.6025 14.4346 16.6924 14.5449 16.6924H15.9512C16.0615 16.6924 16.1512 16.6025 16.1514 16.4922V7.50781C16.1512 7.39749 16.0615 7.30859 15.9512 7.30859H14.4648Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM8.02539 7.43555C7.99046 7.4356 7.95605 7.44447 7.92578 7.46191L5.25586 9C5.15434 9.05863 5.12521 9.19197 5.19238 9.28809L5.86035 10.2441C5.91917 10.3283 6.03246 10.3547 6.12207 10.3047L7.60742 9.47168C7.74063 9.39712 7.90509 9.49286 7.90527 9.64551V16.6191C7.90543 16.7293 7.99429 16.8191 8.10449 16.8193H9.5127C9.62295 16.8192 9.71273 16.7294 9.71289 16.6191V7.63477C9.71273 7.52452 9.62295 7.43567 9.5127 7.43555H8.02539ZM14.6924 7.18164C13.8714 7.18165 13.1661 7.3684 12.5771 7.74316C12.0492 8.0843 11.5948 8.53661 11.2148 9.09961C11.153 9.19172 11.1827 9.31604 11.2773 9.37402L12.4043 10.0605C12.4993 10.1183 12.6232 10.0875 12.6836 9.99414C12.9037 9.65412 13.1405 9.37686 13.3936 9.16211C13.697 8.91225 14.0724 8.78809 14.5186 8.78809C14.7682 8.78816 14.9875 8.83648 15.1748 8.93457C15.3708 9.0327 15.5229 9.17138 15.6299 9.34961C15.7457 9.52799 15.8037 9.74732 15.8037 10.0059C15.8037 10.202 15.7724 10.3853 15.71 10.5547C15.6564 10.7151 15.5757 10.8757 15.4688 11.0361C15.3706 11.1878 15.2454 11.3443 15.0938 11.5049C14.951 11.6655 14.7907 11.84 14.6123 12.0273L11.4717 15.127C11.4337 15.1645 11.4131 15.2161 11.4131 15.2695V16.6191C11.4132 16.7293 11.5021 16.8191 11.6123 16.8193H17.6777C17.788 16.8192 17.8778 16.7294 17.8779 16.6191V15.3857C17.8778 15.2755 17.788 15.1867 17.6777 15.1865H14.1045C13.9246 15.1865 13.8354 14.9667 13.9648 14.8418L15.8701 13.0039C16.1824 12.7005 16.4688 12.3886 16.7275 12.0674C16.9951 11.7374 17.2133 11.3891 17.3828 11.0234C17.5523 10.6578 17.6376 10.2601 17.6377 9.83203C17.6377 9.29683 17.5028 8.832 17.2354 8.43945C16.9767 8.03826 16.6244 7.72972 16.1787 7.51562C15.7415 7.29258 15.2455 7.1817 14.6924 7.18164Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM14.5801 7.04785C13.8216 7.04786 13.1741 7.20427 12.6387 7.5166C12.1678 7.79609 11.7676 8.16773 11.4395 8.63281C11.3754 8.72359 11.404 8.84816 11.498 8.90723L12.624 9.61426C12.7199 9.67443 12.8464 9.64211 12.9092 9.54785C13.0792 9.29246 13.2705 9.08809 13.4824 8.93457C13.7501 8.74725 14.0716 8.6543 14.4463 8.6543C14.6961 8.65431 14.9152 8.69886 15.1025 8.78809C15.2895 8.86836 15.437 8.99292 15.5439 9.16211C15.6597 9.32261 15.7177 9.52402 15.7178 9.76465C15.7177 10.1214 15.5888 10.408 15.3301 10.6221C15.0713 10.8272 14.7451 10.9297 14.3525 10.9297H13.1338C13.0234 10.9297 12.9338 11.0186 12.9336 11.1289V12.3359C12.9338 12.4463 13.0234 12.5361 13.1338 12.5361H14.4727C14.8116 12.5361 15.1019 12.594 15.3428 12.71C15.5925 12.817 15.7851 12.9774 15.9189 13.1914C16.0616 13.3966 16.1328 13.6471 16.1328 13.9414C16.1327 14.2178 16.0616 14.4637 15.9189 14.6777C15.7851 14.8918 15.5973 15.0569 15.3564 15.1729C15.1245 15.2888 14.8522 15.3467 14.54 15.3467C14.0761 15.3467 13.6788 15.2262 13.3486 14.9854C13.0683 14.7751 12.808 14.4628 12.5684 14.0488C12.5103 13.9485 12.3808 13.9127 12.2822 13.9736L11.0498 14.7354C10.9582 14.7921 10.9284 14.9124 10.9854 15.0039C11.3525 15.5939 11.8055 16.0612 12.3447 16.4043C12.9337 16.7701 13.6704 16.9531 14.5537 16.9531C15.2049 16.9531 15.7854 16.8326 16.2939 16.5918C16.8111 16.351 17.2173 16.0073 17.5117 15.5615C17.815 15.1156 17.9666 14.5974 17.9668 14.0088C17.9668 13.5718 17.8727 13.1742 17.6855 12.8174C17.5071 12.4516 17.2434 12.1434 16.8955 11.8936C16.8457 11.8565 16.7944 11.8201 16.7412 11.7861C16.635 11.7183 16.6252 11.5596 16.7217 11.4785C16.9981 11.2378 17.2078 10.9519 17.3506 10.6221C17.4933 10.292 17.5654 9.93433 17.5654 9.55078C17.5654 9.04228 17.4354 8.60031 17.1768 8.22559C16.9181 7.85097 16.5613 7.56069 16.1064 7.35547C15.6603 7.15022 15.1512 7.04786 14.5801 7.04785ZM8.19043 7.30176C8.15538 7.30176 8.12021 7.31063 8.08984 7.32812L5.4209 8.86621C5.31923 8.92479 5.28923 9.05812 5.35645 9.1543L6.02539 10.1104C6.0843 10.1946 6.19743 10.2212 6.28711 10.1709L7.77148 9.33789C7.90473 9.26312 8.06915 9.35899 8.06934 9.51172V16.4854C8.06949 16.5957 8.15917 16.6855 8.26953 16.6855H9.67676C9.78712 16.6855 9.8768 16.5957 9.87695 16.4854V7.50098C9.87679 7.39065 9.78712 7.30176 9.67676 7.30176H8.19043Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -1,4 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.8242 9.86914C14.9412 9.71897 15.1816 9.80183 15.1816 9.99219V12.3691C15.1815 12.4793 15.0926 12.5691 14.9824 12.5693H13.1309C12.9644 12.5693 12.8705 12.3774 12.9727 12.2461L14.8242 9.86914Z" fill="#222631"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM8.01465 7.30859C7.97968 7.30863 7.94534 7.3175 7.91504 7.33496L5.24512 8.87305C5.1435 8.93162 5.11351 9.06497 5.18066 9.16113L5.84961 10.1182C5.90842 10.2024 6.02169 10.2288 6.11133 10.1787L7.5957 9.34473C7.7289 9.26991 7.89326 9.36588 7.89355 9.51855V16.4922C7.89371 16.6024 7.9835 16.6923 8.09375 16.6924H9.50195C9.61215 16.6922 9.70101 16.6024 9.70117 16.4922V7.50781C9.70101 7.39761 9.61215 7.30878 9.50195 7.30859H8.01465ZM14.9873 7.32227C14.925 7.32227 14.866 7.35095 14.8281 7.40039L10.9131 12.5146C10.8864 12.5495 10.8711 12.5928 10.8711 12.6367V14.002C10.8713 14.1122 10.961 14.202 11.0713 14.2021H14.9824C15.0926 14.2023 15.1815 14.2912 15.1816 14.4014V16.4922C15.1818 16.6024 15.2716 16.6923 15.3818 16.6924H16.79C16.9002 16.6922 16.9891 16.6024 16.9893 16.4922V14.4014C16.9894 14.2911 17.0792 14.2023 17.1895 14.2021H17.7402C17.8504 14.202 17.9393 14.1122 17.9395 14.002V12.7686C17.9393 12.6583 17.8504 12.5695 17.7402 12.5693H17.1895C17.0792 12.5692 16.9894 12.4794 16.9893 12.3691V7.52148C16.9891 7.41128 16.9002 7.32245 16.79 7.32227H14.9873Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM12.0986 7.43457C11.9922 7.43457 11.9039 7.51869 11.8984 7.625L11.6514 12.4316C11.6455 12.546 11.7361 12.6416 11.8506 12.6416H14.1699C14.5621 12.6416 14.8926 12.6997 15.1602 12.8154C15.4277 12.9224 15.6336 13.0839 15.7764 13.2979C15.919 13.503 15.9902 13.7574 15.9902 14.0605C15.9902 14.3371 15.9238 14.5827 15.79 14.7969C15.6562 15.011 15.4685 15.176 15.2275 15.292C14.9956 15.4079 14.7233 15.4658 14.4111 15.4658C13.9562 15.4657 13.5587 15.3501 13.2197 15.1182C12.9316 14.9078 12.6673 14.592 12.4268 14.1709C12.369 14.0698 12.2387 14.0335 12.1396 14.0947L10.9072 14.8564C10.8159 14.913 10.7855 15.0316 10.8418 15.123C11.2099 15.721 11.6678 16.188 12.2158 16.5234C12.8136 16.8892 13.5457 17.0722 14.4111 17.0723C15.0535 17.0722 15.6339 16.9471 16.1514 16.6973C16.6687 16.4474 17.0747 16.0908 17.3691 15.627C17.6725 15.163 17.8242 14.614 17.8242 13.9805C17.8242 13.3738 17.699 12.8513 17.4492 12.4141C17.1993 11.9771 16.8114 11.6373 16.2852 11.3965C15.7676 11.1557 15.0886 11.0352 14.25 11.0352H13.7393C13.6245 11.0352 13.5328 10.9388 13.5391 10.8242L13.625 9.25684C13.6308 9.1509 13.7182 9.06757 13.8242 9.06738H17.291C17.4012 9.06719 17.49 8.97831 17.4902 8.86816V7.63477C17.4901 7.5245 17.4013 7.43476 17.291 7.43457H12.0986ZM7.96875 7.4209C7.93378 7.42094 7.89945 7.43078 7.86914 7.44824L5.19922 8.98633C5.09765 9.04492 5.06762 9.17826 5.13477 9.27441L5.80371 10.2314C5.86261 10.3158 5.97573 10.3414 6.06543 10.291L7.5498 9.45801C7.68304 9.38318 7.84745 9.47909 7.84766 9.63184V16.6055C7.84788 16.7157 7.93764 16.8046 8.04785 16.8047H9.45605C9.56621 16.8045 9.65505 16.7156 9.65527 16.6055V7.62109C9.65518 7.51083 9.56629 7.42109 9.45605 7.4209H7.96875Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1,4 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.7344 12.2812C15.0288 12.2812 15.2877 12.3486 15.5107 12.4824C15.7426 12.6073 15.921 12.781 16.0459 13.0039C16.1797 13.227 16.2471 13.4868 16.2471 13.7812C16.2469 14.0753 16.1796 14.3386 16.0459 14.5703C15.921 14.8021 15.7378 14.9854 15.4971 15.1191C15.2652 15.2527 14.984 15.3203 14.6543 15.3203C14.2709 15.3202 13.9358 15.213 13.6504 14.999C13.3649 14.7849 13.1458 14.4405 12.9941 13.9678C12.9311 13.7673 12.8816 13.5383 12.8447 13.2822C12.8373 13.23 12.8514 13.1766 12.8848 13.1357C13.0974 12.8752 13.344 12.6758 13.623 12.5361C13.962 12.3666 14.333 12.2813 14.7344 12.2812Z" fill="#222631"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM14.9756 7.04785C14.2619 7.04791 13.6454 7.17304 13.1279 7.42285C12.6197 7.66375 12.2001 8.00726 11.8701 8.45312C11.5402 8.89018 11.2944 9.41693 11.1338 10.0322C10.9822 10.6389 10.9062 11.3095 10.9062 12.041C10.9063 13.0846 11.0533 13.9726 11.3477 14.7041C11.6421 15.4357 12.0707 15.9942 12.6328 16.3779C13.1949 16.7615 13.8825 16.953 14.6943 16.9531C15.4079 16.9531 16.0198 16.806 16.5283 16.5117C17.0366 16.2174 17.4247 15.8243 17.6924 15.334C17.9689 14.8433 18.1073 14.3026 18.1074 13.7139C18.1074 13.1428 17.9822 12.6299 17.7324 12.1748C17.4915 11.7197 17.1398 11.3576 16.6758 11.0898C16.2208 10.8133 15.6849 10.6748 15.0693 10.6748C14.5878 10.6749 14.1455 10.7555 13.7441 10.916C13.5406 10.9947 13.348 11.0928 13.166 11.21C13.0169 11.3059 12.8065 11.1973 12.8281 11.0215C12.8474 10.8674 12.8708 10.7202 12.9004 10.5811C12.9896 10.1621 13.1235 9.81357 13.3018 9.53711C13.489 9.25177 13.7217 9.03731 13.998 8.89453C14.2745 8.75183 14.5963 8.68073 14.9619 8.68066C15.3455 8.68066 15.6756 8.77465 15.9521 8.96191C16.1874 9.10892 16.4082 9.30675 16.6133 9.55664C16.686 9.6453 16.8174 9.66326 16.9062 9.59082L17.9873 8.70703C18.07 8.6395 18.0854 8.51887 18.0176 8.43652C17.8366 8.21749 17.6251 8.01359 17.3848 7.82422C17.0993 7.59228 16.7559 7.40448 16.3545 7.26172C15.962 7.11902 15.5019 7.04787 14.9756 7.04785ZM7.81543 7.30176C7.78048 7.3018 7.74611 7.31067 7.71582 7.32812L5.0459 8.86621C4.94435 8.92483 4.91524 9.05817 4.98242 9.1543L5.65039 10.1104C5.70922 10.1945 5.82249 10.221 5.91211 10.1709L7.39648 9.33789C7.52973 9.26312 7.69415 9.35899 7.69434 9.51172V16.4854C7.69449 16.5956 7.78431 16.6854 7.89453 16.6855H9.30273C9.41296 16.6854 9.50179 16.5956 9.50195 16.4854V7.50098C9.50179 7.39075 9.41296 7.30192 9.30273 7.30176H7.81543Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM8.41504 7.30859C8.38009 7.30864 8.34572 7.31751 8.31543 7.33496L5.64551 8.87305C5.54393 8.93163 5.51391 9.06498 5.58105 9.16113L6.25 10.1182C6.3088 10.2024 6.42209 10.2287 6.51172 10.1787L7.99609 9.34473C8.12929 9.26991 8.29463 9.36588 8.29492 9.51855V16.4922C8.29508 16.6024 8.38392 16.6922 8.49414 16.6924H9.90234C10.0126 16.6922 10.1024 16.6024 10.1025 16.4922V7.50781C10.1024 7.39759 10.0126 7.30875 9.90234 7.30859H8.41504ZM11.832 7.32227C11.7218 7.32243 11.633 7.41126 11.6328 7.52148V8.75488C11.633 8.86511 11.7218 8.95492 11.832 8.95508H16.0918C16.2478 8.95508 16.3438 9.12551 16.2627 9.25879L11.9248 16.3877C11.8437 16.521 11.9397 16.6924 12.0957 16.6924H13.7129C13.7839 16.6924 13.8498 16.654 13.8857 16.5928L18.3379 9.00098C18.3558 8.97038 18.3662 8.93584 18.3662 8.90039V7.52148C18.3661 7.41126 18.2762 7.32242 18.166 7.32227H11.832Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,5 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.6377 12.4824C14.9945 12.4825 15.2981 12.545 15.5479 12.6699C15.8062 12.7948 16.003 12.9686 16.1367 13.1914C16.2792 13.4054 16.3515 13.6648 16.3516 13.9678C16.3515 14.2531 16.2794 14.5037 16.1367 14.7178C16.0029 14.9318 15.8065 15.0969 15.5479 15.2129C15.2981 15.3289 14.9945 15.3867 14.6377 15.3867C14.281 15.3867 13.9678 15.3287 13.7002 15.2129C13.4419 15.097 13.2413 14.9315 13.0986 14.7178C12.9648 14.5037 12.8975 14.2531 12.8975 13.9678C12.8975 13.6646 12.9649 13.4055 13.0986 13.1914C13.2413 12.9686 13.4418 12.7948 13.7002 12.6699C13.9679 12.545 14.2808 12.4824 14.6377 12.4824Z" fill="#222631"/>
|
||||
<path d="M14.6514 8.60059C15.0437 8.60072 15.356 8.70783 15.5879 8.92188C15.8195 9.13593 15.9365 9.417 15.9365 9.76465C15.9365 10.0054 15.8824 10.2162 15.7754 10.3945C15.6683 10.5638 15.5164 10.6978 15.3203 10.7959C15.1331 10.885 14.9098 10.9296 14.6514 10.9297C14.2322 10.9297 13.9011 10.827 13.6602 10.6221C13.4193 10.408 13.2989 10.1214 13.2988 9.76465C13.2989 9.53283 13.353 9.33156 13.46 9.16211C13.567 8.98386 13.7189 8.84612 13.915 8.74805C14.1203 8.64994 14.3659 8.60059 14.6514 8.60059Z" fill="#222631"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM14.624 7.04785C13.9283 7.04788 13.3439 7.16839 12.8711 7.40918C12.4072 7.64113 12.0544 7.95322 11.8135 8.3457C11.5727 8.72927 11.4522 9.14009 11.4521 9.57715C11.4522 10.041 11.5726 10.4565 11.8135 10.8223C11.9522 11.0329 12.1281 11.2186 12.3408 11.3789C12.4761 11.4812 12.4633 11.7101 12.3164 11.7949C12.1886 11.8687 12.0703 11.951 11.9609 12.041C11.6576 12.2819 11.4253 12.5759 11.2646 12.9238C11.1131 13.2628 11.0372 13.6423 11.0371 14.0615C11.0372 14.6057 11.1756 15.097 11.4521 15.5342C11.7288 15.9711 12.1348 16.3195 12.6699 16.5781C13.2052 16.8278 13.8569 16.9531 14.624 16.9531C15.2039 16.9531 15.718 16.8773 16.1641 16.7256C16.6189 16.5828 16.9937 16.3817 17.2881 16.123C17.5914 15.8643 17.819 15.5608 17.9707 15.2129C18.1312 14.8561 18.2118 14.4718 18.2119 14.0615C18.2119 13.4997 18.0732 13.0128 17.7969 12.6025C17.5204 12.1834 17.114 11.8579 16.5791 11.626C16.5648 11.6197 16.5634 11.6002 16.5771 11.5928C16.9508 11.391 17.2375 11.1345 17.4355 10.8223C17.6763 10.4566 17.7968 10.0409 17.7969 9.57715C17.7968 9.14023 17.6762 8.72916 17.4355 8.3457C17.2036 7.95331 16.8505 7.64114 16.3779 7.40918C15.914 7.16828 15.3288 7.0479 14.624 7.04785ZM7.98438 7.30176C7.94944 7.30182 7.91504 7.31068 7.88477 7.32812L5.21484 8.86621C5.11333 8.92484 5.0842 9.05818 5.15137 9.1543L5.81934 10.1104C5.87816 10.1945 5.99144 10.2209 6.08105 10.1709L7.56641 9.33789C7.69961 9.26333 7.86407 9.35907 7.86426 9.51172V16.4854C7.86442 16.5956 7.95328 16.6854 8.06348 16.6855H9.47168C9.58193 16.6854 9.67172 16.5956 9.67188 16.4854V7.50098C9.67172 7.39073 9.58193 7.30188 9.47168 7.30176H7.98438Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.9 KiB |
@@ -1,4 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.7549 8.68066C15.1474 8.68072 15.4822 8.78785 15.7588 9.00195C16.0442 9.2161 16.2624 9.56423 16.4141 10.0459C16.4761 10.2395 16.525 10.461 16.5615 10.71C16.5689 10.7602 16.5562 10.8122 16.5254 10.8525C16.3221 11.1185 16.0756 11.3233 15.7852 11.4648C15.4462 11.6342 15.0752 11.7197 14.6738 11.7197C14.3798 11.7196 14.1165 11.6569 13.8848 11.5322C13.6618 11.3984 13.4825 11.2191 13.3486 10.9961C13.2239 10.7643 13.1622 10.5049 13.1621 10.2197C13.1622 9.9258 13.2241 9.66235 13.3486 9.43066C13.4823 9.19897 13.6659 9.01565 13.8975 8.88184C14.1383 8.74803 14.4249 8.68072 14.7549 8.68066Z" fill="#222631"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM14.7148 7.04785C14.0102 7.04791 13.3983 7.19493 12.8809 7.48926C12.3725 7.7837 11.9787 8.17637 11.7021 8.66699C11.4346 9.14875 11.3008 9.68943 11.3008 10.2871C11.3009 10.849 11.4261 11.3623 11.6758 11.8262C11.9256 12.2812 12.2784 12.6472 12.7334 12.9238C13.1884 13.1915 13.7243 13.3251 14.3398 13.3252C14.8303 13.3252 15.2726 13.2492 15.665 13.0977C15.8699 13.0138 16.0623 12.9116 16.2422 12.793C16.3907 12.695 16.6022 12.803 16.5801 12.9795C16.5609 13.132 16.537 13.2789 16.5078 13.4189C16.4186 13.8381 16.2801 14.1911 16.0928 14.4766C15.9143 14.7529 15.6866 14.9638 15.4102 15.1064C15.1337 15.2489 14.8117 15.3203 14.4463 15.3203C14.0719 15.3202 13.7414 15.2311 13.4561 15.0527C13.2208 14.8983 13.001 14.6947 12.7959 14.4434C12.7234 14.3545 12.5918 14.3368 12.5029 14.4092L11.4229 15.293C11.3402 15.3606 11.3248 15.4819 11.3926 15.5645C11.5721 15.7832 11.778 15.9876 12.0107 16.1768C12.3052 16.4087 12.6533 16.5965 13.0547 16.7393C13.4561 16.882 13.9162 16.9531 14.4336 16.9531C15.1471 16.9531 15.7591 16.8325 16.2676 16.5918C16.7848 16.3421 17.209 15.9984 17.5391 15.5615C17.8692 15.1155 18.11 14.5833 18.2617 13.9678C18.4222 13.3523 18.5029 12.6825 18.5029 11.96C18.5029 10.9162 18.3549 10.0285 18.0605 9.29688C17.7661 8.56511 17.3376 8.00678 16.7754 7.62305C16.2134 7.23959 15.5265 7.04792 14.7148 7.04785ZM8.27734 7.30176C8.24237 7.30179 8.20804 7.31066 8.17773 7.32812L5.50781 8.86621C5.40623 8.92481 5.37618 9.05815 5.44336 9.1543L6.1123 10.1104C6.17114 10.1945 6.28439 10.221 6.37402 10.1709L7.8584 9.33789C7.99164 9.26312 8.15606 9.35899 8.15625 9.51172V16.4854C8.15641 16.5956 8.24619 16.6854 8.35645 16.6855H9.76465C9.87484 16.6854 9.96371 16.5956 9.96387 16.4854V7.50098C9.96371 7.39077 9.87484 7.30195 9.76465 7.30176H8.27734Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM12.0586 6.52539C11.2089 6.52539 10.4787 6.71953 9.86914 7.10742C9.32104 7.46159 8.85015 7.93144 8.45605 8.5166C8.39387 8.60895 8.42349 8.73404 8.51855 8.79199L9.69629 9.50977C9.79128 9.56767 9.91525 9.53683 9.97559 9.44336C10.2044 9.08885 10.4506 8.79964 10.7139 8.57617C11.0279 8.3176 11.4162 8.1885 11.8779 8.18848C12.1365 8.18848 12.3627 8.23923 12.5566 8.34082C12.7598 8.4424 12.9175 8.58484 13.0283 8.76953C13.1483 8.95414 13.2079 9.18061 13.208 9.44824C13.208 9.65136 13.1759 9.84118 13.1113 10.0166C13.0559 10.1828 12.9722 10.3494 12.8613 10.5156C12.7598 10.6725 12.6305 10.8339 12.4736 11C12.3259 11.1662 12.1593 11.3471 11.9746 11.541L8.72363 14.751C8.68559 14.7886 8.66406 14.8401 8.66406 14.8936V16.2998C8.66406 16.4103 8.7538 16.5 8.86426 16.5H15.1553C15.2657 16.5 15.3555 16.4103 15.3555 16.2998V15.0098C15.3555 14.8993 15.2657 14.8096 15.1553 14.8096H11.4326C11.2529 14.8094 11.1646 14.5907 11.2939 14.4658L13.2773 12.5518C13.6005 12.2378 13.8963 11.9145 14.1641 11.582C14.441 11.2404 14.6673 10.8804 14.8428 10.502C15.0182 10.1233 15.1055 9.71186 15.1055 9.26855C15.1055 8.71456 14.9674 8.23442 14.6904 7.82812C14.4226 7.41253 14.0575 7.09372 13.5957 6.87207C13.1433 6.64127 12.631 6.52545 12.0586 6.52539Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,4 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.7803 8.7207C16.1818 8.72072 16.508 8.85912 16.7578 9.13574C17.0164 9.40341 17.2081 9.78292 17.333 10.2734C17.4668 10.7641 17.5342 11.34 17.5342 12C17.5342 12.4995 17.4981 12.9511 17.4268 13.3525C17.3554 13.7449 17.2434 14.0885 17.0918 14.3828C16.949 14.6682 16.766 14.8922 16.543 15.0527C16.3289 15.2041 16.0744 15.2803 15.7803 15.2803C15.3878 15.2802 15.0577 15.1455 14.79 14.8779C14.5314 14.6014 14.334 14.218 14.2002 13.7275C14.0753 13.2368 14.0137 12.6602 14.0137 12C14.0137 11.5008 14.0488 11.0545 14.1201 10.6621C14.1915 10.2607 14.2987 9.91634 14.4414 9.63086C14.593 9.33658 14.781 9.1136 15.0039 8.96191C15.227 8.80132 15.4859 8.72073 15.7803 8.7207Z" fill="#222631"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM15.7803 7.04785C15.1646 7.04787 14.6289 7.173 14.1738 7.42285C13.728 7.67262 13.353 8.02477 13.0498 8.47949C12.7554 8.9256 12.5315 9.45293 12.3799 10.0596C12.2372 10.6572 12.166 11.3043 12.166 12C12.1661 12.9277 12.2951 13.7672 12.5537 14.5166C12.8214 15.2658 13.2189 15.8597 13.7451 16.2969C14.2805 16.734 14.9594 16.9531 15.7803 16.9531C16.3958 16.9531 16.9317 16.8279 17.3867 16.5781C17.8416 16.3283 18.2164 15.9801 18.5107 15.5342C18.8051 15.0792 19.0242 14.5527 19.167 13.9551C19.3187 13.3484 19.3945 12.6959 19.3945 12C19.3945 11.0633 19.2645 10.2239 19.0059 9.4834C18.7471 8.73409 18.3497 8.14029 17.8145 7.70312C17.2791 7.2661 16.601 7.04787 15.7803 7.04785ZM8.61035 7.04785C7.78948 7.0479 7.08402 7.23462 6.49512 7.60938C5.96734 7.95049 5.51371 8.40294 5.13379 8.96582C5.07169 9.05804 5.1014 9.18229 5.19629 9.24023L6.32324 9.92676C6.41808 9.98426 6.54109 9.95335 6.60156 9.86035C6.82182 9.52045 7.0595 9.24305 7.3125 9.02832C7.6158 8.77873 7.99062 8.65433 8.43652 8.6543C8.68639 8.6543 8.90538 8.70262 9.09277 8.80078C9.28897 8.89892 9.4408 9.03745 9.54785 9.21582C9.66375 9.39425 9.72168 9.6134 9.72168 9.87207C9.72163 10.0682 9.69034 10.2515 9.62793 10.4209C9.57441 10.5813 9.49363 10.7419 9.38672 10.9023C9.2886 11.0539 9.16329 11.2106 9.01172 11.3711C8.86903 11.5316 8.70859 11.7063 8.53027 11.8936L5.39062 14.9932C5.35259 15.0307 5.33108 15.0823 5.33105 15.1357V16.4854C5.33121 16.5957 5.42089 16.6855 5.53125 16.6855H11.5957C11.7061 16.6855 11.7957 16.5957 11.7959 16.4854V15.252C11.7957 15.1416 11.7061 15.0527 11.5957 15.0527H8.02246C7.84268 15.0526 7.75441 14.8329 7.88379 14.708L9.78809 12.8701C10.1003 12.5668 10.3868 12.2547 10.6455 11.9336C10.913 11.6037 11.1313 11.2552 11.3008 10.8896C11.4702 10.524 11.5556 10.1263 11.5557 9.69824C11.5557 9.16294 11.4219 8.69826 11.1543 8.30566C10.8955 7.90413 10.5428 7.59601 10.0967 7.38184C9.6594 7.15874 9.16364 7.04785 8.61035 7.04785Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.8 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM12.1592 6.87402C11.3742 6.87405 10.7045 7.03616 10.1504 7.35938C9.6056 7.68261 9.1531 8.12617 8.79297 8.68945L10.3164 9.64453C10.5195 9.29373 10.7557 9.02105 11.0234 8.82715C11.3004 8.63343 11.6329 8.53715 12.0205 8.53711C12.2789 8.53711 12.5054 8.58259 12.6992 8.6748C12.8931 8.75791 13.0454 8.88804 13.1562 9.06348C13.2763 9.2297 13.3369 9.43721 13.3369 9.68652C13.3369 10.0559 13.2024 10.3516 12.9346 10.5732C12.6668 10.7854 12.3299 10.8916 11.9238 10.8916H10.4551V12.5537H12.0479C12.3987 12.5537 12.6999 12.6144 12.9492 12.7344C13.2076 12.8452 13.4065 13.0119 13.5449 13.2334C13.6925 13.4457 13.7656 13.7043 13.7656 14.0088C13.7656 14.2949 13.6925 14.5489 13.5449 14.7705C13.4064 14.9921 13.2121 15.1632 12.9629 15.2832C12.7228 15.4032 12.4404 15.4629 12.1172 15.4629C11.6372 15.4628 11.2263 15.3381 10.8848 15.0889C10.5524 14.8395 10.2477 14.4519 9.9707 13.9258L8.33594 14.9365C8.73304 15.6291 9.23621 16.1697 9.8457 16.5576C10.4552 16.9362 11.2176 17.126 12.1318 17.126C12.8058 17.1259 13.4063 17.0012 13.9326 16.752C14.4682 16.5026 14.8886 16.1463 15.1934 15.6846C15.5071 15.2229 15.664 14.6874 15.6641 14.0781C15.6641 13.6256 15.567 13.2141 15.373 12.8447C15.1884 12.4663 14.9157 12.1481 14.5557 11.8896C14.4278 11.7946 14.2894 11.7091 14.1406 11.6338C14.2229 11.5787 14.3023 11.5218 14.376 11.46C14.6622 11.2107 14.8786 10.9148 15.0264 10.5732C15.1741 10.2315 15.248 9.86197 15.248 9.46484C15.248 8.93854 15.1144 8.48159 14.8467 8.09375C14.5789 7.70592 14.2092 7.40576 13.7383 7.19336C13.2766 6.98099 12.7502 6.87403 12.1592 6.87402Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,4 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.8652 9.5459C11.9822 9.39576 12.2236 9.47861 12.2236 9.66895V12.1562C12.2236 12.2666 12.1337 12.3563 12.0234 12.3564H10.085C9.91847 12.3564 9.82447 12.1645 9.92676 12.0332L11.8652 9.5459Z" fill="#222631"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM12.0176 6.92578C11.9553 6.92578 11.8962 6.95448 11.8584 7.00391L7.80371 12.3027C7.77706 12.3376 7.76272 12.38 7.7627 12.4238V13.8457C7.76272 13.956 7.85161 14.0457 7.96191 14.0459H12.0234C12.1338 14.0461 12.2236 14.1357 12.2236 14.2461V16.4229C12.2237 16.5332 12.3125 16.6229 12.4229 16.623H13.8936C14.0039 16.6229 14.0937 16.5332 14.0938 16.4229V14.2461C14.0938 14.1357 14.1826 14.0461 14.293 14.0459H14.877C14.9873 14.0457 15.0771 13.956 15.0771 13.8457V12.5566C15.0771 12.4463 14.9873 12.3566 14.877 12.3564H14.293C14.1827 12.3563 14.0938 12.2666 14.0938 12.1562V7.12598C14.0938 7.01562 14.0039 6.92594 13.8936 6.92578H12.0176Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13 15H14.5V12.75L16.25 15H18.075L15.75 12L18.075 9H16.25L14.5 11.25V9H13V15ZM9.5 15H11V13.5H12V12H11V9H9.5V12H8V9H6.5V13.5H9.5V15ZM5 21C4.45 21 3.97917 20.8042 3.5875 20.4125C3.19583 20.0208 3 19.55 3 19V5C3 4.45 3.19583 3.97917 3.5875 3.5875C3.97917 3.19583 4.45 3 5 3H19C19.55 3 20.0208 3.19583 20.4125 3.5875C20.8042 3.97917 21 4.45 21 5V19C21 19.55 20.8042 20.0208 20.4125 20.4125C20.0208 20.8042 19.55 21 19 21H5ZM5 19H19V5H5V19Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 566 B |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM9.66211 7.0127C9.5559 7.01288 9.46849 7.09608 9.46289 7.20215L9.20605 12.1914C9.20036 12.3055 9.29109 12.4011 9.40527 12.4014H11.8135C12.2198 12.4014 12.5618 12.462 12.8389 12.582C13.1156 12.6928 13.3279 12.8587 13.4756 13.0801C13.6233 13.2925 13.6973 13.5561 13.6973 13.8701C13.6972 14.1562 13.6286 14.4103 13.4902 14.6318C13.3518 14.8534 13.1574 15.0245 12.9082 15.1445C12.6681 15.2646 12.3857 15.3242 12.0625 15.3242C11.5918 15.3242 11.1808 15.2047 10.8301 14.9648C10.5301 14.7459 10.2542 14.4162 10.0039 13.9766C9.94623 13.8757 9.81669 13.8394 9.71777 13.9004L8.42969 14.6963C8.33839 14.7528 8.30807 14.8723 8.36426 14.9639C8.74594 15.5856 9.22193 16.0707 9.79102 16.4189C10.4097 16.7975 11.1669 16.9872 12.0625 16.9873C12.7274 16.9873 13.3286 16.8572 13.8643 16.5986C14.3994 16.3401 14.8194 15.971 15.124 15.4912C15.438 15.011 15.5957 14.4427 15.5957 13.7871C15.5957 13.1592 15.4665 12.6185 15.208 12.166C14.9494 11.7135 14.5468 11.3626 14.002 11.1133C13.4664 10.864 12.7644 10.7393 11.8965 10.7393H11.3594C11.2447 10.7392 11.1539 10.6429 11.1602 10.5283L11.249 8.89258C11.2548 8.78652 11.343 8.70312 11.4492 8.70312H15.0488C15.1593 8.70312 15.249 8.61339 15.249 8.50293V7.21289C15.249 7.10243 15.1593 7.0127 15.0488 7.0127H9.66211Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,4 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.2217 12.291C12.5265 12.291 12.7945 12.3605 13.0254 12.499C13.2654 12.6283 13.4498 12.8083 13.5791 13.0391C13.7176 13.27 13.7871 13.538 13.7871 13.8428C13.7871 14.1474 13.7176 14.4201 13.5791 14.6602C13.4499 14.9 13.2608 15.0891 13.0117 15.2275C12.7716 15.3661 12.4804 15.4355 12.1387 15.4355C11.7416 15.4355 11.3951 15.3251 11.0996 15.1035C10.8041 14.8819 10.5779 14.5256 10.4209 14.0361C10.3554 13.8281 10.3037 13.5911 10.2656 13.3252C10.2582 13.273 10.2725 13.2197 10.3057 13.1787C10.5263 12.9075 10.7823 12.6987 11.0723 12.5537C11.4231 12.3784 11.8063 12.291 12.2217 12.291Z" fill="#222631"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM12.4707 6.87402C11.7322 6.87408 11.0951 7.00328 10.5596 7.26172C10.0331 7.51108 9.59855 7.86732 9.25684 8.3291C8.91524 8.78158 8.66132 9.32676 8.49512 9.96387C8.33817 10.5918 8.25977 11.2848 8.25977 12.042C8.25981 13.1224 8.41206 14.0416 8.7168 14.7988C9.02157 15.556 9.46513 16.1332 10.0469 16.5303C10.6287 16.9273 11.3403 17.126 12.1807 17.126C12.9192 17.1259 13.5518 16.9736 14.0781 16.6689C14.6046 16.3642 15.0061 15.9572 15.2832 15.4492C15.5694 14.9414 15.7129 14.3829 15.7129 13.7734C15.7129 13.1824 15.5838 12.6507 15.3252 12.1797C15.0758 11.7088 14.7106 11.3346 14.2305 11.0576C13.7596 10.7714 13.2054 10.6289 12.5684 10.6289C12.0696 10.6289 11.6119 10.7117 11.1963 10.8779C10.9803 10.9614 10.7764 11.0661 10.584 11.1914C10.4353 11.2883 10.2243 11.18 10.2461 11.0039C10.2666 10.8381 10.2925 10.6805 10.3242 10.5312C10.4165 10.0975 10.5547 9.73736 10.7393 9.45117C10.9332 9.15563 11.1737 8.9339 11.46 8.78613C11.7462 8.63841 12.0785 8.56449 12.457 8.56445C12.8542 8.56445 13.1961 8.66152 13.4824 8.85547C13.7277 9.00877 13.9574 9.21617 14.1709 9.47754C14.2435 9.56615 14.3741 9.58308 14.4629 9.51074L15.5928 8.58887C15.6754 8.52131 15.6907 8.39989 15.623 8.31738C15.435 8.08858 15.2155 7.87521 14.9648 7.67773C14.6694 7.43766 14.3139 7.24346 13.8984 7.0957C13.4921 6.94793 13.0156 6.87402 12.4707 6.87402Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM8.92676 7.15137C8.81648 7.15157 8.72754 7.24123 8.72754 7.35156V8.6416C8.72754 8.75193 8.81648 8.84159 8.92676 8.8418H13.3545C13.5105 8.8418 13.6064 9.01224 13.5254 9.14551L9.02344 16.5449C8.94253 16.6782 9.03839 16.8486 9.19434 16.8486H10.8848C10.9557 16.8486 11.0217 16.8111 11.0576 16.75L15.668 8.88867C15.686 8.85799 15.6963 8.82268 15.6963 8.78711V7.35156C15.6963 7.24117 15.6065 7.15148 15.4961 7.15137H8.92676Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 701 B |
@@ -1,5 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.0137 12.499C12.3831 12.499 12.6975 12.5631 12.9561 12.6924C13.2238 12.8217 13.4269 13.0026 13.5654 13.2334C13.713 13.4549 13.7871 13.7224 13.7871 14.0361C13.7871 14.3317 13.7132 14.5908 13.5654 14.8125C13.4269 15.0341 13.2238 15.2052 12.9561 15.3252C12.6975 15.4453 12.3831 15.5049 12.0137 15.5049C11.6443 15.5049 11.321 15.4452 11.0439 15.3252C10.7763 15.2052 10.5687 15.034 10.4209 14.8125C10.2824 14.5908 10.2129 14.3317 10.2129 14.0361C10.2129 13.7223 10.2825 13.4549 10.4209 13.2334C10.5687 13.0025 10.7761 12.8217 11.0439 12.6924C11.321 12.5631 11.6443 12.499 12.0137 12.499Z" fill="#222631"/>
|
||||
<path d="M12.0273 8.48145C12.4336 8.48145 12.757 8.59196 12.9971 8.81348C13.2372 9.03513 13.3574 9.32634 13.3574 9.68652C13.3574 9.93587 13.3022 10.1532 13.1914 10.3379C13.0806 10.5133 12.9229 10.6514 12.7197 10.7529C12.5258 10.8452 12.2951 10.8916 12.0273 10.8916C11.5934 10.8916 11.2513 10.7856 11.002 10.5732C10.7527 10.3516 10.6279 10.0559 10.6279 9.68652C10.6279 9.44645 10.6841 9.23893 10.7949 9.06348C10.9057 8.87877 11.0625 8.73538 11.2656 8.63379C11.478 8.53222 11.7319 8.48146 12.0273 8.48145Z" fill="#222631"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM12 6.87402C11.2796 6.87402 10.6741 6.99869 10.1846 7.24805C9.70456 7.4881 9.34013 7.81162 9.09082 8.21777C8.84153 8.6148 8.71686 9.03978 8.7168 9.49219C8.7168 9.97244 8.84146 10.4026 9.09082 10.7812C9.23693 11.003 9.42303 11.1974 9.64844 11.3652C9.78441 11.4665 9.77192 11.6949 9.625 11.7793C9.48763 11.8579 9.36011 11.9457 9.24316 12.042C8.9292 12.2913 8.68868 12.5959 8.52246 12.9561C8.36552 13.307 8.28711 13.6998 8.28711 14.1338C8.28717 14.697 8.43056 15.2048 8.7168 15.6572C9.0031 16.1097 9.42343 16.4705 9.97754 16.7383C10.5316 16.9967 11.2059 17.126 12 17.126C12.6001 17.126 13.1311 17.0476 13.5928 16.8906C14.0638 16.7429 14.4521 16.5344 14.7568 16.2666C15.0706 15.9989 15.3059 15.6851 15.4629 15.3252C15.6291 14.9559 15.7128 14.5585 15.7129 14.1338C15.7129 13.5519 15.5695 13.0479 15.2832 12.623C14.9969 12.1891 14.5765 11.8524 14.0225 11.6123C14.0079 11.6059 14.0066 11.5867 14.0205 11.5791C14.4075 11.3703 14.7041 11.1046 14.9092 10.7812C15.1585 10.4026 15.2832 9.97244 15.2832 9.49219C15.2831 9.03978 15.1585 8.6148 14.9092 8.21777C14.6691 7.81147 14.3039 7.48815 13.8145 7.24805C13.3343 6.99878 12.7294 6.87405 12 6.87402Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -1,4 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.9785 8.56445C12.3848 8.56445 12.7313 8.67492 13.0176 8.89648C13.3131 9.11813 13.5393 9.47883 13.6963 9.97754C13.7608 10.1787 13.8109 10.409 13.8486 10.668C13.8559 10.7181 13.8432 10.7692 13.8125 10.8096C13.6017 11.0862 13.3465 11.2994 13.0449 11.4463C12.6941 11.6216 12.3109 11.709 11.8955 11.709C11.5909 11.709 11.3182 11.6448 11.0781 11.5156C10.8473 11.3771 10.662 11.1918 10.5234 10.9609C10.3942 10.7209 10.3301 10.4527 10.3301 10.1572C10.3301 9.85258 10.3942 9.57989 10.5234 9.33984C10.6619 9.09982 10.8518 8.91098 11.0918 8.77246C11.3411 8.63394 11.6368 8.56446 11.9785 8.56445Z" fill="#222631"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM11.9365 6.87402C11.2072 6.87409 10.5746 7.02641 10.0391 7.33105C9.51266 7.63583 9.10661 8.04284 8.82031 8.55078C8.5433 9.04943 8.40433 9.60789 8.4043 10.2266C8.4043 10.8084 8.53341 11.3401 8.79199 11.8203C9.05057 12.2912 9.41579 12.6698 9.88672 12.9561C10.3576 13.233 10.9118 13.3711 11.5488 13.3711C12.0568 13.3711 12.5145 13.2927 12.9209 13.1357C13.1383 13.0468 13.342 12.9382 13.5322 12.8115C13.6803 12.7132 13.8918 12.8215 13.8701 12.998C13.85 13.1619 13.8242 13.3188 13.793 13.4688C13.7007 13.9024 13.558 14.2671 13.3643 14.5625C13.1795 14.8488 12.9435 15.0661 12.6572 15.2139C12.371 15.3616 12.0387 15.4355 11.6602 15.4355C11.2723 15.4355 10.9303 15.3429 10.6348 15.1582C10.3895 14.9972 10.1599 14.7852 9.94629 14.5225C9.87392 14.4337 9.74308 14.4169 9.6543 14.4893L8.52441 15.4111C8.44182 15.4787 8.42677 15.5999 8.49414 15.6826C8.68087 15.9113 8.89571 16.1249 9.13867 16.3223C9.44335 16.5623 9.80332 16.7566 10.2188 16.9043C10.6343 17.0521 11.1108 17.126 11.6465 17.126C12.3851 17.1259 13.0177 17.0012 13.5439 16.752C14.0796 16.4934 14.5186 16.1371 14.8604 15.6846C15.2019 15.2229 15.4514 14.6732 15.6084 14.0361C15.7746 13.399 15.8574 12.7059 15.8574 11.958C15.8574 10.8776 15.7051 9.95839 15.4004 9.20117C15.0956 8.44404 14.6521 7.86679 14.0703 7.46973C13.4885 7.07268 12.7769 6.87402 11.9365 6.87402Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="24" height="24" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 147 B |
@@ -1,3 +0,0 @@
|
||||
<svg width="4" height="2" viewBox="0 0 4 2" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.20874 0.75H0" stroke="white" stroke-width="1.5"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 161 B |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.85 17.1C6.7 16.45 7.65 15.9375 8.7 15.5625C9.75 15.1875 10.85 15 12 15C13.15 15 14.25 15.1875 15.3 15.5625C16.35 15.9375 17.3 16.45 18.15 17.1C18.7333 16.4167 19.1875 15.6417 19.5125 14.775C19.8375 13.9083 20 12.9833 20 12C20 9.78333 19.2208 7.89583 17.6625 6.3375C16.1042 4.77917 14.2167 4 12 4C9.78333 4 7.89583 4.77917 6.3375 6.3375C4.77917 7.89583 4 9.78333 4 12C4 12.9833 4.1625 13.9083 4.4875 14.775C4.8125 15.6417 5.26667 16.4167 5.85 17.1ZM12 13C11.0167 13 10.1875 12.6625 9.5125 11.9875C8.8375 11.3125 8.5 10.4833 8.5 9.5C8.5 8.51667 8.8375 7.6875 9.5125 7.0125C10.1875 6.3375 11.0167 6 12 6C12.9833 6 13.8125 6.3375 14.4875 7.0125C15.1625 7.6875 15.5 8.51667 15.5 9.5C15.5 10.4833 15.1625 11.3125 14.4875 11.9875C13.8125 12.6625 12.9833 13 12 13ZM12 22C10.6167 22 9.31667 21.7375 8.1 21.2125C6.88333 20.6875 5.825 19.975 4.925 19.075C4.025 18.175 3.3125 17.1167 2.7875 15.9C2.2625 14.6833 2 13.3833 2 12C2 10.6167 2.2625 9.31667 2.7875 8.1C3.3125 6.88333 4.025 5.825 4.925 4.925C5.825 4.025 6.88333 3.3125 8.1 2.7875C9.31667 2.2625 10.6167 2 12 2C13.3833 2 14.6833 2.2625 15.9 2.7875C17.1167 3.3125 18.175 4.025 19.075 4.925C19.975 5.825 20.6875 6.88333 21.2125 8.1C21.7375 9.31667 22 10.6167 22 12C22 13.3833 21.7375 14.6833 21.2125 15.9C20.6875 17.1167 19.975 18.175 19.075 19.075C18.175 19.975 17.1167 20.6875 15.9 21.2125C14.6833 21.7375 13.3833 22 12 22ZM12 20C12.8833 20 13.7167 19.8708 14.5 19.6125C15.2833 19.3542 16 18.9833 16.65 18.5C16 18.0167 15.2833 17.6458 14.5 17.3875C13.7167 17.1292 12.8833 17 12 17C11.1167 17 10.2833 17.1292 9.5 17.3875C8.71667 17.6458 8 18.0167 7.35 18.5C8 18.9833 8.71667 19.3542 9.5 19.6125C10.2833 19.8708 11.1167 20 12 20ZM12 11C12.4333 11 12.7917 10.8583 13.075 10.575C13.3583 10.2917 13.5 9.93333 13.5 9.5C13.5 9.06667 13.3583 8.70833 13.075 8.425C12.7917 8.14167 12.4333 8 12 8C11.5667 8 11.2083 8.14167 10.925 8.425C10.6417 8.70833 10.5 9.06667 10.5 9.5C10.5 9.93333 10.6417 10.2917 10.925 10.575C11.2083 10.8583 11.5667 11 12 11Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15 21V18H11V8H9V11H2V3H9V6H15V3H22V11H15V8H13V16H15V13H22V21H15ZM17 9H20V5H17V9ZM17 19H20V15H17V19ZM4 9H7V5H4V9Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 244 B |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 13H5V11H11V5H13V11H19V13H13V19H11V13Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 172 B |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 17H13V13H17V11H13V7H11V11H7V13H11V17ZM5 21C4.45 21 3.97917 20.8042 3.5875 20.4125C3.19583 20.0208 3 19.55 3 19V5C3 4.45 3.19583 3.97917 3.5875 3.5875C3.97917 3.19583 4.45 3 5 3H19C19.55 3 20.0208 3.19583 20.4125 3.5875C20.8042 3.97917 21 4.45 21 5V19C21 19.55 20.8042 20.0208 20.4125 20.4125C20.0208 20.8042 19.55 21 19 21H5ZM5 19H19V5H5V19Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 476 B |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 17H13V13H17V11H13V7H11V11H7V13H11V17ZM5 21C4.45 21 3.97917 20.8042 3.5875 20.4125C3.19583 20.0208 3 19.55 3 19V5C3 4.45 3.19583 3.97917 3.5875 3.5875C3.97917 3.19583 4.45 3 5 3H19C19.55 3 20.0208 3.19583 20.4125 3.5875C20.8042 3.97917 21 4.45 21 5V19C21 19.55 20.8042 20.0208 20.4125 20.4125C20.0208 20.8042 19.55 21 19 21H5Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 460 B |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 17H13V13H17V11H13V7H11V11H7V13H11V17ZM12 22C10.6167 22 9.31667 21.7375 8.1 21.2125C6.88333 20.6875 5.825 19.975 4.925 19.075C4.025 18.175 3.3125 17.1167 2.7875 15.9C2.2625 14.6833 2 13.3833 2 12C2 10.6167 2.2625 9.31667 2.7875 8.1C3.3125 6.88333 4.025 5.825 4.925 4.925C5.825 4.025 6.88333 3.3125 8.1 2.7875C9.31667 2.2625 10.6167 2 12 2C13.3833 2 14.6833 2.2625 15.9 2.7875C17.1167 3.3125 18.175 4.025 19.075 4.925C19.975 5.825 20.6875 6.88333 21.2125 8.1C21.7375 9.31667 22 10.6167 22 12C22 13.3833 21.7375 14.6833 21.2125 15.9C20.6875 17.1167 19.975 18.175 19.075 19.075C18.175 19.975 17.1167 20.6875 15.9 21.2125C14.6833 21.7375 13.3833 22 12 22ZM12 20C14.2333 20 16.125 19.225 17.675 17.675C19.225 16.125 20 14.2333 20 12C20 9.76667 19.225 7.875 17.675 6.325C16.125 4.775 14.2333 4 12 4C9.76667 4 7.875 4.775 6.325 6.325C4.775 7.875 4 9.76667 4 12C4 14.2333 4.775 16.125 6.325 17.675C7.875 19.225 9.76667 20 12 20Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 14H13V11H16V9H13V6H11V9H8V11H11V14ZM2 22V4C2 3.45 2.19583 2.97917 2.5875 2.5875C2.97917 2.19583 3.45 2 4 2H20C20.55 2 21.0208 2.19583 21.4125 2.5875C21.8042 2.97917 22 3.45 22 4V16C22 16.55 21.8042 17.0208 21.4125 17.4125C21.0208 17.8042 20.55 18 20 18H6L2 22ZM5.15 16H20V4H4V17.125L5.15 16Z" fill="#222631"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 426 B |