Compare commits
7 Commits
develop
...
npm-v-test
Author | SHA1 | Date |
---|---|---|
John McLear | 29e8915364 | |
webzwo0i | b55a9a2411 | |
webzwo0i | b858f5dd62 | |
webzwo0i | 3c9bcf4c4b | |
webzwo0i | 3a45aa1cb2 | |
webzwo0i | 8ddc26b7af | |
webzwo0i | 9089b8b973 |
|
@ -22,9 +22,9 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: 'npm'
|
||||
|
@ -38,7 +38,7 @@ jobs:
|
|||
sudo apt update
|
||||
sudo apt install -y --no-install-recommends libreoffice libreoffice-pdfimport
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
name: Install the ep_etherpad-lite package from ./src
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: Run the backend tests
|
||||
|
@ -59,9 +59,9 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: 'npm'
|
||||
|
@ -74,12 +74,13 @@ jobs:
|
|||
sudo add-apt-repository -y ppa:libreoffice/ppa
|
||||
sudo apt update
|
||||
sudo apt install -y --no-install-recommends libreoffice libreoffice-pdfimport
|
||||
-
|
||||
name: Install the ep_etherpad-lite package from ./src
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: Install Etherpad plugins
|
||||
# The --legacy-peer-deps flag is required to work around a bug in npm v7:
|
||||
# https://github.com/npm/cli/issues/2199
|
||||
run: >
|
||||
npm install --no-save --legacy-peer-deps
|
||||
npm install
|
||||
ep_align
|
||||
ep_author_hover
|
||||
ep_cursortrace
|
||||
|
@ -93,18 +94,6 @@ jobs:
|
|||
ep_spellcheck
|
||||
ep_subscript_and_superscript
|
||||
ep_table_of_contents
|
||||
# Etherpad core dependencies must be installed after installing the
|
||||
# plugin's dependencies, otherwise npm will try to hoist common
|
||||
# dependencies by removing them from src/node_modules and installing them
|
||||
# in the top-level node_modules. As of v6.14.10, npm's hoist logic appears
|
||||
# to be buggy, because it sometimes removes dependencies from
|
||||
# src/node_modules but fails to add them to the top-level node_modules.
|
||||
# Even if npm correctly hoists the dependencies, the hoisting seems to
|
||||
# confuse tools such as `npm outdated`, `npm update`, and some ESLint
|
||||
# rules.
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: Run the backend tests
|
||||
run: cd src && npm test
|
||||
|
@ -120,9 +109,9 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
|
@ -130,8 +119,11 @@ jobs:
|
|||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
name: Install the ep_etherpad-lite package from ./src
|
||||
run: src/bin/installOnWindows.bat
|
||||
-
|
||||
name: Run npm i once to make bin files available - why is this needed at all?
|
||||
run: npm i
|
||||
-
|
||||
name: Fix up the settings.json
|
||||
run: |
|
||||
|
@ -139,7 +131,7 @@ jobs:
|
|||
powershell -Command "(gc settings.json.holder) -replace '\"points\": 10', '\"points\": 1000' | Out-File -encoding ASCII settings.json"
|
||||
-
|
||||
name: Run the backend tests
|
||||
run: cd src && npm test
|
||||
run: cd src && npm run test-on-windows
|
||||
|
||||
withpluginsWindows:
|
||||
# run on pushes to any branch
|
||||
|
@ -153,21 +145,25 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install the ep_etherpad-lite package from ./src
|
||||
run: src/bin/installOnWindows.bat
|
||||
-
|
||||
name: Run npm i once to make bin files available - why is this needed at all?
|
||||
run: npm i
|
||||
-
|
||||
name: Install Etherpad plugins
|
||||
# The --legacy-peer-deps flag is required to work around a bug in npm
|
||||
# v7: https://github.com/npm/cli/issues/2199
|
||||
run: >
|
||||
npm install --no-save --legacy-peer-deps
|
||||
npm install
|
||||
ep_align
|
||||
ep_author_hover
|
||||
ep_cursortrace
|
||||
|
@ -181,18 +177,6 @@ jobs:
|
|||
ep_spellcheck
|
||||
ep_subscript_and_superscript
|
||||
ep_table_of_contents
|
||||
# Etherpad core dependencies must be installed after installing the
|
||||
# plugin's dependencies, otherwise npm will try to hoist common
|
||||
# dependencies by removing them from src/node_modules and installing them
|
||||
# in the top-level node_modules. As of v6.14.10, npm's hoist logic appears
|
||||
# to be buggy, because it sometimes removes dependencies from
|
||||
# src/node_modules but fails to add them to the top-level node_modules.
|
||||
# Even if npm correctly hoists the dependencies, the hoisting seems to
|
||||
# confuse tools such as `npm outdated`, `npm update`, and some ESLint
|
||||
# rules.
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installOnWindows.bat
|
||||
-
|
||||
name: Fix up the settings.json
|
||||
run: |
|
||||
|
@ -200,4 +184,4 @@ jobs:
|
|||
powershell -Command "(gc settings.json.holder) -replace '\"points\": 10', '\"points\": 1000' | Out-File -encoding ASCII settings.json"
|
||||
-
|
||||
name: Run the backend tests
|
||||
run: cd src && npm test
|
||||
run: cd src && npm run test-on-windows
|
||||
|
|
|
@ -23,7 +23,7 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
|
|
|
@ -15,6 +15,6 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@v3
|
||||
|
|
|
@ -17,17 +17,17 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Check out
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Set up QEMU
|
||||
if: github.event_name == 'push'
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v2
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v2
|
||||
-
|
||||
name: Build and export to Docker
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
load: true
|
||||
|
@ -36,7 +36,7 @@ jobs:
|
|||
cache-to: type=gha,mode=max
|
||||
-
|
||||
name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
cache: 'npm'
|
||||
|
@ -64,7 +64,7 @@ jobs:
|
|||
name: Docker meta
|
||||
if: github.event_name == 'push'
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: etherpad/etherpad
|
||||
tags: |
|
||||
|
@ -75,14 +75,14 @@ jobs:
|
|||
-
|
||||
name: Log in to Docker Hub
|
||||
if: github.event_name == 'push'
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
if: github.event_name == 'push'
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
|
|
@ -8,17 +8,26 @@ permissions:
|
|||
|
||||
jobs:
|
||||
withplugins:
|
||||
if: ${{ github.actor != 'dependabot[bot]' }}
|
||||
name: with plugins
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# node: [16, 19, 20] >> Disabled node 16 and 18 because they do not work
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node: [19, 20]
|
||||
node: [16, 18, 20]
|
||||
|
||||
steps:
|
||||
-
|
||||
name: Fail if Dependabot
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
run: |
|
||||
cat <<EOF >&2
|
||||
Frontend tests skipped because Dependabot can't access secrets.
|
||||
Manually re-run the jobs to run the frontend tests.
|
||||
For more information, see:
|
||||
https://github.blog/changelog/2021-02-19-github-actions-workflows-triggered-by-dependabot-prs-will-run-with-read-only-permissions/
|
||||
EOF
|
||||
exit 1
|
||||
-
|
||||
name: Generate Sauce Labs strings
|
||||
id: sauce_strings
|
||||
|
@ -27,9 +36,9 @@ jobs:
|
|||
printf %s\\n '::set-output name=tunnel_id::${{ github.run_id }}-${{ github.run_number }}-${{ github.job }}-node${{ matrix.node }}'
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: 'npm'
|
||||
|
@ -37,23 +46,11 @@ jobs:
|
|||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install etherpad plugins
|
||||
# We intentionally install an old ep_align version to test upgrades to
|
||||
# the minor version number. The --legacy-peer-deps flag is required to
|
||||
# work around a bug in npm v7: https://github.com/npm/cli/issues/2199
|
||||
run: npm install --no-save --legacy-peer-deps ep_align@0.2.27
|
||||
# Etherpad core dependencies must be installed after installing the
|
||||
# plugin's dependencies, otherwise npm will try to hoist common
|
||||
# dependencies by removing them from src/node_modules and installing them
|
||||
# in the top-level node_modules. As of v6.14.10, npm's hoist logic appears
|
||||
# to be buggy, because it sometimes removes dependencies from
|
||||
# src/node_modules but fails to add them to the top-level node_modules.
|
||||
# Even if npm correctly hoists the dependencies, the hoisting seems to
|
||||
# confuse tools such as `npm outdated`, `npm update`, and some ESLint
|
||||
# rules.
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
name: Install the ep_etherpad-lite package from ./src
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: Install etherpad plugins
|
||||
run: npm install ep_align@0.2.27
|
||||
-
|
||||
name: Install etherpad plugins
|
||||
run: rm -Rf node_modules/ep_align/static/tests/*
|
||||
|
@ -70,15 +67,11 @@ jobs:
|
|||
-
|
||||
name: increase maxHttpBufferSize
|
||||
run: "sed -i 's/\"maxHttpBufferSize\": 10000/\"maxHttpBufferSize\": 100000/' settings.json"
|
||||
-
|
||||
name: Disable import/export rate limiting
|
||||
run: |
|
||||
sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 1000000/' -i settings.json
|
||||
-
|
||||
name: Remove standard frontend test files, so only admin tests are run
|
||||
run: mv src/tests/frontend/specs/* /tmp && mv /tmp/admin*.js src/tests/frontend/specs
|
||||
-
|
||||
uses: saucelabs/sauce-connect-action@v2.3.5
|
||||
uses: saucelabs/sauce-connect-action@v2.3.4
|
||||
with:
|
||||
username: ${{ secrets.SAUCE_USERNAME }}
|
||||
accessKey: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||
|
|
|
@ -10,9 +10,18 @@ jobs:
|
|||
withoutplugins:
|
||||
name: without plugins
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.actor != 'dependabot[bot]' }}
|
||||
|
||||
steps:
|
||||
-
|
||||
name: Fail if Dependabot
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
run: |
|
||||
cat <<EOF >&2
|
||||
Frontend tests skipped because Dependabot can't access secrets.
|
||||
Manually re-run the jobs to run the frontend tests.
|
||||
For more information, see:
|
||||
https://github.blog/changelog/2021-02-19-github-actions-workflows-triggered-by-dependabot-prs-will-run-with-read-only-permissions/
|
||||
EOF
|
||||
exit 1
|
||||
-
|
||||
name: Generate Sauce Labs strings
|
||||
id: sauce_strings
|
||||
|
@ -21,9 +30,9 @@ jobs:
|
|||
printf %s\\n '::set-output name=tunnel_id::${{ github.run_id }}-${{ github.run_number }}-${{ github.job }}'
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
|
@ -31,7 +40,7 @@ jobs:
|
|||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
name: Install the ep_etherpad-lite package from ./src
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: export GIT_HASH to env
|
||||
|
@ -43,9 +52,9 @@ jobs:
|
|||
-
|
||||
name: Disable import/export rate limiting
|
||||
run: |
|
||||
sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 100000000/' -i settings.json
|
||||
sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 0/' -i settings.json
|
||||
-
|
||||
uses: saucelabs/sauce-connect-action@v2.3.5
|
||||
uses: saucelabs/sauce-connect-action@v2.3.4
|
||||
with:
|
||||
username: ${{ secrets.SAUCE_USERNAME }}
|
||||
accessKey: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||
|
@ -65,9 +74,18 @@ jobs:
|
|||
withplugins:
|
||||
name: with plugins
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.actor != 'dependabot[bot]' }}
|
||||
|
||||
steps:
|
||||
-
|
||||
name: Fail if Dependabot
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
run: |
|
||||
cat <<EOF >&2
|
||||
Frontend tests skipped because Dependabot can't access secrets.
|
||||
Manually re-run the jobs to run the frontend tests.
|
||||
For more information, see:
|
||||
https://github.blog/changelog/2021-02-19-github-actions-workflows-triggered-by-dependabot-prs-will-run-with-read-only-permissions/
|
||||
EOF
|
||||
exit 1
|
||||
-
|
||||
name: Generate Sauce Labs strings
|
||||
id: sauce_strings
|
||||
|
@ -76,21 +94,22 @@ jobs:
|
|||
printf %s\\n '::set-output name=tunnel_id::${{ github.run_id }}-${{ github.run_number }}-${{ github.job }}'
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install the ep_etherpad-lite package from ./src
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: Install Etherpad plugins
|
||||
# The --legacy-peer-deps flag is required to work around a bug in npm v7:
|
||||
# https://github.com/npm/cli/issues/2199
|
||||
run: >
|
||||
npm install --no-save --legacy-peer-deps
|
||||
npm install
|
||||
ep_align
|
||||
ep_author_hover
|
||||
ep_cursortrace
|
||||
|
@ -105,18 +124,6 @@ jobs:
|
|||
ep_spellcheck
|
||||
ep_subscript_and_superscript
|
||||
ep_table_of_contents
|
||||
# Etherpad core dependencies must be installed after installing the
|
||||
# plugin's dependencies, otherwise npm will try to hoist common
|
||||
# dependencies by removing them from src/node_modules and installing them
|
||||
# in the top-level node_modules. As of v6.20.10, npm's hoist logic appears
|
||||
# to be buggy, because it sometimes removes dependencies from
|
||||
# src/node_modules but fails to add them to the top-level node_modules.
|
||||
# Even if npm correctly hoists the dependencies, the hoisting seems to
|
||||
# confuse tools such as `npm outdated`, `npm update`, and some ESLint
|
||||
# rules.
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: export GIT_HASH to env
|
||||
id: environment
|
||||
|
@ -127,13 +134,13 @@ jobs:
|
|||
-
|
||||
name: Disable import/export rate limiting
|
||||
run: |
|
||||
sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 1000000/' -i settings.json
|
||||
sed -e '/^ *"importExportRateLimiting":/,/^ *\}/ s/"max":.*/"max": 0/' -i settings.json
|
||||
# XXX we should probably run all tests, because plugins could effect their results
|
||||
-
|
||||
name: Remove standard frontend test files, so only plugin tests are run
|
||||
run: rm src/tests/frontend/specs/*
|
||||
-
|
||||
uses: saucelabs/sauce-connect-action@v2.3.5
|
||||
uses: saucelabs/sauce-connect-action@v2.3.4
|
||||
with:
|
||||
username: ${{ secrets.SAUCE_USERNAME }}
|
||||
accessKey: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||
|
|
|
@ -18,9 +18,9 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
|
@ -29,7 +29,7 @@ jobs:
|
|||
src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install lockfile-lint
|
||||
run: npm install --no-save lockfile-lint --legacy-peer-deps
|
||||
run: npm install --no-save lockfile-lint
|
||||
-
|
||||
name: Run lockfile-lint on package-lock.json
|
||||
run: >
|
||||
|
@ -38,4 +38,6 @@ jobs:
|
|||
--allowed-hosts npm
|
||||
--allowed-schemes https:
|
||||
--allowed-schemes github:
|
||||
--allowed-urls github:mapbox/node-sqlite3#593c9d498be2510d286349134537e3bf89401c4a
|
||||
--allowed-schemes git+ssh:
|
||||
--allowed-schemes file:
|
||||
--allowed-urls git+ssh://git@github.com/mapbox/node-sqlite3.git#593c9d498be2510d286349134537e3bf89401c4a
|
||||
|
|
|
@ -18,9 +18,9 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
|
@ -28,7 +28,7 @@ jobs:
|
|||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
name: Install the ep_etherpad-lite package from ./src
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: Install etherpad-load-test
|
||||
|
@ -48,24 +48,25 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install the ep_etherpad-lite package from ./src
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: Install etherpad-load-test
|
||||
run: sudo npm install -g etherpad-load-test
|
||||
-
|
||||
name: Install etherpad plugins
|
||||
# The --legacy-peer-deps flag is required to work around a bug in npm v7:
|
||||
# https://github.com/npm/cli/issues/2199
|
||||
run: >
|
||||
npm install --no-save --legacy-peer-deps
|
||||
npm install
|
||||
ep_align
|
||||
ep_author_hover
|
||||
ep_cursortrace
|
||||
|
@ -78,18 +79,6 @@ jobs:
|
|||
ep_spellcheck
|
||||
ep_subscript_and_superscript
|
||||
ep_table_of_contents
|
||||
# Etherpad core dependencies must be installed after installing the
|
||||
# plugin's dependencies, otherwise npm will try to hoist common
|
||||
# dependencies by removing them from src/node_modules and installing them
|
||||
# in the top-level node_modules. As of v6.14.10, npm's hoist logic appears
|
||||
# to be buggy, because it sometimes removes dependencies from
|
||||
# src/node_modules but fails to add them to the top-level node_modules.
|
||||
# Even if npm correctly hoists the dependencies, the hoisting seems to
|
||||
# confuse tools such as `npm outdated`, `npm update`, and some ESLint
|
||||
# rules.
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: Run load test
|
||||
run: src/tests/frontend/travis/runnerLoadTest.sh 25 50
|
||||
|
@ -105,9 +94,9 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
|
@ -115,7 +104,7 @@ jobs:
|
|||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
name: Install the ep_etherpad-lite package from ./src
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: Install etherpad-load-test
|
||||
|
|
|
@ -18,9 +18,9 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
|
|
|
@ -22,23 +22,23 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Check out latest release
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: master
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
# FIXME after 1.9.2
|
||||
# After 1.9.2 is released, we need to remove '--legacy-peer-deps --no-save'
|
||||
-
|
||||
name: Install Etherpad plugins
|
||||
# The --legacy-peer-deps flag is required to work around a bug in npm
|
||||
# v7: https://github.com/npm/cli/issues/2199
|
||||
run: >
|
||||
npm install --no-save --legacy-peer-deps
|
||||
npm install --legacy-peer-deps --no-save
|
||||
ep_align
|
||||
ep_author_hover
|
||||
ep_cursortrace
|
||||
|
@ -52,22 +52,13 @@ jobs:
|
|||
ep_spellcheck
|
||||
ep_subscript_and_superscript
|
||||
ep_table_of_contents
|
||||
# Etherpad core dependencies must be installed after installing the
|
||||
# plugin's dependencies, otherwise npm will try to hoist common
|
||||
# dependencies by removing them from src/node_modules and installing them
|
||||
# in the top-level node_modules. As of v6.14.10, npm's hoist logic appears
|
||||
# to be buggy, because it sometimes removes dependencies from
|
||||
# src/node_modules but fails to add them to the top-level node_modules.
|
||||
# Even if npm correctly hoists the dependencies, the hoisting seems to
|
||||
# confuse tools such as `npm outdated`, `npm update`, and some ESLint
|
||||
# rules.
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: Run the backend tests
|
||||
run: cd src && npm test
|
||||
# Because actions/checkout@v4 is called with "ref: master" and without
|
||||
# Because actions/checkout@v3 is called with "ref: master" and without
|
||||
# "fetch-depth: 0", the local clone does not have the ${GITHUB_SHA}
|
||||
# commit. Fetch ${GITHUB_REF} to get the ${GITHUB_SHA} commit. Note that a
|
||||
# plain "git fetch" only fetches "normal" references (refs/heads/* and
|
||||
|
@ -83,17 +74,37 @@ jobs:
|
|||
# commit that merges the PR's source branch to its destination branch.
|
||||
run: git checkout "${GITHUB_SHA}"
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
name: Temporary remove ./node_modules; Remove this after 1.9.2 release
|
||||
run: rm -rf ./node_modules
|
||||
-
|
||||
name: Install the ep_etherpad-lite package from ./src
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: Install Etherpad plugins again; Remove this after 1.9.2 release
|
||||
run: >
|
||||
npm install
|
||||
ep_align
|
||||
ep_author_hover
|
||||
ep_cursortrace
|
||||
ep_font_size
|
||||
ep_hash_auth
|
||||
ep_headings2
|
||||
ep_image_upload
|
||||
ep_markdown
|
||||
ep_readonly_guest
|
||||
ep_set_title_on_pad
|
||||
ep_spellcheck
|
||||
ep_subscript_and_superscript
|
||||
ep_table_of_contents
|
||||
-
|
||||
name: Run the backend tests
|
||||
run: cd src && npm test
|
||||
-
|
||||
name: Install Cypress
|
||||
run: cd src && npm install cypress --legacy-peer-deps
|
||||
run: npm install cypress
|
||||
-
|
||||
name: Run Etherpad & Test Frontend
|
||||
run: |
|
||||
node src/node/server.js &
|
||||
curl --connect-timeout 10 --max-time 20 --retry 5 --retry-delay 10 --retry-max-time 60 --retry-connrefused http://127.0.0.1:9001/p/test
|
||||
./src/node_modules/cypress/bin/cypress run --config-file src/tests/frontend/cypress/cypress.config.js
|
||||
./node_modules/cypress/bin/cypress run --config-file src/tests/frontend/cypress/cypress.config.js
|
||||
|
|
|
@ -24,9 +24,9 @@ jobs:
|
|||
zip
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
|
@ -34,13 +34,16 @@ jobs:
|
|||
src/package-lock.json
|
||||
src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install all dependencies and symlink for ep_etherpad-lite
|
||||
name: Install the ep_etherpad-lite package from ./src
|
||||
shell: msys2 {0}
|
||||
run: src/bin/installDeps.sh
|
||||
-
|
||||
name: Run npm i once to make bin files available - why is this needed at all?
|
||||
run: npm i
|
||||
-
|
||||
name: Run the backend tests
|
||||
shell: msys2 {0}
|
||||
run: cd src && npm test
|
||||
run: cd src && npm run test-on-windows
|
||||
-
|
||||
name: Build the .zip
|
||||
shell: msys2 {0}
|
||||
|
@ -62,7 +65,7 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Download .zip
|
||||
uses: actions/download-artifact@v3
|
||||
|
@ -89,7 +92,7 @@ jobs:
|
|||
# run on pushes to any branch
|
||||
# run on PRs from external forks
|
||||
permissions:
|
||||
contents: write
|
||||
contents: none
|
||||
if: |
|
||||
(github.event_name != 'pull_request')
|
||||
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
|
||||
|
@ -106,7 +109,7 @@ jobs:
|
|||
name: Extract Etherpad
|
||||
run: 7z x etherpad-win.zip -oetherpad
|
||||
-
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'npm'
|
||||
|
@ -115,21 +118,11 @@ jobs:
|
|||
etherpad/src/bin/doc/package-lock.json
|
||||
-
|
||||
name: Install Cypress
|
||||
run: cd etherpad && cd src && npm install cypress --legacy-peer-deps
|
||||
run: cd etherpad && npm install cypress
|
||||
-
|
||||
name: Run Etherpad
|
||||
run: |
|
||||
cd etherpad
|
||||
node node_modules\ep_etherpad-lite\node\server.js &
|
||||
curl --connect-timeout 10 --max-time 20 --retry 5 --retry-delay 10 --retry-max-time 60 --retry-connrefused http://127.0.0.1:9001/p/test
|
||||
src\node_modules\cypress\bin\cypress run --config-file src\tests\frontendcypress\cypress.config.js
|
||||
# On release, upload windows zip to GitHub release tab
|
||||
-
|
||||
name: Rename to etherpad-lite-win.zip
|
||||
shell: powershell
|
||||
run: mv etherpad-win.zip etherpad-lite-win.zip
|
||||
- name: upload binaries to release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: ${{startsWith(github.ref, 'refs/tags/v') }}
|
||||
with:
|
||||
files: etherpad-lite-win.zip
|
||||
node_modules\cypress\bin\cypress run --config-file src\tests\frontendcypress\cypress.config.js
|
||||
|
|
10
.travis.yml
10
.travis.yml
|
@ -28,10 +28,8 @@ _install_libreoffice: &install_libreoffice >-
|
|||
sudo apt-get update &&
|
||||
sudo apt-get -y install libreoffice libreoffice-pdfimport
|
||||
|
||||
# The --legacy-peer-deps flag is required to work around a bug in npm v7:
|
||||
# https://github.com/npm/cli/issues/2199
|
||||
_install_plugins: &install_plugins >-
|
||||
npm install --no-save --legacy-peer-deps
|
||||
npm install
|
||||
ep_align
|
||||
ep_author_hover
|
||||
ep_cursortrace
|
||||
|
@ -64,12 +62,11 @@ jobs:
|
|||
- *install_libreoffice
|
||||
- *set_loglevel_warn
|
||||
- "src/bin/installDeps.sh"
|
||||
- "cd src && npm install && cd -"
|
||||
script:
|
||||
- "cd src && npm test"
|
||||
- name: "Test the Dockerfile"
|
||||
install:
|
||||
- "cd src && npm install && cd -"
|
||||
- "src/bin/installDeps.sh"
|
||||
script:
|
||||
- "docker build -t etherpad:test ."
|
||||
- "docker run -d -p 9001:9001 etherpad:test && sleep 3"
|
||||
|
@ -78,7 +75,6 @@ jobs:
|
|||
install:
|
||||
- *set_loglevel_warn
|
||||
- "src/bin/installDeps.sh"
|
||||
- "cd src && npm install && cd -"
|
||||
- "npm install -g etherpad-load-test"
|
||||
script:
|
||||
- "src/tests/frontend/travis/runnerLoadTest.sh"
|
||||
|
@ -112,7 +108,7 @@ jobs:
|
|||
- "cd src && npm test"
|
||||
- name: "Test the Dockerfile"
|
||||
install:
|
||||
- "cd src && npm install && cd -"
|
||||
- "src/bin/installDeps.sh"
|
||||
script:
|
||||
- "docker build -t etherpad:test ."
|
||||
- "docker run -d -p 9001:9001 etherpad:test && sleep 3"
|
||||
|
|
65
CHANGELOG.md
65
CHANGELOG.md
|
@ -1,53 +1,34 @@
|
|||
# 1.9.4
|
||||
# Next release
|
||||
|
||||
### Compability changes
|
||||
#### Note for admins
|
||||
Etherpad does no longer store it's dependencies in ./src/node_modules by default. Also, Etherpad now
|
||||
stores installed plugins in a package.json file in the root directory and no longer requires quirks
|
||||
like `--legacy-peer-deps` or `--no-save` when invoking npm during plugin installation.
|
||||
|
||||
* Log4js has been updated to the latest version. As it involved a bump of 6 major version.
|
||||
A lot has changed since then. Most notably the console appender has been deprecated. You can find out more about it [here](https://github.com/log4js-node/log4js-node)
|
||||
When you're updating, it's best to use the `./src/bin/installDeps.sh` script. It will `npm link` the
|
||||
src directory, using the package.json file in ./src. This will create the well-known symlink ep_etherpad-lite
|
||||
in ./node_modules, that we've been using for years. However, this will also add a dependency in ./package.json.
|
||||
|
||||
### Notable enhancements and fixes
|
||||
`./src/bin/installDeps.sh` will fail, if you have no ./package.json or ./package-lock.json and your
|
||||
./node_modules directory is not empty, as this is an indicator of installed plugins. You need to remove
|
||||
./node_modules and install all your plugins in the next step.
|
||||
|
||||
* Fix for MySQL: The logger calls were incorrectly configured leading to a crash when e.g. somebody uses a different encoding than standard MySQL encoding.
|
||||
`./src/bin/installDeps.sh` will remove any existing directories in `./src/node_modules`.
|
||||
|
||||
# 1.9.3
|
||||
|
||||
### Compability changes
|
||||
|
||||
* express-rate-limit has been bumped to 7.0.0: This involves the breaking change that "max: 0"
|
||||
in the importExportRateLimiting is set to always trigger. So set it to your desired value.
|
||||
If you haven't changed that value in the settings.json you are all set.
|
||||
|
||||
### Notable enhancements and fixes
|
||||
|
||||
* Bugfixes
|
||||
* Fix etherpad crashing with mongodb database
|
||||
|
||||
* Enhancements
|
||||
* Add surrealdb database support. You can find out more about this database [here](https://surrealdb.com).
|
||||
* Make sqlite faster: The sqlite library has been switched to better-sqlite3. This should lead to better performance.
|
||||
|
||||
# 1.9.2
|
||||
|
||||
### Notable enhancements and fixes
|
||||
|
||||
* Security
|
||||
* Enable session key rotation: This setting can be enabled in the settings.json. It changes the signing key for the cookie authentication in a fixed interval.
|
||||
|
||||
* Bugfixes
|
||||
* Fix appendRevision when creating a new pad via the API without a text.
|
||||
After running `./src/bin/installDeps.sh`, install your plugins with `npm i ep_plugin1 ep_plugin2...` or via
|
||||
`/admin/plugins`.
|
||||
|
||||
|
||||
* Enhancements
|
||||
* Bump JQuery to version 3.7
|
||||
* Update elasticsearch connector to version 8
|
||||
#### Note for plugin authors
|
||||
You can no longer depend on core's dependencies via `require('ep_etherpad-lite/node_modules/$dep')`.
|
||||
Please run `src/bin/checkPlugins.sh` or manually change to `require('$dep')`. We don't recommend
|
||||
that you rely on Etherpad to include specific dependencies in the future. So it's best if you add
|
||||
the dependency in your package.json.
|
||||
For convenience we have added symlinks in ./src/node_modules for the following dependencies:
|
||||
async, cheerio, express, formidable, log4js and supertest.
|
||||
Please note that those symlinks will be removed in a future version, so we strongly recommend that
|
||||
you adapt your require statements.
|
||||
|
||||
### Compatibility changes
|
||||
|
||||
* No compability changes as JQuery maintains excellent backwards compatibility.
|
||||
|
||||
#### For plugin authors
|
||||
|
||||
* Please update to JQuery 3.7. There is an excellent deprecation guide over [here](https://api.jquery.com/category/deprecated/). Version 3.1 to 3.7 are relevant for the upgrade.
|
||||
|
||||
# 1.9.1
|
||||
|
||||
|
|
28
Dockerfile
28
Dockerfile
|
@ -17,9 +17,6 @@ RUN \
|
|||
}
|
||||
ENV TIMEZONE=${TIMEZONE}
|
||||
|
||||
# Control the configuration file to be copied into the container.
|
||||
ARG SETTINGS=./settings.json.docker
|
||||
|
||||
# plugins to install while building the container. By default no plugins are
|
||||
# installed.
|
||||
# If given a value, it has to be a space-separated, quoted list of plugin names.
|
||||
|
@ -81,7 +78,7 @@ RUN \
|
|||
apk add \
|
||||
ca-certificates \
|
||||
git \
|
||||
${INSTALL_ABIWORD:+abiword abiword-plugin-command} \
|
||||
${INSTALL_ABIWORD:+abiword} \
|
||||
${INSTALL_SOFFICE:+libreoffice openjdk8-jre libreoffice-common}
|
||||
|
||||
USER etherpad
|
||||
|
@ -90,30 +87,23 @@ WORKDIR "${EP_DIR}"
|
|||
|
||||
COPY --chown=etherpad:etherpad ./ ./
|
||||
|
||||
# Plugins must be installed before installing Etherpad's dependencies, otherwise
|
||||
# npm will try to hoist common dependencies by removing them from
|
||||
# src/node_modules and installing them in the top-level node_modules. As of
|
||||
# v6.14.10, npm's hoist logic appears to be buggy, because it sometimes removes
|
||||
# dependencies from src/node_modules but fails to add them to the top-level
|
||||
# node_modules. Even if npm correctly hoists the dependencies, the hoisting
|
||||
# seems to confuse tools such as `npm outdated`, `npm update`, and some ESLint
|
||||
# rules.
|
||||
RUN npm config set prefix "${EP_DIR}/.npm-packages"
|
||||
|
||||
RUN ./src/bin/installDeps.sh
|
||||
|
||||
RUN { [ -z "${ETHERPAD_PLUGINS}" ] || \
|
||||
npm install --no-save --legacy-peer-deps ${ETHERPAD_PLUGINS}; } && \
|
||||
src/bin/installDeps.sh && \
|
||||
npm install ${ETHERPAD_PLUGINS}; } && \
|
||||
rm -rf ~/.npm
|
||||
|
||||
# Copy the configuration file.
|
||||
COPY --chown=etherpad:etherpad ${SETTINGS} "${EP_DIR}"/settings.json
|
||||
COPY --chown=etherpad:etherpad ./settings.json.docker "${EP_DIR}"/settings.json
|
||||
|
||||
# Fix group permissions
|
||||
RUN chmod -R g=u .
|
||||
|
||||
USER root
|
||||
RUN cd src && npm link
|
||||
USER etherpad
|
||||
|
||||
HEALTHCHECK --interval=20s --timeout=3s CMD ["etherpad-healthcheck"]
|
||||
HEALTHCHECK --interval=20s --timeout=3s CMD ["./src/bin/etherpad-healthcheck"]
|
||||
|
||||
EXPOSE 9001
|
||||
CMD ["etherpad"]
|
||||
CMD ["./src/node/server.js"]
|
||||
|
|
|
@ -116,7 +116,7 @@ following:
|
|||
|
||||
### Docker container
|
||||
|
||||
Find [here](doc/docker.adoc) information on running Etherpad in a container.
|
||||
Find [here](doc/docker.md) information on running Etherpad in a container.
|
||||
|
||||
## Plugins
|
||||
|
||||
|
@ -140,9 +140,7 @@ Alternatively, you can install plugins from the command line:
|
|||
|
||||
```sh
|
||||
cd /path/to/etherpad-lite
|
||||
# The `--no-save` and `--legacy-peer-deps` arguments are necessary to work
|
||||
# around npm quirks.
|
||||
npm install --no-save --legacy-peer-deps ep_${plugin_name}
|
||||
npm install ep_${plugin_name}
|
||||
```
|
||||
|
||||
Also see [the plugin wiki
|
||||
|
@ -154,7 +152,7 @@ Run the following command in your Etherpad folder to get all of the features
|
|||
visible in the above demo gif:
|
||||
|
||||
```sh
|
||||
npm install --no-save --legacy-peer-deps \
|
||||
npm install \
|
||||
ep_align \
|
||||
ep_comments_page \
|
||||
ep_embedded_hyperlinks2 \
|
||||
|
|
|
@ -283,7 +283,7 @@ Things in context:
|
|||
This hook is called on the client side whenever a user joins or changes. This
|
||||
can be used to create notifications or an alternate user list.
|
||||
|
||||
=== chatNewMessage
|
||||
=== `chatNewMessage`
|
||||
|
||||
Called from: `src/static/js/chat.js`
|
||||
|
||||
|
@ -319,7 +319,7 @@ Context properties:
|
|||
* `duration`: How long (in milliseconds) to display the gritter notification (0
|
||||
to disable).
|
||||
|
||||
=== chatSendMessage
|
||||
=== `chatSendMessage`
|
||||
|
||||
Called from: `src/static/js/chat.js`
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ Things in context:
|
|||
|
||||
If this hook returns an error, the callback to the install function gets an error, too. This seems useful for adding in features when a particular plugin is installed.
|
||||
|
||||
=== init_<plugin name>
|
||||
=== `init_<plugin name>`
|
||||
|
||||
Called from: `src/static/js/pluginfw/plugins.js`
|
||||
|
||||
|
@ -62,7 +62,7 @@ Context properties:
|
|||
* `logger`: An object with the following `console`-like methods: `debug`,
|
||||
`info`, `log`, `warn`, `error`.
|
||||
|
||||
=== expressPreSession
|
||||
=== `expressPreSession`
|
||||
|
||||
Called from: `src/node/hooks/express.js`
|
||||
|
||||
|
@ -92,7 +92,7 @@ exports.expressPreSession = async (hookName, {app}) => {
|
|||
};
|
||||
----
|
||||
|
||||
=== expressConfigure
|
||||
=== `expressConfigure`
|
||||
|
||||
Called from: `src/node/hooks/express.js`
|
||||
|
||||
|
@ -107,7 +107,7 @@ Context properties:
|
|||
* `app`: The Express https://expressjs.com/en/4x/api.html==app[Application]
|
||||
object.
|
||||
|
||||
=== expressCreateServer
|
||||
=== `expressCreateServer`
|
||||
|
||||
Called from: `src/node/hooks/express.js`
|
||||
|
||||
|
@ -210,7 +210,7 @@ Things in context:
|
|||
This hook gets called when the access to the concrete pad is being checked.
|
||||
Return `false` to deny access.
|
||||
|
||||
=== getAuthorId
|
||||
=== `getAuthorId`
|
||||
|
||||
Called from `src/node/db/AuthorManager.js`
|
||||
|
||||
|
@ -267,7 +267,7 @@ exports.getAuthorId = async (hookName, context) => {
|
|||
};
|
||||
----
|
||||
|
||||
=== padCreate
|
||||
=== `padCreate`
|
||||
|
||||
Called from: `src/node/db/Pad.js`
|
||||
|
||||
|
@ -279,7 +279,7 @@ Context properties:
|
|||
* `authorId`: The ID of the author who created the pad.
|
||||
* `author` (**deprecated**): Synonym of `authorId`.
|
||||
|
||||
=== padDefaultContent
|
||||
=== `padDefaultContent`
|
||||
|
||||
Called from `src/node/db/Pad.js`
|
||||
|
||||
|
@ -304,7 +304,7 @@ Context properties:
|
|||
be updated to match. Plugins must check the value of the `type` property
|
||||
before reading this value.
|
||||
|
||||
=== padLoad
|
||||
=== `padLoad`
|
||||
|
||||
Called from: `src/node/db/PadManager.js`
|
||||
|
||||
|
@ -315,7 +315,7 @@ Context properties:
|
|||
* `pad`: The Pad object.
|
||||
|
||||
[#_padupdate]
|
||||
=== padUpdate
|
||||
=== `padUpdate`
|
||||
|
||||
Called from: `src/node/db/Pad.js`
|
||||
|
||||
|
@ -329,7 +329,7 @@ Context properties:
|
|||
* `revs`: The index of the new revision.
|
||||
* `changeset`: The changeset of this revision (see <<_padupdate>>).
|
||||
|
||||
=== padCopy
|
||||
=== `padCopy`
|
||||
|
||||
Called from: `src/node/db/Pad.js`
|
||||
|
||||
|
@ -355,7 +355,7 @@ Usage examples:
|
|||
|
||||
* https://github.com/ether/ep_comments_page
|
||||
|
||||
=== padRemove
|
||||
=== `padRemove`
|
||||
|
||||
Called from: `src/node/db/Pad.js`
|
||||
|
||||
|
@ -370,7 +370,7 @@ Usage examples:
|
|||
|
||||
* https://github.com/ether/ep_comments_page
|
||||
|
||||
=== padCheck
|
||||
=== `padCheck`
|
||||
|
||||
Called from: `src/node/db/Pad.js`
|
||||
|
||||
|
@ -393,7 +393,7 @@ Things in context:
|
|||
|
||||
I have no idea what this is useful for, someone else will have to add this description.
|
||||
|
||||
=== preAuthorize
|
||||
=== `preAuthorize`
|
||||
|
||||
Called from: `src/node/hooks/express/webaccess.js`
|
||||
|
||||
|
@ -700,7 +700,7 @@ exports.authzFailure = (hookName, context, cb) => {
|
|||
};
|
||||
----
|
||||
|
||||
=== handleMessage
|
||||
=== `handleMessage`
|
||||
|
||||
Called from: `src/node/handler/PadMessageHandler.js`
|
||||
|
||||
|
@ -733,7 +733,7 @@ exports.handleMessage = async (hookName, {message, socket}) => {
|
|||
};
|
||||
----
|
||||
|
||||
=== handleMessageSecurity
|
||||
=== `handleMessageSecurity`
|
||||
|
||||
Called from: `src/node/handler/PadMessageHandler.js`
|
||||
|
||||
|
@ -819,7 +819,7 @@ exports.clientVars = (hookName, context, callback) => {
|
|||
};
|
||||
----
|
||||
|
||||
=== getLineHTMLForExport
|
||||
=== `getLineHTMLForExport`
|
||||
|
||||
Called from: `src/node/utils/ExportHtml.js`
|
||||
|
||||
|
@ -968,7 +968,7 @@ exports.exportHtmlAdditionalTagsWithData = function(hook, pad, cb){
|
|||
};
|
||||
----
|
||||
|
||||
=== exportEtherpadAdditionalContent
|
||||
=== `exportEtherpadAdditionalContent`
|
||||
|
||||
Called from `src/node/utils/ExportEtherpad.js` and
|
||||
`src/node/utils/ImportEtherpad.js`.
|
||||
|
@ -990,7 +990,7 @@ Example:
|
|||
exports.exportEtherpadAdditionalContent = () => ['comments'];
|
||||
----
|
||||
|
||||
=== exportEtherpad
|
||||
=== `exportEtherpad`
|
||||
|
||||
Called from `src/node/utils/ExportEtherpad.js`.
|
||||
|
||||
|
@ -1013,7 +1013,7 @@ Context properties:
|
|||
should not assume that it is either the pad's real writable ID or its
|
||||
read-only ID.
|
||||
|
||||
=== importEtherpad
|
||||
=== `importEtherpad`
|
||||
|
||||
Called from `src/node/utils/ImportEtherpad.js`.
|
||||
|
||||
|
@ -1032,7 +1032,7 @@ Context properties:
|
|||
modified.
|
||||
* `srcPadId`: The pad ID used for the pad-specific information in `data`.
|
||||
|
||||
=== import
|
||||
=== `import`
|
||||
|
||||
Called from: `src/node/handler/ImportHandler.js`
|
||||
|
||||
|
@ -1062,7 +1062,7 @@ exports.import = async (hookName, {fileEnding, ImportError}) => {
|
|||
};
|
||||
----
|
||||
|
||||
=== userJoin
|
||||
=== `userJoin`
|
||||
|
||||
Called from: `src/node/handler/PadMessageHandler.js`
|
||||
|
||||
|
@ -1086,7 +1086,7 @@ exports.userJoin = async (hookName, {authorId, displayName, padId}) => {
|
|||
};
|
||||
```
|
||||
|
||||
=== userLeave
|
||||
=== `userLeave`
|
||||
|
||||
Called from: `src/node/handler/PadMessageHandler.js`
|
||||
|
||||
|
@ -1110,7 +1110,7 @@ exports.userLeave = async (hookName, {author, padId}) => {
|
|||
};
|
||||
----
|
||||
|
||||
=== chatNewMessage
|
||||
=== `chatNewMessage`
|
||||
|
||||
Called from: `src/node/handler/PadMessageHandler.js`
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
body {
|
||||
border-top: solid #44b492 5pt;
|
||||
line-height: 150%;
|
||||
font-family: "Quicksand", sans-serif;
|
||||
line-height:150%;
|
||||
font-family: 'Quicksand',sans-serif;
|
||||
color: #313b4a;
|
||||
max-width: 1440px;
|
||||
max-width:800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
@ -12,25 +12,24 @@ a {
|
|||
color: #555;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
h1,h2 {
|
||||
color: #44b492;
|
||||
line-height: 100%;
|
||||
line-height:100%;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 48px;
|
||||
font-size: 48px ;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
h4{
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
h5{
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
|
@ -40,7 +39,7 @@ a:hover {
|
|||
|
||||
pre {
|
||||
background-color: #e0e0e0;
|
||||
padding: 20px;
|
||||
padding:20px;
|
||||
}
|
||||
|
||||
code {
|
||||
|
@ -51,9 +50,7 @@ img {
|
|||
max-width: 100%;
|
||||
}
|
||||
|
||||
table,
|
||||
th,
|
||||
td {
|
||||
table, th, td {
|
||||
text-align: left;
|
||||
border: 1px solid gray;
|
||||
border-collapse: collapse;
|
||||
|
@ -61,7 +58,7 @@ td {
|
|||
|
||||
th {
|
||||
padding: 0.5em;
|
||||
background: #eee;
|
||||
background: #EEE;
|
||||
}
|
||||
|
||||
td {
|
||||
|
|
|
@ -19,7 +19,7 @@ Cookies used by Etherpad.
|
|||
| Session
|
||||
| true
|
||||
| true
|
||||
| Session ID of the https://expressjs.com[Express web framework]. When Etherpad is behind a reverse proxy, and an administrator wants to use session stickiness, he may use this cookie. If you are behind a reverse proxy, please remember to set `trustProxy: true` in `settings.json`. Set in https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/node/hooks/express/webaccess.js#L131[webaccess.js#L131].
|
||||
| Session ID of the https://expressjs.com[Express web framework]. When Etherpad is behind a reverse proxy, and an administrator wants to use session stickiness, he may use this cookie. If you are behind a reverse proxy, please remember to set `trustProxy: true` in `settings.json`. Set in [webaccess.js#L131](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/node/hooks/express/webaccess.js#L131).
|
||||
|
||||
|
||||
|language
|
||||
|
@ -29,7 +29,7 @@ Cookies used by Etherpad.
|
|||
| Session
|
||||
| false
|
||||
| true
|
||||
| The language of the UI (e.g.: `en-GB`, `it`). Set in https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad_editor.js#L111[pad_editor.js#L111].
|
||||
| The language of the UI (e.g.: `en-GB`, `it`). Set in [pad_editor.js#L111](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad_editor.js#L111).
|
||||
|
||||
|
||||
|prefs / prefsHttp
|
||||
|
@ -39,7 +39,7 @@ Cookies used by Etherpad.
|
|||
| year 3000
|
||||
| false
|
||||
| true
|
||||
| Client-side preferences (e.g.: font family, chat always visible, show authorship colors, ...). Set in https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad_cookie.js#L49[pad_cookie.js#L49]. `prefs` is used if Etherpad is accessed over HTTPS, `prefsHttp` if accessed over HTTP. For more info see https://github.com/ether/etherpad-lite/issues/3179.
|
||||
| Client-side preferences (e.g.: font family, chat always visible, show authorship colors, ...). Set in [pad_cookie.js#L49](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad_cookie.js#L49). `prefs` is used if Etherpad is accessed over HTTPS, `prefsHttp` if accessed over HTTP. For more info see https://github.com/ether/etherpad-lite/issues/3179.
|
||||
|
||||
|
||||
|
||||
|
@ -50,7 +50,7 @@ Cookies used by Etherpad.
|
|||
| 60 days
|
||||
| false
|
||||
| true
|
||||
| A random token representing the author, of the form `t.randomstring_of_lenght_20`. The random string is generated by the client, at https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad.js#L55-L66[pad.js#L55-L66]. This cookie is always set by the client at https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad.js#L153-L158[pad.js#L153-L158] without any solicitation from the server. It is used for all the pads accessed via the web UI (not used for the HTTP API). On the server side, its value is accessed at https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/node/db/SecurityManager.js#L33[SecurityManager.js#L33].
|
||||
| A random token representing the author, of the form `t.randomstring_of_lenght_20`. The random string is generated by the client, at (https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad.js#L55-L66[pad.js#L55-L66]). This cookie is always set by the client (at (https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad.js#L153-L158[pad.js#L153-L158])) without any solicitation from the server. It is used for all the pads accessed via the web UI (not used for the HTTP API). On the server side, its value is accessed at [SecurityManager.js#L33](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/node/db/SecurityManager.js#L33).
|
||||
|===
|
||||
|
||||
For more info, visit the related discussion at https://github.com/ether/etherpad-lite/issues/3563.
|
||||
|
|
|
@ -20,13 +20,12 @@ Translations will be send back to us regularly and will eventually appear in the
|
|||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"pad.modals.connected": "Connecté.",
|
||||
"pad.modals.uderdup": "Ouvrir dans une nouvelle fenêtre.",
|
||||
"pad.toolbar.unindent.title": "Dèsindenter",
|
||||
"pad.toolbar.undo.title": "Annuler (Ctrl-Z)",
|
||||
"timeslider.pageTitle": "{{appTitle}} Curseur temporel",
|
||||
...
|
||||
{ "pad.modals.connected": "Connecté."
|
||||
, "pad.modals.uderdup": "Ouvrir dans une nouvelle fenêtre."
|
||||
, "pad.toolbar.unindent.title": "Dèsindenter"
|
||||
, "pad.toolbar.undo.title": "Annuler (Ctrl-Z)"
|
||||
, "timeslider.pageTitle": "{{appTitle}} Curseur temporel",
|
||||
, ...
|
||||
}
|
||||
----
|
||||
|
||||
|
@ -72,7 +71,7 @@ alert(window._('pad.chat'));
|
|||
----
|
||||
==== 2. Create translate files in the locales directory of your plugin
|
||||
|
||||
* The name of the file must be the language code of the language it contains translations for (see https://joker-x.github.io/languages4translatewiki/test/[supported lang codes]; e.g. en ? English, es ? Spanish...)
|
||||
* The name of the file must be the language code of the language it contains translations for (see https://joker-x.github.com/languages4translatewiki/test/[supported lang codes]; e.g. en ? English, es ? Spanish...)
|
||||
* The extension of the file must be `.json`
|
||||
* The default language is English, so your plugin should always provide `en.json`
|
||||
* In order to avoid naming conflicts, your message keys should start with the name of your plugin followed by a dot (see below)
|
||||
|
@ -81,8 +80,7 @@ alert(window._('pad.chat'));
|
|||
|
||||
[source, json]
|
||||
----
|
||||
{
|
||||
"ep_your-plugin.h1": "Heading 1"
|
||||
{ "ep_your-plugin.h1": "Heading 1"
|
||||
}
|
||||
----
|
||||
|
||||
|
@ -90,8 +88,7 @@ alert(window._('pad.chat'));
|
|||
|
||||
[source, json]
|
||||
----
|
||||
{
|
||||
"ep_your-plugin.h1": "Título 1"
|
||||
{ "ep_your-plugin.h1": "Título 1"
|
||||
}
|
||||
----
|
||||
|
||||
|
@ -106,9 +103,8 @@ For example, if you want to replace `Chat` with `Notes`, simply add...
|
|||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"ep_your-plugin.h1": "Heading 1",
|
||||
"pad.chat": "Notes"
|
||||
{ "ep_your-plugin.h1": "Heading 1"
|
||||
, "pad.chat": "Notes"
|
||||
}
|
||||
----
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@ execute its own functionality based on these events.
|
|||
Publicly available plugins can be found in the npm registry (see
|
||||
<https://npmjs.org>). Etherpad's naming convention for plugins is to prefix your
|
||||
plugins with `ep_`. So, e.g. it's `ep_flubberworms`. Thus you can install
|
||||
plugins from npm, using `npm install --no-save --legacy-peer-deps
|
||||
ep_flubberworm` in Etherpad's root directory.
|
||||
plugins from npm, using `npm install ep_flubberworm` in Etherpad's root directory.
|
||||
|
||||
You can also browse to `http://yourEtherpadInstan.ce/admin/plugins`, which will
|
||||
list all installed plugins and those available on npm. It even provides
|
||||
|
@ -64,7 +63,7 @@ translations into `locales/`, though, in order to have them integrated. (See
|
|||
Your plugin definition goes into `ep.json`. In this file you register your hook
|
||||
functions, indicate the parts of your plugin and the order of execution. (A
|
||||
documentation of all available events to hook into can be found in chapter
|
||||
<<Server-side hooks>>.)
|
||||
[hooks](#all_hooks).)
|
||||
|
||||
[source,json]
|
||||
----
|
||||
|
@ -88,7 +87,7 @@ A hook function registration maps a hook name to a hook function specification.
|
|||
The hook function specification looks like `ep_example/file.js:functionName`. It
|
||||
consists of two parts separated by a colon: a module name to `require()` and the
|
||||
name of a function exported by the named module. See
|
||||
https://nodejs.org/docs/latest/api/modules.html#modules_module_exports[`module.exports`]
|
||||
[`module.exports`](https://nodejs.org/docs/latest/api/modules.html#modules_module_exports)
|
||||
for how to export a function.
|
||||
|
||||
For the module name you can omit the `.js` suffix, and if the file is `index.js`
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../src
|
|
@ -207,15 +207,15 @@
|
|||
|
||||
"dbType": "${DB_TYPE:dirty}",
|
||||
"dbSettings": {
|
||||
"host": "${DB_HOST:undefined}",
|
||||
"port": "${DB_PORT:undefined}",
|
||||
"database": "${DB_NAME:undefined}",
|
||||
"user": "${DB_USER:undefined}",
|
||||
"password": "${DB_PASS:undefined}",
|
||||
"charset": "${DB_CHARSET:undefined}",
|
||||
"filename": "${DB_FILENAME:var/dirty.db}",
|
||||
"host": "${DB_HOST:undefined}",
|
||||
"port": "${DB_PORT:undefined}",
|
||||
"database": "${DB_NAME:undefined}",
|
||||
"user": "${DB_USER:undefined}",
|
||||
"password": "${DB_PASS:undefined}",
|
||||
"charset": "${DB_CHARSET:undefined}",
|
||||
"filename": "${DB_FILENAME:var/dirty.db}",
|
||||
"collection": "${DB_COLLECTION:undefined}",
|
||||
"url": "${DB_URL:undefined}"
|
||||
"url": "${DB_URL:undefined}"
|
||||
},
|
||||
|
||||
/*
|
||||
|
|
|
@ -20,7 +20,7 @@ try cd "${workdir}"
|
|||
[ -f src/package.json ] || fatal "failed to cd to etherpad root directory"
|
||||
|
||||
# See https://github.com/msys2/MSYS2-packages/issues/1216
|
||||
export MSYSTEM=winsymlinks:lnk
|
||||
export MSYS=winsymlinks:lnk
|
||||
|
||||
OUTPUT=${workdir}/etherpad-win.zip
|
||||
|
||||
|
@ -29,12 +29,10 @@ trap 'exit 1' HUP INT TERM
|
|||
trap 'log "cleaning up..."; try cd / && try rm -rf "${TMP_FOLDER}"' EXIT
|
||||
|
||||
log "create a clean environment in $TMP_FOLDER..."
|
||||
try export GIT_WORK_TREE=${TMP_FOLDER}; git checkout HEAD -f \
|
||||
try git archive --format=tar HEAD | (try cd "${TMP_FOLDER}" && try tar xf -) \
|
||||
|| fatal "failed to copy etherpad to temporary folder"
|
||||
try mkdir "${TMP_FOLDER}"/.git
|
||||
try git rev-parse HEAD >${TMP_FOLDER}/.git/HEAD
|
||||
try cp -r ./src/node_modules "${TMP_FOLDER}"/src/node_modules
|
||||
|
||||
try cd "${TMP_FOLDER}"
|
||||
[ -f src/package.json ] || fatal "failed to copy etherpad to temporary folder"
|
||||
|
||||
|
@ -48,6 +46,9 @@ try ./src/bin/installDeps.sh
|
|||
log "copy the windows settings template..."
|
||||
try cp settings.json.template settings.json
|
||||
|
||||
log "Because this is a production build, we delete supertest"
|
||||
try rm src/node_modules/supertest
|
||||
|
||||
log "resolve symbolic links..."
|
||||
try cp -rL node_modules node_modules_resolved
|
||||
try rm -rf node_modules
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
"requires": true,
|
||||
"dependencies": {
|
||||
"marked": {
|
||||
"version": "9.1.4",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-9.1.4.tgz",
|
||||
"integrity": "sha512-Mq83CCaClhXqhf8sLQ57c1unNelHEuFadK36ga+GeXR4FeT/5ssaC5PaCRVqMA74VYorzYRqdAaxxteIanh3Kw=="
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-5.1.1.tgz",
|
||||
"integrity": "sha512-bTmmGdEINWmOMDjnPWDxGPQ4qkDLeYorpYbEtFOXzOruTwUE671q4Guiuchn4N8h/v6NGd7916kXsm3Iz4iUSg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"node": ">=12.17.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"marked": "^9.1.4"
|
||||
"marked": "^5.1.1"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"optionalDependencies": {},
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
# minimum required node version
|
||||
REQUIRED_NODE_MAJOR=12
|
||||
REQUIRED_NODE_MINOR=13
|
||||
REQUIRED_NODE_MAJOR=16
|
||||
REQUIRED_NODE_MINOR=0
|
||||
|
||||
# minimum required npm version
|
||||
REQUIRED_NPM_MAJOR=5
|
||||
REQUIRED_NPM_MINOR=5
|
||||
REQUIRED_NPM_MAJOR=8
|
||||
REQUIRED_NPM_MINOR=19
|
||||
|
||||
pecho() { printf %s\\n "$*"; }
|
||||
log() { pecho "$@"; }
|
||||
|
|
|
@ -37,23 +37,63 @@ if [ ! -f "$settings" ]; then
|
|||
cp settings.json.template "$settings" || exit 1
|
||||
fi
|
||||
|
||||
log "Removing src/node_modules."
|
||||
rm -rf ./src/node_modules || true
|
||||
|
||||
# try to determine if plugins were installed using --no-save
|
||||
ROOT_PLUGINS_EXIST=1
|
||||
for file in node_modules/*
|
||||
do
|
||||
if [ ! -e "$file" ]; then break; fi
|
||||
if [ -L "$file" ] && [ "$file" = "node_modules/ep_etherpad-lite" ]; then break; fi
|
||||
|
||||
if expr "$file" : "node_modules/ep_*" > /dev/null; then
|
||||
ROOT_PLUGINS_EXIST=0
|
||||
fi
|
||||
done
|
||||
|
||||
PACKAGE_EXISTS=1
|
||||
PACKAGELOCK_EXISTS=1
|
||||
if test -f ./package.json; then PACKAGE_EXISTS=0;fi
|
||||
if test -f ./package-lock.json; then PACKAGELOCK_EXISTS=0;fi
|
||||
|
||||
if [ "$PACKAGE_EXISTS" = "1" ] || [ "$PACKAGELOCK_EXISTS" = "1" ]; then
|
||||
if [ "$ROOT_PLUGINS_EXIST" = "0" ]; then
|
||||
log "You have plugins in ./node_modules but don't have a package.json or package-lock.json file."
|
||||
log "Please manually remove your ./node_modules directory, run this script again and install any plugins with npm i ep_plugin1 ep_plugin2 afterwards"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log "Linking src as new package ep_etherpad-lite."
|
||||
exit_code=0
|
||||
(cd ./src && npm link --bin-links=false) || exit_code=$?
|
||||
|
||||
if [ "$exit_code" != 0 ]; then
|
||||
log "npm link failed. If there was a permission error, please set a prefix for npm."
|
||||
log "The prefix can be set e.g. with npm config set prefix $HOME/.npm-packages"
|
||||
log "This will create a symlink in $HOME/.npm-packages/lib/node_modules that points to this directory."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Installing dependencies..."
|
||||
(mkdir -p node_modules &&
|
||||
cd node_modules &&
|
||||
{ [ -d ep_etherpad-lite ] || ln -sf ../src ep_etherpad-lite; } &&
|
||||
cd ep_etherpad-lite)
|
||||
|
||||
cd src
|
||||
|
||||
if [ -z "${ETHERPAD_PRODUCTION}" ]; then
|
||||
log "Installing dev dependencies"
|
||||
npm ci --no-optional --omit=optional --include=dev --lockfile-version 1 || exit 1
|
||||
else
|
||||
if [ "$NODE_ENV" = "production" ]; then
|
||||
log "Installing production dependencies"
|
||||
npm ci --no-optional --omit=optional --omit=dev --lockfile-version 1 --production || exit 1
|
||||
npm link ep_etherpad-lite --omit=optional --omit=dev --save --package-lock=true --bin-links=false || exit 1
|
||||
else
|
||||
log "Installing dev dependencies"
|
||||
npm link ep_etherpad-lite --omit=optional --save --package-lock=true --bin-links=false || exit 1
|
||||
fi
|
||||
|
||||
log "Adding symlinks for plugin backwards compatibility"
|
||||
mkdir src/node_modules -p
|
||||
ln -s ../../node_modules/async src/node_modules/async
|
||||
ln -s ../../node_modules/express src/node_modules/express
|
||||
ln -s ../../node_modules/formidable src/node_modules/formidable
|
||||
ln -s ../../node_modules/log4js src/node_modules/log4js
|
||||
ln -s ../../node_modules/supertest src/node_modules/supertest
|
||||
|
||||
|
||||
# Remove all minified data to force node creating it new
|
||||
log "Clearing minified cache..."
|
||||
rm -f var/minified*
|
||||
|
|
|
@ -9,19 +9,34 @@ cmd /C node -e "" || ( echo "Please install node.js ( https://nodejs.org )" && e
|
|||
echo _
|
||||
echo Ensure that all dependencies are up to date... If this is the first time you have run Etherpad please be patient.
|
||||
|
||||
mkdir node_modules
|
||||
cd /D node_modules
|
||||
mklink /D "ep_etherpad-lite" "..\src"
|
||||
echo Deleting old node_modules and src/node_modules
|
||||
del /q .\node_modules
|
||||
del /q .\src\node_modules
|
||||
echo Deleting old package.json and package-lock.json
|
||||
del /q .\package.json
|
||||
del /q .\package-lock.json
|
||||
|
||||
cd /D "ep_etherpad-lite"
|
||||
cmd /C npm ci --legacy-peer-deps || exit /B 1
|
||||
cd /D src
|
||||
cmd /C npm link --bin-links=false || exit /B 1
|
||||
|
||||
cd /D "%~dp0\..\.."
|
||||
cd ..
|
||||
|
||||
cmd /C npm link ep_etherpad-lite --omit=optional --omit=dev --save --package-lock=true --bin-links=false || exit /B 1
|
||||
|
||||
echo _
|
||||
echo Clearing cache...
|
||||
del /S var\minified*
|
||||
|
||||
echo Adding symlinks for plugin backwards compatibility
|
||||
mkdir src\node_modules
|
||||
cd /D src\node_modules
|
||||
mklink /D "async" "..\..\node_modules\async"
|
||||
mklink /D "express" "..\..\node_modules\express"
|
||||
mklink /D "formidable" "..\..\node_modules\formidable"
|
||||
mklink /D "log4js" "..\..\node_modules\log4js"
|
||||
mklink /D "supertest" "..\..\node_modules\supertest"
|
||||
cd ..\..
|
||||
|
||||
echo _
|
||||
echo Setting up settings.json...
|
||||
IF NOT EXIST settings.json (
|
||||
|
|
|
@ -356,18 +356,17 @@ const logger = log4js.getLogger('checkPlugin');
|
|||
logger.warn('Test files not found, please create tests. https://github.com/ether/etherpad-lite/wiki/Creating-a-plugin#writing-and-running-front-end-tests-for-your-plugin');
|
||||
}
|
||||
|
||||
// Install dependencies so we can run ESLint. This should also create or update package-lock.json
|
||||
// if autoFix is enabled.
|
||||
const npmInstall = `npm install${autoFix ? '' : ' --no-package-lock'}`;
|
||||
execSync(npmInstall, {stdio: 'inherit'});
|
||||
// Create the ep_etherpad-lite symlink if necessary. This must be done after running `npm install`
|
||||
// because that command nukes the symlink.
|
||||
try {
|
||||
const d = await fsp.realpath(path.join(pluginPath, 'node_modules/ep_etherpad-lite'));
|
||||
assert.equal(d, epSrcDir);
|
||||
} catch (err) {
|
||||
execSync(`${npmInstall} --no-save ep_etherpad-lite@file:${epSrcDir}`, {stdio: 'inherit'});
|
||||
execSync('./src/bin/installDeps.sh', {stdio: 'inherit'});
|
||||
}
|
||||
|
||||
// Install dependencies so we can run ESLint. This should also create or update package-lock.json
|
||||
// if autoFix is enabled.
|
||||
const npmInstall = `npm install${autoFix ? '' : ' --no-package-lock'}`;
|
||||
execSync(npmInstall, {stdio: 'inherit'});
|
||||
// linting begins
|
||||
try {
|
||||
logger.info('Linting...');
|
||||
|
|
|
@ -11,7 +11,7 @@ TODO: Describe the plugin.
|
|||
From the Etherpad working directory, run:
|
||||
|
||||
```shell
|
||||
npm install --no-save --legacy-peer-deps [plugin_name]
|
||||
npm install [plugin_name]
|
||||
```
|
||||
|
||||
Or, install from Etherpad's `/admin/plugins` page.
|
||||
|
|
|
@ -9,12 +9,9 @@ const childProcess = require('child_process');
|
|||
const log4js = require('log4js');
|
||||
const path = require('path');
|
||||
const semver = require('semver');
|
||||
const {exec} = require('child_process');
|
||||
const {exec} = require("child_process");
|
||||
|
||||
log4js.configure({appenders: {console: {type: 'console'}},
|
||||
categories: {
|
||||
default: {appenders: ['console'], level: 'info'},
|
||||
}});
|
||||
log4js.replaceConsole();
|
||||
|
||||
/*
|
||||
|
||||
|
@ -81,11 +78,11 @@ const assertUpstreamOk = (branch, opts = {}) => {
|
|||
};
|
||||
|
||||
// Check if asciidoctor is installed
|
||||
exec('asciidoctor -v', (err, stdout) => {
|
||||
if (err) {
|
||||
console.log('Please install asciidoctor');
|
||||
console.log('https://asciidoctor.org/docs/install-toolchain/');
|
||||
process.exit(1);
|
||||
exec('asciidoctor -v', (err,stdout)=>{
|
||||
if (err){
|
||||
console.log('Please install asciidoctor')
|
||||
console.log('https://asciidoctor.org/docs/install-toolchain/')
|
||||
process.exit(1)
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -185,8 +182,8 @@ try {
|
|||
console.log('Updating ether.github.com master branch...');
|
||||
run('git pull --ff-only', {cwd: '../ether.github.com/'});
|
||||
console.log('Committing documentation...');
|
||||
run(`cp -R out/doc/ ../ether.github.com/public/doc/v'${newVersion}'`);
|
||||
run(`npm version ${newVersion}`, {cwd: '../ether.github.com'});
|
||||
run(`cp -R out/doc/ ../ether.github.com/doc/v'${newVersion}'`);
|
||||
run(`rm -f latest && ln -s 'v${newVersion}' latest`, {cwd: '../ether.github.com/doc/'});
|
||||
run('git add .', {cwd: '../ether.github.com/'});
|
||||
run(`git commit -m '${newVersion} docs'`, {cwd: '../ether.github.com/'});
|
||||
} catch (err) {
|
||||
|
@ -206,9 +203,12 @@ console.log(' (cd ../ether.github.com && git show)');
|
|||
console.log('If everything looks good then push:');
|
||||
console.log(` git push origin master develop '${newVersion}'`);
|
||||
console.log(' (cd ../ether.github.com && git push)');
|
||||
console.log('Creating a Windows build is not necessary anymore and will be created by GitHub action');
|
||||
console.log('Create a Windows build:');
|
||||
console.log(' bin/buildForWindows.sh');
|
||||
console.log('Visit https://github.com/ether/etherpad-lite/releases/new and create a new release ' +
|
||||
`with 'master' as the target and the version is ${newVersion}. `);
|
||||
console.log('The docs are updated automatically with the new version. While the windows build' +
|
||||
' is generated people can still download the older versions.');
|
||||
`with 'master' as the target and the version is ${newVersion}. Include the windows ` +
|
||||
'zip as an asset');
|
||||
console.log('Once the new docs are uploaded then modify the download links (replace ' +
|
||||
`${currentVersion} with ${newVersion} on etherpad.org and then pull master onto ` +
|
||||
'develop)');
|
||||
console.log('Finally go public with an announcement via our comms channels :)');
|
||||
|
|
|
@ -8,4 +8,4 @@ OUTDATED=$(npm outdated --depth=0 | awk '{print $1}' | grep '^ep_') || {
|
|||
}
|
||||
set -- ${OUTDATED}
|
||||
echo "Updating plugins: $*"
|
||||
exec npm install --no-save "$@"
|
||||
exec npm install "$@"
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Xuacu",
|
||||
"YoaR"
|
||||
"Xuacu"
|
||||
]
|
||||
},
|
||||
"index.newPad": "Nuevu bloc",
|
||||
|
@ -26,9 +25,9 @@
|
|||
"pad.toolbar.embed.title": "Compartir ya incrustar esti bloc",
|
||||
"pad.toolbar.showusers.title": "Amosar los usuarios d'esti bloc",
|
||||
"pad.colorpicker.save": "Guardar",
|
||||
"pad.colorpicker.cancel": "Zarrar",
|
||||
"pad.colorpicker.cancel": "Encaboxar",
|
||||
"pad.loading": "Cargando...",
|
||||
"pad.noCookie": "Nun pudo alcontrase la cookie. ¡Por favor, permite les cookies nel navegador! La sesión y preferencies nun se guarden ente visites. Esto pue debese a qu'Etherpad inclúyese nun iFrame en dalgunos restoladores. Asegúrate de qu'Etherpad tea nel mesmu subdominiu/dominiu que l'iFrame padre",
|
||||
"pad.noCookie": "Nun pudo alcontrase la cookie. ¡Por favor, permite les cookies nel navegador! La sesión y preferencies nun se guarden ente visites. Esto pué debese a qu'Etherpad inclúyese nun iFrame en dalgunos restoladores. Asegúrate de qu'Etherpad tea nel mesmu subdominiu/dominiu que la iFrame padre",
|
||||
"pad.permissionDenied": "Nun tienes permisu pa entrar a esti bloc",
|
||||
"pad.settings.padSettings": "Configuración del bloc",
|
||||
"pad.settings.myView": "la mio vista",
|
||||
|
@ -57,7 +56,7 @@
|
|||
"pad.modals.reconnecting": "Reconeutando col to bloc...",
|
||||
"pad.modals.forcereconnect": "Forzar la reconexón",
|
||||
"pad.modals.reconnecttimer": "Tentando reconeutar en",
|
||||
"pad.modals.cancel": "Zarrar",
|
||||
"pad.modals.cancel": "Encaboxar",
|
||||
"pad.modals.userdup": "Abiertu n'otra ventana",
|
||||
"pad.modals.userdup.explanation": "Esti bloc paez que ta abiertu en más d'una ventana del navegador d'esti ordenador.",
|
||||
"pad.modals.userdup.advice": "Reconeutar pa usar esta ventana.",
|
||||
|
|
|
@ -2,29 +2,13 @@
|
|||
"@metadata": {
|
||||
"authors": [
|
||||
"Baloch Afghanistan",
|
||||
"Moshtank",
|
||||
"Sultanselim baloch"
|
||||
]
|
||||
},
|
||||
"admin.page-title": "کارمسترءِ کُرسی - اترپَد",
|
||||
"admin_plugins": "گݔشانکانءِ کار ءُ بار",
|
||||
"admin_plugins.available": "دسترسݔن گݔشانک",
|
||||
"admin_plugins.available_not-found": "گݔشانکے نݔست اَت۔",
|
||||
"admin_plugins.available_install.value": "پِررݔنَگ",
|
||||
"admin_plugins.available_search.placeholder": "گݔشانکان شۏھاز پہ پِررݔنَگا",
|
||||
"admin_plugins.description": "سرۏشتادی",
|
||||
"admin_plugins.installed": "گݔشانک پِررݔنگ بیت",
|
||||
"admin_plugins.installed_fetching": "پِررݔنتَگݔن گݔشانکانءِ پچ کنگ",
|
||||
"admin_plugins.installed_nothing": "شما ھنگت ھچ گݔشانکے نہ پِررݔنتَگ۔",
|
||||
"admin_plugins.installed_uninstall.value": "پِررݔنتَگݔنءِ بند کنگ",
|
||||
"admin_plugins.last-update": "گُڈی پہ رۏچان",
|
||||
"admin_plugins.name": "نام",
|
||||
"admin_plugins.page-title": "گݔشانکءِ کارمستری - اترپد",
|
||||
"admin_plugins.version": "ورژن",
|
||||
"index.newPad": "دفترچه یادداشت تازه",
|
||||
"index.createOpenPad": "یا ایجاد/بازکردن یک دفترچه یادداشت با نام:",
|
||||
"pad.toolbar.bold.title": "پررنگ (Ctrl-B)",
|
||||
"pad.toolbar.italic.title": "کَش (Ctrl-I)",
|
||||
"pad.toolbar.italic.title": "کج (Ctrl-I)",
|
||||
"pad.toolbar.underline.title": "زیرخط (Ctrl-U)",
|
||||
"pad.toolbar.strikethrough.title": "خط خورده",
|
||||
"pad.toolbar.ol.title": "فهرست مرتب شده",
|
||||
|
@ -37,12 +21,12 @@
|
|||
"pad.toolbar.import_export.title": "درونریزی/برونریزی از/به قالبهای مختلف",
|
||||
"pad.toolbar.timeslider.title": "لغزندهٔ زمان",
|
||||
"pad.toolbar.savedRevision.title": "ذخیرهسازی نسخه",
|
||||
"pad.toolbar.settings.title": "ردانکان",
|
||||
"pad.toolbar.settings.title": "تنظیمات",
|
||||
"pad.toolbar.embed.title": "اشتراک و جاسازی این دفترچه یادداشت",
|
||||
"pad.toolbar.showusers.title": "نمایش کاربران در این دفترچه یادداشت",
|
||||
"pad.colorpicker.save": "سَپت",
|
||||
"pad.colorpicker.cancel": "بجَگ",
|
||||
"pad.loading": "بییگئن...",
|
||||
"pad.colorpicker.save": "زاپاس کورتین",
|
||||
"pad.colorpicker.cancel": "کنسیل",
|
||||
"pad.loading": "...بار بیت",
|
||||
"pad.permissionDenied": "شرمنده، شما را اجازت په دسترسی ای صفحه نیست.",
|
||||
"pad.settings.padSettings": "تنظیمات دفترچه یادداشت",
|
||||
"pad.settings.myView": "منی سۏج",
|
||||
|
@ -55,7 +39,7 @@
|
|||
"pad.settings.language": "زبان:",
|
||||
"pad.importExport.import_export": "درونریزی/برونریزی",
|
||||
"pad.importExport.import": "بارگذاری پروندهی متنی یا سند",
|
||||
"pad.importExport.importSuccessful": "سۏبݔن بیت!",
|
||||
"pad.importExport.importSuccessful": "موفقیت آمیز بود!",
|
||||
"pad.importExport.export": "برونریزی این دفترچه یادداشت با قالب:",
|
||||
"pad.importExport.exporthtml": "HTML",
|
||||
"pad.importExport.exportplain": "سادگین متن",
|
||||
|
@ -82,21 +66,21 @@
|
|||
"pad.modals.badChangeset.cause": "این میتواند به دلیل پیکربندی اشتباه یا سایر رفتارهای غیرمنتظره باشد. اگر فکر میکنید این یک خطا است لطفاً با مدیر خدمت تماس بگیرید. برای ادامهٔ ویرایش سعی کنید که دوباره متصل شوید.",
|
||||
"pad.modals.corruptPad.explanation": "پدی که شما سعی دارید دسترسی پیدا کنید خراب است.",
|
||||
"pad.modals.corruptPad.cause": "این احتمالاً به دلیل تنظیمات اشتباه کارساز یا سایر رفتارهای غیرمنتظره است. لطفاً با مدیر خدمت تماس حاصل کنید.",
|
||||
"pad.modals.deleted": "گار بیت۔",
|
||||
"pad.modals.deleted.explanation": "اے یادداشت پاک کنگ بیتگ۔",
|
||||
"pad.modals.deleted": "پاک کورتین",
|
||||
"pad.modals.deleted.explanation": "این دفترچه یادداشت پاک شدهاست.",
|
||||
"pad.modals.disconnected": "شمئی سکّی کھت اِنت۔",
|
||||
"pad.modals.disconnected.explanation": "اتصال به سرور قطع شدهاست.",
|
||||
"pad.modals.disconnected.cause": "ممکن است سرور در دسترس نباشد. اگر این مشکل باز هم رخ داد مدیر حدمت را آگاه کنید.",
|
||||
"pad.share": "به اشتراکگذاری این دفترچه یادداشت",
|
||||
"pad.share.readonly": "فقط خواندنی",
|
||||
"pad.share.link": "لینک",
|
||||
"pad.share.link": "پیوند",
|
||||
"pad.share.emebdcode": "جاسازی نشانی",
|
||||
"pad.chat": "گفتگو",
|
||||
"pad.chat.title": "بازکردن گفتگو برای این دفترچه یادداشت",
|
||||
"pad.chat.loadmessages": "گݔشترݔں پیگامء چارگ",
|
||||
"timeslider.pageTitle": "لغزندهٔ زمان {{appTitle}}",
|
||||
"timeslider.toolbar.returnbutton": "چَھر کنگ پہ یاددپترا",
|
||||
"timeslider.toolbar.authors": "لککۏک:",
|
||||
"timeslider.toolbar.returnbutton": "بازگشت به دفترچه یادداشت",
|
||||
"timeslider.toolbar.authors": "نویسوک:",
|
||||
"timeslider.toolbar.authorsList": "بدون نویسنده",
|
||||
"timeslider.toolbar.exportlink.title": "درگیزگ",
|
||||
"timeslider.exportCurrent": "برونریزی نگارش کنونی به عنوان:",
|
||||
|
|
|
@ -4,29 +4,13 @@
|
|||
"Christian List",
|
||||
"Joedalton",
|
||||
"Peter Alberti",
|
||||
"Peterleth",
|
||||
"Saederup92",
|
||||
"Steenth"
|
||||
]
|
||||
},
|
||||
"admin.page-title": "Admin Dashboard - Etherpad",
|
||||
"admin_plugins": "Plugin manager",
|
||||
"admin_plugins.available": "Tilgængelige Plugins",
|
||||
"admin_plugins.available_not-found": "Ingen plugins fundet.",
|
||||
"admin_plugins.available_fetching": "Henter...",
|
||||
"admin_plugins.available_install.value": "Installer",
|
||||
"admin_plugins.available_search.placeholder": "Søg efter plugins der kan installeres",
|
||||
"admin_plugins.description": "Beskrivelse",
|
||||
"admin_plugins.installed": "Installerede plugins",
|
||||
"admin_plugins.installed_fetching": "Henter installerede plugins...",
|
||||
"admin_plugins.installed_nothing": "Du har ikke installeret nogen plugins endnu.",
|
||||
"admin_plugins.installed_uninstall.value": "Afinstaller",
|
||||
"admin_plugins.last-update": "Sidst opdateret",
|
||||
"admin_plugins.name": "Navn",
|
||||
"admin_plugins.page-title": "Plugin manager - Etherpad",
|
||||
"admin_plugins.version": "Version",
|
||||
"admin_plugins_info": "Fejlfindingsoplysninger",
|
||||
"admin_plugins_info.hooks": "Installerede hooks",
|
||||
"admin_settings": "Indstillinger",
|
||||
"index.newPad": "Ny Pad",
|
||||
"index.createOpenPad": "eller opret/åbn en Pad med navnet:",
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
"Mklehr",
|
||||
"Nipsky",
|
||||
"Predatorix",
|
||||
"SamTV",
|
||||
"Sebastian Wallroth",
|
||||
"Thargon",
|
||||
"Tim.krieger",
|
||||
|
@ -18,7 +17,7 @@
|
|||
]
|
||||
},
|
||||
"admin.page-title": "Admin Dashboard - Etherpad",
|
||||
"admin_plugins": "Pluginverwaltung",
|
||||
"admin_plugins": "Plugins verwalten",
|
||||
"admin_plugins.available": "Verfügbare Plugins",
|
||||
"admin_plugins.available_not-found": "Keine Plugins gefunden.",
|
||||
"admin_plugins.available_fetching": "Wird abgerufen...",
|
||||
|
@ -41,12 +40,12 @@
|
|||
"admin_plugins_info.plugins": "Installierte Plugins",
|
||||
"admin_plugins_info.page-title": "Plugin Informationen - Etherpad",
|
||||
"admin_plugins_info.version": "Etherpad Version",
|
||||
"admin_plugins_info.version_latest": "Neueste verfügbare Version",
|
||||
"admin_plugins_info.version_latest": "Neueste Version",
|
||||
"admin_plugins_info.version_number": "Versionsnummer",
|
||||
"admin_settings": "Einstellungen",
|
||||
"admin_settings.current": "Derzeitige Konfiguration",
|
||||
"admin_settings.current_example-devel": "Beispielhafte Entwicklungseinstellungs-Templates",
|
||||
"admin_settings.current_example-prod": "Beispiel eines produktiven Templates",
|
||||
"admin_settings.current_example-prod": "Beispiel einer Vorlage für Produktionseinstellungen",
|
||||
"admin_settings.current_restart.value": "Etherpad neustarten",
|
||||
"admin_settings.current_save.value": "Einstellungen speichern",
|
||||
"admin_settings.page-title": "Einstellungen - Etherpad",
|
||||
|
@ -72,9 +71,9 @@
|
|||
"pad.toolbar.showusers.title": "Benutzer dieses Pads anzeigen",
|
||||
"pad.colorpicker.save": "Speichern",
|
||||
"pad.colorpicker.cancel": "Abbrechen",
|
||||
"pad.loading": "Laden …",
|
||||
"pad.loading": "Lade …",
|
||||
"pad.noCookie": "Das Cookie konnte nicht gefunden werden. Bitte erlaube Cookies in deinem Browser! Deine Sitzung und Einstellungen werden zwischen den Besuchen nicht gespeichert. Dies kann darauf zurückzuführen sein, dass Etherpad in einigen Browsern in einem iFrame enthalten ist. Bitte stelle sicher, dass sich Etherpad auf der gleichen Subdomain/Domain wie der übergeordnete iFrame befindet.",
|
||||
"pad.permissionDenied": "Du hast keine Berechtigung, um auf dieses Pad zuzugreifen.",
|
||||
"pad.permissionDenied": "Du hast keine Berechtigung, um auf dieses Pad zuzugreifen",
|
||||
"pad.settings.padSettings": "Pad-Einstellungen",
|
||||
"pad.settings.myView": "Eigene Ansicht",
|
||||
"pad.settings.stickychat": "Unterhaltung immer anzeigen",
|
||||
|
@ -86,7 +85,7 @@
|
|||
"pad.settings.fontType.normal": "Normal",
|
||||
"pad.settings.language": "Sprache:",
|
||||
"pad.settings.about": "Über",
|
||||
"pad.settings.poweredBy": "Betrieben von",
|
||||
"pad.settings.poweredBy": "Powered by",
|
||||
"pad.importExport.import_export": "Import/Export",
|
||||
"pad.importExport.import": "Textdatei oder Dokument hochladen",
|
||||
"pad.importExport.importSuccessful": "Erfolgreich!",
|
||||
|
@ -121,7 +120,7 @@
|
|||
"pad.modals.corruptPad.cause": "Dies könnte an einer falschen Serverkonfiguration oder einem anderen unerwarteten Verhalten liegen. Bitte kontaktiere den Administrator dieses Dienstes.",
|
||||
"pad.modals.deleted": "Gelöscht.",
|
||||
"pad.modals.deleted.explanation": "Dieses Pad wurde entfernt.",
|
||||
"pad.modals.rateLimited": "Durchsatzratenbegrenzt",
|
||||
"pad.modals.rateLimited": "Begrenzte Rate.",
|
||||
"pad.modals.rateLimited.explanation": "Sie haben zu viele Nachrichten an dieses Pad gesendet, so dass die Verbindung unterbrochen wurde.",
|
||||
"pad.modals.rejected.explanation": "Der Server hat eine Nachricht abgelehnt, die von deinem Browser gesendet wurde.",
|
||||
"pad.modals.rejected.cause": "Möglicherweise wurde der Server aktualisiert, während du das Pad angesehen hast, oder es existiert ein Fehler in Etherpad. Versuche, die Seite neu zu laden.",
|
||||
|
@ -141,7 +140,7 @@
|
|||
"timeslider.pageTitle": "{{appTitle}} Bearbeitungsverlauf",
|
||||
"timeslider.toolbar.returnbutton": "Zurück zum Pad",
|
||||
"timeslider.toolbar.authors": "Autoren:",
|
||||
"timeslider.toolbar.authorsList": "Keine Autoren",
|
||||
"timeslider.toolbar.authorsList": "keine Autoren",
|
||||
"timeslider.toolbar.exportlink.title": "Diese Version exportieren",
|
||||
"timeslider.exportCurrent": "Exportiere diese Version als:",
|
||||
"timeslider.version": "Version {{version}}",
|
||||
|
@ -164,7 +163,7 @@
|
|||
"timeslider.month.december": "Dezember",
|
||||
"timeslider.unnamedauthors": "{{num}} {[plural(num) one: unbenannter Autor, other: unbenannte Autoren ]}",
|
||||
"pad.savedrevs.marked": "Diese Version wurde jetzt als gespeicherte Version gekennzeichnet",
|
||||
"pad.savedrevs.timeslider": "Du kannst gespeicherte Versionen durch den Aufruf des Bearbeitungsverlaufs ansehen.",
|
||||
"pad.savedrevs.timeslider": "Du kannst gespeicherte Versionen durch den Aufruf des Bearbeitungsverlaufes ansehen.",
|
||||
"pad.userlist.entername": "Dein Name?",
|
||||
"pad.userlist.unnamed": "unbenannt",
|
||||
"pad.editbar.clearcolors": "Autorenfarben im gesamten Dokument zurücksetzen? Dies kann nicht rückgängig gemacht werden",
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
"@metadata": {
|
||||
"authors": [
|
||||
"Armando-Martin",
|
||||
"Atzerritik",
|
||||
"DDPAT",
|
||||
"Dgstranz",
|
||||
"Fitoschido",
|
||||
|
@ -75,7 +74,7 @@
|
|||
"pad.colorpicker.save": "Guardar",
|
||||
"pad.colorpicker.cancel": "Cancelar",
|
||||
"pad.loading": "Cargando...",
|
||||
"pad.noCookie": "No se pudo encontrar la galleta. ¡Por favor, permita las cookies en su navegador! Su sesión y configuración no se guardarán entre las visitas. Esto puede deberse a que Etherpad está incluido en un iFrame en algunos navegadores. Por favor asegúrese de que Etherpad está en el mismo subdominio/dominio que el iFrame padre",
|
||||
"pad.noCookie": "No se pudo encontrar la «cookie». Permite la utilización de «cookies» en el navegador.",
|
||||
"pad.permissionDenied": "No tienes permiso para acceder a este pad",
|
||||
"pad.settings.padSettings": "Configuración del pad",
|
||||
"pad.settings.myView": "Preferencias personales",
|
||||
|
@ -99,7 +98,7 @@
|
|||
"pad.importExport.exportword": "Microsoft Word",
|
||||
"pad.importExport.exportpdf": "PDF",
|
||||
"pad.importExport.exportopen": "ODF (Open Document Format)",
|
||||
"pad.importExport.abiword.innerHTML": "Solo se puede importar desde texto plano o formatos HTML. Para obtener funciones de importación más avanzadas, <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">instale AbiWord o LibreOffice</a>.",
|
||||
"pad.importExport.abiword.innerHTML": "Solo es posible importar texto sin formato o en HTML. Para obtener funciones de importación más avanzadas es necesario <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-with-AbiWord\">instalar AbiWord</a>.",
|
||||
"pad.modals.connected": "Conectado.",
|
||||
"pad.modals.reconnecting": "Reconectando a tu pad...",
|
||||
"pad.modals.forcereconnect": "Forzar reconexión",
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
"Xabier Armendaritz"
|
||||
]
|
||||
},
|
||||
"admin.page-title": "Kudeaketa panela - Etherpad",
|
||||
"admin.page-title": "Admin Aginte-panela - Etherpad",
|
||||
"admin_plugins": "Plugin-en kudeaketa",
|
||||
"admin_plugins.available": "Eskuragarri dauden plugin-ak",
|
||||
"admin_plugins.available_not-found": "Ez da plugin-ik aurkitu",
|
||||
|
|
|
@ -16,33 +16,8 @@
|
|||
"admin_plugins.available_not-found": "Įskiepių nerasta.",
|
||||
"admin_plugins.available_fetching": "Gaunama…",
|
||||
"admin_plugins.available_install.value": "Įdiegti",
|
||||
"admin_plugins.available_search.placeholder": "Ieškokite įskiepių įdiegimui",
|
||||
"admin_plugins.description": "Aprašymas",
|
||||
"admin_plugins.installed": "Įdiegti įskiepiai",
|
||||
"admin_plugins.installed_fetching": "Gaunami įdiegti papildiniai…",
|
||||
"admin_plugins.installed_nothing": "Dar neįdiegėte jokių papildinių.",
|
||||
"admin_plugins.installed_uninstall.value": "Išinstaluoti",
|
||||
"admin_plugins.last-update": "Paskutinis atnaujinimas",
|
||||
"admin_plugins.name": "Pavadinimas",
|
||||
"admin_plugins.page-title": "Papildinių tvarkyklė – Etherpad",
|
||||
"admin_plugins.version": "Versija",
|
||||
"admin_plugins_info": "Trikčių šalinimo informacija",
|
||||
"admin_plugins_info.parts": "Įdiegtos dalys",
|
||||
"admin_plugins_info.plugins": "Įdiegti papildiniai",
|
||||
"admin_plugins_info.page-title": "Papildinio informacija – Etherpad",
|
||||
"admin_plugins_info.version": "Etherpad versija",
|
||||
"admin_plugins_info.version_latest": "Naujausia prieinama versija",
|
||||
"admin_plugins_info.version_number": "Versijos numeris",
|
||||
"admin_settings": "Nustatymai",
|
||||
"admin_settings.current": "Dabartinė konfigūracija",
|
||||
"admin_settings.current_example-devel": "Kūrimo nustatymų šablono pavyzdys",
|
||||
"admin_settings.current_example-prod": "Gamybos nustatymų šablono pavyzdys",
|
||||
"admin_settings.current_restart.value": "Iš naujo paleisti Etherpad",
|
||||
"admin_settings.current_save.value": "Išsaugoti nustatymus",
|
||||
"admin_settings.page-title": "Nustatymai – Etherpad",
|
||||
"index.newPad": "Naujas bloknotas",
|
||||
"index.createOpenPad": "arba sukurkite/atidarykite Bloknotą su pavadinimu:",
|
||||
"index.openPad": "atidaryti egzistuojantį bloknotą su pavadinimu:",
|
||||
"pad.toolbar.bold.title": "Paryškintasis (Ctrl-B)",
|
||||
"pad.toolbar.italic.title": "Pasvirasis (Ctrl-I)",
|
||||
"pad.toolbar.underline.title": "Pabraukimas (Ctrl-U)",
|
||||
|
@ -75,8 +50,6 @@
|
|||
"pad.settings.fontType": "Šrifto tipas:",
|
||||
"pad.settings.fontType.normal": "Normalus",
|
||||
"pad.settings.language": "Kalba:",
|
||||
"pad.settings.about": "Apie",
|
||||
"pad.settings.poweredBy": "Palaiko",
|
||||
"pad.importExport.import_export": "Importuoti/Eksportuoti",
|
||||
"pad.importExport.import": "Įkelkite bet kokį tekstinį failą arba dokumentą",
|
||||
"pad.importExport.importSuccessful": "Pavyko!",
|
||||
|
@ -87,9 +60,9 @@
|
|||
"pad.importExport.exportword": "Microsoft Word",
|
||||
"pad.importExport.exportpdf": "PDF",
|
||||
"pad.importExport.exportopen": "ODF (Atvirasis dokumento formatas)",
|
||||
"pad.importExport.abiword.innerHTML": "Galite importuoti tik iš paprasto teksto ar HTML formatų. Dėl išplėstinių importavimo funkcijų prašome <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-in-Ubuntu-or-OpenSuse-or-SLES-with-AbiWord\">įdiegti AbiWord ar LibreOffice</a>.",
|
||||
"pad.importExport.abiword.innerHTML": "Galite importuoti tik iš paprasto teksto ar HTML formato. Dėl išplėstinių importavimo funkcijų prašome <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-in-Ubuntu-or-OpenSuse-or-SLES-with-AbiWord\">įdiegti AbiWord</a>.",
|
||||
"pad.modals.connected": "Prisijungta.",
|
||||
"pad.modals.reconnecting": "Iš naujo prisijungiama prie jūsų bloknoto…",
|
||||
"pad.modals.reconnecting": "Iš naujo prisijungiama prie Jūsų bloknoto",
|
||||
"pad.modals.forcereconnect": "Priversti prisijungti iš naujo",
|
||||
"pad.modals.reconnecttimer": "Bandoma vėl prisijungti",
|
||||
"pad.modals.cancel": "Atšaukti",
|
||||
|
@ -111,9 +84,6 @@
|
|||
"pad.modals.corruptPad.cause": "Tai gali nutikti dėl neteisingos serverio konfigūracijos ar kitos netikėtos elgsenos. Prašome susisiekti su paslaugos administratoriumi.",
|
||||
"pad.modals.deleted": "Ištrintas.",
|
||||
"pad.modals.deleted.explanation": "Bloknotas buvo pašalintas.",
|
||||
"pad.modals.rateLimited.explanation": "Išsiuntėte per daug pranešimų į šį bloknotą, todėl jis atjungė jus.",
|
||||
"pad.modals.rejected.explanation": "Serveris atmetė jūsų naršyklės išsiųstą pranešimą.",
|
||||
"pad.modals.rejected.cause": "Gali būti, kad serveris buvo atnaujintas jums peržiūrint bloknotą, o gal tai Etherpad klaida. Pabandykite iš naujo įkelti puslapį.",
|
||||
"pad.modals.disconnected": "Jūs atsijungėte.",
|
||||
"pad.modals.disconnected.explanation": "Ryšys su serveriu nutrūko",
|
||||
"pad.modals.disconnected.cause": "Gali būti, kad serveris yra nepasiekiamas. Prašome informuoti paslaugos administratorių jei tai tęsiasi.",
|
||||
|
@ -126,7 +96,6 @@
|
|||
"pad.chat.loadmessages": "Įkrauti daugiau pranešimų",
|
||||
"pad.chat.stick.title": "Priklijuoti pokalbį",
|
||||
"pad.chat.writeMessage.placeholder": "Rašykite savo žinutę čia",
|
||||
"timeslider.followContents": "Sekite bloknoto turinio atnaujinimus",
|
||||
"timeslider.pageTitle": "{{appTitle}} Laiko slinkiklis",
|
||||
"timeslider.toolbar.returnbutton": "Grįžti į bloknotą",
|
||||
"timeslider.toolbar.authors": "Autoriai:",
|
||||
|
@ -156,7 +125,7 @@
|
|||
"pad.savedrevs.timeslider": "Galite peržiūrėti išsaugotas peržiūras apsilankydami laiko slinkiklyje",
|
||||
"pad.userlist.entername": "Įveskite savo vardą",
|
||||
"pad.userlist.unnamed": "bevardis",
|
||||
"pad.editbar.clearcolors": "Išvalyti autorystės spalvas visame dokumente? To negalima atšaukti",
|
||||
"pad.editbar.clearcolors": "Išvalyti autorystės spalvas visame dokumente?",
|
||||
"pad.impexp.importbutton": "Importuoti dabar",
|
||||
"pad.impexp.importing": "Importuojama...",
|
||||
"pad.impexp.confirmimport": "Failo importavimas pakeis dabartinį bloknoto tekstą. Ar tikrai norite tęsti?",
|
||||
|
@ -165,6 +134,5 @@
|
|||
"pad.impexp.uploadFailed": "Įkėlimas nepavyko, bandykite dar kartą",
|
||||
"pad.impexp.importfailed": "Importuoti nepavyko",
|
||||
"pad.impexp.copypaste": "Prašome nukopijuoti ir įklijuoti",
|
||||
"pad.impexp.exportdisabled": "Eksportavimas {{type}} formatu yra išjungtas. Prašome susisiekti su savo sistemos administratoriumi dėl informacijos.",
|
||||
"pad.impexp.maxFileSize": "Failas per didelis. Susisiekite su svetainės administratoriumi, kad padidintų leistiną importuoti failo dydį"
|
||||
"pad.impexp.exportdisabled": "Eksportavimas {{type}} formatu yra išjungtas. Prašome susisiekti su savo sistemos administratoriumi dėl informacijos."
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
"pad.settings.chatandusers": "Ammustra sa tzarrada e is utentes",
|
||||
"pad.settings.colorcheck": "Colores de autoria",
|
||||
"pad.settings.linenocheck": "Nùmeros de lìnia",
|
||||
"pad.settings.rtlcheck": "Boles lèghere su cuntenutu dae dereta a manca?",
|
||||
"pad.settings.rtlcheck": "Cuntenutu dae manca a dereta",
|
||||
"pad.settings.fontType": "Tipu de caràtere:",
|
||||
"pad.settings.fontType.normal": "Normale",
|
||||
"pad.settings.language": "Lìngua:",
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
"@metadata": {
|
||||
"authors": [
|
||||
"AmaryllisGardener",
|
||||
"CiphriusKane",
|
||||
"John Reid",
|
||||
"Nintendofan885"
|
||||
]
|
||||
|
@ -51,7 +50,7 @@
|
|||
"pad.importExport.exportword": "Microsoft Word",
|
||||
"pad.importExport.exportpdf": "PDF",
|
||||
"pad.importExport.exportopen": "ODF (Open Document Format)",
|
||||
"pad.importExport.abiword.innerHTML": "Ye can anely import fae plain tex or HTML formats. Fur mair advanced import features please <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-in-Ubuntu-or-OpenSuse-or-SLES-with-AbiWord\">install abiword</a>.",
|
||||
"pad.importExport.abiword.innerHTML": "Ye can yinly import fae plain tex or HTML formats. Fer mair advanced import features please <a href=\"https://github.com/ether/etherpad-lite/wiki/How-to-enable-importing-and-exporting-different-file-formats-in-Ubuntu-or-OpenSuse-or-SLES-with-AbiWord\">install abiword</a>.",
|
||||
"pad.modals.connected": "Connected.",
|
||||
"pad.modals.reconnecting": "Reconnectin til yer pad..",
|
||||
"pad.modals.forcereconnect": "Force reconnect",
|
||||
|
@ -77,7 +76,7 @@
|
|||
"pad.modals.disconnected.explanation": "The connection til the server wis loast",
|
||||
"pad.modals.disconnected.cause": "The server micht be onavailable. Please notify the service admeenistrater gif this continues tae happen.",
|
||||
"pad.share": "Share this pad",
|
||||
"pad.share.readonly": "Read anely",
|
||||
"pad.share.readonly": "Read yinly",
|
||||
"pad.share.link": "Airtin",
|
||||
"pad.share.emebdcode": "Embed URL",
|
||||
"pad.chat": "Chait",
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
"Shangkuanlc",
|
||||
"Shirayuki",
|
||||
"Simon Shek",
|
||||
"Wehwei",
|
||||
"Winston Sung"
|
||||
"Wehwei"
|
||||
]
|
||||
},
|
||||
"admin.page-title": "管理員面板 - Etherpad",
|
||||
|
@ -135,7 +134,7 @@
|
|||
"pad.chat.writeMessage.placeholder": "在此編寫您的訊息",
|
||||
"timeslider.followContents": "關注記事本內容更新",
|
||||
"timeslider.pageTitle": "{{appTitle}}時間軸",
|
||||
"timeslider.toolbar.returnbutton": "返回記事本",
|
||||
"timeslider.toolbar.returnbutton": "返回到記事本",
|
||||
"timeslider.toolbar.authors": "協作者:",
|
||||
"timeslider.toolbar.authorsList": "無協作者",
|
||||
"timeslider.toolbar.exportlink.title": "匯出",
|
||||
|
|
|
@ -33,7 +33,7 @@ const exportTxt = require('../utils/ExportTxt');
|
|||
const importHtml = require('../utils/ImportHtml');
|
||||
const cleanText = require('./Pad').cleanText;
|
||||
const PadDiff = require('../utils/padDiff');
|
||||
const {checkValidRev, isInt} = require('../utils/checkValidRev');
|
||||
const { checkValidRev, isInt } = require('../utils/checkValidRev');
|
||||
|
||||
/* ********************
|
||||
* GROUP FUNCTIONS ****
|
||||
|
@ -193,13 +193,6 @@ Example returns:
|
|||
{code: 1, message:"padID does not exist", data: null}
|
||||
{code: 1, message:"text too long", data: null}
|
||||
*/
|
||||
/**
|
||||
*
|
||||
* @param {String} padID the id of the pad
|
||||
* @param {String} text the text of the pad
|
||||
* @param {String} authorId the id of the author, defaulting to empty string
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
exports.setText = async (padID, text, authorId = '') => {
|
||||
// text is required
|
||||
if (typeof text !== 'string') {
|
||||
|
@ -221,10 +214,7 @@ Example returns:
|
|||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
{code: 1, message:"text too long", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
@param {String} text the text of the pad
|
||||
@param {String} authorId the id of the author, defaulting to empty string
|
||||
*/
|
||||
*/
|
||||
exports.appendText = async (padID, text, authorId = '') => {
|
||||
// text is required
|
||||
if (typeof text !== 'string') {
|
||||
|
@ -243,9 +233,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: {text:"Welcome <strong>Text</strong>"}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
@param {String} rev the revision number, defaulting to the latest revision
|
||||
@return {Promise<{html: string}>} the html of the pad
|
||||
*/
|
||||
exports.getHTML = async (padID, rev) => {
|
||||
if (rev !== undefined) {
|
||||
|
@ -278,10 +265,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
|
||||
@param {String} padID the id of the pad
|
||||
@param {String} html the html of the pad
|
||||
@param {String} authorId the id of the author, defaulting to empty string
|
||||
*/
|
||||
exports.setHTML = async (padID, html, authorId = '') => {
|
||||
// html string is required
|
||||
|
@ -320,9 +303,6 @@ Example returns:
|
|||
{code: 1, message:"start is higher or equal to the current chatHead", data: null}
|
||||
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
@param {Number} start the start point of the chat-history
|
||||
@param {Number} end the end point of the chat-history
|
||||
*/
|
||||
exports.getChatHistory = async (padID, start, end) => {
|
||||
if (start && end) {
|
||||
|
@ -369,10 +349,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
@param {String} text the text of the chat-message
|
||||
@param {String} authorID the id of the author
|
||||
@param {Number} time the timestamp of the chat-message
|
||||
*/
|
||||
exports.appendChatMessage = async (padID, text, authorID, time) => {
|
||||
// text is required
|
||||
|
@ -402,7 +378,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: {revisions: 56}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
*/
|
||||
exports.getRevisionsCount = async (padID) => {
|
||||
// get the pad
|
||||
|
@ -417,7 +392,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: {savedRevisions: 42}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
*/
|
||||
exports.getSavedRevisionsCount = async (padID) => {
|
||||
// get the pad
|
||||
|
@ -432,7 +406,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: {savedRevisions: [2, 42, 1337]}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
*/
|
||||
exports.listSavedRevisions = async (padID) => {
|
||||
// get the pad
|
||||
|
@ -447,8 +420,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
@param {Number} rev the revision number, defaulting to the latest revision
|
||||
*/
|
||||
exports.saveRevision = async (padID, rev) => {
|
||||
// check if rev is a number
|
||||
|
@ -480,8 +451,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: {lastEdited: 1340815946602}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
@return {Promise<{lastEdited: number}>} the timestamp of the last revision of the pad
|
||||
*/
|
||||
exports.getLastEdited = async (padID) => {
|
||||
// get the pad
|
||||
|
@ -497,9 +466,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"pad does already exist", data: null}
|
||||
@param {String} padName the name of the new pad
|
||||
@param {String} text the initial text of the pad
|
||||
@param {String} authorId the id of the author, defaulting to empty string
|
||||
*/
|
||||
exports.createPad = async (padID, text, authorId = '') => {
|
||||
if (padID) {
|
||||
|
@ -525,7 +491,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
*/
|
||||
exports.deletePad = async (padID) => {
|
||||
const pad = await getPadSafe(padID, true);
|
||||
|
@ -539,9 +504,6 @@ exports.deletePad = async (padID) => {
|
|||
|
||||
{code:0, message:"ok", data:null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
@param {Number} rev the revision number, defaulting to the latest revision
|
||||
@param {String} authorId the id of the author, defaulting to empty string
|
||||
*/
|
||||
exports.restoreRevision = async (padID, rev, authorId = '') => {
|
||||
// check if rev is a number
|
||||
|
@ -606,9 +568,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: {padID: destinationID}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} sourceID the id of the source pad
|
||||
@param {String} destinationID the id of the destination pad
|
||||
@param {Boolean} force whether to overwrite the destination pad if it exists
|
||||
*/
|
||||
exports.copyPad = async (sourceID, destinationID, force) => {
|
||||
const pad = await getPadSafe(sourceID, true);
|
||||
|
@ -623,10 +582,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: {padID: destinationID}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} sourceID the id of the source pad
|
||||
@param {String} destinationID the id of the destination pad
|
||||
@param {Boolean} force whether to overwrite the destination pad if it exists
|
||||
@param {String} authorId the id of the author, defaulting to empty string
|
||||
*/
|
||||
exports.copyPadWithoutHistory = async (sourceID, destinationID, force, authorId = '') => {
|
||||
const pad = await getPadSafe(sourceID, true);
|
||||
|
@ -641,9 +596,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: {padID: destinationID}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} sourceID the id of the source pad
|
||||
@param {String} destinationID the id of the destination pad
|
||||
@param {Boolean} force whether to overwrite the destination pad if it exists
|
||||
*/
|
||||
exports.movePad = async (sourceID, destinationID, force) => {
|
||||
const pad = await getPadSafe(sourceID, true);
|
||||
|
@ -658,7 +610,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
*/
|
||||
exports.getReadOnlyID = async (padID) => {
|
||||
// we don't need the pad object, but this function does all the security stuff for us
|
||||
|
@ -677,7 +628,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: {padID: padID}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} roID the readonly id of the pad
|
||||
*/
|
||||
exports.getPadID = async (roID) => {
|
||||
// get the PadId
|
||||
|
@ -696,8 +646,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: null}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
@param {Boolean} publicStatus the public status of the pad
|
||||
*/
|
||||
exports.setPublicStatus = async (padID, publicStatus) => {
|
||||
// ensure this is a group pad
|
||||
|
@ -721,7 +669,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: {publicStatus: true}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
*/
|
||||
exports.getPublicStatus = async (padID) => {
|
||||
// ensure this is a group pad
|
||||
|
@ -739,7 +686,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: {authorIDs : ["a.s8oes9dhwrvt0zif", "a.akf8finncvomlqva"]}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
*/
|
||||
exports.listAuthorsOfPad = async (padID) => {
|
||||
// get the pad
|
||||
|
@ -769,8 +715,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok"}
|
||||
{code: 1, message:"padID does not exist"}
|
||||
@param {String} padID the id of the pad
|
||||
@param {String} msg the message to send
|
||||
*/
|
||||
|
||||
exports.sendClientsMessage = async (padID, msg) => {
|
||||
|
@ -796,8 +740,6 @@ Example returns:
|
|||
|
||||
{code: 0, message:"ok", data: {chatHead: 42}}
|
||||
{code: 1, message:"padID does not exist", data: null}
|
||||
@param {String} padID the id of the pad
|
||||
@return {Promise<{chatHead: number}>} the chatHead of the pad
|
||||
*/
|
||||
exports.getChatHead = async (padID) => {
|
||||
// get the pad
|
||||
|
@ -821,9 +763,7 @@ Example returns:
|
|||
}
|
||||
}
|
||||
{"code":4,"message":"no or wrong API Key","data":null}
|
||||
@param {String} padID the id of the pad
|
||||
@param {Number} startRev the start revision number
|
||||
@param {Number} endRev the end revision number
|
||||
|
||||
*/
|
||||
exports.createDiffHTML = async (padID, startRev, endRev) => {
|
||||
// check if startRev is a number
|
||||
|
@ -839,9 +779,11 @@ exports.createDiffHTML = async (padID, startRev, endRev) => {
|
|||
// get the pad
|
||||
const pad = await getPadSafe(padID, true);
|
||||
const headRev = pad.getHeadRevisionNumber();
|
||||
if (startRev > headRev) startRev = headRev;
|
||||
if (startRev > headRev)
|
||||
startRev = headRev;
|
||||
|
||||
if (endRev > headRev) endRev = headRev;
|
||||
if (endRev > headRev)
|
||||
endRev = headRev;
|
||||
|
||||
let padDiff;
|
||||
try {
|
||||
|
@ -868,6 +810,7 @@ exports.createDiffHTML = async (padID, startRev, endRev) => {
|
|||
{"code":0,"message":"ok","data":{"totalPads":3,"totalSessions": 2,"totalActivePads": 1}}
|
||||
{"code":4,"message":"no or wrong API Key","data":null}
|
||||
*/
|
||||
|
||||
exports.getStats = async () => {
|
||||
const sessionInfos = padMessageHandler.sessioninfos;
|
||||
|
||||
|
|
|
@ -93,7 +93,6 @@ exports.getColorPalette = () => [
|
|||
|
||||
/**
|
||||
* Checks if the author exists
|
||||
* @param {String} authorID The id of the author
|
||||
*/
|
||||
exports.doesAuthorExist = async (authorID) => {
|
||||
const author = await db.get(`globalAuthor:${authorID}`);
|
||||
|
@ -101,12 +100,50 @@ exports.doesAuthorExist = async (authorID) => {
|
|||
return author != null;
|
||||
};
|
||||
|
||||
/**
|
||||
exported for backwards compatibility
|
||||
@param {String} authorID The id of the author
|
||||
*/
|
||||
/* exported for backwards compatibility */
|
||||
exports.doesAuthorExists = exports.doesAuthorExist;
|
||||
|
||||
const getAuthor4Token = async (token) => {
|
||||
const author = await mapAuthorWithDBKey('token2author', token);
|
||||
|
||||
// return only the sub value authorID
|
||||
return author ? author.authorID : author;
|
||||
};
|
||||
|
||||
exports.getAuthorId = async (token, user) => {
|
||||
const context = {dbKey: token, token, user};
|
||||
let [authorId] = await hooks.aCallFirst('getAuthorId', context);
|
||||
if (!authorId) authorId = await getAuthor4Token(context.dbKey);
|
||||
return authorId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the AuthorID for a token.
|
||||
*
|
||||
* @deprecated Use `getAuthorId` instead.
|
||||
* @param {String} token The token
|
||||
*/
|
||||
exports.getAuthor4Token = async (token) => {
|
||||
warnDeprecated(
|
||||
'AuthorManager.getAuthor4Token() is deprecated; use AuthorManager.getAuthorId() instead');
|
||||
return await getAuthor4Token(token);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the AuthorID for a mapper.
|
||||
* @param {String} token The mapper
|
||||
* @param {String} name The name of the author (optional)
|
||||
*/
|
||||
exports.createAuthorIfNotExistsFor = async (authorMapper, name) => {
|
||||
const author = await mapAuthorWithDBKey('mapper2author', authorMapper);
|
||||
|
||||
if (name) {
|
||||
// set the name of this author
|
||||
await exports.setAuthorName(author.authorID, name);
|
||||
}
|
||||
|
||||
return author;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the AuthorID for a mapper. We can map using a mapperkey,
|
||||
|
@ -137,60 +174,6 @@ const mapAuthorWithDBKey = async (mapperkey, mapper) => {
|
|||
return {authorID: author};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the AuthorID for a token.
|
||||
* @param {String} token The token of the author
|
||||
* @return {Promise<string|*|{authorID: string}|{authorID: *}>}
|
||||
*/
|
||||
const getAuthor4Token = async (token) => {
|
||||
const author = await mapAuthorWithDBKey('token2author', token);
|
||||
|
||||
// return only the sub value authorID
|
||||
return author ? author.authorID : author;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the AuthorID for a token.
|
||||
* @param {String} token
|
||||
* @param {Object} user
|
||||
* @return {Promise<*>}
|
||||
*/
|
||||
exports.getAuthorId = async (token, user) => {
|
||||
const context = {dbKey: token, token, user};
|
||||
let [authorId] = await hooks.aCallFirst('getAuthorId', context);
|
||||
if (!authorId) authorId = await getAuthor4Token(context.dbKey);
|
||||
return authorId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the AuthorID for a token.
|
||||
*
|
||||
* @deprecated Use `getAuthorId` instead.
|
||||
* @param {String} token The token
|
||||
*/
|
||||
exports.getAuthor4Token = async (token) => {
|
||||
warnDeprecated(
|
||||
'AuthorManager.getAuthor4Token() is deprecated; use AuthorManager.getAuthorId() instead');
|
||||
return await getAuthor4Token(token);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the AuthorID for a mapper.
|
||||
* @param {String} authorMapper The mapper
|
||||
* @param {String} name The name of the author (optional)
|
||||
*/
|
||||
exports.createAuthorIfNotExistsFor = async (authorMapper, name) => {
|
||||
const author = await mapAuthorWithDBKey('mapper2author', authorMapper);
|
||||
|
||||
if (name) {
|
||||
// set the name of this author
|
||||
await exports.setAuthorName(author.authorID, name);
|
||||
}
|
||||
|
||||
return author;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Internal function that creates the database entry for an author
|
||||
* @param {String} name The name of the author
|
||||
|
@ -248,7 +231,7 @@ exports.setAuthorName = async (author, name) => await db.setSub(
|
|||
|
||||
/**
|
||||
* Returns an array of all pads this author contributed to
|
||||
* @param {String} authorID The id of the author
|
||||
* @param {String} author The id of the author
|
||||
*/
|
||||
exports.listPadsOfAuthor = async (authorID) => {
|
||||
/* There are two other places where this array is manipulated:
|
||||
|
@ -272,7 +255,7 @@ exports.listPadsOfAuthor = async (authorID) => {
|
|||
|
||||
/**
|
||||
* Adds a new pad to the list of contributions
|
||||
* @param {String} authorID The id of the author
|
||||
* @param {String} author The id of the author
|
||||
* @param {String} padID The id of the pad the author contributes to
|
||||
*/
|
||||
exports.addPad = async (authorID, padID) => {
|
||||
|
@ -299,7 +282,7 @@ exports.addPad = async (authorID, padID) => {
|
|||
|
||||
/**
|
||||
* Removes a pad from the list of contributions
|
||||
* @param {String} authorID The id of the author
|
||||
* @param {String} author The id of the author
|
||||
* @param {String} padID The id of the pad the author contributes to
|
||||
*/
|
||||
exports.removePad = async (authorID, padID) => {
|
||||
|
|
|
@ -25,10 +25,6 @@ const db = require('./DB');
|
|||
const padManager = require('./PadManager');
|
||||
const sessionManager = require('./SessionManager');
|
||||
|
||||
/**
|
||||
* Lists all groups
|
||||
* @return {Promise<{groupIDs: string[]}>} The ids of all groups
|
||||
*/
|
||||
exports.listAllGroups = async () => {
|
||||
let groups = await db.get('groups');
|
||||
groups = groups || {};
|
||||
|
@ -37,11 +33,6 @@ exports.listAllGroups = async () => {
|
|||
return {groupIDs};
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes a group and all associated pads
|
||||
* @param {String} groupID The id of the group
|
||||
* @return {Promise<void>} Resolves when the group is deleted
|
||||
*/
|
||||
exports.deleteGroup = async (groupID) => {
|
||||
const group = await db.get(`group:${groupID}`);
|
||||
|
||||
|
@ -77,11 +68,6 @@ exports.deleteGroup = async (groupID) => {
|
|||
await db.remove(`group:${groupID}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a group exists
|
||||
* @param {String} groupID the id of the group to delete
|
||||
* @return {Promise<boolean>} Resolves to true if the group exists
|
||||
*/
|
||||
exports.doesGroupExist = async (groupID) => {
|
||||
// try to get the group entry
|
||||
const group = await db.get(`group:${groupID}`);
|
||||
|
@ -89,10 +75,6 @@ exports.doesGroupExist = async (groupID) => {
|
|||
return (group != null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new group
|
||||
* @return {Promise<{groupID: string}>} the id of the new group
|
||||
*/
|
||||
exports.createGroup = async () => {
|
||||
const groupID = `g.${randomString(16)}`;
|
||||
await db.set(`group:${groupID}`, {pads: {}, mappings: {}});
|
||||
|
@ -103,11 +85,6 @@ exports.createGroup = async () => {
|
|||
return {groupID};
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new group if it does not exist already and returns the group ID
|
||||
* @param groupMapper the mapper of the group
|
||||
* @return {Promise<{groupID: string}|{groupID: *}>} a promise that resolves to the group ID
|
||||
*/
|
||||
exports.createGroupIfNotExistsFor = async (groupMapper) => {
|
||||
if (typeof groupMapper !== 'string') {
|
||||
throw new CustomError('groupMapper is not a string', 'apierror');
|
||||
|
@ -126,14 +103,6 @@ exports.createGroupIfNotExistsFor = async (groupMapper) => {
|
|||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a group pad
|
||||
* @param {String} groupID The id of the group
|
||||
* @param {String} padName The name of the pad
|
||||
* @param {String} text The text of the pad
|
||||
* @param {String} authorId The id of the author
|
||||
* @return {Promise<{padID: string}>} a promise that resolves to the id of the new pad
|
||||
*/
|
||||
exports.createGroupPad = async (groupID, padName, text, authorId = '') => {
|
||||
// create the padID
|
||||
const padID = `${groupID}$${padName}`;
|
||||
|
@ -162,11 +131,6 @@ exports.createGroupPad = async (groupID, padName, text, authorId = '') => {
|
|||
return {padID};
|
||||
};
|
||||
|
||||
/**
|
||||
* Lists all pads of a group
|
||||
* @param {String} groupID The id of the group
|
||||
* @return {Promise<{padIDs: string[]}>} a promise that resolves to the ids of all pads of the group
|
||||
*/
|
||||
exports.listPads = async (groupID) => {
|
||||
const exists = await exports.doesGroupExist(groupID);
|
||||
|
||||
|
|
|
@ -25,8 +25,7 @@ const promises = require('../utils/promises');
|
|||
/**
|
||||
* Copied from the Etherpad source code. It converts Windows line breaks to Unix
|
||||
* line breaks and convert Tabs to spaces
|
||||
* @param {String} txt The text to clean
|
||||
* @returns {String} The cleaned text
|
||||
* @param txt
|
||||
*/
|
||||
exports.cleanText = (txt) => txt.replace(/\r\n/g, '\n')
|
||||
.replace(/\r/g, '\n')
|
||||
|
@ -74,16 +73,9 @@ class Pad {
|
|||
return this.publicStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a new revision
|
||||
* @param {Object} aChangeset The changeset to append to the pad
|
||||
* @param {String} authorId The id of the author
|
||||
* @return {Promise<number|string>}
|
||||
*/
|
||||
async appendRevision(aChangeset, authorId = '') {
|
||||
const newAText = Changeset.applyToAText(aChangeset, this.atext, this.pool);
|
||||
if (newAText.text === this.atext.text && newAText.attribs === this.atext.attribs &&
|
||||
this.head !== -1) {
|
||||
if (newAText.text === this.atext.text && newAText.attribs === this.atext.attribs) {
|
||||
return this.head;
|
||||
}
|
||||
Changeset.copyAText(newAText, this.atext);
|
||||
|
@ -166,10 +158,6 @@ class Pad {
|
|||
return await this.db.getSub(`pad:${this.id}:revs:${revNum}`, ['meta', 'atext']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all authors that worked on this pad
|
||||
* @return {[String]} The id of authors who contributed to this pad
|
||||
*/
|
||||
getAllAuthors() {
|
||||
const authorIds = [];
|
||||
|
||||
|
@ -185,7 +173,8 @@ class Pad {
|
|||
async getInternalRevisionAText(targetRev) {
|
||||
const keyRev = this.getKeyRevisionNumber(targetRev);
|
||||
const headRev = this.getHeadRevisionNumber();
|
||||
if (targetRev > headRev) targetRev = headRev;
|
||||
if (targetRev > headRev)
|
||||
targetRev = headRev;
|
||||
const [keyAText, changesets] = await Promise.all([
|
||||
this._getKeyRevisionAText(keyRev),
|
||||
Promise.all(
|
||||
|
|
|
@ -59,7 +59,6 @@ const padList = new class {
|
|||
|
||||
/**
|
||||
* Returns all pads in alphabetical order as array.
|
||||
* @returns {Promise<string[]>} A promise that resolves to an array of pad IDs.
|
||||
*/
|
||||
async getPads() {
|
||||
if (!this._loaded) {
|
||||
|
|
|
@ -26,15 +26,13 @@ const randomString = require('../utils/randomstring');
|
|||
|
||||
/**
|
||||
* checks if the id pattern matches a read-only pad id
|
||||
* @param {String} id the pad's id
|
||||
* @return {Boolean} true if the id is readonly
|
||||
* @param {String} the pad's id
|
||||
*/
|
||||
exports.isReadOnlyId = (id) => id.startsWith('r.');
|
||||
|
||||
/**
|
||||
* returns a read only id for a pad
|
||||
* @param {String} padId the id of the pad
|
||||
* @return {String} the read only id
|
||||
*/
|
||||
exports.getReadOnlyId = async (padId) => {
|
||||
// check if there is a pad2readonly entry
|
||||
|
@ -55,14 +53,12 @@ exports.getReadOnlyId = async (padId) => {
|
|||
/**
|
||||
* returns the padId for a read only id
|
||||
* @param {String} readOnlyId read only id
|
||||
* @return {String} the padId
|
||||
*/
|
||||
exports.getPadId = async (readOnlyId) => await db.get(`readonly2pad:${readOnlyId}`);
|
||||
|
||||
/**
|
||||
* returns the padId and readonlyPadId in an object for any id
|
||||
* @param {String} id read only id or real pad id
|
||||
* @return {Object} an object with the padId and readonlyPadId
|
||||
* @param {String} padIdOrReadonlyPadId read only id or real pad id
|
||||
*/
|
||||
exports.getIds = async (id) => {
|
||||
const readonly = exports.isReadOnlyId(id);
|
||||
|
|
|
@ -49,11 +49,6 @@ const DENY = Object.freeze({accessStatus: 'deny'});
|
|||
*
|
||||
* WARNING: Tokens and session IDs MUST be kept secret, otherwise users will be able to impersonate
|
||||
* each other (which might allow them to gain privileges).
|
||||
* @param {String} padID
|
||||
* @param {String} sessionCookie
|
||||
* @param {String} token
|
||||
* @param {Object} userSettings
|
||||
* @return {DENY|{accessStatus: String, authorID: String}}
|
||||
*/
|
||||
exports.checkAccess = async (padID, sessionCookie, token, userSettings) => {
|
||||
if (!padID) {
|
||||
|
|
|
@ -81,11 +81,6 @@ exports.findAuthorID = async (groupID, sessionCookie) => {
|
|||
return sessionInfo.authorID;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a session exists
|
||||
* @param {String} sessionID The id of the session
|
||||
* @return {Promise<boolean>} Resolves to true if the session exists
|
||||
*/
|
||||
exports.doesSessionExist = async (sessionID) => {
|
||||
// check if the database entry of this session exists
|
||||
const session = await db.get(`session:${sessionID}`);
|
||||
|
@ -94,10 +89,6 @@ exports.doesSessionExist = async (sessionID) => {
|
|||
|
||||
/**
|
||||
* Creates a new session between an author and a group
|
||||
* @param {String} groupID The id of the group
|
||||
* @param {String} authorID The id of the author
|
||||
* @param {Number} validUntil The unix timestamp when the session should expire
|
||||
* @return {Promise<{sessionID: string}>} the id of the new session
|
||||
*/
|
||||
exports.createSession = async (groupID, authorID, validUntil) => {
|
||||
// check if the group exists
|
||||
|
@ -155,11 +146,6 @@ exports.createSession = async (groupID, authorID, validUntil) => {
|
|||
return {sessionID};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the sessioninfos for a session
|
||||
* @param {String} sessionID The id of the session
|
||||
* @return {Promise<Object>} the sessioninfos
|
||||
*/
|
||||
exports.getSessionInfo = async (sessionID) => {
|
||||
// check if the database entry of this session exists
|
||||
const session = await db.get(`session:${sessionID}`);
|
||||
|
@ -175,8 +161,6 @@ exports.getSessionInfo = async (sessionID) => {
|
|||
|
||||
/**
|
||||
* Deletes a session
|
||||
* @param {String} sessionID The id of the session
|
||||
* @return {Promise<void>} Resolves when the session is deleted
|
||||
*/
|
||||
exports.deleteSession = async (sessionID) => {
|
||||
// ensure that the session exists
|
||||
|
@ -202,11 +186,6 @@ exports.deleteSession = async (sessionID) => {
|
|||
await db.remove(`session:${sessionID}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an array of all sessions of a group
|
||||
* @param {String} groupID The id of the group
|
||||
* @return {Promise<Object>} The sessioninfos of all sessions of this group
|
||||
*/
|
||||
exports.listSessionsOfGroup = async (groupID) => {
|
||||
// check that the group exists
|
||||
const exists = await groupManager.doesGroupExist(groupID);
|
||||
|
@ -218,11 +197,6 @@ exports.listSessionsOfGroup = async (groupID) => {
|
|||
return sessions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an array of all sessions of an author
|
||||
* @param {String} authorID The id of the author
|
||||
* @return {Promise<Object>} The sessioninfos of all sessions of this author
|
||||
*/
|
||||
exports.listSessionsOfAuthor = async (authorID) => {
|
||||
// check that the author exists
|
||||
const exists = await authorManager.doesAuthorExist(authorID);
|
||||
|
@ -230,16 +204,12 @@ exports.listSessionsOfAuthor = async (authorID) => {
|
|||
throw new CustomError('authorID does not exist', 'apierror');
|
||||
}
|
||||
|
||||
return await listSessionsWithDBKey(`author2sessions:${authorID}`);
|
||||
const sessions = await listSessionsWithDBKey(`author2sessions:${authorID}`);
|
||||
return sessions;
|
||||
};
|
||||
|
||||
// this function is basically the code listSessionsOfAuthor and listSessionsOfGroup has in common
|
||||
// required to return null rather than an empty object if there are none
|
||||
/**
|
||||
* Returns an array of all sessions of a group
|
||||
* @param {String} dbkey The db key to use to get the sessions
|
||||
* @return {Promise<*>}
|
||||
*/
|
||||
const listSessionsWithDBKey = async (dbkey) => {
|
||||
// get the group2sessions entry
|
||||
const sessionObject = await db.get(dbkey);
|
||||
|
@ -248,7 +218,8 @@ const listSessionsWithDBKey = async (dbkey) => {
|
|||
// iterate through the sessions and get the sessioninfos
|
||||
for (const sessionID of Object.keys(sessions || {})) {
|
||||
try {
|
||||
sessions[sessionID] = await exports.getSessionInfo(sessionID);
|
||||
const sessionInfo = await exports.getSessionInfo(sessionID);
|
||||
sessions[sessionID] = sessionInfo;
|
||||
} catch (err) {
|
||||
if (err.name === 'apierror') {
|
||||
console.warn(`Found bad session ${sessionID} in ${dbkey}`);
|
||||
|
@ -262,9 +233,5 @@ const listSessionsWithDBKey = async (dbkey) => {
|
|||
return sessions;
|
||||
};
|
||||
|
||||
/**
|
||||
* checks if a number is an int
|
||||
* @param {number|string} value
|
||||
* @return {boolean} If the value is an integer
|
||||
*/
|
||||
// checks if a number is an int
|
||||
const isInt = (value) => (parseFloat(value) === parseInt(value)) && !isNaN(value);
|
||||
|
|
|
@ -165,13 +165,12 @@ exports.version = version;
|
|||
|
||||
/**
|
||||
* Handles a HTTP API call
|
||||
* @param {String} apiVersion the version of the api
|
||||
* @param {String} functionName the name of the called function
|
||||
* @param functionName the name of the called function
|
||||
* @param fields the params of the called function
|
||||
* @req express request object
|
||||
* @res express response object
|
||||
*/
|
||||
exports.handle = async function (apiVersion, functionName, fields) {
|
||||
exports.handle = async function (apiVersion, functionName, fields, req, res) {
|
||||
// say goodbye if this is an unknown API version
|
||||
if (!(apiVersion in version)) {
|
||||
throw new createHTTPError.NotFound('no such api version');
|
||||
|
|
|
@ -38,11 +38,6 @@ const tempDirectory = os.tmpdir();
|
|||
|
||||
/**
|
||||
* do a requested export
|
||||
* @param {Object} req the request object
|
||||
* @param {Object} res the response object
|
||||
* @param {String} padId the pad id to export
|
||||
* @param {String} readOnlyId the read only id of the pad to export
|
||||
* @param {String} type the type to export
|
||||
*/
|
||||
exports.doExport = async (req, res, padId, readOnlyId, type) => {
|
||||
// avoid naming the read-only file as the original pad's id
|
||||
|
|
|
@ -73,10 +73,6 @@ const tmpDirectory = os.tmpdir();
|
|||
|
||||
/**
|
||||
* do a requested import
|
||||
* @param {Object} req the request object
|
||||
* @param {Object} res the response object
|
||||
* @param {String} padId the pad id to export
|
||||
* @param {String} authorId the author id to use for the import
|
||||
*/
|
||||
const doImport = async (req, res, padId, authorId) => {
|
||||
// pipe to a file
|
||||
|
@ -93,24 +89,24 @@ const doImport = async (req, res, padId, authorId) => {
|
|||
maxFileSize: settings.importMaxFileSize,
|
||||
});
|
||||
|
||||
let srcFile;
|
||||
let files;
|
||||
let fields;
|
||||
try {
|
||||
[fields, files] = await form.parse(req);
|
||||
} catch (err) {
|
||||
logger.warn(`Import failed due to form error: ${err.stack || err}`);
|
||||
if (err.code === Formidable.formidableErrors.biggerThanMaxFileSize) {
|
||||
throw new ImportError('maxFileSize');
|
||||
}
|
||||
throw new ImportError('uploadFailed');
|
||||
}
|
||||
if (!files.file) {
|
||||
logger.warn('Import failed because form had no file');
|
||||
throw new ImportError('uploadFailed');
|
||||
} else {
|
||||
srcFile = files.file[0].filepath;
|
||||
}
|
||||
// locally wrapped Promise, since form.parse requires a callback
|
||||
let srcFile = await new Promise((resolve, reject) => {
|
||||
form.parse(req, (err, fields, files) => {
|
||||
if (err != null) {
|
||||
logger.warn(`Import failed due to form error: ${err.stack || err}`);
|
||||
// I hate doing indexOf here but I can't see anything to use...
|
||||
if (err && err.stack && err.stack.indexOf('maxFileSize') !== -1) {
|
||||
return reject(new ImportError('maxFileSize'));
|
||||
}
|
||||
return reject(new ImportError('uploadFailed'));
|
||||
}
|
||||
if (!files.file) {
|
||||
logger.warn('Import failed because form had no file');
|
||||
return reject(new ImportError('uploadFailed'));
|
||||
}
|
||||
resolve(files.file.filepath);
|
||||
});
|
||||
});
|
||||
|
||||
// ensure this is a file ending we know, else we change the file ending to .txt
|
||||
// this allows us to accept source code files like .c or .java
|
||||
|
@ -237,14 +233,6 @@ const doImport = async (req, res, padId, authorId) => {
|
|||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the request to import a file
|
||||
* @param {Request} req the request object
|
||||
* @param {Response} res the response object
|
||||
* @param {String} padId the pad id to export
|
||||
* @param {String} authorId the author id to use for the import
|
||||
* @return {Promise<void>} a promise
|
||||
*/
|
||||
exports.doImport = async (req, res, padId, authorId = '') => {
|
||||
let httpStatus = 200;
|
||||
let code = 0;
|
||||
|
|
|
@ -35,9 +35,8 @@ const components = {};
|
|||
|
||||
let io;
|
||||
|
||||
/** adds a component
|
||||
* @param {string} moduleName
|
||||
* @param {Module} module
|
||||
/**
|
||||
* adds a component
|
||||
*/
|
||||
exports.addComponent = (moduleName, module) => {
|
||||
if (module == null) return exports.deleteComponent(moduleName);
|
||||
|
@ -45,15 +44,10 @@ exports.addComponent = (moduleName, module) => {
|
|||
module.setSocketIO(io);
|
||||
};
|
||||
|
||||
/**
|
||||
* removes a component
|
||||
* @param {Module} moduleName
|
||||
*/
|
||||
exports.deleteComponent = (moduleName) => { delete components[moduleName]; };
|
||||
|
||||
/**
|
||||
* sets the socket.io and adds event functions for routing
|
||||
* @param {Object} _io the socket.io instance
|
||||
*/
|
||||
exports.setSocketIO = (_io) => {
|
||||
io = _io;
|
||||
|
|
|
@ -1,14 +1,6 @@
|
|||
'use strict';
|
||||
const eejs = require('../../eejs');
|
||||
|
||||
|
||||
/**
|
||||
* Add the admin navigation link
|
||||
* @param hookName {String} the name of the hook
|
||||
* @param args {Object} the object containing the arguments
|
||||
* @param {Function} cb the callback function
|
||||
* @return {*}
|
||||
*/
|
||||
exports.expressCreateServer = (hookName, args, cb) => {
|
||||
args.app.get('/admin', (req, res) => {
|
||||
if ('/' !== req.path[req.path.length - 1]) return res.redirect('./admin/');
|
||||
|
|
|
@ -62,7 +62,7 @@ exports.socketio = (hookName, args, cb) => {
|
|||
const currentVersion = pluginDefs.plugins[plugin].package.version;
|
||||
|
||||
return semver.gt(latestVersion, currentVersion);
|
||||
});
|
||||
}).map((plugin) => ({name: plugin, version: results[plugin].version}));
|
||||
|
||||
socket.emit('results:updatable', {updatable});
|
||||
} catch (err) {
|
||||
|
@ -98,8 +98,8 @@ exports.socketio = (hookName, args, cb) => {
|
|||
}
|
||||
});
|
||||
|
||||
socket.on('install', (pluginName) => {
|
||||
installer.install(pluginName, (err) => {
|
||||
socket.on('install', (pluginName, version) => {
|
||||
installer.install(pluginName, version, (err) => {
|
||||
if (err) console.warn(err.stack || err.toString());
|
||||
|
||||
socket.emit('finished:install', {
|
||||
|
@ -121,13 +121,6 @@ exports.socketio = (hookName, args, cb) => {
|
|||
return cb();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sorts a list of plugins by a property
|
||||
* @param {Object} plugins The plugins to sort
|
||||
* @param {Object} property The property to sort by
|
||||
* @param {String} dir The directory of the plugin
|
||||
* @return {Object[]}
|
||||
*/
|
||||
const sortPluginList = (plugins, property, /* ASC?*/dir) => plugins.sort((a, b) => {
|
||||
if (a[property] < b[property]) {
|
||||
return dir ? -1 : 1;
|
||||
|
|
|
@ -8,19 +8,20 @@ const util = require('util');
|
|||
|
||||
exports.expressPreSession = async (hookName, {app}) => {
|
||||
// The Etherpad client side sends information about how a disconnect happened
|
||||
app.post('/ep/pad/connection-diagnostic-info', async (req, res) => {
|
||||
const [fields, files] = await (new Formidable({})).parse(req);
|
||||
clientLogger.info(`DIAGNOSTIC-INFO: ${fields.diagnosticInfo}`);
|
||||
res.end('OK');
|
||||
app.post('/ep/pad/connection-diagnostic-info', (req, res) => {
|
||||
new Formidable().parse(req, (err, fields, files) => {
|
||||
clientLogger.info(`DIAGNOSTIC-INFO: ${fields.diagnosticInfo}`);
|
||||
res.end('OK');
|
||||
});
|
||||
});
|
||||
|
||||
const parseJserrorForm = async (req) => {
|
||||
const parseJserrorForm = async (req) => await new Promise((resolve, reject) => {
|
||||
const form = new Formidable({
|
||||
maxFileSize: 1, // Files are not expected. Not sure if 0 means unlimited, so 1 is used.
|
||||
});
|
||||
const [fields, files] = await form.parse(req);
|
||||
return fields.errorInfo;
|
||||
};
|
||||
form.on('error', (err) => reject(err));
|
||||
form.parse(req, (err, fields) => err != null ? reject(err) : resolve(fields.errorInfo));
|
||||
});
|
||||
|
||||
// The Etherpad client side sends information about client side javscript errors
|
||||
app.post('/jserror', (req, res, next) => {
|
||||
|
|
|
@ -11,16 +11,13 @@ const securityManager = require('../../db/SecurityManager');
|
|||
const webaccess = require('./webaccess');
|
||||
|
||||
exports.expressCreateServer = (hookName, args, cb) => {
|
||||
const limiter = rateLimit({
|
||||
...settings.importExportRateLimiting,
|
||||
handler: (request, response, next, options) => {
|
||||
if (request.rateLimit.current === request.rateLimit.limit + 1) {
|
||||
// when the rate limiter triggers, write a warning in the logs
|
||||
console.warn('Import/Export rate limiter triggered on ' +
|
||||
`"${request.originalUrl}" for IP address ${request.ip}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
settings.importExportRateLimiting.onLimitReached = (req, res, options) => {
|
||||
// when the rate limiter triggers, write a warning in the logs
|
||||
console.warn('Import/Export rate limiter triggered on ' +
|
||||
`"${req.originalUrl}" for IP address ${req.ip}`);
|
||||
};
|
||||
// The rate limiter is created in this hook so that restarting the server resets the limiter.
|
||||
const limiter = rateLimit(settings.importExportRateLimiting);
|
||||
|
||||
// handle export requests
|
||||
args.app.use('/p/:pad/:rev?/export/:type', limiter);
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
*/
|
||||
|
||||
const OpenAPIBackend = require('openapi-backend').default;
|
||||
const IncomingForm = require('formidable').IncomingForm;
|
||||
const formidable = require('formidable');
|
||||
const {promisify} = require('util');
|
||||
const cloneDeep = require('lodash.clonedeep');
|
||||
const createHTTPError = require('http-errors');
|
||||
|
||||
|
@ -595,13 +596,9 @@ exports.expressPreSession = async (hookName, {app}) => {
|
|||
// read form data if method was POST
|
||||
let formData = {};
|
||||
if (c.request.method === 'post') {
|
||||
const form = new IncomingForm();
|
||||
formData = (await form.parse(req))[0];
|
||||
for (const k of Object.keys(formData)) {
|
||||
if (formData[k] instanceof Array) {
|
||||
formData[k] = formData[k][0];
|
||||
}
|
||||
}
|
||||
const form = new formidable.IncomingForm();
|
||||
const parseForm = promisify(form.parse).bind(form);
|
||||
formData = await parseForm(req);
|
||||
}
|
||||
|
||||
const fields = Object.assign({}, header, params, query, formData);
|
||||
|
@ -696,20 +693,10 @@ exports.expressPreSession = async (hookName, {app}) => {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to get the current root path for an API version
|
||||
* @param {String} version The API version
|
||||
* @param {APIPathStyle} style The style of the API path
|
||||
* @return {String} The root path for the API version
|
||||
*/
|
||||
// helper to get api root
|
||||
const getApiRootForVersion = (version, style = APIPathStyle.FLAT) => `/${style}/${version}`;
|
||||
|
||||
/**
|
||||
* Helper to generate an OpenAPI server object when serving definitions
|
||||
* @param {String} apiRoot The root path for the API version
|
||||
* @param {Request} req The express request object
|
||||
* @return {url: String} The server object for the OpenAPI definition location
|
||||
*/
|
||||
// helper to generate an OpenAPI server object when serving definitions
|
||||
const generateServerForApiVersion = (apiRoot, req) => ({
|
||||
url: `${settings.ssl ? 'https' : 'http'}://${req.headers.host}${apiRoot}`,
|
||||
});
|
||||
|
|
|
@ -160,7 +160,7 @@ class SecretRotator {
|
|||
// TODO: This is racy. If two instances start up at the same time and there are no existing
|
||||
// matching publications, each will generate and publish their own paramters. In practice this
|
||||
// is unlikely to happen, and if it does it can be fixed by restarting both Etherpad instances.
|
||||
const dbKeys = await db.findKeys(`${this._dbPrefix}:*`, null) || [];
|
||||
const dbKeys = await db.findKeys(`${this._dbPrefix}:*`, null);
|
||||
let currentParams = null;
|
||||
let currentId = null;
|
||||
const dbWrites = [];
|
||||
|
|
|
@ -12,4 +12,4 @@ exports.hkdf = util.promisify(crypto.hkdf);
|
|||
/**
|
||||
* Promisified version of Node.js's crypto.randomBytes
|
||||
*/
|
||||
exports.randomBytes = util.promisify(crypto.randomBytes);
|
||||
exports.randomBytes = util.promisify(crypto.randomBytes);
|
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
|
||||
const log4js = require('log4js');
|
||||
log4js.replaceConsole();
|
||||
|
||||
const settings = require('./utils/Settings');
|
||||
|
||||
|
@ -110,7 +111,7 @@ exports.start = async () => {
|
|||
// eslint-disable-next-line promise/no-promise-in-callback
|
||||
exports.exit(err)
|
||||
.catch((err) => {
|
||||
logger.error('Error in process exit', err);
|
||||
logger.error('Error in process exit', JSON.stringify(err));
|
||||
// eslint-disable-next-line n/no-process-exit
|
||||
process.exit(1);
|
||||
});
|
||||
|
@ -275,4 +276,3 @@ exports.exit = async (err = null) => {
|
|||
};
|
||||
|
||||
if (require.main === module) exports.start();
|
||||
if (typeof(PhusionPassenger) !== 'undefined') exports.start();
|
||||
|
|
|
@ -45,7 +45,7 @@ for (let i = 0; i < argv.length; i++) {
|
|||
exports.argv.sessionkey = arg;
|
||||
}
|
||||
|
||||
// Override location of APIKEY.txt file
|
||||
// Override location of settings.json file
|
||||
if (prevArg === '--apikey') {
|
||||
exports.argv.apikey = arg;
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ const minify = async (req, res) => {
|
|||
// Go straight into node_modules
|
||||
// Avoid `require.resolve()`, since 'mustache' and 'mustache/index.js'
|
||||
// would end up resolving to logically distinct resources.
|
||||
filename = path.join('../node_modules/', library, libraryPath);
|
||||
filename = path.join('../../node_modules/', library, libraryPath);
|
||||
}
|
||||
}
|
||||
const [, testf] = /^plugins\/ep_etherpad-lite\/(tests\/frontend\/.*)/.exec(filename) || [];
|
||||
|
|
|
@ -50,24 +50,15 @@ const nonSettings = [
|
|||
|
||||
// This is a function to make it easy to create a new instance. It is important to not reuse a
|
||||
// config object after passing it to log4js.configure() because that method mutates the object. :(
|
||||
const defaultLogConfig = () => ({appenders: {console: {type: 'console'}},
|
||||
categories: {
|
||||
default: {appenders: ['console'], level: 'info'},
|
||||
}});
|
||||
const defaultLogConfig = () => ({appenders: [{type: 'console'}]});
|
||||
const defaultLogLevel = 'INFO';
|
||||
|
||||
const initLogging = (logLevel, config) => {
|
||||
// log4js.configure() modifies exports.logconfig so check for equality first.
|
||||
const logConfigIsDefault = deepEqual(config, defaultLogConfig());
|
||||
log4js.configure(config);
|
||||
log4js.getLogger('console');
|
||||
|
||||
// Overwrites for console output methods
|
||||
console.debug = logger.debug.bind(logger);
|
||||
console.log = logger.info.bind(logger);
|
||||
console.warn = logger.warn.bind(logger);
|
||||
console.error = logger.error.bind(logger);
|
||||
|
||||
log4js.setGlobalLogLevel(logLevel);
|
||||
log4js.replaceConsole();
|
||||
// Log the warning after configuring log4js to increase the chances the user will see it.
|
||||
if (!logConfigIsDefault) logger.warn('The logconfig setting is deprecated.');
|
||||
};
|
||||
|
|
|
@ -2,39 +2,28 @@
|
|||
const semver = require('semver');
|
||||
const settings = require('./Settings');
|
||||
const axios = require('axios');
|
||||
const headers = {
|
||||
'User-Agent': 'Etherpad/' + settings.getEpVersion(),
|
||||
}
|
||||
|
||||
const updateInterval = 60 * 60 * 1000; // 1 hour
|
||||
let infos;
|
||||
let lastLoadingTime = null;
|
||||
|
||||
const loadEtherpadInformations = () => {
|
||||
if (lastLoadingTime !== null && Date.now() - lastLoadingTime < updateInterval) {
|
||||
return Promise.resolve(infos);
|
||||
}
|
||||
|
||||
return axios.get('https://static.etherpad.org/info.json', {headers: headers})
|
||||
.then(async resp => {
|
||||
infos = await resp.data;
|
||||
if (infos === undefined || infos === null) {
|
||||
await Promise.reject("Could not retrieve current version")
|
||||
return
|
||||
}
|
||||
|
||||
lastLoadingTime = Date.now();
|
||||
return await Promise.resolve(infos);
|
||||
})
|
||||
.catch(async err => {
|
||||
return await Promise.reject(err);
|
||||
});
|
||||
}
|
||||
const loadEtherpadInformations = () =>
|
||||
axios.get('https://static.etherpad.org/info.json')
|
||||
.then(async resp => {
|
||||
try {
|
||||
infos = await resp.data;
|
||||
if (infos === undefined || infos === null) {
|
||||
await Promise.reject("Could not retrieve current version")
|
||||
return
|
||||
}
|
||||
return await Promise.resolve(infos);
|
||||
}
|
||||
catch (err) {
|
||||
return await Promise.reject(err);
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
exports.getLatestVersion = () => {
|
||||
exports.needsUpdate().catch();
|
||||
return infos?.latestVersion;
|
||||
exports.needsUpdate();
|
||||
return infos.latestVersion;
|
||||
};
|
||||
|
||||
exports.needsUpdate = async (cb) => {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../../node_modules/async
|
|
@ -0,0 +1 @@
|
|||
../../node_modules/express
|
|
@ -0,0 +1 @@
|
|||
../../node_modules/formidable
|
|
@ -0,0 +1 @@
|
|||
../../node_modules/log4js
|
|
@ -0,0 +1 @@
|
|||
../../node_modules/supertest
|
File diff suppressed because it is too large
Load Diff
|
@ -31,7 +31,7 @@
|
|||
],
|
||||
"dependencies": {
|
||||
"async": "^3.2.4",
|
||||
"axios": "^1.6.0",
|
||||
"axios": "^1.4.0",
|
||||
"clean-css": "^5.3.2",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cross-spawn": "^7.0.3",
|
||||
|
@ -39,35 +39,34 @@
|
|||
"etherpad-require-kernel": "^1.0.15",
|
||||
"etherpad-yajsml": "0.0.12",
|
||||
"express": "4.18.2",
|
||||
"express-rate-limit": "^7.1.3",
|
||||
"express-rate-limit": "^6.7.1",
|
||||
"express-session": "npm:@etherpad/express-session@^1.18.2",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"find-root": "1.1.0",
|
||||
"formidable": "^3.5.1",
|
||||
"formidable": "^2.1.2",
|
||||
"http-errors": "^2.0.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsdom": "^20.0.0",
|
||||
"jsonminify": "0.4.2",
|
||||
"languages4translatewiki": "0.1.3",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
"log4js": "^6.9.1",
|
||||
"log4js": "0.6.38",
|
||||
"measured-core": "^2.0.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"npm": "^6.14.18",
|
||||
"openapi-backend": "^5.10.5",
|
||||
"openapi-backend": "^5.9.2",
|
||||
"proxy-addr": "^2.0.7",
|
||||
"rate-limiter-flexible": "^3.0.3",
|
||||
"rehype": "^13.0.1",
|
||||
"rehype-minify-whitespace": "^6.0.0",
|
||||
"resolve": "1.22.8",
|
||||
"rate-limiter-flexible": "^2.4.1",
|
||||
"rehype": "^12.0.1",
|
||||
"rehype-minify-whitespace": "^5.0.1",
|
||||
"resolve": "1.22.2",
|
||||
"security": "1.0.0",
|
||||
"semver": "^7.5.4",
|
||||
"semver": "^7.5.3",
|
||||
"socket.io": "^2.5.0",
|
||||
"superagent": "^8.1.2",
|
||||
"terser": "^5.24.0",
|
||||
"superagent": "^8.0.9",
|
||||
"terser": "^5.18.2",
|
||||
"threads": "^1.7.0",
|
||||
"tinycon": "0.6.8",
|
||||
"ueberdb2": "^4.2.35",
|
||||
"ueberdb2": "^4.1.7",
|
||||
"underscore": "1.13.6",
|
||||
"unorm": "1.6.0",
|
||||
"wtfnode": "^0.9.1"
|
||||
|
@ -78,33 +77,35 @@
|
|||
"etherpad-lite": "node/server.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.52.0",
|
||||
"eslint-config-etherpad": "^3.0.22",
|
||||
"eslint": "^8.43.0",
|
||||
"eslint-config-etherpad": "^3.0.15",
|
||||
"etherpad-cli-client": "^2.0.2",
|
||||
"mocha": "^10.0.0",
|
||||
"mocha-froth": "^0.2.10",
|
||||
"nodeify": "^1.0.1",
|
||||
"openapi-schema-validation": "^0.4.2",
|
||||
"selenium-webdriver": "^4.15.0",
|
||||
"selenium-webdriver": "^4.10.0",
|
||||
"set-cookie-parser": "^2.6.0",
|
||||
"sinon": "^17.0.1",
|
||||
"sinon": "^15.2.0",
|
||||
"split-grid": "^1.0.11",
|
||||
"supertest": "^6.3.3",
|
||||
"typescript": "^4.9.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.20.1",
|
||||
"npm": ">=6.14.0"
|
||||
"npm": ">=9.8.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ether/etherpad-lite.git"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"test": "mocha --timeout 120000 --recursive tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs",
|
||||
"test-container": "mocha --timeout 5000 tests/container/specs/api"
|
||||
"lint": "../node_modules/eslint/bin/eslint.js .",
|
||||
"test": "../node_modules/mocha/bin/_mocha --timeout 120000 --recursive tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs",
|
||||
"test-on-windows": "mocha --timeout 120000 --recursive tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs",
|
||||
"test-container": "../node_modules/mocha/bin/_mocha --timeout 5000 tests/container/specs/api",
|
||||
"dev": "bash ./bin/run.sh"
|
||||
},
|
||||
"version": "1.9.4",
|
||||
"version": "1.9.1",
|
||||
"license": "Apache-2.0"
|
||||
}
|
||||
|
|
|
@ -2585,17 +2585,17 @@ function Ace2Inner(editorInfo, cssManagers) {
|
|||
const firstEditbarElement = parent.parent.$('#editbar')
|
||||
.children('ul').first().children().first()
|
||||
.children().first().children().first();
|
||||
$(this).trigger('blur');
|
||||
firstEditbarElement.trigger('focus');
|
||||
$(this).blur();
|
||||
firstEditbarElement.focus();
|
||||
evt.preventDefault();
|
||||
}
|
||||
if (!specialHandled && type === 'keydown' &&
|
||||
altKey && keyCode === 67 &&
|
||||
padShortcutEnabled.altC) {
|
||||
// Alt c focuses on the Chat window
|
||||
$(this).trigger('blur');
|
||||
$(this).blur();
|
||||
parent.parent.chat.show();
|
||||
parent.parent.$('#chatinput').trigger('focus');
|
||||
parent.parent.$('#chatinput').focus();
|
||||
evt.preventDefault();
|
||||
}
|
||||
if (!specialHandled && type === 'keydown' &&
|
||||
|
|
|
@ -164,7 +164,7 @@
|
|||
$(window).resize(adjust);
|
||||
|
||||
// Allow for manual triggering if needed.
|
||||
$ta.on('autosize', adjust);
|
||||
$ta.bind('autosize', adjust);
|
||||
|
||||
// Call adjust in case the textarea already contains text.
|
||||
adjust();
|
||||
|
|
|
@ -112,29 +112,33 @@ $(document).ready(() => {
|
|||
|
||||
const updateHandlers = () => {
|
||||
// Search
|
||||
$('#search-query').off('keyup').on('keyup', () => {
|
||||
$('#search-query').unbind('keyup').keyup(() => {
|
||||
search($('#search-query').val());
|
||||
});
|
||||
|
||||
// Prevent form submit
|
||||
$('#search-query').parent().on('submit', () => false);
|
||||
$('#search-query').parent().bind('submit', () => false);
|
||||
|
||||
// update & install
|
||||
$('.do-install, .do-update').off('click').on('click', function (e) {
|
||||
$('.do-install, .do-update').unbind('click').click(function (e) {
|
||||
const $row = $(e.target).closest('tr');
|
||||
const plugin = $row.data('plugin');
|
||||
const update = $row.find('input.do-update');
|
||||
// TODO dont store it here in DOM
|
||||
// version after update or after installing a new package
|
||||
const version = update.length !== 0 ? update.attr('version') : $row.find('.version').text();
|
||||
if ($(this).hasClass('do-install')) {
|
||||
$row.remove().appendTo('#installed-plugins');
|
||||
installed.progress.show(plugin, 'Installing');
|
||||
} else {
|
||||
installed.progress.show(plugin, 'Updating');
|
||||
}
|
||||
socket.emit('install', plugin);
|
||||
socket.emit('install', plugin, version);
|
||||
installed.messages.hide('nothing-installed');
|
||||
});
|
||||
|
||||
// uninstall
|
||||
$('.do-uninstall').off('click').on('click', (e) => {
|
||||
$('.do-uninstall').unbind('click').click((e) => {
|
||||
const $row = $(e.target).closest('tr');
|
||||
const pluginName = $row.data('plugin');
|
||||
socket.emit('uninstall', pluginName);
|
||||
|
@ -143,14 +147,14 @@ $(document).ready(() => {
|
|||
});
|
||||
|
||||
// Sort
|
||||
$('.sort.up').off('click').on('click', function () {
|
||||
$('.sort.up').unbind('click').click(function () {
|
||||
search.sortBy = $(this).attr('data-label').toLowerCase();
|
||||
search.sortDir = false;
|
||||
search.offset = 0;
|
||||
search(search.searchTerm, search.results.length);
|
||||
search.results = [];
|
||||
});
|
||||
$('.sort.down, .sort.none').off('click').on('click', function () {
|
||||
$('.sort.down, .sort.none').unbind('click').click(function () {
|
||||
search.sortBy = $(this).attr('data-label').toLowerCase();
|
||||
search.sortDir = true;
|
||||
search.offset = 0;
|
||||
|
@ -164,7 +168,7 @@ $(document).ready(() => {
|
|||
if (data.query.offset === 0) search.results = [];
|
||||
search.messages.hide('nothing-found');
|
||||
search.messages.hide('fetching');
|
||||
$('#search-query').prop('disabled', false);
|
||||
$('#search-query').removeAttr('disabled');
|
||||
|
||||
console.log('got search results', data);
|
||||
|
||||
|
@ -217,11 +221,13 @@ $(document).ready(() => {
|
|||
});
|
||||
|
||||
socket.on('results:updatable', (data) => {
|
||||
data.updatable.forEach((pluginName) => {
|
||||
const actions = $(`#installed-plugins > tr.${pluginName} .actions`);
|
||||
data.updatable.forEach((plugin) => {
|
||||
const {name, version} = plugin;
|
||||
const actions = $(`#installed-plugins > tr.${name} .actions`);
|
||||
actions.find('.do-update').remove();
|
||||
// TODO dont store version here in DOM
|
||||
actions.append(
|
||||
$('<input>').addClass('do-update').attr('type', 'button').attr('value', 'Update'));
|
||||
$('<input>').addClass('do-update').attr('type', 'button').attr('version', version).attr('value', 'Update'));
|
||||
});
|
||||
updateHandlers();
|
||||
});
|
||||
|
|
|
@ -25,7 +25,7 @@ $(document).ready(() => {
|
|||
/* Check to make sure the JSON is clean before proceeding */
|
||||
if (isJSONClean(settings.results)) {
|
||||
$('.settings').append(settings.results);
|
||||
$('.settings').trigger('focus');
|
||||
$('.settings').focus();
|
||||
$('.settings').autosize();
|
||||
} else {
|
||||
alert('Invalid JSON');
|
||||
|
@ -40,7 +40,7 @@ $(document).ready(() => {
|
|||
socket.emit('saveSettings', $('.settings').val());
|
||||
} else {
|
||||
alert('Invalid JSON');
|
||||
$('.settings').trigger('focus');
|
||||
$('.settings').focus();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -62,7 +62,7 @@ const isJSONClean = (data) => {
|
|||
// this is a bit naive. In theory some key/value might contain the sequences ',]' or ',}'
|
||||
cleanSettings = cleanSettings.replace(',]', ']').replace(',}', '}');
|
||||
try {
|
||||
return typeof JSON.parse(cleanSettings) === 'object';
|
||||
return typeof jQuery.parseJSON(cleanSettings) === 'object';
|
||||
} catch (e) {
|
||||
return false; // the JSON failed to be parsed
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ const loadBroadcastSliderJS = (fireWhenAllScriptsAreLoaded) => {
|
|||
newSavedRevision.css(
|
||||
'left', (position * ($('#ui-slider-bar').width() - 2) / (sliderLength * 1.0)) - 1);
|
||||
$('#ui-slider-bar').append(newSavedRevision);
|
||||
newSavedRevision.on('mouseup', (evt) => {
|
||||
newSavedRevision.mouseup((evt) => {
|
||||
BroadcastSlider.setSliderPosition(position);
|
||||
});
|
||||
savedRevisions.push(newSavedRevision);
|
||||
|
@ -209,21 +209,21 @@ const loadBroadcastSliderJS = (fireWhenAllScriptsAreLoaded) => {
|
|||
|
||||
// assign event handlers to html UI elements after page load
|
||||
fireWhenAllScriptsAreLoaded.push(() => {
|
||||
$(document).on('keyup', (e) => {
|
||||
$(document).keyup((e) => {
|
||||
if (!e) e = window.event;
|
||||
const code = e.keyCode || e.which;
|
||||
|
||||
if (code === 37) { // left
|
||||
if (e.shiftKey) {
|
||||
$('#leftstar').trigger('click');
|
||||
$('#leftstar').click();
|
||||
} else {
|
||||
$('#leftstep').trigger('click');
|
||||
$('#leftstep').click();
|
||||
}
|
||||
} else if (code === 39) { // right
|
||||
if (e.shiftKey) {
|
||||
$('#rightstar').trigger('click');
|
||||
$('#rightstar').click();
|
||||
} else {
|
||||
$('#rightstep').trigger('click');
|
||||
$('#rightstep').click();
|
||||
}
|
||||
} else if (code === 32) { // spacebar
|
||||
$('#playpause_button_icon').trigger('click');
|
||||
|
@ -231,22 +231,22 @@ const loadBroadcastSliderJS = (fireWhenAllScriptsAreLoaded) => {
|
|||
});
|
||||
|
||||
// Resize
|
||||
$(window).on('resize', () => {
|
||||
$(window).resize(() => {
|
||||
updateSliderElements();
|
||||
});
|
||||
|
||||
// Slider click
|
||||
$('#ui-slider-bar').on('mousedown', (evt) => {
|
||||
$('#ui-slider-bar').mousedown((evt) => {
|
||||
$('#ui-slider-handle').css('left', (evt.clientX - $('#ui-slider-bar').offset().left));
|
||||
$('#ui-slider-handle').trigger(evt);
|
||||
});
|
||||
|
||||
// Slider dragging
|
||||
$('#ui-slider-handle').on('mousedown', function (evt) {
|
||||
$('#ui-slider-handle').mousedown(function (evt) {
|
||||
this.startLoc = evt.clientX;
|
||||
this.currentLoc = parseInt($(this).css('left'));
|
||||
sliderActive = true;
|
||||
$(document).on('mousemove', (evt2) => {
|
||||
$(document).mousemove((evt2) => {
|
||||
$(this).css('pointer', 'move');
|
||||
let newloc = this.currentLoc + (evt2.clientX - this.startLoc);
|
||||
if (newloc < 0) newloc = 0;
|
||||
|
@ -257,9 +257,9 @@ const loadBroadcastSliderJS = (fireWhenAllScriptsAreLoaded) => {
|
|||
$(this).css('left', newloc);
|
||||
if (getSliderPosition() !== version) _callSliderCallbacks(version);
|
||||
});
|
||||
$(document).on('mouseup', (evt2) => {
|
||||
$(document).off('mousemove');
|
||||
$(document).off('mouseup');
|
||||
$(document).mouseup((evt2) => {
|
||||
$(document).unbind('mousemove');
|
||||
$(document).unbind('mouseup');
|
||||
sliderActive = false;
|
||||
let newloc = this.currentLoc + (evt2.clientX - this.startLoc);
|
||||
if (newloc < 0) newloc = 0;
|
||||
|
@ -276,12 +276,12 @@ const loadBroadcastSliderJS = (fireWhenAllScriptsAreLoaded) => {
|
|||
});
|
||||
|
||||
// play/pause toggling
|
||||
$('#playpause_button_icon').on('click', (evt) => {
|
||||
$('#playpause_button_icon').click((evt) => {
|
||||
BroadcastSlider.playpause();
|
||||
});
|
||||
|
||||
// next/prev saved revision and changeset
|
||||
$('.stepper').on('click', function (evt) {
|
||||
$('.stepper').click(function (evt) {
|
||||
switch ($(this).attr('id')) {
|
||||
case 'leftstep':
|
||||
setSliderPosition(getSliderPosition() - 1);
|
||||
|
|
|
@ -42,14 +42,11 @@ exports.chat = (() => {
|
|||
},
|
||||
focus: () => {
|
||||
setTimeout(() => {
|
||||
$('#chatinput').trigger('focus');
|
||||
$('#chatinput').focus();
|
||||
}, 100);
|
||||
},
|
||||
// Make chat stick to right hand side of screen
|
||||
stickToScreen(fromInitialCall) {
|
||||
if ($('#options-stickychat').prop('checked')) {
|
||||
$('#options-stickychat').prop('checked', false);
|
||||
}
|
||||
if (pad.settings.hideChat) {
|
||||
return;
|
||||
}
|
||||
|
@ -71,7 +68,7 @@ exports.chat = (() => {
|
|||
this.stickToScreen(true);
|
||||
$('#options-stickychat').prop('checked', true);
|
||||
$('#options-chatandusers').prop('checked', true);
|
||||
$('#options-stickychat').prop('disabled', true);
|
||||
$('#options-stickychat').prop('disabled', 'disabled');
|
||||
userAndChat = true;
|
||||
} else {
|
||||
$('#options-stickychat').prop('disabled', false);
|
||||
|
@ -226,14 +223,14 @@ exports.chat = (() => {
|
|||
// Send the users focus back to the pad
|
||||
if ((evt.altKey === true && evt.which === 67) || evt.which === 27) {
|
||||
// If we're in chat already..
|
||||
$(':focus').trigger('blur'); // required to do not try to remove!
|
||||
$(':focus').blur(); // required to do not try to remove!
|
||||
padeditor.ace.focus(); // Sends focus back to pad
|
||||
evt.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// Clear the chat mentions when the user clicks on the chat input box
|
||||
$('#chatinput').on('click', () => {
|
||||
$('#chatinput').click(() => {
|
||||
chatMentions = 0;
|
||||
Tinycon.setBubble(0);
|
||||
});
|
||||
|
@ -242,14 +239,14 @@ exports.chat = (() => {
|
|||
$('body:not(#chatinput)').on('keypress', function (evt) {
|
||||
if (evt.altKey && evt.which === 67) {
|
||||
// Alt c focuses on the Chat window
|
||||
$(this).trigger('blur');
|
||||
$(this).blur();
|
||||
self.show();
|
||||
$('#chatinput').trigger('focus');
|
||||
$('#chatinput').focus();
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
$('#chatinput').on('keypress', (evt) => {
|
||||
$('#chatinput').keypress((evt) => {
|
||||
// if the user typed enter, fire the send
|
||||
if (evt.key === 'Enter' && !evt.shiftKey) {
|
||||
evt.preventDefault();
|
||||
|
@ -260,7 +257,7 @@ exports.chat = (() => {
|
|||
// initial messages are loaded in pad.js' _afterHandshake
|
||||
|
||||
$('#chatcounter').text(0);
|
||||
$('#chatloadmessagesbutton').on('click', () => {
|
||||
$('#chatloadmessagesbutton').click(() => {
|
||||
const start = Math.max(this.historyPointer - 20, 0);
|
||||
const end = this.historyPointer;
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ const getCollabClient = (ace2editor, serverVars, initialUserInfo, options, _pad)
|
|||
if (browser.firefox) {
|
||||
// Prevent "escape" from taking effect and canceling a comet connection;
|
||||
// doesn't work if focus is on an iframe.
|
||||
$(window).on('keydown', (evt) => {
|
||||
$(window).bind('keydown', (evt) => {
|
||||
if (evt.which === 27) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ const randomPadName = () => {
|
|||
};
|
||||
|
||||
$(() => {
|
||||
$('#go2Name').on('submit', () => {
|
||||
$('#go2Name').submit(() => {
|
||||
const padname = $('#padname').val();
|
||||
if (padname.length > 0) {
|
||||
window.location = `p/${encodeURIComponent(padname.trim())}`;
|
||||
|
@ -51,7 +51,7 @@ $(() => {
|
|||
return false;
|
||||
});
|
||||
|
||||
$('#button').on('click', () => {
|
||||
$('#button').click(() => {
|
||||
window.location = `p/${randomPadName()}`;
|
||||
});
|
||||
|
||||
|
|
|
@ -412,12 +412,10 @@ const pad = {
|
|||
setTimeout(() => {
|
||||
padeditor.ace.focus();
|
||||
}, 0);
|
||||
const optionsStickyChat = $('#options-stickychat');
|
||||
optionsStickyChat.on('click', () => { chat.stickToScreen(); });
|
||||
// if we have a cookie for always showing chat then show it
|
||||
if (padcookie.getPref('chatAlwaysVisible')) {
|
||||
chat.stickToScreen(true); // stick it to the screen
|
||||
optionsStickyChat.prop('checked', true); // set the checkbox to on
|
||||
$('#options-stickychat').prop('checked', true); // set the checkbox to on
|
||||
}
|
||||
// if we have a cookie for always showing chat then show it
|
||||
if (padcookie.getPref('chatAndUsers')) {
|
||||
|
@ -439,8 +437,8 @@ const pad = {
|
|||
// Prevent sticky chat or chat and users to be checked for mobiles
|
||||
const checkChatAndUsersVisibility = (x) => {
|
||||
if (x.matches) { // If media query matches
|
||||
$('#options-chatandusers:checked').trigger('click');
|
||||
$('#options-stickychat:checked').trigger('click');
|
||||
$('#options-chatandusers:checked').click();
|
||||
$('#options-stickychat:checked').click();
|
||||
}
|
||||
};
|
||||
const mobileMatch = window.matchMedia('(max-width: 800px)');
|
||||
|
@ -713,7 +711,7 @@ const pad = {
|
|||
$('form#reconnectform input.diagnosticInfo').val(JSON.stringify(pad.diagnosticInfo));
|
||||
$('form#reconnectform input.missedChanges')
|
||||
.val(JSON.stringify(pad.collabClient.getMissedChanges()));
|
||||
$('form#reconnectform').trigger('submit');
|
||||
$('form#reconnectform').submit();
|
||||
},
|
||||
callWhenNotCommitting: (f) => {
|
||||
pad.collabClient.callWhenNotCommitting(f);
|
||||
|
|
|
@ -96,7 +96,7 @@ const whenConnectionIsRestablishedWithServer = (callback, pad) => {
|
|||
};
|
||||
|
||||
const forceReconnection = ($modal) => {
|
||||
$modal.find('#forcereconnect').trigger('click');
|
||||
$modal.find('#forcereconnect').click();
|
||||
};
|
||||
|
||||
const updateCountDownTimerMessage = ($modal, minutes, seconds) => {
|
||||
|
|
|
@ -31,7 +31,7 @@ const padconnectionstatus = (() => {
|
|||
|
||||
const self = {
|
||||
init: () => {
|
||||
$('button#forcereconnect').on('click', () => {
|
||||
$('button#forcereconnect').click(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
},
|
||||
|
|
|
@ -65,13 +65,13 @@ class ToolbarItem {
|
|||
|
||||
bind(callback) {
|
||||
if (this.isButton()) {
|
||||
this.$el.on('click', (event) => {
|
||||
$(':focus').trigger('blur');
|
||||
this.$el.click((event) => {
|
||||
$(':focus').blur();
|
||||
callback(this.getCommand(), this);
|
||||
event.preventDefault();
|
||||
});
|
||||
} else if (this.isSelect()) {
|
||||
this.$el.find('select').on('change', () => {
|
||||
this.$el.find('select').change(() => {
|
||||
callback(this.getCommand(), this);
|
||||
});
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ exports.padeditbar = new class {
|
|||
$('#editbar .editbarbutton').attr('unselectable', 'on'); // for IE
|
||||
this.enable();
|
||||
$('#editbar [data-key]').each((i, elt) => {
|
||||
$(elt).off('click');
|
||||
$(elt).unbind('click');
|
||||
new ToolbarItem($(elt)).bind((command, item) => {
|
||||
this.triggerCommand(command, item);
|
||||
});
|
||||
|
@ -144,11 +144,11 @@ exports.padeditbar = new class {
|
|||
this._bodyKeyEvent(evt);
|
||||
});
|
||||
|
||||
$('.show-more-icon-btn').on('click', () => {
|
||||
$('.show-more-icon-btn').click(() => {
|
||||
$('.toolbar').toggleClass('full-icons');
|
||||
});
|
||||
this.checkAllIconsAreDisplayedInToolbar();
|
||||
$(window).on('resize', _.debounce(() => this.checkAllIconsAreDisplayedInToolbar(), 100));
|
||||
$(window).resize(_.debounce(() => this.checkAllIconsAreDisplayedInToolbar(), 100));
|
||||
|
||||
this._registerDefaultCommands();
|
||||
|
||||
|
@ -168,7 +168,7 @@ exports.padeditbar = new class {
|
|||
}
|
||||
|
||||
// When editor is scrolled, we add a class to style the editbar differently
|
||||
$('iframe[name="ace_outer"]').contents().on('scroll', (ev) => {
|
||||
$('iframe[name="ace_outer"]').contents().scroll((ev) => {
|
||||
$('#editbar').toggleClass('editor-scrolled', $(ev.currentTarget).scrollTop() > 2);
|
||||
});
|
||||
}
|
||||
|
@ -305,12 +305,12 @@ exports.padeditbar = new class {
|
|||
// Close any dropdowns we have open..
|
||||
this.toggleDropDown('none');
|
||||
// Shift focus away from any drop downs
|
||||
$(':focus').trigger('blur'); // required to do not try to remove!
|
||||
$(':focus').blur(); // required to do not try to remove!
|
||||
// Check we're on a pad and not on the timeslider
|
||||
// Or some other window I haven't thought about!
|
||||
if (typeof pad === 'undefined') {
|
||||
// Timeslider probably..
|
||||
$('#editorcontainerbox').trigger('focus'); // Focus back onto the pad
|
||||
$('#editorcontainerbox').focus(); // Focus back onto the pad
|
||||
} else {
|
||||
padeditor.ace.focus(); // Sends focus back to pad
|
||||
// The above focus doesn't always work in FF, you have to hit enter afterwards
|
||||
|
@ -318,10 +318,10 @@ exports.padeditbar = new class {
|
|||
}
|
||||
} else {
|
||||
// Focus on the editbar :)
|
||||
const firstEditbarElement = $('#editbar button').first();
|
||||
const firstEditbarElement = parent.parent.$('#editbar button').first();
|
||||
|
||||
$(evt.currentTarget).trigger('blur');
|
||||
firstEditbarElement.trigger('focus');
|
||||
$(evt.currentTarget).blur();
|
||||
firstEditbarElement.focus();
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
|
@ -341,7 +341,7 @@ exports.padeditbar = new class {
|
|||
this._editbarPosition--;
|
||||
// Allow focus to shift back to end of row and start of row
|
||||
if (this._editbarPosition === -1) this._editbarPosition = focusItems.length - 1;
|
||||
$(focusItems[this._editbarPosition]).trigger('focus');
|
||||
$(focusItems[this._editbarPosition]).focus();
|
||||
}
|
||||
|
||||
// On right arrow move to next button in editbar
|
||||
|
@ -352,7 +352,7 @@ exports.padeditbar = new class {
|
|||
this._editbarPosition++;
|
||||
// Allow focus to shift back to end of row and start of row
|
||||
if (this._editbarPosition >= focusItems.length) this._editbarPosition = 0;
|
||||
$(focusItems[this._editbarPosition]).trigger('focus');
|
||||
$(focusItems[this._editbarPosition]).focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -366,7 +366,7 @@ exports.padeditbar = new class {
|
|||
|
||||
this.registerCommand('settings', () => {
|
||||
this.toggleDropDown('settings');
|
||||
$('#options-stickychat').trigger('focus');
|
||||
$('#options-stickychat').focus();
|
||||
});
|
||||
|
||||
this.registerCommand('import_export', () => {
|
||||
|
@ -374,22 +374,22 @@ exports.padeditbar = new class {
|
|||
// If Import file input exists then focus on it..
|
||||
if ($('#importfileinput').length !== 0) {
|
||||
setTimeout(() => {
|
||||
$('#importfileinput').trigger('focus');
|
||||
$('#importfileinput').focus();
|
||||
}, 100);
|
||||
} else {
|
||||
$('.exportlink').first().trigger('focus');
|
||||
$('.exportlink').first().focus();
|
||||
}
|
||||
});
|
||||
|
||||
this.registerCommand('showusers', () => {
|
||||
this.toggleDropDown('users');
|
||||
$('#myusernameedit').trigger('focus');
|
||||
$('#myusernameedit').focus();
|
||||
});
|
||||
|
||||
this.registerCommand('embed', () => {
|
||||
this.setEmbedLinks();
|
||||
this.toggleDropDown('embed');
|
||||
$('#linkinput').trigger('focus').trigger('select');
|
||||
$('#linkinput').focus().select();
|
||||
});
|
||||
|
||||
this.registerCommand('savedRevision', () => {
|
||||
|
|
|
@ -76,7 +76,7 @@ const padeditor = (() => {
|
|||
});
|
||||
|
||||
// font family change
|
||||
$('#viewfontmenu').on('change', () => {
|
||||
$('#viewfontmenu').change(() => {
|
||||
pad.changeViewOption('padFontFamily', $('#viewfontmenu').val());
|
||||
});
|
||||
|
||||
|
@ -97,7 +97,7 @@ const padeditor = (() => {
|
|||
});
|
||||
});
|
||||
$('#languagemenu').val(html10n.getLanguage());
|
||||
$('#languagemenu').on('change', () => {
|
||||
$('#languagemenu').change(() => {
|
||||
Cookies.set('language', $('#languagemenu').val());
|
||||
window.html10n.localize([$('#languagemenu').val(), 'en']);
|
||||
if ($('select').niceSelect) {
|
||||
|
|
|
@ -38,7 +38,7 @@ const padimpexp = (() => {
|
|||
const fileInputUpdated = () => {
|
||||
$('#importsubmitinput').addClass('throbbold');
|
||||
$('#importformfilediv').addClass('importformenabled');
|
||||
$('#importsubmitinput').prop('disabled', false);
|
||||
$('#importsubmitinput').removeAttr('disabled');
|
||||
$('#importmessagefail').fadeOut('fast');
|
||||
};
|
||||
|
||||
|
@ -69,8 +69,8 @@ const padimpexp = (() => {
|
|||
$('#import_export').removeClass('popup-show');
|
||||
if (directDatabaseAccess) window.location.reload();
|
||||
}
|
||||
$('#importsubmitinput').prop('disabled', false).val(html10n.get('pad.impexp.importbutton'));
|
||||
window.setTimeout(() => $('#importfileinput').prop('disabled', false), 0);
|
||||
$('#importsubmitinput').removeAttr('disabled').val(html10n.get('pad.impexp.importbutton'));
|
||||
window.setTimeout(() => $('#importfileinput').removeAttr('disabled'), 0);
|
||||
$('#importstatusball').hide();
|
||||
addImportFrames();
|
||||
})();
|
||||
|
@ -162,9 +162,9 @@ const padimpexp = (() => {
|
|||
}
|
||||
|
||||
addImportFrames();
|
||||
$('#importfileinput').on('change', fileInputUpdated);
|
||||
$('#importform').off('submit').on('submit', fileInputSubmit);
|
||||
$('.disabledexport').on('click', cantExport);
|
||||
$('#importfileinput').change(fileInputUpdated);
|
||||
$('#importform').unbind('submit').submit(fileInputSubmit);
|
||||
$('.disabledexport').click(cantExport);
|
||||
},
|
||||
disable: () => {
|
||||
$('#impexp-disabled-clickcatcher').show();
|
||||
|
|
|
@ -325,23 +325,23 @@ const paduserlist = (() => {
|
|||
};
|
||||
|
||||
const setUpEditable = (jqueryNode, valueGetter, valueSetter) => {
|
||||
jqueryNode.on('focus', (evt) => {
|
||||
jqueryNode.bind('focus', (evt) => {
|
||||
const oldValue = valueGetter();
|
||||
if (jqueryNode.val() !== oldValue) {
|
||||
jqueryNode.val(oldValue);
|
||||
}
|
||||
jqueryNode.addClass('editactive').removeClass('editempty');
|
||||
});
|
||||
jqueryNode.on('blur', (evt) => {
|
||||
jqueryNode.bind('blur', (evt) => {
|
||||
const newValue = jqueryNode.removeClass('editactive').val();
|
||||
valueSetter(newValue);
|
||||
});
|
||||
padutils.bindEnterAndEscape(jqueryNode, () => {
|
||||
jqueryNode.trigger('blur');
|
||||
jqueryNode.blur();
|
||||
}, () => {
|
||||
jqueryNode.val(valueGetter()).trigger('blur');
|
||||
jqueryNode.val(valueGetter()).blur();
|
||||
});
|
||||
jqueryNode.prop('disabled', false).addClass('editable');
|
||||
jqueryNode.removeAttr('disabled').addClass('editable');
|
||||
};
|
||||
|
||||
let pad = undefined;
|
||||
|
@ -369,15 +369,15 @@ const paduserlist = (() => {
|
|||
});
|
||||
|
||||
// color picker
|
||||
$('#myswatchbox').on('click', showColorPicker);
|
||||
$('#mycolorpicker .pickerswatchouter').on('click', function () {
|
||||
$('#myswatchbox').click(showColorPicker);
|
||||
$('#mycolorpicker .pickerswatchouter').click(function () {
|
||||
$('#mycolorpicker .pickerswatchouter').removeClass('picked');
|
||||
$(this).addClass('picked');
|
||||
});
|
||||
$('#mycolorpickersave').on('click', () => {
|
||||
$('#mycolorpickersave').click(() => {
|
||||
closeColorPicker(true);
|
||||
});
|
||||
$('#mycolorpickercancel').on('click', () => {
|
||||
$('#mycolorpickercancel').click(() => {
|
||||
closeColorPicker(false);
|
||||
});
|
||||
//
|
||||
|
@ -587,7 +587,7 @@ const showColorPicker = () => {
|
|||
|
||||
li.appendTo(colorsList);
|
||||
|
||||
li.on('click', (event) => {
|
||||
li.bind('click', (event) => {
|
||||
$('#colorpickerswatches li').removeClass('picked');
|
||||
$(event.target).addClass('picked');
|
||||
|
||||
|
|
|
@ -224,7 +224,7 @@ const padutils = {
|
|||
// It is work on Windows (IE8, Chrome 6.0.472), CentOs (Firefox 3.0) and Mac OSX (Firefox
|
||||
// 3.6.10, Chrome 6.0.472, Safari 5.0).
|
||||
if (onEnter) {
|
||||
node.on('keypress', (evt) => {
|
||||
node.keypress((evt) => {
|
||||
if (evt.which === 13) {
|
||||
onEnter(evt);
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ const padutils = {
|
|||
}
|
||||
|
||||
if (onEscape) {
|
||||
node.on('keydown', (evt) => {
|
||||
node.keydown((evt) => {
|
||||
if (evt.which === 27) {
|
||||
onEscape(evt);
|
||||
}
|
||||
|
@ -299,7 +299,7 @@ const padutils = {
|
|||
}
|
||||
field.removeClass('editempty');
|
||||
});
|
||||
field.on('blur', () => {
|
||||
field.blur(() => {
|
||||
if (!field.val()) {
|
||||
clear();
|
||||
}
|
||||
|
@ -313,11 +313,11 @@ const padutils = {
|
|||
if (value) {
|
||||
$(node).attr('checked', 'checked');
|
||||
} else {
|
||||
$(node).prop('checked', false);
|
||||
$(node).removeAttr('checked');
|
||||
}
|
||||
},
|
||||
bindCheckboxChange: (node, func) => {
|
||||
$(node).on('change', func);
|
||||
$(node).change(func);
|
||||
},
|
||||
encodeUserId: (userId) => userId.replace(/[^a-y0-9]/g, (c) => {
|
||||
if (c === '.') return '-';
|
||||
|
|
|
@ -31,10 +31,7 @@ exports.uninstall = async (pluginName, cb = null) => {
|
|||
cb = wrapTaskCb(cb);
|
||||
logger.info(`Uninstalling plugin ${pluginName}...`);
|
||||
try {
|
||||
// The --no-save flag prevents npm from creating package.json or package-lock.json.
|
||||
// The --legacy-peer-deps flag is required to work around a bug in npm v7:
|
||||
// https://github.com/npm/cli/issues/2199
|
||||
await runCmd(['npm', 'uninstall', '--no-save', '--legacy-peer-deps', pluginName]);
|
||||
await runCmd(['npm', 'uninstall', pluginName]);
|
||||
} catch (err) {
|
||||
logger.error(`Failed to uninstall plugin ${pluginName}`);
|
||||
cb(err || new Error(err));
|
||||
|
@ -46,20 +43,17 @@ exports.uninstall = async (pluginName, cb = null) => {
|
|||
cb(null);
|
||||
};
|
||||
|
||||
exports.install = async (pluginName, cb = null) => {
|
||||
exports.install = async (pluginName, version, cb = null) => {
|
||||
cb = wrapTaskCb(cb);
|
||||
logger.info(`Installing plugin ${pluginName}...`);
|
||||
logger.info(`Installing plugin ${pluginName}@${version}...`);
|
||||
try {
|
||||
// The --no-save flag prevents npm from creating package.json or package-lock.json.
|
||||
// The --legacy-peer-deps flag is required to work around a bug in npm v7:
|
||||
// https://github.com/npm/cli/issues/2199
|
||||
await runCmd(['npm', 'install', '--no-save', '--legacy-peer-deps', pluginName]);
|
||||
await runCmd(['npm', 'install', `${pluginName}@${version}`]);
|
||||
} catch (err) {
|
||||
logger.error(`Failed to install plugin ${pluginName}`);
|
||||
logger.error(`Failed to install plugin ${pluginName}@${version}`);
|
||||
cb(err || new Error(err));
|
||||
throw err;
|
||||
}
|
||||
logger.info(`Successfully installed plugin ${pluginName}`);
|
||||
logger.info(`Successfully installed plugin ${pluginName}@${version}`);
|
||||
await hooks.aCallAll('pluginInstall', {pluginName});
|
||||
await plugins.update();
|
||||
cb(null);
|
||||
|
|
|
@ -46,7 +46,7 @@ if (window.location.hash.toLowerCase() === '#skinvariantsbuilder') {
|
|||
$('#skin-variant-full-width').prop('checked', $('html').hasClass('full-width-editor'));
|
||||
};
|
||||
|
||||
$('.skin-variant').on('change', () => {
|
||||
$('.skin-variant').change(() => {
|
||||
updateSkinVariantsClasses();
|
||||
});
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ const init = () => {
|
|||
// get all the export links
|
||||
exportLinks = $('#export > .exportlink');
|
||||
|
||||
$('button#forcereconnect').on('click', () => {
|
||||
$('button#forcereconnect').click(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
|
@ -159,7 +159,7 @@ const handleClientVars = (message) => {
|
|||
$('#rightstep').attr('title', html10n.get('timeslider.forwardRevision'));
|
||||
|
||||
// font family change
|
||||
$('#viewfontmenu').on('change', function () {
|
||||
$('#viewfontmenu').change(function () {
|
||||
$('#innerdocbody').css('font-family', $(this).val() || '');
|
||||
});
|
||||
};
|
||||
|
|
|
@ -33,7 +33,7 @@ $._farbtastic = function (container, options) {
|
|||
fb.linkTo = function (callback) {
|
||||
// Unbind previous nodes
|
||||
if (typeof fb.callback == 'object') {
|
||||
$(fb.callback).off('keyup').on('keyup', fb.updateValue);
|
||||
$(fb.callback).unbind('keyup', fb.updateValue);
|
||||
}
|
||||
|
||||
// Reset color
|
||||
|
@ -45,7 +45,7 @@ $._farbtastic = function (container, options) {
|
|||
}
|
||||
else if (typeof callback == 'object' || typeof callback == 'string') {
|
||||
fb.callback = $(callback);
|
||||
fb.callback.on('keyup', fb.updateValue);
|
||||
fb.callback.bind('keyup', fb.updateValue);
|
||||
if (fb.callback[0].value) {
|
||||
fb.setColor(fb.callback[0].value);
|
||||
}
|
||||
|
@ -388,7 +388,7 @@ $._farbtastic = function (container, options) {
|
|||
fb.mousedown = function (event) {
|
||||
// Capture mouse
|
||||
if (!$._farbtastic.dragging) {
|
||||
$(document).on('mousemove', fb.mousemove).on('mouseup', fb.mouseup);
|
||||
$(document).bind('mousemove', fb.mousemove).bind('mouseup', fb.mouseup);
|
||||
$._farbtastic.dragging = true;
|
||||
}
|
||||
|
||||
|
@ -429,8 +429,8 @@ $._farbtastic = function (container, options) {
|
|||
*/
|
||||
fb.mouseup = function () {
|
||||
// Uncapture mouse
|
||||
$(document).off('mousemove', fb.mousemove);
|
||||
$(document).off('mouseup', fb.mouseup);
|
||||
$(document).unbind('mousemove', fb.mousemove);
|
||||
$(document).unbind('mouseup', fb.mouseup);
|
||||
$._farbtastic.dragging = false;
|
||||
}
|
||||
|
||||
|
@ -519,7 +519,7 @@ $._farbtastic = function (container, options) {
|
|||
fb.initWidget();
|
||||
|
||||
// Install mousedown handler (the others are set on the document on-demand)
|
||||
$('canvas.farbtastic-overlay', container).on('mousedown',fb.mousedown);
|
||||
$('canvas.farbtastic-overlay', container).mousedown(fb.mousedown);
|
||||
|
||||
// Set linked elements/callback
|
||||
if (options.callback) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -123,7 +123,7 @@
|
|||
$dropdown.find('.list').css('max-height', $maxListHeight + 'px');
|
||||
|
||||
} else {
|
||||
$dropdown.trigger('focus');
|
||||
$dropdown.focus();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
window.customStart = () => {
|
||||
$('#pad_title').show();
|
||||
$('.buttonicon').on('mousedown', function () { $(this).parent().addClass('pressed'); });
|
||||
$('.buttonicon').on('mouseup', function () { $(this).parent().removeClass('pressed'); });
|
||||
$('.buttonicon').mousedown(function () { $(this).parent().addClass('pressed'); });
|
||||
$('.buttonicon').mouseup(function () { $(this).parent().removeClass('pressed'); });
|
||||
};
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<title><%- padId %></title>
|
||||
<meta name="generator" content="Etherpad"/>
|
||||
<meta name="author" content="Etherpad"/>
|
||||
<meta name="changedby" content="Etherpad"/>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="generator" content="Etherpad">
|
||||
<meta name="author" content="Etherpad">
|
||||
<meta name="changedby" content="Etherpad">
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
ol {
|
||||
counter-reset: item;
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
<% e.begin_block("mySettings"); %>
|
||||
<h2 data-l10n-id="pad.settings.myView"></h2>
|
||||
<p class="hide-for-mobile">
|
||||
<input type="checkbox" id="options-stickychat">
|
||||
<input type="checkbox" id="options-stickychat" onClick="chat.stickToScreen();">
|
||||
<label for="options-stickychat" data-l10n-id="pad.settings.stickychat"></label>
|
||||
</p>
|
||||
<p class="hide-for-mobile">
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue