mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-20 15:36:16 -04:00
Compare commits
No commits in common. "develop" and "2.2.4" have entirely different histories.
90 changed files with 3794 additions and 6920 deletions
8
.github/workflows/backend-tests.yml
vendored
8
.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, 22]
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout repository
|
name: Checkout repository
|
||||||
|
@ -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
|
||||||
|
@ -84,7 +84,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
node: [20, 22, 23]
|
node: [18, 20, 22]
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout repository
|
name: Checkout repository
|
||||||
|
@ -113,7 +113,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
|
||||||
|
|
30
.github/workflows/docker.yml
vendored
30
.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'
|
||||||
|
@ -36,7 +33,7 @@ jobs:
|
||||||
name: Build and export to Docker
|
name: Build and export to Docker
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: ./etherpad
|
context: .
|
||||||
target: production
|
target: production
|
||||||
load: true
|
load: true
|
||||||
tags: ${{ env.TEST_TAG }}
|
tags: ${{ env.TEST_TAG }}
|
||||||
|
@ -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@v6
|
||||||
with:
|
with:
|
||||||
context: ./etherpad
|
context: .
|
||||||
target: production
|
target: production
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
|
@ -116,28 +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
|
|
||||||
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
|
|
||||||
|
|
2
.github/workflows/frontend-admin-tests.yml
vendored
2
.github/workflows/frontend-admin-tests.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
node: [20, 22, 23]
|
node: [20, 22]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
|
|
|
@ -24,7 +24,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
node: [20, 22, 23]
|
node: [18, 20, 22]
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Check out latest release
|
name: Check out latest release
|
||||||
|
@ -43,7 +43,7 @@ jobs:
|
||||||
- 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
|
||||||
|
|
43
CHANGELOG.md
43
CHANGELOG.md
|
@ -1,46 +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
|
# 2.2.4
|
||||||
|
|
||||||
### Notable enhancements and fixes
|
### Notable enhancements and fixes
|
||||||
|
|
50
Dockerfile
50
Dockerfile
|
@ -3,10 +3,9 @@
|
||||||
# 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
|
RUN npm install -g pnpm@9.0.4
|
||||||
WORKDIR /opt/etherpad-lite
|
WORKDIR /opt/etherpad-lite
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN pnpm install
|
RUN pnpm install
|
||||||
|
@ -100,7 +99,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@9.0.4 -g && \
|
||||||
apk update && apk upgrade && \
|
apk update && apk upgrade && \
|
||||||
apk add --no-cache \
|
apk add --no-cache \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
|
@ -114,49 +113,26 @@ 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/HEA[D] ./.git/HEAD
|
||||||
|
COPY --chown=etherpad:etherpad ./.git/ref[s] ./.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
|
||||||
|
|
||||||
FROM build AS build_git
|
|
||||||
ONBUILD COPY --chown=etherpad:etherpad ./.git/HEA[D] ./.git/HEAD
|
|
||||||
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 ./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/ templates/admin./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/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}" ] || [ ! -z "${ETHERPAD_GITHUB_PLUGINS}" ]; then \
|
||||||
pnpm run plugins i ${ETHERPAD_PLUGINS} ${ETHERPAD_GITHUB_PLUGINS:+--github ${ETHERPAD_GITHUB_PLUGINS}}; \
|
pnpm run plugins i ${ETHERPAD_PLUGINS} ${ETHERPAD_LOCAL_PLUGINS:+--path ${ETHERPAD_LOCAL_PLUGINS}} ${ETHERPAD_GITHUB_PLUGINS:+--github ${ETHERPAD_GITHUB_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
|
||||||
|
@ -165,13 +141,9 @@ 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/templates/admin ./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/src/static/oidc ./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}" ] || [ ! -z "${ETHERPAD_GITHUB_PLUGINS}" ]; then \
|
||||||
RUN bash -c ./bin/installLocalPlugins.sh
|
pnpm run plugins i ${ETHERPAD_PLUGINS} ${ETHERPAD_LOCAL_PLUGINS:+--path ${ETHERPAD_LOCAL_PLUGINS}} ${ETHERPAD_GITHUB_PLUGINS:+--github ${ETHERPAD_GITHUB_PLUGINS}}; \
|
||||||
|
|
||||||
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
|
fi
|
||||||
|
|
||||||
# Copy the configuration file.
|
# Copy the configuration file.
|
||||||
|
|
27
README.md
27
README.md
|
@ -90,7 +90,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:
|
||||||
|
@ -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,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "admin",
|
"name": "admin",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2.3.0",
|
"version": "2.2.4",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
@ -11,32 +11,32 @@
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-switch": "^1.1.4"
|
"@radix-ui/react-switch": "^1.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@radix-ui/react-dialog": "^1.1.7",
|
"@radix-ui/react-dialog": "^1.1.1",
|
||||||
"@radix-ui/react-toast": "^1.2.7",
|
"@radix-ui/react-toast": "^1.2.1",
|
||||||
"@types/react": "^19.1.2",
|
"@types/react": "^18.3.5",
|
||||||
"@types/react-dom": "^19.1.2",
|
"@types/react-dom": "^18.2.25",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.30.1",
|
"@typescript-eslint/eslint-plugin": "^8.4.0",
|
||||||
"@typescript-eslint/parser": "^8.30.1",
|
"@typescript-eslint/parser": "^8.4.0",
|
||||||
"@vitejs/plugin-react-swc": "^3.9.0",
|
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||||
"eslint": "^9.23.0",
|
"eslint": "^9.9.1",
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.19",
|
"eslint-plugin-react-refresh": "^0.4.11",
|
||||||
"i18next": "^25.0.0",
|
"i18next": "^23.14.0",
|
||||||
"i18next-browser-languagedetector": "^8.0.5",
|
"i18next-browser-languagedetector": "^8.0.0",
|
||||||
"lucide-react": "^0.501.0",
|
"lucide-react": "^0.439.0",
|
||||||
"react": "^19.1.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hook-form": "^7.55.0",
|
"react-hook-form": "^7.53.0",
|
||||||
"react-i18next": "^15.4.1",
|
"react-i18next": "^15.0.1",
|
||||||
"react-router-dom": "^7.5.1",
|
"react-router-dom": "^6.26.1",
|
||||||
"socket.io-client": "^4.8.1",
|
"socket.io-client": "^4.7.5",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.5.4",
|
||||||
"vite": "^6.3.2",
|
"vite": "^5.4.3",
|
||||||
"vite-plugin-static-copy": "^2.3.1",
|
"vite-plugin-static-copy": "^1.0.6",
|
||||||
"vite-plugin-svgr": "^4.3.0",
|
"vite-plugin-svgr": "^4.2.0",
|
||||||
"zustand": "^5.0.3"
|
"zustand": "^4.5.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,23 +6,22 @@ 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, PhoneCall} 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')
|
||||||
})
|
})
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -76,7 +75,7 @@ export const App = () => {
|
||||||
useStore.getState().setShowLoading(false);
|
useStore.getState().setShowLoading(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
settingSocket.on('saveprogress', (status) => {
|
settingSocket.on('saveprogress', (status)=>{
|
||||||
console.log(status)
|
console.log(status)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -86,7 +85,7 @@ export const App = () => {
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
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">
|
||||||
|
@ -94,11 +93,7 @@ export const App = () => {
|
||||||
<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) {
|
|
||||||
setSidebarOpen(false)
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
<li><NavLink to="/plugins"><Cable/><Trans i18nKey="admin_plugins"/></NavLink></li>
|
<li><NavLink to="/plugins"><Cable/><Trans i18nKey="admin_plugins"/></NavLink></li>
|
||||||
<li><NavLink to={"/settings"}><Wrench/><Trans i18nKey="admin_settings"/></NavLink></li>
|
<li><NavLink to={"/settings"}><Wrench/><Trans i18nKey="admin_settings"/></NavLink></li>
|
||||||
<li><NavLink to={"/help"}> <Construction/> <Trans i18nKey="admin_plugins_info"/></NavLink></li>
|
<li><NavLink to={"/help"}> <Construction/> <Trans i18nKey="admin_plugins_info"/></NavLink></li>
|
||||||
|
@ -108,9 +103,6 @@ export const App = () => {
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button id="icon-button" onClick={() => {
|
|
||||||
setSidebarOpen(!sidebarOpen)
|
|
||||||
}}><LucideMenu/></button>
|
|
||||||
<div className="innerwrapper">
|
<div className="innerwrapper">
|
||||||
<Outlet/>
|
<Outlet/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
--etherpad-color: #0f775b;
|
--etherpad-color: #0f775b;
|
||||||
--etherpad-comp: #9C8840;
|
--etherpad-comp: #9C8840;
|
||||||
--etherpad-light: #99FF99;
|
--etherpad-light: #99FF99;
|
||||||
--sidebar-width: 20em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
|
@ -29,21 +28,17 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
div.menu {
|
div.menu {
|
||||||
left: 0;
|
|
||||||
transition: left .3s;
|
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: var(--sidebar-width);
|
max-width: 20%;
|
||||||
z-index: 99;
|
min-width: 20%;
|
||||||
position: fixed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-button{
|
||||||
.icon-button {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
background-color: var(--etherpad-color);
|
background-color: var(--etherpad-color);
|
||||||
|
@ -113,7 +108,7 @@ div.menu li {
|
||||||
|
|
||||||
|
|
||||||
div.menu li:has(.active) {
|
div.menu li:has(.active) {
|
||||||
background-color: #9C885C;
|
background-color: #9C885C ;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.menu li a {
|
div.menu li a {
|
||||||
|
@ -121,15 +116,13 @@ div.menu li a {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
div.innerwrapper {
|
div.innerwrapper {
|
||||||
transition: margin-left .3s;
|
|
||||||
isolation: isolate;
|
|
||||||
background-color: #F0F0F0;
|
background-color: #F0F0F0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
flex-grow: 100;
|
flex-grow: 100;
|
||||||
margin-left: var(--sidebar-width);
|
padding: 20px;
|
||||||
padding: 20px 20px 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div.innerwrapper-err {
|
div.innerwrapper-err {
|
||||||
|
@ -137,9 +130,11 @@ div.innerwrapper-err {
|
||||||
}
|
}
|
||||||
|
|
||||||
#wrapper {
|
#wrapper {
|
||||||
|
display: flex;
|
||||||
background: none repeat scroll 0px 0px #FFFFFF;
|
background: none repeat scroll 0px 0px #FFFFFF;
|
||||||
box-shadow: 0px 1px 10px rgba(0, 0, 0, 0.2);
|
box-shadow: 0px 1px 10px rgba(0, 0, 0, 0.2);
|
||||||
min-height: 100%; /*always display a scrollbar*/
|
min-height: 100%;/*always display a scrollbar*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
|
@ -178,17 +173,14 @@ input {
|
||||||
.sort {
|
.sort {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sort:after {
|
.sort:after {
|
||||||
content: '▲▼'
|
content: '▲▼'
|
||||||
}
|
}
|
||||||
|
|
||||||
.sort.up:after {
|
.sort.up:after {
|
||||||
content: '▲'
|
content:'▲'
|
||||||
}
|
}
|
||||||
|
|
||||||
.sort.down:after {
|
.sort.down:after {
|
||||||
content: '▼'
|
content:'▼'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -204,14 +196,11 @@ table {
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-container {
|
|
||||||
width: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
max-height: 90vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#available-plugins th:first-child, #available-plugins th:nth-child(2) {
|
|
||||||
|
|
||||||
|
#available-plugins th:first-child, #available-plugins th:nth-child(2){
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,40 +212,35 @@ td, th {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#installed-plugins td > div {
|
#installed-plugins td>div {
|
||||||
position: relative; /* Allows us to position the loading indicator relative to this row */
|
position: relative;/* Allows us to position the loading indicator relative to this row */
|
||||||
display: inline-block; /*make this fill the whole cell*/
|
display: inline-block; /*make this fill the whole cell*/
|
||||||
width: 100%;
|
width:100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messages {
|
.messages {
|
||||||
height: 5em;
|
height: 5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messages * {
|
.messages * {
|
||||||
display: none;
|
display: none;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messages .fetching {
|
.messages .fetching {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress {
|
.progress {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0; left: 0; bottom:0; right:0;
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
padding: auto;
|
padding: auto;
|
||||||
|
|
||||||
background: rgb(255, 255, 255);
|
background: rgb(255,255,255);
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#search-progress.progress {
|
#search-progress.progress {
|
||||||
padding-top: 20%;
|
padding-top: 20%;
|
||||||
background: rgba(255, 255, 255, 0.3);
|
background: rgba(255,255,255,0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress * {
|
.progress * {
|
||||||
|
@ -318,45 +302,17 @@ pre {
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#icon-button {
|
|
||||||
color: var(--etherpad-color);
|
|
||||||
top: 10px;
|
|
||||||
background-color: transparent;
|
|
||||||
border: none;
|
|
||||||
z-index: 99;
|
|
||||||
position: absolute;
|
|
||||||
left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.inner-menu span:nth-child(2) {
|
|
||||||
display: flex;
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#wrapper.closed .menu {
|
|
||||||
left: calc(-1 * var(--sidebar-width));
|
|
||||||
}
|
|
||||||
|
|
||||||
#wrapper.closed .innerwrapper {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 800px) {
|
@media (max-width: 800px) {
|
||||||
|
|
||||||
div.innerwrapper {
|
div.innerwrapper {
|
||||||
margin-left: 0;
|
padding: 0 15px 15px 15px;
|
||||||
}
|
|
||||||
|
|
||||||
.inner-menu {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div.menu {
|
div.menu {
|
||||||
|
padding: 1px 15px 0 15px;
|
||||||
|
position: static;
|
||||||
height: auto;
|
height: auto;
|
||||||
border-right: none;
|
border-right: none;
|
||||||
--sidebar-width: 100%;
|
width: auto;
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,7 +376,6 @@ pre {
|
||||||
|
|
||||||
.login-background {
|
.login-background {
|
||||||
background-image: url("/fond.jpg");
|
background-image: url("/fond.jpg");
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -434,11 +389,12 @@ pre {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-inner-box [type=submit] {
|
.login-inner-box [type=submit]{
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.login-textinput {
|
.login-textinput {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
@ -449,20 +405,14 @@ pre {
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-box {
|
.login-box {
|
||||||
|
width: 20%;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 40px;
|
border-radius: 40px;
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-inner-box{
|
||||||
@media (max-width: 900px) {
|
|
||||||
.login-box {
|
|
||||||
width: 90%
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-inner-box {
|
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
@ -530,6 +480,7 @@ pre {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.ToastViewport {
|
.ToastViewport {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
|
@ -567,24 +518,19 @@ pre {
|
||||||
column-gap: 15px;
|
column-gap: 15px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ToastRoot[data-state='open'] {
|
.ToastRoot[data-state='open'] {
|
||||||
animation: slideIn 150ms cubic-bezier(0.16, 1, 0.3, 1);
|
animation: slideIn 150ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ToastRoot[data-state='closed'] {
|
.ToastRoot[data-state='closed'] {
|
||||||
animation: hide 100ms ease-in;
|
animation: hide 100ms ease-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ToastRoot[data-swipe='move'] {
|
.ToastRoot[data-swipe='move'] {
|
||||||
transform: translateX(var(--radix-toast-swipe-move-x));
|
transform: translateX(var(--radix-toast-swipe-move-x));
|
||||||
}
|
}
|
||||||
|
|
||||||
.ToastRoot[data-swipe='cancel'] {
|
.ToastRoot[data-swipe='cancel'] {
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
transition: transform 200ms ease-out;
|
transition: transform 200ms ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ToastRoot[data-swipe='end'] {
|
.ToastRoot[data-swipe='end'] {
|
||||||
animation: swipeOut 100ms ease-out;
|
animation: swipeOut 100ms ease-out;
|
||||||
}
|
}
|
||||||
|
@ -651,7 +597,7 @@ pre {
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
height: 2.5rem;
|
height: 2.5rem;
|
||||||
width: 100%;
|
width: 100vh;
|
||||||
padding: 5px 5px 5px 30px;
|
padding: 5px 5px 5px 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -738,7 +684,7 @@ table tbody tr.active-row {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.pad-pagination {
|
.pad-pagination{
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
@ -765,7 +711,7 @@ table tbody tr.active-row {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pad-pagination > span {
|
.pad-pagination >span {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -819,11 +765,9 @@ input, button, select, optgroup, textarea {
|
||||||
box-shadow: 0 2px 10px var(--black-a7);
|
box-shadow: 0 2px 10px var(--black-a7);
|
||||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.SwitchRoot:focus {
|
.SwitchRoot:focus {
|
||||||
box-shadow: 0 0 0 2px black;
|
box-shadow: 0 0 0 2px black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.SwitchRoot[data-state='checked'] {
|
.SwitchRoot[data-state='checked'] {
|
||||||
background-color: var(--etherpad-color);
|
background-color: var(--etherpad-color);
|
||||||
}
|
}
|
||||||
|
@ -839,7 +783,6 @@ input, button, select, optgroup, textarea {
|
||||||
transform: translateX(2px);
|
transform: translateX(2px);
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
.SwitchThumb[data-state='checked'] {
|
.SwitchThumb[data-state='checked'] {
|
||||||
transform: translateX(25px);
|
transform: translateX(25px);
|
||||||
}
|
}
|
||||||
|
@ -860,7 +803,7 @@ input, button, select, optgroup, textarea {
|
||||||
color: white
|
color: white
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-pads {
|
.search-pads{
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ 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";
|
import {determineSorting} from "../utils/sorting.ts";
|
||||||
|
|
||||||
|
@ -12,8 +12,7 @@ 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>({
|
const [searchParams, setSearchParams] = useState<SearchParams>({
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 99999,
|
limit: 99999,
|
||||||
|
@ -50,7 +49,7 @@ export const HomePage = () => {
|
||||||
}, [plugins, searchParams])
|
}, [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
|
||||||
|
@ -79,16 +78,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 => {
|
||||||
|
if (plugin.name === pluginName) {
|
||||||
return {
|
return {
|
||||||
...plugin,
|
...plugin,
|
||||||
updatable: true
|
updatable: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return plugin
|
return plugin
|
||||||
|
}))
|
||||||
})
|
})
|
||||||
setInstalledPlugins(newInstalledPlugins)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
pluginsSocket.on('finished:install', () => {
|
pluginsSocket.on('finished:install', () => {
|
||||||
|
@ -159,7 +159,6 @@ export const HomePage = () => {
|
||||||
})
|
})
|
||||||
}, 500, [searchTerm])
|
}, 500, [searchTerm])
|
||||||
|
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<h1><Trans i18nKey="admin_plugins"/></h1>
|
<h1><Trans i18nKey="admin_plugins"/></h1>
|
||||||
|
|
||||||
|
@ -181,7 +180,7 @@ export const HomePage = () => {
|
||||||
<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>
|
||||||
|
@ -194,7 +193,6 @@ export const HomePage = () => {
|
||||||
<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>
|
||||||
|
@ -243,5 +241,4 @@ export const HomePage = () => {
|
||||||
</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,7 +23,6 @@ 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){
|
||||||
|
@ -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,21 +100,6 @@ 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>
|
||||||
|
@ -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>
|
||||||
|
|
|
@ -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,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
|
|
|
@ -57,7 +57,7 @@ createDirIfNotExists('../out/doc/api')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
exec(`asciidoctor -D ../out/doc ../doc/index.adoc ../*/**.adoc -a VERSION=${VERSION}`)
|
exec(`asciidoctor -D ../out/doc ../doc/index.adoc */**.adoc -a VERSION=${VERSION}`)
|
||||||
exec(`asciidoctor -D ../out/doc/api ../doc/api/*.adoc -a VERSION=${VERSION}`)
|
exec(`asciidoctor -D ../out/doc/api ../doc/api/*.adoc -a VERSION=${VERSION}`)
|
||||||
|
|
||||||
copyFolderSync('../doc/public/', '../out/doc/')
|
copyFolderSync('../doc/public/', '../out/doc/')
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
{
|
{
|
||||||
"name": "bin",
|
"name": "bin",
|
||||||
"version": "2.3.0",
|
"version": "2.2.4",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "checkAllPads.js",
|
"main": "checkAllPads.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
"doc": "doc"
|
"doc": "doc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.8.4",
|
"axios": "^1.7.7",
|
||||||
"ep_etherpad-lite": "workspace:../src",
|
"ep_etherpad-lite": "workspace:../src",
|
||||||
"log4js": "^6.9.1",
|
"log4js": "^6.9.1",
|
||||||
"semver": "^7.7.1",
|
"semver": "^7.6.3",
|
||||||
"tsx": "^4.19.3",
|
"tsx": "^4.19.0",
|
||||||
"ueberdb2": "^5.0.6"
|
"ueberdb2": "^4.2.103"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^22.5.4",
|
||||||
"@types/semver": "^7.7.0",
|
"@types/semver": "^7.5.8",
|
||||||
"typescript": "^5.8.2"
|
"typescript": "^5.5.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"makeDocs": "node --import tsx make_docs.ts",
|
"makeDocs": "node --import tsx make_docs.ts",
|
||||||
|
|
|
@ -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,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vitepress": "^1.6.3"
|
"vitepress": "^1.3.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docs:dev": "vitepress dev",
|
"docs:dev": "vitepress dev",
|
||||||
|
|
|
@ -58,7 +58,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:
|
|
@ -42,7 +42,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:
|
||||||
|
|
3
local_plugins/.gitignore
vendored
3
local_plugins/.gitignore
vendored
|
@ -1,3 +0,0 @@
|
||||||
# ignore everything
|
|
||||||
*
|
|
||||||
!.gitignore
|
|
|
@ -50,6 +50,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.2.4",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
}
|
}
|
||||||
|
|
6707
pnpm-lock.yaml
generated
6707
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,14 +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 authentication method used by the server.
|
||||||
The default value is sso
|
The default value is sso
|
||||||
|
@ -202,15 +194,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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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?
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -82,12 +82,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": "تم فقدان الاتصال بالخادم",
|
||||||
|
|
|
@ -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,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",
|
||||||
|
|
||||||
|
|
|
@ -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,7 +1,6 @@
|
||||||
{
|
{
|
||||||
"@metadata": {
|
"@metadata": {
|
||||||
"authors": [
|
"authors": [
|
||||||
"Akmaie Ajam",
|
|
||||||
"Atriwidada",
|
"Atriwidada",
|
||||||
"Bennylin",
|
"Bennylin",
|
||||||
"IvanLanin",
|
"IvanLanin",
|
||||||
|
@ -20,7 +19,6 @@
|
||||||
"admin_plugins.installed": "Plugin terpasang",
|
"admin_plugins.installed": "Plugin terpasang",
|
||||||
"admin_plugins.installed_fetching": "Mengambil plugin yang terpasang…",
|
"admin_plugins.installed_fetching": "Mengambil plugin yang terpasang…",
|
||||||
"admin_plugins.installed_nothing": "Anda belum memasang plugin apa pun.",
|
"admin_plugins.installed_nothing": "Anda belum memasang plugin apa pun.",
|
||||||
"admin_plugins.installed_uninstall.value": "Uninstal",
|
|
||||||
"admin_plugins.last-update": "Pembaruan terakhir",
|
"admin_plugins.last-update": "Pembaruan terakhir",
|
||||||
"admin_plugins.name": "Nama",
|
"admin_plugins.name": "Nama",
|
||||||
"admin_plugins.page-title": "Manajer plugin - Etherpad",
|
"admin_plugins.page-title": "Manajer plugin - Etherpad",
|
||||||
|
@ -76,8 +74,6 @@
|
||||||
"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.settings.poweredBy": "Ditenagai oleh",
|
||||||
"pad.importExport.import_export": "Impor/Ekspor",
|
"pad.importExport.import_export": "Impor/Ekspor",
|
||||||
|
|
|
@ -82,8 +82,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": "가져오기/내보내기",
|
||||||
|
|
|
@ -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"
|
|
||||||
}
|
|
|
@ -63,7 +63,7 @@
|
||||||
"pad.colorpicker.save": "Išsaugoti",
|
"pad.colorpicker.save": "Išsaugoti",
|
||||||
"pad.colorpicker.cancel": "Atšaukti",
|
"pad.colorpicker.cancel": "Atšaukti",
|
||||||
"pad.loading": "Įkraunama...",
|
"pad.loading": "Įkraunama...",
|
||||||
"pad.noCookie": "Slapuko rasti nepavyko. Prašome leisti slapukus savo naršyklėje! Jūsų sesija ir nustatymai nebus išsaugoti tarp apsilankymų. Taip gali būti dėl to, kad kai kuriose naršyklėse Etherpad yra įtrauktas į iFrame. Įsitikinkite, kad Etherpad yra tame pačiame padomenyje / domene kaip ir pirminis iFrame.",
|
"pad.noCookie": "Slapuko nepavyko rasti. Prašome leisti slapukus interneto naršyklėje!",
|
||||||
"pad.permissionDenied": "Jūs neturite leidimo patekti į šį bloknotą",
|
"pad.permissionDenied": "Jūs neturite leidimo patekti į šį bloknotą",
|
||||||
"pad.settings.padSettings": "Bloknoto nustatymai",
|
"pad.settings.padSettings": "Bloknoto nustatymai",
|
||||||
"pad.settings.myView": "Mano Vaizdas",
|
"pad.settings.myView": "Mano Vaizdas",
|
||||||
|
@ -75,8 +75,6 @@
|
||||||
"pad.settings.fontType": "Šrifto tipas:",
|
"pad.settings.fontType": "Šrifto tipas:",
|
||||||
"pad.settings.fontType.normal": "Normalus",
|
"pad.settings.fontType.normal": "Normalus",
|
||||||
"pad.settings.language": "Kalba:",
|
"pad.settings.language": "Kalba:",
|
||||||
"pad.settings.deletePad": "Ištrinti bloką",
|
|
||||||
"pad.delete.confirm": "Ar tikrai norite ištrinti šį bloką?",
|
|
||||||
"pad.settings.about": "Apie",
|
"pad.settings.about": "Apie",
|
||||||
"pad.settings.poweredBy": "Palaiko",
|
"pad.settings.poweredBy": "Palaiko",
|
||||||
"pad.importExport.import_export": "Importuoti/Eksportuoti",
|
"pad.importExport.import_export": "Importuoti/Eksportuoti",
|
||||||
|
|
|
@ -74,8 +74,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": "Увоз/Извоз",
|
||||||
|
|
|
@ -85,8 +85,6 @@
|
||||||
"pad.settings.fontType": "Lettertype:",
|
"pad.settings.fontType": "Lettertype:",
|
||||||
"pad.settings.fontType.normal": "Normaal",
|
"pad.settings.fontType.normal": "Normaal",
|
||||||
"pad.settings.language": "Taal:",
|
"pad.settings.language": "Taal:",
|
||||||
"pad.settings.deletePad": "Pad verwijderen",
|
|
||||||
"pad.delete.confirm": "Wilt u dit pad echt verwijderen?",
|
|
||||||
"pad.settings.about": "Over",
|
"pad.settings.about": "Over",
|
||||||
"pad.settings.poweredBy": "Aangedreven door",
|
"pad.settings.poweredBy": "Aangedreven door",
|
||||||
"pad.importExport.import_export": "Importeren/exporteren",
|
"pad.importExport.import_export": "Importeren/exporteren",
|
||||||
|
|
|
@ -9,16 +9,6 @@
|
||||||
"ਪ੍ਰਚਾਰਕ"
|
"ਪ੍ਰਚਾਰਕ"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"admin_plugins.available_fetching": "ਲਿਆਉਣਾ ਪਿਆਂ...",
|
|
||||||
"admin_plugins.available_install.value": "ਜੜੋ",
|
|
||||||
"admin_plugins.description": "ਵੇਰਵਾ",
|
|
||||||
"admin_plugins.last-update": "ਆਖਰੀ ਵਾਰ ਨਵਿਆਈਆ ਗਿਆ",
|
|
||||||
"admin_plugins.name": "ਨਾਂ",
|
|
||||||
"admin_plugins_info": "ਸਮੱਸਿਆ ਨਿਵਾਰਣ ਜਾਣਕਾਰੀ",
|
|
||||||
"admin_settings": "ਤਰਜੀਹਾਂ",
|
|
||||||
"admin_settings.current": "ਮੌਜੂਦਾ ਬਣਤਰ",
|
|
||||||
"admin_settings.current_save.value": "ਤਰਜੀਹਾਂ ਸੰਭਾਲੋ",
|
|
||||||
"admin_settings.page-title": "ਤਰਜੀਹਾਂ - ਈਥਰਪੈਡ",
|
|
||||||
"index.newPad": "ਨਵਾਂ ਪੈਡ",
|
"index.newPad": "ਨਵਾਂ ਪੈਡ",
|
||||||
"index.createOpenPad": "ਜਾਂ ਨਾਂ ਨਾਲ ਨਵਾਂ ਪੈਡ ਬਣਾਓ/ਖੋਲ੍ਹੋ:",
|
"index.createOpenPad": "ਜਾਂ ਨਾਂ ਨਾਲ ਨਵਾਂ ਪੈਡ ਬਣਾਓ/ਖੋਲ੍ਹੋ:",
|
||||||
"pad.toolbar.bold.title": "ਗੂੜ੍ਹਾ (Ctrl-B)",
|
"pad.toolbar.bold.title": "ਗੂੜ੍ਹਾ (Ctrl-B)",
|
||||||
|
@ -35,7 +25,7 @@
|
||||||
"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": "ਸੰਭਾਲੋ",
|
||||||
|
@ -50,11 +40,10 @@
|
||||||
"pad.settings.colorcheck": "ਲੇਖਕੀ ਰੰਗ",
|
"pad.settings.colorcheck": "ਲੇਖਕੀ ਰੰਗ",
|
||||||
"pad.settings.linenocheck": "ਲਕੀਰ ਨੰਬਰ",
|
"pad.settings.linenocheck": "ਲਕੀਰ ਨੰਬਰ",
|
||||||
"pad.settings.rtlcheck": "ਸਮੱਗਰੀ ਸੱਜੇ ਤੋਂ ਖੱਬੇ ਪੜ੍ਹਨੀ ਹੈ?",
|
"pad.settings.rtlcheck": "ਸਮੱਗਰੀ ਸੱਜੇ ਤੋਂ ਖੱਬੇ ਪੜ੍ਹਨੀ ਹੈ?",
|
||||||
"pad.settings.fontType": "ਅੱਖਰ ਦੀ ਕਿਸਮ:",
|
"pad.settings.fontType": "ਫੋਂਟ ਕਿਸਮ:",
|
||||||
"pad.settings.fontType.normal": "ਆਮ",
|
"pad.settings.fontType.normal": "ਸਧਾਰਨ",
|
||||||
"pad.settings.language": "ਭਾਸ਼ਾ:",
|
"pad.settings.language": "ਭਾਸ਼ਾ:",
|
||||||
"pad.settings.about": "ਬਾਬਤ",
|
"pad.importExport.import_export": "ਇੰਪੋਰਟ/ਐਕਸਪੋਰਟ",
|
||||||
"pad.importExport.import_export": "ਦਰਾਮਦ/ਬਰਾਮਦ",
|
|
||||||
"pad.importExport.import": "ਕੋਈ ਵੀ ਟੈਕਸਟ ਫਾਇਲ ਜਾਂ ਦਸਤਾਵੇਜ਼ ਅੱਪਲੋਡ ਕਰੋ",
|
"pad.importExport.import": "ਕੋਈ ਵੀ ਟੈਕਸਟ ਫਾਇਲ ਜਾਂ ਦਸਤਾਵੇਜ਼ ਅੱਪਲੋਡ ਕਰੋ",
|
||||||
"pad.importExport.importSuccessful": "ਸਫ਼ਲ!",
|
"pad.importExport.importSuccessful": "ਸਫ਼ਲ!",
|
||||||
"pad.importExport.export": "ਮੌਜੂਦਾ ਪੈਡ ਨੂੰ ਐਕਸਪੋਰਟ ਕਰੋ:",
|
"pad.importExport.export": "ਮੌਜੂਦਾ ਪੈਡ ਨੂੰ ਐਕਸਪੋਰਟ ਕਰੋ:",
|
||||||
|
@ -68,7 +57,6 @@
|
||||||
"pad.modals.connected": "ਕੁਨੈਕਟ ਹੈ।",
|
"pad.modals.connected": "ਕੁਨੈਕਟ ਹੈ।",
|
||||||
"pad.modals.reconnecting": "..ਤੁਹਾਡੇ ਪੈਡ ਨਾਲ ਮੁੜ-ਕੁਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ",
|
"pad.modals.reconnecting": "..ਤੁਹਾਡੇ ਪੈਡ ਨਾਲ ਮੁੜ-ਕੁਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ",
|
||||||
"pad.modals.forcereconnect": "ਧੱਕੇ ਨਾਲ ਮੁੜ-ਕੁਨੈਕਟ ਕਰੋ",
|
"pad.modals.forcereconnect": "ਧੱਕੇ ਨਾਲ ਮੁੜ-ਕੁਨੈਕਟ ਕਰੋ",
|
||||||
"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": "ਸਗੋਂ ਇਹ ਬਾਰੀ ਵਰਤਣ ਵਾਸਤੇ ਮੁੜ ਜੁੜੋ।",
|
||||||
|
@ -92,12 +80,11 @@
|
||||||
"pad.modals.disconnected.cause": "ਸਰਵਰ ਨਾਮੌਜੂਦ ਹੋ ਸਕਦਾ ਹੈ। ਜੇਕਰ ਇਹ ਹੁੰਦਾ ਰਹੇ ਤਾਂ ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਸੇਵਾ ਪ੍ਰਬੰਧਕ ਨੂੰ ਖ਼ਬਰ ਕਰੋ।",
|
"pad.modals.disconnected.cause": "ਸਰਵਰ ਨਾਮੌਜੂਦ ਹੋ ਸਕਦਾ ਹੈ। ਜੇਕਰ ਇਹ ਹੁੰਦਾ ਰਹੇ ਤਾਂ ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਸੇਵਾ ਪ੍ਰਬੰਧਕ ਨੂੰ ਖ਼ਬਰ ਕਰੋ।",
|
||||||
"pad.share": "ਇਹ ਪੈਡ ਸਾਂਝਾ ਕਰੋ",
|
"pad.share": "ਇਹ ਪੈਡ ਸਾਂਝਾ ਕਰੋ",
|
||||||
"pad.share.readonly": "ਕੇਵਲ ਪੜ੍ਹਨ ਲਈ",
|
"pad.share.readonly": "ਕੇਵਲ ਪੜ੍ਹਨ ਲਈ",
|
||||||
"pad.share.link": "ਕੜੀ",
|
"pad.share.link": "ਲਿੰਕ",
|
||||||
"pad.share.emebdcode": "ਇੰਬੈੱਡ URL",
|
"pad.share.emebdcode": "ਇੰਬੈੱਡ URL",
|
||||||
"pad.chat": "ਗੱਲਬਾਤ",
|
"pad.chat": "ਗੱਲਬਾਤ",
|
||||||
"pad.chat.title": "ਇਹ ਪੈਡ ਲਈ ਗੱਲਬਾਤ ਖੋਲ੍ਹੋ।",
|
"pad.chat.title": "ਇਹ ਪੈਡ ਲਈ ਗੱਲਬਾਤ ਖੋਲ੍ਹੋ।",
|
||||||
"pad.chat.loadmessages": "ਹੋਰ ਸੁਨੇਹੇ ਲੱਦੋ",
|
"pad.chat.loadmessages": "ਹੋਰ ਸੁਨੇਹੇ ਲੋਡ ਕਰੋ",
|
||||||
"pad.chat.writeMessage.placeholder": "ਆਪਣਾ ਸੁਨੇਹਾ ਇੱਥੇ ਲਿਖੋ",
|
|
||||||
"timeslider.pageTitle": "{{appTitle}} ਸਮਾਂ-ਲਕੀਰ",
|
"timeslider.pageTitle": "{{appTitle}} ਸਮਾਂ-ਲਕੀਰ",
|
||||||
"timeslider.toolbar.returnbutton": "ਪੈਡ ਉੱਤੇ ਵਾਪਸ",
|
"timeslider.toolbar.returnbutton": "ਪੈਡ ਉੱਤੇ ਵਾਪਸ",
|
||||||
"timeslider.toolbar.authors": "ਲੇਖਕ:",
|
"timeslider.toolbar.authors": "ਲੇਖਕ:",
|
||||||
|
@ -123,18 +110,18 @@
|
||||||
"timeslider.month.november": "ਨਵੰਬਰ",
|
"timeslider.month.november": "ਨਵੰਬਰ",
|
||||||
"timeslider.month.december": "ਦਸੰਬਰ",
|
"timeslider.month.december": "ਦਸੰਬਰ",
|
||||||
"timeslider.unnamedauthors": "{{num}} ਬੇਨਾਮ {[plural(num) one: ਲੇਖਕ, other: ਲੇਖਕ ]}",
|
"timeslider.unnamedauthors": "{{num}} ਬੇਨਾਮ {[plural(num) one: ਲੇਖਕ, other: ਲੇਖਕ ]}",
|
||||||
"pad.savedrevs.marked": "ਇਹ ਦੁਹਰਾਅ ਨੂੰ ਹੁਣ ਸੰਭਾਲੇ ਹੋਏ ਦੁਹਰਾਅ ਵਜੋਂ ਮੰਨਿਆ ਗਿਆ ਹੈ",
|
"pad.savedrevs.marked": "ਇਹ ਰੀਵਿਜ਼ਨ ਨੂੰ ਹੁਣ ਸੰਭਾਲੇ ਹੋਏ ਰੀਵਿਜ਼ਨ ਵਜੋਂ ਮੰਨਿਆ ਗਿਆ ਹੈ",
|
||||||
"pad.savedrevs.timeslider": "ਤੁਸੀੰ ਸਾੰਭੀਆੰ ਹੋਈਆੰ ਵਰਜਨਾੰ ਸਮਾੰਸਲਾਈਡਰ ਤੇ ਜਾ ਕੇ ਵੇਖ ਸਕਦੇ ਹੋ",
|
"pad.savedrevs.timeslider": "ਤੁਸੀੰ ਸਾੰਭੀਆੰ ਹੋਈਆੰ ਵਰਜਨਾੰ ਸਮਾੰਸਲਾਈਡਰ ਤੇ ਜਾ ਕੇ ਵੇਖ ਸਕਦੇ ਹੋ",
|
||||||
"pad.userlist.entername": "ਆਪਣਾ ਨਾਂ ਦਿਉ",
|
"pad.userlist.entername": "ਆਪਣਾ ਨਾਂ ਦਿਉ",
|
||||||
"pad.userlist.unnamed": "ਬੇਨਾਮ",
|
"pad.userlist.unnamed": "ਬੇਨਾਮ",
|
||||||
"pad.editbar.clearcolors": "ਪੂਰੇ ਦਸਾਤਵੇਜ਼ ਉੱਤੇ ਪਰਮਾਣਕਿਤਾ ਰੰਗ ਸਾਫ਼ ਕਰਨੇ ਹਨ?",
|
"pad.editbar.clearcolors": "ਪੂਰੇ ਦਸਾਤਵੇਜ਼ ਉੱਤੇ ਪਰਮਾਣਕਿਤਾ ਰੰਗ ਸਾਫ਼ ਕਰਨੇ ਹਨ?",
|
||||||
"pad.impexp.importbutton": "ਹੁਣੇ ਦਰਾਮਦ ਕਰੋ",
|
"pad.impexp.importbutton": "ਹੁਣੇ ਇੰਪੋਰਟ ਕਰੋ",
|
||||||
"pad.impexp.importing": "...ਇੰਪੋਰਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ",
|
"pad.impexp.importing": "...ਇੰਪੋਰਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ",
|
||||||
"pad.impexp.confirmimport": "ਕੋਈ ਫ਼ਾਈਲ ਦਰਾਮਦ ਕਾਰਨ ਨਾਲ਼ ਪੈਡ ਦੀ ਮੌਜੂਦਾ ਲਿਖਤ ਉੱਤੇ ਲਿਖਿਆ ਜਾਵੇਗਾ। ਕੀ ਤੁਸੀਂ ਸੱਚੀਂ ਇਹ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?",
|
"pad.impexp.confirmimport": "ਕੋਈ ਫ਼ਾਈਲ ਦਰਾਮਦ ਕਾਰਨ ਨਾਲ਼ ਪੈਡ ਦੀ ਮੌਜੂਦਾ ਲਿਖਤ ਉੱਤੇ ਲਿਖਿਆ ਜਾਵੇਗਾ। ਕੀ ਤੁਸੀਂ ਸੱਚੀਂ ਇਹ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?",
|
||||||
"pad.impexp.convertFailed": "ਅਸੀਂ ਇਸ ਫ਼ਾਈਲ ਦੀ ਦਰਾਮਦ ਨਹੀਂ ਕਰ ਸਕੇ। ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਕੋਈ ਵੱਖਰੀ ਦਸਤਾਵੇਜ਼ੀ ਰੂਪ-ਰੇਖਾ ਵਰਤੋ ਜਾਂ ਹੱਥੀਂ ਨਕਲ-ਚੇਪੀ ਕਰੋ।",
|
"pad.impexp.convertFailed": "ਅਸੀਂ ਇਸ ਫ਼ਾਈਲ ਦੀ ਦਰਾਮਦ ਨਹੀਂ ਕਰ ਸਕੇ। ਮਿਹਰਬਾਨੀ ਕਰਕੇ ਕੋਈ ਵੱਖਰੀ ਦਸਤਾਵੇਜ਼ੀ ਰੂਪ-ਰੇਖਾ ਵਰਤੋ ਜਾਂ ਹੱਥੀਂ ਨਕਲ-ਚੇਪੀ ਕਰੋ।",
|
||||||
"pad.impexp.padHasData": "ਅਸੀ ਇਸ ਫਾਈਲ ਨੂੰ ਦਰਾਮਦ ਨਹੀੰ ਕਰ ਸਕੇ ਕਿਉੰਕਿ ਇਸ ਕਾਗਜ਼ ਉੱਤੇ ਪਹਿਲਾਂ ਹੀ ਤਬਦੀਲੀਆਂ ਕੀਤੀਆਂ ਜਾ ਚੁਕੀਆਂ ਹਨ, ਕਿਰਪਾ ਕਰਕੇ ਨਵੇਂ ਕਾਗਜ਼ ਵਿਚ ਦਰਾਮਦ ਕਰੋ",
|
"pad.impexp.padHasData": "ਅਸੀ ਇਸ ਫਾਈਲ ਨੂੰ ਆਯਾਤ ਨਹੀੰ ਕਰ ਸਕੇ ਕਿਉੰਕਿ ਇਸ ਪੈਡ ਉੱਤੇ ਪਹਿਲਾੰ ਹੀ ਤਬਦੀਲੀਆੰ ਕੀਤੀਆੰ ਜਾ ਚੁਕੀਆੰ ਹਨ, ਕਿਰਪਾ ਕਰਕੇ ਨਵੇੰ ਪੈਡ ਵਿਚ ਆਯਾਤ ਕਰੋ",
|
||||||
"pad.impexp.uploadFailed": "ਅੱਪਲੋਡ ਲਈ ਫੇਲ੍ਹ ਹੈ, ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ।",
|
"pad.impexp.uploadFailed": "ਅੱਪਲੋਡ ਲਈ ਫੇਲ੍ਹ ਹੈ, ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ।",
|
||||||
"pad.impexp.importfailed": "ਦਰਾਮਦ ਨਾਕਾਮ",
|
"pad.impexp.importfailed": "ਇੰਪੋਰਟ ਫੇਲ੍ਹ ਹੈ",
|
||||||
"pad.impexp.copypaste": "ਕਾਪੀ ਕਰੋ ਚੇਪੋ ਜੀ",
|
"pad.impexp.copypaste": "ਕਾਪੀ ਕਰੋ ਚੇਪੋ ਜੀ",
|
||||||
"pad.impexp.exportdisabled": "{{type}} ਫਾਰਮੈਟ ਵਜੋਂ ਬਰਾਮਦ ਕਰਨਾ ਬੰਦ ਹੈ। ਵੇਰਵੇ ਵਾਸਤੇ ਆਪਣੇ ਸਿਸਟਮ ਦੇ ਪਰਬੰਧਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"
|
"pad.impexp.exportdisabled": "{{type}} ਫਾਰਮੈਟ ਵਜੋਂ ਬਰਾਮਦ ਕਰਨਾ ਬੰਦ ਹੈ। ਵੇਰਵੇ ਵਾਸਤੇ ਆਪਣੇ ਸਿਸਟਮ ਦੇ ਪਰਬੰਧਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,8 +71,6 @@
|
||||||
"pad.settings.rtlcheck": "Ël contnù, dev-lo esse lesù da drita a snistra?",
|
"pad.settings.rtlcheck": "Ël contnù, dev-lo esse lesù da drita a snistra?",
|
||||||
"pad.settings.fontType": "Sòrt ëd caràter:",
|
"pad.settings.fontType": "Sòrt ëd caràter:",
|
||||||
"pad.settings.language": "Lenga:",
|
"pad.settings.language": "Lenga:",
|
||||||
"pad.settings.deletePad": "Eliminé ël blochet",
|
|
||||||
"pad.delete.confirm": "Veul-lo për da bon eliminé cost blochet?",
|
|
||||||
"pad.settings.about": "A propòsit",
|
"pad.settings.about": "A propòsit",
|
||||||
"pad.settings.poweredBy": "Potensià da",
|
"pad.settings.poweredBy": "Potensià da",
|
||||||
"pad.importExport.import_export": "Amporté/Esporté",
|
"pad.importExport.import_export": "Amporté/Esporté",
|
||||||
|
|
|
@ -23,22 +23,22 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"admin.page-title": "Painel administrativo - Etherpad",
|
"admin.page-title": "Painel administrativo - Etherpad",
|
||||||
"admin_plugins": "Gerenciador de complementos",
|
"admin_plugins": "Gerente de complementos",
|
||||||
"admin_plugins.available": "Plugins disponíveis",
|
"admin_plugins.available": "Plugins disponíveis",
|
||||||
"admin_plugins.available_not-found": "Nenhum plugin encontrado.",
|
"admin_plugins.available_not-found": "Nenhuma complementos encontrado.",
|
||||||
"admin_plugins.available_fetching": "Buscando…",
|
"admin_plugins.available_fetching": "Buscando…",
|
||||||
"admin_plugins.available_install.value": "Instalar",
|
"admin_plugins.available_install.value": "Instalar",
|
||||||
"admin_plugins.available_search.placeholder": "Procure plugins para instalar",
|
"admin_plugins.available_search.placeholder": "Procure por plug-ins para instalar",
|
||||||
"admin_plugins.description": "Descrição",
|
"admin_plugins.description": "Descrição",
|
||||||
"admin_plugins.installed": "Plugins instalados",
|
"admin_plugins.installed": "Complementos instalados",
|
||||||
"admin_plugins.installed_fetching": "Buscando plugins instalados…",
|
"admin_plugins.installed_fetching": "Buscando plug-ins instalados…",
|
||||||
"admin_plugins.installed_nothing": "Você ainda não instalou nenhum plugin.",
|
"admin_plugins.installed_nothing": "Você ainda não instalou nenhum plug-in.",
|
||||||
"admin_plugins.installed_uninstall.value": "Desinstalar",
|
"admin_plugins.installed_uninstall.value": "Desinstalar",
|
||||||
"admin_plugins.last-update": "Última atualização",
|
"admin_plugins.last-update": "Última atualização",
|
||||||
"admin_plugins.name": "Nome",
|
"admin_plugins.name": "Nome",
|
||||||
"admin_plugins.page-title": "Gerenciador de plugins - Etherpad",
|
"admin_plugins.page-title": "Gerenciador de plug-ins - Etherpad",
|
||||||
"admin_plugins.version": "Versão",
|
"admin_plugins.version": "Versão",
|
||||||
"admin_plugins_info": "Resolução de problemas",
|
"admin_plugins_info": "Informações sobre solução",
|
||||||
"admin_plugins_info.hooks": "Ganchos instalados",
|
"admin_plugins_info.hooks": "Ganchos instalados",
|
||||||
"admin_plugins_info.hooks_client": "Ganchos do lado do cliente",
|
"admin_plugins_info.hooks_client": "Ganchos do lado do cliente",
|
||||||
"admin_plugins_info.hooks_server": "Ganchos do lado do servidor",
|
"admin_plugins_info.hooks_server": "Ganchos do lado do servidor",
|
||||||
|
@ -90,8 +90,6 @@
|
||||||
"pad.settings.fontType": "Tipo de fonte:",
|
"pad.settings.fontType": "Tipo de fonte:",
|
||||||
"pad.settings.fontType.normal": "Normal",
|
"pad.settings.fontType.normal": "Normal",
|
||||||
"pad.settings.language": "Idioma:",
|
"pad.settings.language": "Idioma:",
|
||||||
"pad.settings.deletePad": "Apagar Pad",
|
|
||||||
"pad.delete.confirm": "Tem certeza de que quer deletar este pad?",
|
|
||||||
"pad.settings.about": "Sobre",
|
"pad.settings.about": "Sobre",
|
||||||
"pad.settings.poweredBy": "Fornecido por",
|
"pad.settings.poweredBy": "Fornecido por",
|
||||||
"pad.importExport.import_export": "Importar/Exportar",
|
"pad.importExport.import_export": "Importar/Exportar",
|
||||||
|
@ -129,7 +127,7 @@
|
||||||
"pad.modals.deleted": "Excluído.",
|
"pad.modals.deleted": "Excluído.",
|
||||||
"pad.modals.deleted.explanation": "Esta nota foi removida.",
|
"pad.modals.deleted.explanation": "Esta nota foi removida.",
|
||||||
"pad.modals.rateLimited": "Limitado.",
|
"pad.modals.rateLimited": "Limitado.",
|
||||||
"pad.modals.rateLimited.explanation": "Você enviou muitas mensagens para esta nota por isso será desconectado.",
|
"pad.modals.rateLimited.explanation": "Você enviou muitas mensagens para este pad por isso será desconectado.",
|
||||||
"pad.modals.rejected.explanation": "O servidor rejeitou uma mensagem que foi enviada pelo seu navegador.",
|
"pad.modals.rejected.explanation": "O servidor rejeitou uma mensagem que foi enviada pelo seu navegador.",
|
||||||
"pad.modals.rejected.cause": "O server pode ter sido atualizado enquanto visualizava esta nota, ou talvez seja apenas um bug do Etherpad. Tenta recarregar a página.",
|
"pad.modals.rejected.cause": "O server pode ter sido atualizado enquanto visualizava esta nota, ou talvez seja apenas um bug do Etherpad. Tenta recarregar a página.",
|
||||||
"pad.modals.disconnected": "Você foi desconectado.",
|
"pad.modals.disconnected": "Você foi desconectado.",
|
||||||
|
@ -144,7 +142,7 @@
|
||||||
"pad.chat.loadmessages": "Carregar mais mensagens",
|
"pad.chat.loadmessages": "Carregar mais mensagens",
|
||||||
"pad.chat.stick.title": "Cole o bate-papo na tela",
|
"pad.chat.stick.title": "Cole o bate-papo na tela",
|
||||||
"pad.chat.writeMessage.placeholder": "Escreva sua mensagem aqui",
|
"pad.chat.writeMessage.placeholder": "Escreva sua mensagem aqui",
|
||||||
"timeslider.followContents": "Siga as atualizações de conteúdo da nota",
|
"timeslider.followContents": "Siga as atualizações de conteúdo do pad",
|
||||||
"timeslider.pageTitle": "Linha do tempo de {{appTitle}}",
|
"timeslider.pageTitle": "Linha do tempo de {{appTitle}}",
|
||||||
"timeslider.toolbar.returnbutton": "Retornar para a nota",
|
"timeslider.toolbar.returnbutton": "Retornar para a nota",
|
||||||
"timeslider.toolbar.authors": "Autores:",
|
"timeslider.toolbar.authors": "Autores:",
|
||||||
|
@ -153,9 +151,9 @@
|
||||||
"timeslider.exportCurrent": "Exportar a versão atual em formato:",
|
"timeslider.exportCurrent": "Exportar a versão atual em formato:",
|
||||||
"timeslider.version": "Versão {{version}}",
|
"timeslider.version": "Versão {{version}}",
|
||||||
"timeslider.saved": "Salvo em {{day}} de {{month}} de {{year}}",
|
"timeslider.saved": "Salvo em {{day}} de {{month}} de {{year}}",
|
||||||
"timeslider.playPause": "Reproduzir / Pausar conteúdos da Nota",
|
"timeslider.playPause": "Reproduzir / Pausar conteúdo no Pad",
|
||||||
"timeslider.backRevision": "Voltar a uma revisão anterior nesta Nota",
|
"timeslider.backRevision": "Voltar a uma revisão anterior neste Pad",
|
||||||
"timeslider.forwardRevision": "Ir a uma revisão posterior nesta Nota",
|
"timeslider.forwardRevision": "Ir a uma revisão posterior neste Pad",
|
||||||
"timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
|
"timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}",
|
||||||
"timeslider.month.january": "Janeiro",
|
"timeslider.month.january": "Janeiro",
|
||||||
"timeslider.month.february": "Fevereiro",
|
"timeslider.month.february": "Fevereiro",
|
||||||
|
|
|
@ -87,8 +87,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": "Импорт/экспорт",
|
||||||
|
|
|
@ -77,8 +77,6 @@
|
||||||
"pad.settings.fontType": "Vrsta pisave:",
|
"pad.settings.fontType": "Vrsta pisave:",
|
||||||
"pad.settings.fontType.normal": "Normalno",
|
"pad.settings.fontType.normal": "Normalno",
|
||||||
"pad.settings.language": "Jezik:",
|
"pad.settings.language": "Jezik:",
|
||||||
"pad.settings.deletePad": "Izbriši ploščico",
|
|
||||||
"pad.delete.confirm": "Res želite izbrisati to ploščico?",
|
|
||||||
"pad.settings.about": "Kolofon",
|
"pad.settings.about": "Kolofon",
|
||||||
"pad.settings.poweredBy": "Omogoča",
|
"pad.settings.poweredBy": "Omogoča",
|
||||||
"pad.importExport.import_export": "Uvoz/Izvoz",
|
"pad.importExport.import_export": "Uvoz/Izvoz",
|
||||||
|
|
|
@ -76,8 +76,6 @@
|
||||||
"pad.settings.fontType": "Typsnitt:",
|
"pad.settings.fontType": "Typsnitt:",
|
||||||
"pad.settings.fontType.normal": "Normal",
|
"pad.settings.fontType.normal": "Normal",
|
||||||
"pad.settings.language": "Språk:",
|
"pad.settings.language": "Språk:",
|
||||||
"pad.settings.deletePad": "Radera block",
|
|
||||||
"pad.delete.confirm": "Vill du verkligen radera detta block?",
|
|
||||||
"pad.settings.about": "Om",
|
"pad.settings.about": "Om",
|
||||||
"pad.settings.poweredBy": "Drivs av",
|
"pad.settings.poweredBy": "Drivs av",
|
||||||
"pad.importExport.import_export": "Importera/Exportera",
|
"pad.importExport.import_export": "Importera/Exportera",
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
"Shangkuanlc",
|
"Shangkuanlc",
|
||||||
"Shizhao",
|
"Shizhao",
|
||||||
"Stang",
|
"Stang",
|
||||||
"TFX202X",
|
|
||||||
"VulpesVulpes825",
|
"VulpesVulpes825",
|
||||||
"Yfdyh000",
|
"Yfdyh000",
|
||||||
"乌拉跨氪",
|
"乌拉跨氪",
|
||||||
|
@ -93,8 +92,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": "导入/导出",
|
||||||
|
|
|
@ -82,8 +82,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": "導入/匯出",
|
||||||
|
|
|
@ -20,15 +20,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {MapArrayType} from "../types/MapType";
|
import {MapArrayType} from "../types/MapType";
|
||||||
import { jwtDecode } from "jwt-decode";
|
|
||||||
const api = require('../db/API');
|
const api = require('../db/API');
|
||||||
const padManager = require('../db/PadManager');
|
const padManager = require('../db/PadManager');
|
||||||
const settings = require('../utils/Settings');
|
|
||||||
import createHTTPError from 'http-errors';
|
import createHTTPError from 'http-errors';
|
||||||
import {Http2ServerRequest} from "node:http2";
|
import {Http2ServerRequest, Http2ServerResponse} from "node:http2";
|
||||||
import {publicKeyExported} from "../security/OAuth2Provider";
|
import {publicKeyExported} from "../security/OAuth2Provider";
|
||||||
import {jwtVerify} from "jose";
|
import {jwtVerify} from "jose";
|
||||||
import {APIFields, apikey} from './APIKeyHandler'
|
import {apikey} from './APIKeyHandler'
|
||||||
// a list of all functions
|
// a list of all functions
|
||||||
const version:MapArrayType<any> = {};
|
const version:MapArrayType<any> = {};
|
||||||
|
|
||||||
|
@ -142,7 +141,6 @@ version['1.3.0'] = {
|
||||||
setText: ['padID', 'text', 'authorId'],
|
setText: ['padID', 'text', 'authorId'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// set the latest available API version here
|
// set the latest available API version here
|
||||||
exports.latestApiVersion = '1.3.0';
|
exports.latestApiVersion = '1.3.0';
|
||||||
|
|
||||||
|
@ -150,6 +148,13 @@ exports.latestApiVersion = '1.3.0';
|
||||||
exports.version = version;
|
exports.version = version;
|
||||||
|
|
||||||
|
|
||||||
|
type APIFields = {
|
||||||
|
apikey: string;
|
||||||
|
api_key: string;
|
||||||
|
padID: string;
|
||||||
|
padName: string;
|
||||||
|
authorization: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles an HTTP API call
|
* Handles an HTTP API call
|
||||||
|
@ -183,17 +188,8 @@ exports.handle = async function (apiVersion: string, functionName: string, field
|
||||||
throw new createHTTPError.Unauthorized('no or wrong API Key');
|
throw new createHTTPError.Unauthorized('no or wrong API Key');
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const clientIds: string[] = settings.sso.clients?.map((client: {client_id: string}) => client.client_id);
|
await jwtVerify(req.headers.authorization!.replace("Bearer ", ""), publicKeyExported!, {algorithms: ['RS256'],
|
||||||
const jwtToCheck = req.headers.authorization.replace("Bearer ", "")
|
|
||||||
const payload = jwtDecode(jwtToCheck)
|
|
||||||
// client_credentials
|
|
||||||
if (clientIds.includes(<string>payload.sub)) {
|
|
||||||
await jwtVerify(jwtToCheck, publicKeyExported!, {algorithms: ['RS256']})
|
|
||||||
} else {
|
|
||||||
// authorization_code
|
|
||||||
await jwtVerify(jwtToCheck, publicKeyExported!, {algorithms: ['RS256'],
|
|
||||||
requiredClaims: ["admin"]})
|
requiredClaims: ["admin"]})
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new createHTTPError.Unauthorized('no or wrong OAuth token');
|
throw new createHTTPError.Unauthorized('no or wrong OAuth token');
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,16 +7,6 @@ const settings = require('../utils/Settings');
|
||||||
|
|
||||||
const apiHandlerLogger = log4js.getLogger('APIHandler');
|
const apiHandlerLogger = log4js.getLogger('APIHandler');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type APIFields = {
|
|
||||||
apikey: string;
|
|
||||||
api_key: string;
|
|
||||||
padID: string;
|
|
||||||
padName: string;
|
|
||||||
authorization: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure we have an apikey
|
// ensure we have an apikey
|
||||||
export let apikey:string|null = null;
|
export let apikey:string|null = null;
|
||||||
const apikeyFilename = absolutePaths.makeAbsolute(argv.apikey || './APIKEY.txt');
|
const apikeyFilename = absolutePaths.makeAbsolute(argv.apikey || './APIKEY.txt');
|
||||||
|
|
|
@ -43,7 +43,7 @@ import {RateLimiterMemory} from 'rate-limiter-flexible';
|
||||||
import {ChangesetRequest, PadUserInfo, SocketClientRequest} from "../types/SocketClientRequest";
|
import {ChangesetRequest, PadUserInfo, SocketClientRequest} from "../types/SocketClientRequest";
|
||||||
import {APool, AText, PadAuthor, PadType} from "../types/PadType";
|
import {APool, AText, PadAuthor, PadType} from "../types/PadType";
|
||||||
import {ChangeSet} from "../types/ChangeSet";
|
import {ChangeSet} from "../types/ChangeSet";
|
||||||
import {ChatMessageMessage, ClientReadyMessage, ClientSaveRevisionMessage, ClientSuggestUserName, ClientUserChangesMessage, ClientVarMessage, CustomMessage, PadDeleteMessage, UserNewInfoMessage} from "../../static/js/types/SocketIOMessage";
|
import {ChatMessageMessage, ClientReadyMessage, ClientSaveRevisionMessage, ClientSuggestUserName, ClientUserChangesMessage, ClientVarMessage, CustomMessage, UserNewInfoMessage} from "../../static/js/types/SocketIOMessage";
|
||||||
import {Builder} from "../../static/js/Builder";
|
import {Builder} from "../../static/js/Builder";
|
||||||
const webaccess = require('../hooks/express/webaccess');
|
const webaccess = require('../hooks/express/webaccess');
|
||||||
const { checkValidRev } = require('../utils/checkValidRev');
|
const { checkValidRev } = require('../utils/checkValidRev');
|
||||||
|
@ -211,45 +211,6 @@ exports.handleDisconnect = async (socket:any) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handlePadDelete = async (socket: any, padDeleteMessage: PadDeleteMessage) => {
|
|
||||||
const session = sessioninfos[socket.id];
|
|
||||||
if (!session || !session.author || !session.padId) throw new Error('session not ready');
|
|
||||||
if (await padManager.doesPadExist(padDeleteMessage.data.padId)) {
|
|
||||||
const retrievedPad = await padManager.getPad(padDeleteMessage.data.padId)
|
|
||||||
// Only the one doing the first revision can delete the pad, otherwise people could troll a lot
|
|
||||||
const firstContributor = await retrievedPad.getRevisionAuthor(0)
|
|
||||||
if (session.author === firstContributor) {
|
|
||||||
retrievedPad.remove()
|
|
||||||
} else {
|
|
||||||
|
|
||||||
type ShoutMessage = {
|
|
||||||
message: string,
|
|
||||||
sticky: boolean,
|
|
||||||
}
|
|
||||||
|
|
||||||
const messageToShout: ShoutMessage = {
|
|
||||||
message: 'You are not the creator of this pad, so you cannot delete it',
|
|
||||||
sticky: false
|
|
||||||
}
|
|
||||||
const messageToSend = {
|
|
||||||
type: "COLLABROOM",
|
|
||||||
data: {
|
|
||||||
type: "shoutMessage",
|
|
||||||
payload: {
|
|
||||||
message: messageToShout,
|
|
||||||
timestamp: Date.now()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
socket.emit('shout',
|
|
||||||
messageToSend
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a message from a user
|
* Handles a message from a user
|
||||||
* @param socket the socket.io Socket object for the client
|
* @param socket the socket.io Socket object for the client
|
||||||
|
@ -389,7 +350,6 @@ exports.handleMessage = async (socket:any, message: ClientVarMessage) => {
|
||||||
stats.counter('pendingEdits').inc();
|
stats.counter('pendingEdits').inc();
|
||||||
await padChannels.enqueue(thisSession.padId, {socket, message});
|
await padChannels.enqueue(thisSession.padId, {socket, message});
|
||||||
break;
|
break;
|
||||||
case 'PAD_DELETE': await handlePadDelete(socket, message.data as unknown as PadDeleteMessage); break;
|
|
||||||
case 'USERINFO_UPDATE': await handleUserInfoUpdate(socket, message as unknown as UserNewInfoMessage); break;
|
case 'USERINFO_UPDATE': await handleUserInfoUpdate(socket, message as unknown as UserNewInfoMessage); break;
|
||||||
case 'CHAT_MESSAGE': await handleChatMessage(socket, message as unknown as ChatMessageMessage); break;
|
case 'CHAT_MESSAGE': await handleChatMessage(socket, message as unknown as ChatMessageMessage); break;
|
||||||
case 'GET_CHAT_MESSAGES': await handleGetChatMessages(socket, message); break;
|
case 'GET_CHAT_MESSAGES': await handleGetChatMessages(socket, message); break;
|
||||||
|
@ -1187,13 +1147,13 @@ const getChangesetInfo = async (pad: PadType, startNum: number, endNum:number, g
|
||||||
getPadLines(pad, startNum - 1),
|
getPadLines(pad, startNum - 1),
|
||||||
// Get all needed composite Changesets.
|
// Get all needed composite Changesets.
|
||||||
...compositesChangesetNeeded.map(async (item) => {
|
...compositesChangesetNeeded.map(async (item) => {
|
||||||
const changeset = await exports.composePadChangesets(pad, item.start, item.end);
|
const changeset = await composePadChangesets(pad, item.start, item.end);
|
||||||
composedChangesets[`${item.start}/${item.end}`] = changeset;
|
composedChangesets[`${item.start}/${item.end}`] = changeset;
|
||||||
}),
|
}),
|
||||||
// Get all needed revision Dates.
|
// Get all needed revision Dates.
|
||||||
...revTimesNeeded.map(async (revNum) => {
|
...revTimesNeeded.map(async (revNum) => {
|
||||||
const revDate = await pad.getRevisionDate(revNum);
|
const revDate = await pad.getRevisionDate(revNum);
|
||||||
revisionDate[revNum] = revDate;
|
revisionDate[revNum] = Math.floor(revDate / 1000);
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -1253,7 +1213,7 @@ const getPadLines = async (pad: PadType, revNum: number) => {
|
||||||
* Tries to rebuild the composePadChangeset function of the original Etherpad
|
* Tries to rebuild the composePadChangeset function of the original Etherpad
|
||||||
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L241
|
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L241
|
||||||
*/
|
*/
|
||||||
exports.composePadChangesets = async (pad: PadType, startNum: number, endNum: number) => {
|
const composePadChangesets = async (pad: PadType, startNum: number, endNum: number) => {
|
||||||
// fetch all changesets we need
|
// fetch all changesets we need
|
||||||
const headNum = pad.getHeadRevisionNumber();
|
const headNum = pad.getHeadRevisionNumber();
|
||||||
endNum = Math.min(endNum, headNum + 1);
|
endNum = Math.min(endNum, headNum + 1);
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,12 +5,11 @@ import {ErrorCaused} from "../../types/ErrorCaused";
|
||||||
import {QueryType} from "../../types/QueryType";
|
import {QueryType} from "../../types/QueryType";
|
||||||
|
|
||||||
import {getAvailablePlugins, install, search, uninstall} from "../../../static/js/pluginfw/installer";
|
import {getAvailablePlugins, install, search, uninstall} from "../../../static/js/pluginfw/installer";
|
||||||
import {PackageData, PackageInfo} from "../../types/PackageInfo";
|
import {PackageData} from "../../types/PackageInfo";
|
||||||
import semver from 'semver';
|
|
||||||
import log4js from 'log4js';
|
|
||||||
import {MapArrayType} from "../../types/MapType";
|
|
||||||
|
|
||||||
const pluginDefs = require('../../../static/js/pluginfw/plugin_defs');
|
const pluginDefs = require('../../../static/js/pluginfw/plugin_defs');
|
||||||
|
import semver from 'semver';
|
||||||
|
import log4js from 'log4js';
|
||||||
const logger = log4js.getLogger('adminPlugins');
|
const logger = log4js.getLogger('adminPlugins');
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,42 +20,27 @@ exports.socketio = (hookName:string, args:ArgsExpressType, cb:Function) => {
|
||||||
const {session: {user: {is_admin: isAdmin} = {}} = {}} = socket.conn.request;
|
const {session: {user: {is_admin: isAdmin} = {}} = {}} = socket.conn.request;
|
||||||
if (!isAdmin) return;
|
if (!isAdmin) return;
|
||||||
|
|
||||||
const checkPluginForUpdates = async () => {
|
socket.on('getInstalled', (query:string) => {
|
||||||
let results: MapArrayType<PackageInfo>
|
|
||||||
try {
|
|
||||||
results = await getAvailablePlugins(/* maxCacheAge:*/ 60 * 10);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error checking for plugin updates:', error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return Object.keys(pluginDefs.plugins).filter((plugin) => {
|
|
||||||
if (!results[plugin]) return false;
|
|
||||||
|
|
||||||
const latestVersion = results[plugin].version;
|
|
||||||
const currentVersion = pluginDefs.plugins[plugin].package.version;
|
|
||||||
|
|
||||||
return semver.gt(latestVersion, currentVersion);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.on('getInstalled', async (query: string) => {
|
|
||||||
// send currently installed plugins
|
// send currently installed plugins
|
||||||
const installed =
|
const installed =
|
||||||
Object.keys(pluginDefs.plugins).map((plugin) => pluginDefs.plugins[plugin].package);
|
Object.keys(pluginDefs.plugins).map((plugin) => pluginDefs.plugins[plugin].package);
|
||||||
|
|
||||||
const updatable = await checkPluginForUpdates();
|
|
||||||
|
|
||||||
installed.forEach((plugin) => {
|
|
||||||
plugin.updatable = updatable.includes(plugin.name);
|
|
||||||
})
|
|
||||||
|
|
||||||
socket.emit('results:installed', {installed});
|
socket.emit('results:installed', {installed});
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('checkUpdates', async () => {
|
socket.on('checkUpdates', async () => {
|
||||||
// Check plugins for updates
|
// Check plugins for updates
|
||||||
try {
|
try {
|
||||||
const updatable = checkPluginForUpdates();
|
const results = await getAvailablePlugins(/* maxCacheAge:*/ 60 * 10);
|
||||||
|
|
||||||
|
const updatable = Object.keys(pluginDefs.plugins).filter((plugin) => {
|
||||||
|
if (!results[plugin]) return false;
|
||||||
|
|
||||||
|
const latestVersion = results[plugin].version;
|
||||||
|
const currentVersion = pluginDefs.plugins[plugin].package.version;
|
||||||
|
|
||||||
|
return semver.gt(latestVersion, currentVersion);
|
||||||
|
});
|
||||||
|
|
||||||
socket.emit('results:updatable', {updatable});
|
socket.emit('results:updatable', {updatable});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -13,7 +13,6 @@ const settings = require('../../utils/Settings');
|
||||||
const UpdateCheck = require('../../utils/UpdateCheck');
|
const UpdateCheck = require('../../utils/UpdateCheck');
|
||||||
const padManager = require('../../db/PadManager');
|
const padManager = require('../../db/PadManager');
|
||||||
const api = require('../../db/API');
|
const api = require('../../db/API');
|
||||||
const cleanup = require('../../utils/Cleanup');
|
|
||||||
|
|
||||||
|
|
||||||
const queryPadLimit = 12;
|
const queryPadLimit = 12;
|
||||||
|
@ -253,40 +252,6 @@ exports.socketio = (hookName: string, {io}: any) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on('cleanupPadRevisions', async (padId: string) => {
|
|
||||||
if (!settings.cleanup.enabled) {
|
|
||||||
socket.emit('results:cleanupPadRevisions', {
|
|
||||||
error: 'Cleanup disabled. Enable cleanup in settings.json: cleanup.enabled => true',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const padExists = await padManager.doesPadExists(padId);
|
|
||||||
if (padExists) {
|
|
||||||
logger.info(`Cleanup pad revisions: ${padId}`);
|
|
||||||
try {
|
|
||||||
const result = await cleanup.deleteRevisions(padId, settings.cleanup.keepRevisions)
|
|
||||||
if (result) {
|
|
||||||
socket.emit('results:cleanupPadRevisions', {
|
|
||||||
padId: padId,
|
|
||||||
keepRevisions: settings.cleanup.keepRevisions,
|
|
||||||
});
|
|
||||||
logger.info('successful cleaned up pad: ', padId)
|
|
||||||
} else {
|
|
||||||
socket.emit('results:cleanupPadRevisions', {
|
|
||||||
error: 'Error cleaning up pad',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
logger.error(`Error in pad ${padId}: ${err.stack || err}`);
|
|
||||||
socket.emit('results:cleanupPadRevisions', {
|
|
||||||
error: err.toString(),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
socket.on('restartServer', async () => {
|
socket.on('restartServer', async () => {
|
||||||
logger.info('Admin request to restart server through a socket on /admin/settings');
|
logger.info('Admin request to restart server through a socket on /admin/settings');
|
||||||
settings.reloadSettings();
|
settings.reloadSettings();
|
||||||
|
|
|
@ -12,7 +12,6 @@ const webaccess = require('./webaccess');
|
||||||
const plugins = require('../../../static/js/pluginfw/plugin_defs');
|
const plugins = require('../../../static/js/pluginfw/plugin_defs');
|
||||||
|
|
||||||
import {build, buildSync} from 'esbuild'
|
import {build, buildSync} from 'esbuild'
|
||||||
import {ArgsExpressType} from "../../types/ArgsExpressType";
|
|
||||||
let ioI: { sockets: { sockets: any[]; }; } | null = null
|
let ioI: { sockets: { sockets: any[]; }; } | null = null
|
||||||
|
|
||||||
exports.socketio = (hookName: string, {io}: any) => {
|
exports.socketio = (hookName: string, {io}: any) => {
|
||||||
|
@ -20,7 +19,7 @@ exports.socketio = (hookName: string, {io}: any) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exports.expressPreSession = async (hookName:string, {app}:ArgsExpressType) => {
|
exports.expressPreSession = async (hookName:string, {app}:any) => {
|
||||||
// This endpoint is intended to conform to:
|
// This endpoint is intended to conform to:
|
||||||
// https://www.ietf.org/archive/id/draft-inadarei-api-health-check-06.html
|
// https://www.ietf.org/archive/id/draft-inadarei-api-health-check-06.html
|
||||||
app.get('/health', (req:any, res:any) => {
|
app.get('/health', (req:any, res:any) => {
|
||||||
|
@ -112,9 +111,9 @@ const convertTypescript = (content: string) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleLiveReload = async (args: ArgsExpressType, padString: string, timeSliderString: string, indexString: any) => {
|
const handleLiveReload = async (args: any, padString: string, timeSliderString: string, indexString: any) => {
|
||||||
const chokidar = await import('chokidar')
|
const chokidar = await import('chokidar')
|
||||||
const watcher = chokidar.watch(path.join(settings.root, 'src', 'static', 'js'), {});
|
const watcher = chokidar.watch(path.join(settings.root, 'src', 'static', 'js'));
|
||||||
let routeHandlers: { [key: string]: Function } = {};
|
let routeHandlers: { [key: string]: Function } = {};
|
||||||
|
|
||||||
const setRouteHandler = (path: string, newHandler: Function) => {
|
const setRouteHandler = (path: string, newHandler: Function) => {
|
||||||
|
@ -244,7 +243,7 @@ const convertTypescriptWatched = (content: string, cb: (output:string, hash: str
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.expressCreateServer = async (_hookName: string, args: ArgsExpressType, cb: Function) => {
|
exports.expressCreateServer = async (hookName: string, args: any, cb: Function) => {
|
||||||
const padString = eejs.require('ep_etherpad-lite/templates/padBootstrap.js', {
|
const padString = eejs.require('ep_etherpad-lite/templates/padBootstrap.js', {
|
||||||
pluginModules: (() => {
|
pluginModules: (() => {
|
||||||
const pluginModules = new Set();
|
const pluginModules = new Set();
|
||||||
|
@ -295,20 +294,32 @@ exports.expressCreateServer = async (_hookName: string, args: ArgsExpressType, c
|
||||||
fileNamePad = `padbootstrap-${padSliderWrite.hash}.min.js`
|
fileNamePad = `padbootstrap-${padSliderWrite.hash}.min.js`
|
||||||
fileNameTimeSlider = `timeSliderBootstrap-${timeSliderWrite.hash}.min.js`
|
fileNameTimeSlider = `timeSliderBootstrap-${timeSliderWrite.hash}.min.js`
|
||||||
fileNameIndex = `indexBootstrap-${indexWrite.hash}.min.js`
|
fileNameIndex = `indexBootstrap-${indexWrite.hash}.min.js`
|
||||||
|
const pathNamePad = path.join(outdir, fileNamePad)
|
||||||
|
const pathNameTimeSlider = path.join(outdir, fileNameTimeSlider)
|
||||||
|
const pathNameIndex = path.join(outdir, 'index.js')
|
||||||
|
|
||||||
args.app.get("/"+fileNamePad, (_req, res) => {
|
if (!fs.existsSync(pathNamePad)) {
|
||||||
res.header('Content-Type', 'application/javascript');
|
fs.writeFileSync(pathNamePad, padSliderWrite.output);
|
||||||
res.send(padSliderWrite.output)
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(pathNameIndex)) {
|
||||||
|
fs.writeFileSync(pathNameIndex, indexWrite.output);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(pathNameTimeSlider)) {
|
||||||
|
fs.writeFileSync(pathNameTimeSlider,timeSliderWrite.output)
|
||||||
|
}
|
||||||
|
|
||||||
|
args.app.get("/"+fileNamePad, (req: any, res: any) => {
|
||||||
|
res.sendFile(pathNamePad)
|
||||||
})
|
})
|
||||||
|
|
||||||
args.app.get("/"+fileNameIndex, (_req, res) => {
|
args.app.get("/"+fileNameIndex, (req: any, res: any) => {
|
||||||
res.header('Content-Type', 'application/javascript');
|
res.sendFile(pathNameIndex)
|
||||||
res.send(indexWrite.output)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
args.app.get("/"+fileNameTimeSlider, (_req, res) => {
|
args.app.get("/"+fileNameTimeSlider, (req: any, res: any) => {
|
||||||
res.header('Content-Type', 'application/javascript');
|
res.sendFile(pathNameTimeSlider)
|
||||||
res.send(timeSliderWrite.output)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// serve index.html under /
|
// serve index.html under /
|
||||||
|
|
|
@ -49,21 +49,8 @@ exports.userCanModify = (padId: string, req: SocketClientRequest) => {
|
||||||
// Exported so that tests can set this to 0 to avoid unnecessary test slowness.
|
// Exported so that tests can set this to 0 to avoid unnecessary test slowness.
|
||||||
exports.authnFailureDelayMs = 1000;
|
exports.authnFailureDelayMs = 1000;
|
||||||
|
|
||||||
const staticResources = [
|
|
||||||
/^\/padbootstrap-[a-zA-Z0-9]+\.min\.js$/,
|
|
||||||
/^\/timeSliderBootstrap-[a-zA-Z0-9]+\.min\.js$/,
|
|
||||||
/^\/manifest.json$/
|
|
||||||
]
|
|
||||||
|
|
||||||
const checkAccess = async (req:any, res:any, next: Function) => {
|
const checkAccess = async (req:any, res:any, next: Function) => {
|
||||||
const requireAdmin = req.path.toLowerCase().startsWith('/admin-auth');
|
const requireAdmin = req.path.toLowerCase().startsWith('/admin-auth');
|
||||||
for (const staticResource of staticResources) {
|
|
||||||
if (req.path.match(staticResource)) {
|
|
||||||
console.log(`Loading [${staticResource}] ${req.path}`);
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ///////////////////////////////////////////////////////////////////////////////////////////////
|
// ///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Step 1: Check the preAuthorize hook for early permit/deny (permit is only allowed for non-admin
|
// Step 1: Check the preAuthorize hook for early permit/deny (permit is only allowed for non-admin
|
||||||
|
|
|
@ -30,7 +30,7 @@ const configuration: Configuration = {
|
||||||
if(account === undefined) {
|
if(account === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
if (account.is_admin ) {
|
if (account.is_admin) {
|
||||||
return {
|
return {
|
||||||
accountId: id,
|
accountId: id,
|
||||||
claims: () => ({
|
claims: () => ({
|
||||||
|
@ -153,7 +153,7 @@ export const expressCreateServer = async (hookName: string, args: ArgsExpressTyp
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
args.app.post('/interaction/:uid', async (req, res, next) => {
|
args.app.post('/interaction/:uid', async (req: Http2ServerRequest, res: Http2ServerResponse, next:Function) => {
|
||||||
const formid = new IncomingForm();
|
const formid = new IncomingForm();
|
||||||
try {
|
try {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -226,7 +226,7 @@ export const expressCreateServer = async (hookName: string, args: ArgsExpressTyp
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
args.app.get('/interaction/:uid', async (req, res, next) => {
|
args.app.get('/interaction/:uid', async (req: Request, res: Response, next: Function) => {
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
uid, prompt, params, session,
|
uid, prompt, params, session,
|
||||||
|
|
|
@ -42,10 +42,6 @@ if (settings.dumpOnUncleanExit) {
|
||||||
const addProxyToAxios = (url: URL) => {
|
const addProxyToAxios = (url: URL) => {
|
||||||
axios.defaults.proxy = {
|
axios.defaults.proxy = {
|
||||||
host: url.hostname,
|
host: url.hostname,
|
||||||
auth: {
|
|
||||||
username: url.username,
|
|
||||||
password: url.password,
|
|
||||||
},
|
|
||||||
port: Number(url.port),
|
port: Number(url.port),
|
||||||
protocol: url.protocol,
|
protocol: url.protocol,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import {Express} from "express";
|
|
||||||
|
|
||||||
export type ArgsExpressType = {
|
export type ArgsExpressType = {
|
||||||
app:Express,
|
app:any,
|
||||||
io: any,
|
io: any,
|
||||||
server:any
|
server:any
|
||||||
}
|
}
|
|
@ -1,9 +0,0 @@
|
||||||
import {AChangeSet} from "./PadType";
|
|
||||||
|
|
||||||
export type Revision = {
|
|
||||||
changeset: AChangeSet,
|
|
||||||
meta: {
|
|
||||||
author: string,
|
|
||||||
timestamp: number,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,168 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
import {AChangeSet} from "../types/PadType";
|
|
||||||
import {Revision} from "../types/Revision";
|
|
||||||
|
|
||||||
const promises = require('./promises');
|
|
||||||
const padManager = require('ep_etherpad-lite/node/db/PadManager');
|
|
||||||
const db = require('ep_etherpad-lite/node/db/DB');
|
|
||||||
const Changeset = require('ep_etherpad-lite/static/js/Changeset');
|
|
||||||
const padMessageHandler = require('ep_etherpad-lite/node/handler/PadMessageHandler');
|
|
||||||
const log4js = require('log4js');
|
|
||||||
const logger = log4js.getLogger('cleanup');
|
|
||||||
|
|
||||||
exports.deleteAllRevisions = async (padID: string): Promise<void> => {
|
|
||||||
|
|
||||||
const randomPadId = padID + 'aertdfdf' + Math.random().toString(10)
|
|
||||||
|
|
||||||
let pad = await padManager.getPad(padID);
|
|
||||||
await pad.copyPadWithoutHistory(randomPadId, false);
|
|
||||||
pad = await padManager.getPad(randomPadId);
|
|
||||||
await pad.copyPadWithoutHistory(padID, true);
|
|
||||||
await pad.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
const createRevision = async (aChangeset: AChangeSet, timestamp: number, isKeyRev: boolean, authorId: string, atext: any, pool: any) => {
|
|
||||||
|
|
||||||
if (authorId !== '') pool.putAttrib(['author', authorId]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
changeset: aChangeset,
|
|
||||||
meta: {
|
|
||||||
author: authorId,
|
|
||||||
timestamp: timestamp,
|
|
||||||
...isKeyRev ? {
|
|
||||||
pool: pool,
|
|
||||||
atext: atext,
|
|
||||||
} : {},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.deleteRevisions = async (padId: string, keepRevisions: number): Promise<boolean> => {
|
|
||||||
|
|
||||||
logger.debug('Start cleanup revisions', padId)
|
|
||||||
|
|
||||||
let pad = await padManager.getPad(padId);
|
|
||||||
await pad.check()
|
|
||||||
|
|
||||||
logger.debug('Initial pad is valid')
|
|
||||||
|
|
||||||
if (pad.head <= keepRevisions) {
|
|
||||||
logger.debug('Pad has not enough revisions')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
padMessageHandler.kickSessionsFromPad(padId)
|
|
||||||
|
|
||||||
const cleanupUntilRevision = pad.head - keepRevisions
|
|
||||||
logger.debug('Composing changesets: ', cleanupUntilRevision)
|
|
||||||
const changeset = await padMessageHandler.composePadChangesets(pad, 0, cleanupUntilRevision + 1)
|
|
||||||
|
|
||||||
const revisions: Revision[] = [];
|
|
||||||
|
|
||||||
await promises.timesLimit(keepRevisions + 1, 500, async (i: number) => {
|
|
||||||
const rev = i + cleanupUntilRevision
|
|
||||||
revisions[rev] = await pad.getRevision(rev)
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.debug('Loaded revisions: ', revisions.length)
|
|
||||||
|
|
||||||
await promises.timesLimit(pad.head + 1, 500, async (i: string) => {
|
|
||||||
await db.remove(`pad:${padId}:revs:${i}`, null);
|
|
||||||
});
|
|
||||||
|
|
||||||
let padContent = await db.get(`pad:${padId}`)
|
|
||||||
padContent.head = keepRevisions
|
|
||||||
if (padContent.savedRevisions) {
|
|
||||||
let newSavedRevisions = []
|
|
||||||
|
|
||||||
for (let i = 0; i < padContent.savedRevisions.length; i++) {
|
|
||||||
if (padContent.savedRevisions[i].revNum > cleanupUntilRevision) {
|
|
||||||
padContent.savedRevisions[i].revNum = padContent.savedRevisions[i].revNum - cleanupUntilRevision
|
|
||||||
newSavedRevisions.push(padContent.savedRevisions[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
padContent.savedRevisions = newSavedRevisions
|
|
||||||
}
|
|
||||||
await db.set(`pad:${padId}`, padContent);
|
|
||||||
|
|
||||||
let newAText = Changeset.makeAText('\n');
|
|
||||||
let pool = pad.apool()
|
|
||||||
|
|
||||||
newAText = Changeset.applyToAText(changeset, newAText, pool);
|
|
||||||
|
|
||||||
const revision = await createRevision(
|
|
||||||
changeset,
|
|
||||||
revisions[cleanupUntilRevision].meta.timestamp,
|
|
||||||
0 === pad.getKeyRevisionNumber(0),
|
|
||||||
'',
|
|
||||||
newAText,
|
|
||||||
pool
|
|
||||||
);
|
|
||||||
|
|
||||||
const p: Promise<void>[] = [];
|
|
||||||
|
|
||||||
p.push(db.set(`pad:${padId}:revs:0`, revision))
|
|
||||||
|
|
||||||
p.push(promises.timesLimit(keepRevisions, 500, async (i: number) => {
|
|
||||||
const rev = i + cleanupUntilRevision + 1
|
|
||||||
const newRev = rev - cleanupUntilRevision;
|
|
||||||
|
|
||||||
newAText = Changeset.applyToAText(revisions[rev].changeset, newAText, pool);
|
|
||||||
|
|
||||||
const revision = await createRevision(
|
|
||||||
revisions[rev].changeset,
|
|
||||||
revisions[rev].meta.timestamp,
|
|
||||||
newRev === pad.getKeyRevisionNumber(newRev),
|
|
||||||
revisions[rev].meta.author,
|
|
||||||
newAText,
|
|
||||||
pool
|
|
||||||
);
|
|
||||||
|
|
||||||
await db.set(`pad:${padId}:revs:${newRev}`, revision);
|
|
||||||
}));
|
|
||||||
|
|
||||||
await Promise.all(p)
|
|
||||||
|
|
||||||
logger.debug('Finished migration. Checking pad now')
|
|
||||||
|
|
||||||
padManager.unloadPad(padId);
|
|
||||||
|
|
||||||
let newPad = await padManager.getPad(padId);
|
|
||||||
await newPad.check();
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.checkTodos = async () => {
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
||||||
|
|
||||||
// TODO: Move to settings
|
|
||||||
const settings = {
|
|
||||||
minHead: 100,
|
|
||||||
keepRevisions: 100,
|
|
||||||
minAge: 1,//1000 * 60 * 60 * 24,
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all((await padManager.listAllPads()).padIDs.map(async (padId: string) => {
|
|
||||||
// TODO: Handle concurrency
|
|
||||||
const pad = await padManager.getPad(padId);
|
|
||||||
|
|
||||||
const revisionDate = await pad.getRevisionDate(pad.getHeadRevisionNumber())
|
|
||||||
|
|
||||||
if (pad.head < settings.minHead || padMessageHandler.padUsersCount(padId) > 0 || Date.now() < revisionDate + settings.minAge) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await exports.deleteRevisions(padId, settings.keepRevisions)
|
|
||||||
if (result) {
|
|
||||||
logger.info('successful cleaned up pad: ', padId)
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
logger.error(`Error in pad ${padId}: ${err.stack || err}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
|
@ -107,7 +107,6 @@ exports.ttl = {
|
||||||
RefreshToken: 1 * 24 * 60 * 60, // 1 day in seconds
|
RefreshToken: 1 * 24 * 60 * 60, // 1 day in seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.updateServer = "https://static.etherpad.org"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -380,14 +379,6 @@ exports.sso = {
|
||||||
*/
|
*/
|
||||||
exports.showSettingsInAdminPage = true;
|
exports.showSettingsInAdminPage = true;
|
||||||
|
|
||||||
/*
|
|
||||||
* Settings for cleanup of pads
|
|
||||||
*/
|
|
||||||
exports.cleanup = {
|
|
||||||
enabled: false,
|
|
||||||
keepRevisions: 100,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* By default, when caret is moved out of viewport, it scrolls the minimum
|
* By default, when caret is moved out of viewport, it scrolls the minimum
|
||||||
* height needed to make this line visible.
|
* height needed to make this line visible.
|
||||||
|
|
|
@ -20,7 +20,7 @@ const loadEtherpadInformations = () => {
|
||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
return axios.get(`${settings.updateServer}/info.json`, {headers: headers})
|
return axios.get('https://static.etherpad.org/info.json', {headers: headers})
|
||||||
.then(async (resp: any) => {
|
.then(async (resp: any) => {
|
||||||
infos = await resp.data;
|
infos = await resp.data;
|
||||||
if (infos === undefined || infos === null) {
|
if (infos === undefined || infos === null) {
|
||||||
|
|
|
@ -32,90 +32,86 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@etherpad/express-session": "^1.18.4",
|
"@etherpad/express-session": "^1.18.4",
|
||||||
"async": "^3.2.6",
|
"async": "^3.2.6",
|
||||||
"axios": "^1.8.4",
|
"axios": "^1.7.7",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.6",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cross-spawn": "^7.0.6",
|
"cross-spawn": "^7.0.3",
|
||||||
"ejs": "^3.1.10",
|
"ejs": "^3.1.10",
|
||||||
"esbuild": "^0.25.2",
|
"esbuild": "^0.23.1",
|
||||||
"express": "4.21.2",
|
"express": "4.19.2",
|
||||||
"express-rate-limit": "^7.5.0",
|
"express-rate-limit": "^7.4.0",
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"find-root": "1.1.0",
|
"find-root": "1.1.0",
|
||||||
"formidable": "^3.5.2",
|
"formidable": "^3.5.1",
|
||||||
"http-errors": "^2.0.0",
|
"http-errors": "^2.0.0",
|
||||||
"jose": "^5.10.0",
|
"jose": "^5.8.0",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"jsdom": "^26.0.0",
|
"jsdom": "^25.0.0",
|
||||||
"jsonminify": "0.4.2",
|
"jsonminify": "0.4.2",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"jwt-decode": "^4.0.0",
|
|
||||||
"languages4translatewiki": "0.1.3",
|
"languages4translatewiki": "0.1.3",
|
||||||
"live-plugin-manager": "^1.0.0",
|
"live-plugin-manager": "^1.0.0",
|
||||||
"lodash.clonedeep": "4.5.0",
|
"lodash.clonedeep": "4.5.0",
|
||||||
"log4js": "^6.9.1",
|
"log4js": "^6.9.1",
|
||||||
"lru-cache": "^11.1.0",
|
"lru-cache": "^11.0.1",
|
||||||
"measured-core": "^2.0.0",
|
"measured-core": "^2.0.0",
|
||||||
"mime-types": "^3.0.1",
|
"mime-types": "^2.1.35",
|
||||||
"oidc-provider": "^8.8.1",
|
"oidc-provider": "^8.5.1",
|
||||||
"openapi-backend": "^5.11.1",
|
"openapi-backend": "^5.10.6",
|
||||||
"proxy-addr": "^2.0.7",
|
"proxy-addr": "^2.0.7",
|
||||||
"rate-limiter-flexible": "^7.0.0",
|
"rate-limiter-flexible": "^5.0.3",
|
||||||
"rehype": "^13.0.2",
|
"rehype": "^13.0.1",
|
||||||
"rehype-minify-whitespace": "^6.0.2",
|
"rehype-minify-whitespace": "^6.0.0",
|
||||||
"resolve": "1.22.10",
|
"resolve": "1.22.8",
|
||||||
"rusty-store-kv": "^1.3.1",
|
|
||||||
"security": "1.0.0",
|
"security": "1.0.0",
|
||||||
"semver": "^7.7.1",
|
"semver": "^7.6.3",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.7.5",
|
||||||
"socket.io-client": "^4.8.1",
|
"socket.io-client": "^4.7.5",
|
||||||
"superagent": "10.2.0",
|
"superagent": "10.1.0",
|
||||||
"swagger-ui-express": "^5.0.1",
|
|
||||||
"tinycon": "0.6.8",
|
"tinycon": "0.6.8",
|
||||||
"tsx": "4.19.3",
|
"tsx": "4.19.0",
|
||||||
"ueberdb2": "^5.0.6",
|
"ueberdb2": "^4.2.103",
|
||||||
"underscore": "1.13.7",
|
"underscore": "1.13.7",
|
||||||
"unorm": "1.6.0",
|
"unorm": "1.6.0",
|
||||||
"wtfnode": "^0.10.0"
|
"wtfnode": "^0.9.3",
|
||||||
|
"rusty-store-kv": "^1.2.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"etherpad-healthcheck": "../bin/etherpad-healthcheck",
|
"etherpad-healthcheck": "../bin/etherpad-healthcheck",
|
||||||
"etherpad-lite": "node/server.ts"
|
"etherpad-lite": "node/server.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.52.0",
|
"@playwright/test": "^1.47.0",
|
||||||
"@types/async": "^3.2.24",
|
"@types/async": "^3.2.24",
|
||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.21",
|
||||||
"@types/formidable": "^3.4.5",
|
"@types/formidable": "^3.4.5",
|
||||||
"@types/http-errors": "^2.0.4",
|
"@types/http-errors": "^2.0.4",
|
||||||
"@types/jquery": "^3.5.32",
|
"@types/jquery": "^3.5.30",
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
"@types/jsdom": "^21.1.7",
|
"@types/jsdom": "^21.1.7",
|
||||||
"@types/jsonwebtoken": "^9.0.9",
|
"@types/jsonwebtoken": "^9.0.6",
|
||||||
"@types/mime-types": "^2.1.4",
|
"@types/mime-types": "^2.1.4",
|
||||||
"@types/mocha": "^10.0.9",
|
"@types/mocha": "^10.0.7",
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^22.5.4",
|
||||||
"@types/oidc-provider": "^8.8.1",
|
"@types/oidc-provider": "^8.5.2",
|
||||||
"@types/semver": "^7.7.0",
|
"@types/semver": "^7.5.8",
|
||||||
"@types/sinon": "^17.0.3",
|
"@types/sinon": "^17.0.3",
|
||||||
"@types/supertest": "^6.0.2",
|
"@types/supertest": "^6.0.2",
|
||||||
"@types/swagger-ui-express": "^4.1.8",
|
"@types/underscore": "^1.11.15",
|
||||||
"@types/underscore": "^1.13.0",
|
"chokidar": "^3.6.0",
|
||||||
"@types/whatwg-mimetype": "^3.0.2",
|
"eslint": "^9.9.1",
|
||||||
"chokidar": "^4.0.3",
|
|
||||||
"eslint": "^9.23.0",
|
|
||||||
"eslint-config-etherpad": "^4.0.4",
|
"eslint-config-etherpad": "^4.0.4",
|
||||||
"etherpad-cli-client": "^3.0.2",
|
"etherpad-cli-client": "^3.0.2",
|
||||||
"mocha": "^11.1.0",
|
"mocha": "^10.7.3",
|
||||||
"mocha-froth": "^0.2.10",
|
"mocha-froth": "^0.2.10",
|
||||||
"nodeify": "^1.0.1",
|
"nodeify": "^1.0.1",
|
||||||
"openapi-schema-validation": "^0.4.2",
|
"openapi-schema-validation": "^0.4.2",
|
||||||
"set-cookie-parser": "^2.7.1",
|
"set-cookie-parser": "^2.7.0",
|
||||||
"sinon": "^20.0.0",
|
"sinon": "^18.0.0",
|
||||||
"split-grid": "^1.0.11",
|
"split-grid": "^1.0.11",
|
||||||
"supertest": "^7.1.0",
|
"supertest": "^7.0.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.5.4",
|
||||||
"vitest": "^3.1.1"
|
"vitest": "^2.0.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.18.2",
|
"node": ">=18.18.2",
|
||||||
|
@ -142,6 +138,6 @@
|
||||||
"debug:socketio": "cross-env DEBUG=socket.io* node --require tsx/cjs node/server.ts",
|
"debug:socketio": "cross-env DEBUG=socket.io* node --require tsx/cjs node/server.ts",
|
||||||
"test:vitest": "vitest"
|
"test:vitest": "vitest"
|
||||||
},
|
},
|
||||||
"version": "2.3.0",
|
"version": "2.2.4",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
for (const name of names) console[name] = noop;
|
for (const name of names) console[name] = noop;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scheduler = window; // hack for opera required
|
const scheduler = parent; // hack for opera required
|
||||||
|
|
||||||
const performDocumentReplaceRange = (start, end, newText) => {
|
const performDocumentReplaceRange = (start, end, newText) => {
|
||||||
if (start === undefined) start = rep.selStart;
|
if (start === undefined) start = rep.selStart;
|
||||||
|
@ -240,7 +240,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
bgcolor = fadeColor(bgcolor, info.fade);
|
bgcolor = fadeColor(bgcolor, info.fade);
|
||||||
}
|
}
|
||||||
const textColor =
|
const textColor =
|
||||||
colorutils.textColorFromBackgroundColor(bgcolor, window.clientVars.skinName);
|
colorutils.textColorFromBackgroundColor(bgcolor, parent.parent.clientVars.skinName);
|
||||||
const styles = [
|
const styles = [
|
||||||
cssManagers.inner.selectorStyle(authorSelector),
|
cssManagers.inner.selectorStyle(authorSelector),
|
||||||
cssManagers.parent.selectorStyle(authorSelector),
|
cssManagers.parent.selectorStyle(authorSelector),
|
||||||
|
@ -1270,7 +1270,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
const prevLine = rep.lines.prev(thisLine);
|
const prevLine = rep.lines.prev(thisLine);
|
||||||
const prevLineText = prevLine.text;
|
const prevLineText = prevLine.text;
|
||||||
let theIndent = /^ *(?:)/.exec(prevLineText)[0];
|
let theIndent = /^ *(?:)/.exec(prevLineText)[0];
|
||||||
const shouldIndent = window.clientVars.indentationOnNewLine;
|
const shouldIndent = parent.parent.clientVars.indentationOnNewLine;
|
||||||
if (shouldIndent && /[[(:{]\s*$/.exec(prevLineText)) {
|
if (shouldIndent && /[[(:{]\s*$/.exec(prevLineText)) {
|
||||||
theIndent += THE_TAB;
|
theIndent += THE_TAB;
|
||||||
}
|
}
|
||||||
|
@ -2023,7 +2023,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
const isPadLoading = (t) => t === 'setup' || t === 'setBaseText' || t === 'importText';
|
const isPadLoading = (t) => t === 'setup' || t === 'setBaseText' || t === 'importText';
|
||||||
|
|
||||||
const updateStyleButtonState = (attribName, hasStyleOnRepSelection) => {
|
const updateStyleButtonState = (attribName, hasStyleOnRepSelection) => {
|
||||||
const $formattingButton = window.$(`[data-key="${attribName}"]`).find('a');
|
const $formattingButton = parent.parent.$(`[data-key="${attribName}"]`).find('a');
|
||||||
$formattingButton.toggleClass(SELECT_BUTTON_CLASS, hasStyleOnRepSelection);
|
$formattingButton.toggleClass(SELECT_BUTTON_CLASS, hasStyleOnRepSelection);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2277,7 +2277,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const hideEditBarDropdowns = () => {
|
const hideEditBarDropdowns = () => {
|
||||||
window.padeditbar.toggleDropDown('none');
|
window.parent.parent.padeditbar.toggleDropDown('none');
|
||||||
};
|
};
|
||||||
|
|
||||||
const renumberList = (lineNum) => {
|
const renumberList = (lineNum) => {
|
||||||
|
@ -2582,7 +2582,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
specialHandled = specialHandledInHook.indexOf(true) !== -1;
|
specialHandled = specialHandledInHook.indexOf(true) !== -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const padShortcutEnabled = window.clientVars.padShortcutEnabled;
|
const padShortcutEnabled = parent.parent.clientVars.padShortcutEnabled;
|
||||||
if (!specialHandled && isTypeForSpecialKey &&
|
if (!specialHandled && isTypeForSpecialKey &&
|
||||||
altKey && keyCode === 120 &&
|
altKey && keyCode === 120 &&
|
||||||
padShortcutEnabled.altF9) {
|
padShortcutEnabled.altF9) {
|
||||||
|
@ -2591,7 +2591,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
// As ubuntu cannot use Alt F10....
|
// As ubuntu cannot use Alt F10....
|
||||||
// Focus on the editbar.
|
// Focus on the editbar.
|
||||||
// -- TODO: Move Focus back to previous state (we know it so we can use it)
|
// -- TODO: Move Focus back to previous state (we know it so we can use it)
|
||||||
const firstEditbarElement = window.$('#editbar')
|
const firstEditbarElement = parent.parent.$('#editbar')
|
||||||
.children('ul').first().children().first()
|
.children('ul').first().children().first()
|
||||||
.children().first().children().first();
|
.children().first().children().first();
|
||||||
$(this).trigger('blur');
|
$(this).trigger('blur');
|
||||||
|
@ -2603,8 +2603,8 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
padShortcutEnabled.altC) {
|
padShortcutEnabled.altC) {
|
||||||
// Alt c focuses on the Chat window
|
// Alt c focuses on the Chat window
|
||||||
$(this).trigger('blur');
|
$(this).trigger('blur');
|
||||||
window.chat.show();
|
parent.parent.chat.show();
|
||||||
window.$('#chatinput').trigger('focus');
|
parent.parent.$('#chatinput').trigger('focus');
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
}
|
}
|
||||||
if (!specialHandled && type === 'keydown' &&
|
if (!specialHandled && type === 'keydown' &&
|
||||||
|
@ -2626,12 +2626,12 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
if (authorId) authorIds.add(authorId);
|
if (authorId) authorIds.add(authorId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const idToName = new Map(window.pad.userList().map((a) => [a.userId, a.name]));
|
const idToName = new Map(parent.parent.pad.userList().map((a) => [a.userId, a.name]));
|
||||||
const myId = window.clientVars.userId;
|
const myId = parent.parent.clientVars.userId;
|
||||||
const authors =
|
const authors =
|
||||||
[...authorIds].map((id) => id === myId ? 'me' : idToName.get(id) || 'unknown');
|
[...authorIds].map((id) => id === myId ? 'me' : idToName.get(id) || 'unknown');
|
||||||
|
|
||||||
window.$.gritter.add({
|
parent.parent.$.gritter.add({
|
||||||
title: 'Line Authors',
|
title: 'Line Authors',
|
||||||
text:
|
text:
|
||||||
authors.length === 0 ? 'No author information is available'
|
authors.length === 0 ? 'No author information is available'
|
||||||
|
@ -2680,7 +2680,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
specialHandled = true;
|
specialHandled = true;
|
||||||
|
|
||||||
// close all gritters when the user hits escape key
|
// close all gritters when the user hits escape key
|
||||||
window.$.gritter.removeAll();
|
parent.parent.$.gritter.removeAll();
|
||||||
}
|
}
|
||||||
if (!specialHandled && isTypeForCmdKey &&
|
if (!specialHandled && isTypeForCmdKey &&
|
||||||
/* Do a saved revision on ctrl S */
|
/* Do a saved revision on ctrl S */
|
||||||
|
@ -2688,13 +2688,13 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
!evt.altKey &&
|
!evt.altKey &&
|
||||||
padShortcutEnabled.cmdS) {
|
padShortcutEnabled.cmdS) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
const originalBackground = window.$('#revisionlink').css('background');
|
const originalBackground = parent.parent.$('#revisionlink').css('background');
|
||||||
window.$('#revisionlink').css({background: 'lightyellow'});
|
parent.parent.$('#revisionlink').css({background: 'lightyellow'});
|
||||||
scheduler.setTimeout(() => {
|
scheduler.setTimeout(() => {
|
||||||
window.$('#revisionlink').css({background: originalBackground});
|
parent.parent.$('#revisionlink').css({background: originalBackground});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
/* The parent.parent part of this is BAD and I feel bad.. It may break something */
|
||||||
window.pad.collabClient.sendMessage({type: 'SAVE_REVISION'});
|
parent.parent.pad.collabClient.sendMessage({type: 'SAVE_REVISION'});
|
||||||
specialHandled = true;
|
specialHandled = true;
|
||||||
}
|
}
|
||||||
if (!specialHandled && isTypeForSpecialKey &&
|
if (!specialHandled && isTypeForSpecialKey &&
|
||||||
|
@ -3475,7 +3475,7 @@ function Ace2Inner(editorInfo, cssManagers) {
|
||||||
// use that for displaying the side div line number inline with the first line
|
// use that for displaying the side div line number inline with the first line
|
||||||
// of content -- This is used in ep_headings, ep_font_size etc. where the line
|
// of content -- This is used in ep_headings, ep_font_size etc. where the line
|
||||||
// height is increased.
|
// height is increased.
|
||||||
const elementStyle = window.getComputedStyle(docLine.firstElementChild);
|
const elementStyle = window.getComputedStyle(docLine.firstChild);
|
||||||
const lineHeight = parseInt(elementStyle.getPropertyValue('line-height'));
|
const lineHeight = parseInt(elementStyle.getPropertyValue('line-height'));
|
||||||
const marginBottom = parseInt(elementStyle.getPropertyValue('margin-bottom'));
|
const marginBottom = parseInt(elementStyle.getPropertyValue('margin-bottom'));
|
||||||
lineHeights.push(lineHeight + marginBottom);
|
lineHeights.push(lineHeight + marginBottom);
|
||||||
|
|
|
@ -186,7 +186,7 @@ const loadBroadcastJS = (socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
|
||||||
|
|
||||||
mutateTextLines(changeset, padContents);
|
mutateTextLines(changeset, padContents);
|
||||||
padContents.currentRevision = revision;
|
padContents.currentRevision = revision;
|
||||||
padContents.currentTime += timeDelta;
|
padContents.currentTime += timeDelta * 1000;
|
||||||
|
|
||||||
updateTimer();
|
updateTimer();
|
||||||
|
|
||||||
|
@ -299,7 +299,7 @@ const loadBroadcastJS = (socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
|
||||||
// Loading changeset history for new revision
|
// Loading changeset history for new revision
|
||||||
loadChangesetsForRevision(newRevision, update);
|
loadChangesetsForRevision(newRevision, update);
|
||||||
// Loading changeset history for old revision (to make diff between old and new revision)
|
// Loading changeset history for old revision (to make diff between old and new revision)
|
||||||
loadChangesetsForRevision(padContents.currentRevision);
|
loadChangesetsForRevision(padContents.currentRevision - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const authors = _.map(padContents.getActiveAuthors(), (name) => authorData[name]);
|
const authors = _.map(padContents.getActiveAuthors(), (name) => authorData[name]);
|
||||||
|
|
|
@ -140,7 +140,7 @@ const makeChangesetTracker = (scheduler, apool, aceCallbacksProvider) => {
|
||||||
toSubmit = compose(submittedChangeset, userChangeset, apool);
|
toSubmit = compose(submittedChangeset, userChangeset, apool);
|
||||||
} else {
|
} else {
|
||||||
// Get my authorID
|
// Get my authorID
|
||||||
const authorId = window.pad.myUserInfo.userId;
|
const authorId = parent.parent.pad.myUserInfo.userId;
|
||||||
|
|
||||||
// Sanitize authorship: Replace all author attributes with this user's author ID in case the
|
// Sanitize authorship: Replace all author attributes with this user's author ID in case the
|
||||||
// text was copied from another author.
|
// text was copied from another author.
|
||||||
|
|
|
@ -75,20 +75,11 @@ const padeditor = (() => {
|
||||||
padutils.setCheckbox($('#options-rtlcheck'), ('rtl' === html10n.getDirection()));
|
padutils.setCheckbox($('#options-rtlcheck'), ('rtl' === html10n.getDirection()));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// font family change
|
// font family change
|
||||||
$('#viewfontmenu').on('change', () => {
|
$('#viewfontmenu').on('change', () => {
|
||||||
pad.changeViewOption('padFontFamily', $('#viewfontmenu').val());
|
pad.changeViewOption('padFontFamily', $('#viewfontmenu').val());
|
||||||
});
|
});
|
||||||
|
|
||||||
// delete pad
|
|
||||||
$('#delete-pad').on('click', () => {
|
|
||||||
if (window.confirm(html10n.get('pad.delete.confirm'))) {
|
|
||||||
pad.collabClient.sendMessage({type: 'PAD_DELETE', data:{padId: pad.getPadId()}});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Language
|
// Language
|
||||||
html10n.bind('localized', () => {
|
html10n.bind('localized', () => {
|
||||||
$('#languagemenu').val(html10n.getLanguage());
|
$('#languagemenu').val(html10n.getLanguage());
|
||||||
|
|
|
@ -186,7 +186,7 @@ const paduserlist = (() => {
|
||||||
const tr = input.closest('tr');
|
const tr = input.closest('tr');
|
||||||
if (tr.length > 0) {
|
if (tr.length > 0) {
|
||||||
const index = tr.parent().children().index(tr);
|
const index = tr.parent().children().index(tr);
|
||||||
if (index >= 0 && rowsPresent.length > index) {
|
if (index >= 0) {
|
||||||
const userId = rowsPresent[index].data.id;
|
const userId = rowsPresent[index].data.id;
|
||||||
rowManagerMakeNameEditor($(this), userId);
|
rowManagerMakeNameEditor($(this), userId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,18 +162,23 @@ export const install = async (pluginName: string, cb:Function|null = null) => {
|
||||||
export let availablePlugins:MapArrayType<PackageInfo>|null = null;
|
export let availablePlugins:MapArrayType<PackageInfo>|null = null;
|
||||||
let cacheTimestamp = 0;
|
let cacheTimestamp = 0;
|
||||||
|
|
||||||
export const getAvailablePlugins = async (maxCacheAge: number | false) => {
|
export const getAvailablePlugins = (maxCacheAge: number|false) => {
|
||||||
const nowTimestamp = Math.round(Date.now() / 1000);
|
const nowTimestamp = Math.round(Date.now() / 1000);
|
||||||
|
|
||||||
|
return new Promise<MapArrayType<PackageInfo>>(async (resolve, reject) => {
|
||||||
// check cache age before making any request
|
// check cache age before making any request
|
||||||
if (availablePlugins && maxCacheAge && (nowTimestamp - cacheTimestamp) <= maxCacheAge) {
|
if (availablePlugins && maxCacheAge && (nowTimestamp - cacheTimestamp) <= maxCacheAge) {
|
||||||
return availablePlugins;
|
return resolve(availablePlugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
const pluginsLoaded: AxiosResponse<MapArrayType<PackageInfo>> = await axios.get(`${settings.updateServer}/plugins.json`, {headers})
|
await axios.get('https://static.etherpad.org/plugins.json', {headers})
|
||||||
|
.then((pluginsLoaded:AxiosResponse<MapArrayType<PackageInfo>>) => {
|
||||||
availablePlugins = pluginsLoaded.data;
|
availablePlugins = pluginsLoaded.data;
|
||||||
cacheTimestamp = nowTimestamp;
|
cacheTimestamp = nowTimestamp;
|
||||||
return availablePlugins;
|
resolve(availablePlugins);
|
||||||
|
})
|
||||||
|
.catch(async (err) => reject(err));
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -206,7 +211,4 @@ export const search = (searchTerm: string, maxCacheAge: number) => getAvailableP
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
).catch((err)=>{
|
);
|
||||||
logger.error(`Error searching plugins: ${err}`);
|
|
||||||
return {} as MapArrayType<PackageInfo>;
|
|
||||||
});
|
|
||||||
|
|
|
@ -15,8 +15,7 @@ class Scroll {
|
||||||
// DOM reference
|
// DOM reference
|
||||||
this.outerWin = outerWin;
|
this.outerWin = outerWin;
|
||||||
this.doc = this.outerWin.contentDocument!;
|
this.doc = this.outerWin.contentDocument!;
|
||||||
this.rootDocument = document;
|
this.rootDocument = parent.parent.document;
|
||||||
console.log(this.rootDocument)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary(rep: RepModel, isScrollableEvent: boolean, innerHeight: number) {
|
scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary(rep: RepModel, isScrollableEvent: boolean, innerHeight: number) {
|
||||||
|
@ -113,7 +112,7 @@ class Scroll {
|
||||||
};
|
};
|
||||||
|
|
||||||
_getEditorPositionTop() {
|
_getEditorPositionTop() {
|
||||||
const editor = document.getElementsByTagName('iframe');
|
const editor = parent.document.getElementsByTagName('iframe');
|
||||||
const editorPositionTop = editor[0].offsetTop;
|
const editorPositionTop = editor[0].offsetTop;
|
||||||
return editorPositionTop;
|
return editorPositionTop;
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,7 +24,7 @@ const connect = (etherpadBaseUrl, namespace = '/', options = {}) => {
|
||||||
let socketOptions = {
|
let socketOptions = {
|
||||||
path: socketioUrl.pathname,
|
path: socketioUrl.pathname,
|
||||||
upgrade: true,
|
upgrade: true,
|
||||||
transports: ['polling', 'websocket'],
|
transports: ['websocket'],
|
||||||
};
|
};
|
||||||
socketOptions = Object.assign(options, socketOptions);
|
socketOptions = Object.assign(options, socketOptions);
|
||||||
|
|
||||||
|
|
|
@ -192,14 +192,6 @@ export type ClientSaveRevisionMessage = {
|
||||||
type: 'SAVE_REVISION'
|
type: 'SAVE_REVISION'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export type PadDeleteMessage = {
|
|
||||||
type: 'PAD_DELETE'
|
|
||||||
data: {
|
|
||||||
padId: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GetChatMessageMessage = {
|
export type GetChatMessageMessage = {
|
||||||
type: 'GET_CHAT_MESSAGES',
|
type: 'GET_CHAT_MESSAGES',
|
||||||
start: number,
|
start: number,
|
||||||
|
@ -291,7 +283,7 @@ export type ChangesetRequestMessage = {
|
||||||
|
|
||||||
export type CollabroomMessage = {
|
export type CollabroomMessage = {
|
||||||
type: 'COLLABROOM'
|
type: 'COLLABROOM'
|
||||||
data: ClientSendUserInfoUpdate | ClientUserChangesMessage | ChatMessageMessage | GetChatMessageMessage | ClientSaveRevisionMessage | ClientMessageMessage | PadDeleteMessage
|
data: ClientSendUserInfoUpdate | ClientUserChangesMessage | ChatMessageMessage | GetChatMessageMessage | ClientSaveRevisionMessage | ClientMessageMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ClientVarMessage = | ClientVarData | ClientDisconnectedMessage | ClientReadyMessage| ChangesetRequestMessage | CollabroomMessage | CustomMessage
|
export type ClientVarMessage = | ClientVarData | ClientDisconnectedMessage | ClientReadyMessage| ChangesetRequestMessage | CollabroomMessage | CustomMessage
|
||||||
|
|
2
src/static/js/vendors/html10n.ts
vendored
2
src/static/js/vendors/html10n.ts
vendored
|
@ -538,7 +538,7 @@ export class Html10n {
|
||||||
for (let i=0, n=langs.length; i < n; i++) {
|
for (let i=0, n=langs.length; i < n; i++) {
|
||||||
lang = langs[i]
|
lang = langs[i]
|
||||||
if(!lang) continue;
|
if(!lang) continue;
|
||||||
if(!langs.includes(lang)) {// uh, we don't have this lang availbable..
|
if(!(lang in langs)) {// uh, we don't have this lang availbable..
|
||||||
// then check for related langs
|
// then check for related langs
|
||||||
if(~lang.indexOf('-') != -1) {
|
if(~lang.indexOf('-') != -1) {
|
||||||
lang = lang.split('-')[0];
|
lang = lang.split('-')[0];
|
||||||
|
|
|
@ -81,7 +81,3 @@
|
||||||
.skin-variant-container {
|
.skin-variant-container {
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
|
|
||||||
#delete-pad {
|
|
||||||
background-color: #ff7b72;
|
|
||||||
}
|
|
||||||
|
|
|
@ -164,10 +164,10 @@
|
||||||
</p>
|
</p>
|
||||||
<% e.end_block(); %>
|
<% e.end_block(); %>
|
||||||
</div>
|
</div>
|
||||||
<button data-l10n-id="pad.settings.delete" id="delete-pad">Delete pad</button>
|
|
||||||
<h2 data-l10n-id="pad.settings.about">About</h2>
|
<h2 data-l10n-id="pad.settings.about">About</h2>
|
||||||
<span data-l10n-id="pad.settings.poweredBy">Powered by</span>
|
<span data-l10n-id="pad.settings.poweredBy">Powered by</span>
|
||||||
<a href="https://etherpad.org" target="_blank" referrerpolicy="no-referrer" rel="noopener">Etherpad</a>
|
<a href="https://etherpad.org">Etherpad</a>
|
||||||
<% if (settings.exposeVersion) { %>(commit <%=settings.getGitCommit()%>)<% } %>
|
<% if (settings.exposeVersion) { %>(commit <%=settings.getGitCommit()%>)<% } %>
|
||||||
</div></div>
|
</div></div>
|
||||||
|
|
||||||
|
|
|
@ -250,19 +250,6 @@ export const sendUserChanges = async (socket:any, data:any) => await sendMessage
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Convenience function to send a delete pad request.
|
|
||||||
*/
|
|
||||||
export const sendPadDelete = async (socket:any, data:any) => await sendMessage(socket, {
|
|
||||||
type: 'PAD_DELETE',
|
|
||||||
component: 'pad',
|
|
||||||
data: {
|
|
||||||
padId: data.padId
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience function that waits for an ACCEPT_COMMIT message. Asserts that the new revision
|
* Convenience function that waits for an ACCEPT_COMMIT message. Asserts that the new revision
|
||||||
* matches the expected revision.
|
* matches the expected revision.
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ep_etherpad-lite": "workspace:../src",
|
"ep_etherpad-lite": "workspace:../src",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.5.4",
|
||||||
"vite": "^6.3.2"
|
"vite": "^5.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -385,7 +385,7 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<button data-l10n-id="pad.settings.delete">Delete pad</button>
|
|
||||||
<h2 data-l10n-id="pad.settings.about">About</h2>
|
<h2 data-l10n-id="pad.settings.about">About</h2>
|
||||||
<span data-l10n-id="pad.settings.poweredBy">Powered by</span>
|
<span data-l10n-id="pad.settings.poweredBy">Powered by</span>
|
||||||
<a href="https://etherpad.org">Etherpad</a>
|
<a href="https://etherpad.org">Etherpad</a>
|
||||||
|
|
2
var/js/.gitignore
vendored
Normal file
2
var/js/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*.js
|
||||||
|
*.map
|
Loading…
Add table
Add a link
Reference in a new issue