mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-06-15 18:54:45 -04:00
Compare commits
No commits in common. "develop" and "2.0.2" have entirely different histories.
299 changed files with 43665 additions and 29792 deletions
|
@ -25,5 +25,3 @@ Dockerfile
|
||||||
settings.json
|
settings.json
|
||||||
src/node_modules
|
src/node_modules
|
||||||
admin/node_modules
|
admin/node_modules
|
||||||
ui/node_modules
|
|
||||||
node_modules
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
charset = utf-8
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
end_of_line = lf
|
|
||||||
# editorconfig-tools is unable to ignore longs strings or urls
|
|
||||||
max_line_length = off
|
|
||||||
|
|
||||||
[CHANGELOG.md]
|
|
||||||
indent_size = 4
|
|
||||||
|
|
||||||
[*.bat]
|
|
||||||
end_of_line = crlf
|
|
12
.env.default
12
.env.default
|
@ -4,15 +4,15 @@
|
||||||
# Always ensure to load the env variables in every terminal session.
|
# Always ensure to load the env variables in every terminal session.
|
||||||
# Otherwise the env variables will not be available
|
# Otherwise the env variables will not be available
|
||||||
|
|
||||||
DOCKER_COMPOSE_APP_PORT_PUBLISHED=9001
|
DOCKER_COMPOSE_APP_DEV_PORT_PUBLISHED=9001
|
||||||
DOCKER_COMPOSE_APP_PORT_TARGET=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).
|
# 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.
|
# 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_ENV_DEFAULT_PAD_TEXT="Welcome to etherpad"
|
||||||
|
|
||||||
DOCKER_COMPOSE_APP_ADMIN_PASSWORD=
|
DOCKER_COMPOSE_APP_DEV_ADMIN_PASSWORD=
|
||||||
|
|
||||||
DOCKER_COMPOSE_POSTGRES_DATABASE=db
|
DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_DATABASE=db
|
||||||
DOCKER_COMPOSE_POSTGRES_PASSWORD=etherpad-lite-password
|
DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PASSWORD=etherpad-lite-password
|
||||||
DOCKER_COMPOSE_POSTGRES_USER=etherpad-lite-user
|
DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_USER=etherpad-lite-user
|
|
@ -1,18 +0,0 @@
|
||||||
# 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
|
|
46
.github/workflows/backend-tests.yml
vendored
46
.github/workflows/backend-tests.yml
vendored
|
@ -24,7 +24,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
node: [20, 22, 23]
|
node: [18, 20, 21]
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout repository
|
name: Checkout repository
|
||||||
|
@ -33,10 +33,10 @@ jobs:
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -53,7 +53,7 @@ jobs:
|
||||||
run: pnpm config set auto-install-peers false
|
run: pnpm config set auto-install-peers false
|
||||||
-
|
-
|
||||||
name: Install libreoffice
|
name: Install libreoffice
|
||||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.0
|
uses: awalsh128/cache-apt-pkgs-action@v1.4.2
|
||||||
with:
|
with:
|
||||||
packages: libreoffice libreoffice-pdfimport
|
packages: libreoffice libreoffice-pdfimport
|
||||||
version: 1.0
|
version: 1.0
|
||||||
|
@ -69,9 +69,6 @@ jobs:
|
||||||
-
|
-
|
||||||
name: Run the backend tests
|
name: Run the backend tests
|
||||||
run: pnpm test
|
run: pnpm test
|
||||||
- name: Run the new vitest tests
|
|
||||||
working-directory: src
|
|
||||||
run: pnpm run test:vitest
|
|
||||||
|
|
||||||
withpluginsLinux:
|
withpluginsLinux:
|
||||||
# run on pushes to any branch
|
# run on pushes to any branch
|
||||||
|
@ -84,7 +81,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
node: [20, 22, 23]
|
node: [18, 20, 21]
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout repository
|
name: Checkout repository
|
||||||
|
@ -93,10 +90,10 @@ jobs:
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -113,7 +110,7 @@ jobs:
|
||||||
run: pnpm config set auto-install-peers false
|
run: pnpm config set auto-install-peers false
|
||||||
-
|
-
|
||||||
name: Install libreoffice
|
name: Install libreoffice
|
||||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.0
|
uses: awalsh128/cache-apt-pkgs-action@v1.4.2
|
||||||
with:
|
with:
|
||||||
packages: libreoffice libreoffice-pdfimport
|
packages: libreoffice libreoffice-pdfimport
|
||||||
version: 1.0
|
version: 1.0
|
||||||
|
@ -136,6 +133,7 @@ jobs:
|
||||||
ep_font_size
|
ep_font_size
|
||||||
ep_hash_auth
|
ep_hash_auth
|
||||||
ep_headings2
|
ep_headings2
|
||||||
|
ep_image_upload
|
||||||
ep_markdown
|
ep_markdown
|
||||||
ep_readonly_guest
|
ep_readonly_guest
|
||||||
ep_set_title_on_pad
|
ep_set_title_on_pad
|
||||||
|
@ -145,9 +143,6 @@ jobs:
|
||||||
-
|
-
|
||||||
name: Run the backend tests
|
name: Run the backend tests
|
||||||
run: pnpm test
|
run: pnpm test
|
||||||
- name: Run the new vitest tests
|
|
||||||
working-directory: src
|
|
||||||
run: pnpm run test:vitest
|
|
||||||
|
|
||||||
withoutpluginsWindows:
|
withoutpluginsWindows:
|
||||||
# run on pushes to any branch
|
# run on pushes to any branch
|
||||||
|
@ -165,10 +160,10 @@ jobs:
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -199,11 +194,7 @@ jobs:
|
||||||
powershell -Command "(gc settings.json.holder) -replace '\"points\": 10', '\"points\": 1000' | Out-File -encoding ASCII settings.json"
|
powershell -Command "(gc settings.json.holder) -replace '\"points\": 10', '\"points\": 1000' | Out-File -encoding ASCII settings.json"
|
||||||
-
|
-
|
||||||
name: Run the backend tests
|
name: Run the backend tests
|
||||||
working-directory: src
|
run: cd src && pnpm test
|
||||||
run: pnpm test
|
|
||||||
- name: Run the new vitest tests
|
|
||||||
working-directory: src
|
|
||||||
run: pnpm run test:vitest
|
|
||||||
|
|
||||||
withpluginsWindows:
|
withpluginsWindows:
|
||||||
# run on pushes to any branch
|
# run on pushes to any branch
|
||||||
|
@ -221,11 +212,11 @@ jobs:
|
||||||
-
|
-
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 21
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -258,6 +249,7 @@ jobs:
|
||||||
ep_font_size
|
ep_font_size
|
||||||
ep_hash_auth
|
ep_hash_auth
|
||||||
ep_headings2
|
ep_headings2
|
||||||
|
ep_image_upload
|
||||||
ep_markdown
|
ep_markdown
|
||||||
ep_readonly_guest
|
ep_readonly_guest
|
||||||
ep_set_title_on_pad
|
ep_set_title_on_pad
|
||||||
|
@ -283,8 +275,4 @@ jobs:
|
||||||
powershell -Command "(gc settings.json.holder) -replace '\"points\": 10', '\"points\": 1000' | Out-File -encoding ASCII settings.json"
|
powershell -Command "(gc settings.json.holder) -replace '\"points\": 10', '\"points\": 1000' | Out-File -encoding ASCII settings.json"
|
||||||
-
|
-
|
||||||
name: Run the backend tests
|
name: Run the backend tests
|
||||||
working-directory: src
|
run: cd src && pnpm test
|
||||||
run: pnpm test
|
|
||||||
- name: Run the new vitest tests
|
|
||||||
working-directory: src
|
|
||||||
run: pnpm run test:vitest
|
|
||||||
|
|
4
.github/workflows/build-and-deploy-docs.yml
vendored
4
.github/workflows/build-and-deploy-docs.yml
vendored
|
@ -35,10 +35,10 @@ jobs:
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v5
|
uses: actions/configure-pages@v5
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
39
.github/workflows/docker.yml
vendored
39
.github/workflows/docker.yml
vendored
|
@ -22,9 +22,6 @@ jobs:
|
||||||
-
|
-
|
||||||
name: Check out
|
name: Check out
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
|
||||||
path: etherpad
|
|
||||||
|
|
||||||
-
|
-
|
||||||
name: Set up QEMU
|
name: Set up QEMU
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
|
@ -34,9 +31,9 @@ jobs:
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
-
|
-
|
||||||
name: Build and export to Docker
|
name: Build and export to Docker
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: ./etherpad
|
context: .
|
||||||
target: production
|
target: production
|
||||||
load: true
|
load: true
|
||||||
tags: ${{ env.TEST_TAG }}
|
tags: ${{ env.TEST_TAG }}
|
||||||
|
@ -47,10 +44,10 @@ jobs:
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -65,7 +62,6 @@ jobs:
|
||||||
${{ runner.os }}-pnpm-store-
|
${{ runner.os }}-pnpm-store-
|
||||||
-
|
-
|
||||||
name: Test
|
name: Test
|
||||||
working-directory: etherpad
|
|
||||||
run: |
|
run: |
|
||||||
docker run --rm -d -p 9001:9001 --name test ${{ env.TEST_TAG }}
|
docker run --rm -d -p 9001:9001 --name test ${{ env.TEST_TAG }}
|
||||||
./bin/installDeps.sh
|
./bin/installDeps.sh
|
||||||
|
@ -102,11 +98,10 @@ jobs:
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
-
|
-
|
||||||
name: Build and push
|
name: Build and push
|
||||||
id: build-docker
|
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: ./etherpad
|
context: .
|
||||||
target: production
|
target: production
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
|
@ -116,29 +111,7 @@ jobs:
|
||||||
uses: peter-evans/dockerhub-description@v4
|
uses: peter-evans/dockerhub-description@v4
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
with:
|
with:
|
||||||
readme-filepath: ./etherpad/README.md
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
repository: etherpad/etherpad
|
repository: etherpad/etherpad
|
||||||
enable-url-completion: true
|
enable-url-completion: true
|
||||||
- name: Check out
|
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/develop'
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
path: ether-charts
|
|
||||||
repository: ether/ether-charts
|
|
||||||
token: ${{ secrets.ETHER_CHART_TOKEN }}
|
|
||||||
- name: Update tag in values-dev.yaml
|
|
||||||
if: success() && github.ref == 'refs/heads/develop'
|
|
||||||
working-directory: ether-charts
|
|
||||||
run: |
|
|
||||||
sed -i 's/tag: ".*"/tag: "${{ steps.build-docker.outputs.digest }}"/' values-dev.yaml
|
|
||||||
- name: Commit and push changes
|
|
||||||
working-directory: ether-charts
|
|
||||||
if: success() && github.ref == 'refs/heads/develop'
|
|
||||||
run: |
|
|
||||||
git config --global user.name 'github-actions[bot]'
|
|
||||||
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
|
||||||
git add values-dev.yaml
|
|
||||||
git commit -m 'Update develop image tag'
|
|
||||||
git push
|
|
||||||
|
|
21
.github/workflows/frontend-admin-tests.yml
vendored
21
.github/workflows/frontend-admin-tests.yml
vendored
|
@ -11,13 +11,14 @@ permissions:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
withplugins:
|
withplugins:
|
||||||
|
if: ${{ github.actor != 'dependabot[bot]' }}
|
||||||
name: with plugins
|
name: with plugins
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
node: [20, 22, 23]
|
node: [20, 21]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
|
@ -33,10 +34,10 @@ jobs:
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -49,13 +50,6 @@ jobs:
|
||||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-pnpm-store-
|
${{ runner.os }}-pnpm-store-
|
||||||
- name: Cache playwright binaries
|
|
||||||
uses: actions/cache@v4
|
|
||||||
id: playwright-cache
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cache/ms-playwright
|
|
||||||
key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
|
|
||||||
- name: Only install direct dependencies
|
- name: Only install direct dependencies
|
||||||
run: pnpm config set auto-install-peers false
|
run: pnpm config set auto-install-peers false
|
||||||
#-
|
#-
|
||||||
|
@ -75,7 +69,7 @@ jobs:
|
||||||
# rules.
|
# rules.
|
||||||
-
|
-
|
||||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||||
run: pnpm i
|
run: bin/installDeps.sh
|
||||||
#-
|
#-
|
||||||
# name: Install etherpad plugins
|
# name: Install etherpad plugins
|
||||||
# run: rm -Rf node_modules/ep_align/static/tests/*
|
# run: rm -Rf node_modules/ep_align/static/tests/*
|
||||||
|
@ -91,7 +85,7 @@ jobs:
|
||||||
run: "sed -i 's/\"enableAdminUITests\": false/\"enableAdminUITests\": true,\\n\"users\":{\"admin\":{\"password\":\"changeme1\",\"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
|
name: increase maxHttpBufferSize
|
||||||
run: "sed -i 's/\"maxHttpBufferSize\": 50000/\"maxHttpBufferSize\": 10000000/' settings.json"
|
run: "sed -i 's/\"maxHttpBufferSize\": 10000/\"maxHttpBufferSize\": 10000000/' settings.json"
|
||||||
-
|
-
|
||||||
name: Disable import/export rate limiting
|
name: Disable import/export rate limiting
|
||||||
run: |
|
run: |
|
||||||
|
@ -99,6 +93,7 @@ jobs:
|
||||||
- name: Build admin frontend
|
- name: Build admin frontend
|
||||||
working-directory: admin
|
working-directory: admin
|
||||||
run: |
|
run: |
|
||||||
|
pnpm install
|
||||||
pnpm run build
|
pnpm run build
|
||||||
# name: Run the frontend admin tests
|
# name: Run the frontend admin tests
|
||||||
# shell: bash
|
# shell: bash
|
||||||
|
@ -130,7 +125,7 @@ jobs:
|
||||||
- name: Run the frontend admin tests
|
- name: Run the frontend admin tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
pnpm run prod &
|
pnpm run dev &
|
||||||
connected=false
|
connected=false
|
||||||
can_connect() {
|
can_connect() {
|
||||||
curl -sSfo /dev/null http://localhost:9001/ || return 1
|
curl -sSfo /dev/null http://localhost:9001/ || return 1
|
||||||
|
|
45
.github/workflows/frontend-tests.yml
vendored
45
.github/workflows/frontend-tests.yml
vendored
|
@ -26,11 +26,11 @@ jobs:
|
||||||
-
|
-
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 21
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -56,17 +56,10 @@ jobs:
|
||||||
-
|
-
|
||||||
name: Create settings.json
|
name: Create settings.json
|
||||||
run: cp ./src/tests/settings.json settings.json
|
run: cp ./src/tests/settings.json settings.json
|
||||||
- name: Cache playwright binaries
|
|
||||||
uses: actions/cache@v4
|
|
||||||
id: playwright-cache
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cache/ms-playwright
|
|
||||||
key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
|
|
||||||
- name: Run the frontend tests
|
- name: Run the frontend tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
pnpm run prod &
|
pnpm run dev &
|
||||||
connected=false
|
connected=false
|
||||||
can_connect() {
|
can_connect() {
|
||||||
curl -sSfo /dev/null http://localhost:9001/ || return 1
|
curl -sSfo /dev/null http://localhost:9001/ || return 1
|
||||||
|
@ -99,11 +92,11 @@ jobs:
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 21
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -126,17 +119,10 @@ jobs:
|
||||||
run: echo "::set-output name=sha_short::$(git rev-parse --short ${{ github.sha }})"
|
run: echo "::set-output name=sha_short::$(git rev-parse --short ${{ github.sha }})"
|
||||||
- name: Create settings.json
|
- name: Create settings.json
|
||||||
run: cp ./src/tests/settings.json settings.json
|
run: cp ./src/tests/settings.json settings.json
|
||||||
- name: Cache playwright binaries
|
|
||||||
uses: actions/cache@v4
|
|
||||||
id: playwright-cache
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cache/ms-playwright
|
|
||||||
key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
|
|
||||||
- name: Run the frontend tests
|
- name: Run the frontend tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
pnpm run prod &
|
pnpm run dev &
|
||||||
connected=false
|
connected=false
|
||||||
can_connect() {
|
can_connect() {
|
||||||
curl -sSfo /dev/null http://localhost:9001/ || return 1
|
curl -sSfo /dev/null http://localhost:9001/ || return 1
|
||||||
|
@ -173,18 +159,11 @@ jobs:
|
||||||
-
|
-
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 21
|
||||||
- name: Cache playwright binaries
|
- uses: pnpm/action-setup@v3
|
||||||
uses: actions/cache@v4
|
|
||||||
id: playwright-cache
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cache/ms-playwright
|
|
||||||
key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
|
|
||||||
- uses: pnpm/action-setup@v4
|
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -213,7 +192,7 @@ jobs:
|
||||||
- name: Run the frontend tests
|
- name: Run the frontend tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
pnpm run prod &
|
pnpm run dev &
|
||||||
connected=false
|
connected=false
|
||||||
can_connect() {
|
can_connect() {
|
||||||
curl -sSfo /dev/null http://localhost:9001/ || return 1
|
curl -sSfo /dev/null http://localhost:9001/ || return 1
|
||||||
|
|
12
.github/workflows/load-test.yml
vendored
12
.github/workflows/load-test.yml
vendored
|
@ -29,10 +29,10 @@ jobs:
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -73,10 +73,10 @@ jobs:
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -144,10 +144,10 @@ jobs:
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
4
.github/workflows/perform-type-check.yml
vendored
4
.github/workflows/perform-type-check.yml
vendored
|
@ -26,10 +26,10 @@ jobs:
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
6
.github/workflows/rate-limit.yml
vendored
6
.github/workflows/rate-limit.yml
vendored
|
@ -29,10 +29,10 @@ jobs:
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -48,7 +48,7 @@ jobs:
|
||||||
|
|
||||||
-
|
-
|
||||||
name: docker network
|
name: docker network
|
||||||
run: docker network create --subnet=172.23.0.0/16 ep_net
|
run: docker network create --subnet=172.23.42.0/16 ep_net
|
||||||
-
|
-
|
||||||
name: build docker image
|
name: build docker image
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -24,26 +24,26 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
node: [20, 22, 23]
|
node: [18, 20, 21]
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Check out latest release
|
name: Check out latest release
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: develop #FIXME change to master when doing release
|
ref: master
|
||||||
-
|
-
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Only install direct dependencies
|
- name: Only install direct dependencies
|
||||||
run: pnpm config set auto-install-peers false
|
run: pnpm config set auto-install-peers false
|
||||||
- name: Install libreoffice
|
- name: Install libreoffice
|
||||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.0
|
uses: awalsh128/cache-apt-pkgs-action@v1.4.2
|
||||||
with:
|
with:
|
||||||
packages: libreoffice libreoffice-pdfimport
|
packages: libreoffice libreoffice-pdfimport
|
||||||
version: 1.0
|
version: 1.0
|
||||||
|
@ -62,7 +62,7 @@ jobs:
|
||||||
run: pnpm config set auto-install-peers false
|
run: pnpm config set auto-install-peers false
|
||||||
-
|
-
|
||||||
name: Install libreoffice
|
name: Install libreoffice
|
||||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.0
|
uses: awalsh128/cache-apt-pkgs-action@v1.4.2
|
||||||
with:
|
with:
|
||||||
packages: libreoffice libreoffice-pdfimport
|
packages: libreoffice libreoffice-pdfimport
|
||||||
version: 1.0
|
version: 1.0
|
||||||
|
@ -85,6 +85,7 @@ jobs:
|
||||||
ep_font_size
|
ep_font_size
|
||||||
ep_hash_auth
|
ep_hash_auth
|
||||||
ep_headings2
|
ep_headings2
|
||||||
|
ep_image_upload
|
||||||
ep_markdown
|
ep_markdown
|
||||||
ep_readonly_guest
|
ep_readonly_guest
|
||||||
ep_set_title_on_pad
|
ep_set_title_on_pad
|
||||||
|
|
123
.github/workflows/windows.yml
vendored
123
.github/workflows/windows.yml
vendored
|
@ -14,7 +14,6 @@ permissions:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-zip:
|
build-zip:
|
||||||
permissions: write-all
|
|
||||||
# run on pushes to any branch
|
# run on pushes to any branch
|
||||||
# run on PRs from external forks
|
# run on PRs from external forks
|
||||||
if: |
|
if: |
|
||||||
|
@ -35,11 +34,11 @@ jobs:
|
||||||
-
|
-
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 21
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v3
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
version: 9.0.4
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Get pnpm store directory
|
- name: Get pnpm store directory
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -61,26 +60,112 @@ jobs:
|
||||||
-
|
-
|
||||||
name: Run the backend tests
|
name: Run the backend tests
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
working-directory: src
|
run: cd src && pnpm test
|
||||||
run: pnpm test
|
-
|
||||||
|
name: Build the .zip
|
||||||
|
shell: msys2 {0}
|
||||||
|
run: bin/buildForWindows.sh
|
||||||
|
-
|
||||||
|
name: Archive production artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: etherpad-win.zip
|
||||||
|
path: etherpad-win.zip
|
||||||
|
|
||||||
|
build-exe:
|
||||||
|
if: |
|
||||||
|
(github.event_name != 'pull_request')
|
||||||
|
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
|
||||||
|
name: Build .exe
|
||||||
|
needs: build-zip
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
-
|
||||||
|
name: Download .zip
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: etherpad-win.zip
|
||||||
|
path: ..
|
||||||
|
-
|
||||||
|
name: Extract .zip
|
||||||
|
working-directory: ..
|
||||||
|
run: 7z x etherpad-win.zip -oetherpad-zip
|
||||||
|
-
|
||||||
|
name: Create installer
|
||||||
|
uses: joncloud/makensis-action@v4.1
|
||||||
|
with:
|
||||||
|
script-file: 'bin/nsis/etherpad.nsi'
|
||||||
|
-
|
||||||
|
name: Archive production artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: etherpad-win.exe
|
||||||
|
path: etherpad-win.exe
|
||||||
|
|
||||||
|
deploy-zip:
|
||||||
|
# run on pushes to any branch
|
||||||
|
# run on PRs from external forks
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
if: |
|
||||||
|
(github.event_name != 'pull_request')
|
||||||
|
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
|
||||||
|
name: Deploy
|
||||||
|
needs: build-zip
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Download zip
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: etherpad-win.zip
|
||||||
|
-
|
||||||
|
name: Extract Etherpad
|
||||||
|
run: 7z x etherpad-win.zip -oetherpad
|
||||||
|
-
|
||||||
|
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\installOnWindows.bat
|
||||||
|
working-directory: etherpad
|
||||||
-
|
-
|
||||||
name: Run Etherpad
|
name: Run Etherpad
|
||||||
working-directory: src
|
working-directory: etherpad/src
|
||||||
run: |
|
run: |
|
||||||
pnpm i
|
pnpm install cypress
|
||||||
pnpm exec playwright install --with-deps
|
.\node_modules\.bin\cypress.cmd install --force
|
||||||
pnpm run prod &
|
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
|
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
|
||||||
pnpm exec playwright install chromium --with-deps
|
pnpm exec cypress run --config-file ./tests/frontend/cypress/cypress.config.js
|
||||||
pnpm run test-ui --project=chromium
|
# On release, upload windows zip to GitHub release tab
|
||||||
# On release, create release
|
-
|
||||||
- name: Generate Changelog
|
name: Rename to etherpad-lite-win.zip
|
||||||
if: ${{startsWith(github.ref, 'refs/tags/v') }}
|
shell: powershell
|
||||||
working-directory: bin
|
run: mv etherpad-win.zip etherpad-lite-win.zip
|
||||||
run: pnpm run generateChangelog ${{ github.ref }} > ${{ github.workspace }}-CHANGELOG.txt
|
- name: upload binaries to release
|
||||||
- name: Release
|
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
if: ${{startsWith(github.ref, 'refs/tags/v') }}
|
if: ${{startsWith(github.ref, 'refs/tags/v') }}
|
||||||
with:
|
with:
|
||||||
body_path: ${{ github.workspace }}-CHANGELOG.txt
|
files: etherpad-lite-win.zip
|
||||||
make_latest: true
|
|
||||||
|
|
133
CHANGELOG.md
133
CHANGELOG.md
|
@ -1,108 +1,3 @@
|
||||||
# 2.3.0
|
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
|
||||||
|
|
||||||
- Added possibility to cluster Etherpads behind reverse proxy. There is now a new reverse proxy designed for Etherpads that handles multiple Etherpads and the created pads in them. It will assign the pad assignement to an Etherpad at random but once the choice was made it will always reverse proxy the same backend. This allows to host multiple concurrent Etherpads and benefit from multi core systems even though one Etherpad is singlethreaded.
|
|
||||||
- Added reverse proxy configuration for replacing Nginx. In the past there were some issues with nginx and its configuration. This reverse proxy allows you to handle your configuration with ease.
|
|
||||||
|
|
||||||
If you want to find out more about the reverse proxy method check out the repository https://github.com/ether/etherpad-proxy . It also contains a sample docker-compose file with three Etherpads and one etherpad-proxy. Of course you need to adapt the settings.json.template to your liking and map it into the reverse proxy image before you are ready :).
|
|
||||||
|
|
||||||
|
|
||||||
- Added client authorization to work with Etherpad. Before it would get blocked because it doesn't have the required claim. As this is now fixed etherpad-proxy can also work with your new OAuth2 configuration and retrieve a token via client credentials flow.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 2.2.7
|
|
||||||
|
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
|
||||||
|
|
||||||
- We migrated all important pages to React 19 and React Router v7
|
|
||||||
|
|
||||||
Besides that only dependency updates.
|
|
||||||
|
|
||||||
|
|
||||||
-> Have a merry Christmas and a happy new year. 🎄 🎁
|
|
||||||
|
|
||||||
|
|
||||||
# 2.2.6
|
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
|
||||||
|
|
||||||
- Added option to delete a pad by the creator. This option can be found in the settings menu. When you click on it you get a confirm dialog and after that you have the chance to completely erase the pad.
|
|
||||||
|
|
||||||
|
|
||||||
# 2.2.5
|
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
|
||||||
|
|
||||||
- Fixed timeslider not scrolling when the revision count is a multiple of 100
|
|
||||||
- Added new Restful API for version 2 of Etherpad. It is available at /api-docs
|
|
||||||
|
|
||||||
|
|
||||||
# 2.2.4
|
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
|
||||||
|
|
||||||
- Switched to new SQLite backend
|
|
||||||
- Fixed rusty-store-kv module not found
|
|
||||||
|
|
||||||
|
|
||||||
# 2.2.3
|
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
|
||||||
|
|
||||||
- Introduced a new in process database `rustydb` that represents a fast key value store written in Rust.
|
|
||||||
- Readded window._ as a shortcut for getting text
|
|
||||||
- Added support for migrating any ueberdb database to another. You can now switch as you please. See here: https://docs.etherpad.org/cli.html
|
|
||||||
- Further Typescript movements
|
|
||||||
- A lot of security issues fixed and reviewed in this release. Please update.
|
|
||||||
|
|
||||||
|
|
||||||
# 2.2.2
|
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
|
||||||
|
|
||||||
- Removal of Etherpad require kernel: We finally managed to include esbuild to bundle our frontend code together. So no matter how many plugins your server has it is always one JavaScript file. This boosts performance dramatically.
|
|
||||||
- Added log layoutType: This lets you print the log in either colored or basic (black and white text)
|
|
||||||
- Introduced esbuild for bundling CSS files
|
|
||||||
- Cache all files to be bundled in memory for faster load speed
|
|
||||||
|
|
||||||
|
|
||||||
# 2.1.1
|
|
||||||
|
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
|
||||||
|
|
||||||
- Fixed failing Docker build when checked out as git submodule. Thanks to @neurolabs
|
|
||||||
- Fixed: Fallback to websocket and polling when unknown(old) config is present for socket io
|
|
||||||
- Fixed: Next page disabled if zero page by @samyakj023
|
|
||||||
- On CTRL+CLICK bring the window back to focus by Helder Sepulveda
|
|
||||||
|
|
||||||
# 2.1.0
|
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
|
||||||
|
|
||||||
- Added PWA support. You can now add your Etherpad instance to your home screen on your mobile device or desktop.
|
|
||||||
- Fixed live plugin manager versions clashing. Thanks to @yacchin1205
|
|
||||||
- Fixed a bug in the pad panel where pagination was not working correctly when sorting by pad name
|
|
||||||
|
|
||||||
### Compatibility changes
|
|
||||||
|
|
||||||
- Reintroduced APIKey.txt support. You can now switch between APIKey and OAuth2.0 authentication. This can be toggled with the setting authenticationMethod. The default is OAuth2. If you want to use the APIKey method you can set that to `apikey`.
|
|
||||||
|
|
||||||
|
|
||||||
# 2.0.3
|
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
|
||||||
|
|
||||||
- Added documentation for replacing apikeys with oauth2
|
|
||||||
- Bumped live plugin manager to 0.20.0. Thanks to @fgreinacher
|
|
||||||
- Added better documentation for using docker-compose with Etherpad
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 2.0.2
|
# 2.0.2
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
### Notable enhancements and fixes
|
||||||
|
@ -130,7 +25,7 @@ Besides that only dependency updates.
|
||||||
- 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)
|
- 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.
|
- 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.
|
- 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.
|
- 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
|
### Notable enhancements and fixes
|
||||||
|
|
||||||
|
@ -140,17 +35,17 @@ Besides that only dependency updates.
|
||||||
* Enhancements
|
* 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.
|
- 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.
|
- 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.
|
- 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
|
- 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 plugins i first-plugin second-plugin` or if you want to install from path you can do:
|
- 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 plugins i --path ../path-to-plugin`
|
`pnpm run install-plugins --path ../path-to-plugin`
|
||||||
|
|
||||||
|
|
||||||
# 1.9.7
|
# 1.9.7
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
### Notable enhancements and fixes
|
||||||
|
|
||||||
* Added Live Plugin Manager: Plugins are now installed into a separate folder on the host system. This folder is called `plugin_packages`.
|
* Added Live Plugin Manager: Plugins are now installed into a separate folder on the host system. This folder is called `plugin_packages`.
|
||||||
That way the plugins are separated from the normal etherpad installation.
|
That way the plugins are separated from the normal etherpad installation.
|
||||||
* Make repairPad.js more verbose
|
* Make repairPad.js more verbose
|
||||||
* Fixed favicon not being loaded correctly
|
* Fixed favicon not being loaded correctly
|
||||||
|
@ -173,19 +68,19 @@ That way the plugins are separated from the normal etherpad installation.
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
### Notable enhancements and fixes
|
||||||
|
|
||||||
* The support for the tidy program to tidy up HTML files has been removed. This decision was made because it hasn't been updated for years and also caused an incompability when exporting a pad with Abiword.
|
* The support for the tidy program to tidy up HTML files has been removed. This decision was made because it hasn't been updated for years and also caused an incompability when exporting a pad with Abiword.
|
||||||
|
|
||||||
|
|
||||||
# 1.9.4
|
# 1.9.4
|
||||||
|
|
||||||
### Compatibility changes
|
### Compatibility changes
|
||||||
|
|
||||||
* Log4js has been updated to the latest version. As it involved a bump of 6 major version.
|
* Log4js has been updated to the latest version. As it involved a bump of 6 major version.
|
||||||
A lot has changed since then. Most notably the console appender has been deprecated. You can find out more about it [here](https://github.com/log4js-node/log4js-node)
|
A lot has changed since then. Most notably the console appender has been deprecated. You can find out more about it [here](https://github.com/log4js-node/log4js-node)
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
### Notable enhancements and fixes
|
||||||
|
|
||||||
* Fix for MySQL: The logger calls were incorrectly configured leading to a crash when e.g. somebody uses a different encoding than standard MySQL encoding.
|
* Fix for MySQL: The logger calls were incorrectly configured leading to a crash when e.g. somebody uses a different encoding than standard MySQL encoding.
|
||||||
|
|
||||||
# 1.9.3
|
# 1.9.3
|
||||||
|
|
||||||
|
@ -193,7 +88,7 @@ That way the plugins are separated from the normal etherpad installation.
|
||||||
|
|
||||||
* express-rate-limit has been bumped to 7.0.0: This involves the breaking change that "max: 0"
|
* express-rate-limit has been bumped to 7.0.0: This involves the breaking change that "max: 0"
|
||||||
in the importExportRateLimiting is set to always trigger. So set it to your desired value.
|
in the importExportRateLimiting is set to always trigger. So set it to your desired value.
|
||||||
If you haven't changed that value in the settings.json you are all set.
|
If you haven't changed that value in the settings.json you are all set.
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
### Notable enhancements and fixes
|
||||||
|
|
||||||
|
@ -212,7 +107,7 @@ If you haven't changed that value in the settings.json you are all set.
|
||||||
* Enable session key rotation: This setting can be enabled in the settings.json. It changes the signing key for the cookie authentication in a fixed interval.
|
* Enable session key rotation: This setting can be enabled in the settings.json. It changes the signing key for the cookie authentication in a fixed interval.
|
||||||
|
|
||||||
* Bugfixes
|
* Bugfixes
|
||||||
* Fix appendRevision when creating a new pad via the API without a text.
|
* Fix appendRevision when creating a new pad via the API without a text.
|
||||||
|
|
||||||
|
|
||||||
* Enhancements
|
* Enhancements
|
||||||
|
@ -221,11 +116,11 @@ If you haven't changed that value in the settings.json you are all set.
|
||||||
|
|
||||||
### Compatibility changes
|
### Compatibility changes
|
||||||
|
|
||||||
* No compability changes as JQuery maintains excellent backwards compatibility.
|
* No compability changes as JQuery maintains excellent backwards compatibility.
|
||||||
|
|
||||||
#### For plugin authors
|
#### For plugin authors
|
||||||
|
|
||||||
* Please update to JQuery 3.7. There is an excellent deprecation guide over [here](https://api.jquery.com/category/deprecated/). Version 3.1 to 3.7 are relevant for the upgrade.
|
* Please update to JQuery 3.7. There is an excellent deprecation guide over [here](https://api.jquery.com/category/deprecated/). Version 3.1 to 3.7 are relevant for the upgrade.
|
||||||
|
|
||||||
# 1.9.1
|
# 1.9.1
|
||||||
|
|
||||||
|
@ -233,7 +128,7 @@ If you haven't changed that value in the settings.json you are all set.
|
||||||
|
|
||||||
* Security
|
* Security
|
||||||
* Limit requested revisions in timeslider and export to head revision. (affects v1.9.0)
|
* Limit requested revisions in timeslider and export to head revision. (affects v1.9.0)
|
||||||
|
|
||||||
* Bugfixes
|
* Bugfixes
|
||||||
* revisions in `CHANGESET_REQ` (timeslider) and export (txt, html, custom)
|
* revisions in `CHANGESET_REQ` (timeslider) and export (txt, html, custom)
|
||||||
are now checked to be numbers.
|
are now checked to be numbers.
|
||||||
|
@ -247,7 +142,7 @@ If you haven't changed that value in the settings.json you are all set.
|
||||||
* tests: drop windows 7 test coverage & use chrome latest for admin tests
|
* tests: drop windows 7 test coverage & use chrome latest for admin tests
|
||||||
* Require Node 16 for Etherpad and target Node 20 for testing
|
* Require Node 16 for Etherpad and target Node 20 for testing
|
||||||
|
|
||||||
|
|
||||||
# 1.9.0
|
# 1.9.0
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
### Notable enhancements and fixes
|
||||||
|
|
81
Dockerfile
81
Dockerfile
|
@ -3,17 +3,16 @@
|
||||||
# https://github.com/ether/etherpad-lite
|
# https://github.com/ether/etherpad-lite
|
||||||
#
|
#
|
||||||
# Author: muxator
|
# Author: muxator
|
||||||
ARG BUILD_ENV=git
|
|
||||||
|
|
||||||
FROM node:alpine AS adminbuild
|
FROM node:alpine as adminBuild
|
||||||
RUN npm install -g pnpm@latest
|
|
||||||
WORKDIR /opt/etherpad-lite
|
WORKDIR /opt/etherpad-lite
|
||||||
COPY . .
|
COPY ./ ./
|
||||||
RUN pnpm install
|
RUN cd ./admin && npm install -g pnpm && pnpm install && pnpm run build --outDir ./dist
|
||||||
RUN pnpm run build:ui
|
RUN cd ./ui && pnpm install && pnpm run build --outDir ./dist
|
||||||
|
|
||||||
|
|
||||||
FROM node:alpine AS build
|
FROM node:alpine as build
|
||||||
LABEL maintainer="Etherpad team, https://github.com/ether/etherpad-lite"
|
LABEL maintainer="Etherpad team, https://github.com/ether/etherpad-lite"
|
||||||
|
|
||||||
# Set these arguments when building the image from behind a proxy
|
# Set these arguments when building the image from behind a proxy
|
||||||
|
@ -50,14 +49,6 @@ ARG ETHERPAD_PLUGINS=
|
||||||
# ETHERPAD_LOCAL_PLUGINS="../ep_my_plugin ../ep_another_plugin"
|
# ETHERPAD_LOCAL_PLUGINS="../ep_my_plugin ../ep_another_plugin"
|
||||||
ARG ETHERPAD_LOCAL_PLUGINS=
|
ARG ETHERPAD_LOCAL_PLUGINS=
|
||||||
|
|
||||||
# github plugins to install while building the container. By default no plugins are
|
|
||||||
# installed.
|
|
||||||
# If given a value, it has to be a space-separated, quoted list of plugin names.
|
|
||||||
#
|
|
||||||
# EXAMPLE:
|
|
||||||
# ETHERPAD_GITHUB_PLUGINS="ether/ep_plugin"
|
|
||||||
ARG ETHERPAD_GITHUB_PLUGINS=
|
|
||||||
|
|
||||||
# Control whether abiword will be installed, enabling exports to DOC/PDF/ODT formats.
|
# Control whether abiword will be installed, enabling exports to DOC/PDF/ODT formats.
|
||||||
# By default, it is not installed.
|
# By default, it is not installed.
|
||||||
# If given any value, abiword will be installed.
|
# If given any value, abiword will be installed.
|
||||||
|
@ -100,7 +91,7 @@ RUN mkdir -p "${EP_DIR}" && chown etherpad:etherpad "${EP_DIR}"
|
||||||
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=863199
|
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=863199
|
||||||
RUN \
|
RUN \
|
||||||
mkdir -p /usr/share/man/man1 && \
|
mkdir -p /usr/share/man/man1 && \
|
||||||
npm install pnpm@latest -g && \
|
npm install pnpm -g && \
|
||||||
apk update && apk upgrade && \
|
apk update && apk upgrade && \
|
||||||
apk add --no-cache \
|
apk add --no-cache \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
|
@ -114,65 +105,39 @@ USER etherpad
|
||||||
WORKDIR "${EP_DIR}"
|
WORKDIR "${EP_DIR}"
|
||||||
|
|
||||||
# etherpads version feature requires this. Only copy what is really needed
|
# 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 ${SETTINGS} ./settings.json
|
||||||
COPY --chown=etherpad:etherpad ./var ./var
|
COPY --chown=etherpad:etherpad ./var ./var
|
||||||
COPY --chown=etherpad:etherpad ./bin ./bin
|
COPY --chown=etherpad:etherpad ./bin ./bin
|
||||||
COPY --chown=etherpad:etherpad ./pnpm-workspace.yaml ./package.json ./
|
COPY --chown=etherpad:etherpad ./pnpm-workspace.yaml ./package.json ./
|
||||||
|
|
||||||
|
FROM build as development
|
||||||
|
|
||||||
|
COPY --chown=etherpad:etherpad ./src/package.json .npmrc ./src/
|
||||||
FROM build AS build_git
|
COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/admin/dist ./src/templates/admin
|
||||||
ONBUILD COPY --chown=etherpad:etherpad ./.git/HEA[D] ./.git/HEAD
|
COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/ui/dist ./src/static/oidc
|
||||||
ONBUILD COPY --chown=etherpad:etherpad ./.git/ref[s] ./.git/refs
|
|
||||||
|
|
||||||
FROM build AS build_copy
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
FROM build_${BUILD_ENV} AS development
|
|
||||||
|
|
||||||
ARG ETHERPAD_PLUGINS=
|
|
||||||
ARG ETHERPAD_LOCAL_PLUGINS=
|
|
||||||
ARG ETHERPAD_LOCAL_PLUGINS_ENV=
|
|
||||||
ARG ETHERPAD_GITHUB_PLUGINS=
|
|
||||||
|
|
||||||
COPY --chown=etherpad:etherpad ./src/ ./src/
|
|
||||||
COPY --chown=etherpad:etherpad --from=adminbuild /opt/etherpad-lite/src/ templates/admin./src/templates/admin
|
|
||||||
COPY --chown=etherpad:etherpad --from=adminbuild /opt/etherpad-lite/src/static/oidc ./src/static/oidc
|
|
||||||
|
|
||||||
COPY --chown=etherpad:etherpad ./local_plugin[s] ./local_plugins/
|
|
||||||
|
|
||||||
RUN bash -c ./bin/installLocalPlugins.sh
|
|
||||||
|
|
||||||
RUN bin/installDeps.sh && \
|
RUN bin/installDeps.sh && \
|
||||||
if [ ! -z "${ETHERPAD_PLUGINS}" ] || [ ! -z "${ETHERPAD_GITHUB_PLUGINS}" ]; then \
|
if [ ! -z "${ETHERPAD_PLUGINS}" ] || [ ! -z "${ETHERPAD_LOCAL_PLUGINS}" ]; then \
|
||||||
pnpm run plugins i ${ETHERPAD_PLUGINS} ${ETHERPAD_GITHUB_PLUGINS:+--github ${ETHERPAD_GITHUB_PLUGINS}}; \
|
pnpm run install-plugins ${ETHERPAD_PLUGINS} ${ETHERPAD_LOCAL_PLUGINS:+--path ${ETHERPAD_LOCAL_PLUGINS}}; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
FROM build_${BUILD_ENV} AS production
|
FROM build as production
|
||||||
|
|
||||||
ARG ETHERPAD_PLUGINS=
|
|
||||||
ARG ETHERPAD_LOCAL_PLUGINS=
|
|
||||||
ARG ETHERPAD_LOCAL_PLUGINS_ENV=
|
|
||||||
ARG ETHERPAD_GITHUB_PLUGINS=
|
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENV ETHERPAD_PRODUCTION=true
|
ENV ETHERPAD_PRODUCTION=true
|
||||||
|
|
||||||
COPY --chown=etherpad:etherpad ./src ./src
|
COPY --chown=etherpad:etherpad ./src ./src
|
||||||
COPY --chown=etherpad:etherpad --from=adminbuild /opt/etherpad-lite/src/templates/admin ./src/templates/admin
|
COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/admin/dist ./src/templates/admin
|
||||||
COPY --chown=etherpad:etherpad --from=adminbuild /opt/etherpad-lite/src/static/oidc ./src/static/oidc
|
COPY --chown=etherpad:etherpad --from=adminBuild /opt/etherpad-lite/ui/dist ./src/static/oidc
|
||||||
|
|
||||||
COPY --chown=etherpad:etherpad ./local_plugin[s] ./local_plugins/
|
RUN bin/installDeps.sh && rm -rf ~/.npm && rm -rf ~/.local && rm -rf ~/.cache && \
|
||||||
|
if [ ! -z "${ETHERPAD_PLUGINS}" ] || [ ! -z "${ETHERPAD_LOCAL_PLUGINS}" ]; then \
|
||||||
|
pnpm run install-plugins ${ETHERPAD_PLUGINS} ${ETHERPAD_LOCAL_PLUGINS:+--path ${ETHERPAD_LOCAL_PLUGINS}}; \
|
||||||
|
fi
|
||||||
|
|
||||||
RUN bash -c ./bin/installLocalPlugins.sh
|
|
||||||
|
|
||||||
RUN bin/installDeps.sh && \
|
|
||||||
if [ ! -z "${ETHERPAD_PLUGINS}" ] || [ ! -z "${ETHERPAD_GITHUB_PLUGINS}" ]; then \
|
|
||||||
pnpm run plugins i ${ETHERPAD_PLUGINS} ${ETHERPAD_GITHUB_PLUGINS:+--github ${ETHERPAD_GITHUB_PLUGINS}}; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Copy the configuration file.
|
# Copy the configuration file.
|
||||||
COPY --chown=etherpad:etherpad ${SETTINGS} "${EP_DIR}"/settings.json
|
COPY --chown=etherpad:etherpad ${SETTINGS} "${EP_DIR}"/settings.json
|
||||||
|
|
155
README.md
155
README.md
|
@ -43,74 +43,74 @@ We're looking for maintainers and have some funding available. Please contact J
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Docker-Compose
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
user: "0:0"
|
|
||||||
image: etherpad/etherpad:latest
|
|
||||||
tty: true
|
|
||||||
stdin_open: true
|
|
||||||
volumes:
|
|
||||||
- plugins:/opt/etherpad-lite/src/plugin_packages
|
|
||||||
- etherpad-var:/opt/etherpad-lite/var
|
|
||||||
depends_on:
|
|
||||||
- postgres
|
|
||||||
environment:
|
|
||||||
NODE_ENV: production
|
|
||||||
ADMIN_PASSWORD: ${DOCKER_COMPOSE_APP_ADMIN_PASSWORD:-admin}
|
|
||||||
DB_CHARSET: ${DOCKER_COMPOSE_APP_DB_CHARSET:-utf8mb4}
|
|
||||||
DB_HOST: postgres
|
|
||||||
DB_NAME: ${DOCKER_COMPOSE_POSTGRES_DATABASE:-etherpad}
|
|
||||||
DB_PASS: ${DOCKER_COMPOSE_POSTGRES_PASSWORD:-admin}
|
|
||||||
DB_PORT: ${DOCKER_COMPOSE_POSTGRES_PORT:-5432}
|
|
||||||
DB_TYPE: "postgres"
|
|
||||||
DB_USER: ${DOCKER_COMPOSE_POSTGRES_USER:-admin}
|
|
||||||
# For now, the env var DEFAULT_PAD_TEXT cannot be unset or empty; it seems to be mandatory in the latest version of etherpad
|
|
||||||
DEFAULT_PAD_TEXT: ${DOCKER_COMPOSE_APP_DEFAULT_PAD_TEXT:- }
|
|
||||||
DISABLE_IP_LOGGING: ${DOCKER_COMPOSE_APP_DISABLE_IP_LOGGING:-false}
|
|
||||||
SOFFICE: ${DOCKER_COMPOSE_APP_SOFFICE:-null}
|
|
||||||
TRUST_PROXY: ${DOCKER_COMPOSE_APP_TRUST_PROXY:-true}
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- "${DOCKER_COMPOSE_APP_PORT_PUBLISHED:-9001}:${DOCKER_COMPOSE_APP_PORT_TARGET:-9001}"
|
|
||||||
|
|
||||||
postgres:
|
|
||||||
image: postgres:15-alpine
|
|
||||||
environment:
|
|
||||||
POSTGRES_DB: ${DOCKER_COMPOSE_POSTGRES_DATABASE:-etherpad}
|
|
||||||
POSTGRES_PASSWORD: ${DOCKER_COMPOSE_POSTGRES_PASSWORD:-admin}
|
|
||||||
POSTGRES_PORT: ${DOCKER_COMPOSE_POSTGRES_PORT:-5432}
|
|
||||||
POSTGRES_USER: ${DOCKER_COMPOSE_POSTGRES_USER:-admin}
|
|
||||||
PGDATA: /var/lib/postgresql/data/pgdata
|
|
||||||
restart: always
|
|
||||||
# Exposing the port is not needed unless you want to access this database instance from the host.
|
|
||||||
# Be careful when other postgres docker container are running on the same port
|
|
||||||
# ports:
|
|
||||||
# - "5432:5432"
|
|
||||||
volumes:
|
|
||||||
- postgres_data:/var/lib/postgresql/data
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
postgres_data:
|
|
||||||
plugins:
|
|
||||||
etherpad-var:
|
|
||||||
```
|
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
[Node.js](https://nodejs.org/) >= **18.18.2**.
|
[Node.js](https://nodejs.org/) >= **18.18.2**.
|
||||||
|
|
||||||
### Windows, macOS, Linux
|
### GNU/Linux and other UNIX-like systems
|
||||||
|
|
||||||
1. Download the latest Node.js runtime from [nodejs.org](https://nodejs.org/).
|
#### Quick install on Debian/Ubuntu
|
||||||
2. Install pnpm: `npm install -g pnpm` (Administrator privileges may be required).
|
|
||||||
3. Clone the repository: `git clone -b master`
|
Install the latest Node.js LTS per [official install instructions](https://github.com/nodesource/distributions#installation-instructions), then:
|
||||||
4. Run `pnpm i`
|
```sh
|
||||||
5. Run `pnpm run build:etherpad`
|
git clone --branch master https://github.com/ether/etherpad-lite.git &&
|
||||||
6. Run `pnpm run prod`
|
cd etherpad-lite &&
|
||||||
7. Visit `http://localhost:9001` in your browser.
|
bin/run.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Manual install
|
||||||
|
|
||||||
|
You'll need Git and [Node.js](https://nodejs.org/) installed.
|
||||||
|
|
||||||
|
**As any user (we recommend creating a separate user called etherpad):**
|
||||||
|
|
||||||
|
1. Move to a folder where you want to install Etherpad.
|
||||||
|
2. Clone the Git repository: `git clone --branch master
|
||||||
|
https://github.com/ether/etherpad-lite.git`
|
||||||
|
3. Change into the new directory containing the cloned source code: `cd
|
||||||
|
etherpad-lite`
|
||||||
|
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 `bin/run.sh` will update the dependencies.
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
#### Prebuilt Windows package
|
||||||
|
|
||||||
|
This package runs on any Windows machine. You can perform a manual installation
|
||||||
|
via git for development purposes, but as this uses symlinks which performs
|
||||||
|
unreliably on Windows, please stick to the prebuilt package if possible.
|
||||||
|
|
||||||
|
1. [Download the latest Windows package](https://etherpad.org/#download)
|
||||||
|
2. Extract the folder
|
||||||
|
|
||||||
|
Run `start.bat` and open <http://localhost:9001> in your browser.
|
||||||
|
|
||||||
|
#### Manually install on Windows
|
||||||
|
|
||||||
|
You'll need [Node.js](https://nodejs.org) and (optionally, though recommended)
|
||||||
|
git.
|
||||||
|
|
||||||
|
1. Grab the source, either:
|
||||||
|
* download <https://github.com/ether/etherpad-lite/zipball/master>
|
||||||
|
* or `git clone --branch master
|
||||||
|
https://github.com/ether/etherpad-lite.git`
|
||||||
|
2. With a "Run as administrator" command prompt execute
|
||||||
|
`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
|
||||||
|
`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.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`
|
||||||
|
|
||||||
### Docker container
|
### Docker container
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ Alternatively, you can install plugins from the command line:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd /path/to/etherpad-lite
|
cd /path/to/etherpad-lite
|
||||||
pnpm run plugins i ep_${plugin_name}
|
pnpm run install-plugins ep_${plugin_name}
|
||||||
```
|
```
|
||||||
|
|
||||||
Also see [the plugin wiki
|
Also see [the plugin wiki
|
||||||
|
@ -150,7 +150,7 @@ Run the following command in your Etherpad folder to get all of the features
|
||||||
visible in the above demo gif:
|
visible in the above demo gif:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pnpm run plugins i \
|
pnpm run install-plugins \
|
||||||
ep_align \
|
ep_align \
|
||||||
ep_comments_page \
|
ep_comments_page \
|
||||||
ep_embedded_hyperlinks2 \
|
ep_embedded_hyperlinks2 \
|
||||||
|
@ -174,31 +174,6 @@ following plugins:
|
||||||
that each user's chosen color, display name, comment ownership, etc. is
|
that each user's chosen color, display name, comment ownership, etc. is
|
||||||
strongly linked to their account.
|
strongly linked to their account.
|
||||||
|
|
||||||
### Upgrade Etherpad
|
|
||||||
|
|
||||||
Run the following command in your Etherpad folder to upgrade
|
|
||||||
|
|
||||||
1. Stop any running Etherpad (manual, systemd ...)
|
|
||||||
2. Get present version
|
|
||||||
```sh
|
|
||||||
git -P tag --contains
|
|
||||||
```
|
|
||||||
3. List versions available
|
|
||||||
```sh
|
|
||||||
git -P tag --list "v*" --merged
|
|
||||||
```
|
|
||||||
4. Select the version
|
|
||||||
```sh
|
|
||||||
git checkout v2.2.5
|
|
||||||
git switch -c v2.2.5
|
|
||||||
```
|
|
||||||
5. Upgrade Etherpad
|
|
||||||
```sh
|
|
||||||
./bin/run.sh
|
|
||||||
```
|
|
||||||
6. Stop with [CTRL-C]
|
|
||||||
7. Restart your Etherpad service
|
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
### Tweak the settings
|
### Tweak the settings
|
||||||
|
|
|
@ -1,42 +1,39 @@
|
||||||
{
|
{
|
||||||
"name": "admin",
|
"name": "admin",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2.3.0",
|
"version": "2.0.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
"build-copy": "tsc && vite build --outDir ../src/templates/admin --emptyOutDir",
|
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {},
|
||||||
"@radix-ui/react-switch": "^1.2.5"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@radix-ui/react-dialog": "^1.1.14",
|
"@radix-ui/react-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-toast": "^1.2.14",
|
"@radix-ui/react-toast": "^1.1.5",
|
||||||
"@types/react": "^19.1.8",
|
"i18next": "^23.10.1",
|
||||||
"@types/react-dom": "^19.1.6",
|
"i18next-browser-languagedetector": "^7.2.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.34.0",
|
"lucide-react": "^0.365.0",
|
||||||
"@typescript-eslint/parser": "^8.34.0",
|
"react": "^18.2.0",
|
||||||
"@vitejs/plugin-react-swc": "^3.10.2",
|
"react-dom": "^18.2.0",
|
||||||
"eslint": "^9.28.0",
|
"react-hook-form": "^7.51.2",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"react-i18next": "^14.1.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.20",
|
"react-router-dom": "^6.22.3",
|
||||||
"i18next": "^25.2.1",
|
"zustand": "^4.5.2",
|
||||||
"i18next-browser-languagedetector": "^8.2.0",
|
"@types/react": "^18.2.74",
|
||||||
"lucide-react": "^0.515.0",
|
"@types/react-dom": "^18.2.24",
|
||||||
"react": "^19.1.0",
|
"@typescript-eslint/eslint-plugin": "^7.5.0",
|
||||||
"react-dom": "^19.1.0",
|
"@typescript-eslint/parser": "^7.5.0",
|
||||||
"react-hook-form": "^7.57.0",
|
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||||
"react-i18next": "^15.5.3",
|
"eslint": "^9.0.0",
|
||||||
"react-router-dom": "^7.6.2",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"socket.io-client": "^4.8.1",
|
"eslint-plugin-react-refresh": "^0.4.5",
|
||||||
"typescript": "^5.8.2",
|
"socket.io-client": "^4.7.5",
|
||||||
"vite": "^6.3.5",
|
"typescript": "^5.4.4",
|
||||||
"vite-plugin-static-copy": "^3.0.0",
|
"vite": "^5.2.8",
|
||||||
"vite-plugin-svgr": "^4.3.0",
|
"vite-plugin-static-copy": "^1.0.2",
|
||||||
"zustand": "^5.0.5"
|
"vite-plugin-svgr": "^4.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
"ep_adminpads2_autoupdate.title": "Aktiviert oder deaktiviert automatische Aktualisierungen für die aktuelle Abfrage.",
|
"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_confirm": "Willst du das Pad {{padID}} wirklich löschen?",
|
||||||
"ep_adminpads2_delete.value": "Löschen",
|
"ep_adminpads2_delete.value": "Löschen",
|
||||||
"ep_adminpads2_cleanup": "Historie aufräumen",
|
|
||||||
"ep_adminpads2_last-edited": "Zuletzt bearbeitet",
|
"ep_adminpads2_last-edited": "Zuletzt bearbeitet",
|
||||||
"ep_adminpads2_loading": "Lädt...",
|
"ep_adminpads2_loading": "Lädt...",
|
||||||
"ep_adminpads2_manage-pads": "Pads verwalten",
|
"ep_adminpads2_manage-pads": "Pads verwalten",
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
"ep_adminpads2_autoupdate.title": "Enables or disables automatic updates for the current query.",
|
"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_confirm": "Do you really want to delete the pad {{padID}}?",
|
||||||
"ep_adminpads2_delete.value": "Delete",
|
"ep_adminpads2_delete.value": "Delete",
|
||||||
"ep_adminpads2_cleanup": "Cleanup revisions",
|
|
||||||
"ep_adminpads2_last-edited": "Last edited",
|
"ep_adminpads2_last-edited": "Last edited",
|
||||||
"ep_adminpads2_loading": "Loading…",
|
"ep_adminpads2_loading": "Loading…",
|
||||||
"ep_adminpads2_manage-pads": "Manage pads",
|
"ep_adminpads2_manage-pads": "Manage pads",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {useEffect, useState} from 'react'
|
import {useEffect} from 'react'
|
||||||
import './App.css'
|
import './App.css'
|
||||||
import {connect} from 'socket.io-client'
|
import {connect} from 'socket.io-client'
|
||||||
import {isJSONClean} from './utils/utils.ts'
|
import {isJSONClean} from './utils/utils.ts'
|
||||||
|
@ -6,115 +6,105 @@ import {NavLink, Outlet, useNavigate} from "react-router-dom";
|
||||||
import {useStore} from "./store/store.ts";
|
import {useStore} from "./store/store.ts";
|
||||||
import {LoadingScreen} from "./utils/LoadingScreen.tsx";
|
import {LoadingScreen} from "./utils/LoadingScreen.tsx";
|
||||||
import {Trans, useTranslation} from "react-i18next";
|
import {Trans, useTranslation} from "react-i18next";
|
||||||
import {Cable, Construction, Crown, NotepadText, Wrench, PhoneCall, LucideMenu} from "lucide-react";
|
import {Cable, Construction, Crown, NotepadText, Wrench} from "lucide-react";
|
||||||
|
|
||||||
const WS_URL = import.meta.env.DEV ? 'http://localhost:9001' : ''
|
const WS_URL = import.meta.env.DEV? 'http://localhost:9001' : ''
|
||||||
export const App = () => {
|
export const App = ()=> {
|
||||||
const setSettings = useStore(state => state.setSettings);
|
const setSettings = useStore(state => state.setSettings);
|
||||||
const {t} = useTranslation()
|
const {t} = useTranslation()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [sidebarOpen, setSidebarOpen] = useState<boolean>(true)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch('/admin-auth/', {
|
fetch('/admin-auth/', {
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
}).then((value) => {
|
}).then((value)=>{
|
||||||
if (!value.ok) {
|
if(!value.ok){
|
||||||
navigate('/login')
|
navigate('/login')
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
}).catch(()=>{
|
||||||
navigate('/login')
|
navigate('/login')
|
||||||
})
|
})
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = t('admin.page-title')
|
document.title = t('admin.page-title')
|
||||||
|
|
||||||
useStore.getState().setShowLoading(true);
|
useStore.getState().setShowLoading(true);
|
||||||
const settingSocket = connect(`${WS_URL}/settings`, {
|
const settingSocket = connect(`${WS_URL}/settings`, {
|
||||||
transports: ['websocket'],
|
transports: ['websocket'],
|
||||||
});
|
});
|
||||||
|
|
||||||
const pluginsSocket = connect(`${WS_URL}/pluginfw/installer`, {
|
const pluginsSocket = connect(`${WS_URL}/pluginfw/installer`, {
|
||||||
transports: ['websocket'],
|
transports: ['websocket'],
|
||||||
})
|
})
|
||||||
|
|
||||||
pluginsSocket.on('connect', () => {
|
pluginsSocket.on('connect', () => {
|
||||||
useStore.getState().setPluginsSocket(pluginsSocket);
|
useStore.getState().setPluginsSocket(pluginsSocket);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
settingSocket.on('connect', () => {
|
settingSocket.on('connect', () => {
|
||||||
useStore.getState().setSettingsSocket(settingSocket);
|
useStore.getState().setSettingsSocket(settingSocket);
|
||||||
useStore.getState().setShowLoading(false)
|
useStore.getState().setShowLoading(false)
|
||||||
settingSocket.emit('load');
|
settingSocket.emit('load');
|
||||||
console.log('connected');
|
console.log('connected');
|
||||||
});
|
});
|
||||||
|
|
||||||
settingSocket.on('disconnect', (reason) => {
|
settingSocket.on('disconnect', (reason) => {
|
||||||
// The settingSocket.io client will automatically try to reconnect for all reasons other than "io
|
// The settingSocket.io client will automatically try to reconnect for all reasons other than "io
|
||||||
// server disconnect".
|
// server disconnect".
|
||||||
useStore.getState().setShowLoading(true)
|
useStore.getState().setShowLoading(true)
|
||||||
if (reason === 'io server disconnect') {
|
if (reason === 'io server disconnect') {
|
||||||
settingSocket.connect();
|
settingSocket.connect();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
settingSocket.on('settings', (settings) => {
|
settingSocket.on('settings', (settings) => {
|
||||||
/* Check whether the settings.json is authorized to be viewed */
|
/* Check whether the settings.json is authorized to be viewed */
|
||||||
if (settings.results === 'NOT_ALLOWED') {
|
if (settings.results === 'NOT_ALLOWED') {
|
||||||
console.log('Not allowed to view settings.json')
|
console.log('Not allowed to view settings.json')
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check to make sure the JSON is clean before proceeding */
|
/* Check to make sure the JSON is clean before proceeding */
|
||||||
if (isJSONClean(settings.results)) {
|
if (isJSONClean(settings.results)) {
|
||||||
setSettings(settings.results);
|
setSettings(settings.results);
|
||||||
} else {
|
} else {
|
||||||
alert('Invalid JSON');
|
alert('Invalid JSON');
|
||||||
}
|
}
|
||||||
useStore.getState().setShowLoading(false);
|
useStore.getState().setShowLoading(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
settingSocket.on('saveprogress', (status) => {
|
settingSocket.on('saveprogress', (status)=>{
|
||||||
console.log(status)
|
console.log(status)
|
||||||
})
|
})
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
settingSocket.disconnect();
|
settingSocket.disconnect();
|
||||||
pluginsSocket.disconnect()
|
pluginsSocket.disconnect()
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <div id="wrapper" className={`${sidebarOpen ? '': 'closed' }`}>
|
return <div id="wrapper">
|
||||||
<LoadingScreen/>
|
<LoadingScreen/>
|
||||||
<div className="menu">
|
<div className="menu">
|
||||||
<div className="inner-menu">
|
<div className="inner-menu">
|
||||||
<span>
|
<span>
|
||||||
<Crown width={40} height={40}/>
|
<Crown width={40} height={40}/>
|
||||||
<h1>Etherpad</h1>
|
<h1>Etherpad</h1>
|
||||||
</span>
|
</span>
|
||||||
<ul onClick={()=>{
|
<ul>
|
||||||
if (window.innerWidth < 768) {
|
<li><NavLink to="/plugins"><Cable/><Trans i18nKey="admin_plugins"/></NavLink></li>
|
||||||
setSidebarOpen(false)
|
<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>
|
||||||
<li><NavLink to="/plugins"><Cable/><Trans i18nKey="admin_plugins"/></NavLink></li>
|
</ul>
|
||||||
<li><NavLink to={"/settings"}><Wrench/><Trans i18nKey="admin_settings"/></NavLink></li>
|
</div>
|
||||||
<li><NavLink to={"/help"}> <Construction/> <Trans i18nKey="admin_plugins_info"/></NavLink></li>
|
</div>
|
||||||
<li><NavLink to={"/pads"}><NotepadText/><Trans
|
<div className="innerwrapper">
|
||||||
i18nKey="ep_admin_pads:ep_adminpads2_manage-pads"/></NavLink></li>
|
<Outlet/>
|
||||||
<li><NavLink to={"/shout"}><PhoneCall/>Communication</NavLink></li>
|
</div>
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<button id="icon-button" onClick={() => {
|
|
||||||
setSidebarOpen(!sidebarOpen)
|
|
||||||
}}><LucideMenu/></button>
|
|
||||||
<div className="innerwrapper">
|
|
||||||
<Outlet/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App
|
export default App
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {FC, JSX, ReactElement} from "react";
|
import {FC, ReactElement} from "react";
|
||||||
|
|
||||||
export type IconButtonProps = {
|
export type IconButtonProps = {
|
||||||
icon: JSX.Element,
|
icon: JSX.Element,
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
export type ShoutType = {
|
|
||||||
type: string,
|
|
||||||
data:{
|
|
||||||
type: string,
|
|
||||||
payload: {
|
|
||||||
message: {
|
|
||||||
message: string,
|
|
||||||
sticky: boolean
|
|
||||||
},
|
|
||||||
timestamp: number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -12,7 +12,6 @@ import {I18nextProvider} from "react-i18next";
|
||||||
import i18n from "./localization/i18n.ts";
|
import i18n from "./localization/i18n.ts";
|
||||||
import {PadPage} from "./pages/PadPage.tsx";
|
import {PadPage} from "./pages/PadPage.tsx";
|
||||||
import {ToastDialog} from "./utils/Toast.tsx";
|
import {ToastDialog} from "./utils/Toast.tsx";
|
||||||
import {ShoutPage} from "./pages/ShoutPage.tsx";
|
|
||||||
|
|
||||||
const router = createBrowserRouter(createRoutesFromElements(
|
const router = createBrowserRouter(createRoutesFromElements(
|
||||||
<><Route element={<App/>}>
|
<><Route element={<App/>}>
|
||||||
|
@ -21,7 +20,6 @@ const router = createBrowserRouter(createRoutesFromElements(
|
||||||
<Route path="/settings" element={<SettingsPage/>}/>
|
<Route path="/settings" element={<SettingsPage/>}/>
|
||||||
<Route path="/help" element={<HelpPage/>}/>
|
<Route path="/help" element={<HelpPage/>}/>
|
||||||
<Route path="/pads" element={<PadPage/>}/>
|
<Route path="/pads" element={<PadPage/>}/>
|
||||||
<Route path="/shout" element={<ShoutPage/>}/>
|
|
||||||
</Route><Route path="/login">
|
</Route><Route path="/login">
|
||||||
<Route index element={<LoginScreen/>}/>
|
<Route index element={<LoginScreen/>}/>
|
||||||
</Route></>
|
</Route></>
|
||||||
|
|
|
@ -21,7 +21,7 @@ export const HelpPage = () => {
|
||||||
return <div key={hookName+i}>
|
return <div key={hookName+i}>
|
||||||
<h3>{hookName}</h3>
|
<h3>{hookName}</h3>
|
||||||
<ul>
|
<ul>
|
||||||
{Object.keys(hooks[hookName]).map((hook, i) => <li key={hook+i}>{hook}
|
{Object.keys(hooks[hookName]).map((hook, i) => <li>{hook}
|
||||||
<ul key={hookName+hook+i}>
|
<ul key={hookName+hook+i}>
|
||||||
{Object.keys(hooks[hookName][hook]).map((subHook, i) => <li key={i}>{subHook}</li>)}
|
{Object.keys(hooks[hookName][hook]).map((subHook, i) => <li key={i}>{subHook}</li>)}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -46,12 +46,12 @@ export const HelpPage = () => {
|
||||||
</div>
|
</div>
|
||||||
<h2><Trans i18nKey="admin_plugins.installed"/></h2>
|
<h2><Trans i18nKey="admin_plugins.installed"/></h2>
|
||||||
<ul>
|
<ul>
|
||||||
{helpData.installedPlugins.map((plugin, i) => <li key={plugin+i}>{plugin}</li>)}
|
{helpData.installedPlugins.map((plugin, i) => <li key={i}>{plugin}</li>)}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2><Trans i18nKey="admin_plugins_info.parts"/></h2>
|
<h2><Trans i18nKey="admin_plugins_info.parts"/></h2>
|
||||||
<ul>
|
<ul>
|
||||||
{helpData.installedParts.map((part, i) => <li key={part+i}>{part}</li>)}
|
{helpData.installedParts.map((part, i) => <li key={i}>{part}</li>)}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2><Trans i18nKey="admin_plugins_info.hooks"/></h2>
|
<h2><Trans i18nKey="admin_plugins_info.hooks"/></h2>
|
||||||
|
|
|
@ -4,54 +4,16 @@ import {InstalledPlugin, PluginDef, SearchParams} from "./Plugin.ts";
|
||||||
import {useDebounce} from "../utils/useDebounce.ts";
|
import {useDebounce} from "../utils/useDebounce.ts";
|
||||||
import {Trans, useTranslation} from "react-i18next";
|
import {Trans, useTranslation} from "react-i18next";
|
||||||
import {SearchField} from "../components/SearchField.tsx";
|
import {SearchField} from "../components/SearchField.tsx";
|
||||||
import {ArrowUpFromDot, Download, Trash} from "lucide-react";
|
import {Download, Trash} from "lucide-react";
|
||||||
import {IconButton} from "../components/IconButton.tsx";
|
import {IconButton} from "../components/IconButton.tsx";
|
||||||
import {determineSorting} from "../utils/sorting.ts";
|
|
||||||
|
|
||||||
|
|
||||||
export const HomePage = () => {
|
export const HomePage = () => {
|
||||||
const pluginsSocket = useStore(state=>state.pluginsSocket)
|
const pluginsSocket = useStore(state=>state.pluginsSocket)
|
||||||
const [plugins,setPlugins] = useState<PluginDef[]>([])
|
const [plugins,setPlugins] = useState<PluginDef[]>([])
|
||||||
const installedPlugins = useStore(state=>state.installedPlugins)
|
const [installedPlugins, setInstalledPlugins] = useState<InstalledPlugin[]>([])
|
||||||
const setInstalledPlugins = useStore(state=>state.setInstalledPlugins)
|
|
||||||
const [searchParams, setSearchParams] = useState<SearchParams>({
|
|
||||||
offset: 0,
|
|
||||||
limit: 99999,
|
|
||||||
sortBy: 'name',
|
|
||||||
sortDir: 'asc',
|
|
||||||
searchTerm: ''
|
|
||||||
})
|
|
||||||
|
|
||||||
const filteredInstallablePlugins = useMemo(()=>{
|
|
||||||
return plugins.sort((a, b)=>{
|
|
||||||
if(searchParams.sortBy === "version"){
|
|
||||||
if(searchParams.sortDir === "asc"){
|
|
||||||
return a.version.localeCompare(b.version)
|
|
||||||
}
|
|
||||||
return b.version.localeCompare(a.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
if(searchParams.sortBy === "last-updated"){
|
|
||||||
if(searchParams.sortDir === "asc"){
|
|
||||||
return a.time.localeCompare(b.time)
|
|
||||||
}
|
|
||||||
return b.time.localeCompare(a.time)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (searchParams.sortBy === "name") {
|
|
||||||
if(searchParams.sortDir === "asc"){
|
|
||||||
return a.name.localeCompare(b.name)
|
|
||||||
}
|
|
||||||
return b.name.localeCompare(a.name)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
})
|
|
||||||
}, [plugins, searchParams])
|
|
||||||
|
|
||||||
const sortedInstalledPlugins = useMemo(()=>{
|
const sortedInstalledPlugins = useMemo(()=>{
|
||||||
return useStore.getState().installedPlugins.sort((a, b)=>{
|
return installedPlugins.sort((a, b)=>{
|
||||||
|
|
||||||
if(a.name < b.name){
|
if(a.name < b.name){
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
@ -61,8 +23,14 @@ export const HomePage = () => {
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
|
|
||||||
} ,[installedPlugins, searchParams])
|
} ,[installedPlugins])
|
||||||
|
const [searchParams, setSearchParams] = useState<SearchParams>({
|
||||||
|
offset: 0,
|
||||||
|
limit: 99999,
|
||||||
|
sortBy: 'name',
|
||||||
|
sortDir: 'asc',
|
||||||
|
searchTerm: ''
|
||||||
|
})
|
||||||
const [searchTerm, setSearchTerm] = useState<string>('')
|
const [searchTerm, setSearchTerm] = useState<string>('')
|
||||||
const {t} = useTranslation()
|
const {t} = useTranslation()
|
||||||
|
|
||||||
|
@ -79,16 +47,17 @@ export const HomePage = () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
pluginsSocket.on('results:updatable', (data) => {
|
pluginsSocket.on('results:updatable', (data) => {
|
||||||
const newInstalledPlugins = useStore.getState().installedPlugins.map(plugin => {
|
data.updatable.forEach((pluginName: string) => {
|
||||||
if (data.updatable.includes(plugin.name)) {
|
setInstalledPlugins(installedPlugins.map(plugin => {
|
||||||
return {
|
if (plugin.name === pluginName) {
|
||||||
...plugin,
|
return {
|
||||||
updatable: true
|
...plugin,
|
||||||
}
|
updatable: true
|
||||||
}
|
}
|
||||||
return plugin
|
}
|
||||||
})
|
return plugin
|
||||||
setInstalledPlugins(newInstalledPlugins)
|
}))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
pluginsSocket.on('finished:install', () => {
|
pluginsSocket.on('finished:install', () => {
|
||||||
|
@ -124,20 +93,17 @@ export const HomePage = () => {
|
||||||
if (!pluginsSocket) {
|
if (!pluginsSocket) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pluginsSocket?.emit('search', searchParams)
|
pluginsSocket?.emit('search', searchParams)
|
||||||
|
|
||||||
|
|
||||||
pluginsSocket!.on('results:search', (data: {
|
pluginsSocket!.on('results:search', (data: {
|
||||||
results: PluginDef[]
|
results: PluginDef[]
|
||||||
}) => {
|
}) => {
|
||||||
setPlugins(data.results)
|
setPlugins(data.results)
|
||||||
})
|
})
|
||||||
pluginsSocket!.on('results:searcherror', (data: {error: string}) => {
|
|
||||||
console.log(data.error)
|
|
||||||
useStore.getState().setToastState({
|
|
||||||
open: true,
|
|
||||||
title: "Error retrieving plugins",
|
|
||||||
success: false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}, [searchParams, pluginsSocket]);
|
}, [searchParams, pluginsSocket]);
|
||||||
|
|
||||||
const uninstallPlugin = (pluginName: string)=>{
|
const uninstallPlugin = (pluginName: string)=>{
|
||||||
|
@ -151,6 +117,7 @@ export const HomePage = () => {
|
||||||
setPlugins(plugins.filter(plugin=>plugin.name !== pluginName))
|
setPlugins(plugins.filter(plugin=>plugin.name !== pluginName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
useDebounce(()=>{
|
useDebounce(()=>{
|
||||||
setSearchParams({
|
setSearchParams({
|
||||||
...searchParams,
|
...searchParams,
|
||||||
|
@ -159,7 +126,6 @@ export const HomePage = () => {
|
||||||
})
|
})
|
||||||
}, 500, [searchTerm])
|
}, 500, [searchTerm])
|
||||||
|
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<h1><Trans i18nKey="admin_plugins"/></h1>
|
<h1><Trans i18nKey="admin_plugins"/></h1>
|
||||||
|
|
||||||
|
@ -176,72 +142,47 @@ export const HomePage = () => {
|
||||||
<tbody style={{overflow: 'auto'}}>
|
<tbody style={{overflow: 'auto'}}>
|
||||||
{sortedInstalledPlugins.map((plugin, index) => {
|
{sortedInstalledPlugins.map((plugin, index) => {
|
||||||
return <tr key={index}>
|
return <tr key={index}>
|
||||||
<td><a rel="noopener noreferrer" href={`https://npmjs.com/${plugin.name}`} target="_blank">{plugin.name}</a></td>
|
<td>{plugin.name}</td>
|
||||||
<td>{plugin.version}</td>
|
<td>{plugin.version}</td>
|
||||||
<td>
|
<td>
|
||||||
{
|
{
|
||||||
plugin.updatable ?
|
plugin.updatable ?
|
||||||
<IconButton onClick={() => installPlugin(plugin.name)} icon={<ArrowUpFromDot/>} title="Update"></IconButton>
|
<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)}/>
|
: <IconButton disabled={plugin.name == "ep_etherpad-lite"} icon={<Trash/>} title={<Trans i18nKey="admin_plugins.installed_uninstall.value"/>} onClick={() => uninstallPlugin(plugin.name)}/>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
<h2><Trans i18nKey="admin_plugins.available"/></h2>
|
<h2><Trans i18nKey="admin_plugins.available"/></h2>
|
||||||
<SearchField onChange={v=>{setSearchTerm(v.target.value)}} placeholder={t('admin_plugins.available_search.placeholder')} value={searchTerm}/>
|
<SearchField onChange={v=>{setSearchTerm(v.target.value)}} placeholder={t('admin_plugins.available_search.placeholder')} value={searchTerm}/>
|
||||||
|
|
||||||
<div className="table-container">
|
|
||||||
<table id="available-plugins">
|
<table id="available-plugins">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className={determineSorting(searchParams.sortBy, searchParams.sortDir == "asc", 'name')} onClick={()=>{
|
<th><Trans i18nKey="admin_plugins.name"/></th>
|
||||||
setSearchParams({
|
|
||||||
...searchParams,
|
|
||||||
sortBy: 'name',
|
|
||||||
sortDir: searchParams.sortDir === "asc"? "desc": "asc"
|
|
||||||
})
|
|
||||||
}}>
|
|
||||||
<Trans i18nKey="admin_plugins.name" /></th>
|
|
||||||
<th style={{width: '30%'}}><Trans i18nKey="admin_plugins.description"/></th>
|
<th style={{width: '30%'}}><Trans i18nKey="admin_plugins.description"/></th>
|
||||||
<th className={determineSorting(searchParams.sortBy, searchParams.sortDir == "asc", 'version')} onClick={()=>{
|
<th><Trans i18nKey="admin_plugins.version"/></th>
|
||||||
setSearchParams({
|
<th><Trans i18nKey="admin_plugins.last-update"/></th>
|
||||||
...searchParams,
|
|
||||||
sortBy: 'version',
|
|
||||||
sortDir: searchParams.sortDir === "asc"? "desc": "asc"
|
|
||||||
})
|
|
||||||
}}><Trans i18nKey="admin_plugins.version"/></th>
|
|
||||||
<th className={determineSorting(searchParams.sortBy, searchParams.sortDir == "asc", 'last-updated')} onClick={()=>{
|
|
||||||
setSearchParams({
|
|
||||||
...searchParams,
|
|
||||||
sortBy: 'last-updated',
|
|
||||||
sortDir: searchParams.sortDir === "asc"? "desc": "asc"
|
|
||||||
})
|
|
||||||
}}><Trans i18nKey="admin_plugins.last-update"/></th>
|
|
||||||
<th><Trans i18nKey="ep_admin_pads:ep_adminpads2_action"/></th>
|
<th><Trans i18nKey="ep_admin_pads:ep_adminpads2_action"/></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody style={{overflow: 'auto'}}>
|
<tbody style={{overflow: 'auto'}}>
|
||||||
{(filteredInstallablePlugins.length > 0) ?
|
{plugins.map((plugin) => {
|
||||||
filteredInstallablePlugins.map((plugin) => {
|
return <tr key={plugin.name}>
|
||||||
return <tr key={plugin.name}>
|
<td><a rel="noopener noreferrer" href={`https://npmjs.com/${plugin.name}`} target="_blank">{plugin.name}</a></td>
|
||||||
<td><a rel="noopener noreferrer" href={`https://npmjs.com/${plugin.name}`} target="_blank">{plugin.name}</a></td>
|
<td>{plugin.description}</td>
|
||||||
<td>{plugin.description}</td>
|
<td>{plugin.version}</td>
|
||||||
<td>{plugin.version}</td>
|
<td>{plugin.time}</td>
|
||||||
<td>{plugin.time}</td>
|
<td>
|
||||||
<td>
|
<IconButton icon={<Download/>} onClick={() => installPlugin(plugin.name)} title={<Trans i18nKey="admin_plugins.available_install.value"/>}/>
|
||||||
<IconButton icon={<Download/>} onClick={() => installPlugin(plugin.name)} title={<Trans i18nKey="admin_plugins.available_install.value"/>}/>
|
</td>
|
||||||
</td>
|
</tr>
|
||||||
</tr>
|
})}
|
||||||
})
|
|
||||||
:
|
|
||||||
<tr><td colSpan={5}>{searchTerm == '' ? <Trans i18nKey="pad.loading"/>: <Trans i18nKey="admin_plugins.available_not-found"/>}</td></tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {useDebounce} from "../utils/useDebounce.ts";
|
||||||
import {determineSorting} from "../utils/sorting.ts";
|
import {determineSorting} from "../utils/sorting.ts";
|
||||||
import * as Dialog from "@radix-ui/react-dialog";
|
import * as Dialog from "@radix-ui/react-dialog";
|
||||||
import {IconButton} from "../components/IconButton.tsx";
|
import {IconButton} from "../components/IconButton.tsx";
|
||||||
import {ChevronLeft, ChevronRight, Eye, Trash2, FileStack} from "lucide-react";
|
import {ChevronLeft, ChevronRight, Eye, Trash2} from "lucide-react";
|
||||||
import {SearchField} from "../components/SearchField.tsx";
|
import {SearchField} from "../components/SearchField.tsx";
|
||||||
|
|
||||||
export const PadPage = ()=>{
|
export const PadPage = ()=>{
|
||||||
|
@ -23,11 +23,10 @@ export const PadPage = ()=>{
|
||||||
const pads = useStore(state=>state.pads)
|
const pads = useStore(state=>state.pads)
|
||||||
const [currentPage, setCurrentPage] = useState<number>(0)
|
const [currentPage, setCurrentPage] = useState<number>(0)
|
||||||
const [deleteDialog, setDeleteDialog] = useState<boolean>(false)
|
const [deleteDialog, setDeleteDialog] = useState<boolean>(false)
|
||||||
const [errorText, setErrorText] = useState<string|null>(null)
|
|
||||||
const [padToDelete, setPadToDelete] = useState<string>('')
|
const [padToDelete, setPadToDelete] = useState<string>('')
|
||||||
const pages = useMemo(()=>{
|
const pages = useMemo(()=>{
|
||||||
if(!pads){
|
if(!pads){
|
||||||
return 0;
|
return [0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return Math.ceil(pads!.total / searchParams.limit)
|
return Math.ceil(pads!.total / searchParams.limit)
|
||||||
|
@ -69,35 +68,12 @@ export const PadPage = ()=>{
|
||||||
results: newPads
|
results: newPads
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
settingsSocket.on('results:cleanupPadRevisions', (data)=>{
|
|
||||||
let newPads = useStore.getState().pads?.results ?? []
|
|
||||||
|
|
||||||
if (data.error) {
|
|
||||||
setErrorText(data.error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
newPads.forEach((pad)=>{
|
|
||||||
if (pad.padName === data.padId) {
|
|
||||||
pad.revisionNumber = data.keepRevisions
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
useStore.getState().setPads({
|
|
||||||
results: newPads,
|
|
||||||
total: useStore.getState().pads!.total
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}, [settingsSocket, pads]);
|
}, [settingsSocket, pads]);
|
||||||
|
|
||||||
const deletePad = (padID: string)=>{
|
const deletePad = (padID: string)=>{
|
||||||
settingsSocket?.emit('deletePad', padID)
|
settingsSocket?.emit('deletePad', padID)
|
||||||
}
|
}
|
||||||
|
|
||||||
const cleanupPad = (padID: string)=>{
|
|
||||||
settingsSocket?.emit('cleanupPadRevisions', padID)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
|
@ -124,26 +100,11 @@ export const PadPage = ()=>{
|
||||||
</Dialog.Content>
|
</Dialog.Content>
|
||||||
</Dialog.Portal>
|
</Dialog.Portal>
|
||||||
</Dialog.Root>
|
</Dialog.Root>
|
||||||
<Dialog.Root open={errorText !== null}>
|
|
||||||
<Dialog.Portal>
|
|
||||||
<Dialog.Overlay className="dialog-confirm-overlay"/>
|
|
||||||
<Dialog.Content className="dialog-confirm-content">
|
|
||||||
<div>
|
|
||||||
<div>Error occured: {errorText}</div>
|
|
||||||
<div className="settings-button-bar">
|
|
||||||
<button onClick={() => {
|
|
||||||
setErrorText(null)
|
|
||||||
}}>OK</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Dialog.Content>
|
|
||||||
</Dialog.Portal>
|
|
||||||
</Dialog.Root>
|
|
||||||
<h1><Trans i18nKey="ep_admin_pads:ep_adminpads2_manage-pads"/></h1>
|
<h1><Trans i18nKey="ep_admin_pads:ep_adminpads2_manage-pads"/></h1>
|
||||||
<SearchField value={searchTerm} onChange={v=>setSearchTerm(v.target.value)} placeholder={t('ep_admin_pads:ep_adminpads2_search-heading')}/>
|
<SearchField value={searchTerm} onChange={v=>setSearchTerm(v.target.value)} placeholder={t('ep_admin_pads:ep_adminpads2_search-heading')}/>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr className="search-pads">
|
<tr>
|
||||||
<th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'padName')} onClick={()=>{
|
<th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'padName')} onClick={()=>{
|
||||||
setSearchParams({
|
setSearchParams({
|
||||||
...searchParams,
|
...searchParams,
|
||||||
|
@ -151,19 +112,19 @@ export const PadPage = ()=>{
|
||||||
ascending: !searchParams.ascending
|
ascending: !searchParams.ascending
|
||||||
})
|
})
|
||||||
}}><Trans i18nKey="ep_admin_pads:ep_adminpads2_padname"/></th>
|
}}><Trans i18nKey="ep_admin_pads:ep_adminpads2_padname"/></th>
|
||||||
<th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'userCount')} onClick={()=>{
|
|
||||||
setSearchParams({
|
|
||||||
...searchParams,
|
|
||||||
sortBy: 'userCount',
|
|
||||||
ascending: !searchParams.ascending
|
|
||||||
})
|
|
||||||
}}><Trans i18nKey="ep_admin_pads:ep_adminpads2_pad-user-count"/></th>
|
|
||||||
<th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'lastEdited')} onClick={()=>{
|
<th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'lastEdited')} onClick={()=>{
|
||||||
setSearchParams({
|
setSearchParams({
|
||||||
...searchParams,
|
...searchParams,
|
||||||
sortBy: 'lastEdited',
|
sortBy: 'lastEdited',
|
||||||
ascending: !searchParams.ascending
|
ascending: !searchParams.ascending
|
||||||
})
|
})
|
||||||
|
}}><Trans i18nKey="ep_admin_pads:ep_adminpads2_pad-user-count"/></th>
|
||||||
|
<th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'userCount')} onClick={()=>{
|
||||||
|
setSearchParams({
|
||||||
|
...searchParams,
|
||||||
|
sortBy: 'userCount',
|
||||||
|
ascending: !searchParams.ascending
|
||||||
|
})
|
||||||
}}><Trans i18nKey="ep_admin_pads:ep_adminpads2_last-edited"/></th>
|
}}><Trans i18nKey="ep_admin_pads:ep_adminpads2_last-edited"/></th>
|
||||||
<th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'revisionNumber')} onClick={()=>{
|
<th className={determineSorting(searchParams.sortBy, searchParams.ascending, 'revisionNumber')} onClick={()=>{
|
||||||
setSearchParams({
|
setSearchParams({
|
||||||
|
@ -175,7 +136,7 @@ export const PadPage = ()=>{
|
||||||
<th><Trans i18nKey="ep_admin_pads:ep_adminpads2_action"/></th>
|
<th><Trans i18nKey="ep_admin_pads:ep_adminpads2_action"/></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="search-pads-body">
|
<tbody>
|
||||||
{
|
{
|
||||||
pads?.results?.map((pad)=>{
|
pads?.results?.map((pad)=>{
|
||||||
return <tr key={pad.padName}>
|
return <tr key={pad.padName}>
|
||||||
|
@ -189,9 +150,6 @@ export const PadPage = ()=>{
|
||||||
setPadToDelete(pad.padName)
|
setPadToDelete(pad.padName)
|
||||||
setDeleteDialog(true)
|
setDeleteDialog(true)
|
||||||
}}/>
|
}}/>
|
||||||
<IconButton icon={<FileStack/>} title={<Trans i18nKey="ep_admin_pads:ep_adminpads2_cleanup"/>} onClick={()=>{
|
|
||||||
cleanupPad(pad.padName)
|
|
||||||
}}/>
|
|
||||||
<IconButton icon={<Eye/>} title="view" onClick={()=>window.open(`/p/${pad.padName}`, '_blank')}/>
|
<IconButton icon={<Eye/>} title="view" onClick={()=>window.open(`/p/${pad.padName}`, '_blank')}/>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
@ -208,12 +166,11 @@ export const PadPage = ()=>{
|
||||||
offset: (Number(currentPage)-1)*searchParams.limit})
|
offset: (Number(currentPage)-1)*searchParams.limit})
|
||||||
}}><ChevronLeft/><span>Previous Page</span></button>
|
}}><ChevronLeft/><span>Previous Page</span></button>
|
||||||
<span>{currentPage+1} out of {pages}</span>
|
<span>{currentPage+1} out of {pages}</span>
|
||||||
<button disabled={pages == 0 || pages == currentPage+1} onClick={()=>{
|
<button disabled={pages == currentPage+1} onClick={()=>{
|
||||||
const newCurrentPage = currentPage+1
|
setCurrentPage(currentPage+1)
|
||||||
setCurrentPage(newCurrentPage)
|
|
||||||
setSearchParams({
|
setSearchParams({
|
||||||
...searchParams,
|
...searchParams,
|
||||||
offset: (Number(newCurrentPage))*searchParams.limit
|
offset: (Number(currentPage)-1)*searchParams.limit
|
||||||
})
|
})
|
||||||
}}><span>Next Page</span><ChevronRight/></button>
|
}}><span>Next Page</span><ChevronRight/></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,7 +20,7 @@ export type SearchParams = {
|
||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
offset: number,
|
offset: number,
|
||||||
limit: number,
|
limit: number,
|
||||||
sortBy: 'name'|'version'|'last-updated',
|
sortBy: 'name'|'version',
|
||||||
sortDir: 'asc'|'desc'
|
sortDir: 'asc'|'desc'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import {useStore} from "../store/store.ts";
|
import {useStore} from "../store/store.ts";
|
||||||
import {isJSONClean, cleanComments} from "../utils/utils.ts";
|
import {isJSONClean} from "../utils/utils.ts";
|
||||||
import {Trans} from "react-i18next";
|
import {Trans} from "react-i18next";
|
||||||
import {IconButton} from "../components/IconButton.tsx";
|
import {IconButton} from "../components/IconButton.tsx";
|
||||||
import {RotateCw, Save} from "lucide-react";
|
import {RotateCw, Save} from "lucide-react";
|
||||||
|
|
||||||
export const SettingsPage = ()=>{
|
export const SettingsPage = ()=>{
|
||||||
const settingsSocket = useStore(state=>state.settingsSocket)
|
const settingsSocket = useStore(state=>state.settingsSocket)
|
||||||
const settings = cleanComments(useStore(state=>state.settings))
|
const settings = useStore(state=>state.settings)
|
||||||
|
|
||||||
return <div className="settings-page">
|
return <div>
|
||||||
<h1><Trans i18nKey="admin_settings.current"/></h1>
|
<h1><Trans i18nKey="admin_settings.current"/></h1>
|
||||||
<textarea value={settings} className="settings" onChange={v => {
|
<textarea value={settings} className="settings" onChange={v => {
|
||||||
useStore.getState().setSettings(v.target.value)
|
useStore.getState().setSettings(v.target.value)
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
import {useEffect, useState} from "react";
|
|
||||||
import {SendHorizonal} from 'lucide-react'
|
|
||||||
import {useStore} from "../store/store.ts";
|
|
||||||
import * as Switch from '@radix-ui/react-switch';
|
|
||||||
import {ShoutType} from "../components/ShoutType.ts";
|
|
||||||
|
|
||||||
export const ShoutPage = ()=>{
|
|
||||||
const [totalUsers, setTotalUsers] = useState(0);
|
|
||||||
const [message, setMessage] = useState<string>("");
|
|
||||||
const [sticky, setSticky] = useState<boolean>(false);
|
|
||||||
const socket = useStore(state => state.settingsSocket);
|
|
||||||
const [shouts, setShouts] = useState<ShoutType[]>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetch('/stats')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => setTotalUsers(data.totalUsers));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if(socket) {
|
|
||||||
socket.on('shout', (shout) => {
|
|
||||||
setShouts([...shouts, shout])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [socket, shouts])
|
|
||||||
|
|
||||||
const sendMessage = () => {
|
|
||||||
socket?.emit('shout', {
|
|
||||||
message,
|
|
||||||
sticky
|
|
||||||
});
|
|
||||||
setMessage('')
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1>Communication</h1>
|
|
||||||
{totalUsers > 0 && <p>There {totalUsers>1?"are":"is"} currently {totalUsers} user{totalUsers>1?"s":""} online</p>}
|
|
||||||
<div style={{height: '80vh', display: 'flex', flexDirection: 'column'}}>
|
|
||||||
<div style={{flexGrow: 1, backgroundColor: 'white', overflowY: "auto"}}>
|
|
||||||
{
|
|
||||||
shouts.map((shout) => {
|
|
||||||
return (
|
|
||||||
<div key={shout.data.payload.timestamp} className="message">
|
|
||||||
<div>{shout.data.payload.message.message}</div>
|
|
||||||
<div style={{display: 'flex'}}>
|
|
||||||
<div style={{flexGrow: 1}}></div>
|
|
||||||
<div
|
|
||||||
style={{color: "lightgray"}}>{new Date(shout.data.payload.timestamp).toLocaleTimeString()
|
|
||||||
+ " " + new Date(shout.data.payload.timestamp).toLocaleDateString()}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<form onSubmit={(e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
sendMessage()
|
|
||||||
}} className="send-message search-field" style={{display: 'flex', gap: '10px'}}>
|
|
||||||
<Switch.Root title="Change sticky message" className="SwitchRoot" checked={sticky}
|
|
||||||
onCheckedChange={() => {
|
|
||||||
setSticky(!sticky);
|
|
||||||
}}>
|
|
||||||
<Switch.Thumb className="SwitchThumb"/>
|
|
||||||
</Switch.Root>
|
|
||||||
<input required value={message} onChange={v=>setMessage(v.target.value)}
|
|
||||||
style={{width: '100%', paddingRight: '55px', backgroundColor: '#e0e0e0', flexGrow: 1}}/>
|
|
||||||
<SendHorizonal style={{bottom: '5px', right: '9px', color: '#0f775b'}} onClick={()=>sendMessage()}/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
import {create} from "zustand";
|
import {create} from "zustand";
|
||||||
import {Socket} from "socket.io-client";
|
import {Socket} from "socket.io-client";
|
||||||
import {PadSearchResult} from "../utils/PadSearch.ts";
|
import {PadSearchResult} from "../utils/PadSearch.ts";
|
||||||
import {InstalledPlugin} from "../pages/Plugin.ts";
|
|
||||||
|
|
||||||
type ToastState = {
|
type ToastState = {
|
||||||
description?:string,
|
description?:string,
|
||||||
|
@ -23,9 +22,7 @@ type StoreState = {
|
||||||
toastState: ToastState,
|
toastState: ToastState,
|
||||||
setToastState: (val: ToastState)=>void,
|
setToastState: (val: ToastState)=>void,
|
||||||
pads: PadSearchResult|undefined,
|
pads: PadSearchResult|undefined,
|
||||||
setPads: (pads: PadSearchResult)=>void,
|
setPads: (pads: PadSearchResult)=>void
|
||||||
installedPlugins: InstalledPlugin[],
|
|
||||||
setInstalledPlugins: (plugins: InstalledPlugin[])=>void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,7 +43,5 @@ export const useStore = create<StoreState>()((set) => ({
|
||||||
success: false
|
success: false
|
||||||
},
|
},
|
||||||
pads: undefined,
|
pads: undefined,
|
||||||
setPads: (pads)=>set({pads}),
|
setPads: (pads)=>set({pads})
|
||||||
installedPlugins: [],
|
|
||||||
setInstalledPlugins: (plugins)=>set({installedPlugins: plugins})
|
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -1,14 +1,5 @@
|
||||||
export const cleanComments = (json: string|undefined)=>{
|
const minify = (json: string)=>{
|
||||||
if (json !== undefined){
|
|
||||||
json = json.replace(/\/\*.*?\*\//g, ""); // remove single line comments
|
|
||||||
json = json.replace(/ *\/\*.*(.|\n)*?\*\//g, ""); // remove multi line comments
|
|
||||||
json = json.replace(/[ \t]+$/gm, ""); // trim trailing spaces
|
|
||||||
json = json.replace(/^(\n)/gm, ""); // remove empty lines
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const minify = (json: string)=>{
|
|
||||||
let tokenizer = /"|(\/\*)|(\*\/)|(\/\/)|\n|\r/g,
|
let tokenizer = /"|(\/\*)|(\*\/)|(\/\/)|\n|\r/g,
|
||||||
in_string = false,
|
in_string = false,
|
||||||
in_multiline_comment = false,
|
in_multiline_comment = false,
|
||||||
|
@ -58,6 +49,9 @@ export const minify = (json: string)=>{
|
||||||
return new_str.join("");
|
return new_str.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const isJSONClean = (data: string) => {
|
export const isJSONClean = (data: string) => {
|
||||||
let cleanSettings = minify(data);
|
let cleanSettings = minify(data);
|
||||||
// this is a bit naive. In theory some key/value might contain the sequences ',]' or ',}'
|
// this is a bit naive. In theory some key/value might contain the sequences ',]' or ',}'
|
||||||
|
|
|
@ -28,11 +28,8 @@ export default defineConfig({
|
||||||
'/admin-auth/': {
|
'/admin-auth/': {
|
||||||
target: 'http://localhost:9001',
|
target: 'http://localhost:9001',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
rewrite: (path) => path.replace(/^\/admin-prox/, '/admin/')
|
||||||
'/stats': {
|
|
||||||
target: 'http://localhost:9001',
|
|
||||||
changeOrigin: true,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -50,13 +50,21 @@ rm -rf src/node_modules || true
|
||||||
#$(try cd ./bin/installDeps.sh)
|
#$(try cd ./bin/installDeps.sh)
|
||||||
|
|
||||||
# Install admin frontend
|
# Install admin frontend
|
||||||
|
cd admin
|
||||||
try pnpm install
|
try pnpm install
|
||||||
try pnpm run build:etherpad
|
try pnpm run build
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Nuke the admin folder as it is not needed anymore :D
|
# Nuke the admin folder as it is not needed anymore :D
|
||||||
rm -rf admin
|
rm -rf admin
|
||||||
rm -rf oidc
|
|
||||||
rm -rf src/node_modules
|
export NODE_ENV=production
|
||||||
|
try pnpm install --production
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
log "copy the windows settings template..."
|
log "copy the windows settings template..."
|
||||||
try cp settings.json.template settings.json
|
try cp settings.json.template settings.json
|
||||||
|
|
|
@ -25,5 +25,4 @@ if (process.argv.length !== 2) throw new Error('Use: node bin/checkAllPads.js');
|
||||||
console.log(`Pad ${padId}: OK`);
|
console.log(`Pad ${padId}: OK`);
|
||||||
}));
|
}));
|
||||||
console.log('Finished.');
|
console.log('Finished.');
|
||||||
process.exit(0)
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -11,19 +11,12 @@ process.on('unhandledRejection', (err) => { throw err; });
|
||||||
if (process.argv.length !== 3) throw new Error('Use: node bin/checkPad.js $PADID');
|
if (process.argv.length !== 3) throw new Error('Use: node bin/checkPad.js $PADID');
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const padId = process.argv[2];
|
const padId = process.argv[2];
|
||||||
|
(async () => {
|
||||||
const performCheck = async () => {
|
|
||||||
const db = require('ep_etherpad-lite/node/db/DB');
|
const db = require('ep_etherpad-lite/node/db/DB');
|
||||||
await db.init();
|
await db.init();
|
||||||
console.log("Checking if " + padId + " exists")
|
|
||||||
const padManager = require('ep_etherpad-lite/node/db/PadManager');
|
const padManager = require('ep_etherpad-lite/node/db/PadManager');
|
||||||
if (!await padManager.doesPadExists(padId)) throw new Error('Pad does not exist');
|
if (!await padManager.doesPadExists(padId)) throw new Error('Pad does not exist');
|
||||||
const pad = await padManager.getPad(padId);
|
const pad = await padManager.getPad(padId);
|
||||||
await pad.check();
|
await pad.check();
|
||||||
console.log('Finished checking pad.');
|
console.log('Finished.');
|
||||||
process.exit(0)
|
})();
|
||||||
}
|
|
||||||
|
|
||||||
performCheck()
|
|
||||||
.then(e=>console.log("Finished"))
|
|
||||||
.catch(e=>console.log("Finished with errors"))
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
import {PackageData} from "ep_etherpad-lite/node/types/PackageInfo";
|
|
||||||
import {writeFileSync} from "fs";
|
|
||||||
import {installedPluginsPath} from "ep_etherpad-lite/static/js/pluginfw/installer";
|
|
||||||
const pluginsModule = require('ep_etherpad-lite/static/js/pluginfw/plugins');
|
|
||||||
|
|
||||||
export const persistInstalledPlugins = async () => {
|
|
||||||
const plugins:PackageData[] = []
|
|
||||||
const installedPlugins = {plugins: plugins};
|
|
||||||
for (const pkg of Object.values(await pluginsModule.getPackages()) as PackageData[]) {
|
|
||||||
installedPlugins.plugins.push({
|
|
||||||
name: pkg.name,
|
|
||||||
version: pkg.version,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
installedPlugins.plugins = [...new Set(installedPlugins.plugins)];
|
|
||||||
writeFileSync(installedPluginsPath, JSON.stringify(installedPlugins));
|
|
||||||
};
|
|
|
@ -14,7 +14,6 @@ import path from "node:path";
|
||||||
import querystring from "node:querystring";
|
import querystring from "node:querystring";
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import process from "node:process";
|
|
||||||
|
|
||||||
|
|
||||||
process.on('unhandledRejection', (err) => { throw err; });
|
process.on('unhandledRejection', (err) => { throw err; });
|
||||||
|
@ -54,5 +53,4 @@ const settings = require('ep_etherpad-lite/node/utils/Settings');
|
||||||
if (res.data.code === 1) throw new Error(`Error creating session: ${JSON.stringify(res.data)}`);
|
if (res.data.code === 1) throw new Error(`Error creating session: ${JSON.stringify(res.data)}`);
|
||||||
console.log('Session made: ====> create a cookie named sessionID and set the value to',
|
console.log('Session made: ====> create a cookie named sessionID and set the value to',
|
||||||
res.data.data.sessionID);
|
res.data.data.sessionID);
|
||||||
process.exit(0)
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -49,5 +49,4 @@ const settings = require('ep_etherpad-lite/tests/container/loadSettings').loadSe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(`Deleted ${deleteCount} sessions`);
|
console.log(`Deleted ${deleteCount} sessions`);
|
||||||
process.exit(0)
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -38,5 +38,4 @@ const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'});
|
||||||
const deleteAttempt = await axios.post(uri);
|
const deleteAttempt = await axios.post(uri);
|
||||||
if (deleteAttempt.data.code === 1) throw new Error(`Error deleting pad ${deleteAttempt.data}`);
|
if (deleteAttempt.data.code === 1) throw new Error(`Error deleting pad ${deleteAttempt.data}`);
|
||||||
console.log('Deleted pad', deleteAttempt.data);
|
console.log('Deleted pad', deleteAttempt.data);
|
||||||
process.exit(0)
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -60,5 +60,4 @@ const padId = process.argv[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('finished');
|
console.log('finished');
|
||||||
process.exit(0)
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
import {readFileSync} from "node:fs";
|
|
||||||
|
|
||||||
const changelog = readFileSync('../changelog.md')
|
|
||||||
const changelogText = changelog.toString()
|
|
||||||
const changelogLines = changelogText.split('\n')
|
|
||||||
|
|
||||||
|
|
||||||
let cliArgs = process.argv.slice(2)
|
|
||||||
|
|
||||||
let tagVar = cliArgs[0]
|
|
||||||
|
|
||||||
if (!tagVar) {
|
|
||||||
console.error("No tag provided")
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
tagVar = tagVar.replace("refs/tags/v", "")
|
|
||||||
|
|
||||||
let startNum = -1
|
|
||||||
let endline = 0
|
|
||||||
|
|
||||||
let counter = 0
|
|
||||||
for (const line of changelogLines) {
|
|
||||||
if (line.trim().startsWith("#") && (line.match(new RegExp("#", "g"))||[]).length === 1) {
|
|
||||||
if (startNum !== -1) {
|
|
||||||
endline = counter-1
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
const sanitizedLine = line.replace("#","").trim()
|
|
||||||
if(sanitizedLine.includes(tagVar)) {
|
|
||||||
startNum = counter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
counter++
|
|
||||||
}
|
|
||||||
|
|
||||||
let currentReleaseNotes = changelogLines.slice(startNum, endline).join('\n')
|
|
||||||
console.log(currentReleaseNotes)
|
|
|
@ -6,8 +6,7 @@ import util from "node:util";
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
import log4js from 'log4js';
|
import log4js from 'log4js';
|
||||||
import readline from 'readline';
|
import readline from 'readline';
|
||||||
import {Database} from "ueberdb2";
|
import ueberDB from "ueberdb2";
|
||||||
import process from "node:process";
|
|
||||||
|
|
||||||
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
||||||
process.on('unhandledRejection', (err) => { throw err; });
|
process.on('unhandledRejection', (err) => { throw err; });
|
||||||
|
@ -57,7 +56,7 @@ const unescape = (val: string) => {
|
||||||
writeInterval: 100,
|
writeInterval: 100,
|
||||||
json: false, // data is already json encoded
|
json: false, // data is already json encoded
|
||||||
};
|
};
|
||||||
const db = new Database( // eslint-disable-line new-cap
|
const db = new ueberDB.Database( // eslint-disable-line new-cap
|
||||||
settings.dbType,
|
settings.dbType,
|
||||||
settings.dbSettings,
|
settings.dbSettings,
|
||||||
dbWrapperSettings,
|
dbWrapperSettings,
|
||||||
|
@ -97,8 +96,6 @@ const unescape = (val: string) => {
|
||||||
'depended on dbms this may take some time..\n');
|
'depended on dbms this may take some time..\n');
|
||||||
|
|
||||||
const closeDB = util.promisify(db.close.bind(db));
|
const closeDB = util.promisify(db.close.bind(db));
|
||||||
// @ts-ignore
|
|
||||||
await closeDB(null);
|
await closeDB(null);
|
||||||
log(`finished, imported ${keyNo} keys.`);
|
log(`finished, imported ${keyNo} keys.`);
|
||||||
process.exit(0)
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
set -euo pipefail
|
|
||||||
IFS=$'\n\t'
|
|
||||||
|
|
||||||
trim() {
|
|
||||||
local var="$*"
|
|
||||||
# remove leading whitespace characters
|
|
||||||
var="${var#"${var%%[![:space:]]*}"}"
|
|
||||||
# remove trailing whitespace characters
|
|
||||||
var="${var%"${var##*[![:space:]]}"}"
|
|
||||||
printf '%s' "$var"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Move to the Etherpad base directory.
|
|
||||||
MY_DIR=$(cd "${0%/*}" && pwd -P) || exit 1
|
|
||||||
cd "${MY_DIR}/.." || exit 1
|
|
||||||
|
|
||||||
# Source constants and useful functions
|
|
||||||
. bin/functions.sh
|
|
||||||
|
|
||||||
PNPM_OPTIONS=
|
|
||||||
if [ ! -z "${NODE_ENV-}" ]; then
|
|
||||||
if [ "$NODE_ENV" == 'production' ]; then
|
|
||||||
PNPM_OPTIONS='--prod'
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -z "${ETHERPAD_LOCAL_PLUGINS_ENV-}" ]; then
|
|
||||||
if [ "$ETHERPAD_LOCAL_PLUGINS_ENV" == 'production' ]; then
|
|
||||||
PNPM_OPTIONS='--prod'
|
|
||||||
elif [ "$ETHERPAD_LOCAL_PLUGINS_ENV" == 'development' ]; then
|
|
||||||
PNPM_OPTIONS='-D'
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -z "${ETHERPAD_LOCAL_PLUGINS}" ]; then
|
|
||||||
readarray -d ' ' plugins <<< "${ETHERPAD_LOCAL_PLUGINS}"
|
|
||||||
for plugin in "${plugins[@]}"; do
|
|
||||||
plugin=$(trim "$plugin")
|
|
||||||
if [ -d "local_plugins/${plugin}" ]; then
|
|
||||||
echo "Installing plugin: '${plugin}'"
|
|
||||||
pnpm install -w ${PNPM_OPTIONS:-} "local_plugins/${plugin}/"
|
|
||||||
else
|
|
||||||
( echo "Error. Directory 'local_plugins/${plugin}' for local plugin " \
|
|
||||||
"'${plugin}' missing" >&2 )
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
echo 'No local plugings to install.'
|
|
||||||
fi
|
|
59
bin/installPlugins.ts
Normal file
59
bin/installPlugins.ts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import {writeFileSync} from 'fs'
|
||||||
|
import {linkInstaller, installedPluginsPath} from "ep_etherpad-lite/static/js/pluginfw/installer";
|
||||||
|
import {PackageData} from "ep_etherpad-lite/node/types/PackageInfo";
|
||||||
|
|
||||||
|
const pluginsModule = require('ep_etherpad-lite/static/js/pluginfw/plugins');
|
||||||
|
if (process.argv.length === 2) {
|
||||||
|
console.error('Expected at least one argument!');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = process.argv.slice(2)
|
||||||
|
|
||||||
|
let registryPlugins: string[] = [];
|
||||||
|
let localPlugins: string[] = [];
|
||||||
|
|
||||||
|
if (args.indexOf('--path') !== -1) {
|
||||||
|
const indexToSplit = args.indexOf('--path');
|
||||||
|
registryPlugins = args.slice(0, indexToSplit);
|
||||||
|
localPlugins = args.slice(indexToSplit + 1);
|
||||||
|
} else {
|
||||||
|
registryPlugins = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
const persistInstalledPlugins = async () => {
|
||||||
|
const plugins:PackageData[] = []
|
||||||
|
const installedPlugins = {plugins: plugins};
|
||||||
|
for (const pkg of Object.values(await pluginsModule.getPackages()) as PackageData[]) {
|
||||||
|
installedPlugins.plugins.push({
|
||||||
|
name: pkg.name,
|
||||||
|
version: pkg.version,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
installedPlugins.plugins = [...new Set(installedPlugins.plugins)];
|
||||||
|
writeFileSync(installedPluginsPath, JSON.stringify(installedPlugins));
|
||||||
|
};
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
for (const plugin of registryPlugins) {
|
||||||
|
console.log(`Installing plugin from registry: ${plugin}`)
|
||||||
|
if (plugin.includes('@')) {
|
||||||
|
const [name, version] = plugin.split('@');
|
||||||
|
await linkInstaller.installPlugin(name, version);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
await linkInstaller.installPlugin(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const plugin of localPlugins) {
|
||||||
|
console.log(`Installing plugin from path: ${plugin}`);
|
||||||
|
await linkInstaller.installFromPath(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await run();
|
||||||
|
await persistInstalledPlugins();
|
||||||
|
})();
|
|
@ -1,63 +0,0 @@
|
||||||
import {exec} from 'child_process'
|
|
||||||
import fs from 'fs'
|
|
||||||
import path from 'path'
|
|
||||||
|
|
||||||
import pjson from '../src/package.json'
|
|
||||||
|
|
||||||
const VERSION=pjson.version
|
|
||||||
console.log(`Building docs for version ${VERSION}`)
|
|
||||||
|
|
||||||
const createDirIfNotExists = (dir: fs.PathLike) => {
|
|
||||||
if (!fs.existsSync(dir)){
|
|
||||||
fs.mkdirSync(dir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function copyFolderSync(from: fs.PathLike, to: fs.PathLike) {
|
|
||||||
if(fs.existsSync(to)){
|
|
||||||
const stat = fs.lstatSync(to)
|
|
||||||
if (stat.isDirectory()){
|
|
||||||
fs.rmSync(to, { recursive: true })
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
fs.rmSync(to)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fs.mkdirSync(to);
|
|
||||||
fs.readdirSync(from).forEach(element => {
|
|
||||||
if (fs.lstatSync(path.join(<string>from, element)).isFile()) {
|
|
||||||
if (typeof from === "string") {
|
|
||||||
if (typeof to === "string") {
|
|
||||||
fs.copyFileSync(path.join(from, element), path.join(to, element))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (typeof from === "string") {
|
|
||||||
if (typeof to === "string") {
|
|
||||||
copyFolderSync(path.join(from, element), path.join(to, element))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
exec('asciidoctor -v', (err,stdout)=>{
|
|
||||||
if (err){
|
|
||||||
console.log('Please install asciidoctor')
|
|
||||||
console.log('https://asciidoctor.org/docs/install-toolchain/')
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
createDirIfNotExists('../out')
|
|
||||||
createDirIfNotExists('../out/doc')
|
|
||||||
createDirIfNotExists('../out/doc/api')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
exec(`asciidoctor -D ../out/doc ../doc/index.adoc ../*/**.adoc -a VERSION=${VERSION}`)
|
|
||||||
exec(`asciidoctor -D ../out/doc/api ../doc/api/*.adoc -a VERSION=${VERSION}`)
|
|
||||||
|
|
||||||
copyFolderSync('../doc/public/', '../out/doc/')
|
|
|
@ -1,83 +0,0 @@
|
||||||
// DB migration
|
|
||||||
import {readFileSync} from 'node:fs'
|
|
||||||
import {Database, DatabaseType} from "ueberdb2";
|
|
||||||
import path from "node:path";
|
|
||||||
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
|
||||||
|
|
||||||
|
|
||||||
// file1 = source, file2 = target
|
|
||||||
// pnpm run migrateDB --file1 <db1.json> --file2 <db2.json>
|
|
||||||
const arg = process.argv.slice(2);
|
|
||||||
|
|
||||||
if (arg.length != 4) {
|
|
||||||
console.error('Wrong number of arguments!. Call with pnpm run migrateDB --file1 source.json target.json')
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
type SettingsConfig = {
|
|
||||||
dbType: string,
|
|
||||||
dbSettings: any
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
"dbType": "<your-db-type>",
|
|
||||||
"dbSettings": {
|
|
||||||
<your-db-settings>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
let firstDBSettingsFile: string
|
|
||||||
let secondDBSettingsFile: string
|
|
||||||
|
|
||||||
|
|
||||||
if (arg[0] == "--file1") {
|
|
||||||
firstDBSettingsFile = arg[1]
|
|
||||||
} else if (arg[0] === "--file2") {
|
|
||||||
secondDBSettingsFile = arg[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg[2] == "--file1") {
|
|
||||||
firstDBSettingsFile = arg[3]
|
|
||||||
} else if (arg[2] === "--file2") {
|
|
||||||
secondDBSettingsFile = arg[3]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const settingsfile = JSON.parse(readFileSync(path.join(settings.root,firstDBSettingsFile!)).toString()) as SettingsConfig
|
|
||||||
const settingsfile2 = JSON.parse(readFileSync(path.join(settings.root,secondDBSettingsFile!)).toString()) as SettingsConfig
|
|
||||||
|
|
||||||
console.log(settingsfile2)
|
|
||||||
if ("filename" in settingsfile.dbSettings) {
|
|
||||||
settingsfile.dbSettings.filename = path.join(settings.root, settingsfile.dbSettings.filename)
|
|
||||||
console.log(settingsfile.dbType + " location is "+ settingsfile.dbSettings.filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("filename" in settingsfile2.dbSettings) {
|
|
||||||
settingsfile2.dbSettings.filename = path.join(settings.root, settingsfile2.dbSettings.filename)
|
|
||||||
console.log(settingsfile2.dbType + " location is "+ settingsfile2.dbSettings.filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
const ueberdb1 = new Database(settingsfile.dbType as DatabaseType, settingsfile.dbSettings)
|
|
||||||
const ueberdb2 = new Database(settingsfile2.dbType as DatabaseType, settingsfile2.dbSettings)
|
|
||||||
|
|
||||||
const handleSync = async ()=>{
|
|
||||||
await ueberdb1.init()
|
|
||||||
await ueberdb2.init()
|
|
||||||
|
|
||||||
const allKeys = await ueberdb1.findKeys('*','')
|
|
||||||
for (const key of allKeys) {
|
|
||||||
const foundVal = await ueberdb1.get(key)!
|
|
||||||
await ueberdb2.set(key, foundVal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSync().then(()=>{
|
|
||||||
console.log("Done syncing dbs")
|
|
||||||
}).catch(e=>{
|
|
||||||
console.log(`Error syncing db ${e}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
import {Database} from "ueberdb2";
|
import ueberDB from "ueberdb2";
|
||||||
import log4js from 'log4js';
|
import log4js from 'log4js';
|
||||||
import util from 'util';
|
import util from 'util';
|
||||||
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
||||||
|
@ -23,7 +23,7 @@ process.on('unhandledRejection', (err) => { throw err; });
|
||||||
cache: '0', // The cache slows things down when you're mostly writing.
|
cache: '0', // The cache slows things down when you're mostly writing.
|
||||||
writeInterval: 0, // Write directly to the database, don't buffer
|
writeInterval: 0, // Write directly to the database, don't buffer
|
||||||
};
|
};
|
||||||
const db = new Database( // eslint-disable-line new-cap
|
const db = new ueberDB.Database( // eslint-disable-line new-cap
|
||||||
settings.dbType,
|
settings.dbType,
|
||||||
settings.dbSettings,
|
settings.dbSettings,
|
||||||
dbWrapperSettings,
|
dbWrapperSettings,
|
||||||
|
@ -31,7 +31,7 @@ process.on('unhandledRejection', (err) => { throw err; });
|
||||||
await db.init();
|
await db.init();
|
||||||
|
|
||||||
console.log('Waiting for dirtyDB to parse its file.');
|
console.log('Waiting for dirtyDB to parse its file.');
|
||||||
const dirty = new Database('dirty', `${__dirname}/../var/dirty.db`);
|
const dirty = await new ueberDB.Database('dirty',`${__dirname}/../var/dirty.db`);
|
||||||
await dirty.init();
|
await dirty.init();
|
||||||
const keys = await dirty.findKeys('*', '')
|
const keys = await dirty.findKeys('*', '')
|
||||||
|
|
||||||
|
@ -57,5 +57,4 @@ process.on('unhandledRejection', (err) => { throw err; });
|
||||||
await db.close(null);
|
await db.close(null);
|
||||||
await dirty.close(null);
|
await dirty.close(null);
|
||||||
console.log('Finished.');
|
console.log('Finished.');
|
||||||
process.exit(0)
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,26 +1,25 @@
|
||||||
{
|
{
|
||||||
"name": "bin",
|
"name": "bin",
|
||||||
"version": "2.3.0",
|
"version": "2.0.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "checkAllPads.js",
|
"main": "checkAllPads.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
"doc": "doc"
|
"doc": "doc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.8.4",
|
"axios": "^1.6.8",
|
||||||
"ep_etherpad-lite": "workspace:../src",
|
"ep_etherpad-lite": "workspace:../src",
|
||||||
"log4js": "^6.9.1",
|
"log4js": "^6.9.1",
|
||||||
"semver": "^7.7.2",
|
"semver": "^7.6.0",
|
||||||
"tsx": "^4.20.3",
|
"tsx": "^4.7.2",
|
||||||
"ueberdb2": "^5.0.14"
|
"ueberdb2": "^4.2.63"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.0.1",
|
"@types/node": "^20.12.5",
|
||||||
"@types/semver": "^7.7.0",
|
"@types/semver": "^7.5.8",
|
||||||
"typescript": "^5.8.2"
|
"typescript": "^5.4.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"makeDocs": "node --import tsx make_docs.ts",
|
|
||||||
"checkPad": "node --import tsx checkPad.ts",
|
"checkPad": "node --import tsx checkPad.ts",
|
||||||
"checkAllPads": "node --import tsx checkAllPads.ts",
|
"checkAllPads": "node --import tsx checkAllPads.ts",
|
||||||
"createUserSession": "node --import tsx createUserSession.ts",
|
"createUserSession": "node --import tsx createUserSession.ts",
|
||||||
|
@ -33,9 +32,7 @@
|
||||||
"rebuildPad": "node --import tsx rebuildPad.ts",
|
"rebuildPad": "node --import tsx rebuildPad.ts",
|
||||||
"stalePlugins": "node --import tsx ./plugins/stalePlugins.ts",
|
"stalePlugins": "node --import tsx ./plugins/stalePlugins.ts",
|
||||||
"checkPlugin": "node --import tsx ./plugins/checkPlugin.ts",
|
"checkPlugin": "node --import tsx ./plugins/checkPlugin.ts",
|
||||||
"plugins": "node --import tsx ./plugins.ts",
|
"install-plugins": "node --import tsx ./installPlugins.ts"
|
||||||
"generateChangelog": "node --import tsx generateReleaseNotes.ts",
|
|
||||||
"migrateDB": "node --import tsx migrateDB.ts"
|
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
|
|
117
bin/plugins.ts
117
bin/plugins.ts
|
@ -1,117 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import {linkInstaller, checkForMigration} from "ep_etherpad-lite/static/js/pluginfw/installer";
|
|
||||||
import {persistInstalledPlugins} from "./commonPlugins";
|
|
||||||
import fs from "node:fs";
|
|
||||||
const settings = require('ep_etherpad-lite/node/utils/Settings');
|
|
||||||
|
|
||||||
if (process.argv.length === 2) {
|
|
||||||
console.error('Expected at least one argument!');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let args = process.argv.slice(2)
|
|
||||||
|
|
||||||
|
|
||||||
const possibleActions = [
|
|
||||||
"i",
|
|
||||||
"install",
|
|
||||||
"rm",
|
|
||||||
"remove",
|
|
||||||
"ls",
|
|
||||||
"list"
|
|
||||||
]
|
|
||||||
|
|
||||||
const install = ()=> {
|
|
||||||
const argsAsString: string = args.join(" ");
|
|
||||||
const regexRegistryPlugins = /(?<=i\s)(.*?)(?=--github|--path|$)/;
|
|
||||||
const regexLocalPlugins = /(?<=--path\s)(.*?)(?=--github|$)/;
|
|
||||||
const regexGithubPlugins = /(?<=--github\s)(.*?)(?=--path|$)/;
|
|
||||||
const registryPlugins = argsAsString.match(regexRegistryPlugins)?.[0]?.split(" ")?.filter(s => s) || [];
|
|
||||||
const localPlugins = argsAsString.match(regexLocalPlugins)?.[0]?.split(" ")?.filter(s => s) || [];
|
|
||||||
const githubPlugins = argsAsString.match(regexGithubPlugins)?.[0]?.split(" ")?.filter(s => s) || [];
|
|
||||||
|
|
||||||
async function run() {
|
|
||||||
for (const plugin of registryPlugins) {
|
|
||||||
if (possibleActions.includes(plugin)){
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
console.log(`Installing plugin from registry: ${plugin}`)
|
|
||||||
if (plugin.includes('@')) {
|
|
||||||
const [name, version] = plugin.split('@');
|
|
||||||
await linkInstaller.installPlugin(name, version);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
await linkInstaller.installPlugin(plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const plugin of localPlugins) {
|
|
||||||
console.log(`Installing plugin from path: ${plugin}`);
|
|
||||||
await linkInstaller.installFromPath(plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const plugin of githubPlugins) {
|
|
||||||
console.log(`Installing plugin from github: ${plugin}`);
|
|
||||||
await linkInstaller.installFromGitHub(plugin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
await checkForMigration();
|
|
||||||
await run();
|
|
||||||
await persistInstalledPlugins();
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
|
|
||||||
const list = ()=>{
|
|
||||||
const walk = async () => {
|
|
||||||
const plugins = fs.readFileSync(settings.root+"/var/installed_plugins.json", "utf-8")
|
|
||||||
const pluginNames = JSON.parse(plugins).plugins.map((plugin: any) => plugin.name).join(", ")
|
|
||||||
|
|
||||||
console.log("Installed plugins are:", pluginNames)
|
|
||||||
}
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
await walk();
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
|
|
||||||
const remove = (plugins: string[])=>{
|
|
||||||
const walk = async () => {
|
|
||||||
for (const plugin of plugins) {
|
|
||||||
console.log(`Uninstalling plugin: ${plugin}`)
|
|
||||||
await linkInstaller.uninstallPlugin(plugin);
|
|
||||||
}
|
|
||||||
await persistInstalledPlugins();
|
|
||||||
}
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
await checkForMigration();
|
|
||||||
await walk();
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
|
|
||||||
let action = args[0];
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case "install":
|
|
||||||
install();
|
|
||||||
break;
|
|
||||||
case "i":
|
|
||||||
install();
|
|
||||||
break;
|
|
||||||
case "ls":
|
|
||||||
list();
|
|
||||||
break;
|
|
||||||
case "list":
|
|
||||||
list();
|
|
||||||
break;
|
|
||||||
case "rm":
|
|
||||||
remove(args.slice(1));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.error('Expected at least one argument!');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -474,6 +474,6 @@ log4js.configure({
|
||||||
logger.info('No changes.');
|
logger.info('No changes.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('Finished');
|
logger.info('Finished');
|
||||||
process.exit(0)
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -69,7 +69,7 @@ jobs:
|
||||||
working-directory: ./etherpad-lite
|
working-directory: ./etherpad-lite
|
||||||
run: |
|
run: |
|
||||||
pnpm link --global $PLUGIN_NAME
|
pnpm link --global $PLUGIN_NAME
|
||||||
pnpm run plugins i --path ../../plugin
|
pnpm run install-plugins --path ../../plugin
|
||||||
env:
|
env:
|
||||||
PLUGIN_NAME: ${{ steps.plugin_name.outputs.plugin_name }}
|
PLUGIN_NAME: ${{ steps.plugin_name.outputs.plugin_name }}
|
||||||
- name: Link ep_etherpad-lite
|
- name: Link ep_etherpad-lite
|
||||||
|
|
|
@ -78,7 +78,7 @@ jobs:
|
||||||
- name: Run the frontend tests
|
- name: Run the frontend tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
pnpm run prod &
|
pnpm run dev &
|
||||||
connected=false
|
connected=false
|
||||||
can_connect() {
|
can_connect() {
|
||||||
curl -sSfo /dev/null http://localhost:9001/ || return 1
|
curl -sSfo /dev/null http://localhost:9001/ || return 1
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
// Returns a list of stale plugins and their authors email
|
// Returns a list of stale plugins and their authors email
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import process from "node:process";
|
|
||||||
const currentTime = new Date();
|
const currentTime = new Date();
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
|
@ -20,5 +19,4 @@ const currentTime = new Date();
|
||||||
console.log(`${name}, ${res.data[plugin].data.maintainers[0].email}`);
|
console.log(`${name}, ${res.data[plugin].data.maintainers[0].email}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
process.exit(0)
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
|
|
||||||
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an
|
||||||
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
// unhandled rejection into an uncaught exception, which does cause Node.js to exit.
|
||||||
import process from "node:process";
|
|
||||||
|
|
||||||
process.on('unhandledRejection', (err) => { throw err; });
|
process.on('unhandledRejection', (err) => { throw err; });
|
||||||
|
|
||||||
if (process.argv.length !== 4 && process.argv.length !== 5) {
|
if (process.argv.length !== 4 && process.argv.length !== 5) {
|
||||||
|
@ -84,5 +82,4 @@ const newPadId = process.argv[4] || `${padId}-rebuilt`;
|
||||||
|
|
||||||
await db.shutdown();
|
await db.shutdown();
|
||||||
console.info('finished');
|
console.info('finished');
|
||||||
process.exit(0)
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -197,7 +197,7 @@ try {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('Building documentation...');
|
console.log('Building documentation...');
|
||||||
run('pnpm run makeDocs');
|
run('node ./make_docs.js');
|
||||||
console.log('Updating ether.github.com master branch...');
|
console.log('Updating ether.github.com master branch...');
|
||||||
run('git pull --ff-only', {cwd: '../ether.github.com/'});
|
run('git pull --ff-only', {cwd: '../ether.github.com/'});
|
||||||
console.log('Committing documentation...');
|
console.log('Committing documentation...');
|
||||||
|
|
|
@ -57,5 +57,4 @@ let valueCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.info(`Finished: Replaced ${valueCount} values in the database`);
|
console.info(`Finished: Replaced ${valueCount} values in the database`);
|
||||||
process.exit(0)
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -45,4 +45,4 @@ fi
|
||||||
log "Starting Etherpad..."
|
log "Starting Etherpad..."
|
||||||
|
|
||||||
# cd src
|
# cd src
|
||||||
exec pnpm run prod "$@"
|
exec pnpm run dev "$@"
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||||
"resolveJsonModule": true, /* Enable importing .json files. */
|
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,6 @@ export default defineConfig({
|
||||||
{ text: 'Stats', link: '/stats.md' },
|
{ text: 'Stats', link: '/stats.md' },
|
||||||
{text: 'Skins', link: '/skins.md' },
|
{text: 'Skins', link: '/skins.md' },
|
||||||
{text: 'Demo', link: '/demo.md' },
|
{text: 'Demo', link: '/demo.md' },
|
||||||
{text: 'CLI', link: '/cli.md'},
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Changeset Library
|
# Changeset Library
|
||||||
|
|
||||||
The [changeset
|
The [changeset
|
||||||
library](https://github.com/ether/etherpad-lite/blob/develop/src/static/js/Changeset.ts)
|
library](https://github.com/ether/etherpad-lite/blob/develop/src/static/js/Changeset.js)
|
||||||
provides tools to create, read, and apply changesets.
|
provides tools to create, read, and apply changesets.
|
||||||
|
|
||||||
## Changeset
|
## Changeset
|
||||||
|
|
|
@ -28,7 +28,7 @@ Portal maps the internal userid to an etherpad author.
|
||||||
#### Request
|
#### Request
|
||||||
|
|
||||||
```http
|
```http
|
||||||
GET /api/1/createAuthorIfNotExistsFor?name=Michael&authorMapper=7
|
GET /api/1/createAuthorIfNotExistsFor?apikey=secret&name=Michael&authorMapper=7
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ GET /api/1/createAuthorIfNotExistsFor?name=Michael&authorMapper=7
|
||||||
> Portal maps the internal userid to an etherpad group:
|
> Portal maps the internal userid to an etherpad group:
|
||||||
|
|
||||||
```http
|
```http
|
||||||
GET http://pad.domain/api/1/createGroupIfNotExistsFor?groupMapper=7
|
GET http://pad.domain/api/1/createGroupIfNotExistsFor?apikey=secret&groupMapper=7
|
||||||
```
|
```
|
||||||
|
|
||||||
### Response
|
### Response
|
||||||
|
@ -56,7 +56,7 @@ GET http://pad.domain/api/1/createGroupIfNotExistsFor?groupMapper=7
|
||||||
#### Request
|
#### Request
|
||||||
|
|
||||||
```http
|
```http
|
||||||
GET http://pad.domain/api/1/createGroupPad?groupID=g.s8oes9dhwrvt0zif&padName=samplePad&text=This is the first sentence in the pad
|
GET http://pad.domain/api/1/createGroupPad?apikey=secret&groupID=g.s8oes9dhwrvt0zif&padName=samplePad&text=This is the first sentence in the pad
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Response
|
#### Response
|
||||||
|
@ -70,7 +70,7 @@ GET http://pad.domain/api/1/createGroupPad?groupID=g.s8oes9dhwrvt0zif&padName=sa
|
||||||
#### Request
|
#### Request
|
||||||
|
|
||||||
```http
|
```http
|
||||||
GET http://pad.domain/api/1/createSession?groupID=g.s8oes9dhwrvt0zif&authorID=a.s8oes9dhwrvt0zif&validUntil=1312201246
|
GET http://pad.domain/api/1/createSession?apikey=secret&groupID=g.s8oes9dhwrvt0zif&authorID=a.s8oes9dhwrvt0zif&validUntil=1312201246
|
||||||
```
|
```
|
||||||
|
|
||||||
### Response
|
### Response
|
||||||
|
@ -87,7 +87,7 @@ A portal (such as WordPress) wants to transform the contents of a pad that multi
|
||||||
|
|
||||||
Portal retrieves the contents of the pad for entry into the db as a blog post:
|
Portal retrieves the contents of the pad for entry into the db as a blog post:
|
||||||
|
|
||||||
> Request: `http://pad.domain/api/1/getText?&padID=g.s8oes9dhwrvt0zif$123`
|
> Request: `http://pad.domain/api/1/getText?apikey=secret&padID=g.s8oes9dhwrvt0zif$123`
|
||||||
>
|
>
|
||||||
> Response: `{code: 0, message:"ok", data: {text:"Welcome Text"}}`
|
> Response: `{code: 0, message:"ok", data: {text:"Welcome Text"}}`
|
||||||
|
|
||||||
|
@ -108,23 +108,23 @@ The API is accessible via HTTP. Starting from **1.8**, API endpoints can be invo
|
||||||
|
|
||||||
The URL of the HTTP request is of the form: `/api/$APIVERSION/$FUNCTIONNAME`. $APIVERSION depends on the endpoint you want to use. Depending on the verb you use (GET or POST) **parameters** can be passed differently.
|
The URL of the HTTP request is of the form: `/api/$APIVERSION/$FUNCTIONNAME`. $APIVERSION depends on the endpoint you want to use. Depending on the verb you use (GET or POST) **parameters** can be passed differently.
|
||||||
|
|
||||||
When invoking via GET (mandatory until **1.7.5** included), parameters must be included in the query string (example: `/api/$APIVERSION/$FUNCTIONNAME?param1=value1`). Please note that starting with nodejs 8.14+ the total size of HTTP request headers has been capped to 8192 bytes. This limits the quantity of data that can be sent in an API request.
|
When invoking via GET (mandatory until **1.7.5** included), parameters must be included in the query string (example: `/api/$APIVERSION/$FUNCTIONNAME?apikey=<APIKEY>¶m1=value1`). Please note that starting with nodejs 8.14+ the total size of HTTP request headers has been capped to 8192 bytes. This limits the quantity of data that can be sent in an API request.
|
||||||
|
|
||||||
Starting from Etherpad **1.8** it is also possible to invoke the HTTP API via POST. In this case, querystring parameters will still be accepted, but **any parameter with the same name sent via POST will take precedence**. If you need to send large chunks of text (for example, for `setText()`) it is advisable to invoke via POST.
|
Starting from Etherpad **1.8** it is also possible to invoke the HTTP API via POST. In this case, querystring parameters will still be accepted, but **any parameter with the same name sent via POST will take precedence**. If you need to send large chunks of text (for example, for `setText()`) it is advisable to invoke via POST.
|
||||||
|
|
||||||
Example with cURL using GET (toy example, no encoding):
|
Example with cURL using GET (toy example, no encoding):
|
||||||
```
|
```
|
||||||
curl "http://pad.domain/api/1/setText?padID=padname&text=this_text_will_NOT_be_encoded_by_curl_use_next_example"
|
curl "http://pad.domain/api/1/setText?apikey=secret&padID=padname&text=this_text_will_NOT_be_encoded_by_curl_use_next_example"
|
||||||
```
|
```
|
||||||
|
|
||||||
Example with cURL using GET (better example, encodes text):
|
Example with cURL using GET (better example, encodes text):
|
||||||
```
|
```
|
||||||
curl "http://pad.domain/api/1/setText?padID=padname" --get --data-urlencode "text=Text sent via GET with proper encoding. For big documents, please use POST"
|
curl "http://pad.domain/api/1/setText?apikey=secret&padID=padname" --get --data-urlencode "text=Text sent via GET with proper encoding. For big documents, please use POST"
|
||||||
```
|
```
|
||||||
|
|
||||||
Example with cURL using POST:
|
Example with cURL using POST:
|
||||||
```
|
```
|
||||||
curl "http://pad.domain/api/1/setText?padID=padname" --data-urlencode "text=Text sent via POST with proper encoding. For big texts (>8 KB), use this method"
|
curl "http://pad.domain/api/1/setText?apikey=secret&padID=padname" --data-urlencode "text=Text sent via POST with proper encoding. For big texts (>8 KB), use this method"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Response Format
|
### Response Format
|
||||||
|
@ -161,49 +161,7 @@ Responses are valid JSON in the following format:
|
||||||
|
|
||||||
### Authentication
|
### Authentication
|
||||||
|
|
||||||
Authentication works via an OAuth token that is sent with each request as an Authorization header, i.e. `Authorization: Bearer YOUR_TOKEN`. You can add new clients that can sign in via the API by adding new entries to the sso section in the settings.json.
|
Authentication works via a token that is sent with each request as a post parameter. There is a single token per Etherpad deployment. This token will be random string, generated by Etherpad at the first start. It will be saved in APIKEY.txt in the root folder of Etherpad. Only Etherpad and the requesting application knows this key. Token management will not be exposed through this API.
|
||||||
|
|
||||||
|
|
||||||
#### Example for browser login clients
|
|
||||||
|
|
||||||
This example illustrates how to add a new client that can sign in via the API using the browser login method. This method is used for users trying to sign in to the API via the browser. You can log in with the users in the settings.json file. The redirect URI is the URL where the user is redirected after the login. This is normally your etherpad instance url.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"client_id": "admin_client",
|
|
||||||
"client_secret": "admin",
|
|
||||||
"grant_types": ["authorization_code"],
|
|
||||||
"response_types": ["code"],
|
|
||||||
"redirect_uris": ["http://my-etherpad-instance.com"],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### Example for services
|
|
||||||
|
|
||||||
This example illustrates how to add a new client that can sign in via the API using the client credentials method. This method is used for services trying to sign in to the API where there is no browser.
|
|
||||||
E.g. a service that creates a pad for a user or a service that inserts a text into a pad. Just make sure that the secret is complex enough as anybody who knows the secret can access the API.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"client_id": "client_credentials",
|
|
||||||
"redirect_uris": [],
|
|
||||||
"response_types": [],
|
|
||||||
"grant_types": ["code"],
|
|
||||||
"client_secret": "client_credentials",
|
|
||||||
"extraParams": [
|
|
||||||
{
|
|
||||||
"name": "admin",
|
|
||||||
"value": "true"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Obtain a Bearer token:
|
|
||||||
|
|
||||||
`curl --request POST --url 'https://your.server.tld/oidc/token' --header 'content-type: application/x-www-form-urlencoded' --data grant_type=client_credentials --data client_id=client_credentials --data client_secret=client_credentials`
|
|
||||||
|
|
||||||
|
|
||||||
### Node Interoperability
|
### Node Interoperability
|
||||||
|
|
||||||
|
|
29
doc/cli.md
29
doc/cli.md
|
@ -1,29 +0,0 @@
|
||||||
# CLI
|
|
||||||
|
|
||||||
You can find different tools for migrating things, checking your Etherpad health in the bin directory.
|
|
||||||
One of these is the migrateDB command. It takes two settings.json files and copies data from one source to another one.
|
|
||||||
In this example we migrate from the old dirty db to the new rustydb engine. So we copy these files to the root of the etherpad-directory.
|
|
||||||
|
|
||||||
````json
|
|
||||||
{
|
|
||||||
"dbType": "dirty",
|
|
||||||
"dbSettings": {
|
|
||||||
"filename": "./var/rusty.db"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
````json
|
|
||||||
{
|
|
||||||
"dbType": "rustydb",
|
|
||||||
"dbSettings": {
|
|
||||||
"filename": "./var/rusty2.db"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
|
||||||
|
|
||||||
After that we need to move the data from dirty to rustydb.
|
|
||||||
Therefore, we call `pnpm run migrateDB --file1 test1.json --file2 test2.json` with these two files in our root directories. After some time the data should be copied over to the new database.
|
|
|
@ -510,7 +510,7 @@ For the editor container, you can also make it full width by adding `full-width-
|
||||||
|
|
||||||
| `SOCKETIO_MAX_HTTP_BUFFER_SIZE`
|
| `SOCKETIO_MAX_HTTP_BUFFER_SIZE`
|
||||||
| The maximum size (in bytes) of a single message accepted via Socket.IO. If a client sends a larger message, its connection gets closed to prevent DoS (memory exhaustion) attacks.
|
| The maximum size (in bytes) of a single message accepted via Socket.IO. If a client sends a larger message, its connection gets closed to prevent DoS (memory exhaustion) attacks.
|
||||||
| `50000`
|
| `10000`
|
||||||
|
|
||||||
| `LOAD_TEST`
|
| `LOAD_TEST`
|
||||||
| Allow Load Testing tools to hit the Etherpad Instance. WARNING: this will disable security on the instance.
|
| Allow Load Testing tools to hit the Etherpad Instance. WARNING: this will disable security on the instance.
|
||||||
|
|
|
@ -213,7 +213,7 @@ For the editor container, you can also make it full width by adding `full-width-
|
||||||
| `FOCUS_LINE_PERCENTAGE_ARROW_UP` | Percentage of viewport height to be additionally scrolled when user presses arrow up in the line of the top of the viewport. Set to 0 to let the scroll to be handled as default by Etherpad | `0` |
|
| `FOCUS_LINE_PERCENTAGE_ARROW_UP` | Percentage of viewport height to be additionally scrolled when user presses arrow up in the line of the top of the viewport. Set to 0 to let the scroll to be handled as default by Etherpad | `0` |
|
||||||
| `FOCUS_LINE_DURATION` | Time (in milliseconds) used to animate the scroll transition. Set to 0 to disable animation | `0` |
|
| `FOCUS_LINE_DURATION` | Time (in milliseconds) used to animate the scroll transition. Set to 0 to disable animation | `0` |
|
||||||
| `FOCUS_LINE_CARET_SCROLL` | Flag to control if it should scroll when user places the caret in the last line of the viewport | `false` |
|
| `FOCUS_LINE_CARET_SCROLL` | Flag to control if it should scroll when user places the caret in the last line of the viewport | `false` |
|
||||||
| `SOCKETIO_MAX_HTTP_BUFFER_SIZE` | The maximum size (in bytes) of a single message accepted via Socket.IO. If a client sends a larger message, its connection gets closed to prevent DoS (memory exhaustion) attacks. | `50000` |
|
| `SOCKETIO_MAX_HTTP_BUFFER_SIZE` | The maximum size (in bytes) of a single message accepted via Socket.IO. If a client sends a larger message, its connection gets closed to prevent DoS (memory exhaustion) attacks. | `10000` |
|
||||||
| `LOAD_TEST` | Allow Load Testing tools to hit the Etherpad Instance. WARNING: this will disable security on the instance. | `false` |
|
| `LOAD_TEST` | Allow Load Testing tools to hit the Etherpad Instance. WARNING: this will disable security on the instance. | `false` |
|
||||||
| `DUMP_ON_UNCLEAN_EXIT` | Enable dumping objects preventing a clean exit of Node.js. WARNING: this has a significant performance impact. | `false` |
|
| `DUMP_ON_UNCLEAN_EXIT` | Enable dumping objects preventing a clean exit of Node.js. WARNING: this has a significant performance impact. | `false` |
|
||||||
| `EXPOSE_VERSION` | Expose Etherpad version in the web interface and in the Server http header. Do not enable on production machines. | `false` |
|
| `EXPOSE_VERSION` | Expose Etherpad version in the web interface and in the Server http header. Do not enable on production machines. | `false` |
|
||||||
|
@ -278,43 +278,58 @@ docker run -d \
|
||||||
## Ready to use Docker Compose
|
## Ready to use Docker Compose
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
# Add this file to extend the docker-compose setup, e.g.:
|
||||||
|
# docker-compose build --no-cache
|
||||||
|
# docker-compose up -d --build --force-recreate
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
user: "0:0"
|
build:
|
||||||
image: etherpad/etherpad:latest
|
context: .
|
||||||
|
args:
|
||||||
|
ETHERPAD_PLUGINS:
|
||||||
|
# change from development to production if needed
|
||||||
|
target: development
|
||||||
tty: true
|
tty: true
|
||||||
stdin_open: true
|
stdin_open: true
|
||||||
volumes:
|
volumes:
|
||||||
- plugins:/opt/etherpad-lite/src/plugin_packages
|
# no volume mapping of node_modules as otherwise the build-time installed plugins will be overwritten with the mount
|
||||||
- etherpad-var:/opt/etherpad-lite/var
|
# the same applies to package.json and pnpm-lock.yaml in root dir as these would also get overwritten and build time installed plugins will be removed
|
||||||
|
- ./src:/opt/etherpad-lite/src
|
||||||
|
- ./bin:/opt/etherpad-lite/bin
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: production
|
# change from development to production if needed
|
||||||
ADMIN_PASSWORD: ${DOCKER_COMPOSE_APP_ADMIN_PASSWORD:-admin}
|
NODE_ENV: development
|
||||||
DB_CHARSET: ${DOCKER_COMPOSE_APP_DB_CHARSET:-utf8mb4}
|
ADMIN_PASSWORD: ${DOCKER_COMPOSE_APP_DEV_ADMIN_PASSWORD}
|
||||||
|
DB_CHARSET: ${DOCKER_COMPOSE_APP_DEV_ENV_DB_CHARSET:-utf8mb4}
|
||||||
DB_HOST: postgres
|
DB_HOST: postgres
|
||||||
DB_NAME: ${DOCKER_COMPOSE_POSTGRES_DATABASE:-etherpad}
|
DB_NAME: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_DATABASE:?}
|
||||||
DB_PASS: ${DOCKER_COMPOSE_POSTGRES_PASSWORD:-admin}
|
DB_PASS: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PASSWORD:?}
|
||||||
DB_PORT: ${DOCKER_COMPOSE_POSTGRES_PORT:-5432}
|
DB_PORT: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PORT:-5432}
|
||||||
DB_TYPE: "postgres"
|
DB_TYPE: "postgres"
|
||||||
DB_USER: ${DOCKER_COMPOSE_POSTGRES_USER:-admin}
|
DB_USER: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_USER:?}
|
||||||
# For now, the env var DEFAULT_PAD_TEXT cannot be unset or empty; it seems to be mandatory in the latest version of etherpad
|
# For now, the env var DEFAULT_PAD_TEXT cannot be unset or empty; it seems to be mandatory in the latest version of etherpad
|
||||||
DEFAULT_PAD_TEXT: ${DOCKER_COMPOSE_APP_DEFAULT_PAD_TEXT:- }
|
DEFAULT_PAD_TEXT: ${DOCKER_COMPOSE_APP_DEV_ENV_DEFAULT_PAD_TEXT:- }
|
||||||
DISABLE_IP_LOGGING: ${DOCKER_COMPOSE_APP_DISABLE_IP_LOGGING:-false}
|
DISABLE_IP_LOGGING: ${DOCKER_COMPOSE_APP_DEV_ENV_DISABLE_IP_LOGGING:-true}
|
||||||
SOFFICE: ${DOCKER_COMPOSE_APP_SOFFICE:-null}
|
SOFFICE: ${DOCKER_COMPOSE_APP_DEV_ENV_SOFFICE:-null}
|
||||||
TRUST_PROXY: ${DOCKER_COMPOSE_APP_TRUST_PROXY:-true}
|
TRUST_PROXY: ${DOCKER_COMPOSE_APP_DEV_ENV_TRUST_PROXY:-true}
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- "${DOCKER_COMPOSE_APP_PORT_PUBLISHED:-9001}:${DOCKER_COMPOSE_APP_PORT_TARGET:-9001}"
|
- "${DOCKER_COMPOSE_APP_DEV_PORT_PUBLISHED:-9001}:${DOCKER_COMPOSE_APP_DEV_PORT_TARGET:-9001}"
|
||||||
|
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:15-alpine
|
image: postgres:15-alpine
|
||||||
|
# Pass config parameters to the mysql server.
|
||||||
|
# Find more information below when you need to generate the ssl-relevant file your self
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: ${DOCKER_COMPOSE_POSTGRES_DATABASE:-etherpad}
|
POSTGRES_DB: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_DATABASE:?}
|
||||||
POSTGRES_PASSWORD: ${DOCKER_COMPOSE_POSTGRES_PASSWORD:-admin}
|
POSTGRES_PASSWORD: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PASSWORD:?}
|
||||||
POSTGRES_PORT: ${DOCKER_COMPOSE_POSTGRES_PORT:-5432}
|
POSTGRES_PORT: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PORT:-5432}
|
||||||
POSTGRES_USER: ${DOCKER_COMPOSE_POSTGRES_USER:-admin}
|
POSTGRES_USER: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_USER:?}
|
||||||
PGDATA: /var/lib/postgresql/data/pgdata
|
PGDATA: /var/lib/postgresql/data/pgdata
|
||||||
restart: always
|
restart: always
|
||||||
# Exposing the port is not needed unless you want to access this database instance from the host.
|
# Exposing the port is not needed unless you want to access this database instance from the host.
|
||||||
|
@ -326,6 +341,4 @@ services:
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
plugins:
|
|
||||||
etherpad-var:
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vitepress": "^1.6.3"
|
"vitepress": "^1.0.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docs:dev": "vitepress dev",
|
"docs:dev": "vitepress dev",
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
version: "3.8"
|
|
||||||
|
|
||||||
# Add this file to extend the docker-compose setup, e.g.:
|
|
||||||
# docker-compose build --no-cache
|
|
||||||
# docker-compose up -d --build --force-recreate
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
args:
|
|
||||||
# Attention: installed plugins in the node_modules folder get overwritten during volume mount in dev
|
|
||||||
ETHERPAD_PLUGINS:
|
|
||||||
# change from development to production if needed
|
|
||||||
target: development
|
|
||||||
tty: true
|
|
||||||
stdin_open: true
|
|
||||||
volumes:
|
|
||||||
# no volume mapping of node_modules as otherwise the build-time installed plugins will be overwritten with the mount
|
|
||||||
# the same applies to package.json and pnpm-lock.yaml in root dir as these would also get overwritten and build time installed plugins will be removed
|
|
||||||
- ./src:/opt/etherpad-lite/src
|
|
||||||
- ./bin:/opt/etherpad-lite/bin
|
|
||||||
depends_on:
|
|
||||||
- postgres
|
|
||||||
environment:
|
|
||||||
# change from development to production if needed
|
|
||||||
NODE_ENV: development
|
|
||||||
ADMIN_PASSWORD: ${DOCKER_COMPOSE_APP_DEV_ADMIN_PASSWORD}
|
|
||||||
DB_CHARSET: ${DOCKER_COMPOSE_APP_DEV_ENV_DB_CHARSET:-utf8mb4}
|
|
||||||
DB_HOST: postgres
|
|
||||||
DB_NAME: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_DATABASE:?}
|
|
||||||
DB_PASS: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PASSWORD:?}
|
|
||||||
DB_PORT: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PORT:-5432}
|
|
||||||
DB_TYPE: "postgres"
|
|
||||||
DB_USER: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_USER:?}
|
|
||||||
# For now, the env var DEFAULT_PAD_TEXT cannot be unset or empty; it seems to be mandatory in the latest version of etherpad
|
|
||||||
DEFAULT_PAD_TEXT: ${DOCKER_COMPOSE_APP_DEV_ENV_DEFAULT_PAD_TEXT:- }
|
|
||||||
DISABLE_IP_LOGGING: ${DOCKER_COMPOSE_APP_DEV_ENV_DISABLE_IP_LOGGING:-true}
|
|
||||||
SOFFICE: ${DOCKER_COMPOSE_APP_DEV_ENV_SOFFICE:-null}
|
|
||||||
TRUST_PROXY: ${DOCKER_COMPOSE_APP_DEV_ENV_TRUST_PROXY:-true}
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- "${DOCKER_COMPOSE_APP_DEV_PORT_PUBLISHED:-9001}:${DOCKER_COMPOSE_APP_DEV_PORT_TARGET:-9001}"
|
|
||||||
|
|
||||||
postgres:
|
|
||||||
image: postgres:15-alpine
|
|
||||||
# Pass config parameters to the mysql server.
|
|
||||||
# Find more information below when you need to generate the ssl-relevant file your self
|
|
||||||
environment:
|
|
||||||
POSTGRES_DB: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_DATABASE:?}
|
|
||||||
POSTGRES_PASSWORD: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PASSWORD:?}
|
|
||||||
POSTGRES_PORT: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PORT:-5432}
|
|
||||||
POSTGRES_USER: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_USER:?}
|
|
||||||
PGDATA: /var/lib/postgresql/data/pgdata
|
|
||||||
restart: always
|
|
||||||
# Exposing the port is not needed unless you want to access this database instance from the host.
|
|
||||||
# Be careful when other postgres docker container are running on the same port
|
|
||||||
# ports:
|
|
||||||
# - "5432:5432"
|
|
||||||
volumes:
|
|
||||||
- postgres_data:/var/lib/postgresql/data
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
postgres_data:
|
|
|
@ -1,40 +1,55 @@
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
# Add this file to extend the docker-compose setup, e.g.:
|
||||||
|
# docker-compose build --no-cache
|
||||||
|
# docker-compose up -d --build --force-recreate
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
user: "0:0"
|
build:
|
||||||
image: etherpad/etherpad:latest
|
context: .
|
||||||
|
args:
|
||||||
|
ETHERPAD_PLUGINS:
|
||||||
|
# change from development to production if needed
|
||||||
|
target: development
|
||||||
tty: true
|
tty: true
|
||||||
stdin_open: true
|
stdin_open: true
|
||||||
volumes:
|
volumes:
|
||||||
- plugins:/opt/etherpad-lite/src/plugin_packages
|
# no volume mapping of node_modules as otherwise the build-time installed plugins will be overwritten with the mount
|
||||||
- etherpad-var:/opt/etherpad-lite/var
|
# the same applies to package.json and pnpm-lock.yaml in root dir as these would also get overwritten and build time installed plugins will be removed
|
||||||
|
- ./src:/opt/etherpad-lite/src
|
||||||
|
- ./bin:/opt/etherpad-lite/bin
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: production
|
# change from development to production if needed
|
||||||
ADMIN_PASSWORD: ${DOCKER_COMPOSE_APP_ADMIN_PASSWORD:-admin}
|
NODE_ENV: development
|
||||||
DB_CHARSET: ${DOCKER_COMPOSE_APP_DB_CHARSET:-utf8mb4}
|
ADMIN_PASSWORD: ${DOCKER_COMPOSE_APP_DEV_ADMIN_PASSWORD}
|
||||||
|
DB_CHARSET: ${DOCKER_COMPOSE_APP_DEV_ENV_DB_CHARSET:-utf8mb4}
|
||||||
DB_HOST: postgres
|
DB_HOST: postgres
|
||||||
DB_NAME: ${DOCKER_COMPOSE_POSTGRES_DATABASE:-etherpad}
|
DB_NAME: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_DATABASE:?}
|
||||||
DB_PASS: ${DOCKER_COMPOSE_POSTGRES_PASSWORD:-admin}
|
DB_PASS: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PASSWORD:?}
|
||||||
DB_PORT: ${DOCKER_COMPOSE_POSTGRES_PORT:-5432}
|
DB_PORT: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PORT:-5432}
|
||||||
DB_TYPE: "postgres"
|
DB_TYPE: "postgres"
|
||||||
DB_USER: ${DOCKER_COMPOSE_POSTGRES_USER:-admin}
|
DB_USER: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_USER:?}
|
||||||
# For now, the env var DEFAULT_PAD_TEXT cannot be unset or empty; it seems to be mandatory in the latest version of etherpad
|
# For now, the env var DEFAULT_PAD_TEXT cannot be unset or empty; it seems to be mandatory in the latest version of etherpad
|
||||||
DEFAULT_PAD_TEXT: ${DOCKER_COMPOSE_APP_DEFAULT_PAD_TEXT:- }
|
DEFAULT_PAD_TEXT: ${DOCKER_COMPOSE_APP_DEV_ENV_DEFAULT_PAD_TEXT:- }
|
||||||
DISABLE_IP_LOGGING: ${DOCKER_COMPOSE_APP_DISABLE_IP_LOGGING:-false}
|
DISABLE_IP_LOGGING: ${DOCKER_COMPOSE_APP_DEV_ENV_DISABLE_IP_LOGGING:-true}
|
||||||
SOFFICE: ${DOCKER_COMPOSE_APP_SOFFICE:-null}
|
SOFFICE: ${DOCKER_COMPOSE_APP_DEV_ENV_SOFFICE:-null}
|
||||||
TRUST_PROXY: ${DOCKER_COMPOSE_APP_TRUST_PROXY:-true}
|
TRUST_PROXY: ${DOCKER_COMPOSE_APP_DEV_ENV_TRUST_PROXY:-true}
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- "${DOCKER_COMPOSE_APP_PORT_PUBLISHED:-9001}:${DOCKER_COMPOSE_APP_PORT_TARGET:-9001}"
|
- "${DOCKER_COMPOSE_APP_DEV_PORT_PUBLISHED:-9001}:${DOCKER_COMPOSE_APP_DEV_PORT_TARGET:-9001}"
|
||||||
|
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:15-alpine
|
image: postgres:15-alpine
|
||||||
|
# Pass config parameters to the mysql server.
|
||||||
|
# Find more information below when you need to generate the ssl-relevant file your self
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: ${DOCKER_COMPOSE_POSTGRES_DATABASE:-etherpad}
|
POSTGRES_DB: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_DATABASE:?}
|
||||||
POSTGRES_PASSWORD: ${DOCKER_COMPOSE_POSTGRES_PASSWORD:-admin}
|
POSTGRES_PASSWORD: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PASSWORD:?}
|
||||||
POSTGRES_PORT: ${DOCKER_COMPOSE_POSTGRES_PORT:-5432}
|
POSTGRES_PORT: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_PORT:-5432}
|
||||||
POSTGRES_USER: ${DOCKER_COMPOSE_POSTGRES_USER:-admin}
|
POSTGRES_USER: ${DOCKER_COMPOSE_POSTGRES_DEV_ENV_POSTGRES_USER:?}
|
||||||
PGDATA: /var/lib/postgresql/data/pgdata
|
PGDATA: /var/lib/postgresql/data/pgdata
|
||||||
restart: always
|
restart: always
|
||||||
# Exposing the port is not needed unless you want to access this database instance from the host.
|
# Exposing the port is not needed unless you want to access this database instance from the host.
|
||||||
|
@ -42,9 +57,7 @@ services:
|
||||||
# ports:
|
# ports:
|
||||||
# - "5432:5432"
|
# - "5432:5432"
|
||||||
volumes:
|
volumes:
|
||||||
- postgres_data:/var/lib/postgresql/data
|
- postgres_data:/var/lib/postgresql/data/pgdata
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
plugins:
|
|
||||||
etherpad-var:
|
|
3
local_plugins/.gitignore
vendored
3
local_plugins/.gitignore
vendored
|
@ -1,3 +0,0 @@
|
||||||
# ignore everything
|
|
||||||
*
|
|
||||||
!.gitignore
|
|
55
make_docs.js
Normal file
55
make_docs.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import {exec} from 'child_process'
|
||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
import pjson from './src/package.json' assert {type: "json"}
|
||||||
|
|
||||||
|
const VERSION=pjson.version
|
||||||
|
console.log(`Building docs for version ${VERSION}`)
|
||||||
|
|
||||||
|
const createDirIfNotExists = (dir) => {
|
||||||
|
if (!fs.existsSync(dir)){
|
||||||
|
fs.mkdirSync(dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function copyFolderSync(from, to) {
|
||||||
|
if(fs.existsSync(to)){
|
||||||
|
const stat = fs.lstatSync(to)
|
||||||
|
if (stat.isDirectory()){
|
||||||
|
fs.rmSync(to, { recursive: true })
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
fs.rmSync(to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fs.mkdirSync(to);
|
||||||
|
fs.readdirSync(from).forEach(element => {
|
||||||
|
if (fs.lstatSync(path.join(from, element)).isFile()) {
|
||||||
|
fs.copyFileSync(path.join(from, element), path.join(to, element))
|
||||||
|
} else {
|
||||||
|
copyFolderSync(path.join(from, element), path.join(to, element))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
exec('asciidoctor -v', (err,stdout)=>{
|
||||||
|
if (err){
|
||||||
|
console.log('Please install asciidoctor')
|
||||||
|
console.log('https://asciidoctor.org/docs/install-toolchain/')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
createDirIfNotExists('./out')
|
||||||
|
createDirIfNotExists('./out/doc')
|
||||||
|
createDirIfNotExists('./out/doc/api')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
exec(`asciidoctor -D out/doc doc/index.adoc */**.adoc -a VERSION=${VERSION}`)
|
||||||
|
exec(`asciidoctor -D out/doc/api ./doc/api/*.adoc -a VERSION=${VERSION}`)
|
||||||
|
|
||||||
|
copyFolderSync('./doc/public/', './out/doc/')
|
11
package.json
11
package.json
|
@ -15,7 +15,6 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "pnpm --filter ep_etherpad-lite run lint",
|
"lint": "pnpm --filter ep_etherpad-lite run lint",
|
||||||
"test": "pnpm --filter ep_etherpad-lite run test",
|
"test": "pnpm --filter ep_etherpad-lite run test",
|
||||||
"test-utils": "pnpm --filter ep_etherpad-lite run test-utils",
|
|
||||||
"test-container": "pnpm --filter ep_etherpad-lite run test-container",
|
"test-container": "pnpm --filter ep_etherpad-lite run test-container",
|
||||||
"dev": "pnpm --filter ep_etherpad-lite run dev",
|
"dev": "pnpm --filter ep_etherpad-lite run dev",
|
||||||
"prod": "pnpm --filter ep_etherpad-lite run prod",
|
"prod": "pnpm --filter ep_etherpad-lite run prod",
|
||||||
|
@ -25,13 +24,7 @@
|
||||||
"test-ui:ui": "pnpm --filter ep_etherpad-lite run test-ui:ui",
|
"test-ui:ui": "pnpm --filter ep_etherpad-lite run test-ui:ui",
|
||||||
"test-admin": "pnpm --filter ep_etherpad-lite run test-admin",
|
"test-admin": "pnpm --filter ep_etherpad-lite run test-admin",
|
||||||
"test-admin:ui": "pnpm --filter ep_etherpad-lite run test-admin:ui",
|
"test-admin:ui": "pnpm --filter ep_etherpad-lite run test-admin:ui",
|
||||||
"plugins": "pnpm --filter bin run plugins",
|
"install-plugins": "pnpm --filter bin run install-plugins"
|
||||||
"install-plugins": "pnpm --filter bin run plugins i",
|
|
||||||
"remove-plugins": "pnpm --filter bin run remove-plugins",
|
|
||||||
"list-plugins": "pnpm --filter bin run list-plugins",
|
|
||||||
"build:etherpad": "pnpm --filter admin run build-copy && pnpm --filter ui run build-copy",
|
|
||||||
"build:ui": "pnpm --filter ui run build-copy && pnpm --filter admin run build-copy",
|
|
||||||
"makeDocs": "pnpm --filter bin run makeDocs"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ep_etherpad-lite": "workspace:./src"
|
"ep_etherpad-lite": "workspace:./src"
|
||||||
|
@ -50,6 +43,6 @@
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/ether/etherpad-lite.git"
|
"url": "https://github.com/ether/etherpad-lite.git"
|
||||||
},
|
},
|
||||||
"version": "2.3.0",
|
"version": "2.0.2",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
}
|
}
|
||||||
|
|
13150
pnpm-lock.yaml
generated
13150
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -4,7 +4,3 @@ packages:
|
||||||
- bin
|
- bin
|
||||||
- doc
|
- doc
|
||||||
- ui
|
- ui
|
||||||
onlyBuiltDependencies:
|
|
||||||
- '@scarf/scarf'
|
|
||||||
- '@swc/core'
|
|
||||||
- esbuild
|
|
||||||
|
|
|
@ -96,7 +96,7 @@
|
||||||
* 3) if you want to use newlines in the default value of a string parameter,
|
* 3) if you want to use newlines in the default value of a string parameter,
|
||||||
* use "\n" as usual.
|
* use "\n" as usual.
|
||||||
*
|
*
|
||||||
* "defaultPadText" : "${DEFAULT_PAD_TEXT:Line 1\nLine 2}"
|
* "defaultPadText" : "${DEFAULT_PAD_TEXT}Line 1\nLine 2"
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -171,21 +171,6 @@
|
||||||
*/
|
*/
|
||||||
"showSettingsInAdminPage": "${SHOW_SETTINGS_IN_ADMIN_PAGE:true}",
|
"showSettingsInAdminPage": "${SHOW_SETTINGS_IN_ADMIN_PAGE:true}",
|
||||||
|
|
||||||
/*
|
|
||||||
* Settings for cleanup of pads
|
|
||||||
*/
|
|
||||||
"cleanup": {
|
|
||||||
"enabled": false,
|
|
||||||
"keepRevisions": 5
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
The authentication method used by the server.
|
|
||||||
The default value is sso
|
|
||||||
If you want to use the old authentication system, change this to apikey
|
|
||||||
*/
|
|
||||||
"authenticationMethod": "${AUTHENTICATION_METHOD:sso}",
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Node native SSL support
|
* Node native SSL support
|
||||||
*
|
*
|
||||||
|
@ -202,15 +187,6 @@
|
||||||
},
|
},
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Enables the use of a different server. We have a different one that syncs changes from the original server.
|
|
||||||
* It is hosted on GitHub and should not be blocked by many firewalls.
|
|
||||||
* https://etherpad.org/ep_infos
|
|
||||||
*/
|
|
||||||
|
|
||||||
"updateServer": "https://etherpad.org/ep_infos",
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The type of the database.
|
* The type of the database.
|
||||||
*
|
*
|
||||||
|
@ -561,7 +537,7 @@
|
||||||
* value to work properly, but increasing the value increases susceptibility
|
* value to work properly, but increasing the value increases susceptibility
|
||||||
* to denial of service attacks (malicious clients can exhaust memory).
|
* to denial of service attacks (malicious clients can exhaust memory).
|
||||||
*/
|
*/
|
||||||
"maxHttpBufferSize": "${SOCKETIO_MAX_HTTP_BUFFER_SIZE:50000}"
|
"maxHttpBufferSize": "${SOCKETIO_MAX_HTTP_BUFFER_SIZE:10000}"
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -693,16 +669,5 @@
|
||||||
"redirect_uris": ["${USER_REDIRECT:http://localhost:9001/}"]
|
"redirect_uris": ["${USER_REDIRECT:http://localhost:9001/}"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
|
|
||||||
/* Set the time to live for the tokens
|
|
||||||
This is the time of seconds a user is logged into Etherpad
|
|
||||||
"ttl": {
|
|
||||||
"AccessToken": 3600,
|
|
||||||
"AuthorizationCode": 600,
|
|
||||||
"ClientCredentials": 3600,
|
|
||||||
"IdToken": 3600,
|
|
||||||
"RefreshToken": 86400
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@
|
||||||
* 3) if you want to use newlines in the default value of a string parameter,
|
* 3) if you want to use newlines in the default value of a string parameter,
|
||||||
* use "\n" as usual.
|
* use "\n" as usual.
|
||||||
*
|
*
|
||||||
* "defaultPadText" : "${DEFAULT_PAD_TEXT:Line 1\nLine 2}"
|
* "defaultPadText" : "${DEFAULT_PAD_TEXT}Line 1\nLine 2"
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -162,14 +162,6 @@
|
||||||
*/
|
*/
|
||||||
"showSettingsInAdminPage": true,
|
"showSettingsInAdminPage": true,
|
||||||
|
|
||||||
/*
|
|
||||||
* Settings for cleanup of pads
|
|
||||||
*/
|
|
||||||
"cleanup": {
|
|
||||||
"enabled": false,
|
|
||||||
"keepRevisions": 5
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Node native SSL support
|
* Node native SSL support
|
||||||
*
|
*
|
||||||
|
@ -279,14 +271,6 @@
|
||||||
"pageDown": true
|
"pageDown": true
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
|
||||||
* Enables the use of a different server. We have a different one that syncs changes from the original server.
|
|
||||||
* It is hosted on GitHub and should not be blocked by many firewalls.
|
|
||||||
* https://etherpad.org/ep_infos
|
|
||||||
*/
|
|
||||||
|
|
||||||
"updateServer": "https://etherpad.org/ep_infos",
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Should we suppress errors from being visible in the default Pad Text?
|
* Should we suppress errors from being visible in the default Pad Text?
|
||||||
*/
|
*/
|
||||||
|
@ -553,7 +537,7 @@
|
||||||
* value to work properly, but increasing the value increases susceptibility
|
* value to work properly, but increasing the value increases susceptibility
|
||||||
* to denial of service attacks (malicious clients can exhaust memory).
|
* to denial of service attacks (malicious clients can exhaust memory).
|
||||||
*/
|
*/
|
||||||
"maxHttpBufferSize": 50000
|
"maxHttpBufferSize": 10000
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -602,13 +586,6 @@
|
||||||
*/
|
*/
|
||||||
"importMaxFileSize": 52428800, // 50 * 1024 * 1024
|
"importMaxFileSize": 52428800, // 50 * 1024 * 1024
|
||||||
|
|
||||||
/*
|
|
||||||
The authentication method used by the server.
|
|
||||||
The default value is sso
|
|
||||||
If you want to use the old authentication system, change this to apikey
|
|
||||||
*/
|
|
||||||
"authenticationMethod": "${AUTHENTICATION_METHOD:sso}",
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* From Etherpad 1.8.5 onwards, when Etherpad is in production mode commits from individual users are rate limited
|
* From Etherpad 1.8.5 onwards, when Etherpad is in production mode commits from individual users are rate limited
|
||||||
*
|
*
|
||||||
|
@ -664,13 +641,6 @@
|
||||||
*/
|
*/
|
||||||
"loglevel": "INFO",
|
"loglevel": "INFO",
|
||||||
|
|
||||||
/*
|
|
||||||
* The log layout type to use.
|
|
||||||
*
|
|
||||||
* Valid values: basic, colored
|
|
||||||
*/
|
|
||||||
"logLayoutType": "colored",
|
|
||||||
|
|
||||||
/* Override any strings found in locale directories */
|
/* Override any strings found in locale directories */
|
||||||
"customLocaleStrings": {},
|
"customLocaleStrings": {},
|
||||||
|
|
||||||
|
@ -701,15 +671,4 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the time to live for the tokens
|
|
||||||
This is the time of seconds a user is logged into Etherpad
|
|
||||||
"ttl": {
|
|
||||||
"AccessToken": 3600,
|
|
||||||
"AuthorizationCode": 600,
|
|
||||||
"ClientCredentials": 3600,
|
|
||||||
"IdToken": 3600,
|
|
||||||
"RefreshToken": 86400
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
15
src/ep.json
15
src/ep.json
|
@ -42,8 +42,7 @@
|
||||||
"name": "specialpages",
|
"name": "specialpages",
|
||||||
"hooks": {
|
"hooks": {
|
||||||
"expressCreateServer": "ep_etherpad-lite/node/hooks/express/specialpages",
|
"expressCreateServer": "ep_etherpad-lite/node/hooks/express/specialpages",
|
||||||
"expressPreSession": "ep_etherpad-lite/node/hooks/express/specialpages",
|
"expressPreSession": "ep_etherpad-lite/node/hooks/express/specialpages"
|
||||||
"socketio": "ep_etherpad-lite/node/hooks/express/specialpages"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -58,12 +57,6 @@
|
||||||
"expressCreateServer": "ep_etherpad-lite/node/hooks/express/padurlsanitize"
|
"expressCreateServer": "ep_etherpad-lite/node/hooks/express/padurlsanitize"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "pwa",
|
|
||||||
"hooks": {
|
|
||||||
"expressCreateServer": "ep_etherpad-lite/node/hooks/express/pwa"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "apicalls",
|
"name": "apicalls",
|
||||||
"hooks": {
|
"hooks": {
|
||||||
|
@ -82,12 +75,6 @@
|
||||||
"expressCreateServer": "ep_etherpad-lite/node/hooks/express/errorhandling"
|
"expressCreateServer": "ep_etherpad-lite/node/hooks/express/errorhandling"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "restApi",
|
|
||||||
"hooks": {
|
|
||||||
"expressCreateServer": "ep_etherpad-lite/node/handler/RestAPI"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "socketio",
|
"name": "socketio",
|
||||||
"hooks": {
|
"hooks": {
|
||||||
|
|
|
@ -16,18 +16,10 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"admin.page-title": "لوحة تحكم المسؤول - Etherpad",
|
"admin.page-title": "لوحة تحكم المسؤول - Etherpad",
|
||||||
"admin_plugins": "مدير المكونات الإضافية",
|
|
||||||
"admin_plugins.available": "المكونات الإضافية المتاحة",
|
|
||||||
"admin_plugins.available_not-found": "لم يتم العثور على أي مكونات إضافية.",
|
|
||||||
"admin_plugins.available_fetching": "جاري الجلب…",
|
|
||||||
"admin_plugins.available_install.value": "تنصيب",
|
|
||||||
"admin_plugins.available_search.placeholder": "ابحث عن المكونات الإضافية للتثبيت",
|
|
||||||
"admin_plugins.description": "الوصف",
|
"admin_plugins.description": "الوصف",
|
||||||
"admin_plugins.installed": "الإضافات المثبتة",
|
"admin_plugins.installed": "الإضافات المثبتة",
|
||||||
"admin_plugins.installed_fetching": "جارٍ إحضار المكونات الإضافية المثبتة ...",
|
"admin_plugins.installed_fetching": "جارٍ إحضار المكونات الإضافية المثبتة ...",
|
||||||
"admin_plugins.installed_nothing": "لم تقم بتثبيت أي مكونات إضافية حتى الآن.",
|
"admin_plugins.installed_nothing": "لم تقم بتثبيت أي مكونات إضافية حتى الآن.",
|
||||||
"admin_plugins.installed_uninstall.value": "إلغاء التثبيت",
|
|
||||||
"admin_plugins.last-update": "آخر تحديث",
|
|
||||||
"admin_plugins.name": "الاسم",
|
"admin_plugins.name": "الاسم",
|
||||||
"admin_plugins.page-title": "مدير البرنامج المساعد - Etherpad",
|
"admin_plugins.page-title": "مدير البرنامج المساعد - Etherpad",
|
||||||
"admin_plugins.version": "الإصدار",
|
"admin_plugins.version": "الإصدار",
|
||||||
|
@ -39,10 +31,8 @@
|
||||||
"admin_plugins_info.plugins": "الإضافات المثبتة",
|
"admin_plugins_info.plugins": "الإضافات المثبتة",
|
||||||
"admin_plugins_info.page-title": "معلومات البرنامج المساعد - Etherpad",
|
"admin_plugins_info.page-title": "معلومات البرنامج المساعد - Etherpad",
|
||||||
"admin_plugins_info.version": "إصدار Etherpad",
|
"admin_plugins_info.version": "إصدار Etherpad",
|
||||||
"admin_plugins_info.version_latest": "أحدث إصدار متاح",
|
|
||||||
"admin_plugins_info.version_number": "رقم الإصدار",
|
"admin_plugins_info.version_number": "رقم الإصدار",
|
||||||
"admin_settings": "إعدادات",
|
"admin_settings": "إعدادات",
|
||||||
"admin_settings.current": "التكوين الحالي",
|
|
||||||
"admin_settings.current_example-devel": "مثال على قالب إعدادات التطوير",
|
"admin_settings.current_example-devel": "مثال على قالب إعدادات التطوير",
|
||||||
"admin_settings.current_example-prod": "مثال على قالب إعدادات الإنتاج",
|
"admin_settings.current_example-prod": "مثال على قالب إعدادات الإنتاج",
|
||||||
"admin_settings.current_restart.value": "أعد تشغيل Etherpad",
|
"admin_settings.current_restart.value": "أعد تشغيل Etherpad",
|
||||||
|
@ -83,8 +73,6 @@
|
||||||
"pad.settings.fontType": "نوع الخط:",
|
"pad.settings.fontType": "نوع الخط:",
|
||||||
"pad.settings.fontType.normal": "عادي",
|
"pad.settings.fontType.normal": "عادي",
|
||||||
"pad.settings.language": "اللغة:",
|
"pad.settings.language": "اللغة:",
|
||||||
"pad.settings.deletePad": "حذف الوسادة",
|
|
||||||
"pad.delete.confirm": "هل تريد حقا حذف هذه الوسادة؟",
|
|
||||||
"pad.settings.about": "حول",
|
"pad.settings.about": "حول",
|
||||||
"pad.settings.poweredBy": "مدعوم من",
|
"pad.settings.poweredBy": "مدعوم من",
|
||||||
"pad.importExport.import_export": "استيراد/تصدير",
|
"pad.importExport.import_export": "استيراد/تصدير",
|
||||||
|
@ -122,8 +110,6 @@
|
||||||
"pad.modals.deleted": "محذوف.",
|
"pad.modals.deleted": "محذوف.",
|
||||||
"pad.modals.deleted.explanation": "تمت إزالة هذا الباد.",
|
"pad.modals.deleted.explanation": "تمت إزالة هذا الباد.",
|
||||||
"pad.modals.rateLimited": "معدل محدود.",
|
"pad.modals.rateLimited": "معدل محدود.",
|
||||||
"pad.modals.rateLimited.explanation": "لقد أرسلت الكثير من الرسائل إلى هذه اللوحة مما أدى إلى قطع الاتصال بك.",
|
|
||||||
"pad.modals.rejected.explanation": "رفض الخادم الرسالة التي أرسلها متصفحك.",
|
|
||||||
"pad.modals.rejected.cause": "ربما تم تحديث الخادم أثناء عرض اللوحة ، أو ربما كان هناك خطأ في Etherpad. حاول إعادة تحميل الصفحة.",
|
"pad.modals.rejected.cause": "ربما تم تحديث الخادم أثناء عرض اللوحة ، أو ربما كان هناك خطأ في Etherpad. حاول إعادة تحميل الصفحة.",
|
||||||
"pad.modals.disconnected": "لم تعد متصلا.",
|
"pad.modals.disconnected": "لم تعد متصلا.",
|
||||||
"pad.modals.disconnected.explanation": "تم فقدان الاتصال بالخادم",
|
"pad.modals.disconnected.explanation": "تم فقدان الاتصال بالخادم",
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
"MuratTheTurkish",
|
"MuratTheTurkish",
|
||||||
"Mushviq Abdulla",
|
"Mushviq Abdulla",
|
||||||
"NMW03",
|
"NMW03",
|
||||||
"Nemoralis",
|
|
||||||
"Neriman2003",
|
"Neriman2003",
|
||||||
"Vesely35",
|
"Vesely35",
|
||||||
"Wertuose"
|
"Wertuose"
|
||||||
|
|
|
@ -35,10 +35,6 @@
|
||||||
"admin_plugins_info.version_number": "Нумар вэрсіі",
|
"admin_plugins_info.version_number": "Нумар вэрсіі",
|
||||||
"admin_settings": "Налады",
|
"admin_settings": "Налады",
|
||||||
"admin_settings.current": "Цяперашняя канфігурацыя",
|
"admin_settings.current": "Цяперашняя канфігурацыя",
|
||||||
"admin_settings.current_example-devel": "Прыклад шаблёну наладаў распрацоўкі",
|
|
||||||
"admin_settings.current_example-prod": "Прыклад шаблёну наладаў вытворчасьці",
|
|
||||||
"admin_settings.current_restart.value": "Перазапуск Etherpad",
|
|
||||||
"admin_settings.current_save.value": "Захаваць налады",
|
|
||||||
"admin_settings.page-title": "Налады — Etherpad",
|
"admin_settings.page-title": "Налады — Etherpad",
|
||||||
"index.newPad": "Стварыць",
|
"index.newPad": "Стварыць",
|
||||||
"index.createOpenPad": "ці тварыць/адкрыць дакумэнт з назвай:",
|
"index.createOpenPad": "ці тварыць/адкрыць дакумэнт з назвай:",
|
||||||
|
@ -76,7 +72,6 @@
|
||||||
"pad.settings.fontType.normal": "Звычайны",
|
"pad.settings.fontType.normal": "Звычайны",
|
||||||
"pad.settings.language": "Мова:",
|
"pad.settings.language": "Мова:",
|
||||||
"pad.settings.about": "Пра",
|
"pad.settings.about": "Пра",
|
||||||
"pad.settings.poweredBy": "Працуе на",
|
|
||||||
"pad.importExport.import_export": "Імпарт/Экспарт",
|
"pad.importExport.import_export": "Імпарт/Экспарт",
|
||||||
"pad.importExport.import": "Загрузіжайце любыя тэкставыя файлы або дакумэнты",
|
"pad.importExport.import": "Загрузіжайце любыя тэкставыя файлы або дакумэнты",
|
||||||
"pad.importExport.importSuccessful": "Пасьпяхова!",
|
"pad.importExport.importSuccessful": "Пасьпяхова!",
|
||||||
|
@ -111,9 +106,6 @@
|
||||||
"pad.modals.corruptPad.cause": "Гэта можа быць выклікана няправільнай канфігурацыяй сэрвэру або іншымі нечаканымі дзеяньнямі. Калі ласка, скантактуйцеся з адміністратарам службы.",
|
"pad.modals.corruptPad.cause": "Гэта можа быць выклікана няправільнай канфігурацыяй сэрвэру або іншымі нечаканымі дзеяньнямі. Калі ласка, скантактуйцеся з адміністратарам службы.",
|
||||||
"pad.modals.deleted": "Выдалены.",
|
"pad.modals.deleted": "Выдалены.",
|
||||||
"pad.modals.deleted.explanation": "Гэты дакумэнт быў выдалены.",
|
"pad.modals.deleted.explanation": "Гэты дакумэнт быў выдалены.",
|
||||||
"pad.modals.rateLimited": "Хуткасьць абмежаваная.",
|
|
||||||
"pad.modals.rateLimited.explanation": "Вы адаслалі так шмат паведамленьняў, што гэты дакумэнт вас адключыў.",
|
|
||||||
"pad.modals.rejected.explanation": "Сэрвэр адхіліў паведамленьне, адасланае вашым броўзэрам.",
|
|
||||||
"pad.modals.disconnected": "Вы былі адключаныя.",
|
"pad.modals.disconnected": "Вы былі адключаныя.",
|
||||||
"pad.modals.disconnected.explanation": "Злучэньне з сэрвэрам было страчанае",
|
"pad.modals.disconnected.explanation": "Злучэньне з сэрвэрам было страчанае",
|
||||||
"pad.modals.disconnected.cause": "Магчыма, сэрвэр недаступны. Калі ласка, паведаміце адміністратару службы, калі праблема будзе паўтарацца.",
|
"pad.modals.disconnected.cause": "Магчыма, сэрвэр недаступны. Калі ласка, паведаміце адміністратару службы, калі праблема будзе паўтарацца.",
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
"Bellayet",
|
"Bellayet",
|
||||||
"Greatder",
|
"Greatder",
|
||||||
"Nasir8891",
|
"Nasir8891",
|
||||||
"RiazACU",
|
|
||||||
"Sankarshan",
|
"Sankarshan",
|
||||||
"Sibabrata Banerjee",
|
"Sibabrata Banerjee",
|
||||||
"আজিজ",
|
"আজিজ",
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
{
|
|
||||||
"@metadata": {
|
|
||||||
"authors": [
|
|
||||||
"Умар"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"admin.page-title": "Администраторан панель — Etherpad",
|
|
||||||
"admin_plugins": "Плагинийн менеджер",
|
|
||||||
"admin_plugins.available": "ТӀекхочуш йолу плагинаш",
|
|
||||||
"admin_plugins.available_not-found": "Плагинаш ца карийна.",
|
|
||||||
"admin_plugins.available_fetching": "Схьаоьцуш...",
|
|
||||||
"admin_plugins.available_install.value": "ДӀахӀоттайе",
|
|
||||||
"admin_plugins.installed_uninstall.value": "ДӀайаккха",
|
|
||||||
"admin_plugins.last-update": "ТӀаьххьара карлайаккхар",
|
|
||||||
"admin_plugins.name": "ЦӀе",
|
|
||||||
"admin_plugins.page-title": "Плагинийн менеджер — Etherpad",
|
|
||||||
"admin_plugins.version": "Верси",
|
|
||||||
"admin_plugins_info.version_number": "Версин лоьмар",
|
|
||||||
"admin_settings": "Нисдаран гӀирс",
|
|
||||||
"admin_settings.current": "Карара конфигураци",
|
|
||||||
"pad.colorpicker.save": "Ӏалашйан",
|
|
||||||
"pad.colorpicker.cancel": "Йухайаккхар",
|
|
||||||
"pad.loading": "Чуйолуш…",
|
|
||||||
"pad.permissionDenied": "Хьан бакъонаш йац тӀекхача",
|
|
||||||
"pad.settings.padSettings": "Документан нисдаран гӀирс",
|
|
||||||
"pad.settings.myView": "Сан васт",
|
|
||||||
"pad.settings.stickychat": "Гуттара а гайта чат",
|
|
||||||
"pad.settings.language": "Мотт:",
|
|
||||||
"pad.settings.about": "Проектах лаьцна",
|
|
||||||
"pad.importExport.importSuccessful": "Кхиамца!",
|
|
||||||
"pad.modals.cancel": "Йухайаккхар",
|
|
||||||
"pad.share.link": "Хьажорг",
|
|
||||||
"pad.chat": "Чат",
|
|
||||||
"timeslider.toolbar.authors": "Авторш:",
|
|
||||||
"timeslider.toolbar.exportlink.title": "Экспорт",
|
|
||||||
"timeslider.month.january": "январь",
|
|
||||||
"timeslider.month.february": "февраль",
|
|
||||||
"timeslider.month.march": "март",
|
|
||||||
"timeslider.month.april": "апрель",
|
|
||||||
"timeslider.month.may": "май",
|
|
||||||
"timeslider.month.june": "июнь",
|
|
||||||
"timeslider.month.july": "июль",
|
|
||||||
"timeslider.month.august": "август",
|
|
||||||
"timeslider.month.september": "сентябрь",
|
|
||||||
"timeslider.month.october": "октябрь",
|
|
||||||
"timeslider.month.november": "ноябрь",
|
|
||||||
"timeslider.month.december": "декабрь",
|
|
||||||
"pad.impexp.importing": "Импорт йар..."
|
|
||||||
}
|
|
|
@ -11,8 +11,7 @@
|
||||||
"Mormegil",
|
"Mormegil",
|
||||||
"Peldrjan",
|
"Peldrjan",
|
||||||
"Quinn",
|
"Quinn",
|
||||||
"Spotter",
|
"Spotter"
|
||||||
"The astrea"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"admin.page-title": "Ovládací panel Správce - Etherpad",
|
"admin.page-title": "Ovládací panel Správce - Etherpad",
|
||||||
|
@ -83,8 +82,6 @@
|
||||||
"pad.settings.fontType": "Typ písma:",
|
"pad.settings.fontType": "Typ písma:",
|
||||||
"pad.settings.fontType.normal": "Normální",
|
"pad.settings.fontType.normal": "Normální",
|
||||||
"pad.settings.language": "Jazyk:",
|
"pad.settings.language": "Jazyk:",
|
||||||
"pad.settings.deletePad": "Smazat pad",
|
|
||||||
"pad.delete.confirm": "Opravdu chcete tento pad smazat?",
|
|
||||||
"pad.settings.about": "O projektu",
|
"pad.settings.about": "O projektu",
|
||||||
"pad.settings.poweredBy": "Běží na",
|
"pad.settings.poweredBy": "Běží na",
|
||||||
"pad.importExport.import_export": "Import/Export",
|
"pad.importExport.import_export": "Import/Export",
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
"Predatorix",
|
"Predatorix",
|
||||||
"SamTV",
|
"SamTV",
|
||||||
"Sebastian Wallroth",
|
"Sebastian Wallroth",
|
||||||
"Ssgl",
|
|
||||||
"Thargon",
|
"Thargon",
|
||||||
"Tim.krieger",
|
"Tim.krieger",
|
||||||
"Wikinaut",
|
"Wikinaut",
|
||||||
|
@ -86,8 +85,6 @@
|
||||||
"pad.settings.fontType": "Schriftart:",
|
"pad.settings.fontType": "Schriftart:",
|
||||||
"pad.settings.fontType.normal": "Normal",
|
"pad.settings.fontType.normal": "Normal",
|
||||||
"pad.settings.language": "Sprache:",
|
"pad.settings.language": "Sprache:",
|
||||||
"pad.settings.deletePad": "Pad löschen",
|
|
||||||
"pad.delete.confirm": "Möchtest du dieses Pad wirklich löschen?",
|
|
||||||
"pad.settings.about": "Über",
|
"pad.settings.about": "Über",
|
||||||
"pad.settings.poweredBy": "Betrieben von",
|
"pad.settings.poweredBy": "Betrieben von",
|
||||||
"pad.importExport.import_export": "Import/Export",
|
"pad.importExport.import_export": "Import/Export",
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
"authors": [
|
"authors": [
|
||||||
"1917 Ekim Devrimi",
|
"1917 Ekim Devrimi",
|
||||||
"Erdemaslancan",
|
"Erdemaslancan",
|
||||||
"GolyatGeri",
|
|
||||||
"Gorizon",
|
"Gorizon",
|
||||||
"Gırd",
|
"Gırd",
|
||||||
"Kumkumuk",
|
"Kumkumuk",
|
||||||
|
@ -79,8 +78,6 @@
|
||||||
"pad.settings.fontType": "Babeta nuşti:",
|
"pad.settings.fontType": "Babeta nuşti:",
|
||||||
"pad.settings.fontType.normal": "Normal",
|
"pad.settings.fontType.normal": "Normal",
|
||||||
"pad.settings.language": "Zıwan:",
|
"pad.settings.language": "Zıwan:",
|
||||||
"pad.settings.deletePad": "Defteri bıesterê",
|
|
||||||
"pad.delete.confirm": "Şıma raşti wazenê ke nê defteri bıesterên?",
|
|
||||||
"pad.settings.about": "Heqa",
|
"pad.settings.about": "Heqa",
|
||||||
"pad.settings.poweredBy": "Pheştidayoğ",
|
"pad.settings.poweredBy": "Pheştidayoğ",
|
||||||
"pad.importExport.import_export": "Zerredayış/Teberdayış",
|
"pad.importExport.import_export": "Zerredayış/Teberdayış",
|
||||||
|
|
|
@ -72,8 +72,6 @@
|
||||||
"pad.settings.fontType": "Pismowa družyna:",
|
"pad.settings.fontType": "Pismowa družyna:",
|
||||||
"pad.settings.fontType.normal": "Normalny",
|
"pad.settings.fontType.normal": "Normalny",
|
||||||
"pad.settings.language": "Rěc:",
|
"pad.settings.language": "Rěc:",
|
||||||
"pad.settings.deletePad": "Zapisnik lašowaś",
|
|
||||||
"pad.delete.confirm": "Cośo napšawdu toś ten zapisnik lašowaś?",
|
|
||||||
"pad.settings.about": "Wó",
|
"pad.settings.about": "Wó",
|
||||||
"pad.settings.poweredBy": "Pódpěrany wót",
|
"pad.settings.poweredBy": "Pódpěrany wót",
|
||||||
"pad.importExport.import_export": "Import/Eksport",
|
"pad.importExport.import_export": "Import/Eksport",
|
||||||
|
|
|
@ -14,12 +14,10 @@
|
||||||
"admin_plugins": "Διαχειριστής πρόσθετων",
|
"admin_plugins": "Διαχειριστής πρόσθετων",
|
||||||
"admin_plugins.available": "Διαθέσιμα πρόσθετα",
|
"admin_plugins.available": "Διαθέσιμα πρόσθετα",
|
||||||
"admin_plugins.available_not-found": "Δεν βρέθηκαν πρόσθετα.",
|
"admin_plugins.available_not-found": "Δεν βρέθηκαν πρόσθετα.",
|
||||||
"admin_plugins.available_fetching": "Ανακτάται...",
|
|
||||||
"admin_plugins.available_install.value": "Εγκατάσταση",
|
"admin_plugins.available_install.value": "Εγκατάσταση",
|
||||||
"admin_plugins.available_search.placeholder": "Αναζητήστε πρόσθετα για εγκατάσταση",
|
"admin_plugins.available_search.placeholder": "Αναζητήστε πρόσθετα για εγκατάσταση",
|
||||||
"admin_plugins.description": "Περιγραφή",
|
"admin_plugins.description": "Περιγραφή",
|
||||||
"admin_plugins.installed": "Εγκατεστημένα πρόσθετα",
|
"admin_plugins.installed": "Εγκατεστημένα πρόσθετα",
|
||||||
"admin_plugins.installed_fetching": "Ανάκτηση εγκατεστημένων προσθηκών…",
|
|
||||||
"admin_plugins.installed_nothing": "Δεν έχετε εγκαταστήσει πρόσθετα ακόμη.",
|
"admin_plugins.installed_nothing": "Δεν έχετε εγκαταστήσει πρόσθετα ακόμη.",
|
||||||
"admin_plugins.installed_uninstall.value": "Απεγκατάσταση",
|
"admin_plugins.installed_uninstall.value": "Απεγκατάσταση",
|
||||||
"admin_plugins.last-update": "Τελευταία ενημέρωση",
|
"admin_plugins.last-update": "Τελευταία ενημέρωση",
|
||||||
|
@ -77,8 +75,6 @@
|
||||||
"pad.settings.fontType": "Τύπος γραμματοσειράς:",
|
"pad.settings.fontType": "Τύπος γραμματοσειράς:",
|
||||||
"pad.settings.fontType.normal": "Κανονική",
|
"pad.settings.fontType.normal": "Κανονική",
|
||||||
"pad.settings.language": "Γλώσσα:",
|
"pad.settings.language": "Γλώσσα:",
|
||||||
"pad.settings.deletePad": "Διαγραφή Pad",
|
|
||||||
"pad.delete.confirm": "Θέλετε πραγματικά να διαγράψετε αυτό το pad;",
|
|
||||||
"pad.settings.about": "Σχετικά",
|
"pad.settings.about": "Σχετικά",
|
||||||
"pad.settings.poweredBy": "Υποστηρίζεται από",
|
"pad.settings.poweredBy": "Υποστηρίζεται από",
|
||||||
"pad.importExport.import_export": "Εισαγωγή/Εξαγωγή",
|
"pad.importExport.import_export": "Εισαγωγή/Εξαγωγή",
|
||||||
|
@ -93,7 +89,7 @@
|
||||||
"pad.importExport.exportopen": "ODF (Open Document Format)",
|
"pad.importExport.exportopen": "ODF (Open Document Format)",
|
||||||
"pad.importExport.abiword.innerHTML": "Μπορείτε να εισάγετε απλό κείμενο ή HTML. Για προηγμένες δυνατότητες εισαγωγής παρακαλούμε <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">εγκαταστήστε το AbiWord ή το LibreOffice</a>.",
|
"pad.importExport.abiword.innerHTML": "Μπορείτε να εισάγετε απλό κείμενο ή HTML. Για προηγμένες δυνατότητες εισαγωγής παρακαλούμε <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">εγκαταστήστε το AbiWord ή το LibreOffice</a>.",
|
||||||
"pad.modals.connected": "Συνδεμένοι.",
|
"pad.modals.connected": "Συνδεμένοι.",
|
||||||
"pad.modals.reconnecting": "Επανασύνδεση στο pad σας…",
|
"pad.modals.reconnecting": "Επανασύνδεση στο pad σας...",
|
||||||
"pad.modals.forcereconnect": "Επιβολή επανασύνδεσης",
|
"pad.modals.forcereconnect": "Επιβολή επανασύνδεσης",
|
||||||
"pad.modals.reconnecttimer": "Προσπάθεια επανασύνδεσης σε",
|
"pad.modals.reconnecttimer": "Προσπάθεια επανασύνδεσης σε",
|
||||||
"pad.modals.cancel": "Ακύρωση",
|
"pad.modals.cancel": "Ακύρωση",
|
||||||
|
@ -115,9 +111,7 @@
|
||||||
"pad.modals.corruptPad.cause": "Αυτό μπορεί να οφείλεται σε ένα λάθος στη ρύθμιση του διακομιστή ή κάποια άλλη απρόβλεπτη συμπεριφορά. Παρακαλώ επικοινωνήστε με τον διαχειριστή της υπηρεσίας.",
|
"pad.modals.corruptPad.cause": "Αυτό μπορεί να οφείλεται σε ένα λάθος στη ρύθμιση του διακομιστή ή κάποια άλλη απρόβλεπτη συμπεριφορά. Παρακαλώ επικοινωνήστε με τον διαχειριστή της υπηρεσίας.",
|
||||||
"pad.modals.deleted": "Διεγράφη.",
|
"pad.modals.deleted": "Διεγράφη.",
|
||||||
"pad.modals.deleted.explanation": "Αυτό το pad έχει καταργηθεί.",
|
"pad.modals.deleted.explanation": "Αυτό το pad έχει καταργηθεί.",
|
||||||
"pad.modals.rateLimited.explanation": "Στείλατε πάρα πολλά μηνύματα σε αυτό το pad, επομένως σας αποσύνδεσε.",
|
|
||||||
"pad.modals.rejected.explanation": "Ο διακομιστής απέρριψε ένα μήνυμα που στάλθηκε από το πρόγραμμα περιήγησής σας.",
|
"pad.modals.rejected.explanation": "Ο διακομιστής απέρριψε ένα μήνυμα που στάλθηκε από το πρόγραμμα περιήγησής σας.",
|
||||||
"pad.modals.rejected.cause": "Ο διακομιστής μπορεί να έχει ενημερωθεί ενώ προβάλλατε το pad ή ίσως υπάρχει σφάλμα στο Etherpad. Δοκιμάστε να φορτώσετε ξανά τη σελίδα.",
|
|
||||||
"pad.modals.disconnected": "Είστε αποσυνδεδεμένοι.",
|
"pad.modals.disconnected": "Είστε αποσυνδεδεμένοι.",
|
||||||
"pad.modals.disconnected.explanation": "Χάθηκε η σύνδεση με τον διακομιστή",
|
"pad.modals.disconnected.explanation": "Χάθηκε η σύνδεση με τον διακομιστή",
|
||||||
"pad.modals.disconnected.cause": "Ο διακομιστής μπορεί να μην είναι διαθέσιμος. Παρακαλούμε ειδοποιήστε τον διαχειριστή της υπηρεσίας εάν εξακολουθεί να συμβαίνει αυτό.",
|
"pad.modals.disconnected.cause": "Ο διακομιστής μπορεί να μην είναι διαθέσιμος. Παρακαλούμε ειδοποιήστε τον διαχειριστή της υπηρεσίας εάν εξακολουθεί να συμβαίνει αυτό.",
|
||||||
|
@ -130,7 +124,6 @@
|
||||||
"pad.chat.loadmessages": "Φόρτωση περισσότερων μηνυμάτων",
|
"pad.chat.loadmessages": "Φόρτωση περισσότερων μηνυμάτων",
|
||||||
"pad.chat.stick.title": "Κρατήστε τη συνομιλία στην οθόνη",
|
"pad.chat.stick.title": "Κρατήστε τη συνομιλία στην οθόνη",
|
||||||
"pad.chat.writeMessage.placeholder": "Γράψτε το μήνυμα σας εδώ",
|
"pad.chat.writeMessage.placeholder": "Γράψτε το μήνυμα σας εδώ",
|
||||||
"timeslider.followContents": "Ακολουθήστε τις ενημερώσεις περιεχομένου του pad",
|
|
||||||
"timeslider.pageTitle": "{{appTitle}} Χρονοδιάγραμμα",
|
"timeslider.pageTitle": "{{appTitle}} Χρονοδιάγραμμα",
|
||||||
"timeslider.toolbar.returnbutton": "Επιστροφή στο pad",
|
"timeslider.toolbar.returnbutton": "Επιστροφή στο pad",
|
||||||
"timeslider.toolbar.authors": "Συντάκτες:",
|
"timeslider.toolbar.authors": "Συντάκτες:",
|
||||||
|
|
|
@ -72,8 +72,6 @@
|
||||||
"pad.settings.fontType": "Font type:",
|
"pad.settings.fontType": "Font type:",
|
||||||
"pad.settings.fontType.normal": "Normal",
|
"pad.settings.fontType.normal": "Normal",
|
||||||
"pad.settings.language": "Language:",
|
"pad.settings.language": "Language:",
|
||||||
"pad.settings.deletePad": "Delete Pad",
|
|
||||||
"pad.delete.confirm": "Do you really want to delete this pad?",
|
|
||||||
"pad.settings.about": "About",
|
"pad.settings.about": "About",
|
||||||
"pad.settings.poweredBy": "Powered by",
|
"pad.settings.poweredBy": "Powered by",
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
"BMRG14",
|
"BMRG14",
|
||||||
"Beginneruser",
|
"Beginneruser",
|
||||||
"Dalba",
|
"Dalba",
|
||||||
"Ebrahim",
|
|
||||||
"Ebraminio",
|
"Ebraminio",
|
||||||
"FarsiNevis",
|
"FarsiNevis",
|
||||||
"Jeeputer",
|
"Jeeputer",
|
||||||
|
|
|
@ -87,8 +87,6 @@
|
||||||
"pad.settings.fontType": "Fonttityyppi:",
|
"pad.settings.fontType": "Fonttityyppi:",
|
||||||
"pad.settings.fontType.normal": "normaali",
|
"pad.settings.fontType.normal": "normaali",
|
||||||
"pad.settings.language": "Kieli:",
|
"pad.settings.language": "Kieli:",
|
||||||
"pad.settings.deletePad": "Poista muistio",
|
|
||||||
"pad.delete.confirm": "Haluatko todella poistaa tämän muistion?",
|
|
||||||
"pad.settings.about": "Tietoja",
|
"pad.settings.about": "Tietoja",
|
||||||
"pad.settings.poweredBy": "Palvelun mahdollistaa",
|
"pad.settings.poweredBy": "Palvelun mahdollistaa",
|
||||||
"pad.importExport.import_export": "Tuonti/vienti",
|
"pad.importExport.import_export": "Tuonti/vienti",
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
"Jean-Frédéric",
|
"Jean-Frédéric",
|
||||||
"Leviathan",
|
"Leviathan",
|
||||||
"Macofe",
|
"Macofe",
|
||||||
"Mahabarata",
|
|
||||||
"Maxim21",
|
"Maxim21",
|
||||||
"McDutchie",
|
"McDutchie",
|
||||||
"Metroitendo",
|
"Metroitendo",
|
||||||
|
@ -22,7 +21,6 @@
|
||||||
"Peter17",
|
"Peter17",
|
||||||
"Quenenni",
|
"Quenenni",
|
||||||
"Rastus Vernon",
|
"Rastus Vernon",
|
||||||
"Spf",
|
|
||||||
"Stephane Cottin",
|
"Stephane Cottin",
|
||||||
"Thibaut120094",
|
"Thibaut120094",
|
||||||
"Tux-tn",
|
"Tux-tn",
|
||||||
|
@ -99,8 +97,6 @@
|
||||||
"pad.settings.fontType": "Type de police :",
|
"pad.settings.fontType": "Type de police :",
|
||||||
"pad.settings.fontType.normal": "Normal",
|
"pad.settings.fontType.normal": "Normal",
|
||||||
"pad.settings.language": "Langue :",
|
"pad.settings.language": "Langue :",
|
||||||
"pad.settings.deletePad": "Supprimer le bloc-notes",
|
|
||||||
"pad.delete.confirm": "Voulez-vous vraiment supprimer ce bloc-notes ?",
|
|
||||||
"pad.settings.about": "À propos",
|
"pad.settings.about": "À propos",
|
||||||
"pad.settings.poweredBy": "Propulsé par",
|
"pad.settings.poweredBy": "Propulsé par",
|
||||||
"pad.importExport.import_export": "Importer/Exporter",
|
"pad.importExport.import_export": "Importer/Exporter",
|
||||||
|
@ -161,7 +157,7 @@
|
||||||
"timeslider.toolbar.exportlink.title": "Exporter",
|
"timeslider.toolbar.exportlink.title": "Exporter",
|
||||||
"timeslider.exportCurrent": "Exporter la version actuelle sous :",
|
"timeslider.exportCurrent": "Exporter la version actuelle sous :",
|
||||||
"timeslider.version": "Version {{version}}",
|
"timeslider.version": "Version {{version}}",
|
||||||
"timeslider.saved": "Enregistrée le {{day}} {{month}} {{year}}",
|
"timeslider.saved": "Enregistré le {{day}} {{month}} {{year}}",
|
||||||
"timeslider.playPause": "Lecture / Pause des contenus du bloc-notes",
|
"timeslider.playPause": "Lecture / Pause des contenus du bloc-notes",
|
||||||
"timeslider.backRevision": "Reculer d’une révision dans ce bloc-notes",
|
"timeslider.backRevision": "Reculer d’une révision dans ce bloc-notes",
|
||||||
"timeslider.forwardRevision": "Avancer d’une révision dans ce bloc-notes",
|
"timeslider.forwardRevision": "Avancer d’une révision dans ce bloc-notes",
|
||||||
|
|
|
@ -42,9 +42,9 @@
|
||||||
"index.newPad": "Novo documento",
|
"index.newPad": "Novo documento",
|
||||||
"index.createOpenPad": "ou crea/abre un documento co nome:",
|
"index.createOpenPad": "ou crea/abre un documento co nome:",
|
||||||
"index.openPad": "abrir un Pad existente co nome:",
|
"index.openPad": "abrir un Pad existente co nome:",
|
||||||
"pad.toolbar.bold.title": "Grosa (Ctrl+B)",
|
"pad.toolbar.bold.title": "Resaltado (Ctrl-B)",
|
||||||
"pad.toolbar.italic.title": "Cursiva (Ctrl+I)",
|
"pad.toolbar.italic.title": "Cursiva (Ctrl-I)",
|
||||||
"pad.toolbar.underline.title": "Subliñar (Ctrl+U)",
|
"pad.toolbar.underline.title": "Subliñar (Ctrl-U)",
|
||||||
"pad.toolbar.strikethrough.title": "Riscar (Ctrl+5)",
|
"pad.toolbar.strikethrough.title": "Riscar (Ctrl+5)",
|
||||||
"pad.toolbar.ol.title": "Lista ordenada (Ctrl+Shift+N)",
|
"pad.toolbar.ol.title": "Lista ordenada (Ctrl+Shift+N)",
|
||||||
"pad.toolbar.ul.title": "Lista sen ordenar (Ctrl+Shift+L)",
|
"pad.toolbar.ul.title": "Lista sen ordenar (Ctrl+Shift+L)",
|
||||||
|
@ -74,8 +74,6 @@
|
||||||
"pad.settings.fontType": "Tipo de letra:",
|
"pad.settings.fontType": "Tipo de letra:",
|
||||||
"pad.settings.fontType.normal": "Normal",
|
"pad.settings.fontType.normal": "Normal",
|
||||||
"pad.settings.language": "Lingua:",
|
"pad.settings.language": "Lingua:",
|
||||||
"pad.settings.deletePad": "Borrar o documento",
|
|
||||||
"pad.delete.confirm": "Queres borrar este documento?",
|
|
||||||
"pad.settings.about": "Acerca de",
|
"pad.settings.about": "Acerca de",
|
||||||
"pad.settings.poweredBy": "Grazas a",
|
"pad.settings.poweredBy": "Grazas a",
|
||||||
"pad.importExport.import_export": "Importar/Exportar",
|
"pad.importExport.import_export": "Importar/Exportar",
|
||||||
|
@ -90,7 +88,7 @@
|
||||||
"pad.importExport.exportopen": "ODF (Open Document Format)",
|
"pad.importExport.exportopen": "ODF (Open Document Format)",
|
||||||
"pad.importExport.abiword.innerHTML": "Só podes importar texto simple ou formatos HTML. Para obter máis información sobre as características de importación avanzadas <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">instala AbiWord</a>.",
|
"pad.importExport.abiword.innerHTML": "Só podes importar texto simple ou formatos HTML. Para obter máis información sobre as características de importación avanzadas <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">instala AbiWord</a>.",
|
||||||
"pad.modals.connected": "Conectado.",
|
"pad.modals.connected": "Conectado.",
|
||||||
"pad.modals.reconnecting": "Reconectando co teu documento...",
|
"pad.modals.reconnecting": "Reconectando co seu documento...",
|
||||||
"pad.modals.forcereconnect": "Forzar a reconexión",
|
"pad.modals.forcereconnect": "Forzar a reconexión",
|
||||||
"pad.modals.reconnecttimer": "Intentarase reconectar en",
|
"pad.modals.reconnecttimer": "Intentarase reconectar en",
|
||||||
"pad.modals.cancel": "Cancelar",
|
"pad.modals.cancel": "Cancelar",
|
||||||
|
|
|
@ -76,8 +76,6 @@
|
||||||
"pad.settings.fontType": "סוג גופן:",
|
"pad.settings.fontType": "סוג גופן:",
|
||||||
"pad.settings.fontType.normal": "רגיל",
|
"pad.settings.fontType.normal": "רגיל",
|
||||||
"pad.settings.language": "שפה:",
|
"pad.settings.language": "שפה:",
|
||||||
"pad.settings.deletePad": "מחיקת פנקס",
|
|
||||||
"pad.delete.confirm": "למחוק את הפנקס הזה?",
|
|
||||||
"pad.settings.about": "על אודות",
|
"pad.settings.about": "על אודות",
|
||||||
"pad.settings.poweredBy": "מופעל על גבי",
|
"pad.settings.poweredBy": "מופעל על גבי",
|
||||||
"pad.importExport.import_export": "יבוא/יצוא",
|
"pad.importExport.import_export": "יבוא/יצוא",
|
||||||
|
|
|
@ -72,8 +72,6 @@
|
||||||
"pad.settings.fontType": "Pismowa družina:",
|
"pad.settings.fontType": "Pismowa družina:",
|
||||||
"pad.settings.fontType.normal": "Normalny",
|
"pad.settings.fontType.normal": "Normalny",
|
||||||
"pad.settings.language": "Rěč:",
|
"pad.settings.language": "Rěč:",
|
||||||
"pad.settings.deletePad": "Zapisnik zhašeć",
|
|
||||||
"pad.delete.confirm": "Chceće woprawdźe tutón zapisnik zhašeć?",
|
|
||||||
"pad.settings.about": "Wo",
|
"pad.settings.about": "Wo",
|
||||||
"pad.settings.poweredBy": "Spěchowany wot",
|
"pad.settings.poweredBy": "Spěchowany wot",
|
||||||
"pad.importExport.import_export": "Import/Eksport",
|
"pad.importExport.import_export": "Import/Eksport",
|
||||||
|
|
|
@ -72,8 +72,6 @@
|
||||||
"pad.settings.fontType": "Typo de litteras:",
|
"pad.settings.fontType": "Typo de litteras:",
|
||||||
"pad.settings.fontType.normal": "Normal",
|
"pad.settings.fontType.normal": "Normal",
|
||||||
"pad.settings.language": "Lingua:",
|
"pad.settings.language": "Lingua:",
|
||||||
"pad.settings.deletePad": "Deler pad",
|
|
||||||
"pad.delete.confirm": "Es tu secur de voler deler iste pad?",
|
|
||||||
"pad.settings.about": "A proposito",
|
"pad.settings.about": "A proposito",
|
||||||
"pad.settings.poweredBy": "Actionate per",
|
"pad.settings.poweredBy": "Actionate per",
|
||||||
"pad.importExport.import_export": "Importar/Exportar",
|
"pad.importExport.import_export": "Importar/Exportar",
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
{
|
{
|
||||||
"@metadata": {
|
"@metadata": {
|
||||||
"authors": [
|
"authors": [
|
||||||
"Akmaie Ajam",
|
|
||||||
"Atriwidada",
|
|
||||||
"Bennylin",
|
"Bennylin",
|
||||||
"IvanLanin",
|
"IvanLanin",
|
||||||
"Marwan Mohamad",
|
"Marwan Mohamad",
|
||||||
|
@ -12,39 +10,13 @@
|
||||||
"admin.page-title": "Dasbor Pengurus - Etherpad",
|
"admin.page-title": "Dasbor Pengurus - Etherpad",
|
||||||
"admin_plugins": "Manajer plugin",
|
"admin_plugins": "Manajer plugin",
|
||||||
"admin_plugins.available": "Plugin yang tersedia",
|
"admin_plugins.available": "Plugin yang tersedia",
|
||||||
"admin_plugins.available_not-found": "Tidak ada plugin yang ditemukan.",
|
|
||||||
"admin_plugins.available_fetching": "Mengambil…",
|
|
||||||
"admin_plugins.available_install.value": "Instal",
|
"admin_plugins.available_install.value": "Instal",
|
||||||
"admin_plugins.available_search.placeholder": "Cari plugin yang akan dipasang",
|
|
||||||
"admin_plugins.description": "Deskripsi",
|
|
||||||
"admin_plugins.installed": "Plugin terpasang",
|
|
||||||
"admin_plugins.installed_fetching": "Mengambil plugin yang terpasang…",
|
|
||||||
"admin_plugins.installed_nothing": "Anda belum memasang plugin apa pun.",
|
|
||||||
"admin_plugins.installed_uninstall.value": "Uninstal",
|
|
||||||
"admin_plugins.last-update": "Pembaruan terakhir",
|
|
||||||
"admin_plugins.name": "Nama",
|
|
||||||
"admin_plugins.page-title": "Manajer plugin - Etherpad",
|
|
||||||
"admin_plugins.version": "Versi",
|
"admin_plugins.version": "Versi",
|
||||||
"admin_plugins_info": "Informasi penelusuran masalah",
|
|
||||||
"admin_plugins_info.hooks": "Kait terpasang",
|
|
||||||
"admin_plugins_info.hooks_client": "Kait sisi klien",
|
|
||||||
"admin_plugins_info.hooks_server": "Kait sisi server",
|
|
||||||
"admin_plugins_info.parts": "Bagian terpasang",
|
|
||||||
"admin_plugins_info.plugins": "Plugin terpasang",
|
|
||||||
"admin_plugins_info.page-title": "Informasi plugin - Etherpad",
|
|
||||||
"admin_plugins_info.version": "Versi Etherpad",
|
|
||||||
"admin_plugins_info.version_latest": "Versi terakhir yang tersedia",
|
|
||||||
"admin_plugins_info.version_number": "Nomor versi",
|
|
||||||
"admin_settings": "Pengaturan",
|
"admin_settings": "Pengaturan",
|
||||||
"admin_settings.current": "Konfigurasi kini",
|
|
||||||
"admin_settings.current_example-devel": "Contoh templat pengaturan pengembangan",
|
|
||||||
"admin_settings.current_example-prod": "Contoh templat pengaturan produksi",
|
|
||||||
"admin_settings.current_restart.value": "Jalankan ulang Etherpad",
|
|
||||||
"admin_settings.current_save.value": "Simpan pengaturan",
|
"admin_settings.current_save.value": "Simpan pengaturan",
|
||||||
"admin_settings.page-title": "Pengaturan - Etherpad",
|
"admin_settings.page-title": "Pengaturan - Etherpad",
|
||||||
"index.newPad": "Pad baru",
|
"index.newPad": "Pad baru",
|
||||||
"index.createOpenPad": "atau buat/buka Pad dengan nama:",
|
"index.createOpenPad": "atau buat/buka Pad dengan nama:",
|
||||||
"index.openPad": "buka Pad yang ada dengan nama:",
|
|
||||||
"pad.toolbar.bold.title": "Tebal (Ctrl-B)",
|
"pad.toolbar.bold.title": "Tebal (Ctrl-B)",
|
||||||
"pad.toolbar.italic.title": "Miring (Ctrl-I)",
|
"pad.toolbar.italic.title": "Miring (Ctrl-I)",
|
||||||
"pad.toolbar.underline.title": "Garis bawah (Ctrl-U)",
|
"pad.toolbar.underline.title": "Garis bawah (Ctrl-U)",
|
||||||
|
@ -65,7 +37,7 @@
|
||||||
"pad.colorpicker.save": "Simpan",
|
"pad.colorpicker.save": "Simpan",
|
||||||
"pad.colorpicker.cancel": "Batalkan",
|
"pad.colorpicker.cancel": "Batalkan",
|
||||||
"pad.loading": "Memuat...",
|
"pad.loading": "Memuat...",
|
||||||
"pad.noCookie": "Kuki tidak dapat ditemukan. Izinkan kuki di peramban Anda! Sesi dan pengaturan Anda tidak akan disimpan antar kunjungan. Ini mungkin karena Etherpad disertakan dalam suatu iFrame dalam beberapa Peramban. Harap pastikan Etherpad ada pada sub domain/domain dengan iFrame induk",
|
"pad.noCookie": "Kuki tidak dapat ditemukan. Izinkan kuki di peramban Anda!",
|
||||||
"pad.permissionDenied": "Anda tidak memiliki izin untuk mengakses pad ini",
|
"pad.permissionDenied": "Anda tidak memiliki izin untuk mengakses pad ini",
|
||||||
"pad.settings.padSettings": "Pengaturan Pad",
|
"pad.settings.padSettings": "Pengaturan Pad",
|
||||||
"pad.settings.myView": "Tampilan Saya",
|
"pad.settings.myView": "Tampilan Saya",
|
||||||
|
@ -76,10 +48,7 @@
|
||||||
"pad.settings.rtlcheck": "Membaca dari kanan ke kiri?",
|
"pad.settings.rtlcheck": "Membaca dari kanan ke kiri?",
|
||||||
"pad.settings.fontType": "Jenis fonta:",
|
"pad.settings.fontType": "Jenis fonta:",
|
||||||
"pad.settings.language": "Bahasa:",
|
"pad.settings.language": "Bahasa:",
|
||||||
"pad.settings.deletePad": "Hapus Pad",
|
|
||||||
"pad.delete.confirm": "Apakah Anda benar-benar ingin menghapus pad ini?",
|
|
||||||
"pad.settings.about": "Tentang",
|
"pad.settings.about": "Tentang",
|
||||||
"pad.settings.poweredBy": "Ditenagai oleh",
|
|
||||||
"pad.importExport.import_export": "Impor/Ekspor",
|
"pad.importExport.import_export": "Impor/Ekspor",
|
||||||
"pad.importExport.import": "Unggah setiap berkas teks atau dokumen",
|
"pad.importExport.import": "Unggah setiap berkas teks atau dokumen",
|
||||||
"pad.importExport.importSuccessful": "Berhasil!",
|
"pad.importExport.importSuccessful": "Berhasil!",
|
||||||
|
@ -90,9 +59,9 @@
|
||||||
"pad.importExport.exportword": "Microsoft Word",
|
"pad.importExport.exportword": "Microsoft Word",
|
||||||
"pad.importExport.exportpdf": "PDF",
|
"pad.importExport.exportpdf": "PDF",
|
||||||
"pad.importExport.exportopen": "ODF (Open Document Format)",
|
"pad.importExport.exportopen": "ODF (Open Document Format)",
|
||||||
"pad.importExport.abiword.innerHTML": "Anda hanya dapat mengimpor dari format teks polos atau HTML. Untuk fitur impor yang lebih canggih, <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">pasanglah AbiWord atau LibreOffice</a>.",
|
"pad.importExport.abiword.innerHTML": "Anda hanya dapat mengimpor dari format teks biasa atau HTML. Untuk fitur impor yang lebih canggih, <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">pasanglah AbiWord</a>.",
|
||||||
"pad.modals.connected": "Tersambung.",
|
"pad.modals.connected": "Tersambung.",
|
||||||
"pad.modals.reconnecting": "Menyambungkan kembali ke pad Anda…",
|
"pad.modals.reconnecting": "Menyambungkan kembali ke pad Anda...",
|
||||||
"pad.modals.forcereconnect": "Sambung kembali secara paksa",
|
"pad.modals.forcereconnect": "Sambung kembali secara paksa",
|
||||||
"pad.modals.reconnecttimer": "Mencoba menghubungkan ulang",
|
"pad.modals.reconnecttimer": "Mencoba menghubungkan ulang",
|
||||||
"pad.modals.cancel": "Batalkan",
|
"pad.modals.cancel": "Batalkan",
|
||||||
|
@ -114,10 +83,6 @@
|
||||||
"pad.modals.corruptPad.cause": "Hal ini mungkin disebabkan oleh konfigurasi peladen salah atau sesuatu perilaku yang tidak diperkirakan. Silahkan hubungi administrator Anda jika Anda merasakan ini adalah satu kesalahan.",
|
"pad.modals.corruptPad.cause": "Hal ini mungkin disebabkan oleh konfigurasi peladen salah atau sesuatu perilaku yang tidak diperkirakan. Silahkan hubungi administrator Anda jika Anda merasakan ini adalah satu kesalahan.",
|
||||||
"pad.modals.deleted": "Dihapus",
|
"pad.modals.deleted": "Dihapus",
|
||||||
"pad.modals.deleted.explanation": "Pad ini telah dibuang.",
|
"pad.modals.deleted.explanation": "Pad ini telah dibuang.",
|
||||||
"pad.modals.rateLimited": "Laju Dibatasi.",
|
|
||||||
"pad.modals.rateLimited.explanation": "Anda mengirim terlalu banyak pesan ke pad ini sehingga itu memutus Anda.",
|
|
||||||
"pad.modals.rejected.explanation": "Server menolak suatu pesan yang dikirim oleh peramban Anda.",
|
|
||||||
"pad.modals.rejected.cause": "Server mungkin telah diperbarui ketika Anda sedang melihat pad, atau mungkin ada bug dalam Etherpad. Cobalah memuat ulang halaman.",
|
|
||||||
"pad.modals.disconnected": "Sambungan Anda telah diputuskan.",
|
"pad.modals.disconnected": "Sambungan Anda telah diputuskan.",
|
||||||
"pad.modals.disconnected.explanation": "Sambungan ke peladen terputus",
|
"pad.modals.disconnected.explanation": "Sambungan ke peladen terputus",
|
||||||
"pad.modals.disconnected.cause": "Peladen ini mungkin tidak tersedia. Silakan beritahu administrator jika masalah ini berkelanjutan.",
|
"pad.modals.disconnected.cause": "Peladen ini mungkin tidak tersedia. Silakan beritahu administrator jika masalah ini berkelanjutan.",
|
||||||
|
@ -130,7 +95,6 @@
|
||||||
"pad.chat.loadmessages": "Muatkan lebih banyak pesan",
|
"pad.chat.loadmessages": "Muatkan lebih banyak pesan",
|
||||||
"pad.chat.stick.title": "Tempelkan chat ke layar",
|
"pad.chat.stick.title": "Tempelkan chat ke layar",
|
||||||
"pad.chat.writeMessage.placeholder": "Tuliskan pesan Anda di sini",
|
"pad.chat.writeMessage.placeholder": "Tuliskan pesan Anda di sini",
|
||||||
"timeslider.followContents": "Ikuti pembaruan isi pad",
|
|
||||||
"timeslider.pageTitle": "{{appTitle}} Timeslider",
|
"timeslider.pageTitle": "{{appTitle}} Timeslider",
|
||||||
"timeslider.toolbar.returnbutton": "Kembali ke pad",
|
"timeslider.toolbar.returnbutton": "Kembali ke pad",
|
||||||
"timeslider.toolbar.authors": "Pembuat:",
|
"timeslider.toolbar.authors": "Pembuat:",
|
||||||
|
@ -160,7 +124,7 @@
|
||||||
"pad.savedrevs.timeslider": "Anda bisa melihat revisi yang tersimpan dengan mengunjungi timeslider",
|
"pad.savedrevs.timeslider": "Anda bisa melihat revisi yang tersimpan dengan mengunjungi timeslider",
|
||||||
"pad.userlist.entername": "Masukkan nama Anda",
|
"pad.userlist.entername": "Masukkan nama Anda",
|
||||||
"pad.userlist.unnamed": "tanpa nama",
|
"pad.userlist.unnamed": "tanpa nama",
|
||||||
"pad.editbar.clearcolors": "Bersihkan warna penulis pada seluruh dokumen? Ini tidak dapat dibatalkan",
|
"pad.editbar.clearcolors": "Padamkan warna penulis pada seluruh dokumen?",
|
||||||
"pad.impexp.importbutton": "Impor Sekarang",
|
"pad.impexp.importbutton": "Impor Sekarang",
|
||||||
"pad.impexp.importing": "Mengimpor...",
|
"pad.impexp.importing": "Mengimpor...",
|
||||||
"pad.impexp.confirmimport": "Mengimpor berkas akan menimpa teks saat ini di pad ini. Apakah Anda benar-benar ingin melakukannya?",
|
"pad.impexp.confirmimport": "Mengimpor berkas akan menimpa teks saat ini di pad ini. Apakah Anda benar-benar ingin melakukannya?",
|
||||||
|
@ -169,6 +133,5 @@
|
||||||
"pad.impexp.uploadFailed": "Penunggahan gagal, silakan mencoba lagi",
|
"pad.impexp.uploadFailed": "Penunggahan gagal, silakan mencoba lagi",
|
||||||
"pad.impexp.importfailed": "Impor gagal",
|
"pad.impexp.importfailed": "Impor gagal",
|
||||||
"pad.impexp.copypaste": "Silahkan salin tempel",
|
"pad.impexp.copypaste": "Silahkan salin tempel",
|
||||||
"pad.impexp.exportdisabled": "Mengekspor dalam format {{type}} dilarang. Silakan hubungi administrator untuk detilnya.",
|
"pad.impexp.exportdisabled": "Mengekspor dalam format {{type}} dilarang. Silakan hubungi administrator untuk detilnya."
|
||||||
"pad.impexp.maxFileSize": "Berkas terlalu besar. Hubungi administrator situs Anda untuk menaikkan ukuran berkas yang diizinkan untuk impor"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
"Kurousagi",
|
"Kurousagi",
|
||||||
"Revi",
|
"Revi",
|
||||||
"SeoJeongHo",
|
"SeoJeongHo",
|
||||||
"Suleiman the Magnificent Television",
|
|
||||||
"Ykhwong",
|
"Ykhwong",
|
||||||
"그냥기여자",
|
"그냥기여자",
|
||||||
"아라"
|
"아라"
|
||||||
|
@ -20,13 +19,13 @@
|
||||||
"admin_plugins.available_not-found": "플러그인이 없습니다.",
|
"admin_plugins.available_not-found": "플러그인이 없습니다.",
|
||||||
"admin_plugins.available_fetching": "검색 중...",
|
"admin_plugins.available_fetching": "검색 중...",
|
||||||
"admin_plugins.available_install.value": "설치",
|
"admin_plugins.available_install.value": "설치",
|
||||||
"admin_plugins.available_search.placeholder": "설치할 플러그인을 검색",
|
"admin_plugins.available_search.placeholder": "설치할 플러그인을 검색합니다",
|
||||||
"admin_plugins.description": "설명",
|
"admin_plugins.description": "설명",
|
||||||
"admin_plugins.installed": "설치된 플러그인",
|
"admin_plugins.installed": "설치된 플러그인",
|
||||||
"admin_plugins.installed_fetching": "설치된 플러그인을 검색하는 중...",
|
"admin_plugins.installed_fetching": "설치된 플러그인을 검색하는 중...",
|
||||||
"admin_plugins.installed_nothing": "아직 플러인을 설치하지 않으셨습니다.",
|
"admin_plugins.installed_nothing": "아직 플러인을 설치하지 않으셨습니다.",
|
||||||
"admin_plugins.installed_uninstall.value": "제거",
|
"admin_plugins.installed_uninstall.value": "설치 제거",
|
||||||
"admin_plugins.last-update": "마지막 업데이트",
|
"admin_plugins.last-update": "최근 업데이트",
|
||||||
"admin_plugins.name": "이름",
|
"admin_plugins.name": "이름",
|
||||||
"admin_plugins.page-title": "플러그인 관리자 - 이더패드",
|
"admin_plugins.page-title": "플러그인 관리자 - 이더패드",
|
||||||
"admin_plugins.version": "버전",
|
"admin_plugins.version": "버전",
|
||||||
|
@ -82,8 +81,6 @@
|
||||||
"pad.settings.fontType": "글꼴 종류:",
|
"pad.settings.fontType": "글꼴 종류:",
|
||||||
"pad.settings.fontType.normal": "보통",
|
"pad.settings.fontType.normal": "보통",
|
||||||
"pad.settings.language": "언어:",
|
"pad.settings.language": "언어:",
|
||||||
"pad.settings.deletePad": "패드 삭제",
|
|
||||||
"pad.delete.confirm": "이 패드를 삭제하시겠습니까?",
|
|
||||||
"pad.settings.about": "소개",
|
"pad.settings.about": "소개",
|
||||||
"pad.settings.poweredBy": "제공:",
|
"pad.settings.poweredBy": "제공:",
|
||||||
"pad.importExport.import_export": "가져오기/내보내기",
|
"pad.importExport.import_export": "가져오기/내보내기",
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
"admin_settings.current_example-devel": "Юлгю хазырлау джарашдырыуланы шаблону",
|
"admin_settings.current_example-devel": "Юлгю хазырлау джарашдырыуланы шаблону",
|
||||||
"admin_settings.current_example-prod": "Юлгю чыгъарыу джарашдырыуланы шаблону",
|
"admin_settings.current_example-prod": "Юлгю чыгъарыу джарашдырыуланы шаблону",
|
||||||
"admin_settings.current_restart.value": "Etherpad-ны джангыдан башлат",
|
"admin_settings.current_restart.value": "Etherpad-ны джангыдан башлат",
|
||||||
"admin_settings.current_save.value": "Джарашдырыуланы Сакъландыр",
|
"admin_settings.current_save.value": "Джарашдырыуланы Сакъла",
|
||||||
"admin_settings.page-title": "Джарашдырыула — Etherpad",
|
"admin_settings.page-title": "Джарашдырыула — Etherpad",
|
||||||
"index.newPad": "Джангы Блокнот",
|
"index.newPad": "Джангы Блокнот",
|
||||||
"index.createOpenPad": "неда бу ат бла Блокнот болдур/ач:",
|
"index.createOpenPad": "неда бу ат бла Блокнот болдур/ач:",
|
||||||
|
@ -55,10 +55,10 @@
|
||||||
"pad.toolbar.import_export.title": "Файлланы башха форматларын (а/дан) импорт/экспорт",
|
"pad.toolbar.import_export.title": "Файлланы башха форматларын (а/дан) импорт/экспорт",
|
||||||
"pad.toolbar.timeslider.title": "Заман шкала",
|
"pad.toolbar.timeslider.title": "Заман шкала",
|
||||||
"pad.toolbar.savedRevision.title": "Версияны сакъла",
|
"pad.toolbar.savedRevision.title": "Версияны сакъла",
|
||||||
"pad.toolbar.settings.title": "Джарашдырыўла",
|
"pad.toolbar.settings.title": "Джарашдырыула",
|
||||||
"pad.toolbar.embed.title": "Бу блокнотну Джай эмда Ичине сал",
|
"pad.toolbar.embed.title": "Бу блокнотну Джай эмда Ичине сал",
|
||||||
"pad.toolbar.showusers.title": "Хайырланыучуланы бу блокнотда кёргюзт",
|
"pad.toolbar.showusers.title": "Хайырланыучуланы бу блокнотда кёргюзт",
|
||||||
"pad.colorpicker.save": "Сакъландыр",
|
"pad.colorpicker.save": "Сакъла",
|
||||||
"pad.colorpicker.cancel": "Ызына ал",
|
"pad.colorpicker.cancel": "Ызына ал",
|
||||||
"pad.loading": "Джюклениу...",
|
"pad.loading": "Джюклениу...",
|
||||||
"pad.noCookie": "Куки табылмадыла. Бразуеригизде кукилени бир джандырсагъыз! Сизни кириулеригизни арасында сессиягъыз эмда джарашдырыуларыгъыз сакъланныкъ тюлдюле. Буну чуруму, бир къауум браузерледе Etherpad iFrame ичинде болгъаны болургъа болур. Тилейбиз, Etherpad эмда аны башындагъы iFrame бир тюбдоменде/доменде болгъанындан ишексиз болугъуз.",
|
"pad.noCookie": "Куки табылмадыла. Бразуеригизде кукилени бир джандырсагъыз! Сизни кириулеригизни арасында сессиягъыз эмда джарашдырыуларыгъыз сакъланныкъ тюлдюле. Буну чуруму, бир къауум браузерледе Etherpad iFrame ичинде болгъаны болургъа болур. Тилейбиз, Etherpad эмда аны башындагъы iFrame бир тюбдоменде/доменде болгъанындан ишексиз болугъуз.",
|
||||||
|
@ -90,7 +90,7 @@
|
||||||
"pad.modals.reconnecting": "Блокнотугъузгъа джангыдан байлана турады...",
|
"pad.modals.reconnecting": "Блокнотугъузгъа джангыдан байлана турады...",
|
||||||
"pad.modals.forcereconnect": "Джангыдан зор бла байланыу",
|
"pad.modals.forcereconnect": "Джангыдан зор бла байланыу",
|
||||||
"pad.modals.reconnecttimer": "Джангыдан байланыргъа кюрешеди",
|
"pad.modals.reconnecttimer": "Джангыдан байланыргъа кюрешеди",
|
||||||
"pad.modals.cancel": "Ызына ал",
|
"pad.modals.cancel": "Ызына алыу",
|
||||||
"pad.modals.userdup": "Башха терезеде ачыкъды",
|
"pad.modals.userdup": "Башха терезеде ачыкъды",
|
||||||
"pad.modals.userdup.explanation": "Бу блокнот, бу компьютерде бирден аслам бразуре терезеде ачылгъаннга ушайды.",
|
"pad.modals.userdup.explanation": "Бу блокнот, бу компьютерде бирден аслам бразуре терезеде ачылгъаннга ушайды.",
|
||||||
"pad.modals.userdup.advice": "Бу терезени хайырланыб джангыдан байлан",
|
"pad.modals.userdup.advice": "Бу терезени хайырланыб джангыдан байлан",
|
||||||
|
|
|
@ -82,7 +82,7 @@
|
||||||
"timeslider.toolbar.exportlink.title": "Exportéieren",
|
"timeslider.toolbar.exportlink.title": "Exportéieren",
|
||||||
"timeslider.exportCurrent": "Exportéiert déi aktuell Versioun als:",
|
"timeslider.exportCurrent": "Exportéiert déi aktuell Versioun als:",
|
||||||
"timeslider.version": "Versioun {{version}}",
|
"timeslider.version": "Versioun {{version}}",
|
||||||
"timeslider.saved": "Gespäichert de(n) {{day}}. {{month}} {{year}}",
|
"timeslider.saved": "Gespäichert de(n) {{day}} {{month}} {{year}}",
|
||||||
"timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
|
"timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
|
||||||
"timeslider.month.january": "Januar",
|
"timeslider.month.january": "Januar",
|
||||||
"timeslider.month.february": "Februar",
|
"timeslider.month.february": "Februar",
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
{
|
|
||||||
"@metadata": {
|
|
||||||
"authors": [
|
|
||||||
"BOKOBA VEROLY"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"admin.page-title": "Admin Dashboard - Etherpad",
|
|
||||||
"admin_plugins": "Mokambi ya plug-in",
|
|
||||||
"admin_plugins.available": "Ba plugins oyo ezali",
|
|
||||||
"admin_plugins.available_not-found": "Ba plugins ezwamaki te.",
|
|
||||||
"admin_plugins.available_fetching": "Kozwa...",
|
|
||||||
"admin_plugins.available_install.value": "Kotya",
|
|
||||||
"admin_plugins.available_search.placeholder": "Bolukiluki ya ba plugins mpo na kotya",
|
|
||||||
"admin_plugins.description": "Ndimbola",
|
|
||||||
"admin_plugins.installed": "Ba plugins oyo etyamaki",
|
|
||||||
"admin_plugins.installed_fetching": "Kozwa ba plugins oyo etyamaki...",
|
|
||||||
"admin_plugins.installed_nothing": "Otikaki naino ba plugins te.",
|
|
||||||
"admin_plugins.installed_uninstall.value": "Kofungola esika",
|
|
||||||
"admin_plugins.last-update": "Makambo ya sika ya suka",
|
|
||||||
"admin_plugins.name": "Nkombo na yango",
|
|
||||||
"admin_plugins.page-title": "Gestionnaire de greffons — Etherpad",
|
|
||||||
"admin_plugins.version": "Libongoli",
|
|
||||||
"admin_plugins_info": "Informations de résolution de problème",
|
|
||||||
"admin_plugins_info.hooks": "Crochets installés",
|
|
||||||
"admin_plugins_info.hooks_client": "Crochets côté client",
|
|
||||||
"admin_plugins_info.hooks_server": "Crochets côté serveur",
|
|
||||||
"admin_plugins_info.parts": "Biteni oyo batye",
|
|
||||||
"admin_plugins_info.plugins": "Ba plugins oyo etyamaki",
|
|
||||||
"admin_plugins_info.page-title": "Makambo etali ordinatɛrɛ - Etherpad",
|
|
||||||
"admin_plugins_info.version": "Libongoli ya Etherpad",
|
|
||||||
"admin_plugins_info.version_latest": "Libongoli ya sika",
|
|
||||||
"admin_plugins_info.version_number": "Numero ya version",
|
|
||||||
"admin_settings": "Ndenge ya kobongisa yango",
|
|
||||||
"admin_settings.current": "Configuration ya lelo",
|
|
||||||
"admin_settings.current_example-devel": "Ndakisa modèle ya paramètres ya développement",
|
|
||||||
"admin_settings.current_example-prod": "Ndakisa modèle ya paramètres ya production",
|
|
||||||
"admin_settings.current_restart.value": "Bobandi lisusu Etherpad",
|
|
||||||
"admin_settings.current_save.value": "Bomba ba Paramètres",
|
|
||||||
"admin_settings.page-title": "Paramètres - Etherpad ya kosala",
|
|
||||||
"index.newPad": "Pad ya sika",
|
|
||||||
"index.createOpenPad": "to kosala/kofungola Pad na nkombo:",
|
|
||||||
"index.openPad": "kofungola Pad oyo ezali na nkombo:",
|
|
||||||
"pad.toolbar.bold.title": "Makomi ya moindo makasi (Ctrl+B)",
|
|
||||||
"pad.toolbar.underline.title": "Mokanda ya nse (Ctrl+U)",
|
|
||||||
"pad.toolbar.strikethrough.title": "Strikethrough (Ctrl+5)",
|
|
||||||
"pad.toolbar.ol.title": "Liste oyo esɛngami (Ctrl+Shift+N)",
|
|
||||||
"pad.toolbar.ul.title": "Liste oyo etyami na molongo te (Ctrl+Shift+L)",
|
|
||||||
"pad.toolbar.indent.title": "Indent (TAB)",
|
|
||||||
"pad.toolbar.unindent.title": "Mikuwa ya libándá (Shift+TAB)",
|
|
||||||
"pad.toolbar.undo.title": "Undo (Ctrl+Z)",
|
|
||||||
"pad.toolbar.redo.title": "Redo (Ctrl+Y)",
|
|
||||||
"pad.toolbar.clearAuthorship.title": "Langi ya polele ya mokomi (Ctrl+Shift+C)",
|
|
||||||
"pad.toolbar.import_export.title": "Kokotisa/kobimisa na/na ba formats ya ba fichiers ndenge na ndenge",
|
|
||||||
"pad.toolbar.timeslider.title": "Mokambi ya ntango",
|
|
||||||
"pad.toolbar.savedRevision.title": "Kobomba lisusu",
|
|
||||||
"pad.toolbar.settings.title": "Ndenge ya kobongisa yango",
|
|
||||||
"pad.toolbar.embed.title": "Kopesa mpe kobakisa yango",
|
|
||||||
"pad.toolbar.showusers.title": "Tyá bato oyo basalelaka yango",
|
|
||||||
"pad.colorpicker.save": "Kobikisa",
|
|
||||||
"pad.colorpicker.cancel": "Kolongola"
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue