Merge branch 'develop'
This commit is contained in:
commit
8a7dfce330
421 changed files with 18315 additions and 16207 deletions
|
@ -24,3 +24,4 @@ Dockerfile
|
|||
|
||||
settings.json
|
||||
src/node_modules
|
||||
admin/node_modules
|
||||
|
|
18
.env.default
Normal file
18
.env.default
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Please copy and rename this file.
|
||||
#
|
||||
# !Attention!
|
||||
# Always ensure to load the env variables in every terminal session.
|
||||
# Otherwise the env variables will not be available
|
||||
|
||||
DOCKER_COMPOSE_APP_DEV_PORT_PUBLISHED=9001
|
||||
DOCKER_COMPOSE_APP_DEV_PORT_TARGET=9001
|
||||
|
||||
# IMPORTANT: When the env var DEFAULT_PAD_TEXT is unset or empty, then the pad is not established (not the landing page).
|
||||
# The env var DEFAULT_PAD_TEXT seems to be mandatory in the latest version of etherpad.
|
||||
DOCKER_COMPOSE_APP_DEV_ENV_DEFAULT_PAD_TEXT="Welcome to etherpad"
|
||||
|
||||
DOCKER_COMPOSE_APP_DEV_ADMIN_PASSWORD=
|
||||
|
||||
DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_DATABASE=db
|
||||
DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PASSWORD=etherpad-lite-password
|
||||
DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_USER=etherpad-lite-user
|
5
.github/dependabot.yml
vendored
5
.github/dependabot.yml
vendored
|
@ -14,8 +14,3 @@ updates:
|
|||
schedule:
|
||||
interval: "daily"
|
||||
versioning-strategy: "increase"
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/src/bin/doc"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
versioning-strategy: "increase"
|
||||
|
|
165
.github/workflows/backend-tests.yml
vendored
165
.github/workflows/backend-tests.yml
vendored
|
@ -27,22 +27,42 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
-
|
||||
name: Install libreoffice
|
||||
run: |
|
||||
sudo add-apt-repository -y ppa:libreoffice/ppa
|
||||
sudo apt update
|
||||
sudo apt install -y --no-install-recommends libreoffice libreoffice-pdfimport
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.4.2
|
||||
with:
|
||||
packages: libreoffice libreoffice-pdfimport
|
||||
version: 1.0
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installDeps.sh
|
||||
run: bin/installDeps.sh
|
||||
- name: Install admin ui
|
||||
working-directory: admin
|
||||
run: pnpm install
|
||||
- name: Build admin ui
|
||||
working-directory: admin
|
||||
run: pnpm build
|
||||
-
|
||||
name: Run the backend tests
|
||||
run: cd src && npm test
|
||||
run: pnpm test
|
||||
|
||||
withpluginsLinux:
|
||||
# run on pushes to any branch
|
||||
|
@ -64,22 +84,43 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
-
|
||||
name: Install libreoffice
|
||||
run: |
|
||||
sudo add-apt-repository -y ppa:libreoffice/ppa
|
||||
sudo apt update
|
||||
sudo apt install -y --no-install-recommends libreoffice libreoffice-pdfimport
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.4.2
|
||||
with:
|
||||
packages: libreoffice libreoffice-pdfimport
|
||||
version: 1.0
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: bin/installDeps.sh
|
||||
- name: Install admin ui
|
||||
working-directory: admin
|
||||
run: pnpm install
|
||||
- name: Build admin ui
|
||||
working-directory: admin
|
||||
run: pnpm build
|
||||
-
|
||||
name: Install Etherpad plugins
|
||||
# The --legacy-peer-deps flag is required to work around a bug in npm v7:
|
||||
# https://github.com/npm/cli/issues/2199
|
||||
run: >
|
||||
npm install --no-save --legacy-peer-deps
|
||||
pnpm install --workspace-root
|
||||
ep_align
|
||||
ep_author_hover
|
||||
ep_cursortrace
|
||||
|
@ -93,21 +134,9 @@ jobs:
|
|||
ep_spellcheck
|
||||
ep_subscript_and_superscript
|
||||
ep_table_of_contents
|
||||
# Etherpad core dependencies must be installed after installing the
|
||||
# plugin's dependencies, otherwise npm will try to hoist common
|
||||
# dependencies by removing them from src/node_modules and installing them
|
||||
# in the top-level node_modules. As of v6.14.10, npm's hoist logic appears
|
||||
# to be buggy, because it sometimes removes dependencies from
|
||||
# src/node_modules but fails to add them to the top-level node_modules.
|
||||
# Even if npm correctly hoists the dependencies, the hoisting seems to
|
||||
# confuse tools such as `npm outdated`, `npm update`, and some ESLint
|
||||
# rules.
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: Run the backend tests
|
||||
run: cd src && npm test
|
||||
run: pnpm test
|
||||
|
||||
withoutpluginsWindows:
|
||||
# run on pushes to any branch
|
||||
|
@ -125,13 +154,33 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installOnWindows.bat
|
||||
run: bin/installOnWindows.bat
|
||||
- name: Install admin ui
|
||||
working-directory: admin
|
||||
run: pnpm install
|
||||
- name: Build admin ui
|
||||
working-directory: admin
|
||||
run: pnpm build
|
||||
-
|
||||
name: Fix up the settings.json
|
||||
run: |
|
||||
|
@ -139,7 +188,7 @@ jobs:
|
|||
powershell -Command "(gc settings.json.holder) -replace '\"points\": 10', '\"points\": 1000' | Out-File -encoding ASCII settings.json"
|
||||
-
|
||||
name: Run the backend tests
|
||||
run: cd src && npm test
|
||||
run: cd src && pnpm test
|
||||
|
||||
withpluginsWindows:
|
||||
# run on pushes to any branch
|
||||
|
@ -157,17 +206,37 @@ jobs:
|
|||
-
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
node-version: 21
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
- name: Install admin ui
|
||||
working-directory: admin
|
||||
run: pnpm install
|
||||
- name: Build admin ui
|
||||
working-directory: admin
|
||||
run: pnpm build
|
||||
-
|
||||
name: Install Etherpad plugins
|
||||
# The --legacy-peer-deps flag is required to work around a bug in npm
|
||||
# v7: https://github.com/npm/cli/issues/2199
|
||||
run: >
|
||||
npm install --no-save --legacy-peer-deps
|
||||
pnpm install --workspace-root
|
||||
ep_align
|
||||
ep_author_hover
|
||||
ep_cursortrace
|
||||
|
@ -192,7 +261,7 @@ jobs:
|
|||
# rules.
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installOnWindows.bat
|
||||
run: bin/installOnWindows.bat
|
||||
-
|
||||
name: Fix up the settings.json
|
||||
run: |
|
||||
|
@ -200,4 +269,4 @@ jobs:
|
|||
powershell -Command "(gc settings.json.holder) -replace '\"points\": 10', '\"points\": 1000' | Out-File -encoding ASCII settings.json"
|
||||
-
|
||||
name: Run the backend tests
|
||||
run: cd src && npm test
|
||||
run: cd src && pnpm test
|
||||
|
|
26
.github/workflows/docker.yml
vendored
26
.github/workflows/docker.yml
vendored
|
@ -30,6 +30,7 @@ jobs:
|
|||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
target: production
|
||||
load: true
|
||||
tags: ${{ env.TEST_TAG }}
|
||||
cache-from: type=gha
|
||||
|
@ -39,16 +40,28 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
docker run --rm -d -p 9001:9001 --name test ${{ env.TEST_TAG }}
|
||||
./bin/installDeps.sh
|
||||
docker logs -f test &
|
||||
./src/bin/installDeps.sh
|
||||
while true; do
|
||||
echo "Waiting for Docker container to start..."
|
||||
status=$(docker container inspect -f '{{.State.Health.Status}}' test) || exit 1
|
||||
|
@ -58,7 +71,7 @@ jobs:
|
|||
*) printf %s\\n "unexpected status: ${status}" >&2; exit 1;;
|
||||
esac
|
||||
done
|
||||
(cd src && npm run test-container)
|
||||
(cd src && pnpm run test-container)
|
||||
git clean -dxf .
|
||||
-
|
||||
name: Docker meta
|
||||
|
@ -85,6 +98,7 @@ jobs:
|
|||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
target: production
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
|
|
125
.github/workflows/frontend-admin-tests.yml
vendored
125
.github/workflows/frontend-admin-tests.yml
vendored
|
@ -12,11 +12,10 @@ jobs:
|
|||
name: with plugins
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# node: [16, 19, 20] >> Disabled node 16 and 18 because they do not work
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node: [19, 20, 21]
|
||||
node: [20, 21]
|
||||
|
||||
steps:
|
||||
-
|
||||
|
@ -32,16 +31,30 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install etherpad plugins
|
||||
# We intentionally install an old ep_align version to test upgrades to
|
||||
# the minor version number. The --legacy-peer-deps flag is required to
|
||||
# work around a bug in npm v7: https://github.com/npm/cli/issues/2199
|
||||
run: npm install --no-save --legacy-peer-deps ep_align@0.2.27
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
#-
|
||||
# name: Install etherpad plugins
|
||||
# # We intentionally install an old ep_align version to test upgrades to
|
||||
# # the minor version number. The --legacy-peer-deps flag is required to
|
||||
# # work around a bug in npm v7: https://github.com/npm/cli/issues/2199
|
||||
# run: pnpm install --workspace-root ep_align@0.2.27
|
||||
# Etherpad core dependencies must be installed after installing the
|
||||
# plugin's dependencies, otherwise npm will try to hoist common
|
||||
# dependencies by removing them from src/node_modules and installing them
|
||||
|
@ -53,10 +66,10 @@ jobs:
|
|||
# rules.
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: Install etherpad plugins
|
||||
run: rm -Rf node_modules/ep_align/static/tests/*
|
||||
run: bin/installDeps.sh
|
||||
#-
|
||||
# name: Install etherpad plugins
|
||||
# run: rm -Rf node_modules/ep_align/static/tests/*
|
||||
-
|
||||
name: export GIT_HASH to env
|
||||
id: environment
|
||||
|
@ -66,31 +79,67 @@ jobs:
|
|||
run: cp settings.json.template settings.json
|
||||
-
|
||||
name: Write custom settings.json that enables the Admin UI tests
|
||||
run: "sed -i 's/\"enableAdminUITests\": false/\"enableAdminUITests\": true,\\n\"users\":{\"admin\":{\"password\":\"changeme\",\"is_admin\":true}}/' settings.json"
|
||||
run: "sed -i 's/\"enableAdminUITests\": false/\"enableAdminUITests\": true,\\n\"users\":{\"admin\":{\"password\":\"changeme1\",\"is_admin\":true}}/' settings.json"
|
||||
-
|
||||
name: increase maxHttpBufferSize
|
||||
run: "sed -i 's/\"maxHttpBufferSize\": 10000/\"maxHttpBufferSize\": 100000/' settings.json"
|
||||
run: "sed -i 's/\"maxHttpBufferSize\": 10000/\"maxHttpBufferSize\": 10000000/' settings.json"
|
||||
-
|
||||
name: Disable import/export rate limiting
|
||||
run: |
|
||||
sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 1000000/' -i settings.json
|
||||
-
|
||||
name: Remove standard frontend test files, so only admin tests are run
|
||||
run: mv src/tests/frontend/specs/* /tmp && mv /tmp/admin*.js src/tests/frontend/specs
|
||||
-
|
||||
uses: saucelabs/sauce-connect-action@v2.3.6
|
||||
with:
|
||||
username: ${{ secrets.SAUCE_USERNAME }}
|
||||
accessKey: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||
tunnelIdentifier: ${{ steps.sauce_strings.outputs.tunnel_id }}
|
||||
-
|
||||
name: Run the frontend admin tests
|
||||
shell: bash
|
||||
env:
|
||||
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
|
||||
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||
SAUCE_NAME: ${{ steps.sauce_strings.outputs.name }}
|
||||
TRAVIS_JOB_NUMBER: ${{ steps.sauce_strings.outputs.tunnel_id }}
|
||||
GIT_HASH: ${{ steps.environment.outputs.sha_short }}
|
||||
sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 100000000/' -i settings.json
|
||||
- name: Build admin frontend
|
||||
working-directory: admin
|
||||
run: |
|
||||
src/tests/frontend/travis/adminrunner.sh
|
||||
pnpm install
|
||||
pnpm run build
|
||||
# name: Run the frontend admin tests
|
||||
# shell: bash
|
||||
# env:
|
||||
# SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
|
||||
# SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||
# SAUCE_NAME: ${{ steps.sauce_strings.outputs.name }}
|
||||
# TRAVIS_JOB_NUMBER: ${{ steps.sauce_strings.outputs.tunnel_id }}
|
||||
# GIT_HASH: ${{ steps.environment.outputs.sha_short }}
|
||||
# run: |
|
||||
# src/tests/frontend/travis/adminrunner.sh
|
||||
#-
|
||||
# uses: saucelabs/sauce-connect-action@v2.3.6
|
||||
# with:
|
||||
# username: ${{ secrets.SAUCE_USERNAME }}
|
||||
# accessKey: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||
# tunnelIdentifier: ${{ steps.sauce_strings.outputs.tunnel_id }}
|
||||
#-
|
||||
# name: Run the frontend admin tests
|
||||
# shell: bash
|
||||
# env:
|
||||
# SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
|
||||
# SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||
# SAUCE_NAME: ${{ steps.sauce_strings.outputs.name }}
|
||||
# TRAVIS_JOB_NUMBER: ${{ steps.sauce_strings.outputs.tunnel_id }}
|
||||
# GIT_HASH: ${{ steps.environment.outputs.sha_short }}
|
||||
# run: |
|
||||
# src/tests/frontend/travis/adminrunner.sh
|
||||
- name: Run the frontend admin tests
|
||||
shell: bash
|
||||
run: |
|
||||
pnpm run dev &
|
||||
connected=false
|
||||
can_connect() {
|
||||
curl -sSfo /dev/null http://localhost:9001/ || return 1
|
||||
connected=true
|
||||
}
|
||||
now() { date +%s; }
|
||||
start=$(now)
|
||||
while [ $(($(now) - $start)) -le 15 ] && ! can_connect; do
|
||||
sleep 1
|
||||
done
|
||||
cd src
|
||||
pnpm exec playwright install
|
||||
pnpm exec playwright install-deps
|
||||
pnpm run test-admin
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report-${{ matrix.node }}
|
||||
path: src/playwright-report/
|
||||
retention-days: 30
|
||||
|
|
295
.github/workflows/frontend-tests.yml
vendored
295
.github/workflows/frontend-tests.yml
vendored
|
@ -7,10 +7,141 @@ permissions:
|
|||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
withoutplugins:
|
||||
name: without plugins
|
||||
playwright-chrome:
|
||||
name: Playwright Chrome
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Generate Sauce Labs strings
|
||||
id: sauce_strings
|
||||
run: |
|
||||
printf %s\\n '::set-output name=name::${{ github.workflow }} - ${{ github.job }}'
|
||||
printf %s\\n '::set-output name=tunnel_id::${{ github.run_id }}-${{ github.run_number }}-${{ github.job }}'
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 21
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
if: always()
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: bin/installDeps.sh
|
||||
-
|
||||
name: export GIT_HASH to env
|
||||
id: environment
|
||||
run: echo "::set-output name=sha_short::$(git rev-parse --short ${{ github.sha }})"
|
||||
-
|
||||
name: Create settings.json
|
||||
run: cp ./src/tests/settings.json settings.json
|
||||
- name: Run the frontend tests
|
||||
shell: bash
|
||||
run: |
|
||||
pnpm run dev &
|
||||
connected=false
|
||||
can_connect() {
|
||||
curl -sSfo /dev/null http://localhost:9001/ || return 1
|
||||
connected=true
|
||||
}
|
||||
now() { date +%s; }
|
||||
start=$(now)
|
||||
while [ $(($(now) - $start)) -le 15 ] && ! can_connect; do
|
||||
sleep 1
|
||||
done
|
||||
cd src
|
||||
pnpm exec playwright install chromium --with-deps
|
||||
pnpm run test-ui --project=chromium
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report-${{ matrix.node }}-chrome
|
||||
path: src/playwright-report/
|
||||
retention-days: 30
|
||||
playwright-firefox:
|
||||
name: Playwright Firefox
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Generate Sauce Labs strings
|
||||
id: sauce_strings
|
||||
run: |
|
||||
printf %s\\n '::set-output name=name::${{ github.workflow }} - ${{ github.job }}'
|
||||
printf %s\\n '::set-output name=tunnel_id::${{ github.run_id }}-${{ github.run_number }}-${{ github.job }}'
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 21
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
if: always()
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
- name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: bin/installDeps.sh
|
||||
- name: export GIT_HASH to env
|
||||
id: environment
|
||||
run: echo "::set-output name=sha_short::$(git rev-parse --short ${{ github.sha }})"
|
||||
- name: Create settings.json
|
||||
run: cp ./src/tests/settings.json settings.json
|
||||
- name: Run the frontend tests
|
||||
shell: bash
|
||||
run: |
|
||||
pnpm run dev &
|
||||
connected=false
|
||||
can_connect() {
|
||||
curl -sSfo /dev/null http://localhost:9001/ || return 1
|
||||
connected=true
|
||||
}
|
||||
now() { date +%s; }
|
||||
start=$(now)
|
||||
while [ $(($(now) - $start)) -le 15 ] && ! can_connect; do
|
||||
sleep 1
|
||||
done
|
||||
cd src
|
||||
pnpm exec playwright install firefox --with-deps
|
||||
pnpm run test-ui --project=firefox
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report-${{ matrix.node }}-firefox
|
||||
path: src/playwright-report/
|
||||
retention-days: 30
|
||||
playwright-webkit:
|
||||
name: Playwright Webkit
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.actor != 'dependabot[bot]' }}
|
||||
|
||||
steps:
|
||||
-
|
||||
|
@ -25,127 +156,59 @@ jobs:
|
|||
-
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
node-version: 21
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
if: always()
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installDeps.sh
|
||||
run: bin/installDeps.sh
|
||||
-
|
||||
name: export GIT_HASH to env
|
||||
id: environment
|
||||
run: echo "::set-output name=sha_short::$(git rev-parse --short ${{ github.sha }})"
|
||||
-
|
||||
name: Create settings.json
|
||||
run: cp settings.json.template settings.json
|
||||
-
|
||||
name: Disable import/export rate limiting
|
||||
run: |
|
||||
sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 100000000/' -i settings.json
|
||||
-
|
||||
uses: saucelabs/sauce-connect-action@v2.3.6
|
||||
with:
|
||||
username: ${{ secrets.SAUCE_USERNAME }}
|
||||
accessKey: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||
tunnelIdentifier: ${{ steps.sauce_strings.outputs.tunnel_id }}
|
||||
-
|
||||
name: Run the frontend tests
|
||||
run: cp ./src/tests/settings.json settings.json
|
||||
- name: Run the frontend tests
|
||||
shell: bash
|
||||
env:
|
||||
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
|
||||
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||
SAUCE_NAME: ${{ steps.sauce_strings.outputs.name }}
|
||||
TRAVIS_JOB_NUMBER: ${{ steps.sauce_strings.outputs.tunnel_id }}
|
||||
GIT_HASH: ${{ steps.environment.outputs.sha_short }}
|
||||
run: |
|
||||
src/tests/frontend/travis/runner.sh
|
||||
|
||||
withplugins:
|
||||
name: with plugins
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.actor != 'dependabot[bot]' }}
|
||||
|
||||
steps:
|
||||
-
|
||||
name: Generate Sauce Labs strings
|
||||
id: sauce_strings
|
||||
run: |
|
||||
printf %s\\n '::set-output name=name::${{ github.workflow }} - ${{ github.job }}'
|
||||
printf %s\\n '::set-output name=tunnel_id::${{ github.run_id }}-${{ github.run_number }}-${{ github.job }}'
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
pnpm run dev &
|
||||
connected=false
|
||||
can_connect() {
|
||||
curl -sSfo /dev/null http://localhost:9001/ || return 1
|
||||
connected=true
|
||||
}
|
||||
now() { date +%s; }
|
||||
start=$(now)
|
||||
while [ $(($(now) - $start)) -le 15 ] && ! can_connect; do
|
||||
sleep 1
|
||||
done
|
||||
cd src
|
||||
pnpm exec playwright install webkit --with-deps
|
||||
pnpm run test-ui --project=webkit || true
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install Etherpad plugins
|
||||
# The --legacy-peer-deps flag is required to work around a bug in npm v7:
|
||||
# https://github.com/npm/cli/issues/2199
|
||||
run: >
|
||||
npm install --no-save --legacy-peer-deps
|
||||
ep_align
|
||||
ep_author_hover
|
||||
ep_cursortrace
|
||||
ep_embedmedia
|
||||
ep_font_size
|
||||
ep_hash_auth
|
||||
ep_headings2
|
||||
ep_image_upload
|
||||
ep_markdown
|
||||
ep_readonly_guest
|
||||
ep_set_title_on_pad
|
||||
ep_spellcheck
|
||||
ep_subscript_and_superscript
|
||||
ep_table_of_contents
|
||||
# Etherpad core dependencies must be installed after installing the
|
||||
# plugin's dependencies, otherwise npm will try to hoist common
|
||||
# dependencies by removing them from src/node_modules and installing them
|
||||
# in the top-level node_modules. As of v6.20.10, npm's hoist logic appears
|
||||
# to be buggy, because it sometimes removes dependencies from
|
||||
# src/node_modules but fails to add them to the top-level node_modules.
|
||||
# Even if npm correctly hoists the dependencies, the hoisting seems to
|
||||
# confuse tools such as `npm outdated`, `npm update`, and some ESLint
|
||||
# rules.
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: export GIT_HASH to env
|
||||
id: environment
|
||||
run: echo "::set-output name=sha_short::$(git rev-parse --short ${{ github.sha }})"
|
||||
-
|
||||
name: Create settings.json
|
||||
run: cp settings.json.template settings.json
|
||||
-
|
||||
name: Disable import/export rate limiting
|
||||
run: |
|
||||
sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 1000000/' -i settings.json
|
||||
# XXX we should probably run all tests, because plugins could effect their results
|
||||
-
|
||||
name: Remove standard frontend test files, so only plugin tests are run
|
||||
run: rm src/tests/frontend/specs/*
|
||||
-
|
||||
uses: saucelabs/sauce-connect-action@v2.3.6
|
||||
with:
|
||||
username: ${{ secrets.SAUCE_USERNAME }}
|
||||
accessKey: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||
tunnelIdentifier: ${{ steps.sauce_strings.outputs.tunnel_id }}
|
||||
-
|
||||
name: Run the frontend tests
|
||||
shell: bash
|
||||
env:
|
||||
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
|
||||
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||
SAUCE_NAME: ${{ steps.sauce_strings.outputs.name }}
|
||||
TRAVIS_JOB_NUMBER: ${{ steps.sauce_strings.outputs.tunnel_id }}
|
||||
GIT_HASH: ${{ steps.environment.outputs.sha_short }}
|
||||
run: |
|
||||
src/tests/frontend/travis/runner.sh
|
||||
name: playwright-report-${{ matrix.node }}-webkit
|
||||
path: src/playwright-report/
|
||||
retention-days: 30
|
||||
|
||||
|
||||
|
||||
|
|
7
.github/workflows/lint-package-lock.yml
vendored
7
.github/workflows/lint-package-lock.yml
vendored
|
@ -23,13 +23,6 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install lockfile-lint
|
||||
run: npm install --no-save lockfile-lint --legacy-peer-deps
|
||||
-
|
||||
name: Run lockfile-lint on package-lock.json
|
||||
run: >
|
||||
|
|
80
.github/workflows/load-test.yml
vendored
80
.github/workflows/load-test.yml
vendored
|
@ -23,16 +23,30 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installDeps.sh
|
||||
run: bin/installDeps.sh
|
||||
-
|
||||
name: Install etherpad-load-test
|
||||
run: sudo npm install -g etherpad-load-test
|
||||
run: sudo npm install -g etherpad-load-test-socket-io
|
||||
-
|
||||
name: Run load test
|
||||
run: src/tests/frontend/travis/runnerLoadTest.sh 25 50
|
||||
|
@ -53,19 +67,33 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
-
|
||||
name: Install etherpad-load-test
|
||||
run: sudo npm install -g etherpad-load-test
|
||||
run: pnpm install -g etherpad-load-test-socket-io
|
||||
-
|
||||
name: Install etherpad plugins
|
||||
# The --legacy-peer-deps flag is required to work around a bug in npm v7:
|
||||
# https://github.com/npm/cli/issues/2199
|
||||
run: >
|
||||
npm install --no-save --legacy-peer-deps
|
||||
pnpm install --workspace-root
|
||||
ep_align
|
||||
ep_author_hover
|
||||
ep_cursortrace
|
||||
|
@ -89,7 +117,7 @@ jobs:
|
|||
# rules.
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installDeps.sh
|
||||
run: bin/installDeps.sh
|
||||
-
|
||||
name: Run load test
|
||||
run: src/tests/frontend/travis/runnerLoadTest.sh 25 50
|
||||
|
@ -110,16 +138,30 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installDeps.sh
|
||||
run: bin/installDeps.sh
|
||||
-
|
||||
name: Install etherpad-load-test
|
||||
run: sudo npm install -g etherpad-load-test
|
||||
run: sudo npm install -g etherpad-load-test-socket-io
|
||||
-
|
||||
name: Run load test
|
||||
run: src/tests/frontend/travis/runnerLoadTest.sh 5000 5
|
||||
|
|
46
.github/workflows/perform-type-check.yml
vendored
Normal file
46
.github/workflows/perform-type-check.yml
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
name: "Perform type checks"
|
||||
|
||||
# any branch is useful for testing before a PR is submitted
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
|
||||
jobs:
|
||||
performTypeCheck:
|
||||
if: |
|
||||
(github.event_name != 'pull_request')
|
||||
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
|
||||
name: perform type check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: ./bin/installDeps.sh
|
||||
- name: Perform type check
|
||||
working-directory: ./src
|
||||
run: npm run ts-check
|
25
.github/workflows/rate-limit.yml
vendored
25
.github/workflows/rate-limit.yml
vendored
|
@ -23,17 +23,30 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
-
|
||||
name: docker network
|
||||
run: docker network create --subnet=172.23.42.0/16 ep_net
|
||||
-
|
||||
name: build docker image
|
||||
run: |
|
||||
docker build -f Dockerfile -t epl-debian-slim .
|
||||
docker build -f Dockerfile -t epl-debian-slim --build-arg NODE_ENV=develop .
|
||||
docker build -f src/tests/ratelimit/Dockerfile.nginx -t nginx-latest .
|
||||
docker build -f src/tests/ratelimit/Dockerfile.anotherip -t anotherip .
|
||||
-
|
||||
|
@ -44,7 +57,7 @@ jobs:
|
|||
docker run --rm --network ep_net --ip 172.23.42.3 --name anotherip -dt anotherip
|
||||
-
|
||||
name: install dependencies and create symlink for ep_etherpad-lite
|
||||
run: src/bin/installDeps.sh
|
||||
run: bin/installDeps.sh
|
||||
-
|
||||
name: run rate limit test
|
||||
run: |
|
||||
|
|
|
@ -29,14 +29,11 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install Etherpad plugins
|
||||
# The --legacy-peer-deps flag is required to work around a bug in npm
|
||||
# v7: https://github.com/npm/cli/issues/2199
|
||||
# Important: Installer for old master which does not have pnpm right now
|
||||
# The --legacy-peer-deps flag is required to work around a bug in npm v7:
|
||||
# https://github.com/npm/cli/issues/2199
|
||||
run: >
|
||||
npm install --no-save --legacy-peer-deps
|
||||
ep_align
|
||||
|
@ -52,15 +49,6 @@ jobs:
|
|||
ep_spellcheck
|
||||
ep_subscript_and_superscript
|
||||
ep_table_of_contents
|
||||
# Etherpad core dependencies must be installed after installing the
|
||||
# plugin's dependencies, otherwise npm will try to hoist common
|
||||
# dependencies by removing them from src/node_modules and installing them
|
||||
# in the top-level node_modules. As of v6.14.10, npm's hoist logic appears
|
||||
# to be buggy, because it sometimes removes dependencies from
|
||||
# src/node_modules but fails to add them to the top-level node_modules.
|
||||
# Even if npm correctly hoists the dependencies, the hoisting seems to
|
||||
# confuse tools such as `npm outdated`, `npm update`, and some ESLint
|
||||
# rules.
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installDeps.sh
|
||||
|
@ -82,18 +70,44 @@ jobs:
|
|||
# For pull requests, ${GITHUB_SHA} is the automatically generated merge
|
||||
# commit that merges the PR's source branch to its destination branch.
|
||||
run: git checkout "${GITHUB_SHA}"
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
-
|
||||
name: Install libreoffice
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.4.2
|
||||
with:
|
||||
packages: libreoffice libreoffice-pdfimport
|
||||
version: 1.0
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installDeps.sh
|
||||
run: bin/installDeps.sh
|
||||
-
|
||||
name: Run the backend tests
|
||||
run: cd src && npm test
|
||||
run: pnpm test
|
||||
-
|
||||
name: Install Cypress
|
||||
run: cd src && npm install cypress --legacy-peer-deps
|
||||
working-directory: ./src
|
||||
run: pnpm install cypress
|
||||
-
|
||||
name: Run Etherpad & Test Frontend
|
||||
working-directory: ./src
|
||||
run: |
|
||||
node src/node/server.js &
|
||||
pnpm run dev &
|
||||
curl --connect-timeout 10 --max-time 20 --retry 5 --retry-delay 10 --retry-max-time 60 --retry-connrefused http://127.0.0.1:9001/p/test
|
||||
./src/node_modules/cypress/bin/cypress run --config-file src/tests/frontend/cypress/cypress.config.js
|
||||
./node_modules/cypress/bin/cypress run --config-file tests/frontend/cypress/cypress.config.js
|
||||
|
|
72
.github/workflows/windows.yml
vendored
72
.github/workflows/windows.yml
vendored
|
@ -28,23 +28,37 @@ jobs:
|
|||
-
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
node-version: 21
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
shell: msys2 {0}
|
||||
run: src/bin/installDeps.sh
|
||||
run: bin/installDeps.sh
|
||||
-
|
||||
name: Run the backend tests
|
||||
shell: msys2 {0}
|
||||
run: cd src && npm test
|
||||
run: cd src && pnpm test
|
||||
-
|
||||
name: Build the .zip
|
||||
shell: msys2 {0}
|
||||
run: src/bin/buildForWindows.sh
|
||||
run: bin/buildForWindows.sh
|
||||
-
|
||||
name: Archive production artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
|
@ -75,9 +89,9 @@ jobs:
|
|||
run: 7z x etherpad-win.zip -oetherpad-zip
|
||||
-
|
||||
name: Create installer
|
||||
uses: joncloud/makensis-action@v3.7
|
||||
uses: joncloud/makensis-action@v4.1
|
||||
with:
|
||||
script-file: 'src/bin/nsis/etherpad.nsi'
|
||||
script-file: 'bin/nsis/etherpad.nsi'
|
||||
-
|
||||
name: Archive production artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
|
@ -109,27 +123,43 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
etherpad/src/package-lock.json
|
||||
etherpad/src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install Cypress
|
||||
run: cd etherpad && cd src && npm install cypress --legacy-peer-deps
|
||||
- uses: pnpm/action-setup@v3
|
||||
name: Install pnpm
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Only install direct dependencies
|
||||
run: pnpm config set auto-install-peers false
|
||||
- name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: .\bin\installOnWindows.bat
|
||||
working-directory: etherpad
|
||||
-
|
||||
name: Run Etherpad
|
||||
working-directory: etherpad/src
|
||||
run: |
|
||||
cd etherpad
|
||||
node node_modules\ep_etherpad-lite\node\server.js &
|
||||
pnpm install cypress
|
||||
.\node_modules\.bin\cypress.cmd install --force
|
||||
pnpm run prod &
|
||||
curl --connect-timeout 10 --max-time 20 --retry 5 --retry-delay 10 --retry-max-time 60 --retry-connrefused http://127.0.0.1:9001/p/test
|
||||
src\node_modules\cypress\bin\cypress run --config-file src\tests\frontendcypress\cypress.config.js
|
||||
pnpm exec cypress run --config-file ./tests/frontend/cypress/cypress.config.js
|
||||
# On release, upload windows zip to GitHub release tab
|
||||
-
|
||||
name: Rename to etherpad-lite-win.zip
|
||||
shell: powershell
|
||||
run: mv etherpad-win.zip etherpad-lite-win.zip
|
||||
- name: upload binaries to release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: ${{startsWith(github.ref, 'refs/tags/v') }}
|
||||
with:
|
||||
files: etherpad-lite-win.zip
|
||||
|
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -6,6 +6,7 @@ node_modules
|
|||
APIKEY.txt
|
||||
SESSIONKEY.txt
|
||||
var/dirty.db
|
||||
.env
|
||||
*~
|
||||
*.patch
|
||||
npm-debug.log
|
||||
|
@ -22,3 +23,7 @@ out/
|
|||
/src/bin/etherpad-1.deb
|
||||
/src/bin/node.exe
|
||||
plugin_packages
|
||||
/src/templates/admin
|
||||
/src/test-results
|
||||
playwright-report
|
||||
state.json
|
||||
|
|
1
.npmrc
Normal file
1
.npmrc
Normal file
|
@ -0,0 +1 @@
|
|||
auto-install-peers=false
|
32
.travis.yml
32
.travis.yml
|
@ -55,7 +55,7 @@ jobs:
|
|||
- *set_loglevel_warn
|
||||
- *enable_admin_tests
|
||||
- "src/tests/frontend/travis/sauce_tunnel.sh"
|
||||
- "src/bin/installDeps.sh"
|
||||
- "bin/installDeps.sh"
|
||||
- "export GIT_HASH=$(git rev-parse --verify --short HEAD)"
|
||||
script:
|
||||
- "./src/tests/frontend/travis/runner.sh"
|
||||
|
@ -63,22 +63,22 @@ jobs:
|
|||
install:
|
||||
- *install_libreoffice
|
||||
- *set_loglevel_warn
|
||||
- "src/bin/installDeps.sh"
|
||||
- "cd src && npm install && cd -"
|
||||
- "bin/installDeps.sh"
|
||||
- "cd src && pnpm install && cd -"
|
||||
script:
|
||||
- "cd src && npm test"
|
||||
- "cd src && pnpm test"
|
||||
- name: "Test the Dockerfile"
|
||||
install:
|
||||
- "cd src && npm install && cd -"
|
||||
- "cd src && pnpm install && cd -"
|
||||
script:
|
||||
- "docker build -t etherpad:test ."
|
||||
- "docker run -d -p 9001:9001 etherpad:test && sleep 3"
|
||||
- "cd src && npm run test-container"
|
||||
- "cd src && pnpm run test-container"
|
||||
- name: "Load test Etherpad without Plugins"
|
||||
install:
|
||||
- *set_loglevel_warn
|
||||
- "src/bin/installDeps.sh"
|
||||
- "cd src && npm install && cd -"
|
||||
- "bin/installDeps.sh"
|
||||
- "cd src && pnpm install && cd -"
|
||||
- "npm install -g etherpad-load-test"
|
||||
script:
|
||||
- "src/tests/frontend/travis/runnerLoadTest.sh"
|
||||
|
@ -90,7 +90,7 @@ jobs:
|
|||
- *set_loglevel_warn
|
||||
- *enable_admin_tests
|
||||
- "src/tests/frontend/travis/sauce_tunnel.sh"
|
||||
- "src/bin/installDeps.sh"
|
||||
- "bin/installDeps.sh"
|
||||
- "rm src/tests/frontend/specs/*"
|
||||
- *install_plugins
|
||||
- "export GIT_HASH=$(git rev-parse --verify --short HEAD)"
|
||||
|
@ -105,22 +105,22 @@ jobs:
|
|||
install:
|
||||
- *install_libreoffice
|
||||
- *set_loglevel_warn
|
||||
- "src/bin/installDeps.sh"
|
||||
- "bin/installDeps.sh"
|
||||
- *install_plugins
|
||||
- "cd src && npm install && cd -"
|
||||
- "cd src && pnpm install && cd -"
|
||||
script:
|
||||
- "cd src && npm test"
|
||||
- "cd src && pnpm test"
|
||||
- name: "Test the Dockerfile"
|
||||
install:
|
||||
- "cd src && npm install && cd -"
|
||||
- "cd src && pnpm install && cd -"
|
||||
script:
|
||||
- "docker build -t etherpad:test ."
|
||||
- "docker run -d -p 9001:9001 etherpad:test && sleep 3"
|
||||
- "cd src && npm run test-container"
|
||||
- "cd src && pnpm run test-container"
|
||||
- name: "Load test Etherpad with Plugins"
|
||||
install:
|
||||
- *set_loglevel_warn
|
||||
- "src/bin/installDeps.sh"
|
||||
- "bin/installDeps.sh"
|
||||
- *install_plugins
|
||||
- "cd src && npm install && cd -"
|
||||
- "npm install -g etherpad-load-test"
|
||||
|
@ -135,7 +135,7 @@ jobs:
|
|||
- "docker run -p 8081:80 --rm --network ep_net --ip 172.23.42.1 -d nginx-latest"
|
||||
- "docker run --name etherpad-docker -p 9000:9001 --rm --network ep_net --ip 172.23.42.2 -e 'TRUST_PROXY=true' epl-debian-slim &"
|
||||
- "docker run --rm --network ep_net --ip 172.23.42.3 --name anotherip -dt anotherip"
|
||||
- "./src/bin/installDeps.sh"
|
||||
- "./bin/installDeps.sh"
|
||||
script:
|
||||
- "cd src/tests/ratelimit && bash testlimits.sh"
|
||||
|
||||
|
|
24
CHANGELOG.md
24
CHANGELOG.md
|
@ -1,3 +1,27 @@
|
|||
# 2.0.0
|
||||
|
||||
|
||||
### Compatibility changes
|
||||
|
||||
- Socket io has been updated to 4.7.5. This means that the json.send function won't work anymore and needs to be changed to .emit('message', myObj)
|
||||
- Deprecating npm version 6 in favor of pnpm: We have made the decision to switch to the well established pnpm (https://pnpm.io/). It works by symlinking dependencies into a global directory allowing you to have a cleaner and more reliable environment.
|
||||
- Introducing Typescript to the Etherpad core: Etherpad core logic has been rewritten in Typescript allowing for compiler checking of errors.
|
||||
- Rewritten Admin Panel: The Admin panel has been rewritten in React and now features a more pleasant user experience. It now also features an integrated pad searching with sorting functionality.
|
||||
|
||||
### Notable enhancements and fixes
|
||||
|
||||
* Bugfixes
|
||||
- Live Plugin Manager: The live plugin manager caused problems when a plugin had depdendencies defined. This issue is now resolved.
|
||||
|
||||
* Enhancements
|
||||
- pnpm Workspaces: In addition to pnpm we introduced workspaces. A clean way to manage multiple bounded contexts like the admin panel or the bin folder.
|
||||
- Bin folder: The bin folder has been moved from the src folder to the root folder. This change was necessary as the contained scripts do not represent core functionality of the user.
|
||||
- Starting Etherpad: Etherpad can now be started with a single command: `pnpm run prod` in the root directory.
|
||||
- Installing Etherpad: Etherpad no longer symlinks itself in the root directory. This is now also taken care by pnpm, and it just creates a node_modules folder with the src directory`s ep_etherpad-lite folder
|
||||
- Plugins can now be installed simply via the command: `pnpm run install-plugins first-plugin second-plugin` or if you want to install from path you can do:
|
||||
`pnpm run install-plugins --path ../path-to-plugin`
|
||||
|
||||
|
||||
# 1.9.7
|
||||
|
||||
### Notable enhancements and fixes
|
||||
|
|
73
Dockerfile
73
Dockerfile
|
@ -4,9 +4,21 @@
|
|||
#
|
||||
# Author: muxator
|
||||
|
||||
FROM node:lts-alpine
|
||||
FROM node:alpine as adminBuild
|
||||
|
||||
WORKDIR /opt/etherpad-lite
|
||||
COPY ./admin ./admin
|
||||
RUN cd ./admin && npm install -g pnpm && pnpm install && pnpm run build --outDir ./dist
|
||||
|
||||
|
||||
FROM node:alpine as build
|
||||
LABEL maintainer="Etherpad team, https://github.com/ether/etherpad-lite"
|
||||
|
||||
# Set these arguments when building the image from behind a proxy
|
||||
ARG http_proxy=
|
||||
ARG https_proxy=
|
||||
ARG no_proxy=
|
||||
|
||||
ARG TIMEZONE=
|
||||
|
||||
RUN \
|
||||
|
@ -44,11 +56,6 @@ ARG INSTALL_ABIWORD=
|
|||
# INSTALL_LIBREOFFICE=true
|
||||
ARG INSTALL_SOFFICE=
|
||||
|
||||
# By default, Etherpad container is built and run in "production" mode. This is
|
||||
# leaner (development dependencies are not installed) and runs faster (among
|
||||
# other things, assets are minified & compressed).
|
||||
ENV NODE_ENV=production
|
||||
ENV ETHERPAD_PRODUCTION=true
|
||||
# Install dependencies required for modifying access.
|
||||
RUN apk add shadow bash
|
||||
# Follow the principle of least privilege: run as unprivileged user.
|
||||
|
@ -63,8 +70,6 @@ ARG EP_UID=5001
|
|||
ARG EP_GID=0
|
||||
ARG EP_SHELL=
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
RUN groupadd --system ${EP_GID:+--gid "${EP_GID}" --non-unique} etherpad && \
|
||||
useradd --system ${EP_UID:+--uid "${EP_UID}" --non-unique} --gid etherpad \
|
||||
${EP_HOME:+--home-dir "${EP_HOME}"} --create-home \
|
||||
|
@ -77,10 +82,11 @@ RUN mkdir -p "${EP_DIR}" && chown etherpad:etherpad "${EP_DIR}"
|
|||
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=863199
|
||||
RUN \
|
||||
mkdir -p /usr/share/man/man1 && \
|
||||
npm install npm@6 -g && \
|
||||
npm install pnpm -g && \
|
||||
apk update && apk upgrade && \
|
||||
apk add \
|
||||
ca-certificates \
|
||||
curl \
|
||||
git \
|
||||
${INSTALL_ABIWORD:+abiword abiword-plugin-command} \
|
||||
${INSTALL_SOFFICE:+libreoffice openjdk8-jre libreoffice-common}
|
||||
|
@ -89,32 +95,45 @@ USER etherpad
|
|||
|
||||
WORKDIR "${EP_DIR}"
|
||||
|
||||
COPY --chown=etherpad:etherpad ./ ./
|
||||
# etherpads version feature requires this. Only copy what is really needed
|
||||
COPY --chown=etherpad:etherpad ./.git/HEAD ./.git/HEAD
|
||||
COPY --chown=etherpad:etherpad ./.git/refs ./.git/refs
|
||||
COPY --chown=etherpad:etherpad ${SETTINGS} ./settings.json
|
||||
COPY --chown=etherpad:etherpad ./var ./var
|
||||
COPY --chown=etherpad:etherpad ./bin ./bin
|
||||
COPY --chown=etherpad:etherpad ./pnpm-workspace.yaml ./package.json ./
|
||||
|
||||
FROM build as development
|
||||
|
||||
COPY --chown=etherpad:etherpad ./src/package.json .npmrc ./src/pnpm-lock.yaml ./src/
|
||||
COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/admin/dist ./src/templates/admin
|
||||
|
||||
RUN bin/installDeps.sh && \
|
||||
{ [ -z "${ETHERPAD_PLUGINS}" ] || pnpm run install-plugins ${ETHERPAD_PLUGINS}; }
|
||||
|
||||
FROM build as production
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV ETHERPAD_PRODUCTION=true
|
||||
|
||||
COPY --chown=etherpad:etherpad ./src ./src
|
||||
COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/admin/dist ./src/templates/admin
|
||||
|
||||
RUN bin/installDeps.sh && rm -rf ~/.npm && \
|
||||
{ [ -z "${ETHERPAD_PLUGINS}" ] || pnpm run install-plugins ${ETHERPAD_PLUGINS}; }
|
||||
|
||||
# Plugins must be installed before installing Etherpad's dependencies, otherwise
|
||||
# npm will try to hoist common dependencies by removing them from
|
||||
# src/node_modules and installing them in the top-level node_modules. As of
|
||||
# v6.14.10, npm's hoist logic appears to be buggy, because it sometimes removes
|
||||
# dependencies from src/node_modules but fails to add them to the top-level
|
||||
# node_modules. Even if npm correctly hoists the dependencies, the hoisting
|
||||
# seems to confuse tools such as `npm outdated`, `npm update`, and some ESLint
|
||||
# rules.
|
||||
RUN { [ -z "${ETHERPAD_PLUGINS}" ] || \
|
||||
npm install --no-save --legacy-peer-deps ${ETHERPAD_PLUGINS}; } && \
|
||||
src/bin/installDeps.sh && \
|
||||
rm -rf ~/.npm
|
||||
|
||||
# Copy the configuration file.
|
||||
COPY --chown=etherpad:etherpad ${SETTINGS} "${EP_DIR}"/settings.json
|
||||
|
||||
# Fix group permissions
|
||||
RUN chmod -R g=u .
|
||||
# Note: For some reason increases image size from 257 to 334.
|
||||
# RUN chmod -R g=u .
|
||||
|
||||
USER root
|
||||
RUN cd src && npm link
|
||||
USER etherpad
|
||||
|
||||
HEALTHCHECK --interval=20s --timeout=3s CMD ["etherpad-healthcheck"]
|
||||
HEALTHCHECK --interval=5s --timeout=3s \
|
||||
CMD curl --silent http://localhost:9001/health | grep -E "pass|ok|up" > /dev/null || exit 1
|
||||
|
||||
EXPOSE 9001
|
||||
CMD ["etherpad"]
|
||||
CMD ["pnpm", "run", "prod"]
|
||||
|
|
26
README.md
26
README.md
|
@ -56,7 +56,7 @@ Install the latest Node.js LTS per [official install instructions](https://githu
|
|||
```sh
|
||||
git clone --branch master https://github.com/ether/etherpad-lite.git &&
|
||||
cd etherpad-lite &&
|
||||
src/bin/run.sh
|
||||
bin/run.sh
|
||||
```
|
||||
|
||||
#### Manual install
|
||||
|
@ -70,10 +70,10 @@ You'll need Git and [Node.js](https://nodejs.org/) installed.
|
|||
https://github.com/ether/etherpad-lite.git`
|
||||
3. Change into the new directory containing the cloned source code: `cd
|
||||
etherpad-lite`
|
||||
4. Run `src/bin/run.sh` and open http://127.0.0.1:9001 in your browser.
|
||||
4. Run `bin/run.sh` and open http://127.0.0.1:9001 in your browser.
|
||||
|
||||
To update to the latest released version, execute `git pull origin`. The next
|
||||
start with `src/bin/run.sh` will update the dependencies.
|
||||
start with `bin/run.sh` will update the dependencies.
|
||||
|
||||
### Windows
|
||||
|
||||
|
@ -98,17 +98,17 @@ git.
|
|||
* or `git clone --branch master
|
||||
https://github.com/ether/etherpad-lite.git`
|
||||
2. With a "Run as administrator" command prompt execute
|
||||
`src\bin\installOnWindows.bat`
|
||||
`bin\installOnWindows.bat`
|
||||
|
||||
Now, run `start.bat` and open http://localhost:9001 in your browser.
|
||||
|
||||
Update to the latest version with `git pull origin`, then run
|
||||
`src\bin\installOnWindows.bat`, again.
|
||||
`bin\installOnWindows.bat`, again.
|
||||
|
||||
If cloning to a subdirectory within another project, you may need to do the
|
||||
following:
|
||||
|
||||
1. Start the server manually (e.g. `node src/node/server.js`)
|
||||
1. Start the server manually (e.g. `node src/node/server.ts`)
|
||||
2. Edit the db `filename` in `settings.json` to the relative directory with
|
||||
the file (e.g. `application/lib/etherpad-lite/var/dirty.db`)
|
||||
3. Add auto-generated files to the main project `.gitignore`
|
||||
|
@ -139,9 +139,7 @@ Alternatively, you can install plugins from the command line:
|
|||
|
||||
```sh
|
||||
cd /path/to/etherpad-lite
|
||||
# The `--no-save` and `--legacy-peer-deps` arguments are necessary to work
|
||||
# around npm quirks.
|
||||
npm install --no-save --legacy-peer-deps ep_${plugin_name}
|
||||
pnpm run install-plugins ep_${plugin_name}
|
||||
```
|
||||
|
||||
Also see [the plugin wiki
|
||||
|
@ -153,7 +151,7 @@ Run the following command in your Etherpad folder to get all of the features
|
|||
visible in the above demo gif:
|
||||
|
||||
```sh
|
||||
npm install --no-save --legacy-peer-deps \
|
||||
pnpm run install-plugins \
|
||||
ep_align \
|
||||
ep_comments_page \
|
||||
ep_embedded_hyperlinks2 \
|
||||
|
@ -182,7 +180,7 @@ following plugins:
|
|||
### Tweak the settings
|
||||
|
||||
You can modify the settings in `settings.json`. If you need to handle multiple
|
||||
settings files, you can pass the path to a settings file to `src/bin/run.sh`
|
||||
settings files, you can pass the path to a settings file to `bin/run.sh`
|
||||
using the `-s|--settings` option: this allows you to run multiple Etherpad
|
||||
instances from the same installation. Similarly, `--credentials` can be used to
|
||||
give a settings override file, `--apikey` to give a different APIKEY.txt file
|
||||
|
@ -226,11 +224,11 @@ Documentation can be found in `doc/`.
|
|||
|
||||
### Things you should know
|
||||
|
||||
You can debug Etherpad using `src/bin/debugRun.sh`.
|
||||
You can debug Etherpad using `bin/debugRun.sh`.
|
||||
|
||||
You can run Etherpad quickly launching `src/bin/fastRun.sh`. It's convenient for
|
||||
You can run Etherpad quickly launching `bin/fastRun.sh`. It's convenient for
|
||||
developers and advanced users. Be aware that it will skip the dependencies
|
||||
update, so remember to run `src/bin/installDeps.sh` after installing a new
|
||||
update, so remember to run `bin/installDeps.sh` after installing a new
|
||||
dependency or upgrading version.
|
||||
|
||||
If you want to find out how Etherpad's `Easysync` works (the library that makes
|
||||
|
|
18
admin/.eslintrc.cjs
Normal file
18
admin/.eslintrc.cjs
Normal file
|
@ -0,0 +1,18 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
}
|
24
admin/.gitignore
vendored
Normal file
24
admin/.gitignore
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
30
admin/README.md
Normal file
30
admin/README.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
# React + TypeScript + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
|
||||
## Expanding the ESLint configuration
|
||||
|
||||
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||
|
||||
- Configure the top-level `parserOptions` property like this:
|
||||
|
||||
```js
|
||||
export default {
|
||||
// other rules...
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
project: ['./tsconfig.json', './tsconfig.node.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
|
||||
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
|
||||
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
|
14
admin/index.html
Normal file
14
admin/index.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Etherpad Admin Dashboard</title>
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<div id="loading"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
39
admin/package.json
Normal file
39
admin/package.json
Normal file
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"name": "admin",
|
||||
"private": true,
|
||||
"version": "2.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@radix-ui/react-dialog": "^1.0.5",
|
||||
"@radix-ui/react-toast": "^1.1.5",
|
||||
"i18next": "^23.10.1",
|
||||
"i18next-browser-languagedetector": "^7.2.0",
|
||||
"lucide-react": "^0.356.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.51.0",
|
||||
"react-i18next": "^14.1.0",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"zustand": "^4.5.2",
|
||||
"@types/react": "^18.2.56",
|
||||
"@types/react-dom": "^18.2.19",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.2",
|
||||
"@typescript-eslint/parser": "^7.0.2",
|
||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.5",
|
||||
"socket.io-client": "^4.7.4",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.1.4",
|
||||
"vite-plugin-static-copy": "^1.0.1",
|
||||
"vite-plugin-svgr": "^4.2.0"
|
||||
}
|
||||
}
|
BIN
admin/public/Karla-Bold.ttf
Normal file
BIN
admin/public/Karla-Bold.ttf
Normal file
Binary file not shown.
BIN
admin/public/Karla-BoldItalic.ttf
Normal file
BIN
admin/public/Karla-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
admin/public/Karla-ExtraBold.ttf
Normal file
BIN
admin/public/Karla-ExtraBold.ttf
Normal file
Binary file not shown.
BIN
admin/public/Karla-ExtraBoldItalic.ttf
Normal file
BIN
admin/public/Karla-ExtraBoldItalic.ttf
Normal file
Binary file not shown.
BIN
admin/public/Karla-ExtraLight.ttf
Normal file
BIN
admin/public/Karla-ExtraLight.ttf
Normal file
Binary file not shown.
BIN
admin/public/Karla-ExtraLightItalic.ttf
Normal file
BIN
admin/public/Karla-ExtraLightItalic.ttf
Normal file
Binary file not shown.
BIN
admin/public/Karla-Italic.ttf
Normal file
BIN
admin/public/Karla-Italic.ttf
Normal file
Binary file not shown.
BIN
admin/public/Karla-Light.ttf
Normal file
BIN
admin/public/Karla-Light.ttf
Normal file
Binary file not shown.
BIN
admin/public/Karla-LightItalic.ttf
Normal file
BIN
admin/public/Karla-LightItalic.ttf
Normal file
Binary file not shown.
BIN
admin/public/Karla-Medium.ttf
Normal file
BIN
admin/public/Karla-Medium.ttf
Normal file
Binary file not shown.
BIN
admin/public/Karla-MediumItalic.ttf
Normal file
BIN
admin/public/Karla-MediumItalic.ttf
Normal file
Binary file not shown.
BIN
admin/public/Karla-Regular.ttf
Normal file
BIN
admin/public/Karla-Regular.ttf
Normal file
Binary file not shown.
BIN
admin/public/Karla-SemiBold.ttf
Normal file
BIN
admin/public/Karla-SemiBold.ttf
Normal file
Binary file not shown.
BIN
admin/public/Karla-SemiBoldItalic.ttf
Normal file
BIN
admin/public/Karla-SemiBoldItalic.ttf
Normal file
Binary file not shown.
28
admin/public/ep_admin_pads/ar.json
Normal file
28
admin/public/ep_admin_pads/ar.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Meno25",
|
||||
"محمد أحمد عبد الفتاح"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "فعل",
|
||||
"ep_adminpads2_autoupdate-label": "التحديث التلقائي على تغييرات الوسادة",
|
||||
"ep_adminpads2_autoupdate.title": "لتمكين أو تعطيل التحديثات التلقائية للاستعلام الحالي.",
|
||||
"ep_adminpads2_confirm": "هل تريد حقًا حذف الوسادة {{padID}}؟",
|
||||
"ep_adminpads2_delete.value": "حذف",
|
||||
"ep_adminpads2_last-edited": "آخر تعديل",
|
||||
"ep_adminpads2_loading": "جارٍ التحميل...",
|
||||
"ep_adminpads2_manage-pads": "إدارة الفوط",
|
||||
"ep_adminpads2_no-results": "لا توجد نتائج.",
|
||||
"ep_adminpads2_pad-user-count": "عدد المستخدمين الوسادة",
|
||||
"ep_adminpads2_padname": "بادنام",
|
||||
"ep_adminpads2_search-box.placeholder": "مصطلح البحث",
|
||||
"ep_adminpads2_search-button.value": "بحث",
|
||||
"ep_adminpads2_search-done": "اكتمل البحث",
|
||||
"ep_adminpads2_search-error-explanation": "واجه الخادم خطأً أثناء البحث عن منصات:",
|
||||
"ep_adminpads2_search-error-title": "فشل في الحصول على قائمة الوسادة",
|
||||
"ep_adminpads2_search-heading": "ابحث عن الفوط",
|
||||
"ep_adminpads2_title": "إدارة الوسادة",
|
||||
"ep_adminpads2_unknown-error": "خطأ غير معروف",
|
||||
"ep_adminpads2_unknown-status": "حالة غير معروفة"
|
||||
}
|
23
admin/public/ep_admin_pads/bn.json
Normal file
23
admin/public/ep_admin_pads/bn.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"আজিজ",
|
||||
"আফতাবুজ্জামান"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "কার্য",
|
||||
"ep_adminpads2_delete.value": "মুছে ফেলুন",
|
||||
"ep_adminpads2_last-edited": "সর্বশেষ সম্পাদিত",
|
||||
"ep_adminpads2_loading": "লোড হচ্ছে...",
|
||||
"ep_adminpads2_manage-pads": "প্যাড পরিচালনা করুন",
|
||||
"ep_adminpads2_no-results": "ফলাফল নেই",
|
||||
"ep_adminpads2_padname": "প্যাডের নাম",
|
||||
"ep_adminpads2_search-button.value": "অনুসন্ধান",
|
||||
"ep_adminpads2_search-done": "অনুসন্ধান সম্পূর্ণ",
|
||||
"ep_adminpads2_search-error-explanation": "প্যাড অনুসন্ধান করার সময় সার্ভার একটি ত্রুটির সম্মুখীন হয়েছে:",
|
||||
"ep_adminpads2_search-error-title": "প্যাডের তালিকা পেতে ব্যর্থ",
|
||||
"ep_adminpads2_search-heading": "প্যাড অনুসন্ধান করুন",
|
||||
"ep_adminpads2_title": "প্যাড প্রশাসন",
|
||||
"ep_adminpads2_unknown-error": "অজানা ত্রুটি",
|
||||
"ep_adminpads2_unknown-status": "অজানা অবস্থা"
|
||||
}
|
27
admin/public/ep_admin_pads/ca.json
Normal file
27
admin/public/ep_admin_pads/ca.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Mguix"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Acció",
|
||||
"ep_adminpads2_autoupdate-label": "Actualització automàtica en cas de canvis de pad",
|
||||
"ep_adminpads2_autoupdate.title": "Activa o desactiva les actualitzacions automàtiques per a la consulta actual.",
|
||||
"ep_adminpads2_confirm": "Esteu segur que voleu suprimir el pad {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Esborrar",
|
||||
"ep_adminpads2_last-edited": "Darrera modificació",
|
||||
"ep_adminpads2_loading": "S’està carregant…",
|
||||
"ep_adminpads2_manage-pads": "Gestiona els pads",
|
||||
"ep_adminpads2_no-results": "No hi ha cap resultat",
|
||||
"ep_adminpads2_pad-user-count": "Nombre d'usuaris de pads",
|
||||
"ep_adminpads2_padname": "Nom del pad",
|
||||
"ep_adminpads2_search-box.placeholder": "Terme de cerca",
|
||||
"ep_adminpads2_search-button.value": "Cercar",
|
||||
"ep_adminpads2_search-done": "Cerca completa",
|
||||
"ep_adminpads2_search-error-explanation": "El servidor ha trobat un error mentre buscava pads:",
|
||||
"ep_adminpads2_search-error-title": "No s'ha pogut obtenir la llista del pad",
|
||||
"ep_adminpads2_search-heading": "Cerca pads",
|
||||
"ep_adminpads2_title": "Administració del pad",
|
||||
"ep_adminpads2_unknown-error": "Error desconegut",
|
||||
"ep_adminpads2_unknown-status": "Estat desconegut"
|
||||
}
|
27
admin/public/ep_admin_pads/cs.json
Normal file
27
admin/public/ep_admin_pads/cs.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Spotter"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Akce",
|
||||
"ep_adminpads2_autoupdate-label": "Automatická aktualizace změn Padu",
|
||||
"ep_adminpads2_autoupdate.title": "Povolí nebo zakáže automatické aktualizace pro aktuální dotaz.",
|
||||
"ep_adminpads2_confirm": "Opravdu chcete odstranit pad {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Smazat",
|
||||
"ep_adminpads2_last-edited": "Naposledy upraveno",
|
||||
"ep_adminpads2_loading": "Načítání…",
|
||||
"ep_adminpads2_manage-pads": "Spravovat pady",
|
||||
"ep_adminpads2_no-results": "Žádné výsledky",
|
||||
"ep_adminpads2_pad-user-count": "Počet uživatelů padu",
|
||||
"ep_adminpads2_padname": "Název padu",
|
||||
"ep_adminpads2_search-box.placeholder": "Hledaný výraz",
|
||||
"ep_adminpads2_search-button.value": "Hledat",
|
||||
"ep_adminpads2_search-done": "Hledání dokončeno",
|
||||
"ep_adminpads2_search-error-explanation": "Při hledání padů došlo k chybě serveru:",
|
||||
"ep_adminpads2_search-error-title": "Seznam padů se nepodařilo získat",
|
||||
"ep_adminpads2_search-heading": "Hledat pady",
|
||||
"ep_adminpads2_title": "Správa Padu",
|
||||
"ep_adminpads2_unknown-error": "Neznámá chyba",
|
||||
"ep_adminpads2_unknown-status": "Neznámý stav"
|
||||
}
|
27
admin/public/ep_admin_pads/cy.json
Normal file
27
admin/public/ep_admin_pads/cy.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Robin Owain"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Gweithred",
|
||||
"ep_adminpads2_autoupdate-label": "Diweddaru newidiadau pad yn otomatig",
|
||||
"ep_adminpads2_autoupdate.title": "Galluogi neu analluogi diweddaru'r ymholiad cyfredol.",
|
||||
"ep_adminpads2_confirm": "Siwr eich bod am ddileu'r pad {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Dileu",
|
||||
"ep_adminpads2_last-edited": "Golygwyd ddiwethaf",
|
||||
"ep_adminpads2_loading": "Wrthi'n llwytho...",
|
||||
"ep_adminpads2_manage-pads": "Rheoli'r padiau",
|
||||
"ep_adminpads2_no-results": "Dim canlyniad",
|
||||
"ep_adminpads2_pad-user-count": "Cyfri defnyddiwr pad",
|
||||
"ep_adminpads2_padname": "Enwpad",
|
||||
"ep_adminpads2_search-box.placeholder": "Term chwilio",
|
||||
"ep_adminpads2_search-button.value": "Chwilio",
|
||||
"ep_adminpads2_search-done": "Wedi gorffen",
|
||||
"ep_adminpads2_search-error-explanation": "Nam ar y gweinydd wrth chwilio'r padiau:",
|
||||
"ep_adminpads2_search-error-title": "Methwyd a chael y rhestr pad",
|
||||
"ep_adminpads2_search-heading": "Chwilio am badiau",
|
||||
"ep_adminpads2_title": "Gweinyddiaeth y pad",
|
||||
"ep_adminpads2_unknown-error": "Nam o ryw fath",
|
||||
"ep_adminpads2_unknown-status": "Statws anhysbys"
|
||||
}
|
14
admin/public/ep_admin_pads/da.json
Normal file
14
admin/public/ep_admin_pads/da.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Saederup92"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Handling",
|
||||
"ep_adminpads2_delete.value": "Slet",
|
||||
"ep_adminpads2_last-edited": "Sidst redigeret",
|
||||
"ep_adminpads2_loading": "Indlæser...",
|
||||
"ep_adminpads2_no-results": "Ingen resultater",
|
||||
"ep_adminpads2_unknown-error": "Ukendt fejl",
|
||||
"ep_adminpads2_unknown-status": "Ukendt status"
|
||||
}
|
32
admin/public/ep_admin_pads/de.json
Normal file
32
admin/public/ep_admin_pads/de.json
Normal file
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Brettchenweber",
|
||||
"Justman10000",
|
||||
"Lorisobi",
|
||||
"SamTV",
|
||||
"Umlaut",
|
||||
"Zunkelty"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Aktion",
|
||||
"ep_adminpads2_autoupdate-label": "Automatisch bei Pad-Änderungen updaten",
|
||||
"ep_adminpads2_autoupdate.title": "Aktiviert oder deaktiviert automatische Aktualisierungen für die aktuelle Abfrage.",
|
||||
"ep_adminpads2_confirm": "Willst du das Pad {{padID}} wirklich löschen?",
|
||||
"ep_adminpads2_delete.value": "Löschen",
|
||||
"ep_adminpads2_last-edited": "Zuletzt bearbeitet",
|
||||
"ep_adminpads2_loading": "Lädt...",
|
||||
"ep_adminpads2_manage-pads": "Pads verwalten",
|
||||
"ep_adminpads2_no-results": "Keine Ergebnisse",
|
||||
"ep_adminpads2_pad-user-count": "Nutzerzahl des Pads",
|
||||
"ep_adminpads2_padname": "Padname",
|
||||
"ep_adminpads2_search-box.placeholder": "Suchbegriff",
|
||||
"ep_adminpads2_search-button.value": "Suche",
|
||||
"ep_adminpads2_search-done": "Suche vollendet",
|
||||
"ep_adminpads2_search-error-explanation": "Der Server ist bei der Suche nach Pads auf einen Fehler gestoßen:",
|
||||
"ep_adminpads2_search-error-title": "Pad-Liste konnte nicht abgerufen werden",
|
||||
"ep_adminpads2_search-heading": "Nach Pads suchen",
|
||||
"ep_adminpads2_title": "Pad-Verwaltung",
|
||||
"ep_adminpads2_unknown-error": "Unbekannter Fehler",
|
||||
"ep_adminpads2_unknown-status": "Unbekannter Status"
|
||||
}
|
28
admin/public/ep_admin_pads/diq.json
Normal file
28
admin/public/ep_admin_pads/diq.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"1917 Ekim Devrimi",
|
||||
"Mirzali"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Hereketi",
|
||||
"ep_adminpads2_autoupdate-label": "Vurnayışanê pedi otomatik rocane kerê",
|
||||
"ep_adminpads2_autoupdate.title": "Persê mewcudi rê rocaneyışanê otomatika aktiv ke ya zi dewrê ra vecê",
|
||||
"ep_adminpads2_confirm": "Şıma qayılê pedê {{padID}} bıesternê?",
|
||||
"ep_adminpads2_delete.value": "Bestere",
|
||||
"ep_adminpads2_last-edited": "Vurnayışo peyên",
|
||||
"ep_adminpads2_loading": "Bar beno...",
|
||||
"ep_adminpads2_manage-pads": "Pedan idare kerê",
|
||||
"ep_adminpads2_no-results": "Netice çıniyo",
|
||||
"ep_adminpads2_pad-user-count": "Amarê karberanê pedi",
|
||||
"ep_adminpads2_padname": "Padname",
|
||||
"ep_adminpads2_search-box.placeholder": "termê cıgêrayış",
|
||||
"ep_adminpads2_search-button.value": "Cı geyre",
|
||||
"ep_adminpads2_search-done": "Cıgeyrayışi temam",
|
||||
"ep_adminpads2_search-error-explanation": "Server cıgeyrayışê pedan de yew xetaya raşt ame",
|
||||
"ep_adminpads2_search-error-title": "Lista pedi nêgêriye",
|
||||
"ep_adminpads2_search-heading": "Pedan cıgeyrayış",
|
||||
"ep_adminpads2_title": "İdarey pedi",
|
||||
"ep_adminpads2_unknown-error": "Xetaya nêzanıtiye",
|
||||
"ep_adminpads2_unknown-status": "Weziyeto nêzanaye"
|
||||
}
|
27
admin/public/ep_admin_pads/dsb.json
Normal file
27
admin/public/ep_admin_pads/dsb.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Michawiki"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Akcija",
|
||||
"ep_adminpads2_autoupdate-label": "Pśi změnach na zapisniku awtomatiski aktualizěrowaś",
|
||||
"ep_adminpads2_autoupdate.title": "Zmóžnja abo znjemóžnja awtomatiske aktualizacije za aktualne wótpšašowanje.",
|
||||
"ep_adminpads2_confirm": "Cośo napšawdu zapisnik {{padID}} lašowaś?",
|
||||
"ep_adminpads2_delete.value": "Lašowaś",
|
||||
"ep_adminpads2_last-edited": "Slědna změna",
|
||||
"ep_adminpads2_loading": "Zacytujo se...",
|
||||
"ep_adminpads2_manage-pads": "Zapisniki zastojaś",
|
||||
"ep_adminpads2_no-results": "Žedne wuslědki",
|
||||
"ep_adminpads2_pad-user-count": "Licba wužywarjow zapisnika",
|
||||
"ep_adminpads2_padname": "Mě zapisnika",
|
||||
"ep_adminpads2_search-box.placeholder": "Pytańske zapśimjeśe",
|
||||
"ep_adminpads2_search-button.value": "Pytaś",
|
||||
"ep_adminpads2_search-done": "Pytanje dokóńcone",
|
||||
"ep_adminpads2_search-error-explanation": "Serwer jo starcył na zmólku, mjaztym až jo pytał za zapisnikami:",
|
||||
"ep_adminpads2_search-error-title": "Lisćina zapisnikow njedajo se wobstaraś",
|
||||
"ep_adminpads2_search-heading": "Za zapisnikami pytaś",
|
||||
"ep_adminpads2_title": "Zapisnikowa administracija",
|
||||
"ep_adminpads2_unknown-error": "Njeznata zmólka",
|
||||
"ep_adminpads2_unknown-status": "Njeznaty status"
|
||||
}
|
16
admin/public/ep_admin_pads/el.json
Normal file
16
admin/public/ep_admin_pads/el.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Norhorn"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_delete.value": "Διαγραφή",
|
||||
"ep_adminpads2_last-edited": "Τελευταία απεξεργασία",
|
||||
"ep_adminpads2_loading": "Φόρτωση…",
|
||||
"ep_adminpads2_no-results": "Κανένα αποτέλεσμα",
|
||||
"ep_adminpads2_search-box.placeholder": "Αναζήτηση όρων",
|
||||
"ep_adminpads2_search-button.value": "Αναζήτηση",
|
||||
"ep_adminpads2_search-done": "Ολοκλήρωση αναζήτησης",
|
||||
"ep_adminpads2_unknown-error": "Άγνωστο σφάλμα",
|
||||
"ep_adminpads2_unknown-status": "Άγνωστη κατάσταση"
|
||||
}
|
22
admin/public/ep_admin_pads/en.json
Normal file
22
admin/public/ep_admin_pads/en.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"ep_adminpads2_action": "Action",
|
||||
"ep_adminpads2_autoupdate-label": "Auto-update on pad changes",
|
||||
"ep_adminpads2_autoupdate.title": "Enables or disables automatic updates for the current query.",
|
||||
"ep_adminpads2_confirm": "Do you really want to delete the pad {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Delete",
|
||||
"ep_adminpads2_last-edited": "Last edited",
|
||||
"ep_adminpads2_loading": "Loading…",
|
||||
"ep_adminpads2_manage-pads": "Manage pads",
|
||||
"ep_adminpads2_no-results": "No results",
|
||||
"ep_adminpads2_pad-user-count": "Pad user count",
|
||||
"ep_adminpads2_padname": "Padname",
|
||||
"ep_adminpads2_search-box.placeholder": "Search term",
|
||||
"ep_adminpads2_search-button.value": "Search",
|
||||
"ep_adminpads2_search-done": "Search complete",
|
||||
"ep_adminpads2_search-error-explanation": "The server encountered an error while searching for pads:",
|
||||
"ep_adminpads2_search-error-title": "Failed to get pad list",
|
||||
"ep_adminpads2_search-heading": "Search for pads",
|
||||
"ep_adminpads2_title": "Pad administration",
|
||||
"ep_adminpads2_unknown-error": "Unknown error",
|
||||
"ep_adminpads2_unknown-status": "Unknown status"
|
||||
}
|
27
admin/public/ep_admin_pads/eu.json
Normal file
27
admin/public/ep_admin_pads/eu.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Izendegi"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Ekintza",
|
||||
"ep_adminpads2_autoupdate-label": "Automatikoki eguneratu pad-aren aldaketak daudenean",
|
||||
"ep_adminpads2_autoupdate.title": "Oraingo kontsultarako eguneratze automatikoak gaitu edo desgaitzen du.",
|
||||
"ep_adminpads2_confirm": "Ziur zaude {{padID}} pad-a ezabatu nahi duzula?",
|
||||
"ep_adminpads2_delete.value": "Ezabatu",
|
||||
"ep_adminpads2_last-edited": "Azkenengoz editatua",
|
||||
"ep_adminpads2_loading": "Kargatzen...",
|
||||
"ep_adminpads2_manage-pads": "Kudeatu pad-ak",
|
||||
"ep_adminpads2_no-results": "Emaitzarik ez",
|
||||
"ep_adminpads2_pad-user-count": "Pad-erabiltzaile kopurua",
|
||||
"ep_adminpads2_padname": "Pad-izena",
|
||||
"ep_adminpads2_search-box.placeholder": "Bilaketa testua",
|
||||
"ep_adminpads2_search-button.value": "Bilatu",
|
||||
"ep_adminpads2_search-done": "Bilaketa osatu da",
|
||||
"ep_adminpads2_search-error-explanation": "Zerbitzariak errore bat izan du pad-ak bilatzean:",
|
||||
"ep_adminpads2_search-error-title": "Pad-zerrenda eskuratzeak huts egin du",
|
||||
"ep_adminpads2_search-heading": "Bilatu pad-ak",
|
||||
"ep_adminpads2_title": "Pad-en kudeaketa",
|
||||
"ep_adminpads2_unknown-error": "Errore ezezaguna",
|
||||
"ep_adminpads2_unknown-status": "Egoera ezezaguna"
|
||||
}
|
27
admin/public/ep_admin_pads/ff.json
Normal file
27
admin/public/ep_admin_pads/ff.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Ibrahima Malal Sarr"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Baɗal",
|
||||
"ep_adminpads2_autoupdate-label": "Hesɗitin e jaajol tuma baylagol faɗo",
|
||||
"ep_adminpads2_autoupdate.title": "Hurminat walla daaƴa kesɗitine jaaje wonannde ɗaɓɓitannde wonaande.",
|
||||
"ep_adminpads2_confirm": "Aɗa yiɗi e jaati momtude faɗo {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Momtu",
|
||||
"ep_adminpads2_last-edited": "Taƴtaa sakket",
|
||||
"ep_adminpads2_loading": "Nana loowa…",
|
||||
"ep_adminpads2_manage-pads": "Toppito paɗe",
|
||||
"ep_adminpads2_no-results": "Alaa njaltudi",
|
||||
"ep_adminpads2_pad-user-count": "Limoore huutorɓe faɗo",
|
||||
"ep_adminpads2_padname": "Innde faɗo",
|
||||
"ep_adminpads2_search-box.placeholder": "Helmere njiilaw",
|
||||
"ep_adminpads2_search-button.value": "Yiylo",
|
||||
"ep_adminpads2_search-done": "Njiylaw timmii",
|
||||
"ep_adminpads2_search-error-explanation": "Sarworde ndee hawrii e juumre tuma nde yiylotoo faɗo:",
|
||||
"ep_adminpads2_search-error-title": "Horiima heɓde doggol paɗe",
|
||||
"ep_adminpads2_search-heading": "Yiylo paɗe",
|
||||
"ep_adminpads2_title": "Yiylorde paɗe",
|
||||
"ep_adminpads2_unknown-error": "Juumre nde anndaaka",
|
||||
"ep_adminpads2_unknown-status": "Ngonka ka anndaaka"
|
||||
}
|
27
admin/public/ep_admin_pads/fi.json
Normal file
27
admin/public/ep_admin_pads/fi.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Artnay",
|
||||
"Kyykaarme",
|
||||
"MITO",
|
||||
"Maantietäjä",
|
||||
"Yupik"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Toiminto",
|
||||
"ep_adminpads2_delete.value": "Poista",
|
||||
"ep_adminpads2_last-edited": "Viimeksi muokattu",
|
||||
"ep_adminpads2_loading": "Ladataan...",
|
||||
"ep_adminpads2_manage-pads": "Hallitse muistioita",
|
||||
"ep_adminpads2_no-results": "Ei tuloksia",
|
||||
"ep_adminpads2_pad-user-count": "Pad-käyttäjien määrä",
|
||||
"ep_adminpads2_padname": "Muistion nimi",
|
||||
"ep_adminpads2_search-box.placeholder": "Haettava teksti",
|
||||
"ep_adminpads2_search-button.value": "Etsi",
|
||||
"ep_adminpads2_search-done": "Haku valmis",
|
||||
"ep_adminpads2_search-error-explanation": "Palvelimessa tapahtui virhe etsiessään muistioita:",
|
||||
"ep_adminpads2_search-error-title": "Pad-luettelon hakeminen epäonnistui",
|
||||
"ep_adminpads2_search-heading": "Etsi sisältöä",
|
||||
"ep_adminpads2_unknown-error": "Tuntematon virhe",
|
||||
"ep_adminpads2_unknown-status": "Tuntematon tila"
|
||||
}
|
27
admin/public/ep_admin_pads/fr.json
Normal file
27
admin/public/ep_admin_pads/fr.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Verdy p"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Action",
|
||||
"ep_adminpads2_autoupdate-label": "Mise à jour automatique en cas de changements du bloc-notes",
|
||||
"ep_adminpads2_autoupdate.title": "Active ou désactive les mises à jour automatiques pour la requête actuelle.",
|
||||
"ep_adminpads2_confirm": "Voulez-vous vraiment supprimer le bloc-notes {{padID}} ?",
|
||||
"ep_adminpads2_delete.value": "Supprimer",
|
||||
"ep_adminpads2_last-edited": "Dernière modification",
|
||||
"ep_adminpads2_loading": "Chargement en cours...",
|
||||
"ep_adminpads2_manage-pads": "Gérer les bloc-notes",
|
||||
"ep_adminpads2_no-results": "Aucun résultat",
|
||||
"ep_adminpads2_pad-user-count": "Nombre d’utilisateurs du bloc-notes",
|
||||
"ep_adminpads2_padname": "Nom du bloc-notes",
|
||||
"ep_adminpads2_search-box.placeholder": "Terme de recherche",
|
||||
"ep_adminpads2_search-button.value": "Rechercher",
|
||||
"ep_adminpads2_search-done": "Recherche terminée",
|
||||
"ep_adminpads2_search-error-explanation": "Le serveur a rencontré une erreur en cherchant des blocs-notes :",
|
||||
"ep_adminpads2_search-error-title": "Échec d’obtention de la liste de blocs-notes",
|
||||
"ep_adminpads2_search-heading": "Rechercher des blocs-notes",
|
||||
"ep_adminpads2_title": "Administration du bloc-notes",
|
||||
"ep_adminpads2_unknown-error": "Erreur inconnue",
|
||||
"ep_adminpads2_unknown-status": "État inconnu"
|
||||
}
|
27
admin/public/ep_admin_pads/gl.json
Normal file
27
admin/public/ep_admin_pads/gl.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Ghose"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Accións",
|
||||
"ep_adminpads2_autoupdate-label": "Actualización automática dos cambios",
|
||||
"ep_adminpads2_autoupdate.title": "Activa ou desactiva as actualizacións automáticas para a consulta actual.",
|
||||
"ep_adminpads2_confirm": "Tes a certeza de querer eliminar o pad {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Eliminar",
|
||||
"ep_adminpads2_last-edited": "Última edición",
|
||||
"ep_adminpads2_loading": "Cargando…",
|
||||
"ep_adminpads2_manage-pads": "Xestionar pads",
|
||||
"ep_adminpads2_no-results": "Sen resultados",
|
||||
"ep_adminpads2_pad-user-count": "Usuarias neste pad",
|
||||
"ep_adminpads2_padname": "Nome do pad",
|
||||
"ep_adminpads2_search-box.placeholder": "Buscar termo",
|
||||
"ep_adminpads2_search-button.value": "Buscar",
|
||||
"ep_adminpads2_search-done": "Busca completa",
|
||||
"ep_adminpads2_search-error-explanation": "O servidor atopou un fallo cando buscaba pads:",
|
||||
"ep_adminpads2_search-error-title": "Non se obtivo a lista de pads",
|
||||
"ep_adminpads2_search-heading": "Buscar pads",
|
||||
"ep_adminpads2_title": "Administración do pad",
|
||||
"ep_adminpads2_unknown-error": "Erro descoñecido",
|
||||
"ep_adminpads2_unknown-status": "Estado descoñecido"
|
||||
}
|
27
admin/public/ep_admin_pads/he.json
Normal file
27
admin/public/ep_admin_pads/he.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"YaronSh"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "פעולה",
|
||||
"ep_adminpads2_autoupdate-label": "לעדכן אוטומטית כשהמחברת נערכת",
|
||||
"ep_adminpads2_autoupdate.title": "הפעלה או השבתה של עדכונים אוטומטיים לשאילתה הנוכחית.",
|
||||
"ep_adminpads2_confirm": "למחוק את המחברת {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "מחיקה",
|
||||
"ep_adminpads2_last-edited": "עריכה אחרונה",
|
||||
"ep_adminpads2_loading": "בטעינה…",
|
||||
"ep_adminpads2_manage-pads": "ניהול מחברות",
|
||||
"ep_adminpads2_no-results": "אין תוצאות",
|
||||
"ep_adminpads2_pad-user-count": "ספירת משתמשים במחברת",
|
||||
"ep_adminpads2_padname": "שם המחברת",
|
||||
"ep_adminpads2_search-box.placeholder": "הביטוי לחיפוש",
|
||||
"ep_adminpads2_search-button.value": "חיפוש",
|
||||
"ep_adminpads2_search-done": "החיפוש הושלם",
|
||||
"ep_adminpads2_search-error-explanation": "השרת נתקל בשגיאה בעת חיפוש מחברות:",
|
||||
"ep_adminpads2_search-error-title": "קבלת רשימת המחברות נכשלה",
|
||||
"ep_adminpads2_search-heading": "חיפוש אחר מחברות",
|
||||
"ep_adminpads2_title": "ניהול מחברות",
|
||||
"ep_adminpads2_unknown-error": "שגיאה בלתי־ידועה",
|
||||
"ep_adminpads2_unknown-status": "מצב לא ידוע"
|
||||
}
|
27
admin/public/ep_admin_pads/hsb.json
Normal file
27
admin/public/ep_admin_pads/hsb.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Michawiki"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Akcija",
|
||||
"ep_adminpads2_autoupdate-label": "Při změnach na zapisniku awtomatisce aktualizować",
|
||||
"ep_adminpads2_autoupdate.title": "Zmóžnja abo znjemóžnja awtomatiske aktualizacije za aktualne wotprašowanje.",
|
||||
"ep_adminpads2_confirm": "Chceće woprawdźe zapisnik {{padID}} zhašeć?",
|
||||
"ep_adminpads2_delete.value": "Zhašeć",
|
||||
"ep_adminpads2_last-edited": "Poslednja změna",
|
||||
"ep_adminpads2_loading": "Začituje so...",
|
||||
"ep_adminpads2_manage-pads": "Zapisniki rjadować",
|
||||
"ep_adminpads2_no-results": "Žane wuslědki.",
|
||||
"ep_adminpads2_pad-user-count": "Ličba wužiwarjow zapisnika",
|
||||
"ep_adminpads2_padname": "Mjeno zapisnika",
|
||||
"ep_adminpads2_search-box.placeholder": "Pytanske zapřijeće",
|
||||
"ep_adminpads2_search-button.value": "Pytać",
|
||||
"ep_adminpads2_search-done": "Pytanje dokónčene",
|
||||
"ep_adminpads2_search-error-explanation": "Serwer je na zmylk storčił, mjeztym zo je za zapisnikami pytał:",
|
||||
"ep_adminpads2_search-error-title": "Lisćina zapisnikow njeda so wobstarać",
|
||||
"ep_adminpads2_search-heading": "Za zapisnikami pytać",
|
||||
"ep_adminpads2_title": "Zapisnikowa administracija",
|
||||
"ep_adminpads2_unknown-error": "Njeznaty zmylk",
|
||||
"ep_adminpads2_unknown-status": "Njeznaty status"
|
||||
}
|
25
admin/public/ep_admin_pads/hu.json
Normal file
25
admin/public/ep_admin_pads/hu.json
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": []
|
||||
},
|
||||
"ep_adminpads2_action": "Művelet",
|
||||
"ep_adminpads2_autoupdate-label": "Változáskor jegyzetfüzet önműködő frissítése",
|
||||
"ep_adminpads2_autoupdate.title": "Önműködő frissítése az jelenlegi lekérdezéshez be- vagy kikapcsolása.",
|
||||
"ep_adminpads2_confirm": "Biztosan törölni szeretné a(z) {{padID}} jegyzetfüzetet?",
|
||||
"ep_adminpads2_delete.value": "Törlés",
|
||||
"ep_adminpads2_last-edited": "Utoljára szerkesztve",
|
||||
"ep_adminpads2_loading": "Betöltés folyamatban…",
|
||||
"ep_adminpads2_manage-pads": "Jegyzetfüzetek kezelése",
|
||||
"ep_adminpads2_no-results": "Nincs találat",
|
||||
"ep_adminpads2_pad-user-count": "Jegyzetfüzet felhasználók száma",
|
||||
"ep_adminpads2_padname": "Jegyzetfüzet név",
|
||||
"ep_adminpads2_search-box.placeholder": "Keresési kifejezés",
|
||||
"ep_adminpads2_search-button.value": "Keresés",
|
||||
"ep_adminpads2_search-done": "Keresés befejezve",
|
||||
"ep_adminpads2_search-error-explanation": "A kiszolgáló hibát észlelt a jegyzetfüzetek keresésekor:",
|
||||
"ep_adminpads2_search-error-title": "Nem sikerült lekérni a jegyzetfüzet listát",
|
||||
"ep_adminpads2_search-heading": "Jegyzetfüzetek keresése",
|
||||
"ep_adminpads2_title": "Jegyzetfüzet felügyelete",
|
||||
"ep_adminpads2_unknown-error": "Ismeretlen hiba",
|
||||
"ep_adminpads2_unknown-status": "Ismeretlen állapot"
|
||||
}
|
27
admin/public/ep_admin_pads/ia.json
Normal file
27
admin/public/ep_admin_pads/ia.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"McDutchie"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Action",
|
||||
"ep_adminpads2_autoupdate-label": "Actualisar automaticamente le pad in caso de cambiamentos",
|
||||
"ep_adminpads2_autoupdate.title": "Activa o disactiva le actualisationes automatic pro le consulta actual.",
|
||||
"ep_adminpads2_confirm": "Es tu secur de voler deler le pad {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Deler",
|
||||
"ep_adminpads2_last-edited": "Ultime modification",
|
||||
"ep_adminpads2_loading": "Cargamento in curso…",
|
||||
"ep_adminpads2_manage-pads": "Gerer pads",
|
||||
"ep_adminpads2_no-results": "Nulle resultato",
|
||||
"ep_adminpads2_pad-user-count": "Numero de usatores del pad",
|
||||
"ep_adminpads2_padname": "Nomine del pad",
|
||||
"ep_adminpads2_search-box.placeholder": "Termino de recerca",
|
||||
"ep_adminpads2_search-button.value": "Cercar",
|
||||
"ep_adminpads2_search-done": "Recerca terminate",
|
||||
"ep_adminpads2_search-error-explanation": "Le servitor ha incontrate un error durante le recerca de pads:",
|
||||
"ep_adminpads2_search-error-title": "Non poteva obtener le lista de pads",
|
||||
"ep_adminpads2_search-heading": "Cercar pads",
|
||||
"ep_adminpads2_title": "Administration de pads",
|
||||
"ep_adminpads2_unknown-error": "Error incognite",
|
||||
"ep_adminpads2_unknown-status": "Stato incognite"
|
||||
}
|
16
admin/public/ep_admin_pads/it.json
Normal file
16
admin/public/ep_admin_pads/it.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Beta16",
|
||||
"Luca.favorido"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Azione",
|
||||
"ep_adminpads2_delete.value": "Cancella",
|
||||
"ep_adminpads2_last-edited": "Ultima modifica",
|
||||
"ep_adminpads2_loading": "Caricamento…",
|
||||
"ep_adminpads2_no-results": "Nessun risultato",
|
||||
"ep_adminpads2_search-button.value": "Cerca",
|
||||
"ep_adminpads2_unknown-error": "Errore sconosciuto",
|
||||
"ep_adminpads2_unknown-status": "Stato sconosciuto"
|
||||
}
|
13
admin/public/ep_admin_pads/kn.json
Normal file
13
admin/public/ep_admin_pads/kn.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"ಮಲ್ನಾಡಾಚ್ ಕೊಂಕ್ಣೊ"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "ಕ್ರಿಯೆ",
|
||||
"ep_adminpads2_delete.value": "ಅಳಿಸು",
|
||||
"ep_adminpads2_loading": "ತುಂಬಿಸಲಾಗುತ್ತಿದೆ…",
|
||||
"ep_adminpads2_no-results": "ಯಾವ ಫಲಿತಾಂಶಗಳೂ ಇಲ್ಲ",
|
||||
"ep_adminpads2_search-button.value": "ಹುಡುಕು",
|
||||
"ep_adminpads2_unknown-error": "ಅಪರಿಚಿತ ದೋಷ"
|
||||
}
|
28
admin/public/ep_admin_pads/ko.json
Normal file
28
admin/public/ep_admin_pads/ko.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Ykhwong",
|
||||
"그냥기여자"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "동작",
|
||||
"ep_adminpads2_autoupdate-label": "패드 변경 시 자동 업데이트",
|
||||
"ep_adminpads2_autoupdate.title": "현재 쿼리의 자동 업데이트를 활성화하거나 비활성화합니다.",
|
||||
"ep_adminpads2_confirm": "{{padID}} 패드를 삭제하시겠습니까?",
|
||||
"ep_adminpads2_delete.value": "삭제",
|
||||
"ep_adminpads2_last-edited": "최근 편집",
|
||||
"ep_adminpads2_loading": "불러오는 중...",
|
||||
"ep_adminpads2_manage-pads": "패드 관리",
|
||||
"ep_adminpads2_no-results": "결과 없음",
|
||||
"ep_adminpads2_pad-user-count": "패드 사용자 수",
|
||||
"ep_adminpads2_padname": "패드 이름",
|
||||
"ep_adminpads2_search-box.placeholder": "검색어",
|
||||
"ep_adminpads2_search-button.value": "검색",
|
||||
"ep_adminpads2_search-done": "검색 완료",
|
||||
"ep_adminpads2_search-error-explanation": "패드 검색 중 서버에 오류가 발생했습니다:",
|
||||
"ep_adminpads2_search-error-title": "패드 목록 가져오기 실패",
|
||||
"ep_adminpads2_search-heading": "패드 검색",
|
||||
"ep_adminpads2_title": "패드 관리",
|
||||
"ep_adminpads2_unknown-error": "알 수 없는 오류",
|
||||
"ep_adminpads2_unknown-status": "알 수 없는 상태"
|
||||
}
|
27
admin/public/ep_admin_pads/krc.json
Normal file
27
admin/public/ep_admin_pads/krc.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Къарачайлы"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Этиу",
|
||||
"ep_adminpads2_autoupdate-label": "Блокнот тюрлендириулеринде автомат халда джангыртыу",
|
||||
"ep_adminpads2_autoupdate.title": "Баргъан излем ючюн автомат халда джангыртыуланы джандын неда джукълат.",
|
||||
"ep_adminpads2_confirm": "{{padID}} блокнотну керти да кетерирге излеймисиз?",
|
||||
"ep_adminpads2_delete.value": "Кетер",
|
||||
"ep_adminpads2_last-edited": "Ахыр тюзетиу",
|
||||
"ep_adminpads2_loading": "Джюклениу…",
|
||||
"ep_adminpads2_manage-pads": "Блокнотланы оноуун эт",
|
||||
"ep_adminpads2_no-results": "Эсебле джокъдула",
|
||||
"ep_adminpads2_pad-user-count": "Блокнот хайырланыучуланы саны",
|
||||
"ep_adminpads2_padname": "Блокнот ат",
|
||||
"ep_adminpads2_search-box.placeholder": "Терминни изле",
|
||||
"ep_adminpads2_search-button.value": "Изле",
|
||||
"ep_adminpads2_search-done": "Излеу тамамланды",
|
||||
"ep_adminpads2_search-error-explanation": "Сервер, блокнотланы излеген заманда халат табды:",
|
||||
"ep_adminpads2_search-error-title": "Блокнот тизмеси алынамады",
|
||||
"ep_adminpads2_search-heading": "Блокнотла ючюн излеу",
|
||||
"ep_adminpads2_title": "Блокнот башчылыкъ",
|
||||
"ep_adminpads2_unknown-error": "Билинмеген халат",
|
||||
"ep_adminpads2_unknown-status": "Билинмеген турум"
|
||||
}
|
16
admin/public/ep_admin_pads/lb.json
Normal file
16
admin/public/ep_admin_pads/lb.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Robby",
|
||||
"Volvox"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_confirm": "Wëllt Dir de Pad {{padID}} wierklech läschen?",
|
||||
"ep_adminpads2_delete.value": "Läschen",
|
||||
"ep_adminpads2_loading": "Lueden...",
|
||||
"ep_adminpads2_no-results": "Keng Resultater",
|
||||
"ep_adminpads2_padname": "Padnumm",
|
||||
"ep_adminpads2_search-box.placeholder": "Sichbegrëff",
|
||||
"ep_adminpads2_search-button.value": "Sichen",
|
||||
"ep_adminpads2_unknown-error": "Onbekannte Feeler"
|
||||
}
|
27
admin/public/ep_admin_pads/lt.json
Normal file
27
admin/public/ep_admin_pads/lt.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Nokeoo"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Veiksmas",
|
||||
"ep_adminpads2_autoupdate-label": "Automatinis bloknoto keitimų naujinimas",
|
||||
"ep_adminpads2_autoupdate.title": "Įjungia arba išjungia automatinius dabartinės užklausos atnaujinimus.",
|
||||
"ep_adminpads2_confirm": "Ar tikrai norite ištrinti bloknotą {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Ištrinti",
|
||||
"ep_adminpads2_last-edited": "Paskutinis pakeitimas",
|
||||
"ep_adminpads2_loading": "Įkeliama…",
|
||||
"ep_adminpads2_manage-pads": "Tvarkyti bloknotą",
|
||||
"ep_adminpads2_no-results": "Nėra rezultatų",
|
||||
"ep_adminpads2_pad-user-count": "Bloknoto naudotojų skaičius",
|
||||
"ep_adminpads2_padname": "Bloknoto pavadinimas",
|
||||
"ep_adminpads2_search-box.placeholder": "Paieškos terminas",
|
||||
"ep_adminpads2_search-button.value": "Paieška",
|
||||
"ep_adminpads2_search-done": "Paieška baigta",
|
||||
"ep_adminpads2_search-error-explanation": "Serveris susidūrė su klaida ieškant bloknotų:",
|
||||
"ep_adminpads2_search-error-title": "Nepavyko gauti bloknotų sąrašo",
|
||||
"ep_adminpads2_search-heading": "Ieškokite bloknotų",
|
||||
"ep_adminpads2_title": "Bloknotų administravimas",
|
||||
"ep_adminpads2_unknown-error": "Nežinoma klaida",
|
||||
"ep_adminpads2_unknown-status": "Nežinoma būsena"
|
||||
}
|
27
admin/public/ep_admin_pads/mk.json
Normal file
27
admin/public/ep_admin_pads/mk.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Bjankuloski06"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Дејство",
|
||||
"ep_adminpads2_autoupdate-label": "Самоподнова при измени во тетратката",
|
||||
"ep_adminpads2_autoupdate.title": "Овозможува или оневозможува самоподнова на тековното барање.",
|
||||
"ep_adminpads2_confirm": "Дали навистина сакате да ја избришете тетратката {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Избриши",
|
||||
"ep_adminpads2_last-edited": "Последно уредување",
|
||||
"ep_adminpads2_loading": "Вчитувам…",
|
||||
"ep_adminpads2_manage-pads": "Раководење со тетратки",
|
||||
"ep_adminpads2_no-results": "Нема исход",
|
||||
"ep_adminpads2_pad-user-count": "Корисници на тетратката",
|
||||
"ep_adminpads2_padname": "Назив на тетратката",
|
||||
"ep_adminpads2_search-box.placeholder": "Пребаран поим",
|
||||
"ep_adminpads2_search-button.value": "Пребарај",
|
||||
"ep_adminpads2_search-done": "Пребарувањето заврши",
|
||||
"ep_adminpads2_search-error-explanation": "Опслужувачот наиде на грешка при пребарувањето на тетратки:",
|
||||
"ep_adminpads2_search-error-title": "Не можев да го добијам списокот на тетратки",
|
||||
"ep_adminpads2_search-heading": "Пребарај по тетратките",
|
||||
"ep_adminpads2_title": "Администрација на тетратки",
|
||||
"ep_adminpads2_unknown-error": "Непозната грешка",
|
||||
"ep_adminpads2_unknown-status": "Непозната состојба"
|
||||
}
|
27
admin/public/ep_admin_pads/my.json
Normal file
27
admin/public/ep_admin_pads/my.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Andibecker"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "လုပ်ဆောင်ချက်",
|
||||
"ep_adminpads2_autoupdate-label": "pad အပြောင်းအလဲများတွင်အလိုအလျောက်အပ်ဒိတ်လုပ်ပါ",
|
||||
"ep_adminpads2_autoupdate.title": "လက်ရှိမေးမြန်းမှုအတွက်အလိုအလျောက်အပ်ဒိတ်များကိုဖွင့်ပါသို့မဟုတ်ပိတ်ပါ။",
|
||||
"ep_adminpads2_confirm": "pad {{padID}} ကိုသင်တကယ်ဖျက်ချင်လား။",
|
||||
"ep_adminpads2_delete.value": "ဖျက်ပါ",
|
||||
"ep_adminpads2_last-edited": "နောက်ဆုံးတည်းဖြတ်သည်",
|
||||
"ep_adminpads2_loading": "ဖွင့်နေသည်…",
|
||||
"ep_adminpads2_manage-pads": "pads များကိုစီမံပါ",
|
||||
"ep_adminpads2_no-results": "ရလဒ်မရှိပါ",
|
||||
"ep_adminpads2_pad-user-count": "Pad အသုံးပြုသူအရေအတွက်",
|
||||
"ep_adminpads2_padname": "Padname",
|
||||
"ep_adminpads2_search-box.placeholder": "ဝေါဟာရရှာဖွေပါ",
|
||||
"ep_adminpads2_search-button.value": "ရှာဖွေပါ",
|
||||
"ep_adminpads2_search-done": "ရှာဖွေမှုပြီးပါပြီ",
|
||||
"ep_adminpads2_search-error-explanation": "pads များကိုရှာဖွေစဉ်ဆာဗာသည်အမှားတစ်ခုကြုံခဲ့သည်။",
|
||||
"ep_adminpads2_search-error-title": "pad စာရင်းရယူရန်မအောင်မြင်ပါ",
|
||||
"ep_adminpads2_search-heading": "pads များကိုရှာဖွေပါ",
|
||||
"ep_adminpads2_title": "Pad စီမံခန့်ခွဲမှု",
|
||||
"ep_adminpads2_unknown-error": "အမည်မသိအမှား",
|
||||
"ep_adminpads2_unknown-status": "အခြေအနေမသိ"
|
||||
}
|
13
admin/public/ep_admin_pads/nb.json
Normal file
13
admin/public/ep_admin_pads/nb.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"EdoAug"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Handling",
|
||||
"ep_adminpads2_last-edited": "Sist redigert",
|
||||
"ep_adminpads2_loading": "Laster …",
|
||||
"ep_adminpads2_no-results": "Ingen resultater",
|
||||
"ep_adminpads2_search-button.value": "Søk",
|
||||
"ep_adminpads2_search-done": "Søk fullført"
|
||||
}
|
29
admin/public/ep_admin_pads/nl.json
Normal file
29
admin/public/ep_admin_pads/nl.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Aranka",
|
||||
"McDutchie",
|
||||
"Spinster"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Handeling",
|
||||
"ep_adminpads2_autoupdate-label": "Automatisch bijwerken bij aanpassingen aan de pad",
|
||||
"ep_adminpads2_autoupdate.title": "Schakelt automatische updates voor de huidige query in of uit.",
|
||||
"ep_adminpads2_confirm": "Wil je de pad {{padID}} echt verwijderen?",
|
||||
"ep_adminpads2_delete.value": "Verwijderen",
|
||||
"ep_adminpads2_last-edited": "Laatst bewerkt",
|
||||
"ep_adminpads2_loading": "Bezig met laden...",
|
||||
"ep_adminpads2_manage-pads": "Pads beheren",
|
||||
"ep_adminpads2_no-results": "Geen resultaten",
|
||||
"ep_adminpads2_pad-user-count": "Aantal gebruikers van de pad",
|
||||
"ep_adminpads2_padname": "Naam van de pad",
|
||||
"ep_adminpads2_search-box.placeholder": "Zoekterm",
|
||||
"ep_adminpads2_search-button.value": "Zoeken",
|
||||
"ep_adminpads2_search-done": "Zoekopdracht voltooid",
|
||||
"ep_adminpads2_search-error-explanation": "De server heeft een fout aangetroffen tijdens het zoeken naar pads:",
|
||||
"ep_adminpads2_search-error-title": "Kan lijst met pads niet ophalen",
|
||||
"ep_adminpads2_search-heading": "Pads zoeken",
|
||||
"ep_adminpads2_title": "Administratie van pad",
|
||||
"ep_adminpads2_unknown-error": "Onbekende fout",
|
||||
"ep_adminpads2_unknown-status": "Onbekende status"
|
||||
}
|
21
admin/public/ep_admin_pads/oc.json
Normal file
21
admin/public/ep_admin_pads/oc.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Quentí"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Accion",
|
||||
"ep_adminpads2_delete.value": "Suprimir",
|
||||
"ep_adminpads2_last-edited": "Darrièra edicion",
|
||||
"ep_adminpads2_loading": "Cargament…",
|
||||
"ep_adminpads2_manage-pads": "Gerir los pads",
|
||||
"ep_adminpads2_no-results": "Pas cap de resultat",
|
||||
"ep_adminpads2_padname": "Nom del pad",
|
||||
"ep_adminpads2_search-box.placeholder": "Tèrme de recèrca",
|
||||
"ep_adminpads2_search-button.value": "Recercar",
|
||||
"ep_adminpads2_search-done": "Recèrca acabada",
|
||||
"ep_adminpads2_search-heading": "Cercar de pads",
|
||||
"ep_adminpads2_title": "Administracion de pad",
|
||||
"ep_adminpads2_unknown-error": "Error desconeguda",
|
||||
"ep_adminpads2_unknown-status": "Estat desconegut"
|
||||
}
|
27
admin/public/ep_admin_pads/pms.json
Normal file
27
admin/public/ep_admin_pads/pms.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Borichèt"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Assion",
|
||||
"ep_adminpads2_autoupdate-label": "Agiornament automàtich an sle modìfiche ëd plancia",
|
||||
"ep_adminpads2_autoupdate.title": "Abilité o disabilité j'agiornament automàtich për l'arcesta atual.",
|
||||
"ep_adminpads2_confirm": "Veul-lo për da bon dëscancelé la plancia {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Dëscancelé",
|
||||
"ep_adminpads2_last-edited": "Modificà l'ùltima vira",
|
||||
"ep_adminpads2_loading": "Cariament…",
|
||||
"ep_adminpads2_manage-pads": "Gestì le plance",
|
||||
"ep_adminpads2_no-results": "Gnun arzultà",
|
||||
"ep_adminpads2_pad-user-count": "Conteur ëd plancia dl'utent",
|
||||
"ep_adminpads2_padname": "Nòm ëd plancia",
|
||||
"ep_adminpads2_search-box.placeholder": "Tèrmin d'arserca",
|
||||
"ep_adminpads2_search-button.value": "Arserca",
|
||||
"ep_adminpads2_search-done": "Arserca completà",
|
||||
"ep_adminpads2_search-error-explanation": "Ël servent a l'ha rancontrà n'eror an sërcand dle plance:",
|
||||
"ep_adminpads2_search-error-title": "Falì a oten-e la lista ëd plance",
|
||||
"ep_adminpads2_search-heading": "Arserca ëd plance",
|
||||
"ep_adminpads2_title": "Aministrassion ëd plance",
|
||||
"ep_adminpads2_unknown-error": "Eror nen conossù",
|
||||
"ep_adminpads2_unknown-status": "Statù nen conossù"
|
||||
}
|
30
admin/public/ep_admin_pads/pt-br.json
Normal file
30
admin/public/ep_admin_pads/pt-br.json
Normal file
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Duke of Wikipädia",
|
||||
"Eduardo Addad de Oliveira",
|
||||
"Eduardoaddad",
|
||||
"YuriNikolai"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Ação",
|
||||
"ep_adminpads2_autoupdate-label": "Atualizar notas automaticamente",
|
||||
"ep_adminpads2_autoupdate.title": "Habilita ou desabilita atualizações automáticas para a consulta atual.",
|
||||
"ep_adminpads2_confirm": "Você realmente deseja excluir a nota {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Excluir",
|
||||
"ep_adminpads2_last-edited": "Última edição",
|
||||
"ep_adminpads2_loading": "Carregando…",
|
||||
"ep_adminpads2_manage-pads": "Gerenciar notas",
|
||||
"ep_adminpads2_no-results": "Sem resultados",
|
||||
"ep_adminpads2_pad-user-count": "Número de utilizadores na nota",
|
||||
"ep_adminpads2_padname": "Nome da nota",
|
||||
"ep_adminpads2_search-box.placeholder": "Termo de pesquisa",
|
||||
"ep_adminpads2_search-button.value": "Pesquisar",
|
||||
"ep_adminpads2_search-done": "Busca completa",
|
||||
"ep_adminpads2_search-error-explanation": "O servidor encontrou um erro enquanto procurava por notas:",
|
||||
"ep_adminpads2_search-error-title": "Falha ao buscar lista de notas",
|
||||
"ep_adminpads2_search-heading": "Pesquisar por notas",
|
||||
"ep_adminpads2_title": "Administração de notas",
|
||||
"ep_adminpads2_unknown-error": "Erro desconhecido",
|
||||
"ep_adminpads2_unknown-status": "Status desconhecido"
|
||||
}
|
27
admin/public/ep_admin_pads/pt.json
Normal file
27
admin/public/ep_admin_pads/pt.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Guilha"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Ação",
|
||||
"ep_adminpads2_autoupdate-label": "Atualizar automaticamente as notas",
|
||||
"ep_adminpads2_autoupdate.title": "Ativa ou desativa atualizações automáticas na consulta atual.",
|
||||
"ep_adminpads2_confirm": "Tencionas mesmo eliminar a nota {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Eliminar",
|
||||
"ep_adminpads2_last-edited": "Última edição",
|
||||
"ep_adminpads2_loading": "A carregar...",
|
||||
"ep_adminpads2_manage-pads": "Gerir notas",
|
||||
"ep_adminpads2_no-results": "Sem resultados",
|
||||
"ep_adminpads2_pad-user-count": "Número de utilizadores na nota",
|
||||
"ep_adminpads2_padname": "Nome da nota",
|
||||
"ep_adminpads2_search-box.placeholder": "Procurar termo",
|
||||
"ep_adminpads2_search-button.value": "Procurar",
|
||||
"ep_adminpads2_search-done": "Procura completa",
|
||||
"ep_adminpads2_search-error-explanation": "O servidor encontrou um erro enquanto procurava por notas:",
|
||||
"ep_adminpads2_search-error-title": "Falha ao obter lista de notas",
|
||||
"ep_adminpads2_search-heading": "Procurar por notas",
|
||||
"ep_adminpads2_title": "Administração da nota",
|
||||
"ep_adminpads2_unknown-error": "Erro desconhecido",
|
||||
"ep_adminpads2_unknown-status": "Estado desconhecido"
|
||||
}
|
10
admin/public/ep_admin_pads/qqq.json
Normal file
10
admin/public/ep_admin_pads/qqq.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"BryanDavis"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "{{Identical|Action}}",
|
||||
"ep_adminpads2_delete.value": "{{Identical|Delete}}",
|
||||
"ep_adminpads2_search-button.value": "{{Identical|Search}}"
|
||||
}
|
31
admin/public/ep_admin_pads/ru.json
Normal file
31
admin/public/ep_admin_pads/ru.json
Normal file
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"DDPAT",
|
||||
"Ice bulldog",
|
||||
"Megakott",
|
||||
"Okras",
|
||||
"Pacha Tchernof"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Действие",
|
||||
"ep_adminpads2_autoupdate-label": "Автообновление при изменении документа",
|
||||
"ep_adminpads2_autoupdate.title": "Включает или отключает автоматические обновления для текущего запроса.",
|
||||
"ep_adminpads2_confirm": "Вы действительно хотите удалить документ {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Удалить",
|
||||
"ep_adminpads2_last-edited": "Последнее изменение",
|
||||
"ep_adminpads2_loading": "Загружается…",
|
||||
"ep_adminpads2_manage-pads": "Управление документами",
|
||||
"ep_adminpads2_no-results": "Нет результатов",
|
||||
"ep_adminpads2_pad-user-count": "Количество пользователей документа",
|
||||
"ep_adminpads2_padname": "Название документа",
|
||||
"ep_adminpads2_search-box.placeholder": "Искать термин",
|
||||
"ep_adminpads2_search-button.value": "Найти",
|
||||
"ep_adminpads2_search-done": "Поиск завершён",
|
||||
"ep_adminpads2_search-error-explanation": "Сервер обнаружил ошибку при поиске документов:",
|
||||
"ep_adminpads2_search-error-title": "Не удалось получить список документов",
|
||||
"ep_adminpads2_search-heading": "Поиск документов",
|
||||
"ep_adminpads2_title": "Администрирование документов",
|
||||
"ep_adminpads2_unknown-error": "Неизвестная ошибка",
|
||||
"ep_adminpads2_unknown-status": "Неизвестный статус"
|
||||
}
|
27
admin/public/ep_admin_pads/sc.json
Normal file
27
admin/public/ep_admin_pads/sc.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Adr mm"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Atzione",
|
||||
"ep_adminpads2_autoupdate-label": "Atualizatzione automàtica de is modìficas de su pad",
|
||||
"ep_adminpads2_autoupdate.title": "Ativat o disativat is atualizatziones automàticas pro sa chirca atuale.",
|
||||
"ep_adminpads2_confirm": "Seguru chi boles cantzellare su pad {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Cantzella",
|
||||
"ep_adminpads2_last-edited": "Ùrtima modìfica",
|
||||
"ep_adminpads2_loading": "Carrighende...",
|
||||
"ep_adminpads2_manage-pads": "Gesti is pads",
|
||||
"ep_adminpads2_no-results": "Nissunu resurtadu",
|
||||
"ep_adminpads2_pad-user-count": "Nùmeru de utentes de pads",
|
||||
"ep_adminpads2_padname": "Nòmine de su pad",
|
||||
"ep_adminpads2_search-box.placeholder": "Tèrmine de chirca",
|
||||
"ep_adminpads2_search-button.value": "Chirca",
|
||||
"ep_adminpads2_search-done": "Chirca cumpleta",
|
||||
"ep_adminpads2_search-error-explanation": "Su serbidore at agatadu un'errore chirchende pads:",
|
||||
"ep_adminpads2_search-error-title": "Impossìbile otènnere sa lista de pads",
|
||||
"ep_adminpads2_search-heading": "Chirca pads",
|
||||
"ep_adminpads2_title": "Amministratzione de su pad",
|
||||
"ep_adminpads2_unknown-error": "Errore disconnotu",
|
||||
"ep_adminpads2_unknown-status": "Istadu disconnotu"
|
||||
}
|
14
admin/public/ep_admin_pads/sdc.json
Normal file
14
admin/public/ep_admin_pads/sdc.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"F Samaritani"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Azioni",
|
||||
"ep_adminpads2_delete.value": "Canzella",
|
||||
"ep_adminpads2_loading": "carrigghendi...",
|
||||
"ep_adminpads2_no-results": "Nisciun risulthaddu",
|
||||
"ep_adminpads2_search-button.value": "Zercha",
|
||||
"ep_adminpads2_search-heading": "Zirchà dati",
|
||||
"ep_adminpads2_unknown-error": "Errori ischunisciddu"
|
||||
}
|
27
admin/public/ep_admin_pads/sk.json
Normal file
27
admin/public/ep_admin_pads/sk.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Yardom78"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Akcia",
|
||||
"ep_adminpads2_autoupdate-label": "Automatická aktualizácia zmien na poznámkovom bloku",
|
||||
"ep_adminpads2_autoupdate.title": "Zapne alebo vypne automatickú aktualizáciu.",
|
||||
"ep_adminpads2_confirm": "Skutočne chcete vymazať poznámkový blok {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Vymazať",
|
||||
"ep_adminpads2_last-edited": "Posledná úprava",
|
||||
"ep_adminpads2_loading": "Načítavanie...",
|
||||
"ep_adminpads2_manage-pads": "Spravovať poznámkové bloky",
|
||||
"ep_adminpads2_no-results": "Žiadne výsledky",
|
||||
"ep_adminpads2_pad-user-count": "Počet používateľov poznámkového bloku",
|
||||
"ep_adminpads2_padname": "Názov poznámkového bloku",
|
||||
"ep_adminpads2_search-box.placeholder": "Hľadať výraz",
|
||||
"ep_adminpads2_search-button.value": "Hľadať",
|
||||
"ep_adminpads2_search-done": "Hľadanie dokončené",
|
||||
"ep_adminpads2_search-error-explanation": "Pri hľadaní poznámkového bloku došlo k chybe:",
|
||||
"ep_adminpads2_search-error-title": "Nepodarilo sa získať zoznam poznámkových blokov",
|
||||
"ep_adminpads2_search-heading": "Hľadať poznámkový blok",
|
||||
"ep_adminpads2_title": "Správa poznámkového bloku",
|
||||
"ep_adminpads2_unknown-error": "Neznáma chyba",
|
||||
"ep_adminpads2_unknown-status": "Neznámy stav"
|
||||
}
|
20
admin/public/ep_admin_pads/skr-arab.json
Normal file
20
admin/public/ep_admin_pads/skr-arab.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Saraiki"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "عمل",
|
||||
"ep_adminpads2_delete.value": "مٹاؤ",
|
||||
"ep_adminpads2_last-edited": "چھیکڑی تبدیلی",
|
||||
"ep_adminpads2_loading": "لوڈ تھین٘دا پئے۔۔۔",
|
||||
"ep_adminpads2_manage-pads": "پیڈ منیج کرو",
|
||||
"ep_adminpads2_no-results": "کوئی نتیجہ کائنی",
|
||||
"ep_adminpads2_padname": "پیڈ ناں",
|
||||
"ep_adminpads2_search-box.placeholder": "ٹرم ڳولو",
|
||||
"ep_adminpads2_search-button.value": "ڳولو",
|
||||
"ep_adminpads2_search-done": "ڳولݨ پورا تھیا",
|
||||
"ep_adminpads2_search-heading": "پیڈاں دی ڳول",
|
||||
"ep_adminpads2_unknown-error": "نامعلوم غلطی",
|
||||
"ep_adminpads2_unknown-status": "نامعلوم حالت"
|
||||
}
|
28
admin/public/ep_admin_pads/sl.json
Normal file
28
admin/public/ep_admin_pads/sl.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Eleassar",
|
||||
"HairyFotr"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Dejanje",
|
||||
"ep_adminpads2_autoupdate-label": "Samodejno posodabljanje ob spremembah blokcev",
|
||||
"ep_adminpads2_autoupdate.title": "Omogoči ali onemogoči samodejne posodobitve za trenutno poizvedbo.",
|
||||
"ep_adminpads2_confirm": "Ali res želite izbrisati blokec {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Izbriši",
|
||||
"ep_adminpads2_last-edited": "Zadnje urejanje",
|
||||
"ep_adminpads2_loading": "Nalaganje ...",
|
||||
"ep_adminpads2_manage-pads": "Upravljanje blokcev",
|
||||
"ep_adminpads2_no-results": "Ni zadetkov",
|
||||
"ep_adminpads2_pad-user-count": "Število urejevalcev blokca",
|
||||
"ep_adminpads2_padname": "Ime blokca",
|
||||
"ep_adminpads2_search-box.placeholder": "Iskalni izraz",
|
||||
"ep_adminpads2_search-button.value": "Išči",
|
||||
"ep_adminpads2_search-done": "Iskanje končano",
|
||||
"ep_adminpads2_search-error-explanation": "Strežnik je med iskanjem blokcev naletel na napako:",
|
||||
"ep_adminpads2_search-error-title": "Ni bilo mogoče pridobiti seznama blokcev",
|
||||
"ep_adminpads2_search-heading": "Iskanje blokcev",
|
||||
"ep_adminpads2_title": "Upravljanje blokcev",
|
||||
"ep_adminpads2_unknown-error": "Neznana napaka",
|
||||
"ep_adminpads2_unknown-status": "Neznano stanje"
|
||||
}
|
13
admin/public/ep_admin_pads/smn.json
Normal file
13
admin/public/ep_admin_pads/smn.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Yupik"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_delete.value": "Siho",
|
||||
"ep_adminpads2_last-edited": "Majemustáá nubástittum",
|
||||
"ep_adminpads2_search-box.placeholder": "Uuccâmsääni",
|
||||
"ep_adminpads2_search-button.value": "Uusâ",
|
||||
"ep_adminpads2_unknown-error": "Tubdâmettum feilâ",
|
||||
"ep_adminpads2_unknown-status": "Tubdâmettum tile"
|
||||
}
|
16
admin/public/ep_admin_pads/sms.json
Normal file
16
admin/public/ep_admin_pads/sms.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Yupik"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_delete.value": "Jaukkâd",
|
||||
"ep_adminpads2_last-edited": "Mââimõssân muttum",
|
||||
"ep_adminpads2_no-results": "Ij käunnʼjam ni mii",
|
||||
"ep_adminpads2_padname": "Mošttʼtõspõʹmmai nõmm",
|
||||
"ep_adminpads2_search-box.placeholder": "Ooccâmsääʹnn",
|
||||
"ep_adminpads2_search-button.value": "Ooʒʒ",
|
||||
"ep_adminpads2_search-heading": "Ooʒʒ mošttʼtõspõʹmmjid",
|
||||
"ep_adminpads2_unknown-error": "Toobdteʹmes vââʹǩǩ",
|
||||
"ep_adminpads2_unknown-status": "Toobdteʹmes status"
|
||||
}
|
27
admin/public/ep_admin_pads/sq.json
Normal file
27
admin/public/ep_admin_pads/sq.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Besnik b"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Veprim",
|
||||
"ep_adminpads2_autoupdate-label": "Vetëpërditësohu, kur nga ndryshime blloku",
|
||||
"ep_adminpads2_autoupdate.title": "Aktivizon ose çaktivizon përditësim të automatizuara për kërkesën e tanishme.",
|
||||
"ep_adminpads2_confirm": "Doni vërtet të fshihet blloku {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Fshije",
|
||||
"ep_adminpads2_last-edited": "Përpunuar së fundi më",
|
||||
"ep_adminpads2_loading": "Po ngarkohet…",
|
||||
"ep_adminpads2_manage-pads": "Administroni blloqe",
|
||||
"ep_adminpads2_no-results": "S’ka përfundime",
|
||||
"ep_adminpads2_pad-user-count": "Numër përdoruesish blloku",
|
||||
"ep_adminpads2_padname": "Emër blloku",
|
||||
"ep_adminpads2_search-box.placeholder": "Term kërkimi",
|
||||
"ep_adminpads2_search-button.value": "Kërko",
|
||||
"ep_adminpads2_search-done": "Kërkim i plotë",
|
||||
"ep_adminpads2_search-error-explanation": "Shërbyesi hasi një gabim teksa kërkohej për blloqe:",
|
||||
"ep_adminpads2_search-error-title": "S’u arrit të merrej listë blloqesh",
|
||||
"ep_adminpads2_search-heading": "Kërkoni për blloqe",
|
||||
"ep_adminpads2_title": "Administrim blloku",
|
||||
"ep_adminpads2_unknown-error": "Gabim i panjohur",
|
||||
"ep_adminpads2_unknown-status": "Gjendje e panjohur"
|
||||
}
|
28
admin/public/ep_admin_pads/sv.json
Normal file
28
admin/public/ep_admin_pads/sv.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Bengtsson96",
|
||||
"WikiPhoenix"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Åtgärd",
|
||||
"ep_adminpads2_autoupdate-label": "Uppdatera automatiskt när blocket ändras",
|
||||
"ep_adminpads2_autoupdate.title": "Aktivera eller inaktivera automatiska uppdatering för nuvarande förfrågan.",
|
||||
"ep_adminpads2_confirm": "Vill du verkligen radera blocket {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Radera",
|
||||
"ep_adminpads2_last-edited": "Senast redigerad",
|
||||
"ep_adminpads2_loading": "Läser in …",
|
||||
"ep_adminpads2_manage-pads": "Hantera block",
|
||||
"ep_adminpads2_no-results": "Inga resultat",
|
||||
"ep_adminpads2_pad-user-count": "Antal blockanvändare",
|
||||
"ep_adminpads2_padname": "Blocknamn",
|
||||
"ep_adminpads2_search-box.placeholder": "Sökord",
|
||||
"ep_adminpads2_search-button.value": "Sök",
|
||||
"ep_adminpads2_search-done": "Sökning slutförd",
|
||||
"ep_adminpads2_search-error-explanation": "Servern stötte på ett fel vid sökning efter block:",
|
||||
"ep_adminpads2_search-error-title": "Misslyckades att hämta blocklista",
|
||||
"ep_adminpads2_search-heading": "Sök efter block",
|
||||
"ep_adminpads2_title": "Blockadministration",
|
||||
"ep_adminpads2_unknown-error": "Okänt fel",
|
||||
"ep_adminpads2_unknown-status": "Okänd status"
|
||||
}
|
27
admin/public/ep_admin_pads/sw.json
Normal file
27
admin/public/ep_admin_pads/sw.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Andibecker"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Hatua",
|
||||
"ep_adminpads2_autoupdate-label": "Sasisha kiotomatiki kwenye mabadiliko ya pedi",
|
||||
"ep_adminpads2_autoupdate.title": "Huwasha au kulemaza sasisho otomatiki kwa hoja ya sasa.",
|
||||
"ep_adminpads2_confirm": "Je! Kweli unataka kufuta pedi {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Futa",
|
||||
"ep_adminpads2_last-edited": "Ilihaririwa mwisho",
|
||||
"ep_adminpads2_loading": "Inapakia...",
|
||||
"ep_adminpads2_manage-pads": "Dhibiti pedi",
|
||||
"ep_adminpads2_no-results": "Hakuna matokeo",
|
||||
"ep_adminpads2_pad-user-count": "Hesabu ya mtumiaji wa pedi",
|
||||
"ep_adminpads2_padname": "Jina la utani",
|
||||
"ep_adminpads2_search-box.placeholder": "Neno la utaftaji",
|
||||
"ep_adminpads2_search-button.value": "Tafuta",
|
||||
"ep_adminpads2_search-done": "Utafutaji umekamilika",
|
||||
"ep_adminpads2_search-error-explanation": "Seva ilipata hitilafu wakati wa kutafuta pedi:",
|
||||
"ep_adminpads2_search-error-title": "Imeshindwa kupata orodha ya pedi",
|
||||
"ep_adminpads2_search-heading": "Tafuta pedi",
|
||||
"ep_adminpads2_title": "Usimamizi wa pedi",
|
||||
"ep_adminpads2_unknown-error": "Hitilafu isiyojulikana",
|
||||
"ep_adminpads2_unknown-status": "Hali isiyojulikana"
|
||||
}
|
27
admin/public/ep_admin_pads/th.json
Normal file
27
admin/public/ep_admin_pads/th.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Andibecker"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "การกระทำ",
|
||||
"ep_adminpads2_autoupdate-label": "อัปเดตอัตโนมัติเมื่อเปลี่ยนแผ่น",
|
||||
"ep_adminpads2_autoupdate.title": "เปิดหรือปิดการอัปเดตอัตโนมัติสำหรับคิวรีปัจจุบัน",
|
||||
"ep_adminpads2_confirm": "คุณต้องการลบแพด {{padID}} จริงหรือไม่",
|
||||
"ep_adminpads2_delete.value": "ลบ",
|
||||
"ep_adminpads2_last-edited": "แก้ไขล่าสุด",
|
||||
"ep_adminpads2_loading": "กำลังโหลด…",
|
||||
"ep_adminpads2_manage-pads": "จัดการแผ่นรอง",
|
||||
"ep_adminpads2_no-results": "ไม่มีผลลัพธ์",
|
||||
"ep_adminpads2_pad-user-count": "จำนวนผู้ใช้แพด",
|
||||
"ep_adminpads2_padname": "นามแฝง",
|
||||
"ep_adminpads2_search-box.placeholder": "คำที่ต้องการค้นหา",
|
||||
"ep_adminpads2_search-button.value": "ค้นหา",
|
||||
"ep_adminpads2_search-done": "ค้นหาเสร็จสมบูรณ์",
|
||||
"ep_adminpads2_search-error-explanation": "เซิร์ฟเวอร์พบข้อผิดพลาดขณะค้นหาแผ่นอิเล็กโทรด:",
|
||||
"ep_adminpads2_search-error-title": "ไม่สามารถรับรายการแผ่นรอง",
|
||||
"ep_adminpads2_search-heading": "ค้นหาแผ่นรอง",
|
||||
"ep_adminpads2_title": "การบริหารแผ่น",
|
||||
"ep_adminpads2_unknown-error": "ข้อผิดพลาดที่ไม่รู้จัก",
|
||||
"ep_adminpads2_unknown-status": "ไม่ทราบสถานะ"
|
||||
}
|
17
admin/public/ep_admin_pads/tl.json
Normal file
17
admin/public/ep_admin_pads/tl.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Mrkczr"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Kilos",
|
||||
"ep_adminpads2_delete.value": "Burahin",
|
||||
"ep_adminpads2_last-edited": "Huling binago",
|
||||
"ep_adminpads2_loading": "Naglo-load...",
|
||||
"ep_adminpads2_no-results": "Walang mga resulta",
|
||||
"ep_adminpads2_search-box.placeholder": "Mga katagang hahanapin:",
|
||||
"ep_adminpads2_search-button.value": "Hanapin",
|
||||
"ep_adminpads2_search-done": "Natapos na ang paghahanap",
|
||||
"ep_adminpads2_unknown-error": "Hindi nalalamang kamalian",
|
||||
"ep_adminpads2_unknown-status": "Hindi alam na katayuan"
|
||||
}
|
28
admin/public/ep_admin_pads/tr.json
Normal file
28
admin/public/ep_admin_pads/tr.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Hedda",
|
||||
"MuratTheTurkish"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Eylem",
|
||||
"ep_adminpads2_autoupdate-label": "Bloknot değişikliklerinde otomatik güncelleme",
|
||||
"ep_adminpads2_autoupdate.title": "Mevcut sorgu için otomatik güncellemeleri etkinleştirir veya devre dışı bırakır.",
|
||||
"ep_adminpads2_confirm": "{{padID}} bloknotunu gerçekten silmek istiyor musunuz?",
|
||||
"ep_adminpads2_delete.value": "Sil",
|
||||
"ep_adminpads2_last-edited": "Son düzenleme",
|
||||
"ep_adminpads2_loading": "Yükleniyor...",
|
||||
"ep_adminpads2_manage-pads": "Bloknotları yönet",
|
||||
"ep_adminpads2_no-results": "Sonuç yok",
|
||||
"ep_adminpads2_pad-user-count": "Bloknot kullanıcı sayısı",
|
||||
"ep_adminpads2_padname": "Bloknot adı",
|
||||
"ep_adminpads2_search-box.placeholder": "Terimi ara",
|
||||
"ep_adminpads2_search-button.value": "Ara",
|
||||
"ep_adminpads2_search-done": "Arama tamamlandı",
|
||||
"ep_adminpads2_search-error-explanation": "Sunucu, bloknotları ararken bir hatayla karşılaştı:",
|
||||
"ep_adminpads2_search-error-title": "Bloknot listesi alınamadı",
|
||||
"ep_adminpads2_search-heading": "Bloknotları ara",
|
||||
"ep_adminpads2_title": "Bloknot yönetimi",
|
||||
"ep_adminpads2_unknown-error": "Bilinmeyen hata",
|
||||
"ep_adminpads2_unknown-status": "Bilinmeyen durum"
|
||||
}
|
28
admin/public/ep_admin_pads/uk.json
Normal file
28
admin/public/ep_admin_pads/uk.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"DDPAT",
|
||||
"Ice bulldog"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "Дія",
|
||||
"ep_adminpads2_autoupdate-label": "Автоматичне оновлення при зміні майданчика",
|
||||
"ep_adminpads2_autoupdate.title": "Вмикає або вимикає автоматичне оновлення поточного запиту.",
|
||||
"ep_adminpads2_confirm": "Ви дійсно хочете видалити панель {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "Видалити",
|
||||
"ep_adminpads2_last-edited": "Останнє редагування",
|
||||
"ep_adminpads2_loading": "Завантаження…",
|
||||
"ep_adminpads2_manage-pads": "Управління майданчиками",
|
||||
"ep_adminpads2_no-results": "Немає результатів",
|
||||
"ep_adminpads2_pad-user-count": "Кількість майданчиків користувача",
|
||||
"ep_adminpads2_padname": "Назва майданчика",
|
||||
"ep_adminpads2_search-box.placeholder": "Пошуковий термін",
|
||||
"ep_adminpads2_search-button.value": "Пошук",
|
||||
"ep_adminpads2_search-done": "Пошук завершено",
|
||||
"ep_adminpads2_search-error-explanation": "Під час пошуку педів сервер виявив помилку:",
|
||||
"ep_adminpads2_search-error-title": "Не вдалося отримати список панелей",
|
||||
"ep_adminpads2_search-heading": "Пошук майданчиків",
|
||||
"ep_adminpads2_title": "Введення майданчиків",
|
||||
"ep_adminpads2_unknown-error": "Невідома помилка",
|
||||
"ep_adminpads2_unknown-status": "Невідомий статус"
|
||||
}
|
29
admin/public/ep_admin_pads/zh-hans.json
Normal file
29
admin/public/ep_admin_pads/zh-hans.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"GuoPC",
|
||||
"Lakejason0",
|
||||
"沈澄心"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "操作",
|
||||
"ep_adminpads2_autoupdate-label": "在记事本更改时自动更新",
|
||||
"ep_adminpads2_autoupdate.title": "启用或禁用目前查询的自动更新",
|
||||
"ep_adminpads2_confirm": "您确定要删除记事本 {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "删除",
|
||||
"ep_adminpads2_last-edited": "上次编辑于",
|
||||
"ep_adminpads2_loading": "正在加载…",
|
||||
"ep_adminpads2_manage-pads": "管理记事本",
|
||||
"ep_adminpads2_no-results": "没有结果",
|
||||
"ep_adminpads2_pad-user-count": "记事本用户数",
|
||||
"ep_adminpads2_padname": "记事本名称",
|
||||
"ep_adminpads2_search-box.placeholder": "搜索关键词",
|
||||
"ep_adminpads2_search-button.value": "搜索",
|
||||
"ep_adminpads2_search-done": "搜索完成",
|
||||
"ep_adminpads2_search-error-explanation": "搜索记事本时服务器发生错误:",
|
||||
"ep_adminpads2_search-error-title": "获取记事本列表失败",
|
||||
"ep_adminpads2_search-heading": "搜索记事本",
|
||||
"ep_adminpads2_title": "记事本管理",
|
||||
"ep_adminpads2_unknown-error": "未知错误",
|
||||
"ep_adminpads2_unknown-status": "未知状态"
|
||||
}
|
28
admin/public/ep_admin_pads/zh-hant.json
Normal file
28
admin/public/ep_admin_pads/zh-hant.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"HellojoeAoPS",
|
||||
"Kly"
|
||||
]
|
||||
},
|
||||
"ep_adminpads2_action": "操作",
|
||||
"ep_adminpads2_autoupdate-label": "在記事本更改時自動更新",
|
||||
"ep_adminpads2_autoupdate.title": "啟用或停用目前查詢的自動更新。",
|
||||
"ep_adminpads2_confirm": "您確定要刪除記事本 {{padID}}?",
|
||||
"ep_adminpads2_delete.value": "刪除",
|
||||
"ep_adminpads2_last-edited": "上一次編輯",
|
||||
"ep_adminpads2_loading": "載入中…",
|
||||
"ep_adminpads2_manage-pads": "管理記事本",
|
||||
"ep_adminpads2_no-results": "沒有結果",
|
||||
"ep_adminpads2_pad-user-count": "記事本使用者數",
|
||||
"ep_adminpads2_padname": "記事本名稱",
|
||||
"ep_adminpads2_search-box.placeholder": "搜尋關鍵字",
|
||||
"ep_adminpads2_search-button.value": "搜尋",
|
||||
"ep_adminpads2_search-done": "搜尋完成",
|
||||
"ep_adminpads2_search-error-explanation": "當搜尋記事本時伺服器發生錯誤:",
|
||||
"ep_adminpads2_search-error-title": "取得記事本清單失敗",
|
||||
"ep_adminpads2_search-heading": "搜尋記事本",
|
||||
"ep_adminpads2_title": "記事本管理",
|
||||
"ep_adminpads2_unknown-error": "不明錯誤",
|
||||
"ep_adminpads2_unknown-status": "不明狀態"
|
||||
}
|
BIN
admin/public/fond.jpg
Normal file
BIN
admin/public/fond.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 182 KiB |
0
admin/src/App.css
Normal file
0
admin/src/App.css
Normal file
110
admin/src/App.tsx
Normal file
110
admin/src/App.tsx
Normal file
|
@ -0,0 +1,110 @@
|
|||
import {useEffect} from 'react'
|
||||
import './App.css'
|
||||
import {connect} from 'socket.io-client'
|
||||
import {isJSONClean} from './utils/utils.ts'
|
||||
import {NavLink, Outlet, useNavigate} from "react-router-dom";
|
||||
import {useStore} from "./store/store.ts";
|
||||
import {LoadingScreen} from "./utils/LoadingScreen.tsx";
|
||||
import {Trans, useTranslation} from "react-i18next";
|
||||
import {Cable, Construction, Crown, NotepadText, Wrench} from "lucide-react";
|
||||
|
||||
const WS_URL = import.meta.env.DEV? 'http://localhost:9001' : ''
|
||||
export const App = ()=> {
|
||||
const setSettings = useStore(state => state.setSettings);
|
||||
const {t} = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/admin-auth/', {
|
||||
method: 'POST'
|
||||
}).then((value)=>{
|
||||
if(!value.ok){
|
||||
navigate('/login')
|
||||
}
|
||||
}).catch(()=>{
|
||||
navigate('/login')
|
||||
})
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t('admin.page-title')
|
||||
|
||||
useStore.getState().setShowLoading(true);
|
||||
const settingSocket = connect(`${WS_URL}/settings`, {
|
||||
transports: ['websocket'],
|
||||
});
|
||||
|
||||
const pluginsSocket = connect(`${WS_URL}/pluginfw/installer`, {
|
||||
transports: ['websocket'],
|
||||
})
|
||||
|
||||
pluginsSocket.on('connect', () => {
|
||||
useStore.getState().setPluginsSocket(pluginsSocket);
|
||||
});
|
||||
|
||||
|
||||
settingSocket.on('connect', () => {
|
||||
useStore.getState().setSettingsSocket(settingSocket);
|
||||
useStore.getState().setShowLoading(false)
|
||||
settingSocket.emit('load');
|
||||
console.log('connected');
|
||||
});
|
||||
|
||||
settingSocket.on('disconnect', (reason) => {
|
||||
// The settingSocket.io client will automatically try to reconnect for all reasons other than "io
|
||||
// server disconnect".
|
||||
useStore.getState().setShowLoading(true)
|
||||
if (reason === 'io server disconnect') {
|
||||
settingSocket.connect();
|
||||
}
|
||||
});
|
||||
|
||||
settingSocket.on('settings', (settings) => {
|
||||
/* Check whether the settings.json is authorized to be viewed */
|
||||
if (settings.results === 'NOT_ALLOWED') {
|
||||
console.log('Not allowed to view settings.json')
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check to make sure the JSON is clean before proceeding */
|
||||
if (isJSONClean(settings.results)) {
|
||||
setSettings(settings.results);
|
||||
} else {
|
||||
alert('Invalid JSON');
|
||||
}
|
||||
useStore.getState().setShowLoading(false);
|
||||
});
|
||||
|
||||
settingSocket.on('saveprogress', (status)=>{
|
||||
console.log(status)
|
||||
})
|
||||
|
||||
return () => {
|
||||
settingSocket.disconnect();
|
||||
pluginsSocket.disconnect()
|
||||
}
|
||||
}, []);
|
||||
|
||||
return <div id="wrapper">
|
||||
<LoadingScreen/>
|
||||
<div className="menu">
|
||||
<div className="inner-menu">
|
||||
<span>
|
||||
<Crown width={40} height={40}/>
|
||||
<h1>Etherpad</h1>
|
||||
</span>
|
||||
<ul>
|
||||
<li><NavLink to="/plugins"><Cable/><Trans i18nKey="admin_plugins"/></NavLink></li>
|
||||
<li><NavLink to={"/settings"}><Wrench/><Trans i18nKey="admin_settings"/></NavLink></li>
|
||||
<li> <NavLink to={"/help"}> <Construction/> <Trans i18nKey="admin_plugins_info"/></NavLink></li>
|
||||
<li><NavLink to={"/pads"}><NotepadText/><Trans i18nKey="ep_admin_pads:ep_adminpads2_manage-pads"/></NavLink></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="innerwrapper">
|
||||
<Outlet/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
export default App
|
16
admin/src/components/IconButton.tsx
Normal file
16
admin/src/components/IconButton.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import {FC, ReactElement} from "react";
|
||||
|
||||
export type IconButtonProps = {
|
||||
icon: JSX.Element,
|
||||
title: string|ReactElement,
|
||||
onClick: ()=>void,
|
||||
className?: string,
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export const IconButton:FC<IconButtonProps> = ({icon,className,onClick,title, disabled})=>{
|
||||
return <button onClick={onClick} className={"icon-button "+ className} disabled={disabled}>
|
||||
{icon}
|
||||
<span>{title}</span>
|
||||
</button>
|
||||
}
|
14
admin/src/components/SearchField.tsx
Normal file
14
admin/src/components/SearchField.tsx
Normal file
|
@ -0,0 +1,14 @@
|
|||
import {ChangeEventHandler, FC} from "react";
|
||||
import {Search} from 'lucide-react'
|
||||
export type SearchFieldProps = {
|
||||
value: string,
|
||||
onChange: ChangeEventHandler<HTMLInputElement>,
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
export const SearchField:FC<SearchFieldProps> = ({onChange,value, placeholder})=>{
|
||||
return <span className="search-field">
|
||||
<input value={value} onChange={onChange} placeholder={placeholder}/>
|
||||
<Search/>
|
||||
</span>
|
||||
}
|
727
admin/src/index.css
Normal file
727
admin/src/index.css
Normal file
|
@ -0,0 +1,727 @@
|
|||
:root {
|
||||
--etherpad-color: #0f775b;
|
||||
--etherpad-comp: #9C8840;
|
||||
--etherpad-light: #99FF99;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Karla;
|
||||
src: url(/Karla-Regular.ttf);
|
||||
}
|
||||
|
||||
html, body, #root {
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
font-family: "Karla", sans-serif;
|
||||
}
|
||||
|
||||
*, *:before, *:after {
|
||||
box-sizing: inherit;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
color: #333;
|
||||
font: 14px helvetica, sans-serif;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
div.menu {
|
||||
height: 100vh;
|
||||
font-size: 16px;
|
||||
font-weight: bolder;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
max-width: 20%;
|
||||
min-width: 20%;
|
||||
}
|
||||
|
||||
.icon-button{
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
background-color: var(--etherpad-color);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-button svg {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.icon-button span {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
|
||||
div.menu span:first-child {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
div.menu span:first-child svg {
|
||||
margin-right: 10px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
|
||||
div.menu h1 {
|
||||
font-size: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.inner-menu {
|
||||
border-radius: 0 20px 20px 0;
|
||||
padding: 10px;
|
||||
flex-grow: 100;
|
||||
background-color: var(--etherpad-comp);
|
||||
color: white;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
div.menu ul {
|
||||
color: white;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.menu li a {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
div.menu svg {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
div.menu li {
|
||||
padding: 10px;
|
||||
color: white;
|
||||
list-style: none;
|
||||
margin-left: 3px;
|
||||
line-height: 3;
|
||||
}
|
||||
|
||||
|
||||
div.menu li:has(.active) {
|
||||
background-color: #9C885C ;
|
||||
}
|
||||
|
||||
div.menu li a {
|
||||
color: lightgray;
|
||||
}
|
||||
|
||||
|
||||
|
||||
div.innerwrapper {
|
||||
background-color: #F0F0F0;
|
||||
overflow: auto;
|
||||
height: 100vh;
|
||||
flex-grow: 100;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
div.innerwrapper-err {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
display: flex;
|
||||
background: none repeat scroll 0px 0px #FFFFFF;
|
||||
box-shadow: 0px 1px 10px rgba(0, 0, 0, 0.2);
|
||||
min-height: 100%;/*always display a scrollbar*/
|
||||
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 29px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.separator {
|
||||
margin: 10px 0;
|
||||
height: 1px;
|
||||
background: #aaa;
|
||||
background: -webkit-linear-gradient(left, #fff, #aaa 20%, #aaa 80%, #fff);
|
||||
background: -moz-linear-gradient(left, #fff, #aaa 20%, #aaa 80%, #fff);
|
||||
background: -ms-linear-gradient(left, #fff, #aaa 20%, #aaa 80%, #fff);
|
||||
background: -o-linear-gradient(left, #fff, #aaa 20%, #aaa 80%, #fff);
|
||||
}
|
||||
|
||||
form {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#inner {
|
||||
width: 300px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
input {
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
|
||||
.sort {
|
||||
cursor: pointer;
|
||||
}
|
||||
.sort:after {
|
||||
content: '▲▼'
|
||||
}
|
||||
.sort.up:after {
|
||||
content:'▲'
|
||||
}
|
||||
.sort.down:after {
|
||||
content:'▼'
|
||||
}
|
||||
|
||||
|
||||
#installed-plugins thead tr th:nth-child(3) {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 3px;
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#available-plugins th:first-child, #available-plugins th:nth-child(2){
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
td, th {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#installed-plugins td>div {
|
||||
position: relative;/* Allows us to position the loading indicator relative to this row */
|
||||
display: inline-block; /*make this fill the whole cell*/
|
||||
width:100%;
|
||||
}
|
||||
|
||||
.messages {
|
||||
height: 5em;
|
||||
}
|
||||
.messages * {
|
||||
display: none;
|
||||
text-align: center;
|
||||
}
|
||||
.messages .fetching {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.progress {
|
||||
position: absolute;
|
||||
top: 0; left: 0; bottom:0; right:0;
|
||||
padding: auto;
|
||||
|
||||
background: rgb(255,255,255);
|
||||
display: none;
|
||||
}
|
||||
|
||||
#search-progress.progress {
|
||||
padding-top: 20%;
|
||||
background: rgba(255,255,255,0.3);
|
||||
}
|
||||
|
||||
.progress * {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.settings {
|
||||
outline: none;
|
||||
width: 100%;
|
||||
min-height: 80vh;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
#response {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
a:link, a:visited, a:hover, a:focus {
|
||||
color: #333333;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:focus, a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.installed-results a:link,
|
||||
.search-results a:link,
|
||||
.installed-results a:visited,
|
||||
.search-results a:visited,
|
||||
.installed-results a:hover,
|
||||
.search-results a:hover,
|
||||
.installed-results a:focus,
|
||||
.search-results a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.installed-results a:focus,
|
||||
.search-results a:focus,
|
||||
.installed-results a:hover,
|
||||
.search-results a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
div.innerwrapper {
|
||||
padding: 0 15px 15px 15px;
|
||||
}
|
||||
|
||||
div.menu {
|
||||
padding: 1px 15px 0 15px;
|
||||
position: static;
|
||||
height: auto;
|
||||
border-right: none;
|
||||
width: auto;
|
||||
float: left;
|
||||
}
|
||||
|
||||
table {
|
||||
border: none;
|
||||
}
|
||||
|
||||
table, thead, tbody, td, tr {
|
||||
display: block;
|
||||
}
|
||||
|
||||
thead tr {
|
||||
display: none;
|
||||
}
|
||||
|
||||
tr {
|
||||
border: 1px solid #ccc;
|
||||
margin-bottom: 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
td {
|
||||
border: none;
|
||||
border-bottom: 1px solid #eee;
|
||||
position: relative;
|
||||
padding-left: 50%;
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td.name {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
td:before {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
left: 6px;
|
||||
text-align: left;
|
||||
padding-right: 10px;
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
content: attr(data-label);
|
||||
}
|
||||
|
||||
td:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
table input[type="button"] {
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.settings-button-bar {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.login-background {
|
||||
background-image: url("/fond.jpg");
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.login-inner-box div {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.login-inner-box [type=submit]{
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.login-textinput {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background-color: #fffacc;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.login-box {
|
||||
width: 20%;
|
||||
padding: 20px;
|
||||
border-radius: 40px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.login-inner-box{
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.login-title {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
color: var(--etherpad-color);
|
||||
font-size: 4rem;
|
||||
font-weight: 1000;
|
||||
}
|
||||
|
||||
.login-button {
|
||||
padding: 10px;
|
||||
background-color: var(--etherpad-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.dialog-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-color: white;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
|
||||
.dialog-confirm-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
|
||||
.dialog-confirm-content {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
background-color: white;
|
||||
transform: translate(-50%, -50%);
|
||||
padding: 20px;
|
||||
z-index: 101;
|
||||
}
|
||||
|
||||
|
||||
.dialog-content {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
padding: 20px;
|
||||
z-index: 101;
|
||||
}
|
||||
|
||||
.dialog-title {
|
||||
color: var(--etherpad-color);
|
||||
font-size: 2em;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.ToastViewport {
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
right: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
width: 390px;
|
||||
max-width: 100vw;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
z-index: 2147483647;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.ToastRootSuccess {
|
||||
background-color: lawngreen;
|
||||
}
|
||||
|
||||
.ToastRootFailure {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.ToastRootFailure > .ToastTitle {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.ToastRoot {
|
||||
border-radius: 20px;
|
||||
box-shadow: hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px;
|
||||
padding: 15px;
|
||||
display: grid;
|
||||
grid-template-areas: 'title action' 'description action';
|
||||
grid-template-columns: auto max-content;
|
||||
column-gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
.ToastRoot[data-state='open'] {
|
||||
animation: slideIn 150ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
.ToastRoot[data-state='closed'] {
|
||||
animation: hide 100ms ease-in;
|
||||
}
|
||||
.ToastRoot[data-swipe='move'] {
|
||||
transform: translateX(var(--radix-toast-swipe-move-x));
|
||||
}
|
||||
.ToastRoot[data-swipe='cancel'] {
|
||||
transform: translateX(0);
|
||||
transition: transform 200ms ease-out;
|
||||
}
|
||||
.ToastRoot[data-swipe='end'] {
|
||||
animation: swipeOut 100ms ease-out;
|
||||
}
|
||||
|
||||
@keyframes hide {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
transform: translateX(calc(100% + var(--viewport-padding)));
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes swipeOut {
|
||||
from {
|
||||
transform: translateX(var(--radix-toast-swipe-end-x));
|
||||
}
|
||||
to {
|
||||
transform: translateX(calc(100% + var(--viewport-padding)));
|
||||
}
|
||||
}
|
||||
|
||||
.ToastTitle {
|
||||
grid-area: title;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 500;
|
||||
color: var(--slate-12);
|
||||
padding: 10px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.ToastDescription {
|
||||
grid-area: description;
|
||||
margin: 0;
|
||||
color: var(--slate-11);
|
||||
font-size: 13px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.ToastAction {
|
||||
grid-area: action;
|
||||
}
|
||||
|
||||
.help-block {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 20px
|
||||
}
|
||||
|
||||
.search-field {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.search-field input {
|
||||
border-color: transparent;
|
||||
border-radius: 20px;
|
||||
height: 2.5rem;
|
||||
width: 100vh;
|
||||
padding: 5px 5px 5px 30px;
|
||||
}
|
||||
|
||||
.search-field input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.search-field svg {
|
||||
position: absolute;
|
||||
left: 3px;
|
||||
bottom: -3px;
|
||||
}
|
||||
|
||||
|
||||
.search-field svg {
|
||||
color: gray
|
||||
}
|
||||
|
||||
table {
|
||||
margin: 25px 0;
|
||||
font-size: 0.9em;
|
||||
font-family: sans-serif;
|
||||
min-width: 400px;
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
th:first-child {
|
||||
border-top-left-radius: 10px;
|
||||
}
|
||||
|
||||
th:last-child {
|
||||
border-top-right-radius: 10px;
|
||||
}
|
||||
|
||||
table thead tr {
|
||||
font-size: 25px;
|
||||
background-color: var(--etherpad-color);
|
||||
color: #ffffff;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table tbody tr {
|
||||
border-bottom: 1px solid #dddddd;
|
||||
}
|
||||
|
||||
table tr:nth-child(even) td {
|
||||
background-color: lightgray;
|
||||
}
|
||||
|
||||
table tr td {
|
||||
padding: 12px 15px;
|
||||
}
|
||||
|
||||
table tbody tr:nth-of-type(even) {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
table tbody tr:last-of-type {
|
||||
border-bottom: 2px solid #009879;
|
||||
}
|
||||
|
||||
table tbody tr.active-row {
|
||||
font-weight: bold;
|
||||
color: #009879;
|
||||
}
|
||||
|
||||
|
||||
.pad-pagination{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.pad-pagination button {
|
||||
display: flex;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.pad-pagination button:disabled {
|
||||
background: transparent;
|
||||
color: lightgrey;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.pad-pagination span {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.pad-pagination >span {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
|
||||
.login-page .login-form .input-control input[type=text], .login-page .login-form .input-control input[type=email], .login-page .login-form .input-control input[type=password], .login-page .signup-form .input-control input[type=text], .login-page .signup-form .input-control input[type=email], .login-page .signup-form .input-control input[type=password], .login-page .forgot-form .input-control input[type=text], .login-page .forgot-form .input-control input[type=email], .login-page .forgot-form .input-control input[type=password] {
|
||||
width: 100%;
|
||||
padding: 12px 20px;
|
||||
margin: 8px 0;
|
||||
display: inline-block;
|
||||
border-bottom: 2px solid #ccc;
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
background-color: #f8f8f8;
|
||||
-webkit-transition: all 0.3s ease-in-out;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
input, button, select, optgroup, textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.icon-input {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.icon-input svg {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 10px;
|
||||
color: #666;
|
||||
}
|
57
admin/src/localization/i18n.ts
Normal file
57
admin/src/localization/i18n.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
import i18n from 'i18next'
|
||||
import {initReactI18next} from "react-i18next";
|
||||
import LanguageDetector from 'i18next-browser-languagedetector'
|
||||
|
||||
|
||||
import { BackendModule } from 'i18next';
|
||||
|
||||
const LazyImportPlugin: BackendModule = {
|
||||
type: 'backend',
|
||||
init: function () {
|
||||
},
|
||||
read: async function (language, namespace, callback) {
|
||||
|
||||
let baseURL = import.meta.env.BASE_URL
|
||||
if(namespace === "translation") {
|
||||
// If default we load the translation file
|
||||
baseURL+=`/locales/${language}.json`
|
||||
} else {
|
||||
// Else we load the former plugin translation file
|
||||
baseURL+=`/${namespace}/${language}.json`
|
||||
}
|
||||
|
||||
const localeJSON = await fetch(baseURL, {
|
||||
cache: "force-cache"
|
||||
})
|
||||
let json;
|
||||
|
||||
try {
|
||||
json = JSON.parse(await localeJSON.text())
|
||||
} catch(e) {
|
||||
callback(new Error("Error loading"), null);
|
||||
}
|
||||
|
||||
|
||||
callback(null, json);
|
||||
},
|
||||
|
||||
save: function () {
|
||||
},
|
||||
|
||||
create: function () {
|
||||
/* save the missing translation */
|
||||
},
|
||||
};
|
||||
|
||||
i18n
|
||||
.use(LanguageDetector)
|
||||
.use(LazyImportPlugin)
|
||||
.use(initReactI18next)
|
||||
.init(
|
||||
{
|
||||
ns: ['translation','ep_admin_pads'],
|
||||
fallbackLng: 'en'
|
||||
}
|
||||
)
|
||||
|
||||
export default i18n
|
40
admin/src/main.tsx
Normal file
40
admin/src/main.tsx
Normal file
|
@ -0,0 +1,40 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.tsx'
|
||||
import './index.css'
|
||||
import {createBrowserRouter, createRoutesFromElements, Route, RouterProvider} from "react-router-dom";
|
||||
import {HomePage} from "./pages/HomePage.tsx";
|
||||
import {SettingsPage} from "./pages/SettingsPage.tsx";
|
||||
import {LoginScreen} from "./pages/LoginScreen.tsx";
|
||||
import {HelpPage} from "./pages/HelpPage.tsx";
|
||||
import * as Toast from '@radix-ui/react-toast'
|
||||
import {I18nextProvider} from "react-i18next";
|
||||
import i18n from "./localization/i18n.ts";
|
||||
import {PadPage} from "./pages/PadPage.tsx";
|
||||
import {ToastDialog} from "./utils/Toast.tsx";
|
||||
|
||||
const router = createBrowserRouter(createRoutesFromElements(
|
||||
<><Route element={<App/>}>
|
||||
<Route index element={<HomePage/>}/>
|
||||
<Route path="/plugins" element={<HomePage/>}/>
|
||||
<Route path="/settings" element={<SettingsPage/>}/>
|
||||
<Route path="/help" element={<HelpPage/>}/>
|
||||
<Route path="/pads" element={<PadPage/>}/>
|
||||
</Route><Route path="/login">
|
||||
<Route index element={<LoginScreen/>}/>
|
||||
</Route></>
|
||||
), {
|
||||
basename: import.meta.env.BASE_URL
|
||||
})
|
||||
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<Toast.Provider>
|
||||
<ToastDialog/>
|
||||
<RouterProvider router={router}/>
|
||||
</Toast.Provider>
|
||||
</I18nextProvider>
|
||||
</React.StrictMode>,
|
||||
)
|
70
admin/src/pages/HelpPage.tsx
Normal file
70
admin/src/pages/HelpPage.tsx
Normal file
|
@ -0,0 +1,70 @@
|
|||
import {Trans} from "react-i18next";
|
||||
import {useStore} from "../store/store.ts";
|
||||
import {useEffect, useState} from "react";
|
||||
import {HelpObj} from "./Plugin.ts";
|
||||
|
||||
export const HelpPage = () => {
|
||||
const settingsSocket = useStore(state=>state.settingsSocket)
|
||||
const [helpData, setHelpData] = useState<HelpObj>();
|
||||
|
||||
useEffect(() => {
|
||||
if(!settingsSocket) return;
|
||||
settingsSocket?.on('reply:help', (data) => {
|
||||
setHelpData(data)
|
||||
});
|
||||
|
||||
settingsSocket?.emit('help');
|
||||
}, [settingsSocket]);
|
||||
|
||||
const renderHooks = (hooks:Record<string, Record<string, string>>) => {
|
||||
return Object.keys(hooks).map((hookName, i) => {
|
||||
return <div key={hookName+i}>
|
||||
<h3>{hookName}</h3>
|
||||
<ul>
|
||||
{Object.keys(hooks[hookName]).map((hook, i) => <li>{hook}
|
||||
<ul key={hookName+hook+i}>
|
||||
{Object.keys(hooks[hookName][hook]).map((subHook, i) => <li key={i}>{subHook}</li>)}
|
||||
</ul>
|
||||
</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
if (!helpData) return <div></div>
|
||||
|
||||
return <div>
|
||||
<h1><Trans i18nKey="admin_plugins_info.version"/></h1>
|
||||
<div className="help-block">
|
||||
<div><Trans i18nKey="admin_plugins_info.version_number"/></div>
|
||||
<div>{helpData?.epVersion}</div>
|
||||
<div><Trans i18nKey="admin_plugins_info.version_latest"/></div>
|
||||
<div>{helpData.latestVersion}</div>
|
||||
<div>Git sha</div>
|
||||
<div>{helpData.gitCommit}</div>
|
||||
</div>
|
||||
<h2><Trans i18nKey="admin_plugins.installed"/></h2>
|
||||
<ul>
|
||||
{helpData.installedPlugins.map((plugin, i) => <li key={i}>{plugin}</li>)}
|
||||
</ul>
|
||||
|
||||
<h2><Trans i18nKey="admin_plugins_info.parts"/></h2>
|
||||
<ul>
|
||||
{helpData.installedParts.map((part, i) => <li key={i}>{part}</li>)}
|
||||
</ul>
|
||||
|
||||
<h2><Trans i18nKey="admin_plugins_info.hooks"/></h2>
|
||||
{
|
||||
renderHooks(helpData.installedServerHooks)
|
||||
}
|
||||
|
||||
<h2>
|
||||
<Trans i18nKey="admin_plugins_info.hooks_client"/>
|
||||
{
|
||||
renderHooks(helpData.installedClientHooks)
|
||||
}
|
||||
</h2>
|
||||
|
||||
</div>
|
||||
}
|
188
admin/src/pages/HomePage.tsx
Normal file
188
admin/src/pages/HomePage.tsx
Normal file
|
@ -0,0 +1,188 @@
|
|||
import {useStore} from "../store/store.ts";
|
||||
import {useEffect, useMemo, useState} from "react";
|
||||
import {InstalledPlugin, PluginDef, SearchParams} from "./Plugin.ts";
|
||||
import {useDebounce} from "../utils/useDebounce.ts";
|
||||
import {Trans, useTranslation} from "react-i18next";
|
||||
import {SearchField} from "../components/SearchField.tsx";
|
||||
import {Download, Trash} from "lucide-react";
|
||||
import {IconButton} from "../components/IconButton.tsx";
|
||||
|
||||
|
||||
export const HomePage = () => {
|
||||
const pluginsSocket = useStore(state=>state.pluginsSocket)
|
||||
const [plugins,setPlugins] = useState<PluginDef[]>([])
|
||||
const [installedPlugins, setInstalledPlugins] = useState<InstalledPlugin[]>([])
|
||||
const sortedInstalledPlugins = useMemo(()=>{
|
||||
return installedPlugins.sort((a, b)=>{
|
||||
if(a.name < b.name){
|
||||
return -1
|
||||
}
|
||||
if(a.name > b.name){
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
|
||||
} ,[installedPlugins])
|
||||
const [searchParams, setSearchParams] = useState<SearchParams>({
|
||||
offset: 0,
|
||||
limit: 99999,
|
||||
sortBy: 'name',
|
||||
sortDir: 'asc',
|
||||
searchTerm: ''
|
||||
})
|
||||
const [searchTerm, setSearchTerm] = useState<string>('')
|
||||
const {t} = useTranslation()
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if(!pluginsSocket){
|
||||
return
|
||||
}
|
||||
|
||||
pluginsSocket.on('results:installed', (data:{
|
||||
installed: InstalledPlugin[]
|
||||
})=>{
|
||||
setInstalledPlugins(data.installed)
|
||||
})
|
||||
|
||||
pluginsSocket.on('results:updatable', (data) => {
|
||||
data.updatable.forEach((pluginName: string) => {
|
||||
setInstalledPlugins(installedPlugins.map(plugin => {
|
||||
if (plugin.name === pluginName) {
|
||||
return {
|
||||
...plugin,
|
||||
updatable: true
|
||||
}
|
||||
}
|
||||
return plugin
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
pluginsSocket.on('finished:install', () => {
|
||||
pluginsSocket!.emit('getInstalled');
|
||||
})
|
||||
|
||||
pluginsSocket.on('finished:uninstall', () => {
|
||||
console.log("Finished uninstall")
|
||||
})
|
||||
|
||||
|
||||
// Reload on reconnect
|
||||
pluginsSocket.on('connect', ()=>{
|
||||
// Initial retrieval of installed plugins
|
||||
pluginsSocket.emit('getInstalled');
|
||||
pluginsSocket.emit('search', searchParams)
|
||||
})
|
||||
|
||||
pluginsSocket.emit('getInstalled');
|
||||
|
||||
// check for updates every 5mins
|
||||
const interval = setInterval(() => {
|
||||
pluginsSocket.emit('checkUpdates');
|
||||
}, 1000 * 60 * 5);
|
||||
|
||||
return ()=>{
|
||||
clearInterval(interval)
|
||||
}
|
||||
}, [pluginsSocket]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (!pluginsSocket) {
|
||||
return
|
||||
}
|
||||
|
||||
pluginsSocket?.emit('search', searchParams)
|
||||
|
||||
|
||||
pluginsSocket!.on('results:search', (data: {
|
||||
results: PluginDef[]
|
||||
}) => {
|
||||
setPlugins(data.results)
|
||||
})
|
||||
|
||||
|
||||
}, [searchParams, pluginsSocket]);
|
||||
|
||||
const uninstallPlugin = (pluginName: string)=>{
|
||||
pluginsSocket!.emit('uninstall', pluginName);
|
||||
// Remove plugin
|
||||
setInstalledPlugins(installedPlugins.filter(i=>i.name !== pluginName))
|
||||
}
|
||||
|
||||
const installPlugin = (pluginName: string)=>{
|
||||
pluginsSocket!.emit('install', pluginName);
|
||||
setPlugins(plugins.filter(plugin=>plugin.name !== pluginName))
|
||||
}
|
||||
|
||||
|
||||
useDebounce(()=>{
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
offset: 0,
|
||||
searchTerm: searchTerm
|
||||
})
|
||||
}, 500, [searchTerm])
|
||||
|
||||
return <div>
|
||||
<h1><Trans i18nKey="admin_plugins"/></h1>
|
||||
|
||||
<h2><Trans i18nKey="admin_plugins.installed"/></h2>
|
||||
|
||||
<table id="installed-plugins">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><Trans i18nKey="admin_plugins.name"/></th>
|
||||
<th><Trans i18nKey="admin_plugins.version"/></th>
|
||||
<th><Trans i18nKey="ep_admin_pads:ep_adminpads2_action"/></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody style={{overflow: 'auto'}}>
|
||||
{sortedInstalledPlugins.map((plugin, index) => {
|
||||
return <tr key={index}>
|
||||
<td>{plugin.name}</td>
|
||||
<td>{plugin.version}</td>
|
||||
<td>
|
||||
{
|
||||
plugin.updatable ?
|
||||
<button onClick={() => installPlugin(plugin.name)}>Update</button>
|
||||
: <IconButton disabled={plugin.name == "ep_etherpad-lite"} icon={<Trash/>} title={<Trans i18nKey="admin_plugins.installed_uninstall.value"/>} onClick={() => uninstallPlugin(plugin.name)}/>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<h2><Trans i18nKey="admin_plugins.available"/></h2>
|
||||
<SearchField onChange={v=>{setSearchTerm(v.target.value)}} placeholder={t('admin_plugins.available_search.placeholder')} value={searchTerm}/>
|
||||
|
||||
<table id="available-plugins">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><Trans i18nKey="admin_plugins.name"/></th>
|
||||
<th style={{width: '30%'}}><Trans i18nKey="admin_plugins.description"/></th>
|
||||
<th><Trans i18nKey="admin_plugins.version"/></th>
|
||||
<th><Trans i18nKey="admin_plugins.last-update"/></th>
|
||||
<th><Trans i18nKey="ep_admin_pads:ep_adminpads2_action"/></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody style={{overflow: 'auto'}}>
|
||||
{plugins.map((plugin) => {
|
||||
return <tr key={plugin.name}>
|
||||
<td><a rel="noopener noreferrer" href={`https://npmjs.com/${plugin.name}`} target="_blank">{plugin.name}</a></td>
|
||||
<td>{plugin.description}</td>
|
||||
<td>{plugin.version}</td>
|
||||
<td>{plugin.time}</td>
|
||||
<td>
|
||||
<IconButton icon={<Download/>} onClick={() => installPlugin(plugin.name)} title={<Trans i18nKey="admin_plugins.available_install.value"/>}/>
|
||||
</td>
|
||||
</tr>
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue